This is an automated email from the ASF dual-hosted git repository.
eolivelli 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 e0890d0a7 ZOOKEEPER-4697: Add Builder to construct ZooKeeper and
ZooKeeperAdmin (#2001)
e0890d0a7 is described below
commit e0890d0a79372be9c74b6d3f9314d693a89d255c
Author: Kezhu Wang <[email protected]>
AuthorDate: Sat Sep 2 23:49:21 2023 +0800
ZOOKEEPER-4697: Add Builder to construct ZooKeeper and ZooKeeperAdmin
(#2001)
* ZOOKEEPER-4697: Add Builder to construct ZooKeeper and its derivations
Currently, there are 10 constructor variants for `ZooKeeper` and 4 for
`ZooKeeperAdmin`. It is enough for us to resort to a builder.
The `build` method throws `IOException` to make it a drop-in replacement
of existing constructors of `ZooKeeper`.
This pr also unify body of `ZooKeeper` constructor to one. Previously,
there are diverged to two. One has `sessionId` and `sessionPasswd`, and
another doesn't have. This pr uses `sessionId == 0` to differentiate the
two as it is used in server side to differentiate session create and
reconnect.
* Restrict Builder to only ZooKeeper and ZooKeeperAdmin
---
.../main/java/org/apache/zookeeper/ZooKeeper.java | 173 +++++++++++++-------
.../org/apache/zookeeper/admin/ZooKeeperAdmin.java | 6 +
.../apache/zookeeper/client/ZooKeeperBuilder.java | 181 +++++++++++++++++++++
.../apache/zookeeper/client/ZooKeeperOptions.java | 92 +++++++++++
.../zookeeper/ClientCnxnSocketFragilityTest.java | 9 +
.../apache/zookeeper/ClientRequestTimeoutTest.java | 8 +
.../zookeeper/client/ZooKeeperBuilderTest.java | 86 ++++++++++
7 files changed, 492 insertions(+), 63 deletions(-)
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 09006ed10..475430b89 100644
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeper.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeper.java
@@ -46,6 +46,8 @@ import org.apache.zookeeper.client.ConnectStringParser;
import org.apache.zookeeper.client.HostProvider;
import org.apache.zookeeper.client.StaticHostProvider;
import org.apache.zookeeper.client.ZKClientConfig;
+import org.apache.zookeeper.client.ZooKeeperBuilder;
+import org.apache.zookeeper.client.ZooKeeperOptions;
import org.apache.zookeeper.client.ZooKeeperSaslClient;
import org.apache.zookeeper.common.PathUtils;
import org.apache.zookeeper.data.ACL;
@@ -445,7 +447,9 @@ public class ZooKeeper implements AutoCloseable {
* if an invalid chroot path is specified
*/
public ZooKeeper(String connectString, int sessionTimeout, Watcher
watcher) throws IOException {
- this(connectString, sessionTimeout, watcher, false);
+ this(new ZooKeeperBuilder(connectString, sessionTimeout)
+ .withDefaultWatcher(watcher)
+ .toOptions());
}
/**
@@ -498,7 +502,10 @@ public class ZooKeeper implements AutoCloseable {
int sessionTimeout,
Watcher watcher,
ZKClientConfig conf) throws IOException {
- this(connectString, sessionTimeout, watcher, false, conf);
+ this(new ZooKeeperBuilder(connectString, sessionTimeout)
+ .withDefaultWatcher(watcher)
+ .withClientConfig(conf)
+ .toOptions());
}
/**
@@ -564,7 +571,11 @@ public class ZooKeeper implements AutoCloseable {
Watcher watcher,
boolean canBeReadOnly,
HostProvider aHostProvider) throws IOException {
- this(connectString, sessionTimeout, watcher, canBeReadOnly,
aHostProvider, null);
+ this(new ZooKeeperBuilder(connectString, sessionTimeout)
+ .withDefaultWatcher(watcher)
+ .withCanBeReadOnly(canBeReadOnly)
+ .withHostProvider(ignored -> aHostProvider)
+ .toOptions());
}
/**
@@ -634,25 +645,12 @@ public class ZooKeeper implements AutoCloseable {
HostProvider hostProvider,
ZKClientConfig clientConfig
) throws IOException {
- LOG.info(
- "Initiating client connection, connectString={} sessionTimeout={}
watcher={}",
- connectString,
- sessionTimeout,
- watcher);
-
- this.clientConfig = clientConfig != null ? clientConfig : new
ZKClientConfig();
- this.hostProvider = hostProvider;
- ConnectStringParser connectStringParser = new
ConnectStringParser(connectString);
-
- cnxn = createConnection(
- connectStringParser.getChrootPath(),
- hostProvider,
- sessionTimeout,
- this.clientConfig,
- watcher,
- getClientCnxnSocket(),
- canBeReadOnly);
- cnxn.start();
+ this(new ZooKeeperBuilder(connectString, sessionTimeout)
+ .withDefaultWatcher(watcher)
+ .withCanBeReadOnly(canBeReadOnly)
+ .withHostProvider(ignored -> hostProvider)
+ .withClientConfig(clientConfig)
+ .toOptions());
}
ClientCnxn createConnection(
@@ -662,6 +660,8 @@ public class ZooKeeper implements AutoCloseable {
ZKClientConfig clientConfig,
Watcher defaultWatcher,
ClientCnxnSocket clientCnxnSocket,
+ long sessionId,
+ byte[] sessionPasswd,
boolean canBeReadOnly
) throws IOException {
return new ClientCnxn(
@@ -671,6 +671,8 @@ public class ZooKeeper implements AutoCloseable {
clientConfig,
defaultWatcher,
clientCnxnSocket,
+ sessionId,
+ sessionPasswd,
canBeReadOnly);
}
@@ -731,7 +733,10 @@ public class ZooKeeper implements AutoCloseable {
int sessionTimeout,
Watcher watcher,
boolean canBeReadOnly) throws IOException {
- this(connectString, sessionTimeout, watcher, canBeReadOnly,
createDefaultHostProvider(connectString));
+ this(new ZooKeeperBuilder(connectString, sessionTimeout)
+ .withDefaultWatcher(watcher)
+ .withCanBeReadOnly(canBeReadOnly)
+ .toOptions());
}
/**
@@ -794,13 +799,11 @@ public class ZooKeeper implements AutoCloseable {
Watcher watcher,
boolean canBeReadOnly,
ZKClientConfig conf) throws IOException {
- this(
- connectString,
- sessionTimeout,
- watcher,
- canBeReadOnly,
- createDefaultHostProvider(connectString),
- conf);
+ this(new ZooKeeperBuilder(connectString, sessionTimeout)
+ .withDefaultWatcher(watcher)
+ .withCanBeReadOnly(canBeReadOnly)
+ .withClientConfig(conf)
+ .toOptions());
}
/**
@@ -861,7 +864,10 @@ public class ZooKeeper implements AutoCloseable {
Watcher watcher,
long sessionId,
byte[] sessionPasswd) throws IOException {
- this(connectString, sessionTimeout, watcher, sessionId, sessionPasswd,
false);
+ this(new ZooKeeperBuilder(connectString, sessionTimeout)
+ .withDefaultWatcher(watcher)
+ .withSession(sessionId, sessionPasswd)
+ .toOptions());
}
/**
@@ -936,15 +942,12 @@ public class ZooKeeper implements AutoCloseable {
byte[] sessionPasswd,
boolean canBeReadOnly,
HostProvider aHostProvider) throws IOException {
- this(
- connectString,
- sessionTimeout,
- watcher,
- sessionId,
- sessionPasswd,
- canBeReadOnly,
- aHostProvider,
- null);
+ this(new ZooKeeperBuilder(connectString, sessionTimeout)
+ .withDefaultWatcher(watcher)
+ .withSession(sessionId, sessionPasswd)
+ .withCanBeReadOnly(canBeReadOnly)
+ .withHostProvider(ignored -> aHostProvider)
+ .toOptions());
}
/**
@@ -1025,20 +1028,72 @@ public class ZooKeeper implements AutoCloseable {
boolean canBeReadOnly,
HostProvider hostProvider,
ZKClientConfig clientConfig) throws IOException {
- LOG.info(
- "Initiating client connection, connectString={} "
- + "sessionTimeout={} watcher={} sessionId=0x{}
sessionPasswd={}",
- connectString,
- sessionTimeout,
- watcher,
- Long.toHexString(sessionId),
- (sessionPasswd == null ? "<null>" : "<hidden>"));
+ this(new ZooKeeperBuilder(connectString, sessionTimeout)
+ .withSession(sessionId, sessionPasswd)
+ .withDefaultWatcher(watcher)
+ .withCanBeReadOnly(canBeReadOnly)
+ .withHostProvider(ignored -> hostProvider)
+ .withClientConfig(clientConfig)
+ .toOptions());
+ }
+ /**
+ * Create a ZooKeeper client and establish session asynchronously.
+ *
+ * <p>This constructor will initiate connection to the server and return
+ * immediately - potentially (usually) before the session is fully
established.
+ * The watcher from options will be notified of any changes in state. This
+ * notification can come at any point before or after the constructor call
+ * has returned.
+ *
+ * <p>The instantiated ZooKeeper client object will pick an arbitrary
server
+ * from the connect string and attempt to connect to it. If establishment
of
+ * the connection fails, another server in the connect string will be tried
+ * (the order is non-deterministic, as we random shuffle the list), until a
+ * connection is established. The client will continue attempts until the
+ * session is explicitly closed (or the session is expired by the server).
+ *
+ * @param options options for ZooKeeper client
+ * @throws IOException in cases of IO failure
+ */
+ @InterfaceAudience.Private
+ public ZooKeeper(ZooKeeperOptions options) throws IOException {
+ String connectString = options.getConnectString();
+ int sessionTimeout = options.getSessionTimeout();
+ long sessionId = options.getSessionId();
+ byte[] sessionPasswd = sessionId == 0 ? new byte[16] :
options.getSessionPasswd();
+ Watcher watcher = options.getDefaultWatcher();
+ boolean canBeReadOnly = options.isCanBeReadOnly();
+
+ if (sessionId == 0) {
+ LOG.info(
+ "Initiating client connection, connectString={}
sessionTimeout={} watcher={}",
+ connectString,
+ sessionTimeout,
+ watcher);
+ } else {
+ LOG.info(
+ "Initiating client connection, connectString={} "
+ + "sessionTimeout={} watcher={} sessionId=0x{}
sessionPasswd={}",
+ connectString,
+ sessionTimeout,
+ watcher,
+ Long.toHexString(sessionId),
+ (sessionPasswd == null ? "<null>" : "<hidden>"));
+ }
+
+ ZKClientConfig clientConfig = options.getClientConfig();
this.clientConfig = clientConfig != null ? clientConfig : new
ZKClientConfig();
ConnectStringParser connectStringParser = new
ConnectStringParser(connectString);
+ HostProvider hostProvider;
+ if (options.getHostProvider() != null) {
+ hostProvider =
options.getHostProvider().apply(connectStringParser.getServerAddresses());
+ } else {
+ hostProvider = new
StaticHostProvider(connectStringParser.getServerAddresses());
+ }
this.hostProvider = hostProvider;
- cnxn = new ClientCnxn(
+ cnxn = createConnection(
connectStringParser.getChrootPath(),
hostProvider,
sessionTimeout,
@@ -1048,7 +1103,7 @@ public class ZooKeeper implements AutoCloseable {
sessionId,
sessionPasswd,
canBeReadOnly);
- cnxn.seenRwServerBefore = true; // since user has provided sessionId
+ cnxn.seenRwServerBefore = sessionId != 0; // since user has provided
sessionId
cnxn.start();
}
@@ -1120,19 +1175,11 @@ public class ZooKeeper implements AutoCloseable {
long sessionId,
byte[] sessionPasswd,
boolean canBeReadOnly) throws IOException {
- this(
- connectString,
- sessionTimeout,
- watcher,
- sessionId,
- sessionPasswd,
- canBeReadOnly,
- createDefaultHostProvider(connectString));
- }
-
- // default hostprovider
- private static HostProvider createDefaultHostProvider(String
connectString) {
- return new StaticHostProvider(new
ConnectStringParser(connectString).getServerAddresses());
+ this(new ZooKeeperBuilder(connectString, sessionTimeout)
+ .withDefaultWatcher(watcher)
+ .withSession(sessionId, sessionPasswd)
+ .withCanBeReadOnly(canBeReadOnly)
+ .toOptions());
}
// VisibleForTesting
diff --git
a/zookeeper-server/src/main/java/org/apache/zookeeper/admin/ZooKeeperAdmin.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/admin/ZooKeeperAdmin.java
index 8240526a5..4d5dc6fd2 100644
---
a/zookeeper-server/src/main/java/org/apache/zookeeper/admin/ZooKeeperAdmin.java
+++
b/zookeeper-server/src/main/java/org/apache/zookeeper/admin/ZooKeeperAdmin.java
@@ -27,6 +27,7 @@ import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.client.ZKClientConfig;
+import org.apache.zookeeper.client.ZooKeeperOptions;
import org.apache.zookeeper.common.StringUtils;
import org.apache.zookeeper.data.Stat;
import org.apache.zookeeper.proto.GetDataResponse;
@@ -53,6 +54,11 @@ public class ZooKeeperAdmin extends ZooKeeper {
private static final Logger LOG =
LoggerFactory.getLogger(ZooKeeperAdmin.class);
+ @InterfaceAudience.Private
+ public ZooKeeperAdmin(ZooKeeperOptions options) throws IOException {
+ super(options);
+ }
+
/**
* Create a ZooKeeperAdmin object which is used to perform dynamic
reconfiguration
* operations.
diff --git
a/zookeeper-server/src/main/java/org/apache/zookeeper/client/ZooKeeperBuilder.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/client/ZooKeeperBuilder.java
new file mode 100644
index 000000000..796ed24ea
--- /dev/null
+++
b/zookeeper-server/src/main/java/org/apache/zookeeper/client/ZooKeeperBuilder.java
@@ -0,0 +1,181 @@
+/*
+ * 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.client;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.function.Function;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.apache.yetus.audience.InterfaceStability;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.admin.ZooKeeperAdmin;
+
+/**
+ * Builder to construct {@link ZooKeeper} and {@link ZooKeeperAdmin}.
+ */
[email protected]
[email protected]
+public class ZooKeeperBuilder {
+ private final String connectString;
+ private final int sessionTimeout;
+ private Function<Collection<InetSocketAddress>, HostProvider> hostProvider;
+ private Watcher defaultWatcher;
+ private boolean canBeReadOnly = false;
+ private long sessionId = 0;
+ private byte[] sessionPasswd;
+ private ZKClientConfig clientConfig;
+
+ /**
+ * Creates a builder with given connect string and session timeout.
+ *
+ * @param connectString
+ * comma separated host:port pairs, each corresponding to a zk
+ * server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"
+ * If the optional chroot suffix is used the example would look
+ * like: "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002/app/a"
+ * where the client would be rooted at "/app/a" and all paths
+ * would be relative to this root - ie getting/setting/etc...
+ * "/foo/bar" would result in operations being run on
+ * "/app/a/foo/bar" (from the server perspective).
+ * @param sessionTimeoutMs
+ * session timeout in milliseconds
+ */
+ public ZooKeeperBuilder(String connectString, int sessionTimeoutMs) {
+ this.connectString = connectString;
+ this.sessionTimeout = sessionTimeoutMs;
+ }
+
+ /**
+ * Specified watcher to receive state changes, and node events if attached
later.
+ *
+ * @param watcher
+ * a watcher object which will be notified of state changes, may
+ * also be notified for node events
+ * @return this
+ */
+ public ZooKeeperBuilder withDefaultWatcher(Watcher watcher) {
+ this.defaultWatcher = watcher;
+ return this;
+ }
+
+ /**
+ * Specifies a function to construct a {@link HostProvider} with initial
server addresses from connect string.
+ *
+ * @param hostProvider
+ * use this as HostProvider to enable custom behaviour.
+ * @return this
+ */
+ public ZooKeeperBuilder
withHostProvider(Function<Collection<InetSocketAddress>, HostProvider>
hostProvider) {
+ this.hostProvider = hostProvider;
+ return this;
+ }
+
+ /**
+ * Specifies whether the created client is allowed to go to read-only mode
in case of partitioning.
+ *
+ * @param canBeReadOnly
+ * whether the created client is allowed to go to
+ * read-only mode in case of partitioning. Read-only mode
+ * basically means that if the client can't find any majority
+ * servers but there's partitioned server it could reach, it
+ * connects to one in read-only mode, i.e. read requests are
+ * allowed while write requests are not. It continues seeking
for
+ * majority in the background.
+ * @return this
+ * @since 3.4
+ */
+ public ZooKeeperBuilder withCanBeReadOnly(boolean canBeReadOnly) {
+ this.canBeReadOnly = canBeReadOnly;
+ return this;
+ }
+
+ /**
+ * Specifies session id and password in session reestablishment.
+ *
+ * @param sessionId
+ * session id to use if reconnecting, otherwise 0 to open new
session
+ * @param sessionPasswd
+ * password for this session
+ * @return this
+ * @see ZooKeeper#getSessionId()
+ * @see ZooKeeper#getSessionPasswd()
+ */
+ @SuppressFBWarnings({"EI_EXPOSE_REP", "EI_EXPOSE_REP2"})
+ public ZooKeeperBuilder withSession(long sessionId, byte[] sessionPasswd) {
+ this.sessionId = sessionId;
+ this.sessionPasswd = sessionPasswd;
+ return this;
+ }
+
+ /**
+ * Specifies the client config used to construct ZooKeeper instances.
+ *
+ * @param clientConfig
+ * passing this conf object gives each client the flexibility of
+ * configuring properties differently compared to other
instances
+ * @return this
+ * @since 3.5.2
+ */
+ public ZooKeeperBuilder withClientConfig(ZKClientConfig clientConfig) {
+ this.clientConfig = clientConfig;
+ return this;
+ }
+
+ /**
+ * Creates a {@link ZooKeeperOptions} with configured options.
+ *
+ * @apiNote helper to delegate existing constructors to {@link
ZooKeeper#ZooKeeper(ZooKeeperOptions)}
+ */
+ @InterfaceAudience.Private
+ public ZooKeeperOptions toOptions() {
+ return new ZooKeeperOptions(
+ connectString,
+ sessionTimeout,
+ defaultWatcher,
+ hostProvider,
+ canBeReadOnly,
+ sessionId,
+ sessionPasswd,
+ clientConfig
+ );
+ }
+
+ /**
+ * Constructs an instance of {@link ZooKeeper}.
+ *
+ * @return an instance of {@link ZooKeeper}
+ * @throws IOException from constructor of {@link ZooKeeper}
+ */
+ public ZooKeeper build() throws IOException {
+ return new ZooKeeper(toOptions());
+ }
+
+ /**
+ * Constructs an instance of {@link ZooKeeperAdmin}.
+ *
+ * @return an instance of {@link ZooKeeperAdmin}
+ * @throws IOException from constructor of {@link ZooKeeperAdmin}
+ */
+ public ZooKeeperAdmin buildAdmin() throws IOException {
+ return new ZooKeeperAdmin(toOptions());
+ }
+}
diff --git
a/zookeeper-server/src/main/java/org/apache/zookeeper/client/ZooKeeperOptions.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/client/ZooKeeperOptions.java
new file mode 100644
index 000000000..27e5244ba
--- /dev/null
+++
b/zookeeper-server/src/main/java/org/apache/zookeeper/client/ZooKeeperOptions.java
@@ -0,0 +1,92 @@
+/*
+ * 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.client;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.function.Function;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.apache.zookeeper.Watcher;
+
+/**
+ * Options to construct {@link org.apache.zookeeper.ZooKeeper} and {@link
org.apache.zookeeper.admin.ZooKeeperAdmin}.
+ */
[email protected]
+public class ZooKeeperOptions {
+ private final String connectString;
+ private final int sessionTimeout;
+ private final Watcher defaultWatcher;
+ private final Function<Collection<InetSocketAddress>, HostProvider>
hostProvider;
+ private final boolean canBeReadOnly;
+ private final long sessionId;
+ private final byte[] sessionPasswd;
+ private final ZKClientConfig clientConfig;
+
+ ZooKeeperOptions(String connectString,
+ int sessionTimeout,
+ Watcher defaultWatcher,
+ Function<Collection<InetSocketAddress>, HostProvider>
hostProvider,
+ boolean canBeReadOnly,
+ long sessionId,
+ byte[] sessionPasswd,
+ ZKClientConfig clientConfig) {
+ this.connectString = connectString;
+ this.sessionTimeout = sessionTimeout;
+ this.hostProvider = hostProvider;
+ this.defaultWatcher = defaultWatcher;
+ this.canBeReadOnly = canBeReadOnly;
+ this.sessionId = sessionId;
+ this.sessionPasswd = sessionPasswd;
+ this.clientConfig = clientConfig;
+ }
+
+ public String getConnectString() {
+ return connectString;
+ }
+
+ public int getSessionTimeout() {
+ return sessionTimeout;
+ }
+
+ public Watcher getDefaultWatcher() {
+ return defaultWatcher;
+ }
+
+ public Function<Collection<InetSocketAddress>, HostProvider>
getHostProvider() {
+ return hostProvider;
+ }
+
+ public boolean isCanBeReadOnly() {
+ return canBeReadOnly;
+ }
+
+ public long getSessionId() {
+ return sessionId;
+ }
+
+ @SuppressFBWarnings({"EI_EXPOSE_REP", "EI_EXPOSE_REP2"})
+ public byte[] getSessionPasswd() {
+ return sessionPasswd;
+ }
+
+ public ZKClientConfig getClientConfig() {
+ return clientConfig;
+ }
+}
diff --git
a/zookeeper-server/src/test/java/org/apache/zookeeper/ClientCnxnSocketFragilityTest.java
b/zookeeper-server/src/test/java/org/apache/zookeeper/ClientCnxnSocketFragilityTest.java
index 953414468..bd2f1c287 100644
---
a/zookeeper-server/src/test/java/org/apache/zookeeper/ClientCnxnSocketFragilityTest.java
+++
b/zookeeper-server/src/test/java/org/apache/zookeeper/ClientCnxnSocketFragilityTest.java
@@ -286,6 +286,8 @@ public class ClientCnxnSocketFragilityTest extends
QuorumPeerTestBase {
ZKClientConfig zkClientConfig,
Watcher defaultWatcher,
ClientCnxnSocket clientCnxnSocket,
+ long sessionId,
+ byte[] sessionPasswd,
boolean canBeReadOnly
) throws IOException {
super(
@@ -295,6 +297,8 @@ public class ClientCnxnSocketFragilityTest extends
QuorumPeerTestBase {
zkClientConfig,
defaultWatcher,
clientCnxnSocket,
+ sessionId,
+ sessionPasswd,
canBeReadOnly);
}
@@ -351,6 +355,7 @@ public class ClientCnxnSocketFragilityTest extends
QuorumPeerTestBase {
return cnxn.getState().isAlive();
}
+ @Override
ClientCnxn createConnection(
String chrootPath,
HostProvider hostProvider,
@@ -358,6 +363,8 @@ public class ClientCnxnSocketFragilityTest extends
QuorumPeerTestBase {
ZKClientConfig clientConfig,
Watcher defaultWatcher,
ClientCnxnSocket clientCnxnSocket,
+ long sessionId,
+ byte[] sessionPasswd,
boolean canBeReadOnly
) throws IOException {
assertTrue(clientCnxnSocket instanceof FragileClientCnxnSocketNIO);
@@ -369,6 +376,8 @@ public class ClientCnxnSocketFragilityTest extends
QuorumPeerTestBase {
clientConfig,
defaultWatcher,
clientCnxnSocket,
+ sessionId,
+ sessionPasswd,
canBeReadOnly);
return ClientCnxnSocketFragilityTest.this.cnxn;
}
diff --git
a/zookeeper-server/src/test/java/org/apache/zookeeper/ClientRequestTimeoutTest.java
b/zookeeper-server/src/test/java/org/apache/zookeeper/ClientRequestTimeoutTest.java
index ecf39273e..d6b6da94b 100644
---
a/zookeeper-server/src/test/java/org/apache/zookeeper/ClientRequestTimeoutTest.java
+++
b/zookeeper-server/src/test/java/org/apache/zookeeper/ClientRequestTimeoutTest.java
@@ -119,6 +119,8 @@ public class ClientRequestTimeoutTest extends
QuorumPeerTestBase {
ZKClientConfig clientConfig,
Watcher defaultWatcher,
ClientCnxnSocket clientCnxnSocket,
+ long sessionId,
+ byte[] sessionPasswd,
boolean canBeReadOnly
) throws IOException {
super(
@@ -128,6 +130,8 @@ public class ClientRequestTimeoutTest extends
QuorumPeerTestBase {
clientConfig,
defaultWatcher,
clientCnxnSocket,
+ sessionId,
+ sessionPasswd,
canBeReadOnly);
}
@@ -157,6 +161,8 @@ public class ClientRequestTimeoutTest extends
QuorumPeerTestBase {
ZKClientConfig clientConfig,
Watcher defaultWatcher,
ClientCnxnSocket clientCnxnSocket,
+ long sessionId,
+ byte[] sessionPasswd,
boolean canBeReadOnly
) throws IOException {
return new CustomClientCnxn(
@@ -166,6 +172,8 @@ public class ClientRequestTimeoutTest extends
QuorumPeerTestBase {
clientConfig,
defaultWatcher,
clientCnxnSocket,
+ sessionId,
+ sessionPasswd,
canBeReadOnly);
}
diff --git
a/zookeeper-server/src/test/java/org/apache/zookeeper/client/ZooKeeperBuilderTest.java
b/zookeeper-server/src/test/java/org/apache/zookeeper/client/ZooKeeperBuilderTest.java
new file mode 100644
index 000000000..32d30052a
--- /dev/null
+++
b/zookeeper-server/src/test/java/org/apache/zookeeper/client/ZooKeeperBuilderTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.client;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import org.apache.zookeeper.CreateMode;
+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.Time;
+import org.apache.zookeeper.test.ClientBase;
+import org.junit.jupiter.api.Test;
+
+public class ZooKeeperBuilderTest extends ClientBase {
+ private void testClient(BlockingQueue<WatchedEvent> events, ZooKeeper zk)
throws Exception {
+ zk.exists("/test", true);
+ zk.create("/test", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
+ Thread.sleep(100);
+ zk.close();
+
+ WatchedEvent connected = events.poll(10, TimeUnit.SECONDS);
+ assertNotNull(connected);
+ assertEquals(Watcher.Event.EventType.None, connected.getType());
+ assertEquals(Watcher.Event.KeeperState.SyncConnected,
connected.getState());
+
+ WatchedEvent created = events.poll(10, TimeUnit.SECONDS);
+ assertNotNull(created);
+ assertEquals(Watcher.Event.EventType.NodeCreated, created.getType());
+ assertEquals("/test", created.getPath());
+
+ // A sleep(100) before disconnect approve that events receiving in
closing is indeterminate,
+ // but the last should be closed. See ZOOKEEPER-4702.
+ WatchedEvent closed = null;
+ long timeoutMs = TimeUnit.SECONDS.toMillis(10);
+ long deadlineMs = Time.currentElapsedTime() + timeoutMs;
+ while (timeoutMs > 0 && (closed == null || closed.getState() !=
Watcher.Event.KeeperState.Closed)) {
+ WatchedEvent event = events.poll(10, TimeUnit.SECONDS);
+ if (event != null) {
+ closed = event;
+ }
+ timeoutMs = deadlineMs - Time.currentElapsedTime();
+ }
+ assertNotNull(closed);
+ assertEquals(Watcher.Event.EventType.None, closed.getType());
+ assertEquals(Watcher.Event.KeeperState.Closed, closed.getState());
+ }
+
+ @Test
+ public void testBuildClient() throws Exception {
+ BlockingQueue<WatchedEvent> events = new LinkedBlockingQueue<>();
+ ZooKeeper zk = new ZooKeeperBuilder(hostPort, 1000)
+ .withDefaultWatcher(events::offer)
+ .build();
+ testClient(events, zk);
+ }
+
+ @Test
+ public void testBuildAdminClient() throws Exception {
+ BlockingQueue<WatchedEvent> events = new LinkedBlockingQueue<>();
+ ZooKeeper zk = new ZooKeeperBuilder(hostPort, 1000)
+ .withDefaultWatcher(events::offer)
+ .buildAdmin();
+ testClient(events, zk);
+ }
+}