You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by jo...@apache.org on 2021/07/24 15:20:40 UTC

[mina] 01/04: formatted mina.filter.ssl

This is an automated email from the ASF dual-hosted git repository.

johnnyv pushed a commit to branch bugfix/DIRMINA1132
in repository https://gitbox.apache.org/repos/asf/mina.git

commit aa500a68ab8f3b94ae5510d6b5da21da83287b23
Author: Jonathan Valliere <jo...@apache.org>
AuthorDate: Wed Sep 23 13:43:42 2020 -0400

    formatted mina.filter.ssl
---
 .../mina/filter/ssl/BogusTrustManagerFactory.java  |  112 +-
 .../apache/mina/filter/ssl/KeyStoreFactory.java    |  292 ++--
 .../apache/mina/filter/ssl/SslContextFactory.java  |  782 +++++-----
 .../java/org/apache/mina/filter/ssl/SslEvent.java  |    6 +-
 .../java/org/apache/mina/filter/ssl/SslFilter.java | 1643 ++++++++++----------
 .../org/apache/mina/filter/ssl/SslHandler.java     | 1615 +++++++++----------
 .../org/apache/mina/filter/ssl/package-info.java   |    3 +-
 7 files changed, 2233 insertions(+), 2220 deletions(-)

diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/BogusTrustManagerFactory.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/BogusTrustManagerFactory.java
index 5984e69..e0402c2 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/BogusTrustManagerFactory.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/BogusTrustManagerFactory.java
@@ -39,68 +39,68 @@ import javax.net.ssl.X509TrustManager;
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  */
 public class BogusTrustManagerFactory extends TrustManagerFactory {
-    private static final X509TrustManager X509 = new X509TrustManager() {
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
-            // Do nothing
-        }
+	private static final X509TrustManager X509 = new X509TrustManager() {
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
+			// Do nothing
+		}
 
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
-            // Do nothing
-        }
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
+			// Do nothing
+		}
 
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public X509Certificate[] getAcceptedIssuers() {
-            return new X509Certificate[0];
-        }
-    };
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public X509Certificate[] getAcceptedIssuers() {
+			return new X509Certificate[0];
+		}
+	};
 
-    private static final TrustManager[] X509_MANAGERS = new TrustManager[] { X509 };
+	private static final TrustManager[] X509_MANAGERS = new TrustManager[] { X509 };
 
-    /**
-     * Creates a new BogusTrustManagerFactory instance
-     */
-    public BogusTrustManagerFactory() {
-        super(new BogusTrustManagerFactorySpi(), new Provider("MinaBogus", 1.0, "") {
-            private static final long serialVersionUID = -4024169055312053827L;
-        }, "MinaBogus");
-    }
+	/**
+	 * Creates a new BogusTrustManagerFactory instance
+	 */
+	public BogusTrustManagerFactory() {
+		super(new BogusTrustManagerFactorySpi(), new Provider("MinaBogus", 1.0, "") {
+			private static final long serialVersionUID = -4024169055312053827L;
+		}, "MinaBogus");
+	}
 
-    private static class BogusTrustManagerFactorySpi extends TrustManagerFactorySpi {
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        protected TrustManager[] engineGetTrustManagers() {
-            return X509_MANAGERS;
-        }
+	private static class BogusTrustManagerFactorySpi extends TrustManagerFactorySpi {
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		protected TrustManager[] engineGetTrustManagers() {
+			return X509_MANAGERS;
+		}
 
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        protected void engineInit(KeyStore keystore) throws KeyStoreException {
-            // noop
-        }
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		protected void engineInit(KeyStore keystore) throws KeyStoreException {
+			// noop
+		}
 
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        protected void engineInit(ManagerFactoryParameters managerFactoryParameters)
-                throws InvalidAlgorithmParameterException {
-            // noop
-        }
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		protected void engineInit(ManagerFactoryParameters managerFactoryParameters)
+				throws InvalidAlgorithmParameterException {
+			// noop
+		}
 
-    }
+	}
 }
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/KeyStoreFactory.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/KeyStoreFactory.java
index b0903fa..875f039 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/KeyStoreFactory.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/KeyStoreFactory.java
@@ -40,150 +40,150 @@ import java.security.cert.CertificateException;
  */
 public class KeyStoreFactory {
 
-    private String type = "JKS";
-
-    private String provider = null;
-
-    private char[] password = null;
-
-    private byte[] data = null;
-
-    /**
-     * Creates a new {@link KeyStore}. This method will be called
-     * by the base class when Spring creates a bean using this FactoryBean.
-     *
-     * @return a new {@link KeyStore} instance.
-     * @throws KeyStoreException If we can't create an instance of the KeyStore for the given type
-     * @throws NoSuchProviderException If we don't have the provider registered to create the KeyStore
-     * @throws NoSuchAlgorithmException If the KeyStore algorithm cannot be used
-     * @throws CertificateException If the KeyStore certificate cannot be loaded 
-     * @throws IOException If the KeyStore cannot be loaded
-     */
-    public KeyStore newInstance() throws KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException,
-            CertificateException, IOException {
-        if (data == null) {
-            throw new IllegalStateException("data property is not set.");
-        }
-
-        KeyStore ks;
-        if (provider == null) {
-            ks = KeyStore.getInstance(type);
-        } else {
-            ks = KeyStore.getInstance(type, provider);
-        }
-
-        InputStream is = new ByteArrayInputStream(data);
-        
-        try {
-            ks.load(is, password);
-        } finally {
-            try {
-                is.close();
-            } catch (IOException ignored) {
-                // Do nothing
-            }
-        }
-
-        return ks;
-    }
-
-    /**
-     * Sets the type of key store to create. The default is to create a
-     * JKS key store.
-     *
-     * @param type the type to use when creating the key store.
-     * @throws IllegalArgumentException if the specified value is
-     *         <code>null</code>.
-     */
-    public void setType(String type) {
-        if (type == null) {
-            throw new IllegalArgumentException("type");
-        }
-        this.type = type;
-    }
-
-    /**
-     * Sets the key store password. If this value is <code>null</code> no
-     * password will be used to check the integrity of the key store.
-     *
-     * @param password the password or <code>null</code> if no password is
-     *        needed.
-     */
-    public void setPassword(String password) {
-        if (password != null) {
-            this.password = password.toCharArray();
-        } else {
-            this.password = null;
-        }
-    }
-
-    /**
-     * Sets the name of the provider to use when creating the key store. The
-     * default is to use the platform default provider.
-     *
-     * @param provider the name of the provider, e.g. <tt>"SUN"</tt>.
-     */
-    public void setProvider(String provider) {
-        this.provider = provider;
-    }
-
-    /**
-     * Sets the data which contains the key store.
-     *
-     * @param data the byte array that contains the key store
-     */
-    public void setData(byte[] data) {
-        byte[] copy = new byte[data.length];
-        System.arraycopy(data, 0, copy, 0, data.length);
-        this.data = copy;
-    }
-
-    /**
-     * Sets the data which contains the key store.
-     *
-     * @param dataStream the {@link InputStream} that contains the key store
-     * @throws IOException If we can't process the stream
-     */
-    private void setData(InputStream dataStream) throws IOException {
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        try {
-            for (;;) {
-                int readByte = dataStream.read();
-                
-                if (readByte < 0) {
-                    break;
-                }
-                
-                out.write(readByte);
-            }
-            
-            setData(out.toByteArray());
-        } finally {
-            try {
-                dataStream.close();
-            } catch (IOException e) {
-                // Ignore.
-            }
-        }
-    }
-
-    /**
-     * Sets the data which contains the key store.
-     *
-     * @param dataFile the {@link File} that contains the key store
-     * @throws IOException If we can't process the file
-     */
-    public void setDataFile(File dataFile) throws IOException {
-        setData(new BufferedInputStream(new FileInputStream(dataFile)));
-    }
-
-    /**
-     * Sets the data which contains the key store.
-     *
-     * @param dataUrl the {@link URL} that contains the key store.
-     * @throws IOException If we can't process the URL
-     */
-    public void setDataUrl(URL dataUrl) throws IOException {
-        setData(dataUrl.openStream());
-    }
+	private String type = "JKS";
+
+	private String provider = null;
+
+	private char[] password = null;
+
+	private byte[] data = null;
+
+	/**
+	 * Creates a new {@link KeyStore}. This method will be called by the base class
+	 * when Spring creates a bean using this FactoryBean.
+	 *
+	 * @return a new {@link KeyStore} instance.
+	 * @throws KeyStoreException        If we can't create an instance of the
+	 *                                  KeyStore for the given type
+	 * @throws NoSuchProviderException  If we don't have the provider registered to
+	 *                                  create the KeyStore
+	 * @throws NoSuchAlgorithmException If the KeyStore algorithm cannot be used
+	 * @throws CertificateException     If the KeyStore certificate cannot be loaded
+	 * @throws IOException              If the KeyStore cannot be loaded
+	 */
+	public KeyStore newInstance() throws KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException,
+			CertificateException, IOException {
+		if (data == null) {
+			throw new IllegalStateException("data property is not set.");
+		}
+
+		KeyStore ks;
+		if (provider == null) {
+			ks = KeyStore.getInstance(type);
+		} else {
+			ks = KeyStore.getInstance(type, provider);
+		}
+
+		InputStream is = new ByteArrayInputStream(data);
+
+		try {
+			ks.load(is, password);
+		} finally {
+			try {
+				is.close();
+			} catch (IOException ignored) {
+				// Do nothing
+			}
+		}
+
+		return ks;
+	}
+
+	/**
+	 * Sets the type of key store to create. The default is to create a JKS key
+	 * store.
+	 *
+	 * @param type the type to use when creating the key store.
+	 * @throws IllegalArgumentException if the specified value is <code>null</code>.
+	 */
+	public void setType(String type) {
+		if (type == null) {
+			throw new IllegalArgumentException("type");
+		}
+		this.type = type;
+	}
+
+	/**
+	 * Sets the key store password. If this value is <code>null</code> no password
+	 * will be used to check the integrity of the key store.
+	 *
+	 * @param password the password or <code>null</code> if no password is needed.
+	 */
+	public void setPassword(String password) {
+		if (password != null) {
+			this.password = password.toCharArray();
+		} else {
+			this.password = null;
+		}
+	}
+
+	/**
+	 * Sets the name of the provider to use when creating the key store. The default
+	 * is to use the platform default provider.
+	 *
+	 * @param provider the name of the provider, e.g. <tt>"SUN"</tt>.
+	 */
+	public void setProvider(String provider) {
+		this.provider = provider;
+	}
+
+	/**
+	 * Sets the data which contains the key store.
+	 *
+	 * @param data the byte array that contains the key store
+	 */
+	public void setData(byte[] data) {
+		byte[] copy = new byte[data.length];
+		System.arraycopy(data, 0, copy, 0, data.length);
+		this.data = copy;
+	}
+
+	/**
+	 * Sets the data which contains the key store.
+	 *
+	 * @param dataStream the {@link InputStream} that contains the key store
+	 * @throws IOException If we can't process the stream
+	 */
+	private void setData(InputStream dataStream) throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		try {
+			for (;;) {
+				int readByte = dataStream.read();
+
+				if (readByte < 0) {
+					break;
+				}
+
+				out.write(readByte);
+			}
+
+			setData(out.toByteArray());
+		} finally {
+			try {
+				dataStream.close();
+			} catch (IOException e) {
+				// Ignore.
+			}
+		}
+	}
+
+	/**
+	 * Sets the data which contains the key store.
+	 *
+	 * @param dataFile the {@link File} that contains the key store
+	 * @throws IOException If we can't process the file
+	 */
+	public void setDataFile(File dataFile) throws IOException {
+		setData(new BufferedInputStream(new FileInputStream(dataFile)));
+	}
+
+	/**
+	 * Sets the data which contains the key store.
+	 *
+	 * @param dataUrl the {@link URL} that contains the key store.
+	 * @throws IOException If we can't process the URL
+	 */
+	public void setDataUrl(URL dataUrl) throws IOException {
+		setData(dataUrl.openStream());
+	}
 }
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslContextFactory.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslContextFactory.java
index e05d3d6..255eeb5 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslContextFactory.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslContextFactory.java
@@ -33,11 +33,12 @@ import javax.net.ssl.TrustManagerFactory;
 /**
  * A factory that creates and configures a new {@link SSLContext}.
  * <p>
- * If no properties are set the returned {@link SSLContext} will
- * be equivalent to what the following creates:
+ * If no properties are set the returned {@link SSLContext} will be equivalent
+ * to what the following creates:
+ * 
  * <pre>
- *      SSLContext c = SSLContext.getInstance( "TLSv1.2" );
- *      c.init(null, null, null);
+ * SSLContext c = SSLContext.getInstance("TLSv1.2");
+ * c.init(null, null, null);
  * </pre>
  * <p>
  * Use the properties prefixed with <code>keyManagerFactory</code> to control
@@ -50,388 +51,393 @@ import javax.net.ssl.TrustManagerFactory;
  */
 public class SslContextFactory {
 
-    private String provider = null;
-
-    private String protocol = "TLSv1.2";
-
-    private SecureRandom secureRandom = null;
-
-    private KeyStore keyManagerFactoryKeyStore = null;
-
-    private char[] keyManagerFactoryKeyStorePassword = null;
-
-    private KeyManagerFactory keyManagerFactory = null;
-
-    private String keyManagerFactoryAlgorithm = null;
-
-    private String keyManagerFactoryProvider = null;
-
-    private boolean keyManagerFactoryAlgorithmUseDefault = true;
-
-    private KeyStore trustManagerFactoryKeyStore = null;
-
-    private TrustManagerFactory trustManagerFactory = null;
-
-    private String trustManagerFactoryAlgorithm = null;
-
-    private String trustManagerFactoryProvider = null;
-
-    private boolean trustManagerFactoryAlgorithmUseDefault = true;
-
-    private ManagerFactoryParameters trustManagerFactoryParameters = null;
-
-    private int clientSessionCacheSize = -1;
-
-    private int clientSessionTimeout = -1;
-
-    private int serverSessionCacheSize = -1;
-
-    private int serverSessionTimeout = -1;
-
-    /**
-     * Create a new SSLContext instance,using the {@link KeyManagerFactory} and the
-     * {@link TrustManagerFactory}.
-     *  
-     * @return The created instance
-     * @throws Exception If we weren't able to create the SSLContext insyance
-     */
-    public SSLContext newInstance() throws Exception {
-        KeyManagerFactory kmf = this.keyManagerFactory;
-        TrustManagerFactory tmf = this.trustManagerFactory;
-
-        if (kmf == null) {
-            String algorithm = keyManagerFactoryAlgorithm;
-            
-            if (algorithm == null && keyManagerFactoryAlgorithmUseDefault) {
-                algorithm = KeyManagerFactory.getDefaultAlgorithm();
-            }
-            
-            if (algorithm != null) {
-                if (keyManagerFactoryProvider == null) {
-                    kmf = KeyManagerFactory.getInstance(algorithm);
-                } else {
-                    kmf = KeyManagerFactory.getInstance(algorithm, keyManagerFactoryProvider);
-                }
-            }
-        }
-
-        if (tmf == null) {
-            String algorithm = trustManagerFactoryAlgorithm;
-            
-            if (algorithm == null && trustManagerFactoryAlgorithmUseDefault) {
-                algorithm = TrustManagerFactory.getDefaultAlgorithm();
-            }
-            
-            if (algorithm != null) {
-                if (trustManagerFactoryProvider == null) {
-                    tmf = TrustManagerFactory.getInstance(algorithm);
-                } else {
-                    tmf = TrustManagerFactory.getInstance(algorithm, trustManagerFactoryProvider);
-                }
-            }
-        }
-
-        KeyManager[] keyManagers = null;
-        
-        if (kmf != null) {
-            kmf.init(keyManagerFactoryKeyStore, keyManagerFactoryKeyStorePassword);
-            keyManagers = kmf.getKeyManagers();
-        }
-        
-        TrustManager[] trustManagers = null;
-        
-        if (tmf != null) {
-            if (trustManagerFactoryParameters != null) {
-                tmf.init(trustManagerFactoryParameters);
-            } else {
-                tmf.init(trustManagerFactoryKeyStore);
-            }
-            
-            trustManagers = tmf.getTrustManagers();
-        }
-
-        SSLContext context;
-        
-        if (provider == null) {
-            context = SSLContext.getInstance(protocol);
-        } else {
-            context = SSLContext.getInstance(protocol, provider);
-        }
-
-        context.init(keyManagers, trustManagers, secureRandom);
-
-        if (clientSessionCacheSize >= 0) {
-            context.getClientSessionContext().setSessionCacheSize(clientSessionCacheSize);
-        }
-
-        if (clientSessionTimeout >= 0) {
-            context.getClientSessionContext().setSessionTimeout(clientSessionTimeout);
-        }
-
-        if (serverSessionCacheSize >= 0) {
-            context.getServerSessionContext().setSessionCacheSize(serverSessionCacheSize);
-        }
-
-        if (serverSessionTimeout >= 0) {
-            context.getServerSessionContext().setSessionTimeout(serverSessionTimeout);
-        }
-
-        return context;
-    }
-
-    /**
-     * Sets the provider of the new {@link SSLContext}. The default value is
-     * <tt>null</tt>, which means the default provider will be used.
-     * 
-     * @param provider the name of the {@link SSLContext} provider
-     */
-    public void setProvider(String provider) {
-        this.provider = provider;
-    }
-
-    /**
-     * Sets the protocol to use when creating the {@link SSLContext}. The
-     * default is <code>TLS</code>.
-     *
-     * @param protocol the name of the protocol.
-     */
-    public void setProtocol(String protocol) {
-        if (protocol == null) {
-            throw new IllegalArgumentException("protocol");
-        }
-        
-        this.protocol = protocol;
-    }
-
-    /**
-     * If this is set to <tt>true</tt> while no {@link KeyManagerFactory}
-     * has been set using {@link #setKeyManagerFactory(KeyManagerFactory)} and
-     * no algorithm has been set using
-     * {@link #setKeyManagerFactoryAlgorithm(String)} the default algorithm
-     * return by {@link KeyManagerFactory#getDefaultAlgorithm()} will be used.
-     * The default value of this property is <tt>true</tt>.
-     *
-     * @param useDefault <tt>true</tt> or <tt>false</tt>.
-     */
-    public void setKeyManagerFactoryAlgorithmUseDefault(boolean useDefault) {
-        this.keyManagerFactoryAlgorithmUseDefault = useDefault;
-    }
-
-    /**
-     * If this is set to <tt>true</tt> while no {@link TrustManagerFactory}
-     * has been set using {@link #setTrustManagerFactory(TrustManagerFactory)} and
-     * no algorithm has been set using
-     * {@link #setTrustManagerFactoryAlgorithm(String)} the default algorithm
-     * return by {@link TrustManagerFactory#getDefaultAlgorithm()} will be used.
-     * The default value of this property is <tt>true</tt>.
-     *
-     * @param useDefault <tt>true</tt> or <tt>false</tt>.
-     */
-    public void setTrustManagerFactoryAlgorithmUseDefault(boolean useDefault) {
-        this.trustManagerFactoryAlgorithmUseDefault = useDefault;
-    }
-
-    /**
-     * Sets the {@link KeyManagerFactory} to use. If this is set the properties
-     * which are used by this factory bean to create a {@link KeyManagerFactory}
-     * will all be ignored.
-     *
-     * @param factory the factory.
-     */
-    public void setKeyManagerFactory(KeyManagerFactory factory) {
-        this.keyManagerFactory = factory;
-    }
-
-    /**
-     * Sets the algorithm to use when creating the {@link KeyManagerFactory}
-     * using {@link KeyManagerFactory#getInstance(java.lang.String)} or
-     * {@link KeyManagerFactory#getInstance(java.lang.String, java.lang.String)}.
-     * <p>
-     * This property will be ignored if a {@link KeyManagerFactory} has been
-     * set directly using {@link #setKeyManagerFactory(KeyManagerFactory)}.
-     * <p>
-     * If this property isn't set while no {@link KeyManagerFactory} has been
-     * set using {@link #setKeyManagerFactory(KeyManagerFactory)} and
-     * {@link #setKeyManagerFactoryAlgorithmUseDefault(boolean)} has been set to
-     * <tt>true</tt> the value returned
-     * by {@link KeyManagerFactory#getDefaultAlgorithm()} will be used instead.
-     *
-     * @param algorithm the algorithm to use.
-     */
-    public void setKeyManagerFactoryAlgorithm(String algorithm) {
-        this.keyManagerFactoryAlgorithm = algorithm;
-    }
-
-    /**
-     * Sets the provider to use when creating the {@link KeyManagerFactory}
-     * using
-     * {@link KeyManagerFactory#getInstance(java.lang.String, java.lang.String)}.
-     * <p>
-     * This property will be ignored if a {@link KeyManagerFactory} has been
-     * set directly using {@link #setKeyManagerFactory(KeyManagerFactory)}.
-     * <p>
-     * If this property isn't set and no {@link KeyManagerFactory} has been set
-     * using {@link #setKeyManagerFactory(KeyManagerFactory)}
-     * {@link KeyManagerFactory#getInstance(java.lang.String)} will be used
-     * to create the {@link KeyManagerFactory}.
-     *
-     * @param provider the name of the provider.
-     */
-    public void setKeyManagerFactoryProvider(String provider) {
-        this.keyManagerFactoryProvider = provider;
-    }
-
-    /**
-     * Sets the {@link KeyStore} which will be used in the call to
-     * {@link KeyManagerFactory#init(java.security.KeyStore, char[])} when
-     * the {@link SSLContext} is created.
-     *
-     * @param keyStore the key store.
-     */
-    public void setKeyManagerFactoryKeyStore(KeyStore keyStore) {
-        this.keyManagerFactoryKeyStore = keyStore;
-    }
-
-    /**
-     * Sets the password which will be used in the call to
-     * {@link KeyManagerFactory#init(java.security.KeyStore, char[])} when
-     * the {@link SSLContext} is created.
-     *
-     * @param password the password. Use <code>null</code> to disable password.
-     */
-    public void setKeyManagerFactoryKeyStorePassword(String password) {
-        if (password != null) {
-            this.keyManagerFactoryKeyStorePassword = password.toCharArray();
-        } else {
-            this.keyManagerFactoryKeyStorePassword = null;
-        }
-    }
-
-    /**
-     * Sets the {@link TrustManagerFactory} to use. If this is set the
-     * properties which are used by this factory bean to create a
-     * {@link TrustManagerFactory} will all be ignored.
-     *
-     * @param factory
-     *            the factory.
-     */
-    public void setTrustManagerFactory(TrustManagerFactory factory) {
-        this.trustManagerFactory = factory;
-    }
-
-    /**
-     * Sets the algorithm to use when creating the {@link TrustManagerFactory}
-     * using {@link TrustManagerFactory#getInstance(java.lang.String)} or
-     * {@link TrustManagerFactory#getInstance(java.lang.String, java.lang.String)}.
-     * <p>
-     * This property will be ignored if a {@link TrustManagerFactory} has been
-     * set directly using {@link #setTrustManagerFactory(TrustManagerFactory)}.
-     * <p>
-     * If this property isn't set while no {@link TrustManagerFactory} has been
-     * set using {@link #setTrustManagerFactory(TrustManagerFactory)} and
-     * {@link #setTrustManagerFactoryAlgorithmUseDefault(boolean)} has been set to
-     * <tt>true</tt> the value returned
-     * by {@link TrustManagerFactory#getDefaultAlgorithm()} will be used instead.
-     *
-     * @param algorithm the algorithm to use.
-     */
-    public void setTrustManagerFactoryAlgorithm(String algorithm) {
-        this.trustManagerFactoryAlgorithm = algorithm;
-    }
-
-    /**
-     * Sets the {@link KeyStore} which will be used in the call to
-     * {@link TrustManagerFactory#init(java.security.KeyStore)} when
-     * the {@link SSLContext} is created.
-     * <p>
-     * This property will be ignored if {@link ManagerFactoryParameters} has been
-     * set directly using {@link #setTrustManagerFactoryParameters(ManagerFactoryParameters)}.
-     *
-     * @param keyStore the key store.
-     */
-    public void setTrustManagerFactoryKeyStore(KeyStore keyStore) {
-        this.trustManagerFactoryKeyStore = keyStore;
-    }
-
-    /**
-     * Sets the {@link ManagerFactoryParameters} which will be used in the call to
-     * {@link TrustManagerFactory#init(javax.net.ssl.ManagerFactoryParameters)} when
-     * the {@link SSLContext} is created.
-     *
-     * @param parameters describing provider-specific trust material.
-     */
-    public void setTrustManagerFactoryParameters(ManagerFactoryParameters parameters) {
-        this.trustManagerFactoryParameters = parameters;
-    }
-
-    /**
-     * Sets the provider to use when creating the {@link TrustManagerFactory}
-     * using
-     * {@link TrustManagerFactory#getInstance(java.lang.String, java.lang.String)}.
-     * <p>
-     * This property will be ignored if a {@link TrustManagerFactory} has been
-     * set directly using {@link #setTrustManagerFactory(TrustManagerFactory)}.
-     * <p>
-     * If this property isn't set and no {@link TrustManagerFactory} has been set
-     * using {@link #setTrustManagerFactory(TrustManagerFactory)}
-     * {@link TrustManagerFactory#getInstance(java.lang.String)} will be used
-     * to create the {@link TrustManagerFactory}.
-     *
-     * @param provider the name of the provider.
-     */
-    public void setTrustManagerFactoryProvider(String provider) {
-        this.trustManagerFactoryProvider = provider;
-    }
-
-    /**
-     * Sets the {@link SecureRandom} to use when initializing the
-     * {@link SSLContext}. The JVM's default will be used if this isn't set.
-     *
-     * @param secureRandom the {@link SecureRandom} or <code>null</code> if the
-     *        JVM's default should be used.
-     * @see SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], java.security.SecureRandom)
-     */
-    public void setSecureRandom(SecureRandom secureRandom) {
-        this.secureRandom = secureRandom;
-    }
-
-    /**
-     * Sets the SSLSession cache size for the {@link SSLSessionContext} for use in client mode.
-     *
-     * @param size the new session cache size limit; zero means there is no limit.
-     * @see SSLSessionContext#setSessionCacheSize(int size)
-     */
-    public void setClientSessionCacheSize(int size) {
-        this.clientSessionCacheSize = size;
-    }
-
-    /**
-     * Set the SSLSession timeout limit for the {@link SSLSessionContext} for use in client mode.
-     *
-     * @param seconds the new session timeout limit in seconds; zero means there is no limit.
-     * @see SSLSessionContext#setSessionTimeout(int seconds)
-     */
-    public void setClientSessionTimeout(int seconds) {
-        this.clientSessionTimeout = seconds;
-    }
-
-    /**
-     * Sets the SSLSession cache size for the {@link SSLSessionContext} for use in server mode.
-     *
-     * @param serverSessionCacheSize the new session cache size limit; zero means there is no limit.
-     * @see SSLSessionContext#setSessionCacheSize(int)
-     */
-    public void setServerSessionCacheSize(int serverSessionCacheSize) {
-        this.serverSessionCacheSize = serverSessionCacheSize;
-    }
-
-    /**
-     * Set the SSLSession timeout limit for the {@link SSLSessionContext} for use in server mode.
-     *
-     * @param serverSessionTimeout the new session timeout limit in seconds; zero means there is no limit.
-     * @see SSLSessionContext#setSessionTimeout(int)
-     */
-    public void setServerSessionTimeout(int serverSessionTimeout) {
-        this.serverSessionTimeout = serverSessionTimeout;
-    }
+	private String provider = null;
+
+	private String protocol = "TLSv1.2";
+
+	private SecureRandom secureRandom = null;
+
+	private KeyStore keyManagerFactoryKeyStore = null;
+
+	private char[] keyManagerFactoryKeyStorePassword = null;
+
+	private KeyManagerFactory keyManagerFactory = null;
+
+	private String keyManagerFactoryAlgorithm = null;
+
+	private String keyManagerFactoryProvider = null;
+
+	private boolean keyManagerFactoryAlgorithmUseDefault = true;
+
+	private KeyStore trustManagerFactoryKeyStore = null;
+
+	private TrustManagerFactory trustManagerFactory = null;
+
+	private String trustManagerFactoryAlgorithm = null;
+
+	private String trustManagerFactoryProvider = null;
+
+	private boolean trustManagerFactoryAlgorithmUseDefault = true;
+
+	private ManagerFactoryParameters trustManagerFactoryParameters = null;
+
+	private int clientSessionCacheSize = -1;
+
+	private int clientSessionTimeout = -1;
+
+	private int serverSessionCacheSize = -1;
+
+	private int serverSessionTimeout = -1;
+
+	/**
+	 * Create a new SSLContext instance,using the {@link KeyManagerFactory} and the
+	 * {@link TrustManagerFactory}.
+	 * 
+	 * @return The created instance
+	 * @throws Exception If we weren't able to create the SSLContext insyance
+	 */
+	public SSLContext newInstance() throws Exception {
+		KeyManagerFactory kmf = this.keyManagerFactory;
+		TrustManagerFactory tmf = this.trustManagerFactory;
+
+		if (kmf == null) {
+			String algorithm = keyManagerFactoryAlgorithm;
+
+			if (algorithm == null && keyManagerFactoryAlgorithmUseDefault) {
+				algorithm = KeyManagerFactory.getDefaultAlgorithm();
+			}
+
+			if (algorithm != null) {
+				if (keyManagerFactoryProvider == null) {
+					kmf = KeyManagerFactory.getInstance(algorithm);
+				} else {
+					kmf = KeyManagerFactory.getInstance(algorithm, keyManagerFactoryProvider);
+				}
+			}
+		}
+
+		if (tmf == null) {
+			String algorithm = trustManagerFactoryAlgorithm;
+
+			if (algorithm == null && trustManagerFactoryAlgorithmUseDefault) {
+				algorithm = TrustManagerFactory.getDefaultAlgorithm();
+			}
+
+			if (algorithm != null) {
+				if (trustManagerFactoryProvider == null) {
+					tmf = TrustManagerFactory.getInstance(algorithm);
+				} else {
+					tmf = TrustManagerFactory.getInstance(algorithm, trustManagerFactoryProvider);
+				}
+			}
+		}
+
+		KeyManager[] keyManagers = null;
+
+		if (kmf != null) {
+			kmf.init(keyManagerFactoryKeyStore, keyManagerFactoryKeyStorePassword);
+			keyManagers = kmf.getKeyManagers();
+		}
+
+		TrustManager[] trustManagers = null;
+
+		if (tmf != null) {
+			if (trustManagerFactoryParameters != null) {
+				tmf.init(trustManagerFactoryParameters);
+			} else {
+				tmf.init(trustManagerFactoryKeyStore);
+			}
+
+			trustManagers = tmf.getTrustManagers();
+		}
+
+		SSLContext context;
+
+		if (provider == null) {
+			context = SSLContext.getInstance(protocol);
+		} else {
+			context = SSLContext.getInstance(protocol, provider);
+		}
+
+		context.init(keyManagers, trustManagers, secureRandom);
+
+		if (clientSessionCacheSize >= 0) {
+			context.getClientSessionContext().setSessionCacheSize(clientSessionCacheSize);
+		}
+
+		if (clientSessionTimeout >= 0) {
+			context.getClientSessionContext().setSessionTimeout(clientSessionTimeout);
+		}
+
+		if (serverSessionCacheSize >= 0) {
+			context.getServerSessionContext().setSessionCacheSize(serverSessionCacheSize);
+		}
+
+		if (serverSessionTimeout >= 0) {
+			context.getServerSessionContext().setSessionTimeout(serverSessionTimeout);
+		}
+
+		return context;
+	}
+
+	/**
+	 * Sets the provider of the new {@link SSLContext}. The default value is
+	 * <tt>null</tt>, which means the default provider will be used.
+	 * 
+	 * @param provider the name of the {@link SSLContext} provider
+	 */
+	public void setProvider(String provider) {
+		this.provider = provider;
+	}
+
+	/**
+	 * Sets the protocol to use when creating the {@link SSLContext}. The default is
+	 * <code>TLS</code>.
+	 *
+	 * @param protocol the name of the protocol.
+	 */
+	public void setProtocol(String protocol) {
+		if (protocol == null) {
+			throw new IllegalArgumentException("protocol");
+		}
+
+		this.protocol = protocol;
+	}
+
+	/**
+	 * If this is set to <tt>true</tt> while no {@link KeyManagerFactory} has been
+	 * set using {@link #setKeyManagerFactory(KeyManagerFactory)} and no algorithm
+	 * has been set using {@link #setKeyManagerFactoryAlgorithm(String)} the default
+	 * algorithm return by {@link KeyManagerFactory#getDefaultAlgorithm()} will be
+	 * used. The default value of this property is <tt>true</tt>.
+	 *
+	 * @param useDefault <tt>true</tt> or <tt>false</tt>.
+	 */
+	public void setKeyManagerFactoryAlgorithmUseDefault(boolean useDefault) {
+		this.keyManagerFactoryAlgorithmUseDefault = useDefault;
+	}
+
+	/**
+	 * If this is set to <tt>true</tt> while no {@link TrustManagerFactory} has been
+	 * set using {@link #setTrustManagerFactory(TrustManagerFactory)} and no
+	 * algorithm has been set using {@link #setTrustManagerFactoryAlgorithm(String)}
+	 * the default algorithm return by
+	 * {@link TrustManagerFactory#getDefaultAlgorithm()} will be used. The default
+	 * value of this property is <tt>true</tt>.
+	 *
+	 * @param useDefault <tt>true</tt> or <tt>false</tt>.
+	 */
+	public void setTrustManagerFactoryAlgorithmUseDefault(boolean useDefault) {
+		this.trustManagerFactoryAlgorithmUseDefault = useDefault;
+	}
+
+	/**
+	 * Sets the {@link KeyManagerFactory} to use. If this is set the properties
+	 * which are used by this factory bean to create a {@link KeyManagerFactory}
+	 * will all be ignored.
+	 *
+	 * @param factory the factory.
+	 */
+	public void setKeyManagerFactory(KeyManagerFactory factory) {
+		this.keyManagerFactory = factory;
+	}
+
+	/**
+	 * Sets the algorithm to use when creating the {@link KeyManagerFactory} using
+	 * {@link KeyManagerFactory#getInstance(java.lang.String)} or
+	 * {@link KeyManagerFactory#getInstance(java.lang.String, java.lang.String)}.
+	 * <p>
+	 * This property will be ignored if a {@link KeyManagerFactory} has been set
+	 * directly using {@link #setKeyManagerFactory(KeyManagerFactory)}.
+	 * <p>
+	 * If this property isn't set while no {@link KeyManagerFactory} has been set
+	 * using {@link #setKeyManagerFactory(KeyManagerFactory)} and
+	 * {@link #setKeyManagerFactoryAlgorithmUseDefault(boolean)} has been set to
+	 * <tt>true</tt> the value returned by
+	 * {@link KeyManagerFactory#getDefaultAlgorithm()} will be used instead.
+	 *
+	 * @param algorithm the algorithm to use.
+	 */
+	public void setKeyManagerFactoryAlgorithm(String algorithm) {
+		this.keyManagerFactoryAlgorithm = algorithm;
+	}
+
+	/**
+	 * Sets the provider to use when creating the {@link KeyManagerFactory} using
+	 * {@link KeyManagerFactory#getInstance(java.lang.String, java.lang.String)}.
+	 * <p>
+	 * This property will be ignored if a {@link KeyManagerFactory} has been set
+	 * directly using {@link #setKeyManagerFactory(KeyManagerFactory)}.
+	 * <p>
+	 * If this property isn't set and no {@link KeyManagerFactory} has been set
+	 * using {@link #setKeyManagerFactory(KeyManagerFactory)}
+	 * {@link KeyManagerFactory#getInstance(java.lang.String)} will be used to
+	 * create the {@link KeyManagerFactory}.
+	 *
+	 * @param provider the name of the provider.
+	 */
+	public void setKeyManagerFactoryProvider(String provider) {
+		this.keyManagerFactoryProvider = provider;
+	}
+
+	/**
+	 * Sets the {@link KeyStore} which will be used in the call to
+	 * {@link KeyManagerFactory#init(java.security.KeyStore, char[])} when the
+	 * {@link SSLContext} is created.
+	 *
+	 * @param keyStore the key store.
+	 */
+	public void setKeyManagerFactoryKeyStore(KeyStore keyStore) {
+		this.keyManagerFactoryKeyStore = keyStore;
+	}
+
+	/**
+	 * Sets the password which will be used in the call to
+	 * {@link KeyManagerFactory#init(java.security.KeyStore, char[])} when the
+	 * {@link SSLContext} is created.
+	 *
+	 * @param password the password. Use <code>null</code> to disable password.
+	 */
+	public void setKeyManagerFactoryKeyStorePassword(String password) {
+		if (password != null) {
+			this.keyManagerFactoryKeyStorePassword = password.toCharArray();
+		} else {
+			this.keyManagerFactoryKeyStorePassword = null;
+		}
+	}
+
+	/**
+	 * Sets the {@link TrustManagerFactory} to use. If this is set the properties
+	 * which are used by this factory bean to create a {@link TrustManagerFactory}
+	 * will all be ignored.
+	 *
+	 * @param factory the factory.
+	 */
+	public void setTrustManagerFactory(TrustManagerFactory factory) {
+		this.trustManagerFactory = factory;
+	}
+
+	/**
+	 * Sets the algorithm to use when creating the {@link TrustManagerFactory} using
+	 * {@link TrustManagerFactory#getInstance(java.lang.String)} or
+	 * {@link TrustManagerFactory#getInstance(java.lang.String, java.lang.String)}.
+	 * <p>
+	 * This property will be ignored if a {@link TrustManagerFactory} has been set
+	 * directly using {@link #setTrustManagerFactory(TrustManagerFactory)}.
+	 * <p>
+	 * If this property isn't set while no {@link TrustManagerFactory} has been set
+	 * using {@link #setTrustManagerFactory(TrustManagerFactory)} and
+	 * {@link #setTrustManagerFactoryAlgorithmUseDefault(boolean)} has been set to
+	 * <tt>true</tt> the value returned by
+	 * {@link TrustManagerFactory#getDefaultAlgorithm()} will be used instead.
+	 *
+	 * @param algorithm the algorithm to use.
+	 */
+	public void setTrustManagerFactoryAlgorithm(String algorithm) {
+		this.trustManagerFactoryAlgorithm = algorithm;
+	}
+
+	/**
+	 * Sets the {@link KeyStore} which will be used in the call to
+	 * {@link TrustManagerFactory#init(java.security.KeyStore)} when the
+	 * {@link SSLContext} is created.
+	 * <p>
+	 * This property will be ignored if {@link ManagerFactoryParameters} has been
+	 * set directly using
+	 * {@link #setTrustManagerFactoryParameters(ManagerFactoryParameters)}.
+	 *
+	 * @param keyStore the key store.
+	 */
+	public void setTrustManagerFactoryKeyStore(KeyStore keyStore) {
+		this.trustManagerFactoryKeyStore = keyStore;
+	}
+
+	/**
+	 * Sets the {@link ManagerFactoryParameters} which will be used in the call to
+	 * {@link TrustManagerFactory#init(javax.net.ssl.ManagerFactoryParameters)} when
+	 * the {@link SSLContext} is created.
+	 *
+	 * @param parameters describing provider-specific trust material.
+	 */
+	public void setTrustManagerFactoryParameters(ManagerFactoryParameters parameters) {
+		this.trustManagerFactoryParameters = parameters;
+	}
+
+	/**
+	 * Sets the provider to use when creating the {@link TrustManagerFactory} using
+	 * {@link TrustManagerFactory#getInstance(java.lang.String, java.lang.String)}.
+	 * <p>
+	 * This property will be ignored if a {@link TrustManagerFactory} has been set
+	 * directly using {@link #setTrustManagerFactory(TrustManagerFactory)}.
+	 * <p>
+	 * If this property isn't set and no {@link TrustManagerFactory} has been set
+	 * using {@link #setTrustManagerFactory(TrustManagerFactory)}
+	 * {@link TrustManagerFactory#getInstance(java.lang.String)} will be used to
+	 * create the {@link TrustManagerFactory}.
+	 *
+	 * @param provider the name of the provider.
+	 */
+	public void setTrustManagerFactoryProvider(String provider) {
+		this.trustManagerFactoryProvider = provider;
+	}
+
+	/**
+	 * Sets the {@link SecureRandom} to use when initializing the
+	 * {@link SSLContext}. The JVM's default will be used if this isn't set.
+	 *
+	 * @param secureRandom the {@link SecureRandom} or <code>null</code> if the
+	 *                     JVM's default should be used.
+	 * @see SSLContext#init(javax.net.ssl.KeyManager[],
+	 *      javax.net.ssl.TrustManager[], java.security.SecureRandom)
+	 */
+	public void setSecureRandom(SecureRandom secureRandom) {
+		this.secureRandom = secureRandom;
+	}
+
+	/**
+	 * Sets the SSLSession cache size for the {@link SSLSessionContext} for use in
+	 * client mode.
+	 *
+	 * @param size the new session cache size limit; zero means there is no limit.
+	 * @see SSLSessionContext#setSessionCacheSize(int size)
+	 */
+	public void setClientSessionCacheSize(int size) {
+		this.clientSessionCacheSize = size;
+	}
+
+	/**
+	 * Set the SSLSession timeout limit for the {@link SSLSessionContext} for use in
+	 * client mode.
+	 *
+	 * @param seconds the new session timeout limit in seconds; zero means there is
+	 *                no limit.
+	 * @see SSLSessionContext#setSessionTimeout(int seconds)
+	 */
+	public void setClientSessionTimeout(int seconds) {
+		this.clientSessionTimeout = seconds;
+	}
+
+	/**
+	 * Sets the SSLSession cache size for the {@link SSLSessionContext} for use in
+	 * server mode.
+	 *
+	 * @param serverSessionCacheSize the new session cache size limit; zero means
+	 *                               there is no limit.
+	 * @see SSLSessionContext#setSessionCacheSize(int)
+	 */
+	public void setServerSessionCacheSize(int serverSessionCacheSize) {
+		this.serverSessionCacheSize = serverSessionCacheSize;
+	}
+
+	/**
+	 * Set the SSLSession timeout limit for the {@link SSLSessionContext} for use in
+	 * server mode.
+	 *
+	 * @param serverSessionTimeout the new session timeout limit in seconds; zero
+	 *                             means there is no limit.
+	 * @see SSLSessionContext#setSessionTimeout(int)
+	 */
+	public void setServerSessionTimeout(int serverSessionTimeout) {
+		this.serverSessionTimeout = serverSessionTimeout;
+	}
 }
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslEvent.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslEvent.java
index 060d313..e1c497d 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslEvent.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslEvent.java
@@ -22,11 +22,11 @@ package org.apache.mina.filter.ssl;
 import org.apache.mina.filter.FilterEvent;
 
 /**
- * A SSL event sent by {@link SslFilter} when the session is secured or not secured.
+ * A SSL event sent by {@link SslFilter} when the session is secured or not
+ * secured.
  * 
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  */
 public enum SslEvent implements FilterEvent {
-    SECURED,
-    UNSECURED
+	SECURED, UNSECURED
 }
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslFilter.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslFilter.java
index 45d124e..a55995a 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslFilter.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslFilter.java
@@ -49,37 +49,37 @@ import org.slf4j.LoggerFactory;
 
 /**
  * An SSL filter that encrypts and decrypts the data exchanged in the session.
- * Adding this filter triggers SSL handshake procedure immediately by sending
- * a SSL 'hello' message, so you don't need to call
- * {@link #startSsl(IoSession)} manually unless you are implementing StartTLS
- * (see below).  If you don't want the handshake procedure to start
- * immediately, please specify {@code false} as {@code autoStart} parameter in
- * the constructor.
+ * Adding this filter triggers SSL handshake procedure immediately by sending a
+ * SSL 'hello' message, so you don't need to call {@link #startSsl(IoSession)}
+ * manually unless you are implementing StartTLS (see below). If you don't want
+ * the handshake procedure to start immediately, please specify {@code false} as
+ * {@code autoStart} parameter in the constructor.
  * <p>
- * This filter uses an {@link SSLEngine} which was introduced in Java 5, so
- * Java version 5 or above is mandatory to use this filter. And please note that
- * this filter only works for TCP/IP connections.
+ * This filter uses an {@link SSLEngine} which was introduced in Java 5, so Java
+ * version 5 or above is mandatory to use this filter. And please note that this
+ * filter only works for TCP/IP connections.
  *
  * <h2>Implementing StartTLS</h2>
  * <p>
  * You can use {@link #DISABLE_ENCRYPTION_ONCE} attribute to implement StartTLS:
+ * 
  * <pre>
  * public void messageReceived(IoSession session, Object message) {
- *    if (message instanceof MyStartTLSRequest) {
- *        // Insert SSLFilter to get ready for handshaking
- *        session.getFilterChain().addFirst(sslFilter);
+ * 	if (message instanceof MyStartTLSRequest) {
+ * 		// Insert SSLFilter to get ready for handshaking
+ * 		session.getFilterChain().addFirst(sslFilter);
  *
- *        // Disable encryption temporarily.
- *        // This attribute will be removed by SSLFilter
- *        // inside the Session.write() call below.
- *        session.setAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE, Boolean.TRUE);
+ * 		// Disable encryption temporarily.
+ * 		// This attribute will be removed by SSLFilter
+ * 		// inside the Session.write() call below.
+ * 		session.setAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE, Boolean.TRUE);
  *
- *        // Write StartTLSResponse which won't be encrypted.
- *        session.write(new MyStartTLSResponse(OK));
+ * 		// Write StartTLSResponse which won't be encrypted.
+ * 		session.write(new MyStartTLSResponse(OK));
  *
- *        // Now DISABLE_ENCRYPTION_ONCE attribute is cleared.
- *        assert session.getAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE) == null;
- *    }
+ * 		// Now DISABLE_ENCRYPTION_ONCE attribute is cleared.
+ * 		assert session.getAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE) == null;
+ * 	}
  * }
  * </pre>
  *
@@ -87,812 +87,817 @@ import org.slf4j.LoggerFactory;
  * @org.apache.xbean.XBean
  */
 public class SslFilter extends IoFilterAdapter {
-    /** The logger */
-    private static final Logger LOGGER = LoggerFactory.getLogger(SslFilter.class);
-
-    /**
-     * A session attribute key that stores underlying {@link SSLSession}
-     * for each session.
-     */
-    public static final AttributeKey SSL_SESSION = new AttributeKey(SslFilter.class, "session");
-
-    /**
-     * A session attribute key that makes next one write request bypass
-     * this filter (not encrypting the data).  This is a marker attribute,
-     * which means that you can put whatever as its value. ({@link Boolean#TRUE}
-     * is preferred.)  The attribute is automatically removed from the session
-     * attribute map as soon as {@link IoSession#write(Object)} is invoked,
-     * and therefore should be put again if you want to make more messages
-     * bypass this filter.  This is especially useful when you implement
-     * StartTLS.
-     */
-    public static final AttributeKey DISABLE_ENCRYPTION_ONCE = new AttributeKey(SslFilter.class, "disableOnce");
-
-    /**
-     * A session attribute key that makes this filter to emit a
-     * {@link IoHandler#messageReceived(IoSession, Object)} event with a
-     * special message ({@link SslEvent#SECURED} or {@link SslEvent#UNSECURED}).
-     * This is a marker attribute, which means that you can put whatever as its
-     * value. ({@link Boolean#TRUE} is preferred.)  By default, this filter
-     * doesn't emit any events related with SSL session flow control.
-     */
-    public static final AttributeKey USE_NOTIFICATION = new AttributeKey(SslFilter.class, "useNotification");
-
-    /**
-     * A session attribute key that should be set to an {@link InetSocketAddress}.
-     * Setting this attribute causes
-     * {@link SSLContext#createSSLEngine(String, int)} to be called passing the
-     * hostname and port of the {@link InetSocketAddress} to get an
-     * {@link SSLEngine} instance. If not set {@link SSLContext#createSSLEngine()}
-     * will be called.
-     * <br>
-     * Using this feature {@link SSLSession} objects may be cached and reused
-     * when in client mode.
-     *
-     * @see SSLContext#createSSLEngine(String, int)
-     */
-    public static final AttributeKey PEER_ADDRESS = new AttributeKey(SslFilter.class, "peerAddress");
-
-    /** An attribute containing the next filter */
-    private static final AttributeKey NEXT_FILTER = new AttributeKey(SslFilter.class, "nextFilter");
-
-    private static final AttributeKey SSL_HANDLER = new AttributeKey(SslFilter.class, "handler");
-
-    /** The SslContext used */
-    /* No qualifier */final SSLContext sslContext;
-
-    /** A flag used to tell the filter to start the handshake immediately */
-    private final boolean autoStart;
-
-    /** A flag used to determinate if the handshake should start immediately */
-    public static final boolean START_HANDSHAKE = true;
-
-    /** A flag used to determinate if the handshake should wait for the client to initiate the handshake */
-    public static final boolean CLIENT_HANDSHAKE = false;
-
-    private boolean client;
-
-    private boolean needClientAuth;
-
-    private boolean wantClientAuth;
-
-    private String[] enabledCipherSuites;
-
-    private String[] enabledProtocols;
-
-    /**
-     * Creates a new SSL filter using the specified {@link SSLContext}.
-     * The handshake will start immediately after the filter has been added
-     * to the chain.
-     * 
-     * @param sslContext The SSLContext to use
-     */
-    public SslFilter(SSLContext sslContext) {
-        this(sslContext, START_HANDSHAKE);
-    }
-
-    /**
-     * Creates a new SSL filter using the specified {@link SSLContext}.
-     * If the <tt>autostart</tt> flag is set to <tt>true</tt>, the
-     * handshake will start immediately after the filter has been added
-     * to the chain.
-     * 
-     * @param sslContext The SSLContext to use
-     * @param autoStart The flag used to tell the filter to start the handshake immediately
-     */
-    public SslFilter(SSLContext sslContext, boolean autoStart) {
-        if (sslContext == null) {
-            throw new IllegalArgumentException("sslContext");
-        }
-
-        this.sslContext = sslContext;
-        this.autoStart = autoStart;
-    }
-
-    /**
-     * Returns the underlying {@link SSLSession} for the specified session.
-     *
-     * @param session The current session 
-     * @return <tt>null</tt> if no {@link SSLSession} is initialized yet.
-     */
-    public SSLSession getSslSession(IoSession session) {
-        return (SSLSession) session.getAttribute(SSL_SESSION);
-    }
-
-    /**
-     * (Re)starts SSL session for the specified <tt>session</tt> if not started yet.
-     * Please note that SSL session is automatically started by default, and therefore
-     * you don't need to call this method unless you've used TLS closure.
-     *
-     * @param session The session that will be switched to SSL mode
-     * @return <tt>true</tt> if the SSL session has been started, <tt>false</tt> if already started.
-     * @throws SSLException if failed to start the SSL session
-     */
-    public boolean startSsl(IoSession session) throws SSLException {
-        SslHandler sslHandler = getSslSessionHandler(session);
-        boolean started;
-
-        try {
-            synchronized (sslHandler) {
-                if (sslHandler.isOutboundDone()) {
-                    NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
-                    sslHandler.destroy();
-                    sslHandler.init();
-                    sslHandler.handshake(nextFilter);
-                    started = true;
-                } else {
-                    started = false;
-                }
-                sslHandler.flushFilterWrite();
-            }
-            sslHandler.flushMessageReceived();
-        } catch (SSLException se) {
-            sslHandler.release();
-            throw se;
-        }
-
-        return started;
-    }
-
-    /**
-     * An extended toString() method for sessions. If the SSL handshake
-     * is not yet completed, we will print (ssl) in small caps. Once it's
-     * completed, we will use SSL capitalized.
-     */
-    /* no qualifier */String getSessionInfo(IoSession session) {
-        StringBuilder sb = new StringBuilder();
-
-        if (session.getService() instanceof IoAcceptor) {
-            sb.append("Session Server");
-
-        } else {
-            sb.append("Session Client");
-        }
-
-        sb.append('[').append(session.getId()).append(']');
-
-        SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
-
-        if (sslHandler == null) {
-            sb.append("(no sslEngine)");
-        } else if (isSslStarted(session)) {
-            if (sslHandler.isHandshakeComplete()) {
-                sb.append("(SSL)");
-            } else {
-                sb.append("(ssl...)");
-            }
-        }
-
-        return sb.toString();
-    }
-
-    /**
-     * @return <tt>true</tt> if and only if the specified <tt>session</tt> is
-     * encrypted/decrypted over SSL/TLS currently. This method will start
-     * to return <tt>false</tt> after TLS <tt>close_notify</tt> message
-     * is sent and any messages written after then is not going to get encrypted.
-     * 
-     * @param session the session we want to check
-     */
-    public boolean isSslStarted(IoSession session) {
-        SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
-
-        if (sslHandler == null) {
-            return false;
-        }
-
-        synchronized (sslHandler) {
-            return !sslHandler.isOutboundDone();
-        }
-    }
-
-    /**
-     * @return <tt>true</tt> if and only if the conditions for
-     * {@link #isSslStarted(IoSession)} are met, and the handhake has
-     * completed.
-     *
-     * @param session the session we want to check
-     */
-    public boolean isSecured(IoSession session) {
-        SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
-
-        if (sslHandler == null) {
-            return false;
-        }
-
-        synchronized (sslHandler) {
-            return !sslHandler.isOutboundDone() && sslHandler.isHandshakeComplete();
-        }
-    }
-
-
-    /**
-     * Stops the SSL session by sending TLS <tt>close_notify</tt> message to
-     * initiate TLS closure.
-     *
-     * @param session the {@link IoSession} to initiate TLS closure
-     * @return The Future for the initiated closure
-     * @throws SSLException if failed to initiate TLS closure
-     */
-    public WriteFuture stopSsl(IoSession session) throws SSLException {
-        SslHandler sslHandler = getSslSessionHandler(session);
-        NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
-        WriteFuture future;
-
-        try {
-            synchronized (sslHandler) {
-                future = initiateClosure(nextFilter, session);
-                sslHandler.flushFilterWrite();
-            }
-        } catch (SSLException se) {
-            sslHandler.release();
-            throw se;
-        }
-
-        return future;
-    }
-
-    /**
-     * @return <tt>true</tt> if the engine is set to use client mode
-     * when handshaking.
-     */
-    public boolean isUseClientMode() {
-        return client;
-    }
-
-    /**
-     * Configures the engine to use client (or server) mode when handshaking.
-     * 
-     * @param clientMode <tt>true</tt> when we are in client mode, <tt>false</tt> when in server mode
-     */
-    public void setUseClientMode(boolean clientMode) {
-        this.client = clientMode;
-    }
-
-    /**
-     * @return <tt>true</tt> if the engine will <em>require</em> client authentication.
-     * This option is only useful to engines in the server mode.
-     */
-    public boolean isNeedClientAuth() {
-        return needClientAuth;
-    }
-
-    /**
-     * Configures the engine to <em>require</em> client authentication.
-     * This option is only useful for engines in the server mode.
-     * 
-     * @param needClientAuth A flag set when we need to authenticate the client
-     */
-    public void setNeedClientAuth(boolean needClientAuth) {
-        this.needClientAuth = needClientAuth;
-    }
-
-    /**
-     * @return <tt>true</tt> if the engine will <em>request</em> client authentication.
-     * This option is only useful to engines in the server mode.
-     */
-    public boolean isWantClientAuth() {
-        return wantClientAuth;
-    }
-
-    /**
-     * Configures the engine to <em>request</em> client authentication.
-     * This option is only useful for engines in the server mode.
-     * 
-     * @param wantClientAuth A flag set when we want to check the client authentication
-     */
-    public void setWantClientAuth(boolean wantClientAuth) {
-        this.wantClientAuth = wantClientAuth;
-    }
-
-    /**
-     * @return the list of cipher suites to be enabled when {@link SSLEngine}
-     * is initialized. <tt>null</tt> means 'use {@link SSLEngine}'s default.'
-     */
-    public String[] getEnabledCipherSuites() {
-        return enabledCipherSuites;
-    }
-
-    /**
-     * Sets the list of cipher suites to be enabled when {@link SSLEngine}
-     * is initialized.
-     *
-     * @param cipherSuites <tt>null</tt> means 'use {@link SSLEngine}'s default.'
-     */
-    public void setEnabledCipherSuites(String[] cipherSuites) {
-        this.enabledCipherSuites = cipherSuites;
-    }
-
-    /**
-     * @return the list of protocols to be enabled when {@link SSLEngine}
-     * is initialized. <tt>null</tt> means 'use {@link SSLEngine}'s default.'
-     */
-    public String[] getEnabledProtocols() {
-        return enabledProtocols;
-    }
-
-    /**
-     * Sets the list of protocols to be enabled when {@link SSLEngine}
-     * is initialized.
-     *
-     * @param protocols <tt>null</tt> means 'use {@link SSLEngine}'s default.'
-     */
-    public void setEnabledProtocols(String[] protocols) {
-        this.enabledProtocols = protocols;
-    }
-
-    /**
-     * Executed just before the filter is added into the chain, we do :
-     * <ul>
-     * <li>check that we don't have a SSL filter already present
-     * <li>we update the next filter
-     * <li>we create the SSL handler helper class
-     * <li>and we store it into the session's Attributes
-     * </ul>
-     */
-    @Override
-    public void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
-        // Check that we don't have a SSL filter already present in the chain
-        if (parent.contains(SslFilter.class)) {
-            String msg = "Only one SSL filter is permitted in a chain.";
-            LOGGER.error(msg);
-            throw new IllegalStateException(msg);
-        }
-
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("Adding the SSL Filter {} to the chain", name);
-        }
-
-        IoSession session = parent.getSession();
-        session.setAttribute(NEXT_FILTER, nextFilter);
-
-        // Create a SSL handler and start handshake.
-        SslHandler sslHandler = new SslHandler(this, session);
-        
-        // Adding the supported ciphers in the SSLHandler
-        if ((enabledCipherSuites == null) || (enabledCipherSuites.length == 0)) {
-            enabledCipherSuites = sslContext.getServerSocketFactory().getSupportedCipherSuites();
-        }
-
-        sslHandler.init();
-
-        session.setAttribute(SSL_HANDLER, sslHandler);
-    }
-
-    @Override
-    public void onPostAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
-        if (autoStart == START_HANDSHAKE) {
-            initiateHandshake(nextFilter, parent.getSession());
-        }
-    }
-
-    @Override
-    public void onPreRemove(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
-        IoSession session = parent.getSession();
-        stopSsl(session);
-        session.removeAttribute(NEXT_FILTER);
-        session.removeAttribute(SSL_HANDLER);
-    }
-
-    // IoFilter impl.
-    @Override
-    public void sessionClosed(NextFilter nextFilter, IoSession session) throws SSLException {
-        SslHandler sslHandler = getSslSessionHandler(session);
-
-        try {
-            synchronized (sslHandler) {
-                // release resources
-                sslHandler.destroy();
-            }
-        } finally {
-            // notify closed session
-            nextFilter.sessionClosed(session);
-        }
-    }
-
-    @Override
-    public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws SSLException {
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("{}: Message received : {}", getSessionInfo(session), message);
-        }
-
-        SslHandler sslHandler = getSslSessionHandler(session);
-
-	synchronized (sslHandler) {
-	    if (!isSslStarted(session) && sslHandler.isInboundDone()) {
-		// The SSL session must be established first before we
-		// can push data to the application. Store the incoming
-		// data into a queue for a later processing
-		sslHandler.scheduleMessageReceived(nextFilter, message);
-	    } else {
-		IoBuffer buf = (IoBuffer) message;
+	/** The logger */
+	private static final Logger LOGGER = LoggerFactory.getLogger(SslFilter.class);
+
+	/**
+	 * A session attribute key that stores underlying {@link SSLSession} for each
+	 * session.
+	 */
+	public static final AttributeKey SSL_SESSION = new AttributeKey(SslFilter.class, "session");
+
+	/**
+	 * A session attribute key that makes next one write request bypass this filter
+	 * (not encrypting the data). This is a marker attribute, which means that you
+	 * can put whatever as its value. ({@link Boolean#TRUE} is preferred.) The
+	 * attribute is automatically removed from the session attribute map as soon as
+	 * {@link IoSession#write(Object)} is invoked, and therefore should be put again
+	 * if you want to make more messages bypass this filter. This is especially
+	 * useful when you implement StartTLS.
+	 */
+	public static final AttributeKey DISABLE_ENCRYPTION_ONCE = new AttributeKey(SslFilter.class, "disableOnce");
+
+	/**
+	 * A session attribute key that makes this filter to emit a
+	 * {@link IoHandler#messageReceived(IoSession, Object)} event with a special
+	 * message ({@link SslEvent#SECURED} or {@link SslEvent#UNSECURED}). This is a
+	 * marker attribute, which means that you can put whatever as its value.
+	 * ({@link Boolean#TRUE} is preferred.) By default, this filter doesn't emit any
+	 * events related with SSL session flow control.
+	 */
+	public static final AttributeKey USE_NOTIFICATION = new AttributeKey(SslFilter.class, "useNotification");
+
+	/**
+	 * A session attribute key that should be set to an {@link InetSocketAddress}.
+	 * Setting this attribute causes {@link SSLContext#createSSLEngine(String, int)}
+	 * to be called passing the hostname and port of the {@link InetSocketAddress}
+	 * to get an {@link SSLEngine} instance. If not set
+	 * {@link SSLContext#createSSLEngine()} will be called. <br>
+	 * Using this feature {@link SSLSession} objects may be cached and reused when
+	 * in client mode.
+	 *
+	 * @see SSLContext#createSSLEngine(String, int)
+	 */
+	public static final AttributeKey PEER_ADDRESS = new AttributeKey(SslFilter.class, "peerAddress");
+
+	/** An attribute containing the next filter */
+	private static final AttributeKey NEXT_FILTER = new AttributeKey(SslFilter.class, "nextFilter");
+
+	private static final AttributeKey SSL_HANDLER = new AttributeKey(SslFilter.class, "handler");
+
+	/** The SslContext used */
+	/* No qualifier */final SSLContext sslContext;
+
+	/** A flag used to tell the filter to start the handshake immediately */
+	private final boolean autoStart;
+
+	/** A flag used to determinate if the handshake should start immediately */
+	public static final boolean START_HANDSHAKE = true;
+
+	/**
+	 * A flag used to determinate if the handshake should wait for the client to
+	 * initiate the handshake
+	 */
+	public static final boolean CLIENT_HANDSHAKE = false;
+
+	private boolean client;
+
+	private boolean needClientAuth;
+
+	private boolean wantClientAuth;
+
+	private String[] enabledCipherSuites;
+
+	private String[] enabledProtocols;
+
+	/**
+	 * Creates a new SSL filter using the specified {@link SSLContext}. The
+	 * handshake will start immediately after the filter has been added to the
+	 * chain.
+	 * 
+	 * @param sslContext The SSLContext to use
+	 */
+	public SslFilter(SSLContext sslContext) {
+		this(sslContext, START_HANDSHAKE);
+	}
+
+	/**
+	 * Creates a new SSL filter using the specified {@link SSLContext}. If the
+	 * <tt>autostart</tt> flag is set to <tt>true</tt>, the handshake will start
+	 * immediately after the filter has been added to the chain.
+	 * 
+	 * @param sslContext The SSLContext to use
+	 * @param autoStart  The flag used to tell the filter to start the handshake
+	 *                   immediately
+	 */
+	public SslFilter(SSLContext sslContext, boolean autoStart) {
+		if (sslContext == null) {
+			throw new IllegalArgumentException("sslContext");
+		}
+
+		this.sslContext = sslContext;
+		this.autoStart = autoStart;
+	}
+
+	/**
+	 * Returns the underlying {@link SSLSession} for the specified session.
+	 *
+	 * @param session The current session
+	 * @return <tt>null</tt> if no {@link SSLSession} is initialized yet.
+	 */
+	public SSLSession getSslSession(IoSession session) {
+		return (SSLSession) session.getAttribute(SSL_SESSION);
+	}
+
+	/**
+	 * (Re)starts SSL session for the specified <tt>session</tt> if not started yet.
+	 * Please note that SSL session is automatically started by default, and
+	 * therefore you don't need to call this method unless you've used TLS closure.
+	 *
+	 * @param session The session that will be switched to SSL mode
+	 * @return <tt>true</tt> if the SSL session has been started, <tt>false</tt> if
+	 *         already started.
+	 * @throws SSLException if failed to start the SSL session
+	 */
+	public boolean startSsl(IoSession session) throws SSLException {
+		SslHandler sslHandler = getSslSessionHandler(session);
+		boolean started;
+
+		try {
+			synchronized (sslHandler) {
+				if (sslHandler.isOutboundDone()) {
+					NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
+					sslHandler.destroy();
+					sslHandler.init();
+					sslHandler.handshake(nextFilter);
+					started = true;
+				} else {
+					started = false;
+				}
+				sslHandler.flushFilterWrite();
+			}
+			sslHandler.flushMessageReceived();
+		} catch (SSLException se) {
+			sslHandler.release();
+			throw se;
+		}
+
+		return started;
+	}
+
+	/**
+	 * An extended toString() method for sessions. If the SSL handshake is not yet
+	 * completed, we will print (ssl) in small caps. Once it's completed, we will
+	 * use SSL capitalized.
+	 */
+	/* no qualifier */String getSessionInfo(IoSession session) {
+		StringBuilder sb = new StringBuilder();
+
+		if (session.getService() instanceof IoAcceptor) {
+			sb.append("Session Server");
+
+		} else {
+			sb.append("Session Client");
+		}
+
+		sb.append('[').append(session.getId()).append(']');
+
+		SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
+
+		if (sslHandler == null) {
+			sb.append("(no sslEngine)");
+		} else if (isSslStarted(session)) {
+			if (sslHandler.isHandshakeComplete()) {
+				sb.append("(SSL)");
+			} else {
+				sb.append("(ssl...)");
+			}
+		}
+
+		return sb.toString();
+	}
+
+	/**
+	 * @return <tt>true</tt> if and only if the specified <tt>session</tt> is
+	 *         encrypted/decrypted over SSL/TLS currently. This method will start to
+	 *         return <tt>false</tt> after TLS <tt>close_notify</tt> message is sent
+	 *         and any messages written after then is not going to get encrypted.
+	 * 
+	 * @param session the session we want to check
+	 */
+	public boolean isSslStarted(IoSession session) {
+		SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
+
+		if (sslHandler == null) {
+			return false;
+		}
+
+		synchronized (sslHandler) {
+			return !sslHandler.isOutboundDone();
+		}
+	}
+
+	/**
+	 * @return <tt>true</tt> if and only if the conditions for
+	 *         {@link #isSslStarted(IoSession)} are met, and the handhake has
+	 *         completed.
+	 *
+	 * @param session the session we want to check
+	 */
+	public boolean isSecured(IoSession session) {
+		SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
+
+		if (sslHandler == null) {
+			return false;
+		}
+
+		synchronized (sslHandler) {
+			return !sslHandler.isOutboundDone() && sslHandler.isHandshakeComplete();
+		}
+	}
+
+	/**
+	 * Stops the SSL session by sending TLS <tt>close_notify</tt> message to
+	 * initiate TLS closure.
+	 *
+	 * @param session the {@link IoSession} to initiate TLS closure
+	 * @return The Future for the initiated closure
+	 * @throws SSLException if failed to initiate TLS closure
+	 */
+	public WriteFuture stopSsl(IoSession session) throws SSLException {
+		SslHandler sslHandler = getSslSessionHandler(session);
+		NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
+		WriteFuture future;
+
+		try {
+			synchronized (sslHandler) {
+				future = initiateClosure(nextFilter, session);
+				sslHandler.flushFilterWrite();
+			}
+		} catch (SSLException se) {
+			sslHandler.release();
+			throw se;
+		}
+
+		return future;
+	}
+
+	/**
+	 * @return <tt>true</tt> if the engine is set to use client mode when
+	 *         handshaking.
+	 */
+	public boolean isUseClientMode() {
+		return client;
+	}
+
+	/**
+	 * Configures the engine to use client (or server) mode when handshaking.
+	 * 
+	 * @param clientMode <tt>true</tt> when we are in client mode, <tt>false</tt>
+	 *                   when in server mode
+	 */
+	public void setUseClientMode(boolean clientMode) {
+		this.client = clientMode;
+	}
+
+	/**
+	 * @return <tt>true</tt> if the engine will <em>require</em> client
+	 *         authentication. This option is only useful to engines in the server
+	 *         mode.
+	 */
+	public boolean isNeedClientAuth() {
+		return needClientAuth;
+	}
+
+	/**
+	 * Configures the engine to <em>require</em> client authentication. This option
+	 * is only useful for engines in the server mode.
+	 * 
+	 * @param needClientAuth A flag set when we need to authenticate the client
+	 */
+	public void setNeedClientAuth(boolean needClientAuth) {
+		this.needClientAuth = needClientAuth;
+	}
+
+	/**
+	 * @return <tt>true</tt> if the engine will <em>request</em> client
+	 *         authentication. This option is only useful to engines in the server
+	 *         mode.
+	 */
+	public boolean isWantClientAuth() {
+		return wantClientAuth;
+	}
+
+	/**
+	 * Configures the engine to <em>request</em> client authentication. This option
+	 * is only useful for engines in the server mode.
+	 * 
+	 * @param wantClientAuth A flag set when we want to check the client
+	 *                       authentication
+	 */
+	public void setWantClientAuth(boolean wantClientAuth) {
+		this.wantClientAuth = wantClientAuth;
+	}
+
+	/**
+	 * @return the list of cipher suites to be enabled when {@link SSLEngine} is
+	 *         initialized. <tt>null</tt> means 'use {@link SSLEngine}'s default.'
+	 */
+	public String[] getEnabledCipherSuites() {
+		return enabledCipherSuites;
+	}
+
+	/**
+	 * Sets the list of cipher suites to be enabled when {@link SSLEngine} is
+	 * initialized.
+	 *
+	 * @param cipherSuites <tt>null</tt> means 'use {@link SSLEngine}'s default.'
+	 */
+	public void setEnabledCipherSuites(String[] cipherSuites) {
+		this.enabledCipherSuites = cipherSuites;
+	}
+
+	/**
+	 * @return the list of protocols to be enabled when {@link SSLEngine} is
+	 *         initialized. <tt>null</tt> means 'use {@link SSLEngine}'s default.'
+	 */
+	public String[] getEnabledProtocols() {
+		return enabledProtocols;
+	}
+
+	/**
+	 * Sets the list of protocols to be enabled when {@link SSLEngine} is
+	 * initialized.
+	 *
+	 * @param protocols <tt>null</tt> means 'use {@link SSLEngine}'s default.'
+	 */
+	public void setEnabledProtocols(String[] protocols) {
+		this.enabledProtocols = protocols;
+	}
+
+	/**
+	 * Executed just before the filter is added into the chain, we do :
+	 * <ul>
+	 * <li>check that we don't have a SSL filter already present
+	 * <li>we update the next filter
+	 * <li>we create the SSL handler helper class
+	 * <li>and we store it into the session's Attributes
+	 * </ul>
+	 */
+	@Override
+	public void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
+		// Check that we don't have a SSL filter already present in the chain
+		if (parent.contains(SslFilter.class)) {
+			String msg = "Only one SSL filter is permitted in a chain.";
+			LOGGER.error(msg);
+			throw new IllegalStateException(msg);
+		}
+
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("Adding the SSL Filter {} to the chain", name);
+		}
+
+		IoSession session = parent.getSession();
+		session.setAttribute(NEXT_FILTER, nextFilter);
+
+		// Create a SSL handler and start handshake.
+		SslHandler sslHandler = new SslHandler(this, session);
+
+		// Adding the supported ciphers in the SSLHandler
+		if ((enabledCipherSuites == null) || (enabledCipherSuites.length == 0)) {
+			enabledCipherSuites = sslContext.getServerSocketFactory().getSupportedCipherSuites();
+		}
+
+		sslHandler.init();
+
+		session.setAttribute(SSL_HANDLER, sslHandler);
+	}
+
+	@Override
+	public void onPostAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
+		if (autoStart == START_HANDSHAKE) {
+			initiateHandshake(nextFilter, parent.getSession());
+		}
+	}
+
+	@Override
+	public void onPreRemove(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
+		IoSession session = parent.getSession();
+		stopSsl(session);
+		session.removeAttribute(NEXT_FILTER);
+		session.removeAttribute(SSL_HANDLER);
+	}
+
+	// IoFilter impl.
+	@Override
+	public void sessionClosed(NextFilter nextFilter, IoSession session) throws SSLException {
+		SslHandler sslHandler = getSslSessionHandler(session);
 
 		try {
-		    if (sslHandler.isOutboundDone()) {
-			sslHandler.destroy();
-			throw new SSLException("Outbound done");
-		    }
+			synchronized (sslHandler) {
+				// release resources
+				sslHandler.destroy();
+			}
+		} finally {
+			// notify closed session
+			nextFilter.sessionClosed(session);
+		}
+	}
 
-		    // forward read encrypted data to SSL handler
-		    sslHandler.messageReceived(nextFilter, buf.buf());
+	@Override
+	public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws SSLException {
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{}: Message received : {}", getSessionInfo(session), message);
+		}
 
-		    // Handle data to be forwarded to application or written to net
-		    handleSslData(nextFilter, sslHandler);
+		SslHandler sslHandler = getSslSessionHandler(session);
 
-		    if (sslHandler.isInboundDone()) {
-			if (sslHandler.isOutboundDone()) {
-			    sslHandler.destroy();
+		synchronized (sslHandler) {
+			if (!isSslStarted(session) && sslHandler.isInboundDone()) {
+				// The SSL session must be established first before we
+				// can push data to the application. Store the incoming
+				// data into a queue for a later processing
+				sslHandler.scheduleMessageReceived(nextFilter, message);
 			} else {
-			    initiateClosure(nextFilter, session);
+				IoBuffer buf = (IoBuffer) message;
+
+				try {
+					if (sslHandler.isOutboundDone()) {
+						sslHandler.destroy();
+						throw new SSLException("Outbound done");
+					}
+
+					// forward read encrypted data to SSL handler
+					sslHandler.messageReceived(nextFilter, buf.buf());
+
+					// Handle data to be forwarded to application or written to net
+					handleSslData(nextFilter, sslHandler);
+
+					if (sslHandler.isInboundDone()) {
+						if (sslHandler.isOutboundDone()) {
+							sslHandler.destroy();
+						} else {
+							initiateClosure(nextFilter, session);
+						}
+
+						if (buf.hasRemaining()) {
+							// Forward the data received after closure.
+							sslHandler.scheduleMessageReceived(nextFilter, buf);
+						}
+					}
+				} catch (SSLException ssle) {
+					if (!sslHandler.isHandshakeComplete()) {
+						SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
+						newSsle.initCause(ssle);
+						ssle = newSsle;
+
+						// Close the session immediately, the handshake has failed
+						session.closeNow();
+					} else {
+						// Free the SSL Handler buffers
+						sslHandler.release();
+					}
+
+					throw ssle;
+				}
+			}
+		}
+
+		sslHandler.flushMessageReceived();
+	}
+
+	@Override
+	public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) {
+		if (writeRequest instanceof EncryptedWriteRequest) {
+			EncryptedWriteRequest wrappedRequest = (EncryptedWriteRequest) writeRequest;
+			nextFilter.messageSent(session, wrappedRequest.getParentRequest());
+		} else {
+			// ignore extra buffers used for handshaking
+		}
+	}
+
+	@Override
+	public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
+
+		if (cause instanceof WriteToClosedSessionException) {
+			// Filter out SSL close notify, which is likely to fail to flush
+			// due to disconnection.
+			WriteToClosedSessionException e = (WriteToClosedSessionException) cause;
+			List<WriteRequest> failedRequests = e.getRequests();
+			boolean containsCloseNotify = false;
+
+			for (WriteRequest r : failedRequests) {
+				if (isCloseNotify(r.getMessage())) {
+					containsCloseNotify = true;
+					break;
+				}
+			}
+
+			if (containsCloseNotify) {
+				if (failedRequests.size() == 1) {
+					// close notify is the only failed request; bail out.
+					return;
+				}
+
+				List<WriteRequest> newFailedRequests = new ArrayList<>(failedRequests.size() - 1);
+
+				for (WriteRequest r : failedRequests) {
+					if (!isCloseNotify(r.getMessage())) {
+						newFailedRequests.add(r);
+					}
+				}
+
+				if (newFailedRequests.isEmpty()) {
+					// the failedRequests were full with close notify; bail out.
+					return;
+				}
+
+				cause = new WriteToClosedSessionException(newFailedRequests, cause.getMessage(), cause.getCause());
+			}
+		}
+
+		nextFilter.exceptionCaught(session, cause);
+	}
+
+	private boolean isCloseNotify(Object message) {
+		if (!(message instanceof IoBuffer)) {
+			return false;
+		}
+
+		IoBuffer buf = (IoBuffer) message;
+		int offset = buf.position();
+
+		return (buf.get(offset + 0) == 0x15) /* Alert */
+				&& (buf.get(offset + 1) == 0x03) /* TLS/SSL */
+				&& ((buf.get(offset + 2) == 0x00) /* SSL 3.0 */
+						|| (buf.get(offset + 2) == 0x01) /* TLS 1.0 */
+						|| (buf.get(offset + 2) == 0x02) /* TLS 1.1 */
+						|| (buf.get(offset + 2) == 0x03)) /* TLS 1.2 */
+				&& (buf.get(offset + 3) == 0x00); /* close_notify */
+	}
+
+	@Override
+	public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws SSLException {
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{}: Writing Message : {}", getSessionInfo(session), writeRequest);
+		}
+
+		boolean needsFlush = true;
+		SslHandler sslHandler = getSslSessionHandler(session);
+
+		try {
+			synchronized (sslHandler) {
+				if (!isSslStarted(session)) {
+					sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
+				}
+				// Don't encrypt the data if encryption is disabled.
+				else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) {
+					// Remove the marker attribute because it is temporary.
+					session.removeAttribute(DISABLE_ENCRYPTION_ONCE);
+					sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
+				} else {
+					// Otherwise, encrypt the buffer.
+					IoBuffer buf = (IoBuffer) writeRequest.getMessage();
+
+					if (sslHandler.isWritingEncryptedData()) {
+						// data already encrypted; simply return buffer
+						sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
+					} else if (sslHandler.isHandshakeComplete()) {
+						// SSL encrypt
+						sslHandler.encrypt(buf.buf());
+						IoBuffer encryptedBuffer = sslHandler.fetchOutNetBuffer();
+						writeRequest.setMessage(encryptedBuffer);
+						sslHandler.scheduleFilterWrite(nextFilter,
+								new EncryptedWriteRequest(writeRequest, encryptedBuffer));
+					} else {
+						if (session.isConnected()) {
+							// Handshake not complete yet.
+							sslHandler.schedulePreHandshakeWriteRequest(nextFilter, writeRequest);
+						}
+
+						needsFlush = false;
+					}
+				}
+				if (needsFlush) {
+					sslHandler.flushFilterWrite();
+				}
+			}
+		} catch (SSLException se) {
+			sslHandler.release();
+			throw se;
+		}
+	}
+
+	@Override
+	public void filterClose(final NextFilter nextFilter, final IoSession session) throws SSLException {
+		SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
+
+		if (sslHandler == null) {
+			// The connection might already have closed, or
+			// SSL might have not started yet.
+			nextFilter.filterClose(session);
+			return;
+		}
+
+		WriteFuture future = null;
+
+		try {
+			synchronized (sslHandler) {
+				if (isSslStarted(session)) {
+					future = initiateClosure(nextFilter, session);
+					future.addListener(new IoFutureListener<IoFuture>() {
+						@Override
+						public void operationComplete(IoFuture future) {
+							nextFilter.filterClose(session);
+						}
+					});
+				}
+				sslHandler.flushFilterWrite();
+			}
+		} catch (SSLException se) {
+			sslHandler.release();
+			throw se;
+		} finally {
+			if (future == null) {
+				nextFilter.filterClose(session);
+			}
+		}
+	}
+
+	/**
+	 * Initiate the SSL handshake. This can be invoked if you have set the
+	 * 'autoStart' to false when creating the SslFilter instance.
+	 * 
+	 * @param session The session for which the SSL handshake should be done
+	 * @throws SSLException If the handshake failed
+	 */
+	public void initiateHandshake(IoSession session) throws SSLException {
+		IoFilterChain filterChain = session.getFilterChain();
+
+		if (filterChain == null) {
+			throw new SSLException("No filter chain");
+		}
+
+		IoFilter.NextFilter nextFilter = filterChain.getNextFilter(SslFilter.class);
+
+		if (nextFilter == null) {
+			throw new SSLException("No SSL next filter in the chain");
+		}
+
+		initiateHandshake(nextFilter, session);
+	}
+
+	private void initiateHandshake(NextFilter nextFilter, IoSession session) throws SSLException {
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{} : Starting the first handshake", getSessionInfo(session));
+		}
+
+		SslHandler sslHandler = getSslSessionHandler(session);
+
+		try {
+			synchronized (sslHandler) {
+				sslHandler.handshake(nextFilter);
+				sslHandler.flushFilterWrite();
 			}
+			sslHandler.flushMessageReceived();
+		} catch (SSLException se) {
+			sslHandler.release();
+			throw se;
+		}
+	}
+
+	private WriteFuture initiateClosure(NextFilter nextFilter, IoSession session) throws SSLException {
+		SslHandler sslHandler = getSslSessionHandler(session);
+		WriteFuture future = null;
 
-			if (buf.hasRemaining()) {
-			    // Forward the data received after closure.
-			    sslHandler.scheduleMessageReceived(nextFilter, buf);
+		// if already shut down
+		try {
+			synchronized (sslHandler) {
+				if (!sslHandler.closeOutbound()) {
+					return DefaultWriteFuture.newNotWrittenFuture(session,
+							new IllegalStateException("SSL session is shut down already."));
+				}
+
+				// there might be data to write out here?
+				future = sslHandler.writeNetBuffer(nextFilter);
+
+				if (future == null) {
+					future = DefaultWriteFuture.newWrittenFuture(session);
+				}
+
+				if (sslHandler.isInboundDone()) {
+					sslHandler.destroy();
+				}
 			}
-		    }
-		} catch (SSLException ssle) {
-		    if (!sslHandler.isHandshakeComplete()) {
-			SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
-			newSsle.initCause(ssle);
-			ssle = newSsle;
-
-			// Close the session immediately, the handshake has failed
-			session.closeNow();
-		    } else {
-			// Free the SSL Handler buffers
+
+			// Inform that the session is not any more secured
+			session.getFilterChain().fireEvent(SslEvent.UNSECURED);
+		} catch (SSLException se) {
 			sslHandler.release();
-		    }
-
-		    throw ssle;
-		}
-	    }
-	}
-	
-	sslHandler.flushMessageReceived();
-    }
-
-    @Override
-    public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) {
-        if (writeRequest instanceof EncryptedWriteRequest) {
-            EncryptedWriteRequest wrappedRequest = (EncryptedWriteRequest) writeRequest;
-            nextFilter.messageSent(session, wrappedRequest.getParentRequest());
-        } else {
-            // ignore extra buffers used for handshaking
-        }
-    }
-
-    @Override
-    public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
-
-        if (cause instanceof WriteToClosedSessionException) {
-            // Filter out SSL close notify, which is likely to fail to flush
-            // due to disconnection.
-            WriteToClosedSessionException e = (WriteToClosedSessionException) cause;
-            List<WriteRequest> failedRequests = e.getRequests();
-            boolean containsCloseNotify = false;
-
-            for (WriteRequest r : failedRequests) {
-                if (isCloseNotify(r.getMessage())) {
-                    containsCloseNotify = true;
-                    break;
-                }
-            }
-
-            if (containsCloseNotify) {
-                if (failedRequests.size() == 1) {
-                    // close notify is the only failed request; bail out.
-                    return;
-                }
-
-                List<WriteRequest> newFailedRequests = new ArrayList<>(failedRequests.size() - 1);
-
-                for (WriteRequest r : failedRequests) {
-                    if (!isCloseNotify(r.getMessage())) {
-                        newFailedRequests.add(r);
-                    }
-                }
-
-                if (newFailedRequests.isEmpty()) {
-                    // the failedRequests were full with close notify; bail out.
-                    return;
-                }
-
-                cause = new WriteToClosedSessionException(newFailedRequests, cause.getMessage(), cause.getCause());
-            }
-        }
-
-        nextFilter.exceptionCaught(session, cause);
-    }
-
-    private boolean isCloseNotify(Object message) {
-        if (!(message instanceof IoBuffer)) {
-            return false;
-        }
-
-        IoBuffer buf = (IoBuffer) message;
-        int offset = buf.position();
-
-        return (buf.get(offset + 0) == 0x15) /* Alert */
-                && (buf.get(offset + 1) == 0x03) /* TLS/SSL */
-                && ((buf.get(offset + 2) == 0x00) /* SSL 3.0 */
-                        || (buf.get(offset + 2) == 0x01) /* TLS 1.0 */
-                        || (buf.get(offset + 2) == 0x02) /* TLS 1.1 */
-                        || (buf.get(offset + 2) == 0x03)) /* TLS 1.2 */
-                        && (buf.get(offset + 3) == 0x00); /* close_notify */
-    }
-
-    @Override
-    public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws SSLException {
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("{}: Writing Message : {}", getSessionInfo(session), writeRequest);
-        }
-
-        boolean needsFlush = true;
-        SslHandler sslHandler = getSslSessionHandler(session);
-
-        try {
-            synchronized (sslHandler) {
-                if (!isSslStarted(session)) {
-                    sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
-                }
-                // Don't encrypt the data if encryption is disabled.
-                else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) {
-                    // Remove the marker attribute because it is temporary.
-                    session.removeAttribute(DISABLE_ENCRYPTION_ONCE);
-                    sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
-                } else {
-                    // Otherwise, encrypt the buffer.
-                    IoBuffer buf = (IoBuffer) writeRequest.getMessage();
-
-                    if (sslHandler.isWritingEncryptedData()) {
-                        // data already encrypted; simply return buffer
-                        sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
-                    } else if (sslHandler.isHandshakeComplete()) {
-                        // SSL encrypt
-                        sslHandler.encrypt(buf.buf());
-                        IoBuffer encryptedBuffer = sslHandler.fetchOutNetBuffer();
-                        writeRequest.setMessage( encryptedBuffer );
-                        sslHandler.scheduleFilterWrite(nextFilter, new EncryptedWriteRequest(writeRequest,
-                            encryptedBuffer));
-                    } else {
-                        if (session.isConnected()) {
-                            // Handshake not complete yet.
-                            sslHandler.schedulePreHandshakeWriteRequest(nextFilter, writeRequest);
-                        }
-
-                        needsFlush = false;
-                    }
-                }
-                if (needsFlush) {
-                    sslHandler.flushFilterWrite();
-                }
-            }
-        } catch (SSLException se) {
-            sslHandler.release();
-            throw se;
-        }
-    }
-
-    @Override
-    public void filterClose(final NextFilter nextFilter, final IoSession session) throws SSLException {
-        SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
-
-        if (sslHandler == null) {
-            // The connection might already have closed, or
-            // SSL might have not started yet.
-            nextFilter.filterClose(session);
-            return;
-        }
-
-        WriteFuture future = null;
-
-        try {
-            synchronized (sslHandler) {
-                if (isSslStarted(session)) {
-                    future = initiateClosure(nextFilter, session);
-                    future.addListener(new IoFutureListener<IoFuture>() {
-                        @Override
-                        public void operationComplete(IoFuture future) {
-                            nextFilter.filterClose(session);
-                        }
-                    });
-                }
-                sslHandler.flushFilterWrite();
-            }
-        } catch (SSLException se) {
-            sslHandler.release();
-            throw se;
-        } finally {
-            if (future == null) {
-                nextFilter.filterClose(session);
-            }
-        }
-    }
-
-    /**
-     * Initiate the SSL handshake. This can be invoked if you have set the 'autoStart' to
-     * false when creating the SslFilter instance.
-     * 
-     * @param session The session for which the SSL handshake should be done
-     * @throws SSLException If the handshake failed
-     */
-    public void initiateHandshake(IoSession session) throws SSLException {
-        IoFilterChain filterChain = session.getFilterChain();
-        
-        if (filterChain == null) {
-            throw new SSLException("No filter chain");
-        }
-        
-        IoFilter.NextFilter nextFilter = filterChain.getNextFilter(SslFilter.class);
-        
-        if (nextFilter == null) {
-            throw new SSLException("No SSL next filter in the chain");
-        }
-        
-        initiateHandshake(nextFilter, session);
-    }
-
-    private void initiateHandshake(NextFilter nextFilter, IoSession session) throws SSLException {
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("{} : Starting the first handshake", getSessionInfo(session));
-        }
-        
-        SslHandler sslHandler = getSslSessionHandler(session);
-
-        try {
-            synchronized (sslHandler) {
-                sslHandler.handshake(nextFilter);
-                sslHandler.flushFilterWrite();
-            }
-            sslHandler.flushMessageReceived();
-        } catch (SSLException se) {
-            sslHandler.release();
-            throw se;
-        }
-    }
-
-    private WriteFuture initiateClosure(NextFilter nextFilter, IoSession session) throws SSLException {
-        SslHandler sslHandler = getSslSessionHandler(session);
-        WriteFuture future = null;
-
-        // if already shut down
-        try {
-            synchronized(sslHandler) {
-                if (!sslHandler.closeOutbound()) {
-                    return DefaultWriteFuture.newNotWrittenFuture(session, new IllegalStateException(
-                            "SSL session is shut down already."));
-                }
-    
-                // there might be data to write out here?
-                future = sslHandler.writeNetBuffer(nextFilter);
-    
-                if (future == null) {
-                    future = DefaultWriteFuture.newWrittenFuture(session);
-                }
-    
-                if (sslHandler.isInboundDone()) {
-                    sslHandler.destroy();
-                }
-            }
-
-            // Inform that the session is not any more secured
-            session.getFilterChain().fireEvent(SslEvent.UNSECURED);
-        } catch (SSLException se) {
-            sslHandler.release();
-            throw se;
-        }
-
-        return future;
-    }
-
-    // Utilities
-    private void handleSslData(NextFilter nextFilter, SslHandler sslHandler) throws SSLException {
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("{}: Processing the SSL Data ", getSessionInfo(sslHandler.getSession()));
-        }
-
-        // Flush any buffered write requests occurred before handshaking.
-        if (sslHandler.isHandshakeComplete()) {
-            sslHandler.flushPreHandshakeEvents();
-        }
-
-        // Write encrypted data to be written (if any)
-        sslHandler.writeNetBuffer(nextFilter);
-
-        // handle app. data read (if any)
-        handleAppDataRead(nextFilter, sslHandler);
-    }
-
-    private void handleAppDataRead(NextFilter nextFilter, SslHandler sslHandler) {
-        // forward read app data
-        IoBuffer readBuffer = sslHandler.fetchAppBuffer();
-
-        if (readBuffer.hasRemaining()) {
-            sslHandler.scheduleMessageReceived(nextFilter, readBuffer);
-        }
-    }
-
-    private SslHandler getSslSessionHandler(IoSession session) {
-        SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
-
-        if (sslHandler == null) {
-            throw new IllegalStateException();
-        }
-
-        synchronized(sslHandler) {
-            if (sslHandler.getSslFilter() != this) {
-                throw new IllegalArgumentException("Not managed by this filter.");
-            }
-        }
-
-        return sslHandler;
-    }
-
-    /**
-     * A message that is sent from {@link SslFilter} when the connection became
-     * secure or is not secure anymore.
-     *
-     * @author <a href="http://mina.apache.org">Apache MINA Project</a>
-     */
-    public static class SslFilterMessage {
-        private final String name;
-
-        private SslFilterMessage(String name) {
-            this.name = name;
-        }
-
-        @Override
-        public String toString() {
-            return name;
-        }
-    }
-    
-    /**
-     * A private class used to store encrypted messages. This is necessary
-     * to be able to emit the messageSent event with the proper original
-     * message, but not for handshake messages, which will be swallowed.
-     *
-     */
-    /* package protected */ static class EncryptedWriteRequest extends DefaultWriteRequest {
-        // Thee encrypted messagee
-        private final IoBuffer encryptedMessage;
-        
-        // The original message
-        private WriteRequest parentRequest;
-
-        /**
-         * Create a new instance of an EncryptedWriteRequest
-         * @param writeRequest The parent request
-         * @param encryptedMessage The encrypted message
-         */
-        private EncryptedWriteRequest(WriteRequest writeRequest, IoBuffer encryptedMessage) {
-            super(encryptedMessage);
-            parentRequest = writeRequest;
-            this.encryptedMessage = encryptedMessage;
-        }
-
-        /**
-         * @return teh encrypted message
-         */
-        @Override
-        public Object getMessage() {
-            return encryptedMessage;
-        }
-
-        /**
-         * @return The parent WriteRequest
-         */
-        public WriteRequest getParentRequest() {
-            return parentRequest;
-        }
-        
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public WriteFuture getFuture() {
-            return parentRequest.getFuture();
-        }
-    }
+			throw se;
+		}
+
+		return future;
+	}
+
+	// Utilities
+	private void handleSslData(NextFilter nextFilter, SslHandler sslHandler) throws SSLException {
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{}: Processing the SSL Data ", getSessionInfo(sslHandler.getSession()));
+		}
+
+		// Flush any buffered write requests occurred before handshaking.
+		if (sslHandler.isHandshakeComplete()) {
+			sslHandler.flushPreHandshakeEvents();
+		}
+
+		// Write encrypted data to be written (if any)
+		sslHandler.writeNetBuffer(nextFilter);
+
+		// handle app. data read (if any)
+		handleAppDataRead(nextFilter, sslHandler);
+	}
+
+	private void handleAppDataRead(NextFilter nextFilter, SslHandler sslHandler) {
+		// forward read app data
+		IoBuffer readBuffer = sslHandler.fetchAppBuffer();
+
+		if (readBuffer.hasRemaining()) {
+			sslHandler.scheduleMessageReceived(nextFilter, readBuffer);
+		}
+	}
+
+	private SslHandler getSslSessionHandler(IoSession session) {
+		SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
+
+		if (sslHandler == null) {
+			throw new IllegalStateException();
+		}
+
+		synchronized (sslHandler) {
+			if (sslHandler.getSslFilter() != this) {
+				throw new IllegalArgumentException("Not managed by this filter.");
+			}
+		}
+
+		return sslHandler;
+	}
+
+	/**
+	 * A message that is sent from {@link SslFilter} when the connection became
+	 * secure or is not secure anymore.
+	 *
+	 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+	 */
+	public static class SslFilterMessage {
+		private final String name;
+
+		private SslFilterMessage(String name) {
+			this.name = name;
+		}
+
+		@Override
+		public String toString() {
+			return name;
+		}
+	}
+
+	/**
+	 * A private class used to store encrypted messages. This is necessary to be
+	 * able to emit the messageSent event with the proper original message, but not
+	 * for handshake messages, which will be swallowed.
+	 *
+	 */
+	/* package protected */ static class EncryptedWriteRequest extends DefaultWriteRequest {
+		// Thee encrypted messagee
+		private final IoBuffer encryptedMessage;
+
+		// The original message
+		private WriteRequest parentRequest;
+
+		/**
+		 * Create a new instance of an EncryptedWriteRequest
+		 * 
+		 * @param writeRequest     The parent request
+		 * @param encryptedMessage The encrypted message
+		 */
+		private EncryptedWriteRequest(WriteRequest writeRequest, IoBuffer encryptedMessage) {
+			super(encryptedMessage);
+			parentRequest = writeRequest;
+			this.encryptedMessage = encryptedMessage;
+		}
+
+		/**
+		 * @return teh encrypted message
+		 */
+		@Override
+		public Object getMessage() {
+			return encryptedMessage;
+		}
+
+		/**
+		 * @return The parent WriteRequest
+		 */
+		public WriteRequest getParentRequest() {
+			return parentRequest;
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public WriteFuture getFuture() {
+			return parentRequest.getFuture();
+		}
+	}
 }
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslHandler.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslHandler.java
index 71ace82..070ee18 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslHandler.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslHandler.java
@@ -62,812 +62,813 @@ import org.slf4j.LoggerFactory;
  *
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  */
-/** No qualifier*/
+/** No qualifier */
 class SslHandler {
-    /** A logger for this class */
-    private static final Logger LOGGER = LoggerFactory.getLogger(SslHandler.class);
-
-    /** The SSL Filter which has created this handler */
-    private final SslFilter sslFilter;
-
-    /** The current session */
-    private final IoSession session;
-
-    private final Queue<IoFilterEvent> preHandshakeEventQueue = new ConcurrentLinkedQueue<>();
-
-    private final Queue<IoFilterEvent> filterWriteEventQueue = new ConcurrentLinkedQueue<>();
-
-    /** A queue used to stack all the incoming data until the SSL session is established */
-    private final Queue<IoFilterEvent> messageReceivedEventQueue = new ConcurrentLinkedQueue<>();
-
-    private SSLEngine sslEngine;
-
-    /**
-     * Encrypted data from the net
-     */
-    private IoBuffer inNetBuffer;
-
-    /**
-     * Encrypted data to be written to the net
-     */
-    private IoBuffer outNetBuffer;
-
-    /**
-     * Application cleartext data to be read by application
-     */
-    private IoBuffer appBuffer;
-
-    /**
-     * Empty buffer used during initial handshake and close operations
-     */
-    private final IoBuffer emptyBuffer = IoBuffer.allocate(0);
-
-    private SSLEngineResult.HandshakeStatus handshakeStatus;
-
-    /**
-     * A flag set to true when the first SSL handshake has been completed
-     * This is used to avoid sending a notification to the application handler
-     * when we switch to a SECURE or UNSECURE session.
-     */
-    private boolean firstSSLNegociation;
-
-    /** A flag set to true when a SSL Handshake has been completed */
-    private boolean handshakeComplete;
-
-    /** A flag used to indicate to the SslFilter that the buffer
-     * it will write is already encrypted (this will be the case
-     * for data being produced during the handshake). */
-    private boolean writingEncryptedData;
-
-    /**
-     * Create a new SSL Handler, and initialize it.
-     *
-     * @param sslContext
-     * @throws SSLException
-     */
-    /* no qualifier */SslHandler(SslFilter sslFilter, IoSession session) {
-        this.sslFilter = sslFilter;
-        this.session = session;
-    }
-
-    /**
-     * Initialize the SSL handshake.
-     *
-     * @throws SSLException If the underlying SSLEngine handshake initialization failed
-     */
-    /* no qualifier */void init() throws SSLException {
-        if (sslEngine != null) {
-            // We already have a SSL engine created, no need to create a new one
-            return;
-        }
-
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("{} Initializing the SSL Handler", sslFilter.getSessionInfo(session));
-        }
-
-        InetSocketAddress peer = (InetSocketAddress) session.getAttribute(SslFilter.PEER_ADDRESS);
-
-        // Create the SSL engine here
-        if (peer == null) {
-            sslEngine = sslFilter.sslContext.createSSLEngine();
-        } else {
-            sslEngine = sslFilter.sslContext.createSSLEngine(peer.getHostName(), peer.getPort());
-        }
-
-        // Initialize the engine in client mode if necessary
-        sslEngine.setUseClientMode(sslFilter.isUseClientMode());
-
-        // Initialize the different SslEngine modes
-        if (!sslEngine.getUseClientMode()) {
-            // Those parameters are only valid when in server mode
-            if (sslFilter.isWantClientAuth()) {
-                sslEngine.setWantClientAuth(true);
-            }
-
-            if (sslFilter.isNeedClientAuth()) {
-                sslEngine.setNeedClientAuth(true);
-            }
-        }
-
-        // Set the cipher suite to use by this SslEngine instance
-        if (sslFilter.getEnabledCipherSuites() != null) {
-            sslEngine.setEnabledCipherSuites(sslFilter.getEnabledCipherSuites());
-        }
-
-        // Set the list of enabled protocols
-        if (sslFilter.getEnabledProtocols() != null) {
-            sslEngine.setEnabledProtocols(sslFilter.getEnabledProtocols());
-        }
-
-        // TODO : we may not need to call this method...
-        // However, if we don't call it here, the tests are failing. Why?
-        handshakeStatus = sslEngine.getHandshakeStatus();
-
-        // Default value
-        writingEncryptedData = false;
-
-        // We haven't yet started a SSL negotiation
-        // set the flags accordingly
-        firstSSLNegociation = true;
-        handshakeComplete = false;
-
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("{} SSL Handler Initialization done.", sslFilter.getSessionInfo(session));
-        }
-    }
-    
-
-    /**
-     * Release allocated buffers.
-     */
-    /* no qualifier */void destroy() {
-        if (sslEngine == null) {
-            return;
-        }
-
-        // Close inbound and flush all remaining data if available.
-        try {
-            sslEngine.closeInbound();
-        } catch (SSLException e) {
-            if (LOGGER.isDebugEnabled()) {
-                LOGGER.debug("Unexpected exception from SSLEngine.closeInbound().", e);
-            }
-        }
-
-        if (outNetBuffer != null) {
-            outNetBuffer.capacity(sslEngine.getSession().getPacketBufferSize());
-        } else {
-            createOutNetBuffer(0);
-        }
-        try {
-            do {
-                outNetBuffer.clear();
-            } while (sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf()).bytesProduced() > 0);
-        } catch (SSLException e) {
-            // Ignore.
-        } finally {
-            outNetBuffer.free();
-            outNetBuffer = null;
-        }
-
-        sslEngine.closeOutbound();
-        sslEngine = null;
-
-        preHandshakeEventQueue.clear();
-    }
-
-    /**
-     * @return The SSL filter which has created this handler
-     */
-    /* no qualifier */SslFilter getSslFilter() {
-        return sslFilter;
-    }
-
-    /* no qualifier */IoSession getSession() {
-        return session;
-    }
-
-    /**
-     * Check if we are writing encrypted data.
-     */
-    /* no qualifier */boolean isWritingEncryptedData() {
-        return writingEncryptedData;
-    }
-
-    /**
-     * Check if handshake is completed.
-     */
-    /* no qualifier */boolean isHandshakeComplete() {
-        return handshakeComplete;
-    }
-
-    /**
-     * Check if handshake is on going.
-     */
-    /* no qualifier */boolean notHandshaking() {
-        return handshakeStatus == HandshakeStatus.FINISHED || handshakeStatus == HandshakeStatus.NOT_HANDSHAKING;
-    }
-
-    /* no qualifier */boolean isInboundDone() {
-        return sslEngine == null || sslEngine.isInboundDone();
-    }
-
-    /* no qualifier */boolean isOutboundDone() {
-        return sslEngine == null || sslEngine.isOutboundDone();
-    }
-
-    /**
-     * Check if there is any need to complete handshake.
-     */
-    /* no qualifier */boolean needToCompleteHandshake() {
-        return handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone();
-    }
-
-    /* no qualifier */void schedulePreHandshakeWriteRequest(NextFilter nextFilter, WriteRequest writeRequest) {
-        preHandshakeEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
-    }
-
-    /* no qualifier */void flushPreHandshakeEvents() throws SSLException {
-        IoFilterEvent scheduledWrite;
-
-        while ((scheduledWrite = preHandshakeEventQueue.poll()) != null) {
-            sslFilter
-            .filterWrite(scheduledWrite.getNextFilter(), session, (WriteRequest) scheduledWrite.getParameter());
-        }
-    }
-
-    /* no qualifier */void scheduleFilterWrite(NextFilter nextFilter, WriteRequest writeRequest) {
-        filterWriteEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
-    }
-
-    /* no qualifier */void flushFilterWrite() {
-        // Fire events only when the lock is available for this handler.
-        IoFilterEvent event;
-    
-        // We need synchronization here inevitably because filterWrite can be
-        // called simultaneously and cause 'bad record MAC' integrity error.
-        while ((event = filterWriteEventQueue.poll()) != null) {
-            NextFilter nextFilter = event.getNextFilter();
-            nextFilter.filterWrite(session, (WriteRequest) event.getParameter());
-        }
-    }
-
-    /**
-     * Push the newly received data into a queue, waiting for the SSL session
-     * to be fully established
-     *
-     * @param nextFilter The next filter to call
-     * @param message The incoming data
-     */
-    /* no qualifier */void scheduleMessageReceived(NextFilter nextFilter, Object message) {
-        messageReceivedEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.MESSAGE_RECEIVED, session, message));
-    }
-    
-    /* no qualifier */void flushMessageReceived() {
-	IoFilterEvent event;
-
-	while ((event = messageReceivedEventQueue.poll()) != null) {
-	    NextFilter nextFilter = event.getNextFilter();
-	    nextFilter.messageReceived(session, event.getParameter());
-	}
-    }
-
-    /**
-     * Call when data are read from net. It will perform the initial hanshake or decrypt
-     * the data if SSL has been initialiaed.
-     * 
-     * @param buf buffer to decrypt
-     * @param nextFilter Next filter in chain
-     * @throws SSLException on errors
-     */
-    /* no qualifier */void messageReceived(NextFilter nextFilter, ByteBuffer buf) throws SSLException {
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("{} Processing the received message", sslFilter.getSessionInfo(session));
-        }
-
-        // append buf to inNetBuffer
-        if (inNetBuffer == null) {
-            inNetBuffer = IoBuffer.allocate(buf.remaining()).setAutoExpand(true);
-        }
-
-        inNetBuffer.put(buf);
-
-        if (!handshakeComplete) {
-            handshake(nextFilter);
-        } else {
-            // Prepare the net data for reading.
-            inNetBuffer.flip();
-
-            if (!inNetBuffer.hasRemaining()) {
-                return;
-            }
-
-            SSLEngineResult res = unwrap();
-
-            // prepare to be written again
-            if (inNetBuffer.hasRemaining()) {
-                inNetBuffer.compact();
-            } else {
-                inNetBuffer.free();
-                inNetBuffer = null;
-            }
-
-            checkStatus(res);
-
-            renegotiateIfNeeded(nextFilter, res);
-        }
-
-        if (isInboundDone()) {
-            // Rewind the MINA buffer if not all data is processed and inbound
-            // is finished.
-            int inNetBufferPosition = inNetBuffer == null ? 0 : inNetBuffer.position();
-            buf.position(buf.position() - inNetBufferPosition);
-
-            if (inNetBuffer != null) {
-                inNetBuffer.free();
-                inNetBuffer = null;
-            }
-        }
-    }
-
-    /**
-     * Get decrypted application data.
-     * 
-     * @return buffer with data
-     */
-    /* no qualifier */IoBuffer fetchAppBuffer() {
-        if (appBuffer == null) {
-            return IoBuffer.allocate(0);
-        } else {
-            IoBuffer newAppBuffer = appBuffer.flip();
-            appBuffer = null;
-
-            return newAppBuffer.shrink();
-        }
-    }
-
-    /**
-     * Get encrypted data to be sent.
-     * 
-     * @return buffer with data
-     */
-    /* no qualifier */IoBuffer fetchOutNetBuffer() {
-        IoBuffer answer = outNetBuffer;
-        
-        if (answer == null) {
-            return emptyBuffer;
-        }
-
-        outNetBuffer = null;
-        
-        return answer.shrink();
-    }
-
-    /**
-     * Encrypt provided buffer. Encrypted data returned by getOutNetBuffer().
-     * 
-     * @param src
-     *            data to encrypt
-     * @throws SSLException
-     *             on errors
-     */
-    /* no qualifier */void encrypt(ByteBuffer src) throws SSLException {
-        if (!handshakeComplete) {
-            throw new IllegalStateException();
-        }
-
-        if (!src.hasRemaining()) {
-            if (outNetBuffer == null) {
-                outNetBuffer = emptyBuffer;
-            }
-            return;
-        }
-
-        createOutNetBuffer(src.remaining());
-
-        // Loop until there is no more data in src
-        while (src.hasRemaining()) {
-
-            SSLEngineResult result = sslEngine.wrap(src, outNetBuffer.buf());
-            
-            if (result.getStatus() == SSLEngineResult.Status.OK) {
-                if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
-                    doTasks();
-                }
-            } else if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
-                outNetBuffer.capacity(outNetBuffer.capacity() << 1);
-                outNetBuffer.limit(outNetBuffer.capacity());
-            } 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
-     */
-    /* no qualifier */boolean closeOutbound() throws SSLException {
-        if (sslEngine == null || sslEngine.isOutboundDone()) {
-            return false;
-        }
-
-        sslEngine.closeOutbound();
-
-        createOutNetBuffer(0);
-        SSLEngineResult result;
-
-        for (;;) {
-            result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
-            if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
-                outNetBuffer.capacity(outNetBuffer.capacity() << 1);
-                outNetBuffer.limit(outNetBuffer.capacity());
-            } else {
-                break;
-            }
-        }
-
-        if (result.getStatus() != SSLEngineResult.Status.CLOSED) {
-            throw new SSLException("Improper close state: " + result);
-        }
-
-        outNetBuffer.flip();
-
-        return true;
-    }
-
-    /**
-     * @param res
-     * @throws SSLException
-     */
-    private void checkStatus(SSLEngineResult res) throws SSLException {
-
-        SSLEngineResult.Status status = res.getStatus();
-
-        /*
-         * 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.
-         */
-        switch (status) {
-            case BUFFER_OVERFLOW:
-                throw new SSLException("SSLEngine error during decrypt: " + status + " inNetBuffer: " + inNetBuffer
-                    + "appBuffer: " + appBuffer);
-            case CLOSED:
-                Exception exception =new RuntimeIoException("SSL/TLS close_notify received");
-                
-                // Empty the Ssl queue
-                for (IoFilterEvent event:filterWriteEventQueue) {
-                    EncryptedWriteRequest writeRequest = (EncryptedWriteRequest)event.getParameter();
-                    WriteFuture writeFuture = writeRequest.getParentRequest().getFuture();
-                    writeFuture.setException(exception);
-                    writeFuture.notifyAll();
-                }
-                
-                // Empty the session queue
-                WriteRequestQueue queue = session.getWriteRequestQueue();
-                WriteRequest request = null;
-               
-    	    	while ((request = queue.poll(session)) != null) {
-    	    	    WriteFuture writeFuture = request.getFuture();
-    	    	    writeFuture.setException(exception);
-    	    	    writeFuture.notifyAll();
-        	}
-                    
-                // We *must* shutdown session
-                session.closeNow();
-                break;
-            default: 
-                break;
-        }
-    }
-
-    /**
-     * Perform any handshaking processing.
-     */
-    /* no qualifier */void handshake(NextFilter nextFilter) throws SSLException {
-        for (;;) {
-            switch (handshakeStatus) {
-            case FINISHED:
-                if (LOGGER.isDebugEnabled()) {
-                    LOGGER.debug("{} processing the FINISHED state", sslFilter.getSessionInfo(session));
-                }
-
-                session.setAttribute(SslFilter.SSL_SESSION, sslEngine.getSession());
-                handshakeComplete = true;
-
-                // Send the SECURE message only if it's the first SSL handshake
-                if (firstSSLNegociation) {
-                    firstSSLNegociation = false;
-                    
-                    nextFilter.event(session, SslEvent.SECURED);
-                }
-
-                if (LOGGER.isDebugEnabled()) {
-                    if (!isOutboundDone()) {
-                        LOGGER.debug("{} is now secured", sslFilter.getSessionInfo(session));
-                    } else {
-                        LOGGER.debug("{} is not secured yet", sslFilter.getSessionInfo(session));
-                    }
-                }
-
-                return;
-
-            case NEED_TASK:
-                if (LOGGER.isDebugEnabled()) {
-                    LOGGER.debug("{} processing the NEED_TASK state", sslFilter.getSessionInfo(session));
-                }
-
-                handshakeStatus = doTasks();
-                break;
-
-            case NEED_UNWRAP:
-                if (LOGGER.isDebugEnabled()) {
-                    LOGGER.debug("{} processing the NEED_UNWRAP state", sslFilter.getSessionInfo(session));
-                }
-                // we need more data read
-                SSLEngineResult.Status status = unwrapHandshake(nextFilter);
-
-                if (status == SSLEngineResult.Status.BUFFER_UNDERFLOW
-                        && handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED || isInboundDone()) {
-                    // We need more data or the session is closed
-                    return;
-                }
-
-                break;
-
-            case NEED_WRAP:
-            case NOT_HANDSHAKING:
-                if (LOGGER.isDebugEnabled()) {
-                    LOGGER.debug("{} processing the NEED_WRAP state", sslFilter.getSessionInfo(session));
-                }
-
-                // First make sure that the out buffer is completely empty.
-                // Since we cannot call wrap with data left on the buffer
-                if (outNetBuffer != null && outNetBuffer.hasRemaining()) {
-                    return;
-                }
-
-                SSLEngineResult result;
-                createOutNetBuffer(0);
-
-                result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
-
-                while ( result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW ) {
-                    outNetBuffer.capacity(outNetBuffer.capacity() << 1);
-                    outNetBuffer.limit(outNetBuffer.capacity());
-
-                    result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
-                }
-
-                outNetBuffer.flip();
-                handshakeStatus = result.getHandshakeStatus();
-                writeNetBuffer(nextFilter);
-                break;
-
-            default:
-                String msg = "Invalid Handshaking State" + handshakeStatus
-                + " while processing the Handshake for session " + session.getId();
-                LOGGER.error(msg);
-                throw new IllegalStateException(msg);
-            }
-        }
-    }
-
-    private void createOutNetBuffer(int expectedRemaining) {
-        // SSLEngine requires us to allocate unnecessarily big buffer
-        // even for small data. *Shrug*
-        int capacity = Math.max(expectedRemaining, sslEngine.getSession().getPacketBufferSize());
-
-        if (outNetBuffer != null) {
-            outNetBuffer.capacity(capacity);
-        } else {
-            outNetBuffer = IoBuffer.allocate(capacity).minimumCapacity(0);
-        }
-    }
-
-    /* no qualifier */WriteFuture writeNetBuffer(NextFilter nextFilter) throws SSLException {
-        // Check if any net data needed to be writen
-        if (outNetBuffer == null || !outNetBuffer.hasRemaining()) {
-            // no; bail out
-            return null;
-        }
-
-        // set flag that we are writing encrypted data
-        // (used in SSLFilter.filterWrite())
-        writingEncryptedData = true;
-
-        // write net data
-        WriteFuture writeFuture = null;
-
-        try {
-            IoBuffer writeBuffer = fetchOutNetBuffer();
-            writeFuture = new DefaultWriteFuture(session);
-            sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(writeBuffer, writeFuture));
-
-            // loop while more writes required to complete handshake
-            while (needToCompleteHandshake()) {
-                try {
-                    handshake(nextFilter);
-                } catch (SSLException ssle) {
-                    SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
-                    newSsle.initCause(ssle);
-                    throw newSsle;
-                }
-
-                IoBuffer currentOutNetBuffer = fetchOutNetBuffer();
-                
-                if (currentOutNetBuffer != null && currentOutNetBuffer.hasRemaining()) {
-                    writeFuture = new DefaultWriteFuture(session);
-                    sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(currentOutNetBuffer, writeFuture));
-                }
-            }
-        } finally {
-            writingEncryptedData = false;
-        }
-
-        return writeFuture;
-    }
-
-    private SSLEngineResult.Status unwrapHandshake(NextFilter nextFilter) throws SSLException {
-        // Prepare the net data for reading.
-        if (inNetBuffer != null) {
-            inNetBuffer.flip();
-        }
-
-        if ((inNetBuffer == null) || !inNetBuffer.hasRemaining()) {
-            // Need more data.
-            return SSLEngineResult.Status.BUFFER_UNDERFLOW;
-        }
-
-        SSLEngineResult res = unwrap();
-        handshakeStatus = res.getHandshakeStatus();
-
-        checkStatus(res);
-
-        // If handshake finished, no data was produced, and the status is still
-        // ok, try to unwrap more
-        if ((handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED)
-                && (res.getStatus() == SSLEngineResult.Status.OK)
-                && inNetBuffer.hasRemaining()) {
-            res = unwrap();
-
-            // prepare to be written again
-            if (inNetBuffer.hasRemaining()) {
-                inNetBuffer.compact();
-            } else {
-                inNetBuffer.free();
-                inNetBuffer = null;
-            }
-
-            renegotiateIfNeeded(nextFilter, res);
-        } else {
-            // prepare to be written again
-            if (inNetBuffer.hasRemaining()) {
-                inNetBuffer.compact();
-            } else {
-                inNetBuffer.free();
-                inNetBuffer = null;
-            }
-        }
-
-        return res.getStatus();
-    }
-
-    private void renegotiateIfNeeded(NextFilter nextFilter, SSLEngineResult res) throws SSLException {
-        if ((res.getStatus() != SSLEngineResult.Status.CLOSED)
-                && (res.getStatus() != SSLEngineResult.Status.BUFFER_UNDERFLOW)
-                && (res.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)) {
-            // Renegotiation required.
-            handshakeComplete = false;
-            handshakeStatus = res.getHandshakeStatus();
-            handshake(nextFilter);
-        }
-    }
-
-    /**
-     * Decrypt the incoming buffer and move the decrypted data to an
-     * application buffer.
-     */
-    private SSLEngineResult unwrap() throws SSLException {
-        // We first have to create the application buffer if it does not exist
-        if (appBuffer == null) {
-            appBuffer = IoBuffer.allocate(inNetBuffer.remaining());
-        } else {
-            // We already have one, just add the new data into it
-            appBuffer.expand(inNetBuffer.remaining());
-        }
-
-        SSLEngineResult res;
-        Status status;
-        HandshakeStatus localHandshakeStatus;
-
-        do {
-            // Decode the incoming data
-            res = sslEngine.unwrap(inNetBuffer.buf(), appBuffer.buf());
-            status = res.getStatus();
-
-            // We can be processing the Handshake
-            localHandshakeStatus = res.getHandshakeStatus();
-
-            if (status == SSLEngineResult.Status.BUFFER_OVERFLOW) {
-                // We have to grow the target buffer, it's too small.
-                // Then we can call the unwrap method again
-                int newCapacity = sslEngine.getSession().getApplicationBufferSize();
-                
-                if (appBuffer.remaining() >= newCapacity) {
-                    // The buffer is already larger than the max buffer size suggested by the SSL engine.
-                    // Raising it any more will not make sense and it will end up in an endless loop. Throwing an error is safer
-                    throw new SSLException("SSL buffer overflow");
-                }
-
-                appBuffer.expand(newCapacity);
-                continue;
-            }
-        } while (((status == SSLEngineResult.Status.OK) || (status == SSLEngineResult.Status.BUFFER_OVERFLOW))
-                && ((localHandshakeStatus == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) || 
-                        (localHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)));
-
-        return res;
-    }
-
-    /**
-     * Do all the outstanding handshake tasks in the current Thread.
-     */
-    private SSLEngineResult.HandshakeStatus 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) {
-            // TODO : we may have to use a thread pool here to improve the
-            // performances
-            runnable.run();
-        }
-        return sslEngine.getHandshakeStatus();
-    }
-
-    /**
-     * Creates a new MINA 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
-     */
-    /* no qualifier */static IoBuffer copy(ByteBuffer src) {
-        IoBuffer copy = IoBuffer.allocate(src.remaining());
-        copy.put(src);
-        copy.flip();
-        return copy;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-
-        sb.append("SSLStatus <");
-
-        if (handshakeComplete) {
-            sb.append("SSL established");
-        } else {
-            sb.append("Processing Handshake").append("; ");
-            sb.append("Status : ").append(handshakeStatus).append("; ");
-        }
-
-        sb.append(", ");
-        sb.append("HandshakeComplete :").append(handshakeComplete).append(", ");
-        sb.append(">");
-
-        return sb.toString();
-    }
-
-    /**
-     * Free the allocated buffers
-     */
-    /* no qualifier */void release() {
-        if (inNetBuffer != null) {
-            inNetBuffer.free();
-            inNetBuffer = null;
-        }
-
-        if (outNetBuffer != null) {
-            outNetBuffer.free();
-            outNetBuffer = null;
-        }
-    }
+	/** A logger for this class */
+	private static final Logger LOGGER = LoggerFactory.getLogger(SslHandler.class);
+
+	/** The SSL Filter which has created this handler */
+	private final SslFilter sslFilter;
+
+	/** The current session */
+	private final IoSession session;
+
+	private final Queue<IoFilterEvent> preHandshakeEventQueue = new ConcurrentLinkedQueue<>();
+
+	private final Queue<IoFilterEvent> filterWriteEventQueue = new ConcurrentLinkedQueue<>();
+
+	/**
+	 * A queue used to stack all the incoming data until the SSL session is
+	 * established
+	 */
+	private final Queue<IoFilterEvent> messageReceivedEventQueue = new ConcurrentLinkedQueue<>();
+
+	private SSLEngine sslEngine;
+
+	/**
+	 * Encrypted data from the net
+	 */
+	private IoBuffer inNetBuffer;
+
+	/**
+	 * Encrypted data to be written to the net
+	 */
+	private IoBuffer outNetBuffer;
+
+	/**
+	 * Application cleartext data to be read by application
+	 */
+	private IoBuffer appBuffer;
+
+	/**
+	 * Empty buffer used during initial handshake and close operations
+	 */
+	private final IoBuffer emptyBuffer = IoBuffer.allocate(0);
+
+	private SSLEngineResult.HandshakeStatus handshakeStatus;
+
+	/**
+	 * A flag set to true when the first SSL handshake has been completed This is
+	 * used to avoid sending a notification to the application handler when we
+	 * switch to a SECURE or UNSECURE session.
+	 */
+	private boolean firstSSLNegociation;
+
+	/** A flag set to true when a SSL Handshake has been completed */
+	private boolean handshakeComplete;
+
+	/**
+	 * A flag used to indicate to the SslFilter that the buffer it will write is
+	 * already encrypted (this will be the case for data being produced during the
+	 * handshake).
+	 */
+	private boolean writingEncryptedData;
+
+	/**
+	 * Create a new SSL Handler, and initialize it.
+	 *
+	 * @param sslContext
+	 * @throws SSLException
+	 */
+	/* no qualifier */ SslHandler(SslFilter sslFilter, IoSession session) {
+		this.sslFilter = sslFilter;
+		this.session = session;
+	}
+
+	/**
+	 * Initialize the SSL handshake.
+	 *
+	 * @throws SSLException If the underlying SSLEngine handshake initialization
+	 *                      failed
+	 */
+	/* no qualifier */void init() throws SSLException {
+		if (sslEngine != null) {
+			// We already have a SSL engine created, no need to create a new one
+			return;
+		}
+
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{} Initializing the SSL Handler", sslFilter.getSessionInfo(session));
+		}
+
+		InetSocketAddress peer = (InetSocketAddress) session.getAttribute(SslFilter.PEER_ADDRESS);
+
+		// Create the SSL engine here
+		if (peer == null) {
+			sslEngine = sslFilter.sslContext.createSSLEngine();
+		} else {
+			sslEngine = sslFilter.sslContext.createSSLEngine(peer.getHostName(), peer.getPort());
+		}
+
+		// Initialize the engine in client mode if necessary
+		sslEngine.setUseClientMode(sslFilter.isUseClientMode());
+
+		// Initialize the different SslEngine modes
+		if (!sslEngine.getUseClientMode()) {
+			// Those parameters are only valid when in server mode
+			if (sslFilter.isWantClientAuth()) {
+				sslEngine.setWantClientAuth(true);
+			}
+
+			if (sslFilter.isNeedClientAuth()) {
+				sslEngine.setNeedClientAuth(true);
+			}
+		}
+
+		// Set the cipher suite to use by this SslEngine instance
+		if (sslFilter.getEnabledCipherSuites() != null) {
+			sslEngine.setEnabledCipherSuites(sslFilter.getEnabledCipherSuites());
+		}
+
+		// Set the list of enabled protocols
+		if (sslFilter.getEnabledProtocols() != null) {
+			sslEngine.setEnabledProtocols(sslFilter.getEnabledProtocols());
+		}
+
+		// TODO : we may not need to call this method...
+		// However, if we don't call it here, the tests are failing. Why?
+		handshakeStatus = sslEngine.getHandshakeStatus();
+
+		// Default value
+		writingEncryptedData = false;
+
+		// We haven't yet started a SSL negotiation
+		// set the flags accordingly
+		firstSSLNegociation = true;
+		handshakeComplete = false;
+
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{} SSL Handler Initialization done.", sslFilter.getSessionInfo(session));
+		}
+	}
+
+	/**
+	 * Release allocated buffers.
+	 */
+	/* no qualifier */void destroy() {
+		if (sslEngine == null) {
+			return;
+		}
+
+		// Close inbound and flush all remaining data if available.
+		try {
+			sslEngine.closeInbound();
+		} catch (SSLException e) {
+			if (LOGGER.isDebugEnabled()) {
+				LOGGER.debug("Unexpected exception from SSLEngine.closeInbound().", e);
+			}
+		}
+
+		if (outNetBuffer != null) {
+			outNetBuffer.capacity(sslEngine.getSession().getPacketBufferSize());
+		} else {
+			createOutNetBuffer(0);
+		}
+		try {
+			do {
+				outNetBuffer.clear();
+			} while (sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf()).bytesProduced() > 0);
+		} catch (SSLException e) {
+			// Ignore.
+		} finally {
+			outNetBuffer.free();
+			outNetBuffer = null;
+		}
+
+		sslEngine.closeOutbound();
+		sslEngine = null;
+
+		preHandshakeEventQueue.clear();
+	}
+
+	/**
+	 * @return The SSL filter which has created this handler
+	 */
+	/* no qualifier */SslFilter getSslFilter() {
+		return sslFilter;
+	}
+
+	/* no qualifier */IoSession getSession() {
+		return session;
+	}
+
+	/**
+	 * Check if we are writing encrypted data.
+	 */
+	/* no qualifier */boolean isWritingEncryptedData() {
+		return writingEncryptedData;
+	}
+
+	/**
+	 * Check if handshake is completed.
+	 */
+	/* no qualifier */boolean isHandshakeComplete() {
+		return handshakeComplete;
+	}
+
+	/**
+	 * Check if handshake is on going.
+	 */
+	/* no qualifier */boolean notHandshaking() {
+		return handshakeStatus == HandshakeStatus.FINISHED || handshakeStatus == HandshakeStatus.NOT_HANDSHAKING;
+	}
+
+	/* no qualifier */boolean isInboundDone() {
+		return sslEngine == null || sslEngine.isInboundDone();
+	}
+
+	/* no qualifier */boolean isOutboundDone() {
+		return sslEngine == null || sslEngine.isOutboundDone();
+	}
+
+	/**
+	 * Check if there is any need to complete handshake.
+	 */
+	/* no qualifier */boolean needToCompleteHandshake() {
+		return handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone();
+	}
+
+	/* no qualifier */void schedulePreHandshakeWriteRequest(NextFilter nextFilter, WriteRequest writeRequest) {
+		preHandshakeEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
+	}
+
+	/* no qualifier */void flushPreHandshakeEvents() throws SSLException {
+		IoFilterEvent scheduledWrite;
+
+		while ((scheduledWrite = preHandshakeEventQueue.poll()) != null) {
+			sslFilter.filterWrite(scheduledWrite.getNextFilter(), session,
+					(WriteRequest) scheduledWrite.getParameter());
+		}
+	}
+
+	/* no qualifier */void scheduleFilterWrite(NextFilter nextFilter, WriteRequest writeRequest) {
+		filterWriteEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
+	}
+
+	/* no qualifier */void flushFilterWrite() {
+		// Fire events only when the lock is available for this handler.
+		IoFilterEvent event;
+
+		// We need synchronization here inevitably because filterWrite can be
+		// called simultaneously and cause 'bad record MAC' integrity error.
+		while ((event = filterWriteEventQueue.poll()) != null) {
+			NextFilter nextFilter = event.getNextFilter();
+			nextFilter.filterWrite(session, (WriteRequest) event.getParameter());
+		}
+	}
+
+	/**
+	 * Push the newly received data into a queue, waiting for the SSL session to be
+	 * fully established
+	 *
+	 * @param nextFilter The next filter to call
+	 * @param message    The incoming data
+	 */
+	/* no qualifier */void scheduleMessageReceived(NextFilter nextFilter, Object message) {
+		messageReceivedEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.MESSAGE_RECEIVED, session, message));
+	}
+
+	/* no qualifier */void flushMessageReceived() {
+		IoFilterEvent event;
+
+		while ((event = messageReceivedEventQueue.poll()) != null) {
+			NextFilter nextFilter = event.getNextFilter();
+			nextFilter.messageReceived(session, event.getParameter());
+		}
+	}
+
+	/**
+	 * Call when data are read from net. It will perform the initial hanshake or
+	 * decrypt the data if SSL has been initialiaed.
+	 * 
+	 * @param buf        buffer to decrypt
+	 * @param nextFilter Next filter in chain
+	 * @throws SSLException on errors
+	 */
+	/* no qualifier */void messageReceived(NextFilter nextFilter, ByteBuffer buf) throws SSLException {
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{} Processing the received message", sslFilter.getSessionInfo(session));
+		}
+
+		// append buf to inNetBuffer
+		if (inNetBuffer == null) {
+			inNetBuffer = IoBuffer.allocate(buf.remaining()).setAutoExpand(true);
+		}
+
+		inNetBuffer.put(buf);
+
+		if (!handshakeComplete) {
+			handshake(nextFilter);
+		} else {
+			// Prepare the net data for reading.
+			inNetBuffer.flip();
+
+			if (!inNetBuffer.hasRemaining()) {
+				return;
+			}
+
+			SSLEngineResult res = unwrap();
+
+			// prepare to be written again
+			if (inNetBuffer.hasRemaining()) {
+				inNetBuffer.compact();
+			} else {
+				inNetBuffer.free();
+				inNetBuffer = null;
+			}
+
+			checkStatus(res);
+
+			renegotiateIfNeeded(nextFilter, res);
+		}
+
+		if (isInboundDone()) {
+			// Rewind the MINA buffer if not all data is processed and inbound
+			// is finished.
+			int inNetBufferPosition = inNetBuffer == null ? 0 : inNetBuffer.position();
+			buf.position(buf.position() - inNetBufferPosition);
+
+			if (inNetBuffer != null) {
+				inNetBuffer.free();
+				inNetBuffer = null;
+			}
+		}
+	}
+
+	/**
+	 * Get decrypted application data.
+	 * 
+	 * @return buffer with data
+	 */
+	/* no qualifier */IoBuffer fetchAppBuffer() {
+		if (appBuffer == null) {
+			return IoBuffer.allocate(0);
+		} else {
+			IoBuffer newAppBuffer = appBuffer.flip();
+			appBuffer = null;
+
+			return newAppBuffer.shrink();
+		}
+	}
+
+	/**
+	 * Get encrypted data to be sent.
+	 * 
+	 * @return buffer with data
+	 */
+	/* no qualifier */IoBuffer fetchOutNetBuffer() {
+		IoBuffer answer = outNetBuffer;
+
+		if (answer == null) {
+			return emptyBuffer;
+		}
+
+		outNetBuffer = null;
+
+		return answer.shrink();
+	}
+
+	/**
+	 * Encrypt provided buffer. Encrypted data returned by getOutNetBuffer().
+	 * 
+	 * @param src data to encrypt
+	 * @throws SSLException on errors
+	 */
+	/* no qualifier */void encrypt(ByteBuffer src) throws SSLException {
+		if (!handshakeComplete) {
+			throw new IllegalStateException();
+		}
+
+		if (!src.hasRemaining()) {
+			if (outNetBuffer == null) {
+				outNetBuffer = emptyBuffer;
+			}
+			return;
+		}
+
+		createOutNetBuffer(src.remaining());
+
+		// Loop until there is no more data in src
+		while (src.hasRemaining()) {
+
+			SSLEngineResult result = sslEngine.wrap(src, outNetBuffer.buf());
+
+			if (result.getStatus() == SSLEngineResult.Status.OK) {
+				if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
+					doTasks();
+				}
+			} else if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+				outNetBuffer.capacity(outNetBuffer.capacity() << 1);
+				outNetBuffer.limit(outNetBuffer.capacity());
+			} 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
+	 */
+	/* no qualifier */boolean closeOutbound() throws SSLException {
+		if (sslEngine == null || sslEngine.isOutboundDone()) {
+			return false;
+		}
+
+		sslEngine.closeOutbound();
+
+		createOutNetBuffer(0);
+		SSLEngineResult result;
+
+		for (;;) {
+			result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
+			if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+				outNetBuffer.capacity(outNetBuffer.capacity() << 1);
+				outNetBuffer.limit(outNetBuffer.capacity());
+			} else {
+				break;
+			}
+		}
+
+		if (result.getStatus() != SSLEngineResult.Status.CLOSED) {
+			throw new SSLException("Improper close state: " + result);
+		}
+
+		outNetBuffer.flip();
+
+		return true;
+	}
+
+	/**
+	 * @param res
+	 * @throws SSLException
+	 */
+	private void checkStatus(SSLEngineResult res) throws SSLException {
+
+		SSLEngineResult.Status status = res.getStatus();
+
+		/*
+		 * 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.
+		 */
+		switch (status) {
+		case BUFFER_OVERFLOW:
+			throw new SSLException("SSLEngine error during decrypt: " + status + " inNetBuffer: " + inNetBuffer
+					+ "appBuffer: " + appBuffer);
+		case CLOSED:
+			Exception exception = new RuntimeIoException("SSL/TLS close_notify received");
+
+			// Empty the Ssl queue
+			for (IoFilterEvent event : filterWriteEventQueue) {
+				EncryptedWriteRequest writeRequest = (EncryptedWriteRequest) event.getParameter();
+				WriteFuture writeFuture = writeRequest.getParentRequest().getFuture();
+				writeFuture.setException(exception);
+				writeFuture.notifyAll();
+			}
+
+			// Empty the session queue
+			WriteRequestQueue queue = session.getWriteRequestQueue();
+			WriteRequest request = null;
+
+			while ((request = queue.poll(session)) != null) {
+				WriteFuture writeFuture = request.getFuture();
+				writeFuture.setException(exception);
+				writeFuture.notifyAll();
+			}
+
+			// We *must* shutdown session
+			session.closeNow();
+			break;
+		default:
+			break;
+		}
+	}
+
+	/**
+	 * Perform any handshaking processing.
+	 */
+	/* no qualifier */void handshake(NextFilter nextFilter) throws SSLException {
+		for (;;) {
+			switch (handshakeStatus) {
+			case FINISHED:
+				if (LOGGER.isDebugEnabled()) {
+					LOGGER.debug("{} processing the FINISHED state", sslFilter.getSessionInfo(session));
+				}
+
+				session.setAttribute(SslFilter.SSL_SESSION, sslEngine.getSession());
+				handshakeComplete = true;
+
+				// Send the SECURE message only if it's the first SSL handshake
+				if (firstSSLNegociation) {
+					firstSSLNegociation = false;
+
+					nextFilter.event(session, SslEvent.SECURED);
+				}
+
+				if (LOGGER.isDebugEnabled()) {
+					if (!isOutboundDone()) {
+						LOGGER.debug("{} is now secured", sslFilter.getSessionInfo(session));
+					} else {
+						LOGGER.debug("{} is not secured yet", sslFilter.getSessionInfo(session));
+					}
+				}
+
+				return;
+
+			case NEED_TASK:
+				if (LOGGER.isDebugEnabled()) {
+					LOGGER.debug("{} processing the NEED_TASK state", sslFilter.getSessionInfo(session));
+				}
+
+				handshakeStatus = doTasks();
+				break;
+
+			case NEED_UNWRAP:
+				if (LOGGER.isDebugEnabled()) {
+					LOGGER.debug("{} processing the NEED_UNWRAP state", sslFilter.getSessionInfo(session));
+				}
+				// we need more data read
+				SSLEngineResult.Status status = unwrapHandshake(nextFilter);
+
+				if (status == SSLEngineResult.Status.BUFFER_UNDERFLOW
+						&& handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED || isInboundDone()) {
+					// We need more data or the session is closed
+					return;
+				}
+
+				break;
+
+			case NEED_WRAP:
+			case NOT_HANDSHAKING:
+				if (LOGGER.isDebugEnabled()) {
+					LOGGER.debug("{} processing the NEED_WRAP state", sslFilter.getSessionInfo(session));
+				}
+
+				// First make sure that the out buffer is completely empty.
+				// Since we cannot call wrap with data left on the buffer
+				if (outNetBuffer != null && outNetBuffer.hasRemaining()) {
+					return;
+				}
+
+				SSLEngineResult result;
+				createOutNetBuffer(0);
+
+				result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
+
+				while (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+					outNetBuffer.capacity(outNetBuffer.capacity() << 1);
+					outNetBuffer.limit(outNetBuffer.capacity());
+
+					result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
+				}
+
+				outNetBuffer.flip();
+				handshakeStatus = result.getHandshakeStatus();
+				writeNetBuffer(nextFilter);
+				break;
+
+			default:
+				String msg = "Invalid Handshaking State" + handshakeStatus
+						+ " while processing the Handshake for session " + session.getId();
+				LOGGER.error(msg);
+				throw new IllegalStateException(msg);
+			}
+		}
+	}
+
+	private void createOutNetBuffer(int expectedRemaining) {
+		// SSLEngine requires us to allocate unnecessarily big buffer
+		// even for small data. *Shrug*
+		int capacity = Math.max(expectedRemaining, sslEngine.getSession().getPacketBufferSize());
+
+		if (outNetBuffer != null) {
+			outNetBuffer.capacity(capacity);
+		} else {
+			outNetBuffer = IoBuffer.allocate(capacity).minimumCapacity(0);
+		}
+	}
+
+	/* no qualifier */WriteFuture writeNetBuffer(NextFilter nextFilter) throws SSLException {
+		// Check if any net data needed to be writen
+		if (outNetBuffer == null || !outNetBuffer.hasRemaining()) {
+			// no; bail out
+			return null;
+		}
+
+		// set flag that we are writing encrypted data
+		// (used in SSLFilter.filterWrite())
+		writingEncryptedData = true;
+
+		// write net data
+		WriteFuture writeFuture = null;
+
+		try {
+			IoBuffer writeBuffer = fetchOutNetBuffer();
+			writeFuture = new DefaultWriteFuture(session);
+			sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(writeBuffer, writeFuture));
+
+			// loop while more writes required to complete handshake
+			while (needToCompleteHandshake()) {
+				try {
+					handshake(nextFilter);
+				} catch (SSLException ssle) {
+					SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
+					newSsle.initCause(ssle);
+					throw newSsle;
+				}
+
+				IoBuffer currentOutNetBuffer = fetchOutNetBuffer();
+
+				if (currentOutNetBuffer != null && currentOutNetBuffer.hasRemaining()) {
+					writeFuture = new DefaultWriteFuture(session);
+					sslFilter.filterWrite(nextFilter, session,
+							new DefaultWriteRequest(currentOutNetBuffer, writeFuture));
+				}
+			}
+		} finally {
+			writingEncryptedData = false;
+		}
+
+		return writeFuture;
+	}
+
+	private SSLEngineResult.Status unwrapHandshake(NextFilter nextFilter) throws SSLException {
+		// Prepare the net data for reading.
+		if (inNetBuffer != null) {
+			inNetBuffer.flip();
+		}
+
+		if ((inNetBuffer == null) || !inNetBuffer.hasRemaining()) {
+			// Need more data.
+			return SSLEngineResult.Status.BUFFER_UNDERFLOW;
+		}
+
+		SSLEngineResult res = unwrap();
+		handshakeStatus = res.getHandshakeStatus();
+
+		checkStatus(res);
+
+		// If handshake finished, no data was produced, and the status is still
+		// ok, try to unwrap more
+		if ((handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED)
+				&& (res.getStatus() == SSLEngineResult.Status.OK) && inNetBuffer.hasRemaining()) {
+			res = unwrap();
+
+			// prepare to be written again
+			if (inNetBuffer.hasRemaining()) {
+				inNetBuffer.compact();
+			} else {
+				inNetBuffer.free();
+				inNetBuffer = null;
+			}
+
+			renegotiateIfNeeded(nextFilter, res);
+		} else {
+			// prepare to be written again
+			if (inNetBuffer.hasRemaining()) {
+				inNetBuffer.compact();
+			} else {
+				inNetBuffer.free();
+				inNetBuffer = null;
+			}
+		}
+
+		return res.getStatus();
+	}
+
+	private void renegotiateIfNeeded(NextFilter nextFilter, SSLEngineResult res) throws SSLException {
+		if ((res.getStatus() != SSLEngineResult.Status.CLOSED)
+				&& (res.getStatus() != SSLEngineResult.Status.BUFFER_UNDERFLOW)
+				&& (res.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)) {
+			// Renegotiation required.
+			handshakeComplete = false;
+			handshakeStatus = res.getHandshakeStatus();
+			handshake(nextFilter);
+		}
+	}
+
+	/**
+	 * Decrypt the incoming buffer and move the decrypted data to an application
+	 * buffer.
+	 */
+	private SSLEngineResult unwrap() throws SSLException {
+		// We first have to create the application buffer if it does not exist
+		if (appBuffer == null) {
+			appBuffer = IoBuffer.allocate(inNetBuffer.remaining());
+		} else {
+			// We already have one, just add the new data into it
+			appBuffer.expand(inNetBuffer.remaining());
+		}
+
+		SSLEngineResult res;
+		Status status;
+		HandshakeStatus localHandshakeStatus;
+
+		do {
+			// Decode the incoming data
+			res = sslEngine.unwrap(inNetBuffer.buf(), appBuffer.buf());
+			status = res.getStatus();
+
+			// We can be processing the Handshake
+			localHandshakeStatus = res.getHandshakeStatus();
+
+			if (status == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+				// We have to grow the target buffer, it's too small.
+				// Then we can call the unwrap method again
+				int newCapacity = sslEngine.getSession().getApplicationBufferSize();
+
+				if (appBuffer.remaining() >= newCapacity) {
+					// The buffer is already larger than the max buffer size suggested by the SSL
+					// engine.
+					// Raising it any more will not make sense and it will end up in an endless
+					// loop. Throwing an error is safer
+					throw new SSLException("SSL buffer overflow");
+				}
+
+				appBuffer.expand(newCapacity);
+				continue;
+			}
+		} while (((status == SSLEngineResult.Status.OK) || (status == SSLEngineResult.Status.BUFFER_OVERFLOW))
+				&& ((localHandshakeStatus == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
+						|| (localHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)));
+
+		return res;
+	}
+
+	/**
+	 * Do all the outstanding handshake tasks in the current Thread.
+	 */
+	private SSLEngineResult.HandshakeStatus 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) {
+			// TODO : we may have to use a thread pool here to improve the
+			// performances
+			runnable.run();
+		}
+		return sslEngine.getHandshakeStatus();
+	}
+
+	/**
+	 * Creates a new MINA 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
+	 */
+	/* no qualifier */static IoBuffer copy(ByteBuffer src) {
+		IoBuffer copy = IoBuffer.allocate(src.remaining());
+		copy.put(src);
+		copy.flip();
+		return copy;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+
+		sb.append("SSLStatus <");
+
+		if (handshakeComplete) {
+			sb.append("SSL established");
+		} else {
+			sb.append("Processing Handshake").append("; ");
+			sb.append("Status : ").append(handshakeStatus).append("; ");
+		}
+
+		sb.append(", ");
+		sb.append("HandshakeComplete :").append(handshakeComplete).append(", ");
+		sb.append(">");
+
+		return sb.toString();
+	}
+
+	/**
+	 * Free the allocated buffers
+	 */
+	/* no qualifier */void release() {
+		if (inNetBuffer != null) {
+			inNetBuffer.free();
+			inNetBuffer = null;
+		}
+
+		if (outNetBuffer != null) {
+			outNetBuffer.free();
+			outNetBuffer = null;
+		}
+	}
 }
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/package-info.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/package-info.java
index 62cce29..73d622f 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/package-info.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/package-info.java
@@ -19,7 +19,8 @@
  */
 
 /**
- * Classes that implement IoFilter and provide Secure Sockets Layer functionality.
+ * Classes that implement IoFilter and provide Secure Sockets Layer
+ * functionality.
  * 
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  */