zookeeper-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From an...@apache.org
Subject [zookeeper] branch master updated: ZOOKEEPER-3620: Allow to override calls to System.exit in server side code
Date Thu, 28 Nov 2019 11:24:43 GMT
This is an automated email from the ASF dual-hosted git repository.

andor 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 8e89050  ZOOKEEPER-3620: Allow to override calls to System.exit in server side code
8e89050 is described below

commit 8e8905069f4bff670c0492fe9e28ced0f86bca00
Author: Enrico Olivelli <enrico.olivelli@diennea.com>
AuthorDate: Thu Nov 28 12:24:36 2019 +0100

    ZOOKEEPER-3620: Allow to override calls to System.exit in server side code
    
    - Introduce a way to override calls to System.exit
    - enable DM_EXIT spotbugs rule
    
    see https://issues.apache.org/jira/browse/ZOOKEEPER-3620 for more context.
    
    Author: Enrico Olivelli <enrico.olivelli@diennea.com>
    Author: Enrico Olivelli <eolivelli@apache.org>
    
    Reviewers: andor@apache.org
    
    Closes #1147 from eolivelli/fix/ZOOKEEPER-3620-no-systemexit and squashes the following
commits:
    
    a234f85f1 [Enrico Olivelli] Fix checkstyle
    4c4fec49e [Enrico Olivelli] Fix spotbugs warning
    ae339b724 [Enrico Olivelli] Revert changes to VerGen.java
    0e5ee07c5 [Enrico Olivelli] Enable DM_EXIT spotbugs rule for the full code base
    b05a4bf38 [Enrico Olivelli] ZOOKEEPER-3620 Allow to override calls to System.exit in server
side code: - Use a common utility to call System.exit - Override calls to System.exit to a
NO-OP function in tests
---
 excludeFindBugsFilter.xml                          |  3 -
 .../main/java/org/apache/zookeeper/Version.java    |  7 +-
 .../java/org/apache/zookeeper/ZooKeeperMain.java   |  5 +-
 .../java/org/apache/zookeeper/server/DataTree.java |  3 +-
 .../org/apache/zookeeper/server/LogFormatter.java  |  7 +-
 .../org/apache/zookeeper/server/PurgeTxnLog.java   |  3 +-
 .../apache/zookeeper/server/RequestThrottler.java  |  3 +-
 .../apache/zookeeper/server/SnapshotFormatter.java |  8 ++-
 .../apache/zookeeper/server/TraceFormatter.java    |  3 +-
 .../apache/zookeeper/server/ZooKeeperServer.java   |  3 +-
 .../zookeeper/server/ZooKeeperServerMain.java      | 13 ++--
 .../server/persistence/TxnLogToolkit.java          |  5 +-
 .../server/quorum/FollowerZooKeeperServer.java     |  3 +-
 .../apache/zookeeper/server/quorum/Learner.java    |  6 +-
 .../zookeeper/server/quorum/QuorumCnxManager.java  |  3 +-
 .../zookeeper/server/quorum/QuorumPeerMain.java    | 13 ++--
 .../apache/zookeeper/server/util/LogChopper.java   |  5 +-
 .../org/apache/zookeeper/util/ServiceUtils.java    | 77 ++++++++++++++++++++++
 .../org/apache/zookeeper/version/util/VerGen.java  |  4 +-
 .../test/java/org/apache/zookeeper/ZKTestCase.java |  6 ++
 20 files changed, 139 insertions(+), 41 deletions(-)

diff --git a/excludeFindBugsFilter.xml b/excludeFindBugsFilter.xml
index c836911..a296594 100644
--- a/excludeFindBugsFilter.xml
+++ b/excludeFindBugsFilter.xml
@@ -7,8 +7,5 @@
     <!-- this problem is to be addressed in ZOOKEEPER-3227 -->
     <Bug pattern="DM_DEFAULT_ENCODING"/>
 
-    <!-- not really a problem -->
-    <Bug pattern="DM_EXIT"/>
-
 </FindBugsFilter>
 
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/Version.java b/zookeeper-server/src/main/java/org/apache/zookeeper/Version.java
index 181e463..4029c60 100644
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/Version.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/Version.java
@@ -20,6 +20,7 @@ package org.apache.zookeeper;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import org.apache.zookeeper.server.ExitCode;
+import org.apache.zookeeper.util.ServiceUtils;
 
 public class Version implements org.apache.zookeeper.version.Info {
 
@@ -48,7 +49,7 @@ public class Version implements org.apache.zookeeper.version.Info {
         System.out.print("Usage:\tjava -cp ... org.apache.zookeeper.Version "
                          + "[--full | --short | --revision],\n\tPrints --full version "
                          + "info if no arg specified.");
-        System.exit(ExitCode.UNEXPECTED_ERROR.getValue());
+        ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue());
     }
 
     /**
@@ -68,7 +69,7 @@ public class Version implements org.apache.zookeeper.version.Info {
         }
         if (args.length == 0 || (args.length == 1 && args[0].equals("--full"))) {
             System.out.println(getFullVersion());
-            System.exit(ExitCode.EXECUTION_FINISHED.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.EXECUTION_FINISHED.getValue());
         }
         if (args[0].equals("--short")) {
             System.out.println(getVersion());
@@ -77,7 +78,7 @@ public class Version implements org.apache.zookeeper.version.Info {
         } else {
             printUsage();
         }
-        System.exit(ExitCode.EXECUTION_FINISHED.getValue());
+        ServiceUtils.requestSystemExit(ExitCode.EXECUTION_FINISHED.getValue());
     }
 
 }
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeperMain.java b/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeperMain.java
index 857b16f..734cf7e 100644
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeperMain.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeperMain.java
@@ -66,6 +66,7 @@ import org.apache.zookeeper.cli.SyncCommand;
 import org.apache.zookeeper.cli.VersionCommand;
 import org.apache.zookeeper.client.ZKClientConfig;
 import org.apache.zookeeper.server.ExitCode;
+import org.apache.zookeeper.util.ServiceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -353,7 +354,7 @@ public class ZooKeeperMain {
             // Command line args non-null.  Run what was passed.
             processCmd(cl);
         }
-        System.exit(exitCode);
+        ServiceUtils.requestSystemExit(exitCode);
     }
 
     public void executeLine(String line) throws CliException, InterruptedException, IOException
{
@@ -396,7 +397,7 @@ public class ZooKeeperMain {
 
         if (cmd.equals("quit")) {
             zk.close();
-            System.exit(exitCode);
+            ServiceUtils.requestSystemExit(exitCode);
         } else if (cmd.equals("redo") && args.length >= 2) {
             Integer i = Integer.decode(args[1]);
             if (commandCount <= i || i < 0) { // don't allow redoing this redo
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/server/DataTree.java b/zookeeper-server/src/main/java/org/apache/zookeeper/server/DataTree.java
index f370d88..0dc5458 100644
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/server/DataTree.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/server/DataTree.java
@@ -78,6 +78,7 @@ import org.apache.zookeeper.txn.SetACLTxn;
 import org.apache.zookeeper.txn.SetDataTxn;
 import org.apache.zookeeper.txn.Txn;
 import org.apache.zookeeper.txn.TxnHeader;
+import org.apache.zookeeper.util.ServiceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -301,7 +302,7 @@ public class DataTree {
             childWatches = WatchManagerFactory.createWatchManager();
         } catch (Exception e) {
             LOG.error("Unexpected exception when creating WatchManager, exiting abnormally",
e);
-            System.exit(ExitCode.UNEXPECTED_ERROR.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue());
         }
     }
 
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/server/LogFormatter.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/LogFormatter.java
index 459f802..e02a63d 100644
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/server/LogFormatter.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/server/LogFormatter.java
@@ -33,6 +33,7 @@ import org.apache.zookeeper.server.persistence.FileHeader;
 import org.apache.zookeeper.server.persistence.FileTxnLog;
 import org.apache.zookeeper.server.util.SerializeUtils;
 import org.apache.zookeeper.txn.TxnHeader;
+import org.apache.zookeeper.util.ServiceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -51,13 +52,13 @@ public class LogFormatter {
     public static void main(String[] args) throws Exception {
         if (args.length != 1) {
             System.err.println("USAGE: LogFormatter log_file");
-            System.exit(ExitCode.INVALID_INVOCATION.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue());
         }
 
         String error = ZKUtil.validateFileInput(args[0]);
         if (null != error) {
             System.err.println(error);
-            System.exit(ExitCode.INVALID_INVOCATION.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue());
         }
 
         FileInputStream fis = new FileInputStream(args[0]);
@@ -67,7 +68,7 @@ public class LogFormatter {
 
         if (fhdr.getMagic() != FileTxnLog.TXNLOG_MAGIC) {
             System.err.println("Invalid magic number for " + args[0]);
-            System.exit(ExitCode.INVALID_INVOCATION.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue());
         }
         System.out.println("ZooKeeper Transactional Log File with dbid "
                            + fhdr.getDbid()
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/server/PurgeTxnLog.java b/zookeeper-server/src/main/java/org/apache/zookeeper/server/PurgeTxnLog.java
index f7fbde3..0c92066 100644
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/server/PurgeTxnLog.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/server/PurgeTxnLog.java
@@ -30,6 +30,7 @@ import java.util.Set;
 import org.apache.yetus.audience.InterfaceAudience;
 import org.apache.zookeeper.server.persistence.FileTxnSnapLog;
 import org.apache.zookeeper.server.persistence.Util;
+import org.apache.zookeeper.util.ServiceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -230,7 +231,7 @@ public class PurgeTxnLog {
 
     private static void printUsageThenExit() {
         printUsage();
-        System.exit(ExitCode.UNEXPECTED_ERROR.getValue());
+        ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue());
     }
 
 }
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/server/RequestThrottler.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/RequestThrottler.java
index 880ac49..16a1c6f 100644
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/server/RequestThrottler.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/server/RequestThrottler.java
@@ -20,6 +20,7 @@ package org.apache.zookeeper.server;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.util.concurrent.LinkedBlockingQueue;
+import org.apache.zookeeper.util.ServiceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -257,7 +258,7 @@ public class RequestThrottler extends ZooKeeperCriticalThread {
         } catch (InterruptedException e) {
             LOG.warn("Interrupted while waiting for {} to finish", this);
             //TODO apply ZOOKEEPER-575 and remove this line.
-            System.exit(ExitCode.UNEXPECTED_ERROR.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue());
         }
     }
 
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/server/SnapshotFormatter.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/SnapshotFormatter.java
index b3bf51b..a276ef3 100644
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/server/SnapshotFormatter.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/server/SnapshotFormatter.java
@@ -35,6 +35,7 @@ import org.apache.zookeeper.data.StatPersisted;
 import org.apache.zookeeper.server.persistence.FileSnap;
 import org.apache.zookeeper.server.persistence.SnapStream;
 import org.apache.zookeeper.server.persistence.Util;
+import org.apache.zookeeper.util.ServiceUtils;
 import org.json.simple.JSONValue;
 
 /**
@@ -72,18 +73,19 @@ public class SnapshotFormatter {
             System.err.println("USAGE: SnapshotFormatter [-d|-json] snapshot_file");
             System.err.println("       -d dump the data for each znode");
             System.err.println("       -json dump znode info in json format");
-            System.exit(ExitCode.INVALID_INVOCATION.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue());
+            return;
         }
 
         String error = ZKUtil.validateFileInput(snapshotFile);
         if (null != error) {
             System.err.println(error);
-            System.exit(ExitCode.INVALID_INVOCATION.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue());
         }
 
         if (dumpData && dumpJson) {
             System.err.println("Cannot specify both data dump (-d) and json mode (-json)
in same call");
-            System.exit(ExitCode.INVALID_INVOCATION.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue());
         }
 
         new SnapshotFormatter().run(snapshotFile, dumpData, dumpJson);
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/server/TraceFormatter.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/TraceFormatter.java
index 36a1e68..d1fd989 100644
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/server/TraceFormatter.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/server/TraceFormatter.java
@@ -25,6 +25,7 @@ import java.nio.channels.FileChannel;
 import java.text.DateFormat;
 import java.util.Date;
 import org.apache.zookeeper.ZooDefs.OpCode;
+import org.apache.zookeeper.util.ServiceUtils;
 
 public class TraceFormatter {
 
@@ -35,7 +36,7 @@ public class TraceFormatter {
     public static void main(String[] args) throws IOException {
         if (args.length != 1) {
             System.err.println("USAGE: TraceFormatter trace_file");
-            System.exit(ExitCode.INVALID_INVOCATION.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue());
         }
         FileChannel fc = new FileInputStream(args[0]).getChannel();
         while (true) {
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 1e15f34..65be888 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
@@ -80,6 +80,7 @@ import org.apache.zookeeper.server.util.OSMXBean;
 import org.apache.zookeeper.server.util.RequestPathMetricsCollector;
 import org.apache.zookeeper.txn.CreateSessionTxn;
 import org.apache.zookeeper.txn.TxnHeader;
+import org.apache.zookeeper.util.ServiceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -511,7 +512,7 @@ public class ZooKeeperServer implements SessionExpirer, ServerStats.Provider
{
             LOG.error("Severe unrecoverable error, exiting", e);
             // This is a severe error that we cannot recover from,
             // so we need to exit
-            System.exit(ExitCode.TXNLOG_ERROR_TAKING_SNAPSHOT.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.TXNLOG_ERROR_TAKING_SNAPSHOT.getValue());
         }
         long elapsed = Time.currentElapsedTime() - start;
         LOG.info("Snapshot taken in {} ms", elapsed);
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 3b87312..7ddb1de 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
@@ -35,6 +35,7 @@ import org.apache.zookeeper.server.persistence.FileTxnSnapLog;
 import org.apache.zookeeper.server.persistence.FileTxnSnapLog.DatadirException;
 import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException;
 import org.apache.zookeeper.server.util.JvmPauseMonitor;
+import org.apache.zookeeper.util.ServiceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -69,29 +70,29 @@ public class ZooKeeperServerMain {
             LOG.info(USAGE);
             System.err.println(USAGE);
             ZKAuditProvider.addServerStartFailureAuditLog();
-            System.exit(ExitCode.INVALID_INVOCATION.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue());
         } catch (ConfigException e) {
             LOG.error("Invalid config, exiting abnormally", e);
             System.err.println("Invalid config, exiting abnormally");
             ZKAuditProvider.addServerStartFailureAuditLog();
-            System.exit(ExitCode.INVALID_INVOCATION.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue());
         } catch (DatadirException e) {
             LOG.error("Unable to access datadir, exiting abnormally", e);
             System.err.println("Unable to access datadir, exiting abnormally");
             ZKAuditProvider.addServerStartFailureAuditLog();
-            System.exit(ExitCode.UNABLE_TO_ACCESS_DATADIR.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.UNABLE_TO_ACCESS_DATADIR.getValue());
         } catch (AdminServerException e) {
             LOG.error("Unable to start AdminServer, exiting abnormally", e);
             System.err.println("Unable to start AdminServer, exiting abnormally");
             ZKAuditProvider.addServerStartFailureAuditLog();
-            System.exit(ExitCode.ERROR_STARTING_ADMIN_SERVER.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.ERROR_STARTING_ADMIN_SERVER.getValue());
         } catch (Exception e) {
             LOG.error("Unexpected exception, exiting abnormally", e);
             ZKAuditProvider.addServerStartFailureAuditLog();
-            System.exit(ExitCode.UNEXPECTED_ERROR.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue());
         }
         LOG.info("Exiting normally");
-        System.exit(ExitCode.EXECUTION_FINISHED.getValue());
+        ServiceUtils.requestSystemExit(ExitCode.EXECUTION_FINISHED.getValue());
     }
 
     protected void initializeAndRun(String[] args) throws ConfigException, IOException, AdminServerException
{
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/TxnLogToolkit.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/TxnLogToolkit.java
index bb95d00..984fb03 100644
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/TxnLogToolkit.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/TxnLogToolkit.java
@@ -58,6 +58,7 @@ import org.apache.zookeeper.txn.MultiTxn;
 import org.apache.zookeeper.txn.SetDataTxn;
 import org.apache.zookeeper.txn.Txn;
 import org.apache.zookeeper.txn.TxnHeader;
+import org.apache.zookeeper.util.ServiceUtils;
 
 public class TxnLogToolkit implements Closeable {
 
@@ -126,7 +127,7 @@ public class TxnLogToolkit implements Closeable {
             printHelpAndExit(e.getExitCode(), e.getOptions());
         } catch (TxnLogToolkitException e) {
             System.err.println(e.getMessage());
-            System.exit(e.getExitCode());
+            ServiceUtils.requestSystemExit(e.getExitCode());
         }
     }
 
@@ -424,7 +425,7 @@ public class TxnLogToolkit implements Closeable {
     private static void printHelpAndExit(int exitCode, Options options) {
         HelpFormatter help = new HelpFormatter();
         help.printHelp(120, "TxnLogToolkit [-dhrvc] <txn_log_file_name> (-z <zxid>)",
"", options, "");
-        System.exit(exitCode);
+        ServiceUtils.requestSystemExit(exitCode);
     }
 
     private void printStat() {
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/FollowerZooKeeperServer.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/FollowerZooKeeperServer.java
index 4b786da..12e552f 100644
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/FollowerZooKeeperServer.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/FollowerZooKeeperServer.java
@@ -34,6 +34,7 @@ import org.apache.zookeeper.server.SyncRequestProcessor;
 import org.apache.zookeeper.server.ZKDatabase;
 import org.apache.zookeeper.server.persistence.FileTxnSnapLog;
 import org.apache.zookeeper.txn.TxnHeader;
+import org.apache.zookeeper.util.ServiceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -100,7 +101,7 @@ public class FollowerZooKeeperServer extends LearnerZooKeeperServer {
         if (firstElementZxid != zxid) {
             LOG.error("Committing zxid 0x" + Long.toHexString(zxid)
                       + " but next pending txn 0x" + Long.toHexString(firstElementZxid));
-            System.exit(ExitCode.UNMATCHED_TXN_COMMIT.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.UNMATCHED_TXN_COMMIT.getValue());
         }
         Request request = pendingTxns.remove();
         request.logLatency(ServerMetrics.getMetrics().COMMIT_PROPAGATION_LATENCY);
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/Learner.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/Learner.java
index 515c0de..0896d88 100644
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/Learner.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/Learner.java
@@ -52,6 +52,7 @@ import org.apache.zookeeper.server.util.SerializeUtils;
 import org.apache.zookeeper.server.util.ZxidUtils;
 import org.apache.zookeeper.txn.SetDataTxn;
 import org.apache.zookeeper.txn.TxnHeader;
+import org.apache.zookeeper.util.ServiceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -455,14 +456,13 @@ public class Learner {
                 if (!truncated) {
                     // not able to truncate the log
                     LOG.error("Not able to truncate the log 0x{}", Long.toHexString(qp.getZxid()));
-                    System.exit(ExitCode.QUORUM_PACKET_ERROR.getValue());
+                    ServiceUtils.requestSystemExit(ExitCode.QUORUM_PACKET_ERROR.getValue());
                 }
                 zk.getZKDatabase().setlastProcessedZxid(qp.getZxid());
 
             } else {
                 LOG.error("Got unexpected packet from leader: {}, exiting ... ", LearnerHandler.packetToString(qp));
-                System.exit(ExitCode.QUORUM_PACKET_ERROR.getValue());
-
+                ServiceUtils.requestSystemExit(ExitCode.QUORUM_PACKET_ERROR.getValue());
             }
             zk.getZKDatabase().initConfigInZKDatabase(self.getQuorumVerifier());
             zk.createSessionTracker();
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumCnxManager.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumCnxManager.java
index e37986d..47a9ca9 100644
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumCnxManager.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumCnxManager.java
@@ -55,6 +55,7 @@ import org.apache.zookeeper.server.quorum.auth.QuorumAuthServer;
 import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier;
 import org.apache.zookeeper.server.util.ConfigUtils;
 import org.apache.zookeeper.util.CircularBlockingQueue;
+import org.apache.zookeeper.util.ServiceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -825,7 +826,7 @@ public class QuorumCnxManager {
         private static final int DEFAULT_PORT_BIND_MAX_RETRY = 3;
 
         private final int portBindMaxRetry;
-        private Runnable socketBindErrorHandler = () -> System.exit(ExitCode.UNABLE_TO_BIND_QUORUM_PORT.getValue());
+        private Runnable socketBindErrorHandler = () -> ServiceUtils.requestSystemExit(ExitCode.UNABLE_TO_BIND_QUORUM_PORT.getValue());
         volatile ServerSocket ss = null;
 
         public Listener() {
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 bf97488..054a47d 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
@@ -38,6 +38,7 @@ import org.apache.zookeeper.server.persistence.FileTxnSnapLog;
 import org.apache.zookeeper.server.persistence.FileTxnSnapLog.DatadirException;
 import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException;
 import org.apache.zookeeper.server.util.JvmPauseMonitor;
+import org.apache.zookeeper.util.ServiceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -92,29 +93,29 @@ public class QuorumPeerMain {
             LOG.info(USAGE);
             System.err.println(USAGE);
             ZKAuditProvider.addServerStartFailureAuditLog();
-            System.exit(ExitCode.INVALID_INVOCATION.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue());
         } catch (ConfigException e) {
             LOG.error("Invalid config, exiting abnormally", e);
             System.err.println("Invalid config, exiting abnormally");
             ZKAuditProvider.addServerStartFailureAuditLog();
-            System.exit(ExitCode.INVALID_INVOCATION.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue());
         } catch (DatadirException e) {
             LOG.error("Unable to access datadir, exiting abnormally", e);
             System.err.println("Unable to access datadir, exiting abnormally");
             ZKAuditProvider.addServerStartFailureAuditLog();
-            System.exit(ExitCode.UNABLE_TO_ACCESS_DATADIR.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.UNABLE_TO_ACCESS_DATADIR.getValue());
         } catch (AdminServerException e) {
             LOG.error("Unable to start AdminServer, exiting abnormally", e);
             System.err.println("Unable to start AdminServer, exiting abnormally");
             ZKAuditProvider.addServerStartFailureAuditLog();
-            System.exit(ExitCode.ERROR_STARTING_ADMIN_SERVER.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.ERROR_STARTING_ADMIN_SERVER.getValue());
         } catch (Exception e) {
             LOG.error("Unexpected exception, exiting abnormally", e);
             ZKAuditProvider.addServerStartFailureAuditLog();
-            System.exit(ExitCode.UNEXPECTED_ERROR.getValue());
+            ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue());
         }
         LOG.info("Exiting normally");
-        System.exit(ExitCode.EXECUTION_FINISHED.getValue());
+        ServiceUtils.requestSystemExit(ExitCode.EXECUTION_FINISHED.getValue());
     }
 
     protected void initializeAndRun(String[] args) throws ConfigException, IOException, AdminServerException
{
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/LogChopper.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/LogChopper.java
index 3a07078..e0e52a9 100644
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/LogChopper.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/LogChopper.java
@@ -36,6 +36,7 @@ import org.apache.zookeeper.server.ExitCode;
 import org.apache.zookeeper.server.persistence.FileHeader;
 import org.apache.zookeeper.server.persistence.FileTxnLog;
 import org.apache.zookeeper.txn.TxnHeader;
+import org.apache.zookeeper.util.ServiceUtils;
 
 /**
  * this class will chop the log at the specified zxid
@@ -49,7 +50,7 @@ public class LogChopper {
             System.out.println("Usage: LogChopper zxid_to_chop_to txn_log_to_chop chopped_filename");
             System.out.println("    this program will read the txn_log_to_chop file and copy
all the transactions");
             System.out.println("    from it up to (and including) the given zxid into chopped_filename.");
-            System.exit(rc.getValue());
+            ServiceUtils.requestSystemExit(rc.getValue());
         }
         String txnLog = args[1];
         String choppedLog = args[2];
@@ -63,7 +64,7 @@ public class LogChopper {
         } catch (Exception e) {
             System.out.println("Got exception: " + e.getMessage());
         }
-        System.exit(rc.getValue());
+        ServiceUtils.requestSystemExit(rc.getValue());
     }
 
     public static boolean chop(InputStream is, OutputStream os, long zxid) throws IOException
{
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/util/ServiceUtils.java b/zookeeper-server/src/main/java/org/apache/zookeeper/util/ServiceUtils.java
new file mode 100644
index 0000000..68a25eb
--- /dev/null
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/util/ServiceUtils.java
@@ -0,0 +1,77 @@
+/*
+ * 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.util;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.Objects;
+import java.util.function.Consumer;
+import org.apache.zookeeper.server.ExitCode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utilities for service management.
+ */
+public abstract class ServiceUtils {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ServiceUtils.class);
+
+    private ServiceUtils() {
+    }
+
+    /**
+     * Default strategy for shutting down the JVM.
+     */
+    @SuppressFBWarnings("DM_EXIT")
+    public static final Consumer<Integer> SYSTEM_EXIT = (code) -> {
+        LOG.error("Exiting JVM with code {}", code);
+        System.exit(code);
+    };
+
+    /**
+     * No-op strategy, useful for tests.
+     */
+    public static final Consumer<Integer> LOG_ONLY = (code) -> {
+        LOG.error("Fatal error, JVM should exit with code {}. "
+                + "Actually System.exit is disabled", code);
+    };
+
+    private static Consumer<Integer> systemExitProcedure = SYSTEM_EXIT;
+
+    /**
+     * Override system callback. Useful for preventing the JVM to exit in tests
+     * or in applications that are running an in-process ZooKeeper server.
+     *
+     * @param systemExitProcedure
+     */
+    public static void setSystemExitProcedure(Consumer<Integer> systemExitProcedure)
{
+        Objects.requireNonNull(systemExitProcedure);
+        ServiceUtils.systemExitProcedure = systemExitProcedure;
+    }
+
+    /**
+     * Force shutdown of the JVM using System.exit.
+     *
+     * @param code the exit code
+     * @see ExitCode
+     */
+    public static void requestSystemExit(int code) {
+        systemExitProcedure.accept(code);
+    }
+
+}
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/version/util/VerGen.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/version/util/VerGen.java
index c7f9fd9..3e9e62a 100644
--- a/zookeeper-server/src/main/java/org/apache/zookeeper/version/util/VerGen.java
+++ b/zookeeper-server/src/main/java/org/apache/zookeeper/version/util/VerGen.java
@@ -18,6 +18,7 @@
 
 package org.apache.zookeeper.version.util;
 
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
@@ -25,6 +26,7 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import org.apache.zookeeper.server.ExitCode;
 
+@SuppressFBWarnings("DM_EXIT")
 public class VerGen {
 
     private static final String PACKAGE_NAME = "org.apache.zookeeper.version";
@@ -123,7 +125,7 @@ public class VerGen {
             w.write("}\n");
         } catch (IOException e) {
             System.out.println("Unable to generate version.VersionInfoMain file: " + e.getMessage());
-            System.exit(1);
+            System.exit(ExitCode.UNEXPECTED_ERROR.getValue());
         }
     }
 
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/ZKTestCase.java b/zookeeper-server/src/test/java/org/apache/zookeeper/ZKTestCase.java
index d9d481a..78e3cd7 100644
--- a/zookeeper-server/src/test/java/org/apache/zookeeper/ZKTestCase.java
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/ZKTestCase.java
@@ -22,6 +22,7 @@ import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import java.io.File;
 import java.time.LocalDateTime;
+import org.apache.zookeeper.util.ServiceUtils;
 import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.rules.TestWatcher;
@@ -42,6 +43,11 @@ public class ZKTestCase {
     protected static final File testBaseDir = new File(System.getProperty("build.test.dir",
"build"));
     private static final Logger LOG = LoggerFactory.getLogger(ZKTestCase.class);
 
+    static {
+        // Disable System.exit in tests.
+        ServiceUtils.setSystemExitProcedure(ServiceUtils.LOG_ONLY);
+    }
+
     private String testName;
 
     protected String getTestName() {


Mime
View raw message