Parameter To Shutdown API to Terminate NC Services

POST to /admin/shutdown?all=true causes NC Services to exit as part of
cluster shutdown

Change-Id: Ic3009a13f4498f36dd18ea26f861506bf7d930e7
Reviewed-on: https://asterix-gerrit.ics.uci.edu/1172
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Till Westmann <tillw@apache.org>
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ShutdownAPIServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ShutdownAPIServlet.java
index c786dc7..7d2a272 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ShutdownAPIServlet.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ShutdownAPIServlet.java
@@ -45,9 +45,10 @@
 
         ServletContext context = getServletContext();
         IHyracksClientConnection hcc = (IHyracksClientConnection) context.getAttribute(HYRACKS_CONNECTION_ATTR);
+        boolean terminateNCServices = "true".equalsIgnoreCase(request.getParameter("all"));
         Thread t = new Thread(() -> {
             try {
-                hcc.stopCluster();
+                hcc.stopCluster(terminateNCServices);
             } catch (Exception e) {
                 GlobalConfig.ASTERIX_LOGGER.log(Level.SEVERE, "Exception stopping cluster", e);
             }
diff --git a/asterixdb/asterix-client-helper/src/main/java/org/apache/asterix/clienthelper/AsterixHelperExecution.java b/asterixdb/asterix-client-helper/src/main/java/org/apache/asterix/clienthelper/AsterixHelperExecution.java
index 419e660..424c1e3 100644
--- a/asterixdb/asterix-client-helper/src/main/java/org/apache/asterix/clienthelper/AsterixHelperExecution.java
+++ b/asterixdb/asterix-client-helper/src/main/java/org/apache/asterix/clienthelper/AsterixHelperExecution.java
@@ -24,6 +24,7 @@
 import org.apache.asterix.clienthelper.commands.ClientCommand;
 import org.apache.asterix.clienthelper.commands.ClientCommand.Command;
 import org.apache.asterix.clienthelper.commands.GetClusterStateCommand;
+import org.apache.asterix.clienthelper.commands.ShutdownAllCommand;
 import org.apache.asterix.clienthelper.commands.ShutdownCommand;
 import org.apache.asterix.clienthelper.commands.WaitForClusterCommand;
 import org.kohsuke.args4j.CmdLineException;
@@ -108,6 +109,8 @@
                 return new WaitForClusterCommand(args);
             case SHUTDOWN_CLUSTER:
                 return new ShutdownCommand(args);
+            case SHUTDOWN_CLUSTER_ALL:
+                return new ShutdownAllCommand(args);
             default:
                 throw new IllegalStateException("NYI: " + command);
         }
diff --git a/asterixdb/asterix-client-helper/src/main/java/org/apache/asterix/clienthelper/commands/ClientCommand.java b/asterixdb/asterix-client-helper/src/main/java/org/apache/asterix/clienthelper/commands/ClientCommand.java
index 8f0cac0..c86ea36 100644
--- a/asterixdb/asterix-client-helper/src/main/java/org/apache/asterix/clienthelper/commands/ClientCommand.java
+++ b/asterixdb/asterix-client-helper/src/main/java/org/apache/asterix/clienthelper/commands/ClientCommand.java
@@ -30,7 +30,8 @@
     public enum Command {
         GET_CLUSTER_STATE("Get state of cluster (errorcode 0 = UP, non-zero = DOWN)"),
         WAIT_FOR_CLUSTER("Wait for cluster to be ready (errorcode 0 = UP, non-zero = UNKNOWN)"),
-        SHUTDOWN_CLUSTER("Instructs the cluster to shut down"),;
+        SHUTDOWN_CLUSTER("Instructs the cluster to shut down, leaving NCService processes intact"),
+        SHUTDOWN_CLUSTER_ALL("Instructs the cluster to shut down, including NCService processes");
 
         private final String usage;
         private static final Map<String, Command> nameMap = new HashMap<>();
diff --git a/asterixdb/asterix-client-helper/src/main/java/org/apache/asterix/clienthelper/commands/ShutdownAllCommand.java b/asterixdb/asterix-client-helper/src/main/java/org/apache/asterix/clienthelper/commands/ShutdownAllCommand.java
new file mode 100644
index 0000000..3903262
--- /dev/null
+++ b/asterixdb/asterix-client-helper/src/main/java/org/apache/asterix/clienthelper/commands/ShutdownAllCommand.java
@@ -0,0 +1,28 @@
+/*
+ * 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.asterix.clienthelper.commands;
+
+import org.apache.asterix.clienthelper.Args;
+
+public class ShutdownAllCommand extends ShutdownCommand {
+
+    public ShutdownAllCommand(Args args) {
+        super(args, "?all=true");
+    }
+}
diff --git a/asterixdb/asterix-client-helper/src/main/java/org/apache/asterix/clienthelper/commands/ShutdownCommand.java b/asterixdb/asterix-client-helper/src/main/java/org/apache/asterix/clienthelper/commands/ShutdownCommand.java
index ee6e1a7..688f4ad 100644
--- a/asterixdb/asterix-client-helper/src/main/java/org/apache/asterix/clienthelper/commands/ShutdownCommand.java
+++ b/asterixdb/asterix-client-helper/src/main/java/org/apache/asterix/clienthelper/commands/ShutdownCommand.java
@@ -27,9 +27,13 @@
 public class ShutdownCommand extends RemoteCommand {
     private final String shutdownPath;
 
-    public ShutdownCommand(Args args) {
+    public ShutdownCommand(Args args, String extra) {
         super(args);
-        shutdownPath = args.getShutdownPath();
+        shutdownPath = args.getShutdownPath() + extra;
+    }
+
+    public ShutdownCommand(Args args) {
+        this(args, "");
     }
 
     private void clusterLog(String suffix) {
diff --git a/asterixdb/asterix-server/src/main/samples/local/bin/stop-sample-cluster.bat b/asterixdb/asterix-server/src/main/samples/local/bin/stop-sample-cluster.bat
index 7cbf107..dfd1ada 100644
--- a/asterixdb/asterix-server/src/main/samples/local/bin/stop-sample-cluster.bat
+++ b/asterixdb/asterix-server/src/main/samples/local/bin/stop-sample-cluster.bat
@@ -37,15 +37,12 @@
 
 call %INSTALLDIR%\bin\${HELPER_COMMAND} get_cluster_state -quiet
 if %ERRORLEVEL% EQU 0 (
-  call %INSTALLDIR%\bin\${HELPER_COMMAND} shutdown_cluster
+  call %INSTALLDIR%\bin\${HELPER_COMMAND} shutdown_cluster_all
 ) else (
-  echo WARNING: sample cluster does not appear to be running, will attempt to kill any running
-  echo          NCServices and wait for CCDriver to terminate if running.
+  echo WARNING: sample cluster does not appear to be running, will attempt to wait for
+  echo          CCDriver to terminate if running.
 )
 echo.
-echo Terminating NC services...
-powershell "%JAVA_HOME%\bin\jps.exe -v | select-string -pattern ${NC_SERVICE_COMMAND} | %%{ $_.ToString().Split(' ')[0] } | %%{ Stop-Process $_ }"
-
 powershell "Write-Host "Waiting for CCDriver to terminate..." -nonewline; do { if ($running) { Start-Sleep 1 }; %JAVA_HOME%\bin\jps.exe -v | select-string -pattern ${CC_COMMAND} -quiet -outvariable running | Out-Null; Write-Host "." -nonewline } while ($running)"
 echo .done.
 goto :END
diff --git a/asterixdb/asterix-server/src/main/samples/local/bin/stop-sample-cluster.sh b/asterixdb/asterix-server/src/main/samples/local/bin/stop-sample-cluster.sh
index 7451e4e..462d53d 100755
--- a/asterixdb/asterix-server/src/main/samples/local/bin/stop-sample-cluster.sh
+++ b/asterixdb/asterix-server/src/main/samples/local/bin/stop-sample-cluster.sh
@@ -43,13 +43,12 @@
 
 if $INSTALLDIR/bin/${HELPER_COMMAND} get_cluster_state -quiet;
 then
-  $INSTALLDIR/bin/${HELPER_COMMAND} shutdown_cluster
+  $INSTALLDIR/bin/${HELPER_COMMAND} shutdown_cluster_all
 else
-  echo "WARNING: sample cluster does not appear to be running, will attempt to kill any running NCServices and"
-  echo "         wait for CCDriver to terminate if running."
+  echo "WARNING: sample cluster does not appear to be running, will attempt to wait for"
+  echo "         CCDriver to terminate if running."
 fi
 
-$JAVA_HOME/bin/jps | awk '/NCService/ { print $1 }' | xargs kill 2>/dev/null
 first=1
 while [ -n "$($JAVA_HOME/bin/jps | awk '/CCDriver/')" ]; do
   if [ $first ]; then
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceFunctions.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceFunctions.java
index 88c4edb..a7c2a36 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceFunctions.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceFunctions.java
@@ -289,11 +289,20 @@
 
     public static class ClusterShutdownFunction extends Function {
         private static final long serialVersionUID = 1L;
+        private final boolean terminateNCService;
+
+        public ClusterShutdownFunction(boolean terminateNCService) {
+            this.terminateNCService = terminateNCService;
+        }
 
         @Override
         public FunctionId getFunctionId() {
             return FunctionId.CLUSTER_SHUTDOWN;
         }
+
+        public boolean isTerminateNCService() {
+            return terminateNCService;
+        }
     }
 
     public static class GetNodeDetailsJSONFunction extends Function {
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceRemoteProxy.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceRemoteProxy.java
index 86a8ceb..f88f30f 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceRemoteProxy.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceRemoteProxy.java
@@ -118,8 +118,9 @@
     }
 
     @Override
-    public void stopCluster() throws Exception {
-        HyracksClientInterfaceFunctions.ClusterShutdownFunction csdf = new HyracksClientInterfaceFunctions.ClusterShutdownFunction();
+    public void stopCluster(boolean terminateNCService) throws Exception {
+        HyracksClientInterfaceFunctions.ClusterShutdownFunction csdf =
+                new HyracksClientInterfaceFunctions.ClusterShutdownFunction(terminateNCService);
         rpci.call(ipcHandle, csdf);
         //give the CC some time to do final settling after it returns our request
         for (int i = 3; ipcHandle.isConnected() && i > 0; i--) {
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksConnection.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksConnection.java
index 4b27caf..8c0557e 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksConnection.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksConnection.java
@@ -189,8 +189,8 @@
     }
 
     @Override
-    public void stopCluster() throws Exception{
-        hci.stopCluster();
+    public void stopCluster(boolean terminateNCService) throws Exception {
+        hci.stopCluster(terminateNCService);
     }
 
     @Override
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientConnection.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientConnection.java
index 6c15da2..fd4d21b 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientConnection.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientConnection.java
@@ -185,8 +185,9 @@
 
     /**
      * Shuts down all NCs and then the CC.
+     * @param terminateNCService
      */
-    public void stopCluster() throws Exception;
+    public void stopCluster(boolean terminateNCService) throws Exception;
 
     /**
      * Get details of specified node as JSON object
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientInterface.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientInterface.java
index c2af2e7..3f3c120 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientInterface.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientInterface.java
@@ -54,7 +54,7 @@
 
     public JobInfo getJobInfo(JobId jobId) throws Exception;
 
-    public void stopCluster() throws Exception;
+    public void stopCluster(boolean terminateNCService) throws Exception;
 
     public String getNodeDetailsJSON(String nodeId, boolean includeStats, boolean includeConfig) throws Exception;
 
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/ClusterControllerService.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/ClusterControllerService.java
index ce272eb..685a36b 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/ClusterControllerService.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/ClusterControllerService.java
@@ -516,8 +516,10 @@
                     return;
                 }
                 case CLUSTER_SHUTDOWN: {
+                    HyracksClientInterfaceFunctions.ClusterShutdownFunction csf =
+                            (HyracksClientInterfaceFunctions.ClusterShutdownFunction) fn;
                     workQueue.schedule(new ClusterShutdownWork(ClusterControllerService.this,
-                            new IPCResponder<>(handle, mid)));
+                            csf.isTerminateNCService(), new IPCResponder<>(handle, mid)));
                     return;
                 }
 
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/ClusterShutdownWork.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/ClusterShutdownWork.java
index 6acab12..63ca4ae 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/ClusterShutdownWork.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/ClusterShutdownWork.java
@@ -33,13 +33,16 @@
 import org.apache.hyracks.ipc.exceptions.IPCException;
 
 public class ClusterShutdownWork extends SynchronizableWork {
+    private static final Logger LOGGER = Logger.getLogger(ClusterShutdownWork.class.getName());
 
-    private ClusterControllerService ccs;
-    private IResultCallback<Boolean> callback;
-    private static Logger LOGGER = Logger.getLogger(ClusterShutdownWork.class.getName());
+    private final ClusterControllerService ccs;
+    private final boolean terminateNCService;
+    private final IResultCallback<Boolean> callback;
 
-    public ClusterShutdownWork(ClusterControllerService ncs, IResultCallback<Boolean> callback) {
+    public ClusterShutdownWork(ClusterControllerService ncs, boolean terminateNCService,
+                               IResultCallback<Boolean> callback) {
         this.ccs = ncs;
+        this.terminateNCService = terminateNCService;
         this.callback = callback;
     }
 
@@ -106,7 +109,7 @@
 
     protected void shutdownNode(String key, NodeControllerState ncState) {
         try {
-            ncState.getNodeController().shutdown();
+            ncState.getNodeController().shutdown(terminateNCService);
         } catch (Exception e) {
             LOGGER.log(
                     Level.INFO, "Exception shutting down NC " + key + " (possibly dead?), continuing shutdown...", e);
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/base/INodeController.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/base/INodeController.java
index bd550b5..dff5827 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/base/INodeController.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/base/INodeController.java
@@ -51,7 +51,7 @@
 
     public void dumpState(String stateDumpId) throws Exception;
 
-    public void shutdown() throws Exception;
+    public void shutdown(boolean terminateNCService) throws Exception;
 
     public void sendApplicationMessageToNC(byte[] data, DeploymentId deploymentId, String nodeId) throws Exception;
 
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/ipc/CCNCFunctions.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/ipc/CCNCFunctions.java
index aeb2de7..aa9a4fe 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/ipc/CCNCFunctions.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/ipc/CCNCFunctions.java
@@ -1158,10 +1158,20 @@
     public static class ShutdownRequestFunction extends Function {
         private static final long serialVersionUID = 1L;
 
+        private final boolean terminateNCService;
+
+        public ShutdownRequestFunction(boolean terminateNCService) {
+            this.terminateNCService = terminateNCService;
+        }
+
         @Override
         public FunctionId getFunctionId() {
             return FunctionId.SHUTDOWN_REQUEST;
         }
+
+        public boolean isTerminateNCService() {
+            return terminateNCService;
+        }
     }
 
     public static class ShutdownResponseFunction extends Function {
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/ipc/NodeControllerRemoteProxy.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/ipc/NodeControllerRemoteProxy.java
index e4682dc..c3376e6 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/ipc/NodeControllerRemoteProxy.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/ipc/NodeControllerRemoteProxy.java
@@ -90,8 +90,8 @@
     }
 
     @Override
-    public void shutdown() throws Exception {
-        CCNCFunctions.ShutdownRequestFunction sdrf = new CCNCFunctions.ShutdownRequestFunction();
+    public void shutdown(boolean terminateNCService) throws Exception {
+        CCNCFunctions.ShutdownRequestFunction sdrf = new CCNCFunctions.ShutdownRequestFunction(terminateNCService);
         ipcHandle.send(-1, sdrf, null);
     }
 
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/NodeControllerService.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/NodeControllerService.java
index dbf3af0..d7facf0 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/NodeControllerService.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/NodeControllerService.java
@@ -572,7 +572,8 @@
                     return;
 
                 case SHUTDOWN_REQUEST:
-                    queue.schedule(new ShutdownWork(NodeControllerService.this));
+                    final CCNCFunctions.ShutdownRequestFunction sdrf = (CCNCFunctions.ShutdownRequestFunction) fn;
+                    queue.schedule(new ShutdownWork(NodeControllerService.this, sdrf.isTerminateNCService()));
                     return;
 
                 case THREAD_DUMP_REQUEST:
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/work/ShutdownWork.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/work/ShutdownWork.java
index f81882b..4558a91 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/work/ShutdownWork.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/work/ShutdownWork.java
@@ -19,49 +19,49 @@
 
 package org.apache.hyracks.control.nc.work;
 
-import java.net.URL;
-import java.util.List;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import org.apache.hyracks.api.deployment.DeploymentId;
 import org.apache.hyracks.control.common.base.IClusterController;
-import org.apache.hyracks.control.common.deployment.DeploymentStatus;
-import org.apache.hyracks.control.common.deployment.DeploymentUtils;
 import org.apache.hyracks.control.common.work.AbstractWork;
 import org.apache.hyracks.control.nc.NodeControllerService;
 
 public class ShutdownWork extends AbstractWork {
-
-    private final NodeControllerService ncs;
     private static Logger LOGGER = Logger.getLogger(ShutdownWork.class.getName());
+    private final NodeControllerService ncs;
+    private final boolean terminateNCService;
 
-    public ShutdownWork(NodeControllerService ncs) {
+    public ShutdownWork(NodeControllerService ncs, boolean terminateNCService) {
         this.ncs = ncs;
+        this.terminateNCService = terminateNCService;
     }
 
     @Override
     public void run() {
+        IClusterController ccs = ncs.getClusterController();
         try {
-            IClusterController ccs = ncs.getClusterController();
             ccs.notifyShutdown(ncs.getId());
-            LOGGER.info("JVM Exiting.. Bye!");
+        } catch (Exception e) {
+            LOGGER.log(Level.WARNING, "Exception notifying CC of shutdown acknowledgment", e);
+            throw new RuntimeException(e);
+        }
+
+        LOGGER.info("JVM Exiting.. Bye!");
             //run the shutdown in a new thread, so we don't block this last work task
-            Thread t = new Thread() {
+            Thread t = new Thread("NC " + ncs.getId() + " Shutdown") {
+                @Override
                 public void run() {
                     try {
                         ncs.stop();
                     } catch (Exception e) {
-                        LOGGER.severe(e.getMessage());
+                        LOGGER.log(Level.SEVERE, "Exception stopping node controller service", e);
                     } finally {
                         Runtime rt = Runtime.getRuntime();
-                        rt.exit(0);
+                        rt.exit(terminateNCService ? 99 : 0);
                     }
                 }
             };
             t.start();
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
     }
 
 }
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCService.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCService.java
index 4102b4c..5b032d9 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCService.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCService.java
@@ -18,11 +18,6 @@
  */
 package org.apache.hyracks.control.nc.service;
 
-import org.apache.commons.lang3.SystemUtils;
-import org.apache.hyracks.control.common.controllers.IniUtils;
-import org.ini4j.Ini;
-import org.kohsuke.args4j.CmdLineParser;
-
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -37,6 +32,11 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import org.apache.commons.lang3.SystemUtils;
+import org.apache.hyracks.control.common.controllers.IniUtils;
+import org.ini4j.Ini;
+import org.kohsuke.args4j.CmdLineParser;
+
 /**
  * Stand-alone process which listens for configuration information from the
  * CC and starts an NC. Intended to be a constantly-running service.
@@ -164,7 +164,13 @@
             if (LOGGER.isLoggable(Level.INFO)) {
                 LOGGER.info("NCDriver exited with return value " + retval);
             }
-            return (retval == 0);
+            if (retval == 99) {
+                if (LOGGER.isLoggable(Level.INFO)) {
+                    LOGGER.info("Terminating NCService based on return value from NCDriver");
+                }
+                System.exit(0);
+            }
+            return retval == 0;
         } catch (Exception e) {
             if (LOGGER.isLoggable(Level.SEVERE)) {
                 LOGGER.log(Level.SEVERE, "Configuration from CC broken", e);
diff --git a/hyracks-fullstack/hyracks/hyracks-examples/hyracks-shutdown-test/src/test/java/org/apache/hyracks/examples/shutdown/test/ClusterShutdownIT.java b/hyracks-fullstack/hyracks/hyracks-examples/hyracks-shutdown-test/src/test/java/org/apache/hyracks/examples/shutdown/test/ClusterShutdownIT.java
index 97f5a64..c1083c9f 100644
--- a/hyracks-fullstack/hyracks/hyracks-examples/hyracks-shutdown-test/src/test/java/org/apache/hyracks/examples/shutdown/test/ClusterShutdownIT.java
+++ b/hyracks-fullstack/hyracks/hyracks-examples/hyracks-shutdown-test/src/test/java/org/apache/hyracks/examples/shutdown/test/ClusterShutdownIT.java
@@ -36,11 +36,11 @@
     @Test
     public void runShutdown() throws Exception {
         IHyracksClientConnection hcc = new HyracksConnection("localhost", 1098);
-        hcc.stopCluster();
+        hcc.stopCluster(false);
         //what happens here...
         closeTwice.expect(IPCException.class);
         closeTwice.expectMessage("Cannot send on a closed handle");
-        hcc.stopCluster();
+        hcc.stopCluster(false);
         ServerSocket c = null;
         ServerSocket s = null;
         try {