You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by tr...@apache.org on 2005/12/01 06:19:07 UTC

svn commit: r350169 [10/16] - in /directory/network: branches/chain_refactor/src/java/org/apache/mina/common/ trunk/src/examples/org/apache/mina/examples/echoserver/ trunk/src/examples/org/apache/mina/examples/httpserver/ trunk/src/examples/org/apache/...

Modified: directory/network/trunk/src/java/org/apache/mina/filter/codec/support/SimpleProtocolEncoderOutput.java
URL: http://svn.apache.org/viewcvs/directory/network/trunk/src/java/org/apache/mina/filter/codec/support/SimpleProtocolEncoderOutput.java?rev=350169&r1=350168&r2=350169&view=diff
==============================================================================
--- directory/network/trunk/src/java/org/apache/mina/filter/codec/support/SimpleProtocolEncoderOutput.java (original)
+++ directory/network/trunk/src/java/org/apache/mina/filter/codec/support/SimpleProtocolEncoderOutput.java Wed Nov 30 21:17:41 2005
@@ -1,104 +1,104 @@
-/**
- * 
- */
-package org.apache.mina.filter.codec.support;
-
-import org.apache.mina.common.ByteBuffer;
-import org.apache.mina.common.WriteFuture;
-import org.apache.mina.filter.codec.ProtocolEncoderOutput;
-import org.apache.mina.util.Queue;
-
-/**
- * A {@link ProtocolEncoderOutput} based on queue.
- *
- * @author The Apache Directory Project (dev@directory.apache.org)
- * @version $Rev$, $Date$
- */
-public abstract class SimpleProtocolEncoderOutput implements ProtocolEncoderOutput
-{
-    private final Queue bufferQueue = new Queue();
-    
-    public SimpleProtocolEncoderOutput()
-    {
-    }
-    
-    public Queue getBufferQueue()
-    {
-        return bufferQueue;
-    }
-    
-    public void write( ByteBuffer buf )
-    {
-        bufferQueue.push( buf );
-    }
-    
-    public void mergeAll()
-    {
-        int sum = 0;
-        final int size = bufferQueue.size();
-        
-        if( size < 2 )
-        {
-            // no need to merge!
-            return;
-        }
-        
-        // Get the size of merged BB
-        for( int i = size - 1; i >= 0; i -- )
-        {
-            sum += ( ( ByteBuffer ) bufferQueue.get( i ) ).remaining();
-        }
-        
-        // Allocate a new BB that will contain all fragments
-        ByteBuffer newBuf = ByteBuffer.allocate( sum );
-        
-        // and merge all.
-        for( ;; )
-        {
-            ByteBuffer buf = ( ByteBuffer ) bufferQueue.pop();
-            if( buf == null )
-            {
-                break;
-            }
-    
-            newBuf.put( buf );
-            buf.release();
-        }
-        
-        // Push the new buffer finally.
-        newBuf.flip();
-        bufferQueue.push(newBuf);
-    }
-    
-    public WriteFuture flush()
-    {
-        Queue bufferQueue = this.bufferQueue;
-        WriteFuture future = null;
-        if( bufferQueue.isEmpty() )
-        {
-            future = doFlush( ByteBuffer.allocate( 0 ).flip() ); // write zero bytes
-        }
-        else
-        {
-            for( ;; )
-            {
-                ByteBuffer buf = ( ByteBuffer ) bufferQueue.pop();
-                if( buf == null )
-                {
-                    break;
-                }
-                
-                future = doFlush( buf );
-            }
-        }
-        
-        if( future == null )
-        {
-            throw new IllegalStateException( "doFlush must return future." );
-        }
-        
-        return future;
-    }
-    
-    protected abstract WriteFuture doFlush( ByteBuffer buf );
+/**
+ * 
+ */
+package org.apache.mina.filter.codec.support;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.WriteFuture;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+import org.apache.mina.util.Queue;
+
+/**
+ * A {@link ProtocolEncoderOutput} based on queue.
+ *
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev$, $Date$
+ */
+public abstract class SimpleProtocolEncoderOutput implements ProtocolEncoderOutput
+{
+    private final Queue bufferQueue = new Queue();
+    
+    public SimpleProtocolEncoderOutput()
+    {
+    }
+    
+    public Queue getBufferQueue()
+    {
+        return bufferQueue;
+    }
+    
+    public void write( ByteBuffer buf )
+    {
+        bufferQueue.push( buf );
+    }
+    
+    public void mergeAll()
+    {
+        int sum = 0;
+        final int size = bufferQueue.size();
+        
+        if( size < 2 )
+        {
+            // no need to merge!
+            return;
+        }
+        
+        // Get the size of merged BB
+        for( int i = size - 1; i >= 0; i -- )
+        {
+            sum += ( ( ByteBuffer ) bufferQueue.get( i ) ).remaining();
+        }
+        
+        // Allocate a new BB that will contain all fragments
+        ByteBuffer newBuf = ByteBuffer.allocate( sum );
+        
+        // and merge all.
+        for( ;; )
+        {
+            ByteBuffer buf = ( ByteBuffer ) bufferQueue.pop();
+            if( buf == null )
+            {
+                break;
+            }
+    
+            newBuf.put( buf );
+            buf.release();
+        }
+        
+        // Push the new buffer finally.
+        newBuf.flip();
+        bufferQueue.push(newBuf);
+    }
+    
+    public WriteFuture flush()
+    {
+        Queue bufferQueue = this.bufferQueue;
+        WriteFuture future = null;
+        if( bufferQueue.isEmpty() )
+        {
+            future = doFlush( ByteBuffer.allocate( 0 ).flip() ); // write zero bytes
+        }
+        else
+        {
+            for( ;; )
+            {
+                ByteBuffer buf = ( ByteBuffer ) bufferQueue.pop();
+                if( buf == null )
+                {
+                    break;
+                }
+                
+                future = doFlush( buf );
+            }
+        }
+        
+        if( future == null )
+        {
+            throw new IllegalStateException( "doFlush must return future." );
+        }
+        
+        return future;
+    }
+    
+    protected abstract WriteFuture doFlush( ByteBuffer buf );
 }

Modified: directory/network/trunk/src/java/org/apache/mina/filter/support/SSLHandler.java
URL: http://svn.apache.org/viewcvs/directory/network/trunk/src/java/org/apache/mina/filter/support/SSLHandler.java?rev=350169&r1=350168&r2=350169&view=diff
==============================================================================
--- directory/network/trunk/src/java/org/apache/mina/filter/support/SSLHandler.java (original)
+++ directory/network/trunk/src/java/org/apache/mina/filter/support/SSLHandler.java Wed Nov 30 21:17:41 2005
@@ -1,729 +1,729 @@
-/*
- *   @(#) $Id$
- *
- *   Copyright 2004 The Apache Software Foundation
- *
- *   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.
- *
- */
-package org.apache.mina.filter.support;
-
-import java.nio.ByteBuffer;
-
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLEngineResult;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLHandshakeException;
-import javax.net.ssl.SSLSession;
-
-import org.apache.mina.common.IoSession;
-import org.apache.mina.common.WriteFuture;
-import org.apache.mina.common.IoFilter.NextFilter;
-import org.apache.mina.common.IoFilter.WriteRequest;
-import org.apache.mina.filter.SSLFilter;
-import org.apache.mina.util.Queue;
-import org.apache.mina.util.SessionLog;
-
-/**
- * A helper class using the SSLEngine API to decrypt/encrypt data.
- * <p>
- * Each connection has a SSLEngine that is used through the lifetime of the connection.
- * We allocate byte buffers for use as the outbound and inbound network buffers.
- * These buffers handle all of the intermediary data for the SSL connection. To make things easy,
- * we'll require outNetBuffer be completely flushed before trying to wrap any more data.
- *
- * @author The Apache Directory Project (dev@directory.apache.org)
- * @version $Rev$, $Date$
- */
-public class SSLHandler
-{
-    private final SSLFilter parent;
-    private final IoSession session;
-    private final Queue scheduledWrites = new Queue();
-    private final SSLEngine sslEngine;
-
-    /**
-     * Encrypted data from the net
-     */
-    private ByteBuffer inNetBuffer;
-
-    /**
-     * Encrypted data to be written to the net
-     */
-    private ByteBuffer outNetBuffer;
-
-    /**
-     * Applicaton cleartext data to be read by application
-     */
-    private ByteBuffer appBuffer;
-
-    /**
-     * Empty buffer used during initial handshake and close operations
-     */
-    private ByteBuffer hsBB = ByteBuffer.allocate( 0 );
-
-    /**
-     * Handshake status
-     */
-    private SSLEngineResult.HandshakeStatus initialHandshakeStatus;
-
-    /**
-     * Initial handshake complete?
-     */
-    private boolean initialHandshakeComplete;
-
-    private boolean writingEncryptedData = false;
-    
-    /**
-     * Constuctor.
-     *
-     * @param sslc
-     * @throws SSLException 
-     */
-    public SSLHandler( SSLFilter parent, SSLContext sslc, IoSession session ) throws SSLException
-    {
-        this.parent = parent;
-        this.session = session;
-        sslEngine = sslc.createSSLEngine();
-        sslEngine.setUseClientMode( parent.isUseClientMode() );
-
-        if ( parent.isWantClientAuth() )
-        {
-            sslEngine.setWantClientAuth( true );
-        }
-
-        if ( parent.isNeedClientAuth() )
-        {
-            sslEngine.setNeedClientAuth( true );
-        }
-  
-        if( parent.getEnabledCipherSuites() != null )
-        {
-            sslEngine.setEnabledCipherSuites( parent.getEnabledCipherSuites() );
-        }
-        
-        if( parent.getEnabledProtocols() != null )
-        {
-            sslEngine.setEnabledProtocols( parent.getEnabledProtocols() );
-        }
-
-        sslEngine.beginHandshake();   
-        initialHandshakeStatus = sslEngine.getHandshakeStatus();//SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
-        initialHandshakeComplete = false;
-        
-        SSLByteBufferPool.initiate( sslEngine );
-
-        appBuffer = SSLByteBufferPool.getApplicationBuffer();
-
-        inNetBuffer = SSLByteBufferPool.getPacketBuffer();
-        outNetBuffer = SSLByteBufferPool.getPacketBuffer();
-        outNetBuffer.position( 0 );
-        outNetBuffer.limit( 0 );
-    }
-    
-    public SSLFilter getParent()
-    {
-        return parent;
-    }
-    
-    public IoSession getSession()
-    {
-        return session;
-    }
-
-    /**
-     * Check we are writing encrypted data.
-     */
-    public boolean isWritingEncryptedData()
-    {
-        return writingEncryptedData;
-    }
-
-    /**
-     * Check if initial handshake is completed.
-     */
-    public boolean isInitialHandshakeComplete()
-    {
-        return initialHandshakeComplete;
-    }
-
-    public boolean isInboundDone()
-    {
-        return sslEngine.isInboundDone();
-    }
-
-    public boolean isOutboundDone()
-    {
-        return sslEngine.isOutboundDone();
-    }
-
-    /**
-     * Check if there is any need to complete initial handshake.
-     */
-    public boolean needToCompleteInitialHandshake()
-    {
-        return ( initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone() );
-    }
-    
-    public void scheduleWrite( NextFilter nextFilter, WriteRequest writeRequest )
-    {
-        scheduledWrites.push( new ScheduledWrite( nextFilter, writeRequest ) );
-    }
-    
-    public void flushScheduledWrites() throws SSLException
-    {
-        ScheduledWrite scheduledWrite;
-        
-        while( ( scheduledWrite = ( ScheduledWrite ) scheduledWrites.pop() ) != null )
-        {
-            if( SessionLog.isDebugEnabled( session ) )
-            {
-                SessionLog.debug( session, " Flushing buffered write request: " + scheduledWrite.writeRequest );
-            }
-            parent.filterWrite( scheduledWrite.nextFilter, session, scheduledWrite.writeRequest );
-        }
-    }
-
-    /**
-     * Call when data read from net. Will perform inial hanshake or decrypt provided
-     * Buffer.
-     * Decrytpted data reurned by getAppBuffer(), if any.
-     *
-     * @param buf buffer to decrypt
-     * @throws SSLException on errors
-     */
-    public void messageReceived( NextFilter nextFilter, ByteBuffer buf ) throws SSLException
-    {
-        if ( buf.limit() > inNetBuffer.remaining() ) {
-            // We have to expand inNetBuffer
-            inNetBuffer = SSLByteBufferPool.expandBuffer( inNetBuffer,
-                inNetBuffer.capacity() + ( buf.limit() * 2 ) );
-            // We also expand app. buffer (twice the size of in net. buffer)
-            appBuffer = SSLByteBufferPool.expandBuffer( appBuffer, inNetBuffer.capacity() * 2);
-            appBuffer.position( 0 );
-            appBuffer.limit( 0 );
-            if( SessionLog.isDebugEnabled( session ) )
-            {
-                SessionLog.debug( session, 
-                                    " expanded inNetBuffer:" + inNetBuffer );
-                SessionLog.debug( session, 
-                                    " expanded appBuffer:" + appBuffer );
-            }
-        }
-
-        // append buf to inNetBuffer
-        inNetBuffer.put( buf );
-        if( !initialHandshakeComplete )
-        {
-            handshake( nextFilter );
-        }
-        else
-        {
-            decrypt();
-        }
-
-        if( isInboundDone() )
-        {
-            // Rewind the MINA buffer if not all data is processed and inbound is finished.
-            buf.position( buf.position() - inNetBuffer.position() );
-            inNetBuffer.clear();
-        }
-    }
-
-    /**
-     * Get decrypted application data.
-     *
-     * @return buffer with data
-     */
-    public ByteBuffer getAppBuffer()
-    {
-        return appBuffer;
-    }
-
-    /**
-     * Get encrypted data to be sent.
-     *
-     * @return buffer with data
-     */
-    public ByteBuffer getOutNetBuffer()
-    {
-        return outNetBuffer;
-    }
-
-    /**
-     * Encrypt provided buffer. Encytpted data reurned by getOutNetBuffer().
-     *
-     * @param src data to encrypt
-     * @throws SSLException on errors
-     */
-    public void encrypt( ByteBuffer src ) throws SSLException
-    {
-        if( !initialHandshakeComplete )
-        {
-            throw new IllegalStateException();
-        }
-
-        // The data buffer is (must be) empty, we can reuse the entire
-        // buffer.
-        outNetBuffer.clear();
-
-        SSLEngineResult result;
-
-        // Loop until there is no more data in src
-        while ( src.hasRemaining() ) {
-
-            if ( src.remaining() > ( ( outNetBuffer.capacity() - outNetBuffer.position() ) / 2 ) ) {
-                // We have to expand outNetBuffer
-                // Note: there is no way to know the exact size required, but enrypted data
-                // shouln't need to be larger than twice the source data size?
-                outNetBuffer = SSLByteBufferPool.expandBuffer( outNetBuffer, src.capacity() * 2 );
-                if ( SessionLog.isDebugEnabled( session ) ) {
-                    SessionLog.debug( session, " expanded outNetBuffer:" + outNetBuffer );
-                }
-            }
-
-            result = sslEngine.wrap( src, outNetBuffer );
-            if ( SessionLog.isDebugEnabled( session ) ) {
-                SessionLog.debug( session, " Wrap res:" + result );
-            }
-
-            if ( result.getStatus() == SSLEngineResult.Status.OK ) {
-                if ( result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK ) {
-                    doTasks();
-                }
-            } else {
-                throw new SSLException( "SSLEngine error during encrypt: "
-                        + result.getStatus() +
-                        " src: " + src + "outNetBuffer: " + outNetBuffer);
-            }
-        }
-
-        outNetBuffer.flip();
-    }
-
-    /**
-     * Start SSL shutdown process.
-     * 
-     * @return <tt>true</tt> if shutdown process is started.
-     *         <tt>false</tt> if shutdown process is already finished.
-     *
-     * @throws SSLException on errors
-     */
-    public boolean closeOutbound() throws SSLException
-    {
-        if( sslEngine.isOutboundDone() )
-        {
-            return false;
-        }
-        
-        sslEngine.closeOutbound();
-
-        // By RFC 2616, we can "fire and forget" our close_notify
-        // message, so that's what we'll do here.
-        outNetBuffer.clear();
-        SSLEngineResult result = sslEngine.wrap( hsBB, outNetBuffer );
-        if( result.getStatus() != SSLEngineResult.Status.CLOSED )
-        {
-            throw new SSLException( "Improper close state: " + result );
-        }
-        outNetBuffer.flip();
-        return true;
-    }
-
-    /**
-     * Release allocated ByteBuffers.
-     */
-    public void release()
-    {
-        SSLByteBufferPool.release( appBuffer );
-        SSLByteBufferPool.release( inNetBuffer );
-        SSLByteBufferPool.release( outNetBuffer );
-    }
-
-    /**
-     * Decrypt in net buffer. Result is stored in app buffer.
-     *
-     * @throws SSLException
-     */
-    private void decrypt() throws SSLException
-    {
-
-        if( !initialHandshakeComplete )
-        {
-            throw new IllegalStateException();
-        }
-
-        if( appBuffer.hasRemaining() )
-        {
-             if ( SessionLog.isDebugEnabled( session ) ) {
-                 SessionLog.debug( session, " Error: appBuffer not empty!" );
-             }
-            //still app data in buffer!?
-            throw new IllegalStateException();
-        }
-
-        unwrap();
-    }
-
-    /**
-     * @param status
-     * @throws SSLException
-     */
-    private SSLEngineResult.Status checkStatus( SSLEngineResult.Status status ) throws SSLException
-    {
-        if( status != SSLEngineResult.Status.OK &&
-            status != SSLEngineResult.Status.CLOSED &&
-            status != SSLEngineResult.Status.BUFFER_UNDERFLOW )
-        {
-            throw new SSLException( "SSLEngine error during decrypt: " +
-                                    status +
-                                    " inNetBuffer: " + inNetBuffer + "appBuffer: " + appBuffer);
-        }
-        
-        return status;
-    }
-    
-    /**
-     * Perform any handshaking processing.
-     */
-    public void handshake( NextFilter nextFilter ) throws SSLException
-    {
-        if( SessionLog.isDebugEnabled( session ) )
-        {
-            SessionLog.debug( session, " doHandshake()" );
-        }
-        
-        while( !initialHandshakeComplete )
-        {
-            if( initialHandshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED )
-            {
-                session.setAttribute( SSLFilter.SSL_SESSION, sslEngine.getSession() );
-                if( SessionLog.isDebugEnabled( session ) )
-                {
-                    SSLSession sslSession = sslEngine.getSession();
-                    SessionLog.debug( session, "  initialHandshakeStatus=FINISHED" );
-                    SessionLog.debug( session, "  sslSession CipherSuite used " + sslSession.getCipherSuite() );
-                }
-                initialHandshakeComplete = true;
-                if( session.containsAttribute( SSLFilter.USE_NOTIFICATION ) )
-                {
-                    nextFilter.messageReceived( session, SSLFilter.SESSION_SECURED );
-                }
-                break;
-            }
-            else if( initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_TASK )
-            {
-                if( SessionLog.isDebugEnabled( session ) )
-                {
-                    SessionLog.debug( session, "  initialHandshakeStatus=NEED_TASK" );
-                }
-                initialHandshakeStatus = doTasks();
-            }
-            else if( initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP )
-            {
-                // we need more data read
-                if( SessionLog.isDebugEnabled( session ) )
-                {
-                    SessionLog.debug( session, "  initialHandshakeStatus=NEED_UNWRAP" );
-                }
-                SSLEngineResult.Status status = unwrapHandshake();
-                if( ( initialHandshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED 
-                		&& status == SSLEngineResult.Status.BUFFER_UNDERFLOW )
-                        || isInboundDone() )
-                {
-                    // We need more data or the session is closed
-                    break;
-                }
-            }
-            else if( initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP )
-            {
-                if( SessionLog.isDebugEnabled( session ) )
-                {
-                    SessionLog.debug( session, "  initialHandshakeStatus=NEED_WRAP" );
-                }
-                // First make sure that the out buffer is completely empty. Since we
-                // cannot call wrap with data left on the buffer
-                if( outNetBuffer.hasRemaining() )
-                {
-                    if( SessionLog.isDebugEnabled( session ) )
-                    {
-                        SessionLog.debug( session, "  Still data in out buffer!" );
-                    }
-                    break;
-                }
-                outNetBuffer.clear();
-                SSLEngineResult result = sslEngine.wrap( hsBB, outNetBuffer );
-                if( SessionLog.isDebugEnabled( session ) )
-                {
-                    SessionLog.debug( session, " Wrap res:" + result );
-                }
-
-                outNetBuffer.flip();
-                initialHandshakeStatus = result.getHandshakeStatus();
-                writeNetBuffer( nextFilter );
-            }
-            else
-            {
-                throw new IllegalStateException( "Invalid Handshaking State"
-                        + initialHandshakeStatus );
-            }
-        }
-    }
-
-    public WriteFuture writeNetBuffer( NextFilter nextFilter ) throws SSLException
-    {
-        // Check if any net data needed to be writen
-        if( !getOutNetBuffer().hasRemaining() )
-        {
-            // no; bail out
-            return WriteFuture.newNotWrittenFuture();
-        }
-        
-        WriteFuture writeFuture = null;
-        
-        // write net data
-        
-        // set flag that we are writing encrypted data
-        // (used in SSLFilter.filterWrite())
-        writingEncryptedData = true;
-        
-        try
-        {
-            if( SessionLog.isDebugEnabled( session ) )
-            {
-                SessionLog.debug( session, " write outNetBuffer: " + getOutNetBuffer() );
-            }
-            org.apache.mina.common.ByteBuffer writeBuffer = copy( getOutNetBuffer() );
-            if( SessionLog.isDebugEnabled( session ) )
-            {
-                SessionLog.debug( session, " session write: " + writeBuffer );
-            }
-            //debug("outNetBuffer (after copy): {0}", sslHandler.getOutNetBuffer());
-            
-            writeFuture = new WriteFuture();
-            parent.filterWrite( nextFilter, session, new WriteRequest( writeBuffer, writeFuture ) );
-
-            // loop while more writes required to complete handshake
-            while( needToCompleteInitialHandshake() )
-            {
-                try
-                {
-                    handshake( nextFilter );
-                }
-                catch( SSLException ssle )
-                {
-                    SSLException newSSLE = new SSLHandshakeException(
-                            "Initial SSL handshake failed." );
-                    newSSLE.initCause( ssle );
-                    throw newSSLE;
-                }
-                if( getOutNetBuffer().hasRemaining() )
-                {
-                    if( SessionLog.isDebugEnabled( session ) )
-                    {
-                        SessionLog.debug( session, " write outNetBuffer2: " + getOutNetBuffer() );
-                    }
-                    org.apache.mina.common.ByteBuffer writeBuffer2 = copy( getOutNetBuffer() );
-                    writeFuture = new WriteFuture();
-                    parent.filterWrite( nextFilter, session, new WriteRequest( writeBuffer2, writeFuture ) );
-                }
-            }
-        }
-        finally
-        {
-            writingEncryptedData = false;
-        }
-        
-        if( writeFuture != null )
-        {
-            return writeFuture;
-        }
-        else
-        {
-            return WriteFuture.newNotWrittenFuture();
-        }
-    }
-    
-    
-    private SSLEngineResult.Status unwrap() throws SSLException
-    {
-        if( SessionLog.isDebugEnabled( session ) )
-        {
-            SessionLog.debug( session, " unwrap()" );
-        }
-        // Prepare the application buffer to receive decrypted data
-        appBuffer.clear();
-
-        // Prepare the net data for reading.
-        inNetBuffer.flip();
-
-        SSLEngineResult res;
-        do
-        {
-            if( SessionLog.isDebugEnabled( session ) )
-            {
-                SessionLog.debug( session, "   inNetBuffer: " + inNetBuffer );
-                SessionLog.debug( session, "   appBuffer: " + appBuffer );
-            }
-            res = sslEngine.unwrap( inNetBuffer, appBuffer );
-            if( SessionLog.isDebugEnabled( session ) )
-            {
-                SessionLog.debug( session, " Unwrap res:" + res );
-            }
-        }
-        while( res.getStatus() == SSLEngineResult.Status.OK );
-
-        // prepare to be written again
-        inNetBuffer.compact();
-        // prepare app data to be read
-        appBuffer.flip();
-
-        /*
-         * The status may be:
-         * OK - Normal operation
-         * OVERFLOW - Should never happen since the application buffer is
-         *      sized to hold the maximum packet size.
-         * UNDERFLOW - Need to read more data from the socket. It's normal.
-         * CLOSED - The other peer closed the socket. Also normal.
-         */
-        return checkStatus( res.getStatus() );
-    }
-
-    private SSLEngineResult.Status unwrapHandshake() throws SSLException
-    {
-        if( SessionLog.isDebugEnabled( session ) )
-        {
-            SessionLog.debug( session, " unwrapHandshake()" );
-        }
-        // Prepare the application buffer to receive decrypted data
-        appBuffer.clear();
-
-        // Prepare the net data for reading.
-        inNetBuffer.flip();
-
-        SSLEngineResult res;
-        do
-        {
-            if( SessionLog.isDebugEnabled( session ) )
-            {
-                SessionLog.debug( session, "   inNetBuffer: " + inNetBuffer );
-                SessionLog.debug( session, "   appBuffer: " + appBuffer );
-            }
-            res = sslEngine.unwrap( inNetBuffer, appBuffer );
-            if( SessionLog.isDebugEnabled( session ) )
-            {
-                SessionLog.debug( session, " Unwrap res:" + res );
-            }
-
-        }
-        while( res.getStatus() == SSLEngineResult.Status.OK &&
-               res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP );
-
-        initialHandshakeStatus = res.getHandshakeStatus();
-	
-	    // If handshake finished, no data was produced, and the status is still ok,
-		// try to unwrap more
-		if (initialHandshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED
-				&& appBuffer.position() == 0
-				&& res.getStatus() == SSLEngineResult.Status.OK
-				&& inNetBuffer.hasRemaining()) {
-			do {
-				if (SessionLog.isDebugEnabled( session )) {
-					SessionLog.debug( session, "  extra handshake unwrap" );
-                    SessionLog.debug( session, "   inNetBuffer: " + inNetBuffer );
-                    SessionLog.debug( session, "   appBuffer: " + appBuffer );
-				}
-				res = sslEngine.unwrap(inNetBuffer, appBuffer);
-				if (SessionLog.isDebugEnabled( session )) {
-                    SessionLog.debug( session, " Unwrap res:" + res );
-				}
-			} while (res.getStatus() == SSLEngineResult.Status.OK);
-		}
-
-        // prepare to be written again
-        inNetBuffer.compact();
-
-        // prepare app data to be read
-        appBuffer.flip();
-
-        /*
-         * The status may be:
-         * OK - Normal operation
-         * OVERFLOW - Should never happen since the application buffer is
-         *      sized to hold the maximum packet size.
-         * UNDERFLOW - Need to read more data from the socket. It's normal.
-         * CLOSED - The other peer closed the socket. Also normal.
-         */
-        //initialHandshakeStatus = res.getHandshakeStatus();
-        return checkStatus( res.getStatus() );
-    }
-
-    /**
-     * Do all the outstanding handshake tasks in the current Thread.
-     */
-    private SSLEngineResult.HandshakeStatus doTasks()
-    {
-        if( SessionLog.isDebugEnabled( session ) )
-        {
-            SessionLog.debug( session, "   doTasks()" );
-        }
-
-        /*
-         * We could run this in a separate thread, but I don't see the need
-         * for this when used from SSLFilter. Use thread filters in MINA instead?
-         */
-        Runnable runnable;
-        while( ( runnable = sslEngine.getDelegatedTask() ) != null )
-        {
-            if( SessionLog.isDebugEnabled( session ) )
-            {
-                SessionLog.debug( session, "    doTask: " + runnable );
-            }
-            runnable.run();
-        }
-        if( SessionLog.isDebugEnabled( session ) )
-        {
-            SessionLog.debug( session, "   doTasks(): "
-                    + sslEngine.getHandshakeStatus() );
-        }
-        return sslEngine.getHandshakeStatus();
-    }
-
-    private static class ScheduledWrite
-    {
-        private final NextFilter nextFilter;
-        private final WriteRequest writeRequest;
-        
-        public ScheduledWrite( NextFilter nextFilter, WriteRequest writeRequest )
-        {
-            this.nextFilter = nextFilter;
-            this.writeRequest = writeRequest;
-        }
-    }
-    
-    /**
-     * Creates a new Mina byte buffer that is a deep copy of the remaining bytes
-     * in the given buffer (between index buf.position() and buf.limit())
-     *
-     * @param src the buffer to copy
-     * @return the new buffer, ready to read from
-     */
-    public static org.apache.mina.common.ByteBuffer copy( java.nio.ByteBuffer src )
-    {
-        org.apache.mina.common.ByteBuffer copy = org.apache.mina.common.ByteBuffer.allocate( src.remaining() );
-        copy.put( src );
-        copy.flip();
-        return copy;
-    }
-}
+/*
+ *   @(#) $Id$
+ *
+ *   Copyright 2004 The Apache Software Foundation
+ *
+ *   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.
+ *
+ */
+package org.apache.mina.filter.support;
+
+import java.nio.ByteBuffer;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLSession;
+
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.WriteFuture;
+import org.apache.mina.common.IoFilter.NextFilter;
+import org.apache.mina.common.IoFilter.WriteRequest;
+import org.apache.mina.filter.SSLFilter;
+import org.apache.mina.util.Queue;
+import org.apache.mina.util.SessionLog;
+
+/**
+ * A helper class using the SSLEngine API to decrypt/encrypt data.
+ * <p>
+ * Each connection has a SSLEngine that is used through the lifetime of the connection.
+ * We allocate byte buffers for use as the outbound and inbound network buffers.
+ * These buffers handle all of the intermediary data for the SSL connection. To make things easy,
+ * we'll require outNetBuffer be completely flushed before trying to wrap any more data.
+ *
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev$, $Date$
+ */
+public class SSLHandler
+{
+    private final SSLFilter parent;
+    private final IoSession session;
+    private final Queue scheduledWrites = new Queue();
+    private final SSLEngine sslEngine;
+
+    /**
+     * Encrypted data from the net
+     */
+    private ByteBuffer inNetBuffer;
+
+    /**
+     * Encrypted data to be written to the net
+     */
+    private ByteBuffer outNetBuffer;
+
+    /**
+     * Applicaton cleartext data to be read by application
+     */
+    private ByteBuffer appBuffer;
+
+    /**
+     * Empty buffer used during initial handshake and close operations
+     */
+    private ByteBuffer hsBB = ByteBuffer.allocate( 0 );
+
+    /**
+     * Handshake status
+     */
+    private SSLEngineResult.HandshakeStatus initialHandshakeStatus;
+
+    /**
+     * Initial handshake complete?
+     */
+    private boolean initialHandshakeComplete;
+
+    private boolean writingEncryptedData = false;
+    
+    /**
+     * Constuctor.
+     *
+     * @param sslc
+     * @throws SSLException 
+     */
+    public SSLHandler( SSLFilter parent, SSLContext sslc, IoSession session ) throws SSLException
+    {
+        this.parent = parent;
+        this.session = session;
+        sslEngine = sslc.createSSLEngine();
+        sslEngine.setUseClientMode( parent.isUseClientMode() );
+
+        if ( parent.isWantClientAuth() )
+        {
+            sslEngine.setWantClientAuth( true );
+        }
+
+        if ( parent.isNeedClientAuth() )
+        {
+            sslEngine.setNeedClientAuth( true );
+        }
+  
+        if( parent.getEnabledCipherSuites() != null )
+        {
+            sslEngine.setEnabledCipherSuites( parent.getEnabledCipherSuites() );
+        }
+        
+        if( parent.getEnabledProtocols() != null )
+        {
+            sslEngine.setEnabledProtocols( parent.getEnabledProtocols() );
+        }
+
+        sslEngine.beginHandshake();   
+        initialHandshakeStatus = sslEngine.getHandshakeStatus();//SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
+        initialHandshakeComplete = false;
+        
+        SSLByteBufferPool.initiate( sslEngine );
+
+        appBuffer = SSLByteBufferPool.getApplicationBuffer();
+
+        inNetBuffer = SSLByteBufferPool.getPacketBuffer();
+        outNetBuffer = SSLByteBufferPool.getPacketBuffer();
+        outNetBuffer.position( 0 );
+        outNetBuffer.limit( 0 );
+    }
+    
+    public SSLFilter getParent()
+    {
+        return parent;
+    }
+    
+    public IoSession getSession()
+    {
+        return session;
+    }
+
+    /**
+     * Check we are writing encrypted data.
+     */
+    public boolean isWritingEncryptedData()
+    {
+        return writingEncryptedData;
+    }
+
+    /**
+     * Check if initial handshake is completed.
+     */
+    public boolean isInitialHandshakeComplete()
+    {
+        return initialHandshakeComplete;
+    }
+
+    public boolean isInboundDone()
+    {
+        return sslEngine.isInboundDone();
+    }
+
+    public boolean isOutboundDone()
+    {
+        return sslEngine.isOutboundDone();
+    }
+
+    /**
+     * Check if there is any need to complete initial handshake.
+     */
+    public boolean needToCompleteInitialHandshake()
+    {
+        return ( initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone() );
+    }
+    
+    public void scheduleWrite( NextFilter nextFilter, WriteRequest writeRequest )
+    {
+        scheduledWrites.push( new ScheduledWrite( nextFilter, writeRequest ) );
+    }
+    
+    public void flushScheduledWrites() throws SSLException
+    {
+        ScheduledWrite scheduledWrite;
+        
+        while( ( scheduledWrite = ( ScheduledWrite ) scheduledWrites.pop() ) != null )
+        {
+            if( SessionLog.isDebugEnabled( session ) )
+            {
+                SessionLog.debug( session, " Flushing buffered write request: " + scheduledWrite.writeRequest );
+            }
+            parent.filterWrite( scheduledWrite.nextFilter, session, scheduledWrite.writeRequest );
+        }
+    }
+
+    /**
+     * Call when data read from net. Will perform inial hanshake or decrypt provided
+     * Buffer.
+     * Decrytpted data reurned by getAppBuffer(), if any.
+     *
+     * @param buf buffer to decrypt
+     * @throws SSLException on errors
+     */
+    public void messageReceived( NextFilter nextFilter, ByteBuffer buf ) throws SSLException
+    {
+        if ( buf.limit() > inNetBuffer.remaining() ) {
+            // We have to expand inNetBuffer
+            inNetBuffer = SSLByteBufferPool.expandBuffer( inNetBuffer,
+                inNetBuffer.capacity() + ( buf.limit() * 2 ) );
+            // We also expand app. buffer (twice the size of in net. buffer)
+            appBuffer = SSLByteBufferPool.expandBuffer( appBuffer, inNetBuffer.capacity() * 2);
+            appBuffer.position( 0 );
+            appBuffer.limit( 0 );
+            if( SessionLog.isDebugEnabled( session ) )
+            {
+                SessionLog.debug( session, 
+                                    " expanded inNetBuffer:" + inNetBuffer );
+                SessionLog.debug( session, 
+                                    " expanded appBuffer:" + appBuffer );
+            }
+        }
+
+        // append buf to inNetBuffer
+        inNetBuffer.put( buf );
+        if( !initialHandshakeComplete )
+        {
+            handshake( nextFilter );
+        }
+        else
+        {
+            decrypt();
+        }
+
+        if( isInboundDone() )
+        {
+            // Rewind the MINA buffer if not all data is processed and inbound is finished.
+            buf.position( buf.position() - inNetBuffer.position() );
+            inNetBuffer.clear();
+        }
+    }
+
+    /**
+     * Get decrypted application data.
+     *
+     * @return buffer with data
+     */
+    public ByteBuffer getAppBuffer()
+    {
+        return appBuffer;
+    }
+
+    /**
+     * Get encrypted data to be sent.
+     *
+     * @return buffer with data
+     */
+    public ByteBuffer getOutNetBuffer()
+    {
+        return outNetBuffer;
+    }
+
+    /**
+     * Encrypt provided buffer. Encytpted data reurned by getOutNetBuffer().
+     *
+     * @param src data to encrypt
+     * @throws SSLException on errors
+     */
+    public void encrypt( ByteBuffer src ) throws SSLException
+    {
+        if( !initialHandshakeComplete )
+        {
+            throw new IllegalStateException();
+        }
+
+        // The data buffer is (must be) empty, we can reuse the entire
+        // buffer.
+        outNetBuffer.clear();
+
+        SSLEngineResult result;
+
+        // Loop until there is no more data in src
+        while ( src.hasRemaining() ) {
+
+            if ( src.remaining() > ( ( outNetBuffer.capacity() - outNetBuffer.position() ) / 2 ) ) {
+                // We have to expand outNetBuffer
+                // Note: there is no way to know the exact size required, but enrypted data
+                // shouln't need to be larger than twice the source data size?
+                outNetBuffer = SSLByteBufferPool.expandBuffer( outNetBuffer, src.capacity() * 2 );
+                if ( SessionLog.isDebugEnabled( session ) ) {
+                    SessionLog.debug( session, " expanded outNetBuffer:" + outNetBuffer );
+                }
+            }
+
+            result = sslEngine.wrap( src, outNetBuffer );
+            if ( SessionLog.isDebugEnabled( session ) ) {
+                SessionLog.debug( session, " Wrap res:" + result );
+            }
+
+            if ( result.getStatus() == SSLEngineResult.Status.OK ) {
+                if ( result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK ) {
+                    doTasks();
+                }
+            } else {
+                throw new SSLException( "SSLEngine error during encrypt: "
+                        + result.getStatus() +
+                        " src: " + src + "outNetBuffer: " + outNetBuffer);
+            }
+        }
+
+        outNetBuffer.flip();
+    }
+
+    /**
+     * Start SSL shutdown process.
+     * 
+     * @return <tt>true</tt> if shutdown process is started.
+     *         <tt>false</tt> if shutdown process is already finished.
+     *
+     * @throws SSLException on errors
+     */
+    public boolean closeOutbound() throws SSLException
+    {
+        if( sslEngine.isOutboundDone() )
+        {
+            return false;
+        }
+        
+        sslEngine.closeOutbound();
+
+        // By RFC 2616, we can "fire and forget" our close_notify
+        // message, so that's what we'll do here.
+        outNetBuffer.clear();
+        SSLEngineResult result = sslEngine.wrap( hsBB, outNetBuffer );
+        if( result.getStatus() != SSLEngineResult.Status.CLOSED )
+        {
+            throw new SSLException( "Improper close state: " + result );
+        }
+        outNetBuffer.flip();
+        return true;
+    }
+
+    /**
+     * Release allocated ByteBuffers.
+     */
+    public void release()
+    {
+        SSLByteBufferPool.release( appBuffer );
+        SSLByteBufferPool.release( inNetBuffer );
+        SSLByteBufferPool.release( outNetBuffer );
+    }
+
+    /**
+     * Decrypt in net buffer. Result is stored in app buffer.
+     *
+     * @throws SSLException
+     */
+    private void decrypt() throws SSLException
+    {
+
+        if( !initialHandshakeComplete )
+        {
+            throw new IllegalStateException();
+        }
+
+        if( appBuffer.hasRemaining() )
+        {
+             if ( SessionLog.isDebugEnabled( session ) ) {
+                 SessionLog.debug( session, " Error: appBuffer not empty!" );
+             }
+            //still app data in buffer!?
+            throw new IllegalStateException();
+        }
+
+        unwrap();
+    }
+
+    /**
+     * @param status
+     * @throws SSLException
+     */
+    private SSLEngineResult.Status checkStatus( SSLEngineResult.Status status ) throws SSLException
+    {
+        if( status != SSLEngineResult.Status.OK &&
+            status != SSLEngineResult.Status.CLOSED &&
+            status != SSLEngineResult.Status.BUFFER_UNDERFLOW )
+        {
+            throw new SSLException( "SSLEngine error during decrypt: " +
+                                    status +
+                                    " inNetBuffer: " + inNetBuffer + "appBuffer: " + appBuffer);
+        }
+        
+        return status;
+    }
+    
+    /**
+     * Perform any handshaking processing.
+     */
+    public void handshake( NextFilter nextFilter ) throws SSLException
+    {
+        if( SessionLog.isDebugEnabled( session ) )
+        {
+            SessionLog.debug( session, " doHandshake()" );
+        }
+        
+        while( !initialHandshakeComplete )
+        {
+            if( initialHandshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED )
+            {
+                session.setAttribute( SSLFilter.SSL_SESSION, sslEngine.getSession() );
+                if( SessionLog.isDebugEnabled( session ) )
+                {
+                    SSLSession sslSession = sslEngine.getSession();
+                    SessionLog.debug( session, "  initialHandshakeStatus=FINISHED" );
+                    SessionLog.debug( session, "  sslSession CipherSuite used " + sslSession.getCipherSuite() );
+                }
+                initialHandshakeComplete = true;
+                if( session.containsAttribute( SSLFilter.USE_NOTIFICATION ) )
+                {
+                    nextFilter.messageReceived( session, SSLFilter.SESSION_SECURED );
+                }
+                break;
+            }
+            else if( initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_TASK )
+            {
+                if( SessionLog.isDebugEnabled( session ) )
+                {
+                    SessionLog.debug( session, "  initialHandshakeStatus=NEED_TASK" );
+                }
+                initialHandshakeStatus = doTasks();
+            }
+            else if( initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP )
+            {
+                // we need more data read
+                if( SessionLog.isDebugEnabled( session ) )
+                {
+                    SessionLog.debug( session, "  initialHandshakeStatus=NEED_UNWRAP" );
+                }
+                SSLEngineResult.Status status = unwrapHandshake();
+                if( ( initialHandshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED 
+                		&& status == SSLEngineResult.Status.BUFFER_UNDERFLOW )
+                        || isInboundDone() )
+                {
+                    // We need more data or the session is closed
+                    break;
+                }
+            }
+            else if( initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP )
+            {
+                if( SessionLog.isDebugEnabled( session ) )
+                {
+                    SessionLog.debug( session, "  initialHandshakeStatus=NEED_WRAP" );
+                }
+                // First make sure that the out buffer is completely empty. Since we
+                // cannot call wrap with data left on the buffer
+                if( outNetBuffer.hasRemaining() )
+                {
+                    if( SessionLog.isDebugEnabled( session ) )
+                    {
+                        SessionLog.debug( session, "  Still data in out buffer!" );
+                    }
+                    break;
+                }
+                outNetBuffer.clear();
+                SSLEngineResult result = sslEngine.wrap( hsBB, outNetBuffer );
+                if( SessionLog.isDebugEnabled( session ) )
+                {
+                    SessionLog.debug( session, " Wrap res:" + result );
+                }
+
+                outNetBuffer.flip();
+                initialHandshakeStatus = result.getHandshakeStatus();
+                writeNetBuffer( nextFilter );
+            }
+            else
+            {
+                throw new IllegalStateException( "Invalid Handshaking State"
+                        + initialHandshakeStatus );
+            }
+        }
+    }
+
+    public WriteFuture writeNetBuffer( NextFilter nextFilter ) throws SSLException
+    {
+        // Check if any net data needed to be writen
+        if( !getOutNetBuffer().hasRemaining() )
+        {
+            // no; bail out
+            return WriteFuture.newNotWrittenFuture();
+        }
+        
+        WriteFuture writeFuture = null;
+        
+        // write net data
+        
+        // set flag that we are writing encrypted data
+        // (used in SSLFilter.filterWrite())
+        writingEncryptedData = true;
+        
+        try
+        {
+            if( SessionLog.isDebugEnabled( session ) )
+            {
+                SessionLog.debug( session, " write outNetBuffer: " + getOutNetBuffer() );
+            }
+            org.apache.mina.common.ByteBuffer writeBuffer = copy( getOutNetBuffer() );
+            if( SessionLog.isDebugEnabled( session ) )
+            {
+                SessionLog.debug( session, " session write: " + writeBuffer );
+            }
+            //debug("outNetBuffer (after copy): {0}", sslHandler.getOutNetBuffer());
+            
+            writeFuture = new WriteFuture();
+            parent.filterWrite( nextFilter, session, new WriteRequest( writeBuffer, writeFuture ) );
+
+            // loop while more writes required to complete handshake
+            while( needToCompleteInitialHandshake() )
+            {
+                try
+                {
+                    handshake( nextFilter );
+                }
+                catch( SSLException ssle )
+                {
+                    SSLException newSSLE = new SSLHandshakeException(
+                            "Initial SSL handshake failed." );
+                    newSSLE.initCause( ssle );
+                    throw newSSLE;
+                }
+                if( getOutNetBuffer().hasRemaining() )
+                {
+                    if( SessionLog.isDebugEnabled( session ) )
+                    {
+                        SessionLog.debug( session, " write outNetBuffer2: " + getOutNetBuffer() );
+                    }
+                    org.apache.mina.common.ByteBuffer writeBuffer2 = copy( getOutNetBuffer() );
+                    writeFuture = new WriteFuture();
+                    parent.filterWrite( nextFilter, session, new WriteRequest( writeBuffer2, writeFuture ) );
+                }
+            }
+        }
+        finally
+        {
+            writingEncryptedData = false;
+        }
+        
+        if( writeFuture != null )
+        {
+            return writeFuture;
+        }
+        else
+        {
+            return WriteFuture.newNotWrittenFuture();
+        }
+    }
+    
+    
+    private SSLEngineResult.Status unwrap() throws SSLException
+    {
+        if( SessionLog.isDebugEnabled( session ) )
+        {
+            SessionLog.debug( session, " unwrap()" );
+        }
+        // Prepare the application buffer to receive decrypted data
+        appBuffer.clear();
+
+        // Prepare the net data for reading.
+        inNetBuffer.flip();
+
+        SSLEngineResult res;
+        do
+        {
+            if( SessionLog.isDebugEnabled( session ) )
+            {
+                SessionLog.debug( session, "   inNetBuffer: " + inNetBuffer );
+                SessionLog.debug( session, "   appBuffer: " + appBuffer );
+            }
+            res = sslEngine.unwrap( inNetBuffer, appBuffer );
+            if( SessionLog.isDebugEnabled( session ) )
+            {
+                SessionLog.debug( session, " Unwrap res:" + res );
+            }
+        }
+        while( res.getStatus() == SSLEngineResult.Status.OK );
+
+        // prepare to be written again
+        inNetBuffer.compact();
+        // prepare app data to be read
+        appBuffer.flip();
+
+        /*
+         * The status may be:
+         * OK - Normal operation
+         * OVERFLOW - Should never happen since the application buffer is
+         *      sized to hold the maximum packet size.
+         * UNDERFLOW - Need to read more data from the socket. It's normal.
+         * CLOSED - The other peer closed the socket. Also normal.
+         */
+        return checkStatus( res.getStatus() );
+    }
+
+    private SSLEngineResult.Status unwrapHandshake() throws SSLException
+    {
+        if( SessionLog.isDebugEnabled( session ) )
+        {
+            SessionLog.debug( session, " unwrapHandshake()" );
+        }
+        // Prepare the application buffer to receive decrypted data
+        appBuffer.clear();
+
+        // Prepare the net data for reading.
+        inNetBuffer.flip();
+
+        SSLEngineResult res;
+        do
+        {
+            if( SessionLog.isDebugEnabled( session ) )
+            {
+                SessionLog.debug( session, "   inNetBuffer: " + inNetBuffer );
+                SessionLog.debug( session, "   appBuffer: " + appBuffer );
+            }
+            res = sslEngine.unwrap( inNetBuffer, appBuffer );
+            if( SessionLog.isDebugEnabled( session ) )
+            {
+                SessionLog.debug( session, " Unwrap res:" + res );
+            }
+
+        }
+        while( res.getStatus() == SSLEngineResult.Status.OK &&
+               res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP );
+
+        initialHandshakeStatus = res.getHandshakeStatus();
+	
+	    // If handshake finished, no data was produced, and the status is still ok,
+		// try to unwrap more
+		if (initialHandshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED
+				&& appBuffer.position() == 0
+				&& res.getStatus() == SSLEngineResult.Status.OK
+				&& inNetBuffer.hasRemaining()) {
+			do {
+				if (SessionLog.isDebugEnabled( session )) {
+					SessionLog.debug( session, "  extra handshake unwrap" );
+                    SessionLog.debug( session, "   inNetBuffer: " + inNetBuffer );
+                    SessionLog.debug( session, "   appBuffer: " + appBuffer );
+				}
+				res = sslEngine.unwrap(inNetBuffer, appBuffer);
+				if (SessionLog.isDebugEnabled( session )) {
+                    SessionLog.debug( session, " Unwrap res:" + res );
+				}
+			} while (res.getStatus() == SSLEngineResult.Status.OK);
+		}
+
+        // prepare to be written again
+        inNetBuffer.compact();
+
+        // prepare app data to be read
+        appBuffer.flip();
+
+        /*
+         * The status may be:
+         * OK - Normal operation
+         * OVERFLOW - Should never happen since the application buffer is
+         *      sized to hold the maximum packet size.
+         * UNDERFLOW - Need to read more data from the socket. It's normal.
+         * CLOSED - The other peer closed the socket. Also normal.
+         */
+        //initialHandshakeStatus = res.getHandshakeStatus();
+        return checkStatus( res.getStatus() );
+    }
+
+    /**
+     * Do all the outstanding handshake tasks in the current Thread.
+     */
+    private SSLEngineResult.HandshakeStatus doTasks()
+    {
+        if( SessionLog.isDebugEnabled( session ) )
+        {
+            SessionLog.debug( session, "   doTasks()" );
+        }
+
+        /*
+         * We could run this in a separate thread, but I don't see the need
+         * for this when used from SSLFilter. Use thread filters in MINA instead?
+         */
+        Runnable runnable;
+        while( ( runnable = sslEngine.getDelegatedTask() ) != null )
+        {
+            if( SessionLog.isDebugEnabled( session ) )
+            {
+                SessionLog.debug( session, "    doTask: " + runnable );
+            }
+            runnable.run();
+        }
+        if( SessionLog.isDebugEnabled( session ) )
+        {
+            SessionLog.debug( session, "   doTasks(): "
+                    + sslEngine.getHandshakeStatus() );
+        }
+        return sslEngine.getHandshakeStatus();
+    }
+
+    private static class ScheduledWrite
+    {
+        private final NextFilter nextFilter;
+        private final WriteRequest writeRequest;
+        
+        public ScheduledWrite( NextFilter nextFilter, WriteRequest writeRequest )
+        {
+            this.nextFilter = nextFilter;
+            this.writeRequest = writeRequest;
+        }
+    }
+    
+    /**
+     * Creates a new Mina byte buffer that is a deep copy of the remaining bytes
+     * in the given buffer (between index buf.position() and buf.limit())
+     *
+     * @param src the buffer to copy
+     * @return the new buffer, ready to read from
+     */
+    public static org.apache.mina.common.ByteBuffer copy( java.nio.ByteBuffer src )
+    {
+        org.apache.mina.common.ByteBuffer copy = org.apache.mina.common.ByteBuffer.allocate( src.remaining() );
+        copy.put( src );
+        copy.flip();
+        return copy;
+    }
+}

Modified: directory/network/trunk/src/java/org/apache/mina/handler/StreamIoHandler.java
URL: http://svn.apache.org/viewcvs/directory/network/trunk/src/java/org/apache/mina/handler/StreamIoHandler.java?rev=350169&r1=350168&r2=350169&view=diff
==============================================================================
--- directory/network/trunk/src/java/org/apache/mina/handler/StreamIoHandler.java (original)
+++ directory/network/trunk/src/java/org/apache/mina/handler/StreamIoHandler.java Wed Nov 30 21:17:41 2005
@@ -1,188 +1,188 @@
- /*
- *   @(#) $Id$
- *
- *   Copyright 2004 The Apache Software Foundation
- *
- *   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.
- *
- */
-package org.apache.mina.handler;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.SocketTimeoutException;
-
-import org.apache.mina.common.ByteBuffer;
-import org.apache.mina.common.IdleStatus;
-import org.apache.mina.common.IoHandler;
-import org.apache.mina.common.IoHandlerAdapter;
-import org.apache.mina.common.IoSession;
-import org.apache.mina.handler.support.IoSessionInputStream;
-import org.apache.mina.handler.support.IoSessionOutputStream;
-import org.apache.mina.util.SessionLog;
-
-/**
- * A {@link IoHandler} that adapts asynchronous MINA events to stream I/O.
- * <p>
- * Please extend this class and implement
- * {@link #processStreamIo(IoSession, InputStream, OutputStream)} to
- * execute your stream I/O logic; <b>please note that you must forward
- * the process request to other thread or thread pool.</b>
- * 
- * @author The Apache Directory Project (dev@directory.apache.org)
- * @version $Rev$, $Date$
- */
-public abstract class StreamIoHandler extends IoHandlerAdapter
-{
-    private static final String KEY_IN = StreamIoHandler.class.getName() + ".in";
-    private static final String KEY_OUT = StreamIoHandler.class.getName() + ".out";
-    
-    private int readTimeout;
-    private int writeTimeout;
-    
-    protected StreamIoHandler()
-    {
-    }
-    
-    /**
-     * Implement this method to execute your stream I/O logic;
-     * <b>please note that you must forward the process request to other
-     * thread or thread pool.</b>
-     */
-    protected abstract void processStreamIo( IoSession session,
-                                             InputStream in, OutputStream out );
-    
-    /**
-     * Returns read timeout in seconds.
-     * The default value is <tt>0</tt> (disabled).
-     */
-    public int getReadTimeout()
-    {
-        return readTimeout;
-    }
-    
-    /**
-     * Sets read timeout in seconds.
-     * The default value is <tt>0</tt> (disabled).
-     */
-    public void setReadTimeout( int readTimeout )
-    {
-        this.readTimeout = readTimeout;
-    }
-    
-    /**
-     * Returns write timeout in seconds.
-     * The default value is <tt>0</tt> (disabled).
-     */
-    public int getWriteTimeout()
-    {
-        return writeTimeout;
-    }
-    
-    /**
-     * Sets write timeout in seconds.
-     * The default value is <tt>0</tt> (disabled).
-     */
-    public void setWriteTimeout( int writeTimeout )
-    {
-        this.writeTimeout = writeTimeout;
-    }
-
-    /**
-     * Initializes streams and timeout settings.
-     */
-    public void sessionOpened( IoSession session )
-    {
-        // Set timeouts
-        session.setWriteTimeout( writeTimeout );
-        session.setIdleTime( IdleStatus.READER_IDLE, readTimeout );
-
-        // Create streams
-        InputStream in = new IoSessionInputStream();
-        OutputStream out = new IoSessionOutputStream( session );
-        session.setAttribute( KEY_IN, in );
-        session.setAttribute( KEY_OUT, out );
-        processStreamIo( session, in, out );
-    }
-    
-    /**
-     * Closes input stream.
-     */
-    public void sessionClosed( IoSession session )
-    {
-        IoSessionInputStream in = ( IoSessionInputStream ) session.getAttribute( KEY_IN );
-        IoSessionOutputStream out = ( IoSessionOutputStream ) session.getAttribute( KEY_OUT );
-        in.close();
-        out.close();
-    }
-
-    /**
-     * Forwards read data to input stream.
-     */
-    public void messageReceived( IoSession session, Object buf )
-    {
-        final IoSessionInputStream in = ( IoSessionInputStream ) session.getAttribute( KEY_IN );
-        in.write( ( ByteBuffer ) buf );
-    }
-
-    /**
-     * Forwards caught exceptions to input stream.
-     */
-    public void exceptionCaught( IoSession session, Throwable cause )
-    {
-        final IoSessionInputStream in = ( IoSessionInputStream ) session.getAttribute( KEY_IN );
-        
-        IOException e = null;
-        if( cause instanceof StreamIoException )
-        {
-            e = ( IOException ) cause.getCause();
-        }
-        else if( cause instanceof IOException )
-        {
-            e = ( IOException ) cause;
-        }
-        
-        if( e != null && in != null )
-        {
-            in.throwException( e );
-        }
-        else
-        {
-            SessionLog.warn( session, "Unexpected exception.", cause );
-            session.close();
-        }
-    }
-
-    /**
-     * Handles read timeout.
-     */
-    public void sessionIdle( IoSession session, IdleStatus status )
-    {
-        if( status == IdleStatus.READER_IDLE )
-        {
-            throw new StreamIoException(
-                    new SocketTimeoutException( "Read timeout" ) );
-        }
-    }
-
-    private static class StreamIoException extends RuntimeException
-    {
-        private static final long serialVersionUID = 3976736960742503222L;
-
-        public StreamIoException( IOException cause )
-        {
-            super(cause);
-        }
-    }
-}
+/*
+ *   @(#) $Id$
+ *
+ *   Copyright 2004 The Apache Software Foundation
+ *
+ *   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.
+ *
+ */
+package org.apache.mina.handler;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.SocketTimeoutException;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoHandlerAdapter;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.handler.support.IoSessionInputStream;
+import org.apache.mina.handler.support.IoSessionOutputStream;
+import org.apache.mina.util.SessionLog;
+
+/**
+ * A {@link IoHandler} that adapts asynchronous MINA events to stream I/O.
+ * <p>
+ * Please extend this class and implement
+ * {@link #processStreamIo(IoSession, InputStream, OutputStream)} to
+ * execute your stream I/O logic; <b>please note that you must forward
+ * the process request to other thread or thread pool.</b>
+ * 
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev$, $Date$
+ */
+public abstract class StreamIoHandler extends IoHandlerAdapter
+{
+    private static final String KEY_IN = StreamIoHandler.class.getName() + ".in";
+    private static final String KEY_OUT = StreamIoHandler.class.getName() + ".out";
+    
+    private int readTimeout;
+    private int writeTimeout;
+    
+    protected StreamIoHandler()
+    {
+    }
+    
+    /**
+     * Implement this method to execute your stream I/O logic;
+     * <b>please note that you must forward the process request to other
+     * thread or thread pool.</b>
+     */
+    protected abstract void processStreamIo( IoSession session,
+                                             InputStream in, OutputStream out );
+    
+    /**
+     * Returns read timeout in seconds.
+     * The default value is <tt>0</tt> (disabled).
+     */
+    public int getReadTimeout()
+    {
+        return readTimeout;
+    }
+    
+    /**
+     * Sets read timeout in seconds.
+     * The default value is <tt>0</tt> (disabled).
+     */
+    public void setReadTimeout( int readTimeout )
+    {
+        this.readTimeout = readTimeout;
+    }
+    
+    /**
+     * Returns write timeout in seconds.
+     * The default value is <tt>0</tt> (disabled).
+     */
+    public int getWriteTimeout()
+    {
+        return writeTimeout;
+    }
+    
+    /**
+     * Sets write timeout in seconds.
+     * The default value is <tt>0</tt> (disabled).
+     */
+    public void setWriteTimeout( int writeTimeout )
+    {
+        this.writeTimeout = writeTimeout;
+    }
+
+    /**
+     * Initializes streams and timeout settings.
+     */
+    public void sessionOpened( IoSession session )
+    {
+        // Set timeouts
+        session.setWriteTimeout( writeTimeout );
+        session.setIdleTime( IdleStatus.READER_IDLE, readTimeout );
+
+        // Create streams
+        InputStream in = new IoSessionInputStream();
+        OutputStream out = new IoSessionOutputStream( session );
+        session.setAttribute( KEY_IN, in );
+        session.setAttribute( KEY_OUT, out );
+        processStreamIo( session, in, out );
+    }
+    
+    /**
+     * Closes input stream.
+     */
+    public void sessionClosed( IoSession session )
+    {
+        IoSessionInputStream in = ( IoSessionInputStream ) session.getAttribute( KEY_IN );
+        IoSessionOutputStream out = ( IoSessionOutputStream ) session.getAttribute( KEY_OUT );
+        in.close();
+        out.close();
+    }
+
+    /**
+     * Forwards read data to input stream.
+     */
+    public void messageReceived( IoSession session, Object buf )
+    {
+        final IoSessionInputStream in = ( IoSessionInputStream ) session.getAttribute( KEY_IN );
+        in.write( ( ByteBuffer ) buf );
+    }
+
+    /**
+     * Forwards caught exceptions to input stream.
+     */
+    public void exceptionCaught( IoSession session, Throwable cause )
+    {
+        final IoSessionInputStream in = ( IoSessionInputStream ) session.getAttribute( KEY_IN );
+        
+        IOException e = null;
+        if( cause instanceof StreamIoException )
+        {
+            e = ( IOException ) cause.getCause();
+        }
+        else if( cause instanceof IOException )
+        {
+            e = ( IOException ) cause;
+        }
+        
+        if( e != null && in != null )
+        {
+            in.throwException( e );
+        }
+        else
+        {
+            SessionLog.warn( session, "Unexpected exception.", cause );
+            session.close();
+        }
+    }
+
+    /**
+     * Handles read timeout.
+     */
+    public void sessionIdle( IoSession session, IdleStatus status )
+    {
+        if( status == IdleStatus.READER_IDLE )
+        {
+            throw new StreamIoException(
+                    new SocketTimeoutException( "Read timeout" ) );
+        }
+    }
+
+    private static class StreamIoException extends RuntimeException
+    {
+        private static final long serialVersionUID = 3976736960742503222L;
+
+        public StreamIoException( IOException cause )
+        {
+            super(cause);
+        }
+    }
+}

Modified: directory/network/trunk/src/java/org/apache/mina/handler/support/IoSessionInputStream.java
URL: http://svn.apache.org/viewcvs/directory/network/trunk/src/java/org/apache/mina/handler/support/IoSessionInputStream.java?rev=350169&r1=350168&r2=350169&view=diff
==============================================================================
--- directory/network/trunk/src/java/org/apache/mina/handler/support/IoSessionInputStream.java (original)
+++ directory/network/trunk/src/java/org/apache/mina/handler/support/IoSessionInputStream.java Wed Nov 30 21:17:41 2005
@@ -1,204 +1,204 @@
-/*
- *   @(#) $Id$
- *
- *   Copyright 2004 The Apache Software Foundation
- *
- *   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.
- *
- */
-package org.apache.mina.handler.support;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.apache.mina.common.ByteBuffer;
-import org.apache.mina.common.IoHandler;
-import org.apache.mina.common.IoSession;
-
-/**
- * An {@link InputStream} that buffers data read from
- * {@link IoHandler#messageReceived(IoSession, Object)} events.
- *
- * @author The Apache Directory Project (dev@directory.apache.org)
- * @version $Rev$, $Date$
- *
- */
-public class IoSessionInputStream extends InputStream
-{
-    private final ByteBuffer buf;
-    private boolean closed;
-    private boolean released;
-    private IOException exception;
-    private int waiters;
-    
-    public IoSessionInputStream()
-    {
-        buf = ByteBuffer.allocate( 16 );
-        buf.setAutoExpand( true );
-        buf.limit( 0 );
-    }
-
-    public synchronized int available()
-    {
-        if( released )
-        {
-            return 0;
-        }
-        else
-        {
-            return buf.remaining();
-        }
-    }
-
-    public synchronized void close()
-    {
-        if( closed )
-        {
-            return;
-        }
-
-        closed = true;
-        releaseBuffer();
-        
-        if( waiters != 0 )
-        {
-            this.notifyAll();
-        }
-    }
-
-    public void mark( int readlimit )
-    {
-    }
-
-    public boolean markSupported()
-    {
-        return false;
-    }
-
-    public synchronized int read() throws IOException
-    {
-        waitForData();
-        if( released )
-        {
-            return -1;
-        }
-
-        int ret = buf.get() & 0xff;
-        return ret;
-    }
-
-    public synchronized int read( byte[] b, int off, int len ) throws IOException
-    {
-        waitForData();
-        if( released )
-        {
-            return -1;
-        }
-
-        int readBytes;
-        if( len > buf.remaining() )
-        {
-            readBytes = buf.remaining();
-        }
-        else
-        {
-            readBytes = len;
-        }
-        buf.get( b, off, readBytes );
-        
-        return readBytes;
-    }
-
-    public synchronized void reset() throws IOException
-    {
-        throw new IOException( "Mark is not supported." );
-    }
-
-    private void waitForData() throws IOException
-    {
-        if( released )
-        {
-            throw new IOException( "Stream is closed." );
-        }
-
-        waiters ++;
-        while( !released && buf.remaining() == 0 && exception == null )
-        {
-            try
-            {
-                this.wait();
-            }
-            catch( InterruptedException e )
-            {
-            }
-        }
-        waiters --;
-        
-        if( exception != null )
-        {
-            releaseBuffer();
-            throw exception;
-        }
-        
-        if( closed && buf.remaining() == 0 )
-        {
-            releaseBuffer();
-        }
-    }
-    
-    private void releaseBuffer()
-    {
-        if( released )
-        {
-            return;
-        }
-
-        released = true;
-        buf.release();
-    }
-
-    public synchronized void write( ByteBuffer src )
-    {
-        if( closed )
-        {
-            return;
-        }
-        
-        if( buf.hasRemaining() )
-        {
-            this.buf.compact();
-            this.buf.put( src );
-            this.buf.flip();
-        }
-        else
-        {
-            this.buf.clear();
-            this.buf.put( src );
-            this.buf.flip();
-            this.notify();
-        }
-    }
-    
-    public synchronized void throwException( IOException e )
-    {
-        if( exception == null )
-        {
-            exception = e;
-            
-            if( waiters != 0 )
-            {
-                this.notifyAll();
-            }
-        }
-    }
+/*
+ *   @(#) $Id$
+ *
+ *   Copyright 2004 The Apache Software Foundation
+ *
+ *   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.
+ *
+ */
+package org.apache.mina.handler.support;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoSession;
+
+/**
+ * An {@link InputStream} that buffers data read from
+ * {@link IoHandler#messageReceived(IoSession, Object)} events.
+ *
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev$, $Date$
+ *
+ */
+public class IoSessionInputStream extends InputStream
+{
+    private final ByteBuffer buf;
+    private boolean closed;
+    private boolean released;
+    private IOException exception;
+    private int waiters;
+    
+    public IoSessionInputStream()
+    {
+        buf = ByteBuffer.allocate( 16 );
+        buf.setAutoExpand( true );
+        buf.limit( 0 );
+    }
+
+    public synchronized int available()
+    {
+        if( released )
+        {
+            return 0;
+        }
+        else
+        {
+            return buf.remaining();
+        }
+    }
+
+    public synchronized void close()
+    {
+        if( closed )
+        {
+            return;
+        }
+
+        closed = true;
+        releaseBuffer();
+        
+        if( waiters != 0 )
+        {
+            this.notifyAll();
+        }
+    }
+
+    public void mark( int readlimit )
+    {
+    }
+
+    public boolean markSupported()
+    {
+        return false;
+    }
+
+    public synchronized int read() throws IOException
+    {
+        waitForData();
+        if( released )
+        {
+            return -1;
+        }
+
+        int ret = buf.get() & 0xff;
+        return ret;
+    }
+
+    public synchronized int read( byte[] b, int off, int len ) throws IOException
+    {
+        waitForData();
+        if( released )
+        {
+            return -1;
+        }
+
+        int readBytes;
+        if( len > buf.remaining() )
+        {
+            readBytes = buf.remaining();
+        }
+        else
+        {
+            readBytes = len;
+        }
+        buf.get( b, off, readBytes );
+        
+        return readBytes;
+    }
+
+    public synchronized void reset() throws IOException
+    {
+        throw new IOException( "Mark is not supported." );
+    }
+
+    private void waitForData() throws IOException
+    {
+        if( released )
+        {
+            throw new IOException( "Stream is closed." );
+        }
+
+        waiters ++;
+        while( !released && buf.remaining() == 0 && exception == null )
+        {
+            try
+            {
+                this.wait();
+            }
+            catch( InterruptedException e )
+            {
+            }
+        }
+        waiters --;
+        
+        if( exception != null )
+        {
+            releaseBuffer();
+            throw exception;
+        }
+        
+        if( closed && buf.remaining() == 0 )
+        {
+            releaseBuffer();
+        }
+    }
+    
+    private void releaseBuffer()
+    {
+        if( released )
+        {
+            return;
+        }
+
+        released = true;
+        buf.release();
+    }
+
+    public synchronized void write( ByteBuffer src )
+    {
+        if( closed )
+        {
+            return;
+        }
+        
+        if( buf.hasRemaining() )
+        {
+            this.buf.compact();
+            this.buf.put( src );
+            this.buf.flip();
+        }
+        else
+        {
+            this.buf.clear();
+            this.buf.put( src );
+            this.buf.flip();
+            this.notify();
+        }
+    }
+    
+    public synchronized void throwException( IOException e )
+    {
+        if( exception == null )
+        {
+            exception = e;
+            
+            if( waiters != 0 )
+            {
+                this.notifyAll();
+            }
+        }
+    }
 }

Modified: directory/network/trunk/src/java/org/apache/mina/handler/support/IoSessionOutputStream.java
URL: http://svn.apache.org/viewcvs/directory/network/trunk/src/java/org/apache/mina/handler/support/IoSessionOutputStream.java?rev=350169&r1=350168&r2=350169&view=diff
==============================================================================
--- directory/network/trunk/src/java/org/apache/mina/handler/support/IoSessionOutputStream.java (original)
+++ directory/network/trunk/src/java/org/apache/mina/handler/support/IoSessionOutputStream.java Wed Nov 30 21:17:41 2005
@@ -1,73 +1,73 @@
-/*
- *   @(#) $Id$
- *
- *   Copyright 2004 The Apache Software Foundation
- *
- *   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.
- *
- */
-package org.apache.mina.handler.support;
-
-import java.io.OutputStream;
-
-import org.apache.mina.common.ByteBuffer;
-import org.apache.mina.common.IoSession;
-
-/**
- * An {@link OutputStream} that forwards all write operations to
- * the associated {@link IoSession}.
- *
- * @author The Apache Directory Project (dev@directory.apache.org)
- * @version $Rev$, $Date$
- *
- */
-public class IoSessionOutputStream extends OutputStream
-{
-    private final IoSession session;
-    
-    public IoSessionOutputStream( IoSession session )
-    {
-        this.session = session;
-    }
-
-    public void close()
-    {
-        session.close().join();
-    }
-
-    public void flush()
-    {
-    }
-
-    public void write( byte[] b, int off, int len )
-    {
-        ByteBuffer buf = ByteBuffer.wrap( b, off, len );
-        buf.acquire(); // prevent from being pooled.
-        session.write( buf );
-    }
-
-    public void write( byte[] b )
-    {
-        ByteBuffer buf = ByteBuffer.wrap( b );
-        buf.acquire(); // prevent from being pooled.
-        session.write( buf );
-    }
-
-    public void write( int b )
-    {
-        ByteBuffer buf = ByteBuffer.allocate( 1 );
-        buf.put( ( byte ) b );
-        buf.flip();
-        session.write( buf );
-    }
-}
+/*
+ *   @(#) $Id$
+ *
+ *   Copyright 2004 The Apache Software Foundation
+ *
+ *   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.
+ *
+ */
+package org.apache.mina.handler.support;
+
+import java.io.OutputStream;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+
+/**
+ * An {@link OutputStream} that forwards all write operations to
+ * the associated {@link IoSession}.
+ *
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev$, $Date$
+ *
+ */
+public class IoSessionOutputStream extends OutputStream
+{
+    private final IoSession session;
+    
+    public IoSessionOutputStream( IoSession session )
+    {
+        this.session = session;
+    }
+
+    public void close()
+    {
+        session.close().join();
+    }
+
+    public void flush()
+    {
+    }
+
+    public void write( byte[] b, int off, int len )
+    {
+        ByteBuffer buf = ByteBuffer.wrap( b, off, len );
+        buf.acquire(); // prevent from being pooled.
+        session.write( buf );
+    }
+
+    public void write( byte[] b )
+    {
+        ByteBuffer buf = ByteBuffer.wrap( b );
+        buf.acquire(); // prevent from being pooled.
+        session.write( buf );
+    }
+
+    public void write( int b )
+    {
+        ByteBuffer buf = ByteBuffer.allocate( 1 );
+        buf.put( ( byte ) b );
+        buf.flip();
+        session.write( buf );
+    }
+}

Modified: directory/network/trunk/src/java/org/apache/mina/integration/spring/support/AbstractIoSessionManagerFactoryBean.java
URL: http://svn.apache.org/viewcvs/directory/network/trunk/src/java/org/apache/mina/integration/spring/support/AbstractIoSessionManagerFactoryBean.java?rev=350169&r1=350168&r2=350169&view=diff
==============================================================================
--- directory/network/trunk/src/java/org/apache/mina/integration/spring/support/AbstractIoSessionManagerFactoryBean.java (original)
+++ directory/network/trunk/src/java/org/apache/mina/integration/spring/support/AbstractIoSessionManagerFactoryBean.java Wed Nov 30 21:17:41 2005
@@ -18,9 +18,9 @@
  */
 package org.apache.mina.integration.spring.support;
 
+import org.apache.mina.common.DefaultIoFilterChainBuilder;
 import org.apache.mina.common.ExceptionMonitor;
 import org.apache.mina.common.IoFilter;
-import org.apache.mina.common.IoFilterChain;
 import org.apache.mina.common.IoSessionManager;
 import org.apache.mina.integration.spring.IoFilterMapping;
 import org.springframework.beans.factory.config.AbstractFactoryBean;
@@ -72,10 +72,10 @@
         /*
          * Add filters to the end of the filter chain.
          */
-        IoFilterChain filterChain = sessionManager.getFilterChain();
+        DefaultIoFilterChainBuilder builder = sessionManager.getFilterChain();
         for( int i = 0; i < filterMappings.length; i++ )
         {
-            filterChain.addLast( filterMappings[ i ].getName(),
+            builder.addLast( filterMappings[ i ].getName(),
                     filterMappings[ i ].getFilter() );
         }
     }
@@ -94,8 +94,7 @@
         /*
          * Remove all filters.
          */
-        IoFilterChain filterChain = sessionManager.getFilterChain();
-        filterChain.clear();
+        sessionManager.getFilterChain().clear();
     }
 
     /**