You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zookeeper.apache.org by ra...@apache.org on 2015/03/17 18:06:07 UTC

svn commit: r1667358 - in /zookeeper/trunk: ./ src/docs/src/documentation/content/xdocs/ src/java/main/org/apache/zookeeper/ src/java/main/org/apache/zookeeper/common/ src/java/main/org/apache/zookeeper/server/ src/java/main/org/apache/zookeeper/server...

Author: rakeshr
Date: Tue Mar 17 17:06:07 2015
New Revision: 1667358

URL: http://svn.apache.org/r1667358
Log:
ZOOKEEPER-2125 SSL on Netty client-server communication (Hongchao, Ian Dimayuga via rakeshr)

Added:
    zookeeper/trunk/src/java/main/org/apache/zookeeper/common/X509Exception.java
    zookeeper/trunk/src/java/main/org/apache/zookeeper/common/X509Util.java
    zookeeper/trunk/src/java/test/data/ssl/
    zookeeper/trunk/src/java/test/data/ssl/README.md
    zookeeper/trunk/src/java/test/data/ssl/testKeyStore.jks   (with props)
    zookeeper/trunk/src/java/test/data/ssl/testTrustStore.jks   (with props)
    zookeeper/trunk/src/java/test/org/apache/zookeeper/test/SSLTest.java
Modified:
    zookeeper/trunk/CHANGES.txt
    zookeeper/trunk/build.xml
    zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml
    zookeeper/trunk/src/java/main/org/apache/zookeeper/ClientCnxnSocketNetty.java
    zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeper.java
    zookeeper/trunk/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java
    zookeeper/trunk/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java
    zookeeper/trunk/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java
    zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java
    zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ServerConfig.java
    zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java
    zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ZooKeeperServerBean.java
    zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java
    zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/Leader.java
    zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/Learner.java
    zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java
    zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java
    zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java
    zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyZooKeeperServer.java
    zookeeper/trunk/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java
    zookeeper/trunk/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java
    zookeeper/trunk/src/java/test/org/apache/zookeeper/test/ReconfigTest.java

Modified: zookeeper/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/zookeeper/trunk/CHANGES.txt?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/CHANGES.txt (original)
+++ zookeeper/trunk/CHANGES.txt Tue Mar 17 17:06:07 2015
@@ -5,6 +5,8 @@ NEW FEATURES:
 
   ZOOKEEPER-2119 Netty client docs (Hongchao via rakeshr)
 
+  ZOOKEEPER-2125 SSL on Netty client-server communication (Hongchao, Ian Dimayuga via rakeshr)
+
 BUGFIXES:
   ZOOKEEPER-1784 wrong check for COMMITANDACTIVATE in observer code, Learner.java (rgs via shralex).
 

Modified: zookeeper/trunk/build.xml
URL: http://svn.apache.org/viewvc/zookeeper/trunk/build.xml?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/build.xml (original)
+++ zookeeper/trunk/build.xml Tue Mar 17 17:06:07 2015
@@ -77,6 +77,7 @@ xmlns:maven="antlib:org.apache.maven.art
     <property name="test.data.dir" value="${test.java.build.dir}/data" />
     <property name="test.data.invalid.dir" value="${test.data.dir}/invalidsnap" />
     <property name="test.data.buffersize.dir" value="${test.data.dir}/buffersize" />
+    <property name="test.data.ssl.dir" value="${test.data.dir}/ssl" />
     <property name="test.cppunit.dir" value="${test.java.build.dir}/test-cppunit"/>
     <property name="test.tmp.dir" value="${test.java.build.dir}/tmp" />
     <property name="test.output" value="no" />
@@ -1260,6 +1261,7 @@ xmlns:maven="antlib:org.apache.maven.art
         <delete dir="${test.tmp.dir}" />
         <delete dir="${test.data.invalid.dir}" />
         <delete dir="${test.data.buffersize.dir}" />
+        <delete dir="${test.data.ssl.dir}" />
         <delete dir="${test.data.dir}" />
         <mkdir dir="${test.log.dir}" />
         <mkdir dir="${test.tmp.dir}" />
@@ -1272,6 +1274,10 @@ xmlns:maven="antlib:org.apache.maven.art
         <copy todir="${test.data.buffersize.dir}">
             <fileset dir="${basedir}/src/java/test/data/buffersize"/>
         </copy>
+        <mkdir dir="${test.data.ssl.dir}" />
+        <copy todir="${test.data.ssl.dir}">
+            <fileset dir="${basedir}/src/java/test/data/ssl"/>
+        </copy>
     </target>
 
     <condition property="quicktest">
@@ -1643,6 +1649,7 @@ xmlns:maven="antlib:org.apache.maven.art
           <exclude name="**/missing"/>
           <exclude name="**/wrappers*.opt"/>
           <exclude name="CHANGES.txt"/>
+          <exclude name="**/README.md"/>
           <exclude name="README_packaging.txt"/>
           <exclude name="**/TODO"/>
           <exclude name="**/VERSION"/>

Modified: zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml (original)
+++ zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml Tue Mar 17 17:06:07 2015
@@ -614,6 +614,22 @@ server.3=zoo3:2888:3888</programlisting>
             </listitem>
           </varlistentry>
 
+          <varlistentry>
+            <term>secureClientPort</term>
+
+            <listitem>
+              <para>the port to listen on for secure client connections using SSL.
+
+              <emphasis role="bold">clientPort</emphasis> specifies
+                the port for plaintext connections while <emphasis role="bold">
+                  secureClientPort</emphasis> specifies the port for SSL
+                connections. Specifying both enables mixed-mode while omitting
+                either will disable that mode.</para>
+              <para>Note that SSL feature will be enabled when user plugs-in
+                zookeeper.serverCnxnFactory, zookeeper.clientCnxnSocket as Netty.</para>
+            </listitem>
+          </varlistentry>
+
           <varlistentry id="var_datadir">
             <term>dataDir</term>
 
@@ -1035,10 +1051,10 @@ server.3=zoo3:2888:3888</programlisting>
       </section>
 
       <section id="sc_authOptions">
-        <title>Authentication &amp; Authorization Options</title>
+        <title>Encryption, Authentication, Authorization Options</title>
 
         <para>The options in this section allow control over
-        authentication/authorization performed by the service.</para>
+        encryption/authentication/authorization performed by the service.</para>
 
         <variablelist>
           <varlistentry>
@@ -1072,6 +1088,44 @@ server.3=zoo3:2888:3888</programlisting>
               connection.</para>
             </listitem>
           </varlistentry>
+
+          <varlistentry>
+            <term>zookeeper.client.secure</term>
+            <listitem>
+              <para>If you want to connect to server's secure client port, you need to
+                set this property to <emphasis role="bold">true</emphasis> on client.
+                This will connect to server using SSL with specified credentials. Note that
+                you also need to plug-in Netty client.
+              </para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term>ssl.keyStore.location and ssl.keyStore.password</term>
+            <listitem>
+              <para>(Java system properties: <emphasis role="bold">
+                zookeeper.ssl.keyStore.location</emphasis> and <emphasis
+                      role="bold">zookeeper.ssl.keyStore.password</emphasis>)</para>
+
+              <para>Specifies the file path to a JKS containing the local
+                credentials to be used for SSL connections, and the
+                password to unlock the file.</para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term>ssl.trustStore.location and ssl.trustStore.password</term>
+            <listitem>
+              <para>(Java system properties: <emphasis role="bold">
+                zookeeper.ssl.trustStore.location</emphasis> and <emphasis
+                      role="bold">zookeeper.ssl.trustStore.password</emphasis>)</para>
+
+              <para>Specifies the file path to a JKS containing the remote
+                credentials to be used for SSL connections, and the
+                password to unlock the file.</para>
+            </listitem>
+          </varlistentry>
+
         </variablelist>
       </section>
 

Modified: zookeeper/trunk/src/java/main/org/apache/zookeeper/ClientCnxnSocketNetty.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/ClientCnxnSocketNetty.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/ClientCnxnSocketNetty.java (original)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/ClientCnxnSocketNetty.java Tue Mar 17 17:06:07 2015
@@ -20,6 +20,7 @@ package org.apache.zookeeper;
 
 import org.apache.zookeeper.ClientCnxn.EndOfStreamException;
 import org.apache.zookeeper.ClientCnxn.Packet;
+import org.apache.zookeeper.common.X509Util;
 import org.jboss.netty.bootstrap.ClientBootstrap;
 import org.jboss.netty.buffer.ChannelBuffer;
 import org.jboss.netty.buffer.ChannelBuffers;
@@ -36,9 +37,12 @@ import org.jboss.netty.channel.Exception
 import org.jboss.netty.channel.MessageEvent;
 import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
 import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
+import org.jboss.netty.handler.ssl.SslHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
@@ -52,6 +56,8 @@ import java.util.concurrent.atomic.Atomi
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
+import static org.apache.zookeeper.common.X509Exception.SSLContextException;
+
 /**
  * ClientCnxnSocketNetty implements ClientCnxnSocket abstract methods.
  * It's responsible for connecting to server, reading/writing network traffic and
@@ -82,7 +88,7 @@ public class ClientCnxnSocketNetty exten
      * - - cleanup()
      * close()
      * <p/>
-     * Other none lifecycle methods are in jeopardy getting a null channel
+     * Other non-lifecycle methods are in jeopardy getting a null channel
      * when calling in concurrency. We must handle it.
      */
 
@@ -332,13 +338,30 @@ public class ClientCnxnSocketNetty exten
      * connection implementation.
      */
     private class ZKClientPipelineFactory implements ChannelPipelineFactory {
+        private SSLContext sslContext = null;
+        private SSLEngine sslEngine = null;
+
         @Override
         public ChannelPipeline getPipeline() throws Exception {
             ChannelPipeline pipeline = Channels.pipeline();
-            // add ssl here
+            if (Boolean.getBoolean(ZooKeeper.SECURE_CLIENT)) {
+                initSSL(pipeline);
+            }
             pipeline.addLast("handler", new ZKClientHandler());
             return pipeline;
         }
+
+        // The synchronized is to prevent the race on shared variable "sslEngine".
+        // Basically we only need to create it once.
+        private synchronized void initSSL(ChannelPipeline pipeline) throws SSLContextException {
+            if (sslContext == null || sslEngine == null) {
+                sslContext = X509Util.createSSLContext();
+                sslEngine = sslContext.createSSLEngine();
+                sslEngine.setUseClientMode(true);
+            }
+            pipeline.addLast("ssl", new SslHandler(sslEngine));
+            LOG.info("SSL handler added for channel: {}", pipeline.getChannel());
+        }
     }
 
     /**

Modified: zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeper.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeper.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeper.java (original)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeper.java Tue Mar 17 17:06:07 2015
@@ -131,6 +131,8 @@ import org.slf4j.LoggerFactory;
 public class ZooKeeper {
 
     public static final String ZOOKEEPER_CLIENT_CNXN_SOCKET = "zookeeper.clientCnxnSocket";
+    // Setting this to "true" will enable encrypted client-server communication.
+    public static final String SECURE_CLIENT = "zookeeper.client.secure";
 
     protected final ClientCnxn cnxn;
     private static final Logger LOG;

Added: zookeeper/trunk/src/java/main/org/apache/zookeeper/common/X509Exception.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/common/X509Exception.java?rev=1667358&view=auto
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/common/X509Exception.java (added)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/common/X509Exception.java Tue Mar 17 17:06:07 2015
@@ -0,0 +1,67 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.common;
+
+@SuppressWarnings("serial")
+public class X509Exception extends Exception {
+    public X509Exception(String message) {
+        super(message);
+    }
+
+    public X509Exception(Throwable cause) {
+        super(cause);
+    }
+
+    public X509Exception(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public static class KeyManagerException extends X509Exception {
+        public KeyManagerException(String message) {
+            super(message);
+        }
+
+        public KeyManagerException(Throwable cause) {
+            super(cause);
+        }
+    }
+
+    public static class TrustManagerException extends X509Exception {
+        public TrustManagerException(String message) {
+            super(message);
+        }
+
+        public TrustManagerException(Throwable cause) {
+            super(cause);
+        }
+    }
+
+    public static class SSLContextException extends X509Exception {
+        public SSLContextException(String message) {
+            super(message);
+        }
+
+        public SSLContextException(Throwable cause) {
+            super(cause);
+        }
+
+        public SSLContextException(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }
+}

Added: zookeeper/trunk/src/java/main/org/apache/zookeeper/common/X509Util.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/common/X509Util.java?rev=1667358&view=auto
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/common/X509Util.java (added)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/common/X509Util.java Tue Mar 17 17:06:07 2015
@@ -0,0 +1,166 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.common;
+
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.KeyStore;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.zookeeper.common.X509Exception.KeyManagerException;
+import static org.apache.zookeeper.common.X509Exception.SSLContextException;
+import static org.apache.zookeeper.common.X509Exception.TrustManagerException;
+
+/**
+ * Utility code for X509 handling
+ */
+public class X509Util {
+    private static final Logger LOG = LoggerFactory.getLogger(X509Util.class);
+
+    public static final String SSL_KEYSTORE_LOCATION = "zookeeper.ssl.keyStore.location";
+    public static final String SSL_KEYSTORE_PASSWD = "zookeeper.ssl.keyStore.password";
+    public static final String SSL_TRUSTSTORE_LOCATION = "zookeeper.ssl.trustStore.location";
+    public static final String SSL_TRUSTSTORE_PASSWD = "zookeeper.ssl.trustStore.password";
+
+    public static SSLContext createSSLContext() throws SSLContextException {
+        KeyManager[] keyManagers = null;
+        TrustManager[] trustManagers = null;
+
+        String keyStoreLocationProp = System.getProperty(SSL_KEYSTORE_LOCATION);
+        String keyStorePasswordProp = System.getProperty(SSL_KEYSTORE_PASSWD);
+
+        // There are legal states in some use cases for null KeyManager or TrustManager.
+        // But if a user wanna specify one, location and password are required.
+
+        if (keyStoreLocationProp == null && keyStorePasswordProp == null) {
+            LOG.warn("keystore not specified for client connection");
+        } else {
+            if (keyStoreLocationProp == null) {
+                throw new SSLContextException("keystore location not specified for client connection");
+            }
+            if (keyStorePasswordProp == null) {
+                throw new SSLContextException("keystore password not specified for client connection");
+            }
+            try {
+                keyManagers = new KeyManager[]{
+                        createKeyManager(keyStoreLocationProp, keyStorePasswordProp)};
+            } catch (KeyManagerException e) {
+                throw new SSLContextException("Failed to create KeyManager", e);
+            }
+        }
+
+        String trustStoreLocationProp = System.getProperty(SSL_TRUSTSTORE_LOCATION);
+        String trustStorePasswordProp = System.getProperty(SSL_TRUSTSTORE_PASSWD);
+
+        if (trustStoreLocationProp == null && trustStorePasswordProp == null) {
+            LOG.warn("keystore not specified for client connection");
+        } else {
+            if (trustStoreLocationProp == null) {
+                throw new SSLContextException("keystore location not specified for client connection");
+            }
+            if (trustStorePasswordProp == null) {
+                throw new SSLContextException("keystore password not specified for client connection");
+            }
+            try {
+                trustManagers = new TrustManager[]{
+                        createTrustManager(trustStoreLocationProp, trustStorePasswordProp)};
+            } catch (TrustManagerException e) {
+                throw new SSLContextException("Failed to create KeyManager", e);
+            }
+        }
+
+        SSLContext sslContext = null;
+        try {
+            sslContext = SSLContext.getInstance("TLSv1");
+            sslContext.init(keyManagers, trustManagers, null);
+        } catch (Exception e) {
+            throw new SSLContextException(e);
+        }
+        return sslContext;
+    }
+
+    public static X509KeyManager createKeyManager(String keyStoreLocation, String keyStorePassword)
+            throws KeyManagerException {
+        FileInputStream inputStream = null;
+        try {
+            char[] keyStorePasswordChars = keyStorePassword.toCharArray();
+            File keyStoreFile = new File(keyStoreLocation);
+            KeyStore ks = KeyStore.getInstance("JKS");
+            inputStream = new FileInputStream(keyStoreFile);
+            ks.load(inputStream, keyStorePasswordChars);
+            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+            kmf.init(ks, keyStorePasswordChars);
+
+            for (KeyManager km : kmf.getKeyManagers()) {
+                if (km instanceof X509KeyManager) {
+                    return (X509KeyManager) km;
+                }
+            }
+            throw new KeyManagerException("Couldn't find X509KeyManager");
+
+        } catch (Exception e) {
+            throw new KeyManagerException(e);
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {}
+            }
+        }
+    }
+
+    public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword)
+            throws TrustManagerException {
+        FileInputStream inputStream = null;
+        try {
+            char[] trustStorePasswordChars = trustStorePassword.toCharArray();
+            File trustStoreFile = new File(trustStoreLocation);
+            KeyStore ts = KeyStore.getInstance("JKS");
+            inputStream = new FileInputStream(trustStoreFile);
+            ts.load(inputStream, trustStorePasswordChars);
+            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+            tmf.init(ts);
+
+            for (TrustManager tm : tmf.getTrustManagers()) {
+                if (tm instanceof X509TrustManager) {
+                    return (X509TrustManager) tm;
+                }
+            }
+            throw new TrustManagerException("Couldn't find X509TrustManager");
+        } catch (Exception e) {
+            throw new TrustManagerException(e);
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {}
+            }
+        }
+    }
+}
\ No newline at end of file

Modified: zookeeper/trunk/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java (original)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java Tue Mar 17 17:06:07 2015
@@ -129,16 +129,17 @@ public class FinalRequestProcessor imple
             }
         }
 
-        if (request.type == OpCode.closeSession) {
-            ServerCnxnFactory scxn = zks.getServerCnxnFactory();
-            // this might be possible since
-            // we might just be playing diffs from the leader
-            if (scxn != null && request.cnxn == null) {
-                // calling this if we have the cnxn results in the client's
-                // close session response being lost - we've already closed
-                // the session/socket here before we can send the closeSession
-                // in the switch block below
-                scxn.closeSession(request.sessionId);
+        // ZOOKEEPER-558:
+        // In some cases the server does not close the connection (e.g., closeconn buffer
+        // was not being queued — ZOOKEEPER-558) properly. This happens, for example,
+        // when the client closes the connection. The server should still close the session, though.
+        // Calling closeSession() after losing the cnxn, results in the client close session response being dropped.
+        if (request.type == OpCode.closeSession && connClosedByClient(request)) {
+            // We need to check if we can close the session id.
+            // Sometimes the corresponding ServerCnxnFactory could be null because
+            // we are just playing diffs from the leader.
+            if (closeSession(zks.serverCnxnFactory, request.sessionId) ||
+                    closeSession(zks.secureServerCnxnFactory, request.sessionId)) {
                 return;
             }
         }
@@ -472,6 +473,17 @@ public class FinalRequestProcessor imple
         }
     }
 
+    private boolean closeSession(ServerCnxnFactory serverCnxnFactory, long sessionId) {
+        if (serverCnxnFactory == null) {
+            return false;
+        }
+        return serverCnxnFactory.closeSession(sessionId);
+    }
+
+    private boolean connClosedByClient(Request request) {
+        return request.cnxn == null;
+    }
+
     public void shutdown() {
         // we are the final link in the chain
         LOG.info("shutdown of request processor complete");

Modified: zookeeper/trunk/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java (original)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java Tue Mar 17 17:06:07 2015
@@ -639,7 +639,10 @@ public class NIOServerCnxnFactory extend
         new HashSet<SelectorThread>();
 
     @Override
-    public void configure(InetSocketAddress addr, int maxcc) throws IOException {
+    public void configure(InetSocketAddress addr, int maxcc, boolean secure) throws IOException {
+        if (secure) {
+            throw new UnsupportedOperationException("SSL isn't supported in NIOServerCnxn");
+        }
         configureSaslLogin();
 
         maxClientCnxns = maxcc;
@@ -742,12 +745,14 @@ public class NIOServerCnxnFactory extend
     }
 
     @Override
-    public void startup(ZooKeeperServer zks) throws IOException,
-            InterruptedException {
+    public void startup(ZooKeeperServer zks, boolean startServer)
+            throws IOException, InterruptedException {
         start();
         setZooKeeperServer(zks);
-        zks.startdata();
-        zks.startup();
+        if (startServer) {
+            zks.startdata();
+            zks.startup();
+        }
     }
 
     @Override
@@ -908,11 +913,13 @@ public class NIOServerCnxnFactory extend
     }
 
     @Override
-    public void closeSession(long sessionId) {
+    public boolean closeSession(long sessionId) {
         NIOServerCnxn cnxn = sessionMap.remove(sessionId);
         if (cnxn != null) {
             cnxn.close();
+            return true;
         }
+        return false;
     }
 
     @Override

Modified: zookeeper/trunk/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java (original)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java Tue Mar 17 17:06:07 2015
@@ -18,6 +18,7 @@
 
 package org.apache.zookeeper.server;
 
+import static org.apache.zookeeper.common.X509Exception.SSLContextException;
 import static org.jboss.netty.buffer.ChannelBuffers.dynamicBuffer;
 
 import java.io.IOException;
@@ -28,7 +29,10 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Executors;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
 
+import org.apache.zookeeper.common.X509Util;
 import org.jboss.netty.bootstrap.ServerBootstrap;
 import org.jboss.netty.buffer.ChannelBuffer;
 import org.jboss.netty.buffer.ChannelBuffers;
@@ -46,6 +50,7 @@ import org.jboss.netty.channel.WriteComp
 import org.jboss.netty.channel.group.ChannelGroup;
 import org.jboss.netty.channel.group.DefaultChannelGroup;
 import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
+import org.jboss.netty.handler.ssl.SslHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -59,7 +64,7 @@ public class NettyServerCnxnFactory exte
         new HashMap<InetAddress, Set<NettyServerCnxn>>( );
     InetSocketAddress localAddress;
     int maxClientCnxns = 60;
-    
+
     /**
      * This is an inner class since we need to extend SimpleChannelHandler, but
      * NettyServerCnxnFactory already extends ServerCnxnFactory. By making it inner
@@ -241,7 +246,7 @@ public class NettyServerCnxnFactory exte
                 LOG.trace("write complete " + e);
             }
         }
-        
+
     }
     
     CnxnChannelHandler channelHandler = new CnxnChannelHandler();
@@ -261,13 +266,26 @@ public class NettyServerCnxnFactory exte
             @Override
             public ChannelPipeline getPipeline() throws Exception {
                 ChannelPipeline p = Channels.pipeline();
+                if (secure) {
+                    initSSL(p);
+                }
                 p.addLast("servercnxnfactory", channelHandler);
 
                 return p;
             }
         });
     }
-    
+
+    private synchronized void initSSL(ChannelPipeline p) throws SSLContextException {
+        SSLContext sslContext = X509Util.createSSLContext();
+        SSLEngine sslEngine = sslContext.createSSLEngine();
+        sslEngine.setUseClientMode(false);
+        sslEngine.setNeedClientAuth(true);
+
+        p.addLast("ssl", new SslHandler(sslEngine));
+        LOG.info("SSL handler added for channel: {}", p.getChannel());
+    }
+
     @Override
     public void closeAll() {
         if (LOG.isDebugEnabled()) {
@@ -291,7 +309,7 @@ public class NettyServerCnxnFactory exte
     }
 
     @Override
-    public void closeSession(long sessionId) {
+    public boolean closeSession(long sessionId) {
         if (LOG.isDebugEnabled()) {
             LOG.debug("closeSession sessionid:0x" + sessionId);
         }
@@ -302,18 +320,20 @@ public class NettyServerCnxnFactory exte
                 } catch (Exception e) {
                     LOG.warn("exception during session close", e);
                 }
-                break;
+                return true;
             }
         }
+        return false;
     }
 
     @Override
-    public void configure(InetSocketAddress addr, int maxClientCnxns)
+    public void configure(InetSocketAddress addr, int maxClientCnxns, boolean secure)
             throws IOException
     {
         configureSaslLogin();
         localAddress = addr;
         this.maxClientCnxns = maxClientCnxns;
+        this.secure = secure;
     }
 
     /** {@inheritDoc} */
@@ -380,12 +400,14 @@ public class NettyServerCnxnFactory exte
     }
     
     @Override
-    public void startup(ZooKeeperServer zks) throws IOException,
-            InterruptedException {
+    public void startup(ZooKeeperServer zks, boolean startServer)
+            throws IOException, InterruptedException {
         start();
         setZooKeeperServer(zks);
-        zks.startdata();
-        zks.startup();
+        if (startServer) {
+            zks.startdata();
+            zks.startup();
+        }
     }
 
     @Override

Modified: zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java (original)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java Tue Mar 17 17:06:07 2015
@@ -23,7 +23,6 @@ import java.net.InetSocketAddress;
 import java.nio.ByteBuffer;
 import java.util.Collections;
 import java.util.Set;
-import java.util.HashSet;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -43,13 +42,12 @@ import org.slf4j.LoggerFactory;
 public abstract class ServerCnxnFactory {
 
     public static final String ZOOKEEPER_SERVER_CNXN_FACTORY = "zookeeper.serverCnxnFactory";
-
-    public interface PacketProcessor {
-        public void processPacket(ByteBuffer packet, ServerCnxn src);
-    }
     
     private static final Logger LOG = LoggerFactory.getLogger(ServerCnxnFactory.class);
 
+    // Tells whether SSL is enabled on this ServerCnxnFactory
+    protected boolean secure;
+
     /**
      * The buffer will cause the connection to be close when we do a send.
      */
@@ -67,11 +65,19 @@ public abstract class ServerCnxnFactory
         return zkServer;
     }
 
-    public abstract void closeSession(long sessionId);
+    /**
+     * @return true if the cnxn that contains the sessionId exists in this ServerCnxnFactory
+     *         and it's closed. Otherwise false.
+     */
+    public abstract boolean closeSession(long sessionId);
+
+    public void configure(InetSocketAddress addr, int maxcc) throws IOException {
+        configure(addr, maxcc, false);
+    }
+
+    public abstract void configure(InetSocketAddress addr, int maxcc, boolean secure)
+            throws IOException;
 
-    public abstract void configure(InetSocketAddress addr,
-                                   int maxClientCnxns) throws IOException;
-    
     public abstract void reconfigure(InetSocketAddress addr);
     
     
@@ -84,8 +90,14 @@ public abstract class ServerCnxnFactory
     /** Maximum number of connections allowed from particular host (ip) */
     public abstract void setMaxClientCnxnsPerHost(int max);
 
-    public abstract void startup(ZooKeeperServer zkServer)
-        throws IOException, InterruptedException;
+    public void startup(ZooKeeperServer zkServer) throws IOException, InterruptedException {
+        startup(zkServer, true);
+    }
+
+    // This method is to maintain compatiblity of startup(zks) and enable sharing of zks
+    // when we add secureCnxnFactory.
+    public abstract void startup(ZooKeeperServer zkServer, boolean startServer)
+            throws IOException, InterruptedException;
 
     public abstract void join() throws InterruptedException;
 
@@ -94,10 +106,14 @@ public abstract class ServerCnxnFactory
     public abstract void start();
 
     protected ZooKeeperServer zkServer;
-    final public void setZooKeeperServer(ZooKeeperServer zk) {
-        this.zkServer = zk;
-        if (zk != null) {
-            zk.setServerCnxnFactory(this);
+    final public void setZooKeeperServer(ZooKeeperServer zks) {
+        this.zkServer = zks;
+        if (zks != null) {
+            if (secure) {
+                zks.setSecureServerCnxnFactory(this);
+            } else {
+                zks.setServerCnxnFactory(this);
+            }
         }
     }
 

Modified: zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ServerConfig.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ServerConfig.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ServerConfig.java (original)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ServerConfig.java Tue Mar 17 17:06:07 2015
@@ -37,6 +37,7 @@ public class ServerConfig {
     //// to update the "conf" 4letter word
     ////
     protected InetSocketAddress clientPortAddress;
+    protected InetSocketAddress secureClientPortAddress;
     protected File dataDir;
     protected File dataLogDir;
     protected int tickTime = ZooKeeperServer.DEFAULT_TICK_TIME;
@@ -89,18 +90,22 @@ public class ServerConfig {
      * @param config
      */
     public void readFrom(QuorumPeerConfig config) {
-      clientPortAddress = config.getClientPortAddress();
-      dataDir = config.getDataDir();
-      dataLogDir = config.getDataLogDir();
-      tickTime = config.getTickTime();
-      maxClientCnxns = config.getMaxClientCnxns();
-      minSessionTimeout = config.getMinSessionTimeout();
-      maxSessionTimeout = config.getMaxSessionTimeout();
+        clientPortAddress = config.getClientPortAddress();
+        secureClientPortAddress = config.getSecureClientPortAddress();
+        dataDir = config.getDataDir();
+        dataLogDir = config.getDataLogDir();
+        tickTime = config.getTickTime();
+        maxClientCnxns = config.getMaxClientCnxns();
+        minSessionTimeout = config.getMinSessionTimeout();
+        maxSessionTimeout = config.getMaxSessionTimeout();
     }
 
     public InetSocketAddress getClientPortAddress() {
         return clientPortAddress;
     }
+    public InetSocketAddress getSecureClientPortAddress() {
+        return secureClientPortAddress;
+    }
     public File getDataDir() { return dataDir; }
     public File getDataLogDir() { return dataLogDir; }
     public int getTickTime() { return tickTime; }

Modified: zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java (original)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java Tue Mar 17 17:06:07 2015
@@ -118,7 +118,8 @@ public class ZooKeeperServer implements
     final HashMap<String, ChangeRecord> outstandingChangesForPath =
         new HashMap<String, ChangeRecord>();
 
-    private ServerCnxnFactory serverCnxnFactory;
+    protected ServerCnxnFactory serverCnxnFactory;
+    protected ServerCnxnFactory secureServerCnxnFactory;
 
     private final ServerStats serverStats;
     private final ZooKeeperServerListener listener = new ZooKeeperServerListenerImpl();
@@ -177,6 +178,8 @@ public class ZooKeeperServer implements
     public void dumpConf(PrintWriter pwriter) {
         pwriter.print("clientPort=");
         pwriter.println(getClientPort());
+        pwriter.print("secureClientPort=");
+        pwriter.println(getSecureClientPort());
         pwriter.print("dataDir=");
         pwriter.println(zkDb.snapLog.getSnapDir().getAbsolutePath());
         pwriter.print("dataLogDir=");
@@ -184,7 +187,7 @@ public class ZooKeeperServer implements
         pwriter.print("tickTime=");
         pwriter.println(getTickTime());
         pwriter.print("maxClientCnxns=");
-        pwriter.println(serverCnxnFactory.getMaxClientCnxnsPerHost());
+        pwriter.println(getMaxClientCnxnsPerHost());
         pwriter.print("minSessionTimeout=");
         pwriter.println(getMinSessionTimeout());
         pwriter.print("maxSessionTimeout=");
@@ -610,10 +613,14 @@ public class ZooKeeperServer implements
         // register with JMX
         try {
             if (valid) {
-                serverCnxnFactory.registerConnection(cnxn);
+                if (serverCnxnFactory != null && serverCnxnFactory.cnxns.contains(cnxn)) {
+                    serverCnxnFactory.registerConnection(cnxn);
+                } else if (secureServerCnxnFactory != null && secureServerCnxnFactory.cnxns.contains(cnxn)) {
+                    secureServerCnxnFactory.registerConnection(cnxn);
+                }
             }
         } catch (Exception e) {
-                LOG.warn("Failed to register with JMX", e);
+            LOG.warn("Failed to register with JMX", e);
         }
 
         try {
@@ -750,6 +757,10 @@ public class ZooKeeperServer implements
         return serverCnxnFactory;
     }
 
+    public void setSecureServerCnxnFactory(ServerCnxnFactory factory) {
+        secureServerCnxnFactory = factory;
+    }
+
     /**
      * return the last proceesed id from the
      * datatree
@@ -772,7 +783,17 @@ public class ZooKeeperServer implements
      * to this server
      */
     public int getNumAliveConnections() {
-        return serverCnxnFactory.getNumAliveConnections();
+        int numAliveConnections = 0;
+
+        if (serverCnxnFactory != null) {
+            numAliveConnections += serverCnxnFactory.getNumAliveConnections();
+        }
+
+        if (secureServerCnxnFactory != null) {
+            numAliveConnections += secureServerCnxnFactory.getNumAliveConnections();
+        }
+
+        return numAliveConnections;
     }
 
     /**
@@ -817,6 +838,21 @@ public class ZooKeeperServer implements
         return serverCnxnFactory != null ? serverCnxnFactory.getLocalPort() : -1;
     }
 
+    public int getSecureClientPort() {
+        return secureServerCnxnFactory != null ? secureServerCnxnFactory.getLocalPort() : -1;
+    }
+
+    /** Maximum number of connections allowed from particular host (ip) */
+    public int getMaxClientCnxnsPerHost() {
+        if (serverCnxnFactory != null) {
+            return serverCnxnFactory.getMaxClientCnxnsPerHost();
+        }
+        if (secureServerCnxnFactory != null) {
+            return secureServerCnxnFactory.getMaxClientCnxnsPerHost();
+        }
+        return -1;
+    }
+
     public void setTxnLogFactory(FileTxnSnapLog txnLog) {
         this.txnLogFactory = txnLog;
     }
@@ -900,7 +936,12 @@ public class ZooKeeperServer implements
             LOG.info("Client attempting to renew session 0x"
                     + Long.toHexString(clientSessionId)
                     + " at " + cnxn.getRemoteSocketAddress());
-            serverCnxnFactory.closeSession(sessionId);
+            if (serverCnxnFactory != null) {
+                serverCnxnFactory.closeSession(sessionId);
+            }
+            if (secureServerCnxnFactory != null) {
+                secureServerCnxnFactory.closeSession(sessionId);
+            }
             cnxn.setSessionId(sessionId);
             reopenSession(cnxn, sessionId, passwd, sessionTimeout);
         }

Modified: zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ZooKeeperServerBean.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ZooKeeperServerBean.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ZooKeeperServerBean.java (original)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ZooKeeperServerBean.java Tue Mar 17 17:06:07 2015
@@ -90,16 +90,16 @@ public class ZooKeeperServerBean impleme
     }
 
     public int getMaxClientCnxnsPerHost() {
-        ServerCnxnFactory fac = zks.getServerCnxnFactory();
-        if (fac == null) {
-            return -1;
-        }
-        return fac.getMaxClientCnxnsPerHost();
+        return zks.getMaxClientCnxnsPerHost();
     }
 
     public void setMaxClientCnxnsPerHost(int max) {
-        // if fac is null the exception will be propagated to the client
-        zks.getServerCnxnFactory().setMaxClientCnxnsPerHost(max);
+        if (zks.serverCnxnFactory != null) {
+            zks.serverCnxnFactory.setMaxClientCnxnsPerHost(max);
+        }
+        if (zks.secureServerCnxnFactory != null) {
+            zks.secureServerCnxnFactory.setMaxClientCnxnsPerHost(max);
+        }
     }
 
     public int getMinSessionTimeout() {

Modified: zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java (original)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java Tue Mar 17 17:06:07 2015
@@ -42,7 +42,9 @@ public class ZooKeeperServerMain {
     private static final String USAGE =
         "Usage: ZooKeeperServerMain configfile | port datadir [ticktime] [maxcnxns]";
 
+    // ZooKeeper server supports two kinds of connection: unencrypted and encrypted.
     private ServerCnxnFactory cnxnFactory;
+    private ServerCnxnFactory secureCnxnFactory;
 
     private AdminServer adminServer;
 
@@ -122,11 +124,27 @@ public class ZooKeeperServerMain {
             adminServer.setZooKeeperServer(zkServer);
             adminServer.start();
 
-            cnxnFactory = ServerCnxnFactory.createFactory();
-            cnxnFactory.configure(config.getClientPortAddress(),
-                    config.getMaxClientCnxns());
-            cnxnFactory.startup(zkServer);
-            cnxnFactory.join();
+            boolean needStartZKServer = true;
+            if (config.getClientPortAddress() != null) {
+                cnxnFactory = ServerCnxnFactory.createFactory();
+                cnxnFactory.configure(config.getClientPortAddress(), config.getMaxClientCnxns(), false);
+                cnxnFactory.startup(zkServer);
+                // zkServer has been started. So we don't need to start it again in secureCnxnFactory.
+                needStartZKServer = false;
+            }
+            if (config.getSecureClientPortAddress() != null) {
+                secureCnxnFactory = ServerCnxnFactory.createFactory();
+                secureCnxnFactory.configure(config.getSecureClientPortAddress(), config.getMaxClientCnxns(), true);
+                secureCnxnFactory.startup(zkServer, needStartZKServer);
+            }
+
+            if (cnxnFactory != null) {
+                cnxnFactory.join();
+            }
+            if (secureCnxnFactory != null) {
+                secureCnxnFactory.join();
+            }
+
             if (zkServer.isRunning()) {
                 zkServer.shutdown();
             }
@@ -144,7 +162,12 @@ public class ZooKeeperServerMain {
      * Shutdown the serving instance
      */
     protected void shutdown() {
-        cnxnFactory.shutdown();
+        if (cnxnFactory != null) {
+            cnxnFactory.shutdown();
+        }
+        if (secureCnxnFactory != null) {
+            secureCnxnFactory.shutdown();
+        }
         try {
             adminServer.shutdown();
         } catch (AdminServerException e) {

Modified: zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/Leader.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/Leader.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/Leader.java (original)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/Leader.java Tue Mar 17 17:06:07 2015
@@ -532,7 +532,7 @@ public class Leader {
             }
 
             if (!System.getProperty("zookeeper.leaderServes", "yes").equals("no")) {
-                self.cnxnFactory.setZooKeeperServer(zk);
+                self.setZooKeeperServer(zk);
             }
 
             self.adminServer.setZooKeeperServer(zk);
@@ -622,15 +622,14 @@ public class Leader {
         }
 
         // NIO should not accept conenctions
-        self.cnxnFactory.setZooKeeperServer(null);
+        self.setZooKeeperServer(null);
         self.adminServer.setZooKeeperServer(null);
         try {
             ss.close();
         } catch (IOException e) {
             LOG.warn("Ignoring unexpected exception during close",e);
         }
-        // clear all the connections
-        self.cnxnFactory.closeAll();
+        self.closeAllConnections();
         // shutdown the previous zk
         if (zk != null) {
             zk.shutdown();

Modified: zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/Learner.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/Learner.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/Learner.java (original)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/Learner.java Tue Mar 17 17:06:07 2015
@@ -28,7 +28,6 @@ import java.net.ConnectException;
 import java.net.InetSocketAddress;
 import java.net.Socket;
 import java.nio.ByteBuffer;
-import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -41,15 +40,11 @@ import org.apache.jute.OutputArchive;
 import org.apache.jute.Record;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.apache.zookeeper.KeeperException.NoNodeException;
 import org.apache.zookeeper.ZooDefs.OpCode;
-import org.apache.zookeeper.data.Stat;
 import org.apache.zookeeper.server.Request;
 import org.apache.zookeeper.server.ServerCnxn;
 import org.apache.zookeeper.server.ZooTrace;
-import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType;
 import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer;
-import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState;
 import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier;
 import org.apache.zookeeper.server.util.SerializeUtils;
 import org.apache.zookeeper.server.util.ZxidUtils;
@@ -507,7 +502,7 @@ public class Learner {
                        zk.takeSnapshot();
                         self.setCurrentEpoch(newEpoch);
                     }
-                    self.cnxnFactory.setZooKeeperServer(zk);
+                    self.setZooKeeperServer(zk);
                     self.adminServer.setZooKeeperServer(zk);
                     break outerLoop;
                 case Leader.NEWLEADER: // it will be NEWLEADER in v1.0        
@@ -619,10 +614,8 @@ public class Learner {
      * Shutdown the Peer
      */
     public void shutdown() {
-        // set the zookeeper server to null
-        self.cnxnFactory.setZooKeeperServer(null);
-        // clear all the connections
-        self.cnxnFactory.closeAll();
+        self.setZooKeeperServer(null);
+        self.closeAllConnections();
         self.adminServer.setZooKeeperServer(null);
         // shutdown previous zookeeper
         if (zk != null) {

Modified: zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java (original)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java Tue Mar 17 17:06:07 2015
@@ -44,7 +44,6 @@ import java.util.concurrent.atomic.Atomi
 import org.apache.zookeeper.common.AtomicFileWritingIdiom;
 import org.apache.zookeeper.common.AtomicFileWritingIdiom.WriterStatement;
 import org.apache.zookeeper.common.HostNameUtils;
-import org.apache.zookeeper.common.PathUtils;
 import org.apache.zookeeper.common.Time;
 import org.apache.zookeeper.jmx.MBeanRegistry;
 import org.apache.zookeeper.jmx.ZKMBeanInfo;
@@ -99,7 +98,8 @@ public class QuorumPeer extends ZooKeepe
     LeaderElectionBean jmxLeaderElectionBean;
     private QuorumCnxManager qcm;
 
-    /* ZKDatabase is a top level member of quorumpeer
+    /**
+     * ZKDatabase is a top level member of quorumpeer
      * which will be used in all the zookeeperservers
      * instantiated later. Also, it is created once on
      * bootup and only thrown away in case of a truncate
@@ -557,6 +557,8 @@ public class QuorumPeer extends ZooKeepe
     Election electionAlg;
 
     ServerCnxnFactory cnxnFactory;
+    ServerCnxnFactory secureCnxnFactory;
+
     private FileTxnSnapLog logFactory = null;
 
     private final QuorumStats quorumStats;
@@ -615,7 +617,7 @@ public class QuorumPeer extends ZooKeepe
             throw new RuntimeException("My id " + myid + " not in the peer list");
          }
         loadDataBase();
-        cnxnFactory.start();
+        startServerCnxnFactory();
         try {
             adminServer.start();
         } catch (AdminServerException e) {
@@ -1035,7 +1037,7 @@ public class QuorumPeer extends ZooKeepe
         if (follower != null) {
             follower.shutdown();
         }
-        cnxnFactory.shutdown();
+        shutdownServerCnxnFactory();
         if(udpSocket != null) {
             udpSocket.close();
         }
@@ -1160,11 +1162,13 @@ public class QuorumPeer extends ZooKeepe
 
     /** Maximum number of connections allowed from particular host (ip) */
     public int getMaxClientCnxnsPerHost() {
-        ServerCnxnFactory fac = getCnxnFactory();
-        if (fac == null) {
-            return -1;
+        if (cnxnFactory != null) {
+            return cnxnFactory.getMaxClientCnxnsPerHost();
+        }
+        if (secureCnxnFactory != null) {
+            return secureCnxnFactory.getMaxClientCnxnsPerHost();
         }
-        return fac.getMaxClientCnxnsPerHost();
+        return -1;
     }
 
     /** Whether local sessions are enabled */
@@ -1425,16 +1429,56 @@ public class QuorumPeer extends ZooKeepe
         this.quorumListenOnAllIPs = quorumListenOnAllIPs;
     }
 
-    public ServerCnxnFactory getCnxnFactory() {
-        return cnxnFactory;
-    }
-
     public void setCnxnFactory(ServerCnxnFactory cnxnFactory) {
         this.cnxnFactory = cnxnFactory;
     }
 
+    public void setSecureCnxnFactory(ServerCnxnFactory secureCnxnFactory) {
+        this.secureCnxnFactory = secureCnxnFactory;
+    }
+
+    private void startServerCnxnFactory() {
+        if (cnxnFactory != null) {
+            cnxnFactory.start();
+        }
+        if (secureCnxnFactory != null) {
+            secureCnxnFactory.start();
+        }
+    }
+
+    private void shutdownServerCnxnFactory() {
+        if (cnxnFactory != null) {
+            cnxnFactory.shutdown();
+        }
+        if (secureCnxnFactory != null) {
+            secureCnxnFactory.shutdown();
+        }
+    }
+
+    // Leader and learner will control the zookeeper server and pass it into QuorumPeer.
+    public void setZooKeeperServer(ZooKeeperServer zks) {
+        if (cnxnFactory != null) {
+            cnxnFactory.setZooKeeperServer(zks);
+        }
+        if (secureCnxnFactory != null) {
+            secureCnxnFactory.setZooKeeperServer(zks);
+        }
+    }
+
+    public void closeAllConnections() {
+        if (cnxnFactory != null) {
+            cnxnFactory.closeAll();
+        }
+        if (secureCnxnFactory != null) {
+            secureCnxnFactory.closeAll();
+        }
+    }
+
     public int getClientPort() {
-        return cnxnFactory.getLocalPort();
+        if (cnxnFactory != null) {
+            return cnxnFactory.getLocalPort();
+        }
+        return -1;
     }
 
     public void setTxnFactory(FileTxnSnapLog factory) {
@@ -1681,7 +1725,8 @@ public class QuorumPeer extends ZooKeepe
     }
 
     private void updateThreadName() {
-       setName("QuorumPeer" + "[myid=" + getId() + "]" +
-               cnxnFactory.getLocalAddress());
+        String plain = cnxnFactory != null ? cnxnFactory.getLocalAddress().toString() : "disabled";
+        String secure = secureCnxnFactory != null ? secureCnxnFactory.getLocalAddress().toString() : "disabled";
+        setName(String.format("QuorumPeer[myid=%d](plain=%s)(secure=%s)", getId(), plain, secure));
     }
 }

Modified: zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java (original)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java Tue Mar 17 17:06:07 2015
@@ -60,6 +60,7 @@ public class QuorumPeerConfig {
     private static boolean standaloneEnabled = true;
 
     protected InetSocketAddress clientPortAddress;
+    protected InetSocketAddress secureClientPortAddress;
     protected File dataDir;
     protected File dataLogDir;
     protected String dynamicConfigFileStr = null;
@@ -214,7 +215,9 @@ public class QuorumPeerConfig {
     public void parseProperties(Properties zkProp)
     throws IOException, ConfigException {
         int clientPort = 0;
+        int secureClientPort = 0;
         String clientPortAddress = null;
+        String secureClientPortAddress = null;
         VerifyingFileFactory vff = new VerifyingFileFactory.Builder(LOG).warnForRelativePath().build();
         for (Entry<Object, Object> entry : zkProp.entrySet()) {
             String key = entry.getKey().toString().trim();
@@ -231,6 +234,10 @@ public class QuorumPeerConfig {
                 localSessionsUpgradingEnabled = Boolean.parseBoolean(value);
             } else if (key.equals("clientPortAddress")) {
                 clientPortAddress = value.trim();
+            } else if (key.equals("secureClientPort")) {
+                secureClientPort = Integer.parseInt(value);
+            } else if (key.equals("secureClientPortAddress")){
+                secureClientPortAddress = value.trim();
             } else if (key.equals("tickTime")) {
                 tickTime = Integer.parseInt(value);
             } else if (key.equals("maxClientCnxns")) {
@@ -294,15 +301,35 @@ public class QuorumPeerConfig {
         if (dataLogDir == null) {
             dataLogDir = dataDir;
         }
-        if (clientPortAddress != null) {
-           if (clientPort == 0) {
-               throw new IllegalArgumentException("clientPortAddress is set but clientPort is not set");
-        }
-             this.clientPortAddress = new InetSocketAddress(
-                      InetAddress.getByName(clientPortAddress), clientPort);
-        } else if (clientPort!=0){
-             this.clientPortAddress = new InetSocketAddress(clientPort);
-        }    
+
+        if (clientPort == 0) {
+            LOG.info("clientPort is not set");
+            if (this.clientPortAddress != null) {
+                throw new IllegalArgumentException("clientPortAddress is set but clientPort is not set");
+            }
+        } else if (clientPortAddress != null) {
+            this.clientPortAddress = new InetSocketAddress(
+                    InetAddress.getByName(clientPortAddress), clientPort);
+            LOG.info("clientPortAddress is {}", this.clientPortAddress.toString());
+        } else {
+            this.clientPortAddress = new InetSocketAddress(clientPort);
+            LOG.info("clientPortAddress is {}", this.clientPortAddress.toString());
+        }
+
+        if (secureClientPort == 0) {
+            LOG.info("secureClientPort is not set");
+            if (this.secureClientPortAddress != null) {
+                throw new IllegalArgumentException("clientPortAddress is set but clientPort is not set");
+            }
+        } else if (secureClientPortAddress != null) {
+            this.secureClientPortAddress = new InetSocketAddress(
+                    InetAddress.getByName(secureClientPortAddress), secureClientPort);
+            LOG.info("secureClientPortAddress is {}", this.secureClientPortAddress.toString());
+        } else {
+            this.secureClientPortAddress = new InetSocketAddress(secureClientPort);
+            LOG.info("secureClientPortAddress is {}", this.secureClientPortAddress.toString());
+        }
+
         if (tickTime == 0) {
             throw new IllegalArgumentException("tickTime is not set");
         }
@@ -613,6 +640,7 @@ public class QuorumPeerConfig {
     }
 
     public InetSocketAddress getClientPortAddress() { return clientPortAddress; }
+    public InetSocketAddress getSecureClientPortAddress() { return secureClientPortAddress; }
     public File getDataDir() { return dataDir; }
     public File getDataLogDir() { return dataLogDir; }
     public int getTickTime() { return tickTime; }

Modified: zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java (original)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java Tue Mar 17 17:06:07 2015
@@ -135,9 +135,22 @@ public class QuorumPeerMain {
 
       LOG.info("Starting quorum peer");
       try {
-          ServerCnxnFactory cnxnFactory = ServerCnxnFactory.createFactory();
-          cnxnFactory.configure(config.getClientPortAddress(),
-                                config.getMaxClientCnxns());
+          ServerCnxnFactory cnxnFactory = null;
+          ServerCnxnFactory secureCnxnFactory = null;
+
+          if (config.getClientPortAddress() != null) {
+              cnxnFactory = ServerCnxnFactory.createFactory();
+              cnxnFactory.configure(config.getClientPortAddress(),
+                      config.getMaxClientCnxns(),
+                      false);
+          }
+
+          if (config.getSecureClientPortAddress() != null) {
+              secureCnxnFactory = ServerCnxnFactory.createFactory();
+              secureCnxnFactory.configure(config.getSecureClientPortAddress(),
+                      config.getMaxClientCnxns(),
+                      true);
+          }
 
           quorumPeer = new QuorumPeer();
           quorumPeer.setTxnFactory(new FileTxnSnapLog(
@@ -162,6 +175,7 @@ public class QuorumPeerMain {
           }
           quorumPeer.initConfigInZKDatabase();
           quorumPeer.setCnxnFactory(cnxnFactory);
+          quorumPeer.setSecureCnxnFactory(secureCnxnFactory);
           quorumPeer.setLearnerType(config.getPeerType());
           quorumPeer.setSyncEnabled(config.getSyncEnabled());
           quorumPeer.setQuorumListenOnAllIPs(config.getQuorumListenOnAllIPs());

Modified: zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyZooKeeperServer.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyZooKeeperServer.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyZooKeeperServer.java (original)
+++ zookeeper/trunk/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyZooKeeperServer.java Tue Mar 17 17:06:07 2015
@@ -68,7 +68,7 @@ public class ReadOnlyZooKeeperServer ext
         }
         registerJMX(new ReadOnlyBean(this), self.jmxLocalPeerBean);
         super.startup();
-        self.cnxnFactory.setZooKeeperServer(this);
+        self.setZooKeeperServer(this);
         self.adminServer.setZooKeeperServer(this);
         LOG.info("Read-only server started");
     }
@@ -145,9 +145,9 @@ public class ReadOnlyZooKeeperServer ext
         unregisterJMX(this);
 
         // set peer's server to null
-        self.cnxnFactory.setZooKeeperServer(null);
+        self.setZooKeeperServer(null);
         // clear all the connections
-        self.cnxnFactory.closeAll();
+        self.closeAllConnections();
 
         self.adminServer.setZooKeeperServer(null);
 

Added: zookeeper/trunk/src/java/test/data/ssl/README.md
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/test/data/ssl/README.md?rev=1667358&view=auto
==============================================================================
--- zookeeper/trunk/src/java/test/data/ssl/README.md (added)
+++ zookeeper/trunk/src/java/test/data/ssl/README.md Tue Mar 17 17:06:07 2015
@@ -0,0 +1,10 @@
+SSL test data
+===================
+
+testKeyStore.jks
+---
+Testing keystore, password is "testpass".
+
+testTrustStore.jks
+---
+Testing truststore, password is "testpass".

Added: zookeeper/trunk/src/java/test/data/ssl/testKeyStore.jks
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/test/data/ssl/testKeyStore.jks?rev=1667358&view=auto
==============================================================================
Binary file - no diff available.

Propchange: zookeeper/trunk/src/java/test/data/ssl/testKeyStore.jks
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: zookeeper/trunk/src/java/test/data/ssl/testTrustStore.jks
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/test/data/ssl/testTrustStore.jks?rev=1667358&view=auto
==============================================================================
Binary file - no diff available.

Propchange: zookeeper/trunk/src/java/test/data/ssl/testTrustStore.jks
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Modified: zookeeper/trunk/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java (original)
+++ zookeeper/trunk/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java Tue Mar 17 17:06:07 2015
@@ -22,13 +22,10 @@
 package org.apache.zookeeper.server.quorum;
 
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.FilenameFilter;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Properties;
 
 import org.slf4j.Logger;
@@ -49,6 +46,8 @@ public class QuorumPeerTestBase extends
     protected static final Logger LOG = LoggerFactory
             .getLogger(QuorumPeerTestBase.class);
 
+    public static final int TIMEOUT = 3000;
+
     public void process(WatchedEvent event) {
         // ignore for this test
     }
@@ -76,6 +75,12 @@ public class QuorumPeerTestBase extends
             this(myid, quorumCfgSection, true);
         }
 
+        public MainThread(int myid, String quorumCfgSection, Integer secureClientPort, boolean writeDynamicConfigFile)
+                throws  IOException {
+            this(myid, UNSET_STATIC_CLIENTPORT, JettyAdminServer.DEFAULT_PORT, secureClientPort,
+                    quorumCfgSection, null, writeDynamicConfigFile, null);
+        }
+
         public MainThread(int myid, String quorumCfgSection, boolean writeDynamicConfigFile)
                 throws IOException {
             this(myid, UNSET_STATIC_CLIENTPORT, quorumCfgSection, writeDynamicConfigFile);
@@ -114,7 +119,12 @@ public class QuorumPeerTestBase extends
         }
 
         public MainThread(int myid, int clientPort, int adminServerPort, String quorumCfgSection,
-                          String configs, boolean writeDynamicConfigFile, String version)
+                          String configs, boolean writeDynamicConfigFile, String version) throws IOException {
+            this(myid, clientPort, adminServerPort, null, quorumCfgSection, configs, writeDynamicConfigFile, version);
+        }
+
+        public MainThread(int myid, int clientPort, int adminServerPort, Integer secureClientPort,
+                          String quorumCfgSection, String configs, boolean writeDynamicConfigFile, String version)
                 throws IOException {
             tmpDir = ClientBase.createTmpDir();
             LOG.info("id = " + myid + " tmpDir = " + tmpDir + " clientPort = "
@@ -148,6 +158,10 @@ public class QuorumPeerTestBase extends
                 fwriter.write("clientPort=" + clientPort + "\n");
             }
 
+            if (secureClientPort != null) {
+                fwriter.write("secureClientPort=" + secureClientPort + "\n");
+            }
+
             if (writeDynamicConfigFile) {
                 String dynamicConfigFilename = createDynamicFile(quorumCfgSection, version);
                 fwriter.write("dynamicConfigFile=" + dynamicConfigFilename + "\n");

Modified: zookeeper/trunk/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java (original)
+++ zookeeper/trunk/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java Tue Mar 17 17:06:07 2015
@@ -275,8 +275,8 @@ public class Zab1_0Test {
     }
 
     private static final class NullServerCnxnFactory extends ServerCnxnFactory {
-        public void startup(ZooKeeperServer zkServer) throws IOException,
-                InterruptedException {
+        public void startup(ZooKeeperServer zkServer, boolean startServer)
+                throws IOException, InterruptedException {
         }
         public void start() {
         }
@@ -298,10 +298,12 @@ public class Zab1_0Test {
         public Iterable<ServerCnxn> getConnections() {
             return null;
         }
-        public void configure(InetSocketAddress addr, int maxClientCnxns)
+        public void configure(InetSocketAddress addr, int maxcc, boolean secure)
                 throws IOException {
         }
-        public void closeSession(long sessionId) {
+
+        public boolean closeSession(long sessionId) {
+            return false;
         }
         public void closeAll() {
         }

Modified: zookeeper/trunk/src/java/test/org/apache/zookeeper/test/ReconfigTest.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/test/org/apache/zookeeper/test/ReconfigTest.java?rev=1667358&r1=1667357&r2=1667358&view=diff
==============================================================================
--- zookeeper/trunk/src/java/test/org/apache/zookeeper/test/ReconfigTest.java (original)
+++ zookeeper/trunk/src/java/test/org/apache/zookeeper/test/ReconfigTest.java Tue Mar 17 17:06:07 2015
@@ -580,8 +580,7 @@ public class ReconfigTest extends ZKTest
 
         testNormalOperation(zkArr[followerIndex], zkArr[leaderIndex]);
         testServerHasConfig(zkArr[followerIndex], joiningServers, null);
-        Assert.assertTrue(qu.getPeer(followerIndex).peer.getName()
-                .endsWith(String.format(":%d", newClientPort)));
+        Assert.assertEquals(newClientPort, qu.getPeer(followerIndex).peer.getClientPort());
 
         joiningServers.clear();
 

Added: zookeeper/trunk/src/java/test/org/apache/zookeeper/test/SSLTest.java
URL: http://svn.apache.org/viewvc/zookeeper/trunk/src/java/test/org/apache/zookeeper/test/SSLTest.java?rev=1667358&view=auto
==============================================================================
--- zookeeper/trunk/src/java/test/org/apache/zookeeper/test/SSLTest.java (added)
+++ zookeeper/trunk/src/java/test/org/apache/zookeeper/test/SSLTest.java Tue Mar 17 17:06:07 2015
@@ -0,0 +1,159 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ *
+ */
+package org.apache.zookeeper.test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.PortAssignment;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.common.X509Util;
+import org.apache.zookeeper.server.ServerCnxnFactory;
+import org.apache.zookeeper.server.quorum.QuorumPeerTestBase;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SSLTest extends QuorumPeerTestBase {
+
+    @Before
+    public void setup() {
+        String testDataPath = System.getProperty("test.data.dir", "build/test/data");
+        System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory");
+        System.setProperty(ZooKeeper.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty");
+        System.setProperty(ZooKeeper.SECURE_CLIENT, "true");
+        System.setProperty(X509Util.SSL_KEYSTORE_LOCATION, testDataPath + "/ssl/testKeyStore.jks");
+        System.setProperty(X509Util.SSL_KEYSTORE_PASSWD, "testpass");
+        System.setProperty(X509Util.SSL_TRUSTSTORE_LOCATION, testDataPath + "/ssl/testTrustStore.jks");
+        System.setProperty(X509Util.SSL_TRUSTSTORE_PASSWD, "testpass");
+    }
+
+    @After
+    public void teardown() throws Exception {
+        System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY);
+        System.clearProperty(ZooKeeper.ZOOKEEPER_CLIENT_CNXN_SOCKET);
+        System.clearProperty(ZooKeeper.SECURE_CLIENT);
+        System.clearProperty(X509Util.SSL_KEYSTORE_LOCATION);
+        System.clearProperty(X509Util.SSL_KEYSTORE_PASSWD);
+        System.clearProperty(X509Util.SSL_TRUSTSTORE_LOCATION);
+        System.clearProperty(X509Util.SSL_TRUSTSTORE_PASSWD);
+    }
+
+    /**
+     * This test checks that SSL works in cluster setup of ZK servers, which includes:
+     * 1. setting "secureClientPort" in "zoo.cfg" file.
+     * 2. setting jvm flags for serverCnxn, keystore, truststore.
+     * Finally, a zookeeper client should be able to connect to the secure port and
+     * communicate with server via secure connection.
+     * <p/>
+     * Note that in this test a ZK server has two ports -- clientPort and secureClientPort.
+     */
+    @Test
+    public void testSecureQuorumServer() throws Exception {
+        final int SERVER_COUNT = 3;
+        final int clientPorts[] = new int[SERVER_COUNT];
+        final Integer secureClientPorts[] = new Integer[SERVER_COUNT];
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < SERVER_COUNT; i++) {
+            clientPorts[i] = PortAssignment.unique();
+            secureClientPorts[i] = PortAssignment.unique();
+            String server = String.format("server.%d=localhost:%d:%d:participant;localhost:%d",
+                    i, PortAssignment.unique(), PortAssignment.unique(), clientPorts[i]);
+            sb.append(server + "\n");
+        }
+        String quorumCfg = sb.toString();
+
+
+        MainThread[] mt = new MainThread[SERVER_COUNT];
+        for (int i = 0; i < SERVER_COUNT; i++) {
+            mt[i] = new MainThread(i, quorumCfg, secureClientPorts[i], true);
+            mt[i].start();
+        }
+
+        // Servers have been set up. Now go test if secure connection is successful.
+        for (int i = 0; i < SERVER_COUNT; i++) {
+            Assert.assertTrue("waiting for server " + i + " being up",
+                    ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], TIMEOUT));
+
+            final CountDownLatch latch = new CountDownLatch(1);
+            ZooKeeper zk = new ZooKeeper("127.0.0.1:" + secureClientPorts[i], TIMEOUT,
+                    new Watcher() {
+                        @Override
+                        public void process(WatchedEvent event) {
+                            if (event.getState() != Event.KeeperState.SyncConnected) {
+                                Assert.fail("failed to connect to ZK server secure client port");
+                            }
+                            latch.countDown();
+                        }
+                    });
+            if (!latch.await(TIMEOUT, TimeUnit.MILLISECONDS)) {
+                Assert.fail("Timeout connecting to ZK server secure port");
+            }
+            // Do a simple operation to make sure the connection is fine.
+            zk.create("/test", "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+            zk.delete("/test", -1);
+            zk.close();
+        }
+
+        for (int i = 0; i < mt.length; i++) {
+            mt[i].shutdown();
+        }
+    }
+
+
+    /**
+     * Developers might use standalone mode (which is the default for one server).
+     * This test checks SSL works in standalone mode of ZK server.
+     * <p/>
+     * Note that in this test the Zk server has only secureClientPort
+     */
+    @Test
+    public void testSecureStandaloneServer() throws Exception {
+        Integer secureClientPort = PortAssignment.unique();
+        MainThread mt = new MainThread(MainThread.UNSET_MYID, "", secureClientPort, false);
+        mt.start();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        ZooKeeper zk = new ZooKeeper("127.0.0.1:" + secureClientPort, TIMEOUT,
+                new Watcher() {
+                    @Override
+                    public void process(WatchedEvent event) {
+                        if (event.getState() != Event.KeeperState.SyncConnected) {
+                            Assert.fail("failed to connect to ZK server secure client port");
+                        }
+                        latch.countDown();
+                    }
+                });
+        if (!latch.await(TIMEOUT, TimeUnit.MILLISECONDS)) {
+            Assert.fail("Timeout connecting to ZK server secure port");
+        }
+        zk.create("/test", "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+        zk.delete("/test", -1);
+        zk.close();
+        mt.shutdown();
+    }
+}