Skip to content

Commit 12b4e68

Browse files
eolivelliztzg
authored andcommitted
ZOOKEEPER-3874: Official API to start ZooKeeper server from Java
Create an Official API to start a ZooKeeper server node from Java code. The idea is not to run ZooKeeper server inside the same process of an application, but only to have a standard Launcher that can be used from Java and not a bash script. See more context here https://issues.apache.org/jira/browse/ZOOKEEPER-3874 This is how a Java launcher will look like for tests: ``` int clientPort = PortAssignment.unique(); final Properties configZookeeper = new Properties(); configZookeeper.put("clientPort", clientPort + ""); configZookeeper.put("host", "localhost"); configZookeeper.put(".........................."); try (ZooKeeperServerEmbedded zkServer = ZooKeeperServerEmbedded .builder() .baseDir(baseDir) .configuration(configZookeeper) .exitHandler(ExitHandler.LOG_ONLY) .build()) { zkServer.start(); //// wait..... } ``` This feature does not overlap with Curator TestingServer, this feature is meant to be used a fundation for projects like TestingServer but also to run ZooKeeper server nodes in production. This code is running in production at https://www.mag-news.com and https://emailsuccess.com, in such products we are using a Java based process manager Author: Enrico Olivelli <[email protected]> Author: Enrico Olivelli <[email protected]> Reviewers: Damien Diederen <[email protected]> Closes apache#1526 from eolivelli/fix/ZOOKEEPER-3874-embedded-api
1 parent 300d7e9 commit 12b4e68

22 files changed

+1168
-12
lines changed

.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ zookeeper-server/src/main/resources/lib/ant-eclipse-*
7171
zookeeper-server/src/main/resources/lib/ivy-*
7272
zookeeper-server/src/main/java/org/apache/zookeeper/version/Info.java
7373
zookeeper-server/src/main/java/org/apache/zookeeper/version/VersionInfoMain.java
74-
zookeeper-server/src/test/resources/
7574
zookeeper-client/zookeeper-client-c/Makefile.in
7675
zookeeper-client/zookeeper-client-c/aclocal.m4
7776
zookeeper-client/zookeeper-client-c/autom4te.cache/

pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@
425425
<surefire.version>2.22.1</surefire.version>
426426

427427
<surefire-forkcount>8</surefire-forkcount>
428+
<redirectTestOutputToFile>true</redirectTestOutputToFile>
428429

429430
<!-- dependency versions -->
430431
<slf4j.version>1.7.30</slf4j.version>
@@ -702,6 +703,10 @@
702703
<plugin>
703704
<groupId>org.apache.maven.plugins</groupId>
704705
<artifactId>maven-surefire-plugin</artifactId>
706+
<configuration>
707+
<trimStackTrace>false</trimStackTrace>
708+
<redirectTestOutputToFile>${redirectTestOutputToFile}</redirectTestOutputToFile>
709+
</configuration>
705710
</plugin>
706711
<plugin>
707712
<groupId>org.apache.maven.plugins</groupId>
@@ -961,6 +966,7 @@
961966
<exclude>src/main/resources/markdown/skin/*</exclude>
962967
<exclude>src/main/resources/markdown/html/*</exclude>
963968
<exclude>src/main/resources/markdown/images/*</exclude>
969+
<exclude>**/src/test/resources/embedded/*.conf</exclude>
964970
<!-- contrib -->
965971
<exclude>**/JMX-RESOURCES</exclude>
966972
<exclude>**/src/main/resources/mainClasses</exclude>

zookeeper-server/pom.xml

-1
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,6 @@
265265
<reuseForks>false</reuseForks>
266266
<argLine>-Xmx512m -Dtest.junit.threads=${surefire-forkcount} -Dzookeeper.junit.threadid=${surefire.forkNumber} -javaagent:${org.jmockit:jmockit:jar}</argLine>
267267
<basedir>${project.basedir}</basedir>
268-
<redirectTestOutputToFile>true</redirectTestOutputToFile>
269268
<systemPropertyVariables>
270269
<build.test.dir>${project.build.directory}/surefire</build.test.dir>
271270
<zookeeper.DigestAuthenticationProvider.superDigest>super:D/InIHSb7yEEbrWz8b9l71RjZJU=</zookeeper.DigestAuthenticationProvider.superDigest>

zookeeper-server/src/main/java/org/apache/zookeeper/common/StringUtils.java

+9
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,13 @@ public static String joinStrings(List<String> list, String delim) {
6565
return list == null ? null : String.join(delim, list);
6666
}
6767

68+
/**
69+
* Returns true if the string is null or it does not contain any non space characters.
70+
* @param s the string
71+
* @return true if the string is null or it does not contain any non space characters.
72+
*/
73+
public static boolean isBlank(String s) {
74+
return s == null || s.trim().isEmpty();
75+
}
76+
6877
}

zookeeper-server/src/main/java/org/apache/zookeeper/server/NettyServerCnxnFactory.java

+1
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,7 @@ public void configure(InetSocketAddress addr, int maxClientCnxns, int backlog, b
603603
this.maxClientCnxns = maxClientCnxns;
604604
this.secure = secure;
605605
this.listenBacklog = backlog;
606+
LOG.info("configure {} secure: {} on addr {}", this, secure, addr);
606607
}
607608

608609
/** {@inheritDoc} */

zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServer.java

+5
Original file line numberDiff line numberDiff line change
@@ -2172,4 +2172,9 @@ public int getOutstandingHandshakeNum() {
21722172
public boolean isReconfigEnabled() {
21732173
return this.reconfigEnabled;
21742174
}
2175+
2176+
public ZooKeeperServerShutdownHandler getZkShutdownHandler() {
2177+
return zkShutdownHandler;
2178+
}
2179+
21752180
}

zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerMain.java

+22
Original file line numberDiff line numberDiff line change
@@ -242,4 +242,26 @@ ServerCnxnFactory getSecureCnxnFactory() {
242242
return secureCnxnFactory;
243243
}
244244

245+
/**
246+
* Shutdowns properly the service, this method is not a public API.
247+
*/
248+
public void close() {
249+
ServerCnxnFactory primaryCnxnFactory = this.cnxnFactory;
250+
if (primaryCnxnFactory == null) {
251+
// in case of pure TLS we can hook into secureCnxnFactory
252+
primaryCnxnFactory = secureCnxnFactory;
253+
}
254+
if (primaryCnxnFactory == null || primaryCnxnFactory.getZooKeeperServer() == null) {
255+
return;
256+
}
257+
ZooKeeperServerShutdownHandler zkShutdownHandler = primaryCnxnFactory.getZooKeeperServer().getZkShutdownHandler();
258+
zkShutdownHandler.handle(ZooKeeperServer.State.SHUTDOWN);
259+
try {
260+
// ServerCnxnFactory will call the shutdown
261+
primaryCnxnFactory.join();
262+
} catch (InterruptedException ex) {
263+
Thread.currentThread().interrupt();
264+
}
265+
}
266+
245267
}

zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerShutdownHandler.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
* SHUTDOWN server state transitions, which in turn releases the associated
2727
* shutdown latch.
2828
*/
29-
class ZooKeeperServerShutdownHandler {
29+
public final class ZooKeeperServerShutdownHandler {
3030

31-
private final CountDownLatch shutdownLatch;
31+
private final CountDownLatch shutdownLatch;
3232

3333
ZooKeeperServerShutdownHandler(CountDownLatch shutdownLatch) {
3434
this.shutdownLatch = shutdownLatch;
@@ -39,7 +39,7 @@ class ZooKeeperServerShutdownHandler {
3939
*
4040
* @param state new server state
4141
*/
42-
void handle(State state) {
42+
public void handle(State state) {
4343
if (state == State.ERROR || state == State.SHUTDOWN) {
4444
shutdownLatch.countDown();
4545
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package org.apache.zookeeper.server.embedded;
2+
3+
/**
4+
* Licensed to the Apache Software Foundation (ASF) under one
5+
* or more contributor license agreements. See the NOTICE file
6+
* distributed with this work for additional information
7+
* regarding copyright ownership. The ASF licenses this file
8+
* to you under the Apache License, Version 2.0 (the
9+
* "License"); you may not use this file except in compliance
10+
* with the License. You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
/**
22+
* Behaviour of the server in case of internal error.
23+
* When you are running tests you will use {@link #LOG_ONLY},
24+
* but please take care of using {@link #EXIT} when runnning in production.
25+
*/
26+
public enum ExitHandler {
27+
/**
28+
* Exit the Java process.
29+
*/
30+
EXIT,
31+
/**
32+
* Only log the error. This option is meant to be used only in tests.
33+
*/
34+
LOG_ONLY;
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package org.apache.zookeeper.server.embedded;
2+
3+
/**
4+
* Licensed to the Apache Software Foundation (ASF) under one
5+
* or more contributor license agreements. See the NOTICE file
6+
* distributed with this work for additional information
7+
* regarding copyright ownership. The ASF licenses this file
8+
* to you under the Apache License, Version 2.0 (the
9+
* "License"); you may not use this file except in compliance
10+
* with the License. You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
import java.nio.file.Path;
22+
import java.util.Objects;
23+
import java.util.Properties;
24+
import org.apache.yetus.audience.InterfaceAudience;
25+
import org.apache.yetus.audience.InterfaceStability;
26+
27+
/**
28+
* This API allows you to start a ZooKeeper server node from Java code <p>
29+
* The server will run inside the same process.<p>
30+
* Typical usecases are:
31+
* <ul>
32+
* <li>Running automated tests</li>
33+
* <li>Launch ZooKeeper server with a Java based service management system</li>
34+
* </ul>
35+
* <p>
36+
* Please take into consideration that in production usually it is better to not run the client
37+
* together with the server in order to avoid race conditions, especially around how ephemeral nodes work.
38+
*/
39+
@InterfaceAudience.Public
40+
@InterfaceStability.Evolving
41+
public interface ZooKeeperServerEmbedded extends AutoCloseable {
42+
/**
43+
* Builder for ZooKeeperServerEmbedded.
44+
*/
45+
class ZookKeeperServerEmbeddedBuilder {
46+
47+
private Path baseDir;
48+
private Properties configuration;
49+
private ExitHandler exitHandler = ExitHandler.EXIT;
50+
51+
/**
52+
* Base directory of the server.
53+
* The system will create a temporary configuration file inside this directory.
54+
* Please remember that dynamic configuration files wil be saved into this directory by default.
55+
* <p>
56+
* If you do not set a 'dataDir' configuration entry the system will use a subdirectory of baseDir.
57+
* @param baseDir
58+
* @return the builder
59+
*/
60+
public ZookKeeperServerEmbeddedBuilder baseDir(Path baseDir) {
61+
this.baseDir = Objects.requireNonNull(baseDir);
62+
return this;
63+
}
64+
65+
/**
66+
* Set the contents of the main configuration as it would be in zk_server.conf file.
67+
* @param configuration the configuration
68+
* @return the builder
69+
*/
70+
public ZookKeeperServerEmbeddedBuilder configuration(Properties configuration) {
71+
this.configuration = Objects.requireNonNull(configuration);
72+
return this;
73+
}
74+
75+
/**
76+
* Set the behaviour in case of hard system errors, see {@link ExitHandler}.
77+
* @param exitHandler the handler
78+
* @return the builder
79+
*/
80+
public ZookKeeperServerEmbeddedBuilder exitHandler(ExitHandler exitHandler) {
81+
this.exitHandler = Objects.requireNonNull(exitHandler);
82+
return this;
83+
}
84+
85+
/**
86+
* Validate the configuration and create the server, without starting it.
87+
* @return the new server
88+
* @throws Exception
89+
* @see #start()
90+
*/
91+
public ZooKeeperServerEmbedded build() throws Exception {
92+
if (baseDir == null) {
93+
throw new IllegalStateException("baseDir is null");
94+
}
95+
if (configuration == null) {
96+
throw new IllegalStateException("configuration is null");
97+
}
98+
return new ZooKeeperServerEmbeddedImpl(configuration, baseDir, exitHandler);
99+
}
100+
}
101+
102+
static ZookKeeperServerEmbeddedBuilder builder() {
103+
return new ZookKeeperServerEmbeddedBuilder();
104+
}
105+
106+
/**
107+
* Start the server.
108+
* @throws Exception
109+
*/
110+
void start() throws Exception;
111+
112+
/**
113+
* Shutdown gracefully the server and wait for resources to be released.
114+
*/
115+
@Override
116+
void close();
117+
118+
}

0 commit comments

Comments
 (0)