Refactor To Support NCService-based AsterixHyracksIntegrationUtil

Change-Id: Iccc78a8be5d7c6083498aeab51f7c7ce515c8f48
Reviewed-on: https://asterix-gerrit.ics.uci.edu/1330
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/common/AsterixHyracksIntegrationUtil.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/AsterixHyracksIntegrationUtil.java
index ea45203..a8095af 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/AsterixHyracksIntegrationUtil.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/AsterixHyracksIntegrationUtil.java
@@ -21,6 +21,7 @@
 import static org.apache.asterix.api.common.AsterixHyracksIntegrationUtil.LoggerHolder.LOGGER;
 
 import java.io.File;
+import java.io.IOException;
 import java.net.Inet4Address;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -64,20 +65,22 @@
 
     public void init(boolean deleteOldInstanceData) throws Exception {
         ncs = new NodeControllerService[0]; // ensure that ncs is not null
-        propertiesAccessor = AsterixPropertiesAccessor.getInstance();
+        final CCConfig ccConfig = createCCConfig();
+        propertiesAccessor = AsterixPropertiesAccessor.getInstance(ccConfig.getAppConfig());
         if (deleteOldInstanceData) {
             deleteTransactionLogs();
             removeTestStorageFiles();
         }
 
-        cc = new ClusterControllerService(createCCConfig());
+        cc = new ClusterControllerService(ccConfig);
         cc.start();
 
         // Starts ncs.
         List<String> nodes = propertiesAccessor.getNodeNames();
         List<NodeControllerService> nodeControllers = new ArrayList<>();
         for (String ncName : nodes) {
-            NodeControllerService nodeControllerService = new NodeControllerService(createNCConfig(ncName));
+            NodeControllerService nodeControllerService =
+                    new NodeControllerService(fixupIODevices(createNCConfig(ncName)));
             nodeControllers.add(nodeControllerService);
             Thread ncStartThread = new Thread("IntegrationUtil-" + ncName) {
                 @Override
@@ -96,7 +99,7 @@
         ncs = nodeControllers.toArray(new NodeControllerService[nodeControllers.size()]);
     }
 
-    protected CCConfig createCCConfig() {
+    protected CCConfig createCCConfig() throws IOException {
         CCConfig ccConfig = new CCConfig();
         ccConfig.clusterNetIpAddress = Inet4Address.getLoopbackAddress().getHostAddress();
         ccConfig.clientNetIpAddress = Inet4Address.getLoopbackAddress().getHostAddress();
@@ -109,7 +112,7 @@
         return ccConfig;
     }
 
-    protected NCConfig createNCConfig(String ncName) throws AsterixException {
+    protected NCConfig createNCConfig(String ncName) throws AsterixException, IOException {
         NCConfig ncConfig = new NCConfig();
         ncConfig.ccHost = "localhost";
         ncConfig.ccPort = DEFAULT_HYRACKS_CC_CLUSTER_PORT;
@@ -121,15 +124,20 @@
         ncConfig.resultTTL = 30000;
         ncConfig.resultSweepThreshold = 1000;
         ncConfig.appArgs = Collections.singletonList("-virtual-NC");
+        ncConfig.appNCMainClass = NCApplicationEntryPoint.class.getName();
+        return ncConfig;
+    }
+
+    private NCConfig fixupIODevices(NCConfig ncConfig) throws AsterixException {
         String tempPath = System.getProperty(IO_DIR_KEY);
         if (tempPath.endsWith(File.separator)) {
             tempPath = tempPath.substring(0, tempPath.length() - 1);
         }
         LOGGER.info("Using the temp path: " + tempPath);
         // get initial partitions from properties
-        String[] nodeStores = propertiesAccessor.getStores().get(ncName);
+        String[] nodeStores = propertiesAccessor.getStores().get(ncConfig.nodeId);
         if (nodeStores == null) {
-            throw new AsterixException("Coudn't find stores for NC: " + ncName);
+            throw new AsterixException("Couldn't find stores for NC: " + ncConfig.nodeId);
         }
         String tempDirPath = System.getProperty(IO_DIR_KEY);
         if (!tempDirPath.endsWith(File.separator)) {
@@ -146,10 +154,10 @@
                 ncConfig.ioDevices += "," + iodevicePath;
             }
         }
-        ncConfig.appNCMainClass = NCApplicationEntryPoint.class.getName();
         return ncConfig;
     }
 
+
     public String[] getNcNames() {
         return propertiesAccessor.getNodeNames().toArray(new String[propertiesAccessor.getNodeNames().size()]);
     }
@@ -208,7 +216,7 @@
         }
     }
 
-    private void deleteTransactionLogs() throws Exception {
+    private void deleteTransactionLogs() throws IOException {
         for (String ncId : propertiesAccessor.getNodeNames()) {
             File log = new File(propertiesAccessor.getTransactionLogDirs().get(ncId));
             if (log.exists()) {
@@ -224,33 +232,32 @@
      * @param args
      *            unused
      */
-    public static void main(String[] args) {
+    public static void main(String[] args) throws Exception {
         AsterixHyracksIntegrationUtil integrationUtil = new AsterixHyracksIntegrationUtil();
-        run(integrationUtil, Boolean.getBoolean("cleanup.start"), Boolean.getBoolean("cleanup.shutdown"));
+        try {
+            integrationUtil.run(Boolean.getBoolean("cleanup.start"), Boolean.getBoolean("cleanup.shutdown"));
+        } catch (Exception e) {
+            LOGGER.log(Level.WARNING, "Unexpected exception", e);
+            System.exit(1);
+        }
     }
 
-    protected static void run(final AsterixHyracksIntegrationUtil integrationUtil, boolean cleanupOnStart,
-            boolean cleanupOnShutdown) {
+    protected void run(boolean cleanupOnStart, boolean cleanupOnShutdown) throws Exception {
         Runtime.getRuntime().addShutdownHook(new Thread() {
             @Override
             public void run() {
                 try {
-                    integrationUtil.deinit(cleanupOnShutdown);
+                    deinit(cleanupOnShutdown);
                 } catch (Exception e) {
-                    e.printStackTrace();
+                    LOGGER.log(Level.WARNING, "Unexpected exception on shutdown", e);
                 }
             }
         });
-        try {
-            System.setProperty(GlobalConfig.CONFIG_FILE_PROPERTY, "asterix-build-configuration.xml");
+        System.setProperty(GlobalConfig.CONFIG_FILE_PROPERTY, "asterix-build-configuration.xml");
 
-            integrationUtil.init(cleanupOnStart);
-            while (true) {
-                Thread.sleep(10000);
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-            System.exit(1);
+        init(cleanupOnStart);
+        while (true) {
+            Thread.sleep(10000);
         }
     }
 }
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 c76534b..5fdcede 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
@@ -218,7 +218,7 @@
 
     private void connectNCs() throws Exception {
         Ini ini = ccConfig.getIni();
-        if (ini == null) {
+        if (ini == null || Boolean.parseBoolean(ini.get("cc", "virtual.cluster"))) {
             return;
         }
         for (String section : ini.keySet()) {
@@ -237,7 +237,7 @@
 
     private void terminateNCServices() throws Exception {
         Ini ini = ccConfig.getIni();
-        if (ini == null) {
+        if (ini == null || Boolean.parseBoolean(ini.get("cc", "virtual.cluster"))) {
             return;
         }
         List<ShutdownNCServiceWork> shutdownNCServiceWorks = new ArrayList<>();
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/CCConfig.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/CCConfig.java
index 98d6375..d7c3d36 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/CCConfig.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/CCConfig.java
@@ -21,6 +21,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.net.InetAddress;
+import java.net.URL;
 import java.util.List;
 
 import org.apache.hyracks.api.application.IApplicationConfig;
@@ -105,12 +106,20 @@
     @Option(name = "--", handler = StopOptionHandler.class)
     public List<String> appArgs;
 
+    public URL configFileUrl = null;
+
     private Ini ini = null;
 
     private void loadINIFile() throws IOException {
         // This method simply maps from the ini parameters to the CCConfig's fields.
         // It does not apply defaults or any logic.
-        ini = IniUtils.loadINIFile(configFile);
+        if (configFile != null) {
+            ini = IniUtils.loadINIFile(configFile);
+        } else if (configFileUrl != null) {
+            ini = IniUtils.loadINIFile(configFileUrl);
+        } else {
+            return;
+        }
 
         ipAddress = IniUtils.getString(ini, "cc", "address", ipAddress);
         clientNetIpAddress = IniUtils.getString(ini, "cc", "client.address", clientNetIpAddress);
@@ -139,8 +148,8 @@
      *    clusterNetIpAddress to ipAddress
      */
     public void loadConfigAndApplyDefaults() throws IOException {
-        if (configFile != null) {
-            loadINIFile();
+        loadINIFile();
+        if (ini != null) {
             // QQQ This way of passing overridden/defaulted values back into
             // the ini feels clunky, and it's clearly incomplete
             ini.add("cc", "cluster.address", clusterNetIpAddress);
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/IniUtils.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/IniUtils.java
index e999de4..451421d 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/IniUtils.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/IniUtils.java
@@ -22,6 +22,7 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.lang.reflect.Array;
+import java.net.URL;
 
 import org.ini4j.Ini;
 import org.ini4j.Profile.Section;
@@ -93,4 +94,10 @@
         ini.load(conffile);
         return ini;
     }
+
+    public static Ini loadINIFile(URL configURL) throws IOException {
+        Ini ini = new Ini();
+        ini.load(configURL);
+        return ini;
+    }
 }
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/NCConfig.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/NCConfig.java
index 2e47f41..fa7d76a 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/NCConfig.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/NCConfig.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.io.Serializable;
 import java.net.InetAddress;
+import java.net.URL;
 import java.util.List;
 import java.util.Map;
 
@@ -161,10 +162,19 @@
     @Option(name = "--", handler = StopOptionHandler.class)
     public List<String> appArgs;
 
+    public URL configFileUrl = null;
+
     private transient Ini ini = null;
 
     private void loadINIFile() throws IOException {
-        ini = IniUtils.loadINIFile(configFile);
+        if (configFile != null) {
+            ini = IniUtils.loadINIFile(configFile);
+        } else if (configFileUrl != null) {
+            ini = IniUtils.loadINIFile(configFileUrl);
+        } else {
+            return;
+        }
+
         // QQQ This should default to cc/address if cluster.address not set, but
         // that logic really should be handled by the ini file sent from the CC
         ccHost = IniUtils.getString(ini, "cc", "cluster.address", ccHost);
@@ -215,9 +225,7 @@
      *    clusterNetIpAddress to ipAddress
      */
     public void loadConfigAndApplyDefaults() throws IOException {
-        if (configFile != null) {
-            loadINIFile();
-        }
+        loadINIFile();
 
         // "address" is the default for all IP addresses
         if (clusterNetIPAddress == null) {