Support -version Argument For CC/NC Drivers

Output the build version when -version is passed to asterixcc/asterixnc

Change-Id: I7ba105f693170ba781897dd2039783e1c5f6544b
Reviewed-on: https://asterix-gerrit.ics.uci.edu/1637
Reviewed-by: Till Westmann <tillw@apache.org>
Sonar-Qube: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/config/IConfigManager.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/config/IConfigManager.java
index 3944820..777f7a7 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/config/IConfigManager.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/config/IConfigManager.java
@@ -62,4 +62,6 @@
     void addCmdLineSections(Section... sections);
 
     void setUsageFilter(OptionHandlerFilter usageFilter);
+
+    void setVersionString(String versionString);
 }
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/config/ConfigManager.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/config/ConfigManager.java
index 04cf704..63163bb 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/config/ConfigManager.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/config/ConfigManager.java
@@ -82,6 +82,7 @@
     private transient OptionHandlerFilter usageFilter;
     private transient SortedMap<Integer, List<IConfigurator>> configurators = new TreeMap<>();
     private boolean configured;
+    private String versionString = "version undefined";
 
     public ConfigManager() {
         this(null);
@@ -173,6 +174,11 @@
         }
     }
 
+    @Override
+    public void setVersionString(String versionString) {
+        this.versionString = versionString;
+    }
+
     public IOption lookupOption(String section, String key) {
         Map<String, IOption> map = getSectionOptionMap(Section.parseSectionName(section));
         return map == null ? null : map.get(key);
@@ -242,25 +248,27 @@
                     new Args4jArgument());
         }
         LOGGER.fine("parsing cmdline: " + Arrays.toString(args));
+        if (args == null || args.length == 0) {
+            LOGGER.info("no command line args supplied");
+            return appArgs;
+        }
         try {
-            if (args == null || args.length == 0) {
-                LOGGER.info("no command line args supplied");
-                return appArgs;
-            }
             cmdLineParser.parseArgument(args);
-            if (bean.help) {
-                ConfigUtils.printUsage(cmdLineParser, usageFilter, System.err);
-                System.exit(0);
-            }
         } catch (CmdLineException e) {
-            if (bean.help) {
-                ConfigUtils.printUsage(cmdLineParser, usageFilter, System.err);
-                System.exit(0);
-            } else {
+            if (!bean.help) {
                 ConfigUtils.printUsage(e, usageFilter, System.err);
                 throw e;
+            } else {
+                LOGGER.log(Level.FINE, "Ignoring parse exception due to -help", e);
             }
         }
+        if (bean.help) {
+            ConfigUtils.printUsage(cmdLineParser, usageFilter, System.err);
+            System.exit(0);
+        } else if (bean.version) {
+            System.err.println(versionString);
+            System.exit(0);
+        }
         return appArgs;
     }
 
@@ -549,5 +557,8 @@
     private static class Args4jBean {
         @Option(name = "-help", help = true)
         boolean help;
+
+        @Option(name = "-version", help = true)
+        boolean version;
     }
 }
\ No newline at end of file
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/NCDriver.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/NCDriver.java
index d36586f..9d649f6 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/NCDriver.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/NCDriver.java
@@ -44,8 +44,8 @@
             application.registerConfig(configManager);
             NCConfig ncConfig = new NCConfig(nodeId, configManager);
             final NodeControllerService ncService = new NodeControllerService(ncConfig, application);
-            if (LOGGER.isLoggable(Level.SEVERE)) {
-                LOGGER.severe("Setting uncaught exception handler " + ncService.getLifeCycleComponentManager());
+            if (LOGGER.isLoggable(Level.INFO)) {
+                LOGGER.info("Setting uncaught exception handler " + ncService.getLifeCycleComponentManager());
             }
             Thread.currentThread().setUncaughtExceptionHandler(ncService.getLifeCycleComponentManager());
             ncService.start();
diff --git a/hyracks-fullstack/hyracks/hyracks-util/pom.xml b/hyracks-fullstack/hyracks/hyracks-util/pom.xml
index 864bd8e..420ce79 100644
--- a/hyracks-fullstack/hyracks/hyracks-util/pom.xml
+++ b/hyracks-fullstack/hyracks/hyracks-util/pom.xml
@@ -51,6 +51,14 @@
       <artifactId>junit</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-core</artifactId>
+    </dependency>
   </dependencies>
 
 </project>
diff --git a/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/JSONUtil.java b/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/JSONUtil.java
new file mode 100644
index 0000000..7075417
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/JSONUtil.java
@@ -0,0 +1,177 @@
+/*
+ * 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.hyracks.util;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.logging.Logger;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+
+public class JSONUtil {
+
+    private static final Logger LOGGER = Logger.getLogger(JSONUtil.class.getName());
+
+    private static final String INDENT = "\t";
+
+    private static final ObjectMapper SORTED_MAPPER = new ObjectMapper();
+
+    private JSONUtil() {
+    }
+
+    static {
+        SORTED_MAPPER.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
+    }
+
+    public static String convertNode(final JsonNode node) throws JsonProcessingException {
+        final Object obj = SORTED_MAPPER.treeToValue(node, Object.class);
+        return SORTED_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
+    }
+
+    public static String indent(String str, int initialIndent) {
+        ObjectMapper om = new ObjectMapper();
+        try {
+            return appendObj(new StringBuilder(), om.readTree(str), initialIndent).toString();
+        } catch (IOException e) {
+            LOGGER.finest(String.valueOf(e));
+            LOGGER.finest("Could not indent JSON string, returning the input string: " + str);
+            return str;
+        }
+    }
+
+    private static StringBuilder appendOrd(StringBuilder sb, JsonNode o, int indent) {
+        if (o.isObject()) {
+            return appendObj(sb, o, indent);
+        } else if (o.isArray()) {
+            return appendAry(sb, o, indent);
+        } else if (o.isTextual()) {
+            return quoteAndEscape(sb, o.asText());
+        } else if (o.isNull() || o.isIntegralNumber() || o.isBoolean()) {
+            return sb.append(String.valueOf(o));
+        }
+        throw new UnsupportedOperationException(o.getClass().getSimpleName());
+    }
+
+    private static StringBuilder appendObj(StringBuilder builder, JsonNode jobj, int indent) {
+        StringBuilder sb = builder.append("{\n");
+        boolean first = true;
+        for (Iterator<JsonNode> it = jobj.iterator(); it.hasNext();) {
+            final String key = it.next().asText();
+            if (first) {
+                first = false;
+            } else {
+                sb = sb.append(",\n");
+            }
+            sb = indent(sb, indent + 1);
+            sb = quote(sb, key);
+            sb = sb.append(": ");
+            if (jobj.get(key).isArray()) {
+                sb = appendAry(sb, jobj.get(key), indent + 1);
+            } else if (jobj.get(key).isObject()) {
+                sb = appendObj(sb, jobj.get(key), indent + 1);
+            } else {
+                sb = appendOrd(sb, jobj.get(key), indent + 1);
+            }
+        }
+        sb = sb.append("\n");
+        return indent(sb, indent).append("}");
+    }
+
+    private static StringBuilder appendAry(StringBuilder builder, JsonNode jarr, int indent) {
+        StringBuilder sb = builder.append("[\n");
+        for (int i = 0; i < jarr.size(); ++i) {
+            if (i > 0) {
+                sb = sb.append(",\n");
+            }
+            sb = indent(sb, indent + 1);
+            if (jarr.get(i).isArray()) {
+                sb = appendAry(sb, jarr.get(i), indent + 1);
+            } else if (jarr.get(i).isObject()) {
+                sb = appendObj(sb, jarr.get(i), indent + 1);
+            } else {
+                sb = appendOrd(sb, jarr.get(i), indent + 1);
+            }
+        }
+        sb = sb.append("\n");
+        return indent(sb, indent).append("]");
+    }
+
+    private static StringBuilder quote(StringBuilder sb, String str) {
+        return sb.append('"').append(str).append('"');
+    }
+
+    private static StringBuilder indent(StringBuilder sb, int i) {
+        int indent = i;
+        while (indent > 0) {
+            sb.append(INDENT);
+            --indent;
+        }
+        return sb;
+    }
+
+    public static String quoteAndEscape(String str) {
+        return quoteAndEscape(new StringBuilder(), str).toString();
+    }
+
+    private static StringBuilder quoteAndEscape(StringBuilder sb, String str) {
+        return escape(sb.append('"'), str).append('"');
+    }
+
+    public static String escape(String str) {
+        return escape(new StringBuilder(), str).toString();
+    }
+
+    public static StringBuilder escape(StringBuilder sb, String str) {
+        for (int i = 0; i < str.length(); ++i) {
+            appendEsc(sb, str.charAt(i));
+        }
+        return sb;
+    }
+
+    private static StringBuilder appendEsc(StringBuilder sb, char c) {
+        CharSequence cs = esc(c);
+        return cs != null ? sb.append(cs) : sb.append(c);
+    }
+
+    public static CharSequence esc(char c) {
+        switch (c) {
+            case '"':
+                return "\\\"";
+            case '\\':
+                return "\\\\";
+            case '/':
+                return "\\/";
+            case '\b':
+                return "\\b";
+            case '\n':
+                return "\\n";
+            case '\f':
+                return "\\f";
+            case '\r':
+                return "\\r";
+            case '\t':
+                return "\\t";
+            default:
+                return null;
+        }
+    }
+}