Package de.netseeker.ejoe

Source Code of de.netseeker.ejoe.EJClient

/*********************************************************************
* EJClient.java
* created on 08.08.2004 by netseeker
* $Source: /cvsroot/ejoe/EJOE/src/de/netseeker/ejoe/EJClient.java,v $
* $Date: 2007/11/17 10:59:41 $
* $Revision: 1.100 $
*
* ====================================================================
*
*  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.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.rmi.RemoteException;
import java.text.ParseException;
import java.util.Iterator;
import java.util.Properties;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

import de.netseeker.ejoe.adapter.AdapterFactory;
import de.netseeker.ejoe.adapter.SerializeAdapter;
import de.netseeker.ejoe.cache.ByteBufferAllocator;
import de.netseeker.ejoe.core.InJvmProcessor;
import de.netseeker.ejoe.core.InJvmProcessorFactory;
import de.netseeker.ejoe.io.ByteBufferInputStream;
import de.netseeker.ejoe.io.ByteBufferOutputStream;
import de.netseeker.ejoe.io.ChannelInputStream;
import de.netseeker.ejoe.io.DataChannel;
import de.netseeker.ejoe.io.IOUtil;
import de.netseeker.ejoe.io.IncompleteIOException;

/**
* This is the client component of EJOE. You have to use this component to send and retrieve data to/from a EJOE server.
* <p>
* Basic usage:
*
* <pre>
*                             EJClient client = new EJClient(&quot;127.0.0.1&quot;, 12577); //you could also use EJConstants.EJOE_PORT
*                             Object response = client.execute(&quot;Hello World&quot;);
*                             ...
* </pre>
*
* </p>
* <p>
* Usage with persistent connections:
*
* <pre>
*                             EJClient client = new EJClient(&quot;127.0.0.1&quot;, 12577); //you could also use EJConstants.EJOE_PORT
*                             client.enablePersistentConnection(true); //request a persistent connection to the server
*                             Object response = client.execute(&quot;Hello World&quot;);
*                             ...
*                             reponse = client.execute(&quot;Bye World&quot;);
*                             //close the connection
*                             client.close();
*                             ...
* </pre>
*
* </p>
* <p>
* Usage with In-JVM EJServer:
*
* <pre>
*                             EJClient client = new EJClient(&quot;127.0.0.1&quot;, 12577); //you could also use EJConstants.EJOE_PORT
*                             client.setInJvm(true); //enable In-JVM mode, no socket connections will be used
*                             Object response = client.execute(&quot;Hello World&quot;);
*                             ...
*                             reponse = client.execute(&quot;Bye World&quot;);
*                             ...
* </pre>
*
* </p>
*
* @author netseeker aka Michael Manske
* @since 0.3.0
*/
public class EJClient implements Serializable
{
    private static final long             serialVersionUID   = 1L;

    private transient static final Logger log                = Logger.getLogger( EJClient.class.getName() );

    private SerializeAdapter              _adapter;

    private String                        _host              = "127.0.0.1";

    private int                           _port              = EJConstants.EJOE_PORT;

    private int                           _connectionTimeout = EJConstants.EJOE_CONNECTION_TIMEOUT;

    private boolean                       _inJVM             = false;

    private final ConnectionHeader        _clientInfo        = new ConnectionHeader( true );

    private transient ConnectionHeader    _serverInfo;

    private transient SocketChannel       _channel;

    private transient Selector            _selector;

    private Object                        _mutex             = new Object();

    private static Random                 _idGenerator       = new Random();

    /**
     * Creates an instance of the EJOE client pre-configured with settings from the global ejoe.properties file. You
     * MUST provide such an property file on the classpath to use this constructor.
     */
    public EJClient()
    {
        Properties props = new Properties();
        try
        {
            props.load( EJClient.class.getResourceAsStream( "/ejoe.properties" ) );
            loadProperties( props );
        }
        catch ( IOException e )
        {
            log.log( Level.SEVERE, "ejoe.properties could not be read! "
                    + "Make sure you have placed a valid properties file in your classpath.", e );
            throw new RuntimeException( e );
        }
    }

    /**
     * Creates an instance of the EJOE client pre-configured with settings from a global properties file.
     *
     * @param pathToConfigFile path to the properties file
     */
    public EJClient(String pathToConfigFile)
    {
        Properties props = new Properties();
        FileInputStream fis = null;
        try
        {
            fis = new FileInputStream( pathToConfigFile );
            props.load( fis );
            loadProperties( props );
        }
        catch ( IOException e )
        {
            log.log( Level.SEVERE, "ejoe.properties could not be read! "
                    + "Make sure you have placed a valid properties file in your classpath.", e );
            throw new RuntimeException( e );
        }
        finally
        {
            IOUtil.closeQuiet( fis );
        }
    }

    /**
     * Creates an instance of the EJOE client pre-configured with settings from the given properties store.
     *
     * @param properties properties store containing EJClient settings
     */
    public EJClient(Properties properties)
    {
        loadProperties( properties );
    }

    /**
     * Creates an instance of the EJOE client preconfigured to use an instance of
     * de.netseeker.ejoe.adapter.ObjectStreamAdapter for (de)serializing.
     *
     * @param host address (dns name or ip address) of the EJOE server
     * @param port port which the EJOE server listens to
     */
    public EJClient(String host, int port)
    {
        this._host = host;
        this._port = port;
        this._clientInfo.setHost( _host + ':' + _port );
        this._adapter = AdapterFactory.createAdapter( EJConstants.EJOE_DEFAULT_ADAPTER.getName() );
        this._clientInfo.setAdapterName( EJConstants.EJOE_DEFAULT_ADAPTER.getName() );
    }

    /**
     * Creates an instance of the EJOE client.
     *
     * @param host address (dns name or ip address) of the EJOE server
     * @param port port which the EJOE server listens to
     * @param adapter the adapter used for (de)serializing input paramter objects for the server and the return values
     */
    public EJClient(String host, int port, final SerializeAdapter adapter)
    {
        this._host = host;
        this._port = port;
        this._clientInfo.setHost( _host + ':' + _port );
        this._adapter = adapter;
        this._clientInfo.setAdapterName( adapter.getClass().getName() );
    }

    /**
     * Creates an instance of the EJOE client.
     *
     * @param host address (dns name or ip address) of the EJOE server
     * @param port port which the EJOE server listens to
     * @param adapter the adapter used for (de)serializing input paramter objects for the server and the return values
     * @param isPersistent whether EJClient should use a persistent connection to the server or not
     * @param isHttp whether EJClient should wrap requests in HTTP headers or not (lets EJClient socket data look like a
     *            HTTP 1.1 browser communication)
     */
    public EJClient(String host, int port, final SerializeAdapter adapter, boolean isPersistent, boolean isHttp,
                    boolean useCompression)
    {
        this( host, port, adapter );
        enablePersistentConnection( isPersistent );
        enableCompression( useCompression );
        enableHttpPackaging( isHttp );
    }

    /**
     * Sets the connection timeout used when waiting for server responses. A value of zero (the default) blocks
     * indefinitely.
     *
     * @param timeout the new timeout in milliseconds
     */
    public synchronized void setConnectionTimeout( int timeout )
    {
        this._connectionTimeout = timeout;
    }

    /**
     * Tells this client to use compression (if supported by the server) or not.
     *
     * @param enable
     */
    public synchronized void enableCompression( boolean enable )
    {
        this._clientInfo.setCompression( enable );
    }

    /**
     * Tells this client to use compression (if supported by the server) with the given compression level.
     *
     * @param compressionLevel the level of compression to use, must be in range of 0-9
     */
    public synchronized void enableCompression( int compressionLevel )
    {
        this._clientInfo.setCompression( true );
        this._clientInfo.setCompressionLevel( compressionLevel );
    }

    /**
     * Enables/disables usage of a persistent connection. If persistent connection is disabled a new connection will be
     * used for each request.
     *
     * @param enable
     */
    public synchronized void enablePersistentConnection( boolean enable )
    {
        this._clientInfo.setPersistent( enable );
    }

    /**
     * Enables/disables usage of a persistent connection. If persistent connection is disabled a new connection will be
     * used for each request.
     *
     * @param enable
     */
    public synchronized void enableHttpPackaging( boolean enable )
    {
        this._clientInfo.setHttp( enable );
    }

    /**
     * @return the _inJVM
     */
    public boolean isInJVM()
    {
        return _inJVM;
    }

    /**
     * @param injvm the _inJVM to set
     */
    public synchronized void setInJVM( boolean injvm )
    {
        enableHttpPackaging( false );
        _inJVM = injvm;
    }

    /**
     * Controls the used Adapter Strategy:
     * <ul>
     * <li>ADAPTER_STRATEGY_DEFAULT: both, client and server, will serialize and deserialize objects. The client will
     * return a deserialized object</li>
     * <li>ADAPTER_STRATEGY_DIRECT: both, client and server, will NOT serialize and deserialize objects. The client
     * will expect ByteBuffers as arguments and the ServerHandler will get ByteBuffers too. The client will return
     * ByteBuffers to the caller.</li>
     * <li>ADAPTER_STRATEGY_MIXED: both, client and server, will exchange serialized objects. The ServerHandler will
     * get deserialized objects too handle. The client will return a ByteBuffer instead of derserialized objects to the
     * caller.</li>
     * </ul>
     *
     * @param adapterStrategy
     */
    public synchronized void setAdapterStrategy( int adapterStrategy )
    {
        switch ( adapterStrategy )
        {
            case EJConstants.ADAPTER_STRATEGY_DEFAULT:
                this._clientInfo.setIsDirect( false );
                this._clientInfo.setIsMixed( false );
                break;
            case EJConstants.ADAPTER_STRATEGY_DIRECT:
                this._clientInfo.setIsDirect( true );
                this._clientInfo.setIsMixed( false );
                break;
            case EJConstants.ADAPTER_STRATEGY_MIXED:
                this._clientInfo.setIsDirect( false );
                this._clientInfo.setIsMixed( true );
                break;
            default:
                throw new IllegalArgumentException( "Unknown Adapter Strategy: " + adapterStrategy );
        }
    }

    /**
     * Enables remote classloading on the default remote port.
     */
    public synchronized void enableRemoteClassloading()
    {
        if ( Thread.currentThread().getContextClassLoader() instanceof EJClassLoader )
            throw new IllegalStateException( "Remote classloading already enabled!" );

        initClassLoader();
    }

    /**
     * Getter method to get direct access to the underlying ConnectionHeader (as required by the WSIF port
     * implementation)
     *
     * @return the used client configuration represented as ConnectionHeader
     */
    public ConnectionHeader getConnectionHeader()
    {
        return _clientInfo;
    }

    /**
     * Main entry point for client tier implementations. Handles all remote server calls... This method is threadsafe,
     * ensuring that only one request is processed at a time.
     *
     * @param obj input objects for the EJOE Server
     * @return the object(s) returned by the EJOE server
     */
    public Object execute( final Object obj ) throws IOException
    {
        synchronized ( _mutex )
        {
            Object result = null;

            // ensure that only one request is in process at one time without
            // blocking the whole client
            ConnectionHeader clientInfo = this._clientInfo.copy();

            if ( _inJVM )
            {
                InJvmProcessor processor = InJvmProcessorFactory.createProcessor( clientInfo );

                try
                {
                    result = processor.process( obj );
                }
                catch ( Exception e )
                {
                    result = e;
                }
            }
            else
            {
                SelectionKey selKey = null;

                boolean error = false;

                try
                {
                    if ( this._channel == null || !this._channel.isOpen() )
                    {
                        log.log( Level.FINEST, "opening new connection" );
                        openSocketChannel();
                        _channel.register( this._selector, SelectionKey.OP_CONNECT | SelectionKey.OP_WRITE );
                    }
                    else
                    {
                        log.log( Level.FINEST, "reusing persistent connection" );
                        selKey = _channel.keyFor( this._selector );
                        // register this channel again
                        selKey = _channel.register( this._selector, 0 );
                        selKey.interestOps( SelectionKey.OP_WRITE );
                    }

                    Iterator it = null;

                    while ( this._selector.select( _connectionTimeout ) > 0 )
                    {
                        it = this._selector.selectedKeys().iterator();

                        while ( it.hasNext() )
                        {
                            selKey = (SelectionKey) it.next();
                            it.remove();

                            if ( !selKey.isValid() )
                            {
                                continue;
                            }

                            try
                            {
                                if ( selKey.isConnectable() && _channel.isConnectionPending() )
                                {
                                    if ( !_channel.finishConnect() )
                                    {
                                        continue;
                                    }

                                    clientInfo.setChannel( _channel );

                                    if ( log.isLoggable( Level.INFO ) )
                                    {
                                        log.log( Level.INFO, "Connection established to "
                                                + _channel.socket().getRemoteSocketAddress() );
                                    }

                                    _channel.register( this._selector, SelectionKey.OP_WRITE );
                                }
                                if ( selKey.isWritable() )
                                {
                                    if ( _serverInfo == null )
                                    {
                                        log.log( Level.FINEST, "Handshaking server..." );
                                        _serverInfo = DataChannel.getInstance().handshake( clientInfo, _channel,
                                                                                           _connectionTimeout );
                                        if ( _serverInfo == null )
                                        {
                                            throw new IOException( "Connection timeout (" + _connectionTimeout
                                                    + "ms) reached while waiting for handshake completing." );
                                        }
                                        else if ( clientInfo.isHttp() && !_serverInfo.isHttp() )
                                        {
                                            throw new UnsupportedOperationException(
                                                                                     "The server does not permit usage of HTTP packaging!" );
                                        }
                                    }

                                    if ( _serverInfo.hasNonBlockingReadWrite() )
                                    {
                                        send( _serverInfo, clientInfo, selKey, obj );
                                        _channel.register( this._selector, SelectionKey.OP_READ );
                                    }
                                    else
                                    {
                                        selKey.cancel();
                                        _channel.configureBlocking( true );
                                        result = processBlocked( clientInfo, obj );

                                        if ( result != null && (result instanceof Throwable) )
                                        {
                                            if ( !(result instanceof RemoteException) )
                                            {
                                                throw new RemoteException(
                                                                           "The server did return an Exception while handling your request!",
                                                                           (Throwable) result );
                                            }
                                            else
                                            {
                                                throw (RemoteException) result;
                                            }
                                        }

                                        return result;
                                    }
                                }
                                else if ( selKey.isReadable() )
                                {
                                    result = receive( clientInfo );
                                    selKey.cancel();

                                    if ( result != null && (result instanceof Throwable) )
                                    {
                                        if ( !(result instanceof RemoteException) )
                                        {
                                            throw new RemoteException(
                                                                       "The server did return an Exception while handling your request!",
                                                                       (Throwable) result );
                                        }
                                        else
                                        {
                                            throw (RemoteException) result;
                                        }
                                    }

                                    return result;
                                }
                            }
                            catch ( IncompleteIOException ioe )
                            {
                                // selKey.cancel();
                                if ( ioe.getIOBuffer() != null )
                                {
                                    clientInfo.setWaitingBuffer( ioe.getIOBuffer() );
                                    _channel.register( this._selector, ioe.getSelectionInterest() );
                                }
                                else
                                {
                                    // rethrow origin exception to inform the caller
                                    // without the hassle of nested exceptions
                                    throw ioe;
                                }
                            }
                            catch ( ParseException e )
                            {
                                error = true;
                                throw new IOException(
                                                       "Connection closed due to unparseable connection header! Exact message was: "
                                                               + e.getMessage() );
                            }
                            catch ( IOException e )
                            {
                                if ( !(e instanceof RemoteException) )
                                {
                                    error = true;
                                }
                                // rethrow origin exception to inform the caller
                                // without the hassle of nested exceptions
                                throw e;
                            }
                        }
                    }
                }
                finally
                {
                    selKey.cancel();
                    // beeing a little bit paranoid is sometimes better
                    clientInfo.releaseAttachment();
                    clientInfo.releaseWaitingBuffer();
                    if ( _serverInfo != null )
                    {
                        _serverInfo.releaseAttachment();
                        _serverInfo.releaseWaitingBuffer();
                    }

                    if ( error )
                    {
                        close();
                    }
                    else if ( !clientInfo.isPersistent() || _serverInfo == null || !_serverInfo.isPersistent() )
                    {
                        close();
                    }
                    else
                    {
                        // clean out cancelled keys
                        this._selector.selectNow();
                        if ( _channel.isBlocking() ) _channel.configureBlocking( false );
                    }
                }
            }

            if ( result != null && result instanceof Throwable )
            {
                throw new RemoteException( "The server did return an Exception while handling your request!",
                                           (Throwable) result );
            }

            return result;
        }
    }

    /**
     * Asynchrounous entry point for executing server invocations. This method works asynchrounous in that way, as it
     * starts each invocation in a new thread.
     *
     * @param obj input objects for the EJOE Server
     * @param callback callback instance which will be notified if the request was processed or an an error occured
     * @return an unique identifier for the started asynchrounous server invocation
     */
    public long executeAsync( final Object obj, final EJAsyncCallback callback )
    {
        long ident = _idGenerator.nextLong();
        Thread t = new Thread( new EJAsyncWorker( this, obj, callback, ident ) );
        t.setDaemon( true );
        t.start();
        return ident;
    }

    /**
     * Closes an existing persistent connection to the corresponding EJOE server. Invoking this method when using
     * non-persistent connections has no effect.
     */
    public void close()
    {
        synchronized ( _mutex )
        {
            IOUtil.closeQuiet( this._selector );
            IOUtil.closeQuiet( this._channel );
            this._serverInfo = null;
            this._channel = null;
        }
    }

    /**
     * Opens a new socket connection to the EJServer
     *
     * @return
     * @throws IOException
     */
    private void openSocketChannel() throws IOException
    {
        this._channel = SocketChannel.open();
        this._channel.configureBlocking( false );

        Socket socket = this._channel.socket();
        try
        {
            socket.setSoTimeout( this._connectionTimeout );
            socket.setReuseAddress( true );
            socket.setSoLinger( false, 0 );
            socket.setTrafficClass( EJConstants.IPTOS_THROUGHPUT );
        }
        catch ( Exception e )
        {
            // OK, can happen. Probably the current plattform respectively it's
            // IP implementation doesn't allow setting these socket options
        }

        this._selector = Selector.open();
        // clean out cancelled keys
        this._selector.selectNow();

        log.log( Level.INFO, "Opening new connection..." );

        this._channel.connect( new InetSocketAddress( _host, _port ) );
    }

    /**
     * Non-blocking request sending.
     *
     * @param serverInfo ConnectionHeader received from the EJServer
     * @param selKey
     * @param obj the request
     * @throws IOException
     */
    private void send( ConnectionHeader serverInfo, ConnectionHeader clientInfo, SelectionKey selKey, Object obj )
            throws IOException
    {
        ByteBuffer dataBuf = (ByteBuffer) selKey.attachment();
        DataChannel dataChannel = DataChannel.getInstance( serverInfo );
        ByteBufferOutputStream out = null;

        try
        {
            if ( dataBuf == null )
            {
                // usual way: serialize using a adapter
                if ( !clientInfo.isDirect() )
                {
                    out = new ByteBufferOutputStream();
                    serialize( out, obj, clientInfo );
                    dataBuf = out.getBackingBuffer();
                }
                // direct mode: just use the attachement
                else
                {
                    dataBuf = (ByteBuffer) obj;
                }

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

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

                dataChannel.writeHeader( serverInfo, dataBuf, this._connectionTimeout );
            }

            dataChannel.nonBlockingWrite( _channel, dataBuf );
            log.log( Level.FINE, "Request sent." );
        }
        finally
        {
            IOUtil.closeQuiet( out );
        }
    }

    /**
     * Non-blocking response reading.
     *
     * @param clientInfo client ConnectionHeader
     * @return server response received by the corresponding EJServer
     * @throws IOException
     */
    private Object receive( ConnectionHeader clientInfo ) throws IOException
    {
        Object result = null;
        DataChannel dataChannel = DataChannel.getInstance( _serverInfo );
        ByteBuffer dataBuf = clientInfo.getWaitingBuffer();
        ByteBufferInputStream in = null;

        try
        {
            if ( dataBuf == null )
            {
                int length = dataChannel.readHeader( _serverInfo, this._connectionTimeout );
                // server signals a null result?
                if ( length == 0 )
                {
                    return null;
                }

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

                log.log( Level.FINE, "Going to read server response with length: " + length );
            }

            if ( dataBuf.hasRemaining() )
            {
                DataChannel.nonBlockingRead( _channel, dataBuf );
            }

            dataBuf.flip();

            log
                    .log( Level.FINE,
                          "Response read. Now calling (de)serialize adapter to convert response back to object." );

            if ( dataBuf.hasRemaining() )
            {
                try
                {
                    // usual way: deserialize using a adapter
                    if ( !clientInfo.isDirect() && !clientInfo.isMixed() )
                    {
                        in = new ByteBufferInputStream( dataBuf );
                        result = deserialize( in, clientInfo );
                    }
                    // direct or mixed 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 = tmp;
                    }

                    log.log( Level.FINE, "Server response successfully converted to object." );
                }
                finally
                {
                    clientInfo.releaseWaitingBuffer();
                }
            }
        }
        finally
        {
            IOUtil.closeQuiet( in );
        }

        return result;
    }

    /**
     * Blocking request sending
     *
     * @param channel the connected SocketChannel
     * @param obj the request
     * @return server response received by the corresponding EJServer
     * @throws IOException
     */
    private Object processBlocked( ConnectionHeader clientInfo, Object obj ) throws IOException
    {
        Object result = null;

        // usual way: (de)serialize using a adapter
        if ( !clientInfo.isDirect() )
        {
            serialize( Channels.newOutputStream( _channel ), obj, clientInfo );
            // default mode: deserialize the server response
            if ( !clientInfo.isMixed() )
            {
                result = deserialize( new ChannelInputStream( Channels.newInputStream( _channel ) ), clientInfo );
            }
            // mixed mode: don't deserialize the server response
            else
            {
                result = IOUtil.readDirect( new ChannelInputStream( Channels.newInputStream( _channel ) ) );
            }
        }
        // direct mode: don't (de)serialize, just copy and return the read
        // ByteBuffer
        else
        {
            ByteBuffer tmp = (ByteBuffer) obj;
            if ( tmp.position() > 0 )
            {
                tmp.flip();
            }
            IOUtil.writeDirect( Channels.newOutputStream( _channel ), tmp );
            result = IOUtil.readDirect( new ChannelInputStream( Channels.newInputStream( _channel ) ) );
        }

        return result;
    }

    /**
     * Serializes a given request object through the given OutputStream
     *
     * @param out the OutputStream to use
     * @param obj the object to serialize
     * @param serverInfo ConnectionHeader containing the connection data of the connected EJServer
     * @throws IOException
     */
    private void serialize( OutputStream out, Object obj, ConnectionHeader clientInfo ) throws IOException
    {
        boolean compressed = !clientInfo.isHttp() && clientInfo.hasCompression() && _serverInfo.hasCompression();

        try
        {
            IOUtil.adapterSerialize( this._adapter, out, obj, compressed, clientInfo.getCompressionLevel() );
        }
        catch ( Exception e )
        {
            throw new RemoteException( "The client encountered an error while serializing your request data!", e );
        }
    }

    /**
     * Deserializes a server response into an object from the given InputStream
     *
     * @param in the InputStream to read the response from
     * @param serverInfo ConnectionHeader containing the connection data of the connected EJServer
     * @return a deserialized response object
     * @throws IOException
     */
    private Object deserialize( InputStream in, ConnectionHeader clientInfo ) throws IOException
    {
        boolean compressed = clientInfo.hasCompression() && _serverInfo.hasCompression();

        try
        {
            return IOUtil.adapterDeserialize( this._adapter, in, compressed );
        }
        catch ( Exception e )
        {
            throw new RemoteException( "The client encountered an error while deserializing the server response!", e );
        }
    }

    /**
     * Initializes a EJClassloader and sets it as the current context classloader. Also notifies the used
     * SerializeAdapter to recognize the classloader change.
     */
    private void initClassLoader()
    {
        EJClassLoader ejcl = new EJClassLoader( Thread.currentThread().getContextClassLoader(), _host, _port );
        Thread.currentThread().setContextClassLoader( ejcl );
        this._adapter.handleClassLoaderChange( ejcl );
    }

    /**
     * Reads the settings for this client from a given Properties instance
     *
     * @param props
     */
    private void loadProperties( Properties props )
    {
        String clazz = null;

        clazz = props.getProperty( "ejoe.adapter", EJConstants.EJOE_DEFAULT_ADAPTER.getName() );
        _adapter = AdapterFactory.createAdapter( clazz );
        this._clientInfo.setAdapterName( clazz );

        _host = props.getProperty( "ejoe.host", "127.0.0.1" );
        _port = Integer.parseInt( props.getProperty( "ejoe.port", String.valueOf( EJConstants.EJOE_PORT ) ) );

        this._clientInfo.setHost( _host + ':' + _port );

        setConnectionTimeout( Integer.parseInt( props.getProperty( "ejoe.connectionTimeout", String
                .valueOf( EJConstants.EJOE_CONNECTION_TIMEOUT ) ) ) );

        enableCompression( Boolean.valueOf( props.getProperty( "ejoe.compression", "false" ) ).booleanValue() );

        enablePersistentConnection( Boolean.valueOf( props.getProperty( "ejoe.persistentConnection", "true" ) )
                .booleanValue() );

        enableHttpPackaging( Boolean.valueOf( props.getProperty( "ejoe.httpPackaging", "false" ) ).booleanValue() );

        if ( Boolean.valueOf( props.getProperty( "ejoe.remoteClassloader", "false" ) ).booleanValue() )
        {
            enableRemoteClassloading();
        }

        if ( Boolean.valueOf( props.getProperty( "ejoe.inJVM", "false" ) ).booleanValue() )
        {
            setInJVM( true );
        }

        int adapterStrategy = Integer.parseInt( props.getProperty( "ejoe.adapterStrategy", "0" ) );
        setAdapterStrategy( adapterStrategy );
    }
}
TOP

Related Classes of de.netseeker.ejoe.EJClient

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.