Package de.netseeker.ejoe.core

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

/*********************************************************************
* ConnectionWriter.java
* created on 05.03.2005 by netseeker
* $Source: /cvsroot/ejoe/EJOE/src/de/netseeker/ejoe/core/ConnectionWriter.java,v $
* $Date: 2007/03/25 15:03:20 $
* $Revision: 1.4 $
*
* ====================================================================
*
*  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.IOException;
import java.io.OutputStream;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.WritableByteChannel;
import java.rmi.RemoteException;
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.io.ByteBufferOutputStream;
import de.netseeker.ejoe.io.DataChannel;
import de.netseeker.ejoe.io.IOUtil;
import de.netseeker.ejoe.io.IncompleteIOException;

/**
* ConnectionWriter serializes a server answer and sends it through the established connection.
*
* @author netseeker
* @since 0.3.1
*/
final class ConnectionWriter implements Runnable
{
    private static final Logger    log = Logger.getLogger( ConnectionReader.class.getName() );

    private final ChannelRegistrar _registrar;

    private final ConnectionHeader _senderInfo;

    private ConnectionHeader       _receiverInfo;

    /**
     * Creates a new instance of ConnectionWriter
     *
     * @param serverInfo
     * @param clientInfo
     * @param channel
     * @param adapter
     * @param attachment
     */
    public ConnectionWriter(final ChannelRegistrar registrar, final ServerInfo senderInfo, ConnectionHeader receiverInfo)
    {
        this._senderInfo = senderInfo;
        this._receiverInfo = receiverInfo;
        this._registrar = registrar;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Runnable#run()
     */
    public void run()
    {
        try
        {
            if ( this._senderInfo.hasNonBlockingReadWrite() )
            {
                write();
            }
            else
            {
                writeBlocked();
            }

            this._receiverInfo.releaseWaitingBuffer();
            this._receiverInfo.releaseAttachment();

            if ( !this._senderInfo.isPersistent() || !this._receiverInfo.isPersistent() )
            {
                log.log( Level.FINEST, "Non-persistent connection detected, closing connection..." );
                shutdownConnection();
            }
            else if ( this._registrar.isValid() )
            {
                log.log( Level.FINEST,
                         "Persistent connection detected, registering connections for further read events..." );
                this._registrar.register( this._receiverInfo, SelectionKey.OP_READ );
            }
        }
        catch ( ClosedChannelException cce )
        {
            log.log( Level.INFO, "Channel closed by client.", cce );
            shutdownConnection();
        }
        catch ( IncompleteIOException ioe )
        {
            if ( this._registrar.isValid() )
            {
                this._receiverInfo.setWaitingBuffer( ioe.getIOBuffer() );
                this._registrar.register( this._receiverInfo, SelectionKey.OP_WRITE );
            }
        }
        catch ( SocketTimeoutException ste )
        {
            log.log( Level.FINE, "Timeout occured while sending data!", ste );
            shutdownConnection();
        }
        catch ( RemoteException re )
        {
            this._receiverInfo.releaseWaitingBuffer();
            this._receiverInfo.setAttachment( re );
            this._registrar.register( this._receiverInfo, SelectionKey.OP_WRITE );
        }
        catch ( IOException e )
        {
            log.log( Level.WARNING,
                     "!!! IOException while sending data to client propably the client just closed the connection!!!",
                     e );
            shutdownConnection();
        }
        catch ( Throwable e )
        {
            // something goes completely wrong!
            log.log( Level.SEVERE, "!!! Unknown exception while sending data !!!", e );
            shutdownConnection();
        }
    }

    private void shutdownConnection()
    {
        IOUtil.closeQuiet( this._receiverInfo.getChannel() );
        this._receiverInfo = null;
    }

    /**
     * @throws IOException
     */
    private void write() throws IOException
    {
        ByteBufferOutputStream out = null;
        ByteBuffer dataBuf = null;
        SerializeAdapter adapter = this._receiverInfo.getAdapterName() != null ? AdapterFactory
                .createAdapter( this._receiverInfo.getAdapterName() ) : null;
        WritableByteChannel channel = this._receiverInfo.getChannel();
        DataChannel dataChannel = DataChannel.getInstance( this._receiverInfo );

        try
        {
            if ( this._receiverInfo.hasAttachment() )
            {
                // usual way: convert the serialized object to a ByteBuffer
                if ( !this._receiverInfo.isDirect() )
                {
                    out = new ByteBufferOutputStream();
                    serialize( adapter, out );
                    dataBuf = out.getBackingBuffer();
                }
                // direct mode: just use the attachement
                else
                {
                    ByteBuffer tmp = (ByteBuffer) this._receiverInfo.getAttachment();
                    if ( tmp.position() > 0 )
                    {
                        tmp.flip();
                    }
                    dataBuf = tmp.duplicate();
                    ByteBufferAllocator.collect( tmp );
                }

                if ( dataBuf.position() > 0 )
                {
                    dataBuf.flip();
                }

                this._receiverInfo.releaseAttachment();

                dataChannel.writeHeader( this._receiverInfo, dataBuf, EJConstants.EJOE_CONNECTION_TIMEOUT );

                if ( log.isLoggable( Level.FINE ) )
                {
                    byte[] tmp = new byte[dataBuf.remaining()];
                    dataBuf.get( tmp );
                    dataBuf.position( 0 );
                    log.log( Level.FINE, "Going to send server response:\n"
                            + new String( tmp, EJConstants.EJOE_DEFAULT_CHARSET ) );
                }

                dataChannel.nonBlockingWrite( channel, dataBuf );
            }
            else if ( this._receiverInfo.hasWaitingBuffer() )
            {
                dataBuf = this._receiverInfo.getWaitingBuffer();
                dataChannel.nonBlockingWrite( channel, dataBuf );
            }
            // seems that we have no answer for the client
            else
            {
                dataChannel.writeHeader( this._receiverInfo, null, EJConstants.EJOE_CONNECTION_TIMEOUT );
            }

            log.log( Level.FINE, "Server response sent." );
        }
        finally
        {
            IOUtil.closeQuiet( out );
        }
    }

    /**
     * @throws IOException
     */
    private void writeBlocked() throws IOException
    {
        SerializeAdapter adapter = this._receiverInfo.getAdapterName() != null ? AdapterFactory
                .createAdapter( this._receiverInfo.getAdapterName() ) : null;

        log.log( Level.FINE, "Going to send server response... " );
        if ( !this._receiverInfo.isDirect() )
        {
            serialize( adapter, Channels.newOutputStream( this._receiverInfo.getChannel() ) );
        }
        // direct mode: just use the attachement
        else
        {
            ByteBuffer tmp = (ByteBuffer) this._receiverInfo.getAttachment();
            if ( tmp.position() > 0 )
            {
                tmp.flip();
            }
            IOUtil.writeDirect( Channels.newOutputStream( this._receiverInfo.getChannel() ), tmp );
        }

        log.log( Level.FINE, "Server response sent." );
    }

    /**
     * @param out
     * @throws IOException
     */
    private void serialize( SerializeAdapter adapter, OutputStream out ) throws IOException
    {
        boolean compressed = this._senderInfo.hasCompression() && this._receiverInfo.hasCompression();
        try
        {
            IOUtil.adapterSerialize( adapter, out, this._receiverInfo.getAttachment(), compressed, this._senderInfo
                    .getCompressionLevel() );
        }
        catch ( IOException ioe )
        {
            throw ioe;
        }
        catch ( Throwable t )
        {
            throw new RemoteException( "The server encounteres an error while serializing the server response!", t );
        }
    }
}
TOP

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

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.