/*********************************************************************
* ConnectionHeader.java
* created on 04.03.2005 by netseeker
* $Source: /cvsroot/ejoe/EJOE/src/de/netseeker/ejoe/ConnectionHeader.java,v $
* $Date: 2007/03/22 21:01:35 $
* $Revision: 1.35 $
*
* ====================================================================
*
* Copyright 2005-2006 netseeker aka Michael Manske
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*
* This file is part of the ejoe framework.
* For more information on the author, please see
* <http://www.manskes.de/>.
*
*********************************************************************/
package de.netseeker.ejoe;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.text.ParseException;
import de.netseeker.ejoe.cache.ByteBufferAllocator;
import de.netseeker.ejoe.io.IOUtil;
/**
* A simple connection header contining informations about compression and blocking/non-blocking io features. The header
* bits are structured as follows:
* |compression|nio|persistent|http|directMode|mixedMode|useAdapter|canHandleServerHandshakeResponse|
*
* @author netseeker
* @since 0.3.0
*/
public class ConnectionHeader implements Serializable
{
private static final long serialVersionUID = 1L;
private boolean[] _header;
private transient ByteBuffer _waitingBuffer;
private transient SocketChannel _channel;
private transient Object _attachment;
private transient Object _attachementInfo;
private int _compressionLevel = EJConstants.DEFAULT_COMPRESSION_LEVEL;
private String _adapterClass;
private String _host;
private boolean _isClient;
private boolean _isConnected;
/**
* Creates a new instance of ConnectionHeader.
*/
public ConnectionHeader(boolean isClient)
{
this( null, isClient );
}
/**
* Creates a new instance of ConnectionHeader. The host member will be set to the given host.
*
* @param host the host string in the form IP address : port, eg. 127.0.0.1:12577
*/
public ConnectionHeader(String host, boolean isClient)
{
setHost( host );
_header = new boolean[] { false, true, true, false, false, false, false, true };
this._isClient = isClient;
}
/**
* Creates a new instance of ConnectionHeader. The host member will be set to the given host and the channel member
* will be set to given channel.
*
* @param channel The channel to which this connection header applies.
* @param host the host string in the form IP address : port, eg. 127.0.0.1:12577
*/
public ConnectionHeader(SocketChannel channel, String host, boolean isClient)
{
this( host, isClient );
this._channel = channel;
}
/**
* Creates a new instance of ConnectionHeader. The host member will be set to the given host and the channel member
* will be set to given channel.
*
* @param channel The channel to which this connection header applies.
* @param host the host string in the form IP address : port, eg. 127.0.0.1:12577
* @param header The byte representing the settings used for:
* <ul>
* <li>has compression</li>
* <li>has non blocking io</li>
* <li>is connected</li>
* <li>use persistent connection</li>
* </ul>
*/
public ConnectionHeader(SocketChannel channel, String host, boolean isClient, byte header)
{
this( channel, host, isClient );
fromByte( header );
}
/**
* @param channel
* @param host
* @param isClient
* @param header
*/
private ConnectionHeader(String host, boolean isClient, final boolean[] header)
{
this( null, host, isClient );
this._header = header;
}
/**
* @return
*/
public boolean hasCompression()
{
return _header[0];
}
/**
* @param enable
*/
public void setCompression( boolean enable )
{
_header[0] = enable;
}
/**
* @param level
*/
public void setCompressionLevel( int level )
{
this._compressionLevel = level;
}
/**
* @return
*/
public int getCompressionLevel()
{
return this._compressionLevel;
}
/**
* @return
*/
public boolean hasNonBlockingReadWrite()
{
return _header[1];
}
/**
* @param enable
*/
public void setNonBlockingReadWrite( boolean enable )
{
_header[1] = enable;
}
/**
* @return
*/
public boolean isPersistent()
{
return _header[2];
}
/**
* @param enable
*/
public void setPersistent( boolean enable )
{
_header[2] = enable;
}
/**
* @return
*/
public boolean isHttp()
{
return _header[3];
}
/**
* @param http
*/
public void setHttp( boolean enable )
{
_header[3] = enable;
}
/**
* @return
*/
public boolean isDirect()
{
return _header[4];
}
/**
* @param enable
*/
public void setIsDirect( boolean enable )
{
_header[4] = enable;
}
public boolean isMixed()
{
return _header[5];
}
public void setIsMixed( boolean enable )
{
_header[5] = enable;
}
/**
* @return
*/
public boolean hasAdapter()
{
return _header[6];
}
/**
* @param name
*/
public void setAdapterName( String name )
{
this._header[6] = !(name == null || name.length() == 0);
this._adapterClass = name;
}
/**
* @return
*/
public boolean isHandshakeResponseAware()
{
return _header[7];
}
/**
* @param enable
*/
public void setIsHandshakeResponseAware( boolean enable )
{
_header[7] = enable;
}
/**
* @return
*/
public String getAdapterName()
{
return this._adapterClass;
}
/**
* @return
*/
public boolean isConnected()
{
return _isConnected;
}
/**
* @param enable
*/
public void setConnected( boolean enable )
{
_isConnected = enable;
}
/**
* @return
*/
public boolean hasWaitingBuffer()
{
return this._waitingBuffer != null; // && this._waitingBuffer.remaining() > 0;
}
/**
* @return
*/
public ByteBuffer getWaitingBuffer()
{
return this._waitingBuffer;
}
/**
* @param buf
*/
public void setWaitingBuffer( ByteBuffer buf )
{
this._waitingBuffer = buf;
}
/**
*
*/
public void releaseWaitingBuffer()
{
if ( this._waitingBuffer != null )
{
this._waitingBuffer.clear();
this._waitingBuffer = null;
}
}
/**
* @return
*/
public boolean hasAttachment()
{
return this._attachment != null;
}
/**
* @param attachment
*/
public void setAttachment( Object attachment )
{
this._attachment = attachment;
}
/**
* @param attachment
* @param attachmentInfo
*/
public void setAttachment( Object attachment, Object attachmentInfo )
{
this._attachment = attachment;
this._attachementInfo = attachmentInfo;
}
/**
* @return
*/
public boolean isClient()
{
return this._isClient;
}
/**
* @return
*/
public Object getAttachment()
{
return this._attachment;
}
/**
* @return
*/
public Object getAttachementInfo()
{
return this._attachementInfo;
}
/**
*
*/
public void releaseAttachment()
{
this._attachment = null;
this._attachementInfo = null;
}
/**
* @param channel
*/
public void setChannel( SocketChannel channel )
{
this._channel = channel;
}
/**
* @return
*/
public SocketChannel getChannel()
{
return this._channel;
}
/**
* @return
*/
public String getHost()
{
return this._host;
}
/**
* @param host
*/
public void setHost( String host )
{
this._host = host;
}
/**
* Returns a byte representation of the eight most important bits of the connection header
*
* @return a byte containing the eight most important bits of the connection header
*/
public byte toByte()
{
return IOUtil.bBitsToByte( _header );
}
/**
* sets the eight most important bits of the connection header based on the bits contained in the given byte
*
* @param header
*/
public void fromByte( byte header )
{
_header = IOUtil.byteToBBits( header );
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
public String toString()
{
StringBuffer buf = new StringBuffer();
buf.append( "/" );
buf.append( IOUtil.bBitsToSBits( _header ) );
if ( hasAdapter() )
{
buf.append( "/" );
buf.append( getAdapterName().replaceAll( "\\.", "/" ) );
}
return buf.toString();
}
/**
* Extracts the header settings from a formatted header string which follows the mask:
*
* <pre>
* header=12345678[&adapter=somepackage.someadapter]
* </pre>
*
* @param header
*/
public void fromString( String header ) throws ParseException
{
String[] parts = null;
if ( header.startsWith( "/" ) )
{
parts = header.substring( 1 ).split( "/", 2 );
}
else
{
parts = header.split( "/", 2 );
}
if ( parts.length >= 1 )
{
if ( parts[0].length() != 8 )
{
throw new ParseException( "Length of header bytes part must equals eight!", 0 );
}
_header = IOUtil.sBitsToBBits( parts[0] );
if ( parts.length > 0 )
{
setAdapterName( parts[1].replaceAll( "/", "." ) );
}
}
else
{
throw new ParseException( "Missing or wrong formatted header part", 0 );
}
}
/**
* Returns a {@link ByteBuffer} containg all transportable settings of the connection header
*
* @return a {@link ByteBuffer} representation of the connection header
*/
public ByteBuffer toByteBuffer()
{
byte[] adapterArr = null;
int length = 0;
if ( _adapterClass != null )
{
adapterArr = IOUtil.encodeToBytes( _adapterClass );
length = adapterArr.length;
}
// use indirect ByteBuffer because the buffer size will be just some
// bytes and allocation of indirect buffers is much faster
ByteBuffer buf = ByteBufferAllocator.allocate( 5 + length );
buf.put( toByte() );
buf.putInt( length );
if ( adapterArr != null )
{
buf.put( adapterArr );
}
buf.flip();
return buf;
}
/**
* Returns the byte array representation of this connection header
*
* @return an array of bytes containing all transportable settings in the connection header
*/
public byte[] toBytes()
{
ByteBuffer buf = toByteBuffer();
byte[] result = new byte[buf.limit()];
buf.get( result );
return result;
}
/**
* Returns a clean copy of the current instance containing all transportable settings of the connection header
*
* @return a clean copy
*/
public ConnectionHeader copy()
{
ConnectionHeader header = new ConnectionHeader( this._host, this._isClient, (boolean[]) this._header.clone() );
if ( hasAdapter() )
{
header.setAdapterName( this._adapterClass );
}
return header;
}
}