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

anmolnar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zookeeper.git


The following commit(s) were added to refs/heads/master by this push:
     new 9102d0be1 ZOOKEEPER-4835: Make use of Netty optional when no SSL is 
used
9102d0be1 is described below

commit 9102d0be12e66a5c592d8cbf0c6865f68ffa9252
Author: David Smiley <[email protected]>
AuthorDate: Fri Sep 11 15:05:04 2026 -0400

    ZOOKEEPER-4835: Make use of Netty optional when no SSL is used
    
    Reviewers: PDavid, PDavid, anmolnar
    Author: dsmiley
    Closes #2374 from dsmiley/master
---
 pom.xml                                            |   7 +
 zookeeper-assembly/pom.xml                         |  15 ++
 zookeeper-server/pom.xml                           |   8 +
 .../apache/zookeeper/ClientCnxnSocketNetty.java    |   4 +-
 .../main/java/org/apache/zookeeper/ZooKeeper.java  |  24 ++-
 .../zookeeper/cli/HexDumpOutputFormatter.java      |  40 +++-
 ...lientX509Util.java => ClientNettyX509Util.java} |  43 ++---
 .../apache/zookeeper/common/ClientX509Util.java    | 205 +--------------------
 .../java/org/apache/zookeeper/common/X509Util.java |  20 +-
 .../zookeeper/server/NettyServerCnxnFactory.java   |   9 +-
 .../apache/zookeeper/server/ServerCnxnFactory.java |  45 +++++
 .../apache/zookeeper/server/ZooKeeperServer.java   |   4 +-
 .../zookeeper/server/ZooKeeperServerMain.java      |   2 +-
 .../apache/zookeeper/server/quorum/QuorumPeer.java |   2 +-
 .../zookeeper/server/quorum/QuorumPeerMain.java    |   2 +-
 .../apache/zookeeper/NettyOptionalArchTest.java    |  66 +++++++
 .../zookeeper/cli/HexDumpOutputFormatterTest.java  |  43 +++++
 .../org/apache/zookeeper/common/X509UtilTest.java  |  14 +-
 18 files changed, 282 insertions(+), 271 deletions(-)

diff --git a/pom.xml b/pom.xml
index 60b9903d5..c4343539a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -563,6 +563,7 @@
     <commons-io.version>2.17.0</commons-io.version>
     <burningwave.mockdns.version>0.27.2</burningwave.mockdns.version>
     <dnsjava.version>3.5.1</dnsjava.version>
+    <archunit.version>1.4.2</archunit.version>
     <clover-maven-plugin.version>4.4.1</clover-maven-plugin.version>
     <sonar-maven-plugin.version>3.7.0.1746</sonar-maven-plugin.version>
     <maven.min.version>3.5.0</maven.min.version>
@@ -695,6 +696,12 @@
         <artifactId>mockito-core</artifactId>
         <version>${mockito.version}</version>
       </dependency>
+      <dependency>
+        <groupId>com.tngtech.archunit</groupId>
+        <artifactId>archunit-junit5</artifactId>
+        <version>${archunit.version}</version>
+        <scope>test</scope>
+      </dependency>
       <dependency>
         <groupId>io.netty</groupId>
         <artifactId>netty-bom</artifactId>
diff --git a/zookeeper-assembly/pom.xml b/zookeeper-assembly/pom.xml
index 1779c3c47..c92c522c7 100644
--- a/zookeeper-assembly/pom.xml
+++ b/zookeeper-assembly/pom.xml
@@ -119,6 +119,21 @@
       <groupId>org.xerial.snappy</groupId>
       <artifactId>snappy-java</artifactId>
     </dependency>
+    <!-- Netty is an optional dependency of zookeeper-server but should be
+         included in the server distribution for SSL/TLS support. -->
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-handler</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-transport-native-epoll</artifactId>
+      <classifier>linux-x86_64</classifier>
+    </dependency>
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-tcnative-boringssl-static</artifactId>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/zookeeper-server/pom.xml b/zookeeper-server/pom.xml
index d06138838..71c3e3b57 100644
--- a/zookeeper-server/pom.xml
+++ b/zookeeper-server/pom.xml
@@ -65,15 +65,18 @@
     <dependency>
       <groupId>io.netty</groupId>
       <artifactId>netty-handler</artifactId>
+      <optional>true</optional>
     </dependency>
     <dependency>
       <groupId>io.netty</groupId>
       <artifactId>netty-transport-native-epoll</artifactId>
       <classifier>linux-x86_64</classifier>
+      <optional>true</optional>
     </dependency>
     <dependency>
       <groupId>io.netty</groupId>
       <artifactId>netty-tcnative-boringssl-static</artifactId>
+      <optional>true</optional>
     </dependency>
     <dependency>
       <groupId>org.slf4j</groupId>
@@ -180,6 +183,11 @@
       <artifactId>tools</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.tngtech.archunit</groupId>
+      <artifactId>archunit-junit5</artifactId>
+      <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>org.xerial.snappy</groupId>
       <artifactId>snappy-java</artifactId>
diff --git 
a/zookeeper-server/src/main/java/org/apache/zookeeper/ClientCnxnSocketNetty.java
 
b/zookeeper-server/src/main/java/org/apache/zookeeper/ClientCnxnSocketNetty.java
index cc8f5a908..075ea37a3 100644
--- 
a/zookeeper-server/src/main/java/org/apache/zookeeper/ClientCnxnSocketNetty.java
+++ 
b/zookeeper-server/src/main/java/org/apache/zookeeper/ClientCnxnSocketNetty.java
@@ -51,7 +51,7 @@
 import org.apache.zookeeper.ClientCnxn.EndOfStreamException;
 import org.apache.zookeeper.ClientCnxn.Packet;
 import org.apache.zookeeper.client.ZKClientConfig;
-import org.apache.zookeeper.common.ClientX509Util;
+import org.apache.zookeeper.common.ClientNettyX509Util;
 import org.apache.zookeeper.common.NettyUtils;
 import org.apache.zookeeper.common.X509Exception;
 import org.slf4j.Logger;
@@ -446,7 +446,7 @@ private synchronized void initSSL(ChannelPipeline pipeline)
             throws X509Exception.SSLContextException, 
X509Exception.KeyManagerException,
                    X509Exception.TrustManagerException, SSLException {
             if (sslContext == null) {
-                try (ClientX509Util x509Util = new ClientX509Util()) {
+                try (ClientNettyX509Util x509Util = new ClientNettyX509Util()) 
{
                     sslContext = 
x509Util.createNettySslContextForClient(clientConfig);
                 }
             }
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeper.java 
b/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeper.java
index 239f97b0f..5bc7a7b98 100644
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeper.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeper.java
@@ -3141,11 +3141,20 @@ protected SocketAddress testableLocalSocketAddress() {
     }
 
     private ClientCnxnSocket getClientCnxnSocket() throws IOException {
+        // Derived from a same-package class rather than a string literal so 
the shade plugin relocates it.
+        final String nettyClientCnxnSocketName = 
ClientCnxnSocketNIO.class.getPackageName() + ".ClientCnxnSocketNetty";
         String clientCnxnSocketName = 
getClientConfig().getProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET);
-        if (clientCnxnSocketName == null || 
clientCnxnSocketName.equals(ClientCnxnSocketNIO.class.getSimpleName())) {
+        if (clientCnxnSocketName == null) {
+            boolean secureClient = 
getClientConfig().getBoolean(ZKClientConfig.SECURE_CLIENT);
+            if (secureClient) {
+                clientCnxnSocketName = nettyClientCnxnSocketName;
+            } else {
+                clientCnxnSocketName = ClientCnxnSocketNIO.class.getName();
+            }
+        } else if 
(clientCnxnSocketName.equals(ClientCnxnSocketNIO.class.getSimpleName())) {
             clientCnxnSocketName = ClientCnxnSocketNIO.class.getName();
-        } else if 
(clientCnxnSocketName.equals(ClientCnxnSocketNetty.class.getSimpleName())) {
-            clientCnxnSocketName = ClientCnxnSocketNetty.class.getName();
+        } else if (clientCnxnSocketName.equals("ClientCnxnSocketNetty")) {
+            clientCnxnSocketName = nettyClientCnxnSocketName;
         }
 
         try {
@@ -3153,8 +3162,13 @@ private ClientCnxnSocket getClientCnxnSocket() throws 
IOException {
                                                        
.getDeclaredConstructor(ZKClientConfig.class);
             ClientCnxnSocket clientCxnSocket = (ClientCnxnSocket) 
clientCxnConstructor.newInstance(getClientConfig());
             return clientCxnSocket;
-        } catch (Exception e) {
-            throw new IOException("Couldn't instantiate " + 
clientCnxnSocketName, e);
+        } catch (Exception | NoClassDefFoundError e) {
+            String msg = "Couldn't instantiate " + clientCnxnSocketName;
+            if (getClientConfig().getBoolean(ZKClientConfig.SECURE_CLIENT)) {
+                msg += ". SSL/TLS support requires Netty; please add 
netty-handler"
+                    + " (and optionally netty-tcnative-boringssl-static) to 
your project's dependencies.";
+            }
+            throw new IOException(msg, e);
         }
     }
 
diff --git 
a/zookeeper-server/src/main/java/org/apache/zookeeper/cli/HexDumpOutputFormatter.java
 
b/zookeeper-server/src/main/java/org/apache/zookeeper/cli/HexDumpOutputFormatter.java
index d545a422e..a446b54cf 100644
--- 
a/zookeeper-server/src/main/java/org/apache/zookeeper/cli/HexDumpOutputFormatter.java
+++ 
b/zookeeper-server/src/main/java/org/apache/zookeeper/cli/HexDumpOutputFormatter.java
@@ -18,17 +18,45 @@
 
 package org.apache.zookeeper.cli;
 
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufUtil;
-import io.netty.buffer.Unpooled;
-
 public class HexDumpOutputFormatter implements OutputFormatter {
 
     public static final HexDumpOutputFormatter INSTANCE = new 
HexDumpOutputFormatter();
 
+    private static final int BYTES_PER_ROW = 16;
+    private static final int ASCII_PRINTABLE_MIN = 0x20; // space
+    private static final int ASCII_PRINTABLE_MAX = 0x7f; // DEL (exclusive)
+    private static final String LINE_SEPARATOR = System.lineSeparator();
+    private static final String HEADER_LINE =
+        "         +-------------------------------------------------+" + 
LINE_SEPARATOR
+        + "         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |" + 
LINE_SEPARATOR
+        + 
"+--------+-------------------------------------------------+----------------+";
+    private static final String FOOTER_LINE =
+        
"+--------+-------------------------------------------------+----------------+";
+
     @Override
     public String format(byte[] data) {
-        ByteBuf buf = Unpooled.wrappedBuffer(data);
-        return ByteBufUtil.prettyHexDump(buf);
+        if (data == null || data.length == 0) {
+            return "";
+        }
+        StringBuilder sb = new StringBuilder();
+        sb.append(HEADER_LINE).append(LINE_SEPARATOR);
+        for (int offset = 0; offset < data.length; offset += BYTES_PER_ROW) {
+            sb.append(String.format("|%08x|", offset));
+            StringBuilder charPart = new StringBuilder();
+            for (int i = 0; i < BYTES_PER_ROW; i++) {
+                if (offset + i < data.length) {
+                    int b = data[offset + i] & 0xFF;
+                    sb.append(String.format(" %02x", b));
+                    char c = (char) b;
+                    charPart.append(c >= ASCII_PRINTABLE_MIN && c < 
ASCII_PRINTABLE_MAX ? c : '.');
+                } else {
+                    sb.append("   ");
+                    charPart.append(' ');
+                }
+            }
+            sb.append(" 
|").append(charPart).append("|").append(LINE_SEPARATOR);
+        }
+        sb.append(FOOTER_LINE);
+        return sb.toString();
     }
 }
diff --git 
a/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java
 
b/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientNettyX509Util.java
similarity index 90%
copy from 
zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java
copy to 
zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientNettyX509Util.java
index 1c1d6ca88..227fdde8c 100644
--- 
a/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java
+++ 
b/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientNettyX509Util.java
@@ -37,32 +37,14 @@
 import org.slf4j.LoggerFactory;
 
 /**
- * X509 utilities specific for client-server communication framework.
+ * Extends {@link ClientX509Util} with Netty-specific SSL context creation
+ * methods. This class is only loaded when Netty is present on the classpath.
+ * Code that only needs SSL property names should use {@link ClientX509Util}
+ * directly so that Netty remains an optional dependency.
  */
-public class ClientX509Util extends X509Util {
+public class ClientNettyX509Util extends ClientX509Util {
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(ClientX509Util.class);
-
-    private final String sslAuthProviderProperty = getConfigPrefix() + 
"authProvider";
-    private final String sslProviderProperty = getConfigPrefix() + 
"sslProvider";
-
-    @Override
-    protected String getConfigPrefix() {
-        return "zookeeper.ssl.";
-    }
-
-    @Override
-    protected boolean shouldVerifyClientHostname() {
-        return false;
-    }
-
-    public String getSslAuthProviderProperty() {
-        return sslAuthProviderProperty;
-    }
-
-    public String getSslProviderProperty() {
-        return sslProviderProperty;
-    }
+    private static final Logger LOG = 
LoggerFactory.getLogger(ClientNettyX509Util.class);
 
     public SslContext createNettySslContextForClient(ZKConfig config)
         throws X509Exception.SSLContextException, 
X509Exception.KeyManagerException,
@@ -131,7 +113,7 @@ public SslContext createNettySslContextForServer(ZKConfig 
config, KeyManager key
         if (enabledProtocols != null) {
             sslContextBuilder.protocols(enabledProtocols);
         }
-        
sslContextBuilder.clientAuth(getClientAuth(config).toNettyClientAuth());
+        sslContextBuilder.clientAuth(toNettyClientAuth(getClientAuth(config)));
         Iterable<String> enabledCiphers = getCipherSuites(config);
         if (enabledCiphers != null) {
             sslContextBuilder.ciphers(enabledCiphers);
@@ -182,7 +164,7 @@ private SslContext createNettyJdkSslContext(ZKConfig 
config, SSLContext sslConte
             getCipherSuites(config),
             IdentityCipherSuiteFilter.INSTANCE,
             null,
-            isClient ? X509Util.ClientAuth.NONE.toNettyClientAuth() : 
getClientAuth(config).toNettyClientAuth(),
+            isClient ? toNettyClientAuth(X509Util.ClientAuth.NONE) : 
toNettyClientAuth(getClientAuth(config)),
             getEnabledProtocols(config),
             false);
 
@@ -232,6 +214,15 @@ private X509Util.ClientAuth getClientAuth(final ZKConfig 
config) {
         return 
X509Util.ClientAuth.fromPropertyValue(config.getProperty(getSslClientAuthProperty()));
     }
 
+    private static io.netty.handler.ssl.ClientAuth 
toNettyClientAuth(X509Util.ClientAuth clientAuth) {
+        switch (clientAuth) {
+            case NONE: return io.netty.handler.ssl.ClientAuth.NONE;
+            case WANT: return io.netty.handler.ssl.ClientAuth.OPTIONAL;
+            case NEED: return io.netty.handler.ssl.ClientAuth.REQUIRE;
+            default: throw new IllegalArgumentException("Unknown ClientAuth: " 
+ clientAuth);
+        }
+    }
+
     private Iterable<String> getCipherSuites(final ZKConfig config) {
         String cipherSuitesInput = 
config.getProperty(getSslCipherSuitesProperty());
         if (cipherSuitesInput == null) {
diff --git 
a/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java
 
b/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java
index 1c1d6ca88..473aab880 100644
--- 
a/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java
+++ 
b/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java
@@ -18,31 +18,15 @@
 
 package org.apache.zookeeper.common;
 
-import io.netty.handler.ssl.DelegatingSslContext;
-import io.netty.handler.ssl.IdentityCipherSuiteFilter;
-import io.netty.handler.ssl.JdkSslContext;
-import io.netty.handler.ssl.OpenSsl;
-import io.netty.handler.ssl.SslContext;
-import io.netty.handler.ssl.SslContextBuilder;
-import io.netty.handler.ssl.SslProvider;
-import java.security.Security;
-import java.util.Arrays;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLParameters;
-import javax.net.ssl.TrustManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 /**
  * X509 utilities specific for client-server communication framework.
+ *
+ * <p>This class is intentionally free of Netty dependencies so it can be 
loaded
+ * without Netty on the classpath. For Netty SSL context creation use
+ * {@link ClientNettyX509Util}.
  */
 public class ClientX509Util extends X509Util {
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(ClientX509Util.class);
-
     private final String sslAuthProviderProperty = getConfigPrefix() + 
"authProvider";
     private final String sslProviderProperty = getConfigPrefix() + 
"sslProvider";
 
@@ -63,185 +47,4 @@ public String getSslAuthProviderProperty() {
     public String getSslProviderProperty() {
         return sslProviderProperty;
     }
-
-    public SslContext createNettySslContextForClient(ZKConfig config)
-        throws X509Exception.SSLContextException, 
X509Exception.KeyManagerException,
-               X509Exception.TrustManagerException, SSLException {
-        SSLContext suppliedSSLContext = loadSuppliedSSLContext(config);
-        if (suppliedSSLContext != null) {
-            return createNettyJdkSslContext(config, suppliedSSLContext, true);
-        }
-
-        SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
-
-        KeyManager km = buildClientKeyManager(config);
-        if (km != null) {
-            sslContextBuilder.keyManager(km);
-        }
-
-        TrustManager tm = buildTrustManager(config);
-        if (tm != null) {
-            sslContextBuilder.trustManager(tm);
-        }
-
-        handleTcnativeOcspStapling(sslContextBuilder, config);
-        String[] enabledProtocols = getEnabledProtocols(config);
-        if (enabledProtocols != null) {
-            sslContextBuilder.protocols(enabledProtocols);
-        }
-        Iterable<String> enabledCiphers = getCipherSuites(config);
-        if (enabledCiphers != null) {
-            sslContextBuilder.ciphers(enabledCiphers);
-        }
-        sslContextBuilder.sslProvider(getSslProvider(config));
-
-        SslContext sslContext1 = sslContextBuilder.build();
-
-        if ((getFipsMode(config) || tm == null) && 
isServerHostnameVerificationEnabled(config)) {
-            return addHostnameVerification(sslContext1, "Server");
-        } else {
-            return sslContext1;
-        }
-    }
-
-    public SslContext createNettySslContextForServer(ZKConfig config)
-        throws X509Exception.SSLContextException, 
X509Exception.KeyManagerException, X509Exception.TrustManagerException, 
SSLException {
-        SSLContext suppliedSSLContext = loadSuppliedSSLContext(config);
-        if (suppliedSSLContext != null) {
-            return createNettyJdkSslContext(config, suppliedSSLContext, false);
-        }
-
-        KeyManager km = buildKeyManager(config);
-        if (km == null) {
-            throw new X509Exception.SSLContextException(
-                "Keystore is required for SSL server: " + 
getSslKeystoreLocationProperty());
-        }
-        return createNettySslContextForServer(config, km, 
buildServerTrustManager(config));
-    }
-
-    public SslContext createNettySslContextForServer(ZKConfig config, 
KeyManager keyManager, TrustManager trustManager) throws SSLException {
-        SslContextBuilder sslContextBuilder = 
SslContextBuilder.forServer(keyManager);
-
-        if (trustManager != null) {
-            sslContextBuilder.trustManager(trustManager);
-        }
-
-        handleTcnativeOcspStapling(sslContextBuilder, config);
-        String[] enabledProtocols = getEnabledProtocols(config);
-        if (enabledProtocols != null) {
-            sslContextBuilder.protocols(enabledProtocols);
-        }
-        
sslContextBuilder.clientAuth(getClientAuth(config).toNettyClientAuth());
-        Iterable<String> enabledCiphers = getCipherSuites(config);
-        if (enabledCiphers != null) {
-            sslContextBuilder.ciphers(enabledCiphers);
-        }
-        sslContextBuilder.sslProvider(getSslProvider(config));
-
-        SslContext sslContext1 = sslContextBuilder.build();
-
-        if ((getFipsMode(config) || trustManager == null) && 
isClientHostnameVerificationEnabled(config)) {
-            return addHostnameVerification(sslContext1, "Client");
-        } else {
-            return sslContext1;
-        }
-    }
-
-    /**
-     * Wraps a user supplied {@link SSLContext} in a Netty {@link SslContext}, 
applying the configured
-     * protocols, cipher suites, client auth mode and hostname verification on 
top of it.
-     *
-     * <p>A supplied SSLContext carries its own key and trust managers, so it 
can only be used with the
-     * JDK SSL provider: the OpenSSL providers build their own native context 
and cannot delegate to it.
-     *
-     * <p>Unlike the file based path, hostname verification is applied 
whenever it is enabled. The file
-     * based path relies on {@link ZKTrustManager} to verify hostnames and 
only falls back to endpoint
-     * identification when no trust manager is available, which is never the 
case for a supplied context.
-     *
-     * @param config     the configuration to read the SSL options from.
-     * @param sslContext the user supplied SSLContext.
-     * @param isClient   {@code true} to create a client side context, {@code 
false} for server side.
-     * @return the Netty SslContext.
-     * @throws X509Exception.SSLContextException if a non JDK SSL provider is 
configured.
-     */
-    private SslContext createNettyJdkSslContext(ZKConfig config, SSLContext 
sslContext, boolean isClient)
-        throws X509Exception.SSLContextException {
-        SslProvider sslProvider = getSslProvider(config);
-        if (sslProvider != SslProvider.JDK) {
-            throw new X509Exception.SSLContextException("An SSLContext 
supplied through "
-                                                       + 
getSslContextSupplierClassProperty()
-                                                       + " can only be used 
with the JDK SSL provider, but "
-                                                       + 
getSslProviderProperty()
-                                                       + " is set to "
-                                                       + sslProvider);
-        }
-
-        SslContext nettySslContext = new JdkSslContext(
-            sslContext,
-            isClient,
-            getCipherSuites(config),
-            IdentityCipherSuiteFilter.INSTANCE,
-            null,
-            isClient ? X509Util.ClientAuth.NONE.toNettyClientAuth() : 
getClientAuth(config).toNettyClientAuth(),
-            getEnabledProtocols(config),
-            false);
-
-        boolean hostnameVerificationEnabled = isClient
-            ? isServerHostnameVerificationEnabled(config)
-            : isClientHostnameVerificationEnabled(config);
-        if (hostnameVerificationEnabled) {
-            return addHostnameVerification(nettySslContext, isClient ? 
"Server" : "Client");
-        }
-        return nettySslContext;
-    }
-
-    private SslContextBuilder handleTcnativeOcspStapling(SslContextBuilder 
builder, ZKConfig config) {
-        SslProvider sslProvider = getSslProvider(config);
-        boolean tcnative = sslProvider == SslProvider.OPENSSL || sslProvider 
== SslProvider.OPENSSL_REFCNT;
-        boolean ocspEnabled = config.getBoolean(getSslOcspEnabledProperty(), 
Boolean.parseBoolean(Security.getProperty("ocsp.enable")));
-
-        if (tcnative && ocspEnabled && OpenSsl.isOcspSupported()) {
-            builder.enableOcsp(ocspEnabled);
-        }
-        return builder;
-    }
-
-    private SslContext addHostnameVerification(SslContext sslContext, String 
clientOrServer) {
-        return new DelegatingSslContext(sslContext) {
-            @Override
-            protected void initEngine(SSLEngine sslEngine) {
-                SSLParameters sslParameters = sslEngine.getSSLParameters();
-                sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
-                sslEngine.setSSLParameters(sslParameters);
-                if (LOG.isDebugEnabled()) {
-                    LOG.debug("{} hostname verification: enabled HTTPS style 
endpoint identification algorithm", clientOrServer);
-                }
-            }
-        };
-    }
-
-    private String[] getEnabledProtocols(final ZKConfig config) {
-        String enabledProtocolsInput = 
config.getProperty(getSslEnabledProtocolsProperty());
-        if (enabledProtocolsInput == null) {
-            return null;
-        }
-        return enabledProtocolsInput.split(",");
-    }
-
-    private X509Util.ClientAuth getClientAuth(final ZKConfig config) {
-        return 
X509Util.ClientAuth.fromPropertyValue(config.getProperty(getSslClientAuthProperty()));
-    }
-
-    private Iterable<String> getCipherSuites(final ZKConfig config) {
-        String cipherSuitesInput = 
config.getProperty(getSslCipherSuitesProperty());
-        if (cipherSuitesInput == null) {
-            return null;
-        } else {
-            return Arrays.asList(cipherSuitesInput.split(","));
-        }
-    }
-
-    public SslProvider getSslProvider(ZKConfig config) {
-        return 
SslProvider.valueOf(config.getProperty(getSslProviderProperty(), "JDK"));
-    }
 }
diff --git 
a/zookeeper-server/src/main/java/org/apache/zookeeper/common/X509Util.java 
b/zookeeper-server/src/main/java/org/apache/zookeeper/common/X509Util.java
index 993404f78..e6cd00136 100644
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/common/X509Util.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/common/X509Util.java
@@ -55,7 +55,6 @@
 import org.apache.zookeeper.common.X509Exception.KeyManagerException;
 import org.apache.zookeeper.common.X509Exception.SSLContextException;
 import org.apache.zookeeper.common.X509Exception.TrustManagerException;
-import org.apache.zookeeper.server.NettyServerCnxnFactory;
 import org.apache.zookeeper.server.auth.ProviderRegistry;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -69,6 +68,7 @@ public abstract class X509Util implements Closeable, 
AutoCloseable {
 
     private static final String REJECT_CLIENT_RENEGOTIATION_PROPERTY = 
"jdk.tls.rejectClientInitiatedRenegotiation";
     public static final String FIPS_MODE_PROPERTY = "zookeeper.fips-mode";
+    public static final String CLIENT_CERT_RELOAD_KEY = 
"zookeeper.client.certReload";
     private static final boolean FIPS_MODE_DEFAULT = true;
     public static final String TLS_1_1 = "TLSv1.1";
     public static final String TLS_1_2 = "TLSv1.2";
@@ -133,15 +133,9 @@ public static String defaultTlsProtocol(ZKConfig config) {
      * If the config property is not set, the default value is NEED.
      */
     public enum ClientAuth {
-        NONE(io.netty.handler.ssl.ClientAuth.NONE),
-        WANT(io.netty.handler.ssl.ClientAuth.OPTIONAL),
-        NEED(io.netty.handler.ssl.ClientAuth.REQUIRE);
-
-        private final io.netty.handler.ssl.ClientAuth nettyAuth;
-
-        ClientAuth(io.netty.handler.ssl.ClientAuth nettyAuth) {
-            this.nettyAuth = nettyAuth;
-        }
+        NONE,
+        WANT,
+        NEED;
 
         /**
          * Converts a property value to a ClientAuth enum. If the input string 
is empty or null, returns
@@ -156,10 +150,6 @@ public static ClientAuth fromPropertyValue(String prop) {
             }
             return ClientAuth.valueOf(prop.toUpperCase());
         }
-
-        public io.netty.handler.ssl.ClientAuth toNettyClientAuth() {
-            return nettyAuth;
-        }
     }
 
     private final String sslProtocolProperty = getConfigPrefix() + "protocol";
@@ -371,7 +361,7 @@ private void resetDefaultSSLContextAndOptions() throws 
X509Exception.SSLContextE
         SSLContextAndOptions newContext = createSSLContextAndOptions();
         defaultSSLContextAndOptions.set(newContext);
 
-        if (Boolean.getBoolean(NettyServerCnxnFactory.CLIENT_CERT_RELOAD_KEY)) 
{
+        if (Boolean.getBoolean(CLIENT_CERT_RELOAD_KEY)) {
             
ProviderRegistry.addOrUpdateProvider(ProviderRegistry.AUTHPROVIDER_PROPERTY_PREFIX
 + "x509");
         }
     }
diff --git 
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/NettyServerCnxnFactory.java
 
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/NettyServerCnxnFactory.java
index 5198e91c1..2b412cda9 100644
--- 
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/NettyServerCnxnFactory.java
+++ 
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/NettyServerCnxnFactory.java
@@ -61,11 +61,12 @@
 import javax.net.ssl.SSLPeerUnverifiedException;
 import javax.net.ssl.SSLSession;
 import org.apache.zookeeper.KeeperException;
-import org.apache.zookeeper.common.ClientX509Util;
+import org.apache.zookeeper.common.ClientNettyX509Util;
 import org.apache.zookeeper.common.ConfigException;
 import org.apache.zookeeper.common.NettyUtils;
 import org.apache.zookeeper.common.X509Exception;
 import org.apache.zookeeper.common.X509Exception.SSLContextException;
+import org.apache.zookeeper.common.X509Util;
 import org.apache.zookeeper.common.ZKConfig;
 import org.apache.zookeeper.server.NettyServerCnxn.HandshakeState;
 import org.apache.zookeeper.server.auth.ProviderRegistry;
@@ -115,7 +116,7 @@ public void setOutstandingHandshakeLimit(int limit) {
     private InetSocketAddress localAddress;
     private int maxClientCnxns = 60;
     int listenBacklog = -1;
-    private final ClientX509Util x509Util;
+    private final ClientNettyX509Util x509Util;
 
     public static final String NETTY_ADVANCED_FLOW_CONTROL = 
"zookeeper.netty.advancedFlowControl.enabled";
     private boolean advancedFlowControlEnabled = false;
@@ -124,7 +125,7 @@ public void setOutstandingHandshakeLimit(int limit) {
 
     private static final AtomicReference<ByteBufAllocator> TEST_ALLOCATOR = 
new AtomicReference<>(null);
 
-    public static final String CLIENT_CERT_RELOAD_KEY = 
"zookeeper.client.certReload";
+    public static final String CLIENT_CERT_RELOAD_KEY = 
X509Util.CLIENT_CERT_RELOAD_KEY;
 
     /**
      * A handler that detects whether the client would like to use
@@ -514,7 +515,7 @@ private ServerBootstrap 
configureBootstrapAllocator(ServerBootstrap bootstrap) {
     }
 
     NettyServerCnxnFactory() {
-        x509Util = new ClientX509Util();
+        x509Util = new ClientNettyX509Util();
 
         boolean useClientReload = Boolean.getBoolean(CLIENT_CERT_RELOAD_KEY);
         LOG.info("{}={}", CLIENT_CERT_RELOAD_KEY, useClientReload);
diff --git 
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerCnxnFactory.java
 
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerCnxnFactory.java
index f63c1eec4..32f036eb0 100644
--- 
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerCnxnFactory.java
+++ 
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerCnxnFactory.java
@@ -161,6 +161,14 @@ public final void setZooKeeperServer(ZooKeeperServer zks) {
 
     public abstract void closeAll(ServerCnxn.DisconnectReason reason);
 
+    /**
+     * Returns the number of connections that are currently performing a TLS 
handshake.
+     * Non-Netty implementations return 0.
+     */
+    public int getOutstandingHandshakeNum() {
+        return 0;
+    }
+
     /**
      * Attempts to shed approximately the specified percentage of connections.
      *
@@ -228,6 +236,43 @@ public static ServerCnxnFactory createFactory() throws 
IOException {
         }
     }
 
+    /**
+     * Creates a ServerCnxnFactory, defaulting to NettyServerCnxnFactory when
+     * {@code secure} is {@code true} and no explicit factory is configured via
+     * the {@value #ZOOKEEPER_SERVER_CNXN_FACTORY} system property. SSL/TLS
+     * requires Netty; if Netty is not present on the classpath a helpful
+     * {@link IOException} is thrown.
+     *
+     * @param secure {@code true} when the factory will be used for secure 
(SSL/TLS) connections
+     * @return a new ServerCnxnFactory instance
+     * @throws IOException if the factory cannot be instantiated
+     */
+    public static ServerCnxnFactory createFactory(boolean secure) throws 
IOException {
+        String serverCnxnFactoryName = 
System.getProperty(ZOOKEEPER_SERVER_CNXN_FACTORY);
+        if (serverCnxnFactoryName == null) {
+            if (secure) {
+                // Derived from a same-package class rather than a string 
literal so the shade plugin relocates it.
+                serverCnxnFactoryName = 
NIOServerCnxnFactory.class.getPackageName() + ".NettyServerCnxnFactory";
+            } else {
+                serverCnxnFactoryName = NIOServerCnxnFactory.class.getName();
+            }
+        }
+        try {
+            ServerCnxnFactory serverCnxnFactory = (ServerCnxnFactory) 
Class.forName(serverCnxnFactoryName)
+                                                                           
.getDeclaredConstructor()
+                                                                           
.newInstance();
+            LOG.info("Using {} as server connection factory", 
serverCnxnFactoryName);
+            return serverCnxnFactory;
+        } catch (Exception | NoClassDefFoundError e) {
+            String msg = "Couldn't instantiate " + serverCnxnFactoryName;
+            if (secure) {
+                msg += ". SSL/TLS support requires Netty; please add 
netty-handler"
+                    + " (and optionally netty-tcnative-boringssl-static) to 
your project's dependencies.";
+            }
+            throw new IOException(msg, e);
+        }
+    }
+
     public static ServerCnxnFactory createFactory(int clientPort, int 
maxClientCnxns) throws IOException {
         return createFactory(new InetSocketAddress(clientPort), 
maxClientCnxns, -1);
     }
diff --git 
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServer.java
 
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServer.java
index 9545730cb..5c7f5f4ea 100644
--- 
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServer.java
+++ 
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServer.java
@@ -2389,8 +2389,8 @@ public boolean authWriteRequest(Request request) {
     }
 
     public int getOutstandingHandshakeNum() {
-        if (serverCnxnFactory instanceof NettyServerCnxnFactory) {
-            return ((NettyServerCnxnFactory) 
serverCnxnFactory).getOutstandingHandshakeNum();
+        if (serverCnxnFactory != null) {
+            return serverCnxnFactory.getOutstandingHandshakeNum();
         } else {
             return 0;
         }
diff --git 
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerMain.java
 
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerMain.java
index c721e685b..0545827c8 100644
--- 
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerMain.java
+++ 
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerMain.java
@@ -162,7 +162,7 @@ public void runFromConfig(ServerConfig config) throws 
IOException, AdminServerEx
                 needStartZKServer = false;
             }
             if (config.getSecureClientPortAddress() != null) {
-                secureCnxnFactory = ServerCnxnFactory.createFactory();
+                secureCnxnFactory = ServerCnxnFactory.createFactory(true);
                 
secureCnxnFactory.configure(config.getSecureClientPortAddress(), 
config.getMaxClientCnxns(), config.getClientPortListenBacklog(), true);
                 secureCnxnFactory.startup(zkServer, needStartZKServer);
             }
diff --git 
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumPeer.java
 
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumPeer.java
index 56dcbd224..dd599d9a9 100644
--- 
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumPeer.java
+++ 
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumPeer.java
@@ -2429,7 +2429,7 @@ public boolean processReconfig(QuorumVerifier qv, Long 
suggestedLeaderId, Long z
                         // start secureCnxnFactory first
                         try {
                             configureSSLAuth();
-                            secureCnxnFactory = 
ServerCnxnFactory.createFactory();
+                            secureCnxnFactory = 
ServerCnxnFactory.createFactory(true);
                             
secureCnxnFactory.configure(myNewQS.secureClientAddr, getMaxClientCnxns(), 
getClientPortListenBacklog(), true);
                             secureCnxnFactory.start();
 
diff --git 
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumPeerMain.java
 
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumPeerMain.java
index 86b3b8a7c..3fb3d6bf3 100644
--- 
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumPeerMain.java
+++ 
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumPeerMain.java
@@ -170,7 +170,7 @@ public void runFromConfig(QuorumPeerConfig config) throws 
IOException, AdminServ
             }
 
             if (config.getSecureClientPortAddress() != null) {
-                secureCnxnFactory = ServerCnxnFactory.createFactory();
+                secureCnxnFactory = ServerCnxnFactory.createFactory(true);
                 
secureCnxnFactory.configure(config.getSecureClientPortAddress(), 
config.getMaxClientCnxns(), config.getClientPortListenBacklog(), true);
             }
 
diff --git 
a/zookeeper-server/src/test/java/org/apache/zookeeper/NettyOptionalArchTest.java
 
b/zookeeper-server/src/test/java/org/apache/zookeeper/NettyOptionalArchTest.java
new file mode 100644
index 000000000..b95050541
--- /dev/null
+++ 
b/zookeeper-server/src/test/java/org/apache/zookeeper/NettyOptionalArchTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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;
+
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
+import com.tngtech.archunit.base.DescribedPredicate;
+import com.tngtech.archunit.core.domain.JavaClass;
+import com.tngtech.archunit.core.domain.JavaClasses;
+import com.tngtech.archunit.core.importer.ClassFileImporter;
+import com.tngtech.archunit.core.importer.ImportOption;
+import com.tngtech.archunit.lang.ArchRule;
+import java.util.Collections;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Architectural test to enforce that Netty is an optional dependency.
+ *
+ * <p>Only classes whose name contains "Netty" (e.g. {@code 
NettyServerCnxnFactory},
+ * {@code ClientCnxnSocketNetty}, {@code ClientNettyX509Util}) and a small set 
of
+ * explicitly allowed SSL/TLS utility classes ({@code UnifiedServerSocket}) 
may depend
+ * on {@code io.netty} packages. All other ZooKeeper classes must remain 
Netty-free so
+ * that Netty can be an optional dependency for users who do not need SSL/TLS.
+ */
+public class NettyOptionalArchTest {
+
+    @Test
+    public void nonNettyClassesShouldNotDependOnNetty() {
+        JavaClasses importedClasses = new ClassFileImporter(
+                Collections.singletonList(new 
ImportOption.DoNotIncludeTests()))
+                .importPackages("org.apache.zookeeper")
+                .that(new DescribedPredicate<JavaClass>("ZK Non-Netty 
classes") {
+                    @Override
+                    public boolean test(JavaClass javaClass) {
+                        // Exclude classes with "Netty" in their name (e.g. 
NettyServerCnxnFactory,
+                        // ClientCnxnSocketNetty, NettyServerCnxn, NettyUtils, 
ClientNettyX509Util).
+                        // Also exclude UnifiedServerSocket (and its inner 
classes) which legitimately
+                        // uses the Netty SSL API to detect SSL vs plain-text 
connections.
+                        String name = javaClass.getName();
+                        return !name.contains("Netty")
+                            && !name.contains("UnifiedServerSocket");
+                    }
+                });
+
+        ArchRule rule = noClasses().should()
+                .dependOnClassesThat().resideInAnyPackage("io.netty..")
+                
.orShould().dependOnClassesThat().haveSimpleNameContaining("Netty");
+
+        rule.check(importedClasses);
+    }
+}
diff --git 
a/zookeeper-server/src/test/java/org/apache/zookeeper/cli/HexDumpOutputFormatterTest.java
 
b/zookeeper-server/src/test/java/org/apache/zookeeper/cli/HexDumpOutputFormatterTest.java
new file mode 100644
index 000000000..29e4e17b2
--- /dev/null
+++ 
b/zookeeper-server/src/test/java/org/apache/zookeeper/cli/HexDumpOutputFormatterTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.cli;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import java.nio.charset.StandardCharsets;
+import org.junit.jupiter.api.Test;
+
+public class HexDumpOutputFormatterTest {
+
+    @Test
+    public void testFormatOneReadableExample() {
+        byte[] data = ("Hello," + "\n" + "Zoo" + "\u0001" + 
"Keep!").getBytes(StandardCharsets.UTF_8);
+        String lineSeparator = System.lineSeparator();
+
+        String expected = String.join(
+            lineSeparator,
+            "         +-------------------------------------------------+",
+            "         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |",
+            
"+--------+-------------------------------------------------+----------------+",
+            "|00000000| 48 65 6c 6c 6f 2c 0a 5a 6f 6f 01 4b 65 65 70 21 
|Hello,.Zoo.Keep!|",
+            
"+--------+-------------------------------------------------+----------------+");
+
+        assertEquals(expected, HexDumpOutputFormatter.INSTANCE.format(data));
+    }
+}
+
diff --git 
a/zookeeper-server/src/test/java/org/apache/zookeeper/common/X509UtilTest.java 
b/zookeeper-server/src/test/java/org/apache/zookeeper/common/X509UtilTest.java
index 9999e9fd6..494bf6c1e 100644
--- 
a/zookeeper-server/src/test/java/org/apache/zookeeper/common/X509UtilTest.java
+++ 
b/zookeeper-server/src/test/java/org/apache/zookeeper/common/X509UtilTest.java
@@ -739,7 +739,7 @@ public void 
testCreateNettySslContextForClient_customSSLContextClass(
             X509KeyType caKeyType, X509KeyType certKeyType, String 
keyPassword, Integer paramIndex)
             throws Exception {
         init(caKeyType, certKeyType, keyPassword, paramIndex);
-        try (ClientX509Util clientX509Util = new ClientX509Util()) {
+        try (ClientNettyX509Util clientX509Util = new ClientNettyX509Util()) {
             ZKConfig zkConfig = new ZKConfig();
             
zkConfig.setProperty(clientX509Util.getSslContextSupplierClassProperty(), 
SslContextSupplier.class.getName());
             // Disable hostname verification so the JdkSslContext is not 
wrapped in a DelegatingSslContext.
@@ -759,7 +759,7 @@ public void 
testCreateNettySslContextForServer_customSSLContextClass(
             X509KeyType caKeyType, X509KeyType certKeyType, String 
keyPassword, Integer paramIndex)
             throws Exception {
         init(caKeyType, certKeyType, keyPassword, paramIndex);
-        try (ClientX509Util clientX509Util = new ClientX509Util()) {
+        try (ClientNettyX509Util clientX509Util = new ClientNettyX509Util()) {
             ZKConfig zkConfig = new ZKConfig();
             
zkConfig.setProperty(clientX509Util.getSslContextSupplierClassProperty(), 
SslContextSupplier.class.getName());
             // A supplied SSLContext carries its own key material, so no key 
store must be required.
@@ -781,7 +781,7 @@ public void 
testCreateNettySslContext_customSSLContextClassRejectsNonJdkProvider
             X509KeyType caKeyType, X509KeyType certKeyType, String 
keyPassword, Integer paramIndex)
             throws Exception {
         init(caKeyType, certKeyType, keyPassword, paramIndex);
-        try (ClientX509Util clientX509Util = new ClientX509Util()) {
+        try (ClientNettyX509Util clientX509Util = new ClientNettyX509Util()) {
             ZKConfig zkConfig = new ZKConfig();
             
zkConfig.setProperty(clientX509Util.getSslContextSupplierClassProperty(), 
SslContextSupplier.class.getName());
             zkConfig.setProperty(clientX509Util.getSslProviderProperty(), 
"OPENSSL");
@@ -815,7 +815,7 @@ public void 
testCreateSSLContext_hostnameVerificationNoCustomTrustStore(X509KeyT
         // Verify client hostname too
         
System.setProperty(x509Util.getSslClientHostnameVerificationEnabledProperty(), 
"true");
         ZKConfig zkConfig = new ZKConfig();
-        try (ClientX509Util clientX509Util = new ClientX509Util();) {
+        try (ClientNettyX509Util clientX509Util = new ClientNettyX509Util();) {
             UnpooledByteBufAllocator byteBufAllocator = new 
UnpooledByteBufAllocator(false);
             SslContext clientContext = 
clientX509Util.createNettySslContextForClient(zkConfig);
             SSLEngine clientEngine = clientContext.newEngine(byteBufAllocator);
@@ -1075,13 +1075,13 @@ public void 
testSeparateClientKeyStore_nettyClientUsesClientKeyStore(
         System.setProperty(x509Util.getSslClientKeystorePasswdProperty(), 
keyPassword);
         System.setProperty(x509Util.getSslClientKeystoreTypeProperty(), "JKS");
 
-        try {
+        try (ClientNettyX509Util nettyX509Util = new ClientNettyX509Util()) {
             // Netty client context should load from clientKeyStore
-            SslContext clientCtx = ((ClientX509Util) 
x509Util).createNettySslContextForClient(new ZKConfig());
+            SslContext clientCtx = 
nettyX509Util.createNettySslContextForClient(new ZKConfig());
             assertNotNull(clientCtx);
 
             // Netty server context should still load from keyStore (the main 
one)
-            SslContext serverCtx = ((ClientX509Util) 
x509Util).createNettySslContextForServer(new ZKConfig());
+            SslContext serverCtx = 
nettyX509Util.createNettySslContextForServer(new ZKConfig());
             assertNotNull(serverCtx);
         } finally {
             
System.clearProperty(x509Util.getSslClientKeystoreLocationProperty());

Reply via email to