Package de.netseeker.ejoe.io

Source Code of de.netseeker.ejoe.io.DefaultChannel

/*********************************************************************
* DefaultChannel.java
* created on 01.04.2006 by netseeker
* $Source$
* $Date$
* $Revision$
*
* ====================================================================
*
*  Copyright 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 de.netseeker.ejoe.io framework.
* For more information on the author, please see
* <http://www.manskes.de/>.
*
*********************************************************************/

package de.netseeker.ejoe.io;

import java.io.IOException;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.text.ParseException;
import java.util.logging.Level;
import java.util.logging.Logger;

import de.netseeker.ejoe.ConnectionHeader;
import de.netseeker.ejoe.EJConstants;
import de.netseeker.ejoe.cache.ByteBufferAllocator;

/**
* @author netseeker
* @since 0.3.9.1
*/
class DefaultChannel extends DataChannel
{
    private static final Logger   logger      = Logger.getLogger( DefaultChannel.class.getName() );

    private static DefaultChannel dataChannel = new DefaultChannel();

    private DefaultChannel()
    {
        super();
    }

    /**
     * @return
     */
    public static DataChannel getInstance()
    {
        return dataChannel;
    }

    /**
     * @param header
     * @param sendBeforeReceive
     * @param timeout
     * @return
     * @throws IOException
     */
    public ConnectionHeader handshake( final ConnectionHeader header, SocketChannel channel, long timeout )
            throws IOException, ParseException
    {
        ConnectionHeader receiverHeader;
        ByteBuffer magicBuf = null;

        try
        {
            // shall we act as clientside and initialize the handshake?
            if ( header.isClient() )
            {
                // ok, get the control bits as well as the adapter name in a
                // ByteChannel
                ByteBuffer tmpBuf = header.toByteBuffer();
                magicBuf = ByteBufferAllocator.allocate( tmpBuf.remaining() + 4, false );
                magicBuf.putInt( EJConstants.EJOE_MAGIC_NUMBER );
                magicBuf.put( tmpBuf );
                magicBuf.flip();
                logger.log( Level.FINEST, "Sending Clientheader: " + header );
                semiBlockingWrite( channel, magicBuf, timeout );
                if ( magicBuf.hasRemaining() ) return null;
                // the server will just answer with the header byte
                magicBuf.clear();
                magicBuf.limit( 1 );
            }
            else
            {
                // expect to read the header byte and an integer (the size of
                // the forthcoming adapter name string)
                // the magic integer was already read in #handshake
                magicBuf = ByteBufferAllocator.allocate( 5, false );
            }

            // Server: read the client header byte + sizeof adapter name
            // Client: read just the server header byte
            semiBlockingRead( channel, magicBuf, timeout );
            if ( magicBuf.hasRemaining() )
            {
                throw new ParseException( "Header too long!", magicBuf.position() );
            }

            magicBuf.flip();
            InetAddress address = channel.socket().getInetAddress();
            int port = channel.socket().getPort();
            receiverHeader = new ConnectionHeader( channel, address.getHostAddress() + ':' + port, header.isClient(),
                                                   magicBuf.get() );

            // shall we act as server side and answer to the handshake request?
            if ( !header.isClient() )
            {
                if ( receiverHeader.hasAdapter() )
                {
                    // ok, we have already read the client header byte, now read
                    // the adapter name string
                    if ( readAdapterName( receiverHeader, magicBuf, timeout ) == -1 ) return null;
                }

                // answer to the client request and send our serverside
                // headerbyte
                magicBuf.clear();
                magicBuf.limit( 1 );
                magicBuf.put( header.toByte() );
                magicBuf.flip();
                semiBlockingWrite( channel, magicBuf, timeout );
                if ( magicBuf.hasRemaining() ) return null;
            }

            receiverHeader.setConnected( true );
        }
        finally
        {
            ByteBufferAllocator.collect( magicBuf );
        }

        return receiverHeader;
    }

    /**
     * @param receiverHeader
     * @param magicBuf
     * @param timeout
     * @return
     * @throws IOException
     */
    private int readAdapterName( ConnectionHeader receiverHeader, ByteBuffer magicBuf, long timeout )
            throws IOException
    {
        int length = magicBuf.getInt();
        if ( length > 1024 )
        {
            throw new IOException( "Invalid length for adapter name detected. The request is not well formatted!" );
        }

        // client requested a special adapter?
        if ( length > 0 )
        {
            // limit the existing ByteBuffer if it's big enough
            if ( length <= magicBuf.capacity() )
            {
                magicBuf.clear();
                magicBuf.limit( length );
            }
            // otherwise allocate a new one
            else
            {
                magicBuf = ByteBufferAllocator.allocate( length );
            }

            // read the adapter name
            semiBlockingRead( receiverHeader.getChannel(), magicBuf, timeout );
            if ( magicBuf.hasRemaining() ) return -1;
            magicBuf.flip();

            byte[] adapterArr = new byte[length];
            magicBuf.get( adapterArr );
            receiverHeader.setAdapterName( new String( adapterArr, EJConstants.EJOE_DEFAULT_CHARSET ) );
        }

        return length;
    }

    /*
     * (non-Javadoc)
     *
     * @see de.netseeker.ejoe.io.DataChannel#readHeader(long)
     */
    public int readHeader( ConnectionHeader header, long timeout ) throws IOException
    {
        ByteBuffer headerBuf = ByteBufferAllocator.allocate( EJConstants.NIO_HEADER_SIZE, false );
        int read = 0;
        SocketChannel channel = header.getChannel();

        try
        {
            nonBlockingRead( channel, headerBuf );
            headerBuf.flip();
            read = headerBuf.getInt();
            IOUtil.setReceiveBufferSize( channel.socket(), read );
        }
        catch ( IncompleteIOException ioe )
        {
            logger.log( Level.FINEST, "Incomplete header read detected, registering for read again." );
            // ioe.setIOBuffer(null);
            // ioe.setSelectionInterest(SelectionKey.OP_READ);
            throw new IncompleteIOException( null, SelectionKey.OP_READ );
        }
        finally
        {
            ByteBufferAllocator.collect( headerBuf );
        }

        return read;
    }

    /*
     * (non-Javadoc)
     *
     * @see de.netseeker.ejoe.io.DataChannel#writeHeader(de.netseeker.ejoe.ConnectionHeader, java.nio.ByteBuffer, long)
     */
    public void writeHeader( ConnectionHeader header, ByteBuffer buffer, long timeout ) throws IOException
    {
        ByteBuffer headerBuf = ByteBufferAllocator.allocate( EJConstants.NIO_HEADER_SIZE, false );
        SocketChannel channel = header.getChannel();

        int length = buffer != null ? buffer.remaining() : 0;
        headerBuf.putInt( length );
        headerBuf.flip();
        try
        {
            nonBlockingWrite( channel, headerBuf );
            IOUtil.setSendBufferSize( channel.socket(), length );
        }
        catch ( IncompleteIOException ioe )
        {
            logger.log( Level.FINEST, "Incomplete header write detected, registering for write again." );
            // ioe.setIOBuffer(null);
            throw new IncompleteIOException( null, SelectionKey.OP_WRITE );
        }
        finally
        {
            ByteBufferAllocator.collect( headerBuf );
        }
    }
}
TOP

Related Classes of de.netseeker.ejoe.io.DefaultChannel

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.