Package de.netseeker.ejoe.core

Source Code of de.netseeker.ejoe.core.ConnectionReader

/*********************************************************************
* ConnectionReader.java
* created on 05.03.2005 by netseeker
* $Source: /cvsroot/ejoe/EJOE/src/de/netseeker/ejoe/core/ConnectionReader.java,v $
* $Date: 2007/11/17 10:57:03 $
* $Revision: 1.7 $
*
* ====================================================================
*
*  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.core;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.NonReadableChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.rmi.RemoteException;
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.ServerInfo;
import de.netseeker.ejoe.adapter.AdapterFactory;
import de.netseeker.ejoe.adapter.SerializeAdapter;
import de.netseeker.ejoe.cache.ByteBufferAllocator;
import de.netseeker.ejoe.handler.ServerHandler;
import de.netseeker.ejoe.http.HttpResponse;
import de.netseeker.ejoe.io.ByteBufferInputStream;
import de.netseeker.ejoe.io.ChannelInputStream;
import de.netseeker.ejoe.io.DataChannel;
import de.netseeker.ejoe.io.IOUtil;
import de.netseeker.ejoe.io.IncompleteIOException;

/**
* ConnectionReader targets three jobs:
* <ul>
* <li>Read (partial) data from a established client connection</li>
* <li>Invoke the server handler to process the received data</li>
* <li>Hand over the response to the ConnectionProcessor for further processing (sending to the client)</li>
* </ul>
*
* @author netseeker
* @since 0.3.0
*/
public class ConnectionReader implements Runnable
{
    private static final Logger    log = Logger.getLogger( ConnectionReader.class.getName() );

    private final ChannelRegistrar _registrar;

    private ConnectionHeader       _senderInfo;

    private ServerInfo             _receiverInfo;

    /**
     * Creates a new instance of ConnectionReader
     *
     * @param channel a socket channel ready for reading and writing
     * @param adapter (de)serialize adapter
     * @param handler the working horse handling the transported input object
     */
    public ConnectionReader(final ChannelRegistrar registrar, ServerInfo receiverInfo, ConnectionHeader senderInfo)
    {
        this._registrar = registrar;
        this._receiverInfo = receiverInfo;
        this._senderInfo = senderInfo;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Runnable#run()
     */
    public void run()
    {
        Object request = null;
        SocketChannel channel = this._senderInfo.getChannel();

        try
        {
            if ( !this._senderInfo.isConnected() )
            {
                log.log( Level.FINEST, "Handshaking client..." );
                // handshake the client, get infos about compression...
                this._senderInfo = DataChannel.getInstance().handshake( this._receiverInfo, channel,
                                                                        EJConstants.EJOE_CONNECTION_TIMEOUT );
            }

            if ( this._senderInfo != null )
            {
                log.log( Level.FINEST, "Remote requested " + this._senderInfo.getAdapterName() );

                SerializeAdapter adapter = AdapterFactory.createAdapter( this._senderInfo.getAdapterName() );

                if ( this._receiverInfo.hasNonBlockingReadWrite() )
                {
                    log.log( Level.FINEST, "Going to read client request on a non blocking socket..." );
                    request = read( adapter );
                }
                else
                {
                    log.log( Level.FINEST, "Going to read client request on a blocking socket..." );
                    request = readBlocked( adapter );
                }

                this._senderInfo.releaseAttachment();
                this._senderInfo.releaseWaitingBuffer();

                log.log( Level.FINE, "Client request read." );

                Object result = handleObject( request );

                if ( this._registrar.isValid() )
                {
                    this._senderInfo.setAttachment( result );
                    this._registrar.register( this._senderInfo, SelectionKey.OP_WRITE );
                }
            }
            else
            {
                log.log( Level.WARNING, "Connection timeout reached while waiting for Handshake complete. "
                        + "Closing connection." );
                shutdownConnection( channel );
            }
        }
        // partial read detected, registering for read again
        catch ( IncompleteIOException ioe )
        {
            this._senderInfo.setWaitingBuffer( ioe.getIOBuffer() );
            this._registrar.register( this._senderInfo, SelectionKey.OP_READ );
        }
        catch ( EOFException eof )
        {
            log.log( Level.FINEST, "EOF received while reading client data " + "- closing connection." );
            shutdownConnection( channel );
        }
        catch ( ParseException pe )
        {
            log.log( Level.WARNING, "Unparseable connection header detected!", pe );
            if ( !this._senderInfo.isHttp() )
            {
                this._senderInfo.setAttachment( new RemoteException( "Unparseable connection header!", pe ) );
            }
            else
            {
                this._senderInfo.setAttachment( new RemoteException( "Unparseable connection header!", pe ),
                                                HttpResponse.HTTP_BAD_REQUEST );
            }

            this._registrar.register( this._senderInfo, SelectionKey.OP_WRITE );
        }
        catch ( SocketTimeoutException ste )
        {
            log.log( Level.FINE, "Timeout occured while waiting for client data!", ste );
            shutdownConnection( channel );
        }
        catch ( RemoteException re )
        {
            if ( !this._senderInfo.isHttp() )
            {
                this._senderInfo.setAttachment( re );
            }
            else
            {
                this._senderInfo.setAttachment( re, HttpResponse.HTTP_INTERNAL_SERVER_ERROR );
            }
            this._registrar.register( this._senderInfo, SelectionKey.OP_WRITE );
        }
        // the client did something strange with the channel, probably the
        // client is just too slow for
        catch ( NonReadableChannelException e )
        {
            log.log( Level.INFO, "Connection probably closed by client." );
            shutdownConnection( channel );
        }
        // the client did close the connection, or the connection was
        // disconnected
        catch ( ClosedChannelException cce )
        {
            log.log( Level.INFO, "Connection closed by client." );
            shutdownConnection( channel );
        }
        catch ( Throwable e )
        {
            if ( !channel.isBlocking() && channel.isConnected() && channel.isOpen() )
            {
                // something goes completely wrong!
                log.log( Level.WARNING, "!!! Exception while reading client data !!! "
                        + "Probably the client just closed the connection but it could also be a serious failure.", e );
            }
            else
            {
                // seems like a follow-up exception due to disconnect
                log.log( Level.INFO, "Connection propably closed by client.", e );
            }

            shutdownConnection( channel );
        }
    }

    private void shutdownConnection( SocketChannel channel )
    {
        IOUtil.closeQuiet( channel );
        this._senderInfo = null;
    }

    /**
     * Reads and deserialize a request object from a socket channel in non-blocking mode
     *
     * @param adapter the deserialization adapter to use
     * @return the deserialized request object
     * @throws IOException
     * @throws UnsupportedOperationException
     */
    private Object read( SerializeAdapter adapter ) throws IOException
    {
        ByteBufferInputStream in = null;
        ByteBuffer dataBuf = null;
        Object result = null;
        DataChannel dataChannel = DataChannel.getInstance( this._senderInfo );

        try
        {
            if ( !this._senderInfo.hasWaitingBuffer() )
            {
                int length = dataChannel.readHeader( this._senderInfo, EJConstants.EJOE_CONNECTION_TIMEOUT );

                // maybe the DataChannel signals that it has already read
                // partial data
                if ( this._senderInfo.hasWaitingBuffer() )
                {
                    dataBuf = this._senderInfo.getWaitingBuffer();
                }
                else
                {
                    dataBuf = ByteBufferAllocator.allocate( length );
                }

                log.log( Level.FINE, "Going to read client request with length: " + length );
            }
            else
            {
                dataBuf = this._senderInfo.getWaitingBuffer();
            }

            if ( dataBuf.hasRemaining() )
            {
                DataChannel.nonBlockingRead( this._senderInfo.getChannel(), dataBuf );
            }

            dataBuf.flip();

            if ( log.isLoggable( Level.FINE ) )
            {
                byte[] tmp = new byte[dataBuf.remaining()];
                dataBuf.get( tmp );
                dataBuf.position( 0 );
                log.log( Level.FINE, "Client request read:\n" + new String( tmp, EJConstants.EJOE_DEFAULT_CHARSET ) );
            }

            if ( dataBuf.hasRemaining() )
            {
                try
                {
                    // usual way: deserialize using a adapter
                    if ( !this._senderInfo.isDirect() )
                    {
                        dataBuf = dataChannel.decode( dataBuf );
                        in = new ByteBufferInputStream( dataBuf );
                        result = deserialize( adapter, in );
                    }
                    // direct mode: don't deserialize, just copy and return the read
                    // ByteBuffer
                    else
                    {
                        // copy the bytebuffer
                        ByteBuffer tmp = ByteBufferAllocator.allocate( dataBuf.remaining() );
                        tmp.put( dataBuf );
                        tmp.flip();
                        result = dataChannel.decode( tmp );
                    }
                }
                catch ( Throwable t )
                {
                    throw new RemoteException( "Error while preprocessing request!", t );
                }
                finally
                {
                    this._senderInfo.releaseWaitingBuffer();
                }
            }
        }
        finally
        {
            IOUtil.closeQuiet( in );
        }

        return result;
    }

    /**
     * @return the _receiverInfo
     */
    public ServerInfo getReceiverInfo()
    {
        return _receiverInfo;
    }

    /**
     * @return the _senderInfo
     */
    public ConnectionHeader getSenderInfo()
    {
        return _senderInfo;
    }

    /**
     * @return the _registrar
     */
    public ChannelRegistrar getRegistrar()
    {
        return _registrar;
    }

    /**
     * Reads and deserialize a request object from a socket channel in blocking mode
     *
     * @return
     * @throws IOException
     * @throws UnsupportedOperationException
     */
    private Object readBlocked( SerializeAdapter adapter ) throws Exception
    {
        // usual way: deserialize using a adapter
        if ( !this._senderInfo.isDirect() )
        {
            return deserialize( adapter, new ChannelInputStream( Channels
                    .newInputStream( this._senderInfo.getChannel() ) ) );
        }
        // direct mode: don't deserialize, just copy and return the read
        // ByteBuffer
        else
        {
            return IOUtil
                    .readDirect( new ChannelInputStream( Channels.newInputStream( this._senderInfo.getChannel() ) ) );
        }
    }

    /**
     * @param clientInfo
     * @param in
     * @return
     * @throws IOException
     */
    protected Object deserialize( SerializeAdapter adapter, InputStream in ) throws Exception
    {
        boolean compressed = !this._senderInfo.isHttp() && this._receiverInfo.hasCompression()
                && this._senderInfo.hasCompression();

        return IOUtil.adapterDeserialize( adapter, in, compressed );
    }

    /**
     * @param obj
     * @return
     */
    protected Object handleObject( Object obj ) throws RemoteException
    {
        Object result = null;
        ServerHandler handler = this._receiverInfo.getHandler();

        try
        {
            result = handler.handle( obj );
        }
        catch ( Throwable e )
        {
            log.log( Level.WARNING, "Exception in ServerHandler " + this._receiverInfo.getHandler() + " occured.", e );
            throw new RemoteException( "Server failed to proceed your request!", e );
        }

        return result;
    }
}
TOP

Related Classes of de.netseeker.ejoe.core.ConnectionReader

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.