Merge commit 'd9850dc' from 'stabilization-f69489' into 'master'

Change-Id: Ia35c75e8c35720dfd22436f4d08f6986e3aa1b3c
diff --git a/asterixdb/NOTICE b/asterixdb/NOTICE
index 3f18a61..7615782 100644
--- a/asterixdb/NOTICE
+++ b/asterixdb/NOTICE
@@ -1,5 +1,5 @@
 Apache AsterixDB
-Copyright 2015-2018 The Apache Software Foundation
+Copyright 2015-2019 The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/BTreeAccessMethod.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/BTreeAccessMethod.java
index 8d6f04d..c5ff5d1 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/BTreeAccessMethod.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/BTreeAccessMethod.java
@@ -330,8 +330,10 @@
         LimitType[] highKeyLimits = new LimitType[numSecondaryKeys];
         boolean[] lowKeyInclusive = new boolean[numSecondaryKeys];
         boolean[] highKeyInclusive = new boolean[numSecondaryKeys];
-        ILogicalExpression[] constantAtRuntimeExpressions = new ILogicalExpression[numSecondaryKeys];
-        LogicalVariable[] constAtRuntimeExprVars = new LogicalVariable[numSecondaryKeys];
+        ILogicalExpression[] lowKeyConstAtRuntimeExpressions = new ILogicalExpression[numSecondaryKeys];
+        ILogicalExpression[] highKeyConstantAtRuntimeExpressions = new ILogicalExpression[numSecondaryKeys];
+        LogicalVariable[] lowKeyConstAtRuntimeExprVars = new LogicalVariable[numSecondaryKeys];
+        LogicalVariable[] highKeyConstAtRuntimeExprVars = new LogicalVariable[numSecondaryKeys];
 
         /* TODO: For now we don't do any sophisticated analysis of the func exprs to come up with "the best" range
          * predicate. If we can't figure out how to integrate a certain funcExpr into the current predicate,
@@ -368,13 +370,6 @@
             ILogicalExpression searchKeyExpr = returnedSearchKeyExpr.first;
             ILogicalExpression searchKeyEQExpr = null;
             boolean realTypeConvertedToIntegerType = returnedSearchKeyExpr.third;
-            if (searchKeyExpr.getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL) {
-                constantAtRuntimeExpressions[keyPos] = searchKeyExpr;
-                constAtRuntimeExprVars[keyPos] = context.newVar();
-                VariableReferenceExpression varRef = new VariableReferenceExpression(constAtRuntimeExprVars[keyPos]);
-                varRef.setSourceLocation(searchKeyExpr.getSourceLocation());
-                searchKeyExpr = varRef;
-            }
 
             LimitType limit = getLimitType(optFuncExpr, probeSubTree);
             if (limit == null) {
@@ -396,6 +391,24 @@
                 }
             }
 
+            if (searchKeyExpr.getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL) {
+                LogicalVariable constAtRuntimeExprVar = context.newVar();
+                VariableReferenceExpression constAtRuntimeExprVarRef =
+                        new VariableReferenceExpression(constAtRuntimeExprVar);
+                constAtRuntimeExprVarRef.setSourceLocation(searchKeyExpr.getSourceLocation());
+
+                if (limit == LimitType.LOW_INCLUSIVE || limit == LimitType.LOW_EXCLUSIVE || limit == LimitType.EQUAL) {
+                    lowKeyConstAtRuntimeExpressions[keyPos] = searchKeyExpr;
+                    lowKeyConstAtRuntimeExprVars[keyPos] = constAtRuntimeExprVar;
+                }
+                if (limit == LimitType.HIGH_INCLUSIVE || limit == LimitType.HIGH_EXCLUSIVE
+                        || limit == LimitType.EQUAL) {
+                    highKeyConstantAtRuntimeExpressions[keyPos] = searchKeyExpr;
+                    highKeyConstAtRuntimeExprVars[keyPos] = constAtRuntimeExprVar;
+                }
+                searchKeyExpr = constAtRuntimeExprVarRef;
+            }
+
             switch (limit) {
                 case EQUAL: {
                     if (lowKeyLimits[keyPos] == null && highKeyLimits[keyPos] == null) {
@@ -563,9 +576,10 @@
         ArrayList<LogicalVariable> assignKeyVarList = new ArrayList<>();
         ArrayList<Mutable<ILogicalExpression>> assignKeyExprList = new ArrayList<>();
         int numLowKeys = createKeyVarsAndExprs(numSecondaryKeys, lowKeyLimits, lowKeyExprs, assignKeyVarList,
-                assignKeyExprList, keyVarList, context, constantAtRuntimeExpressions, constAtRuntimeExprVars);
+                assignKeyExprList, keyVarList, context, lowKeyConstAtRuntimeExpressions, lowKeyConstAtRuntimeExprVars);
         int numHighKeys = createKeyVarsAndExprs(numSecondaryKeys, highKeyLimits, highKeyExprs, assignKeyVarList,
-                assignKeyExprList, keyVarList, context, constantAtRuntimeExpressions, constAtRuntimeExprVars);
+                assignKeyExprList, keyVarList, context, highKeyConstantAtRuntimeExpressions,
+                highKeyConstAtRuntimeExprVars);
 
         BTreeJobGenParams jobGenParams = new BTreeJobGenParams(chosenIndex.getIndexName(), IndexType.BTREE,
                 dataset.getDataverseName(), dataset.getDatasetName(), retainInput, requiresBroadcast);
diff --git a/asterixdb/asterix-app/pom.xml b/asterixdb/asterix-app/pom.xml
index 77e31c0..f74fbf3 100644
--- a/asterixdb/asterix-app/pom.xml
+++ b/asterixdb/asterix-app/pom.xml
@@ -287,7 +287,7 @@
     <profile>
       <id>asterix-gerrit-asterix-app</id>
       <properties>
-        <test.excludes>**/SqlppExecutionWithCancellationTest.java,**/DmlTest.java,**/RepeatedTest.java,**/SqlppExecutionTest.java,**/*Compression*Test.java,**/*Ssl*Test.java</test.excludes>
+        <test.excludes>**/SqlppExecutionWithCancellationTest.java,**/DmlTest.java,**/RepeatedTest.java,**/SqlppExecutionTest.java,**/AqlExecutionTest.java,**/*Compression*Test.java,**/*Ssl*Test.java</test.excludes>
         <itest.excludes>**/*.java</itest.excludes>
       </properties>
       <build>
@@ -336,7 +336,7 @@
     <profile>
       <id>asterix-gerrit-verify-asterix-app</id>
       <properties>
-        <test.excludes>**/*.java</test.excludes>
+        <test.includes>**/AqlExecutionTest.java</test.includes>
         <itest.excludes>**/SqlppExecution*IT.java,**/RebalanceWithCancellationIT.java</itest.excludes>
         <failIfNoTests>false</failIfNoTests>
       </properties>
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/ExecutionTestUtil.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/ExecutionTestUtil.java
index 4ed36c6..b4c5b26 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/ExecutionTestUtil.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/ExecutionTestUtil.java
@@ -18,6 +18,8 @@
  */
 package org.apache.asterix.test.runtime;
 
+import static org.apache.hyracks.util.file.FileUtil.joinPath;
+
 import java.io.File;
 import java.nio.file.Paths;
 import java.util.ArrayList;
@@ -41,6 +43,15 @@
 
     protected static final Logger LOGGER = LogManager.getLogger();
 
+    static {
+        // hack to ensure we have a unique location for external libraries in our tests (asterix cluster has a shared home directory)
+        String appHome = joinPath(System.getProperty("app.home", System.getProperty("user.home")),
+                "appHome" + (int) (Math.random() * Integer.MAX_VALUE));
+        LOGGER.info("setting app.home to {}", appHome);
+        System.setProperty("app.home", appHome);
+        new File(appHome).deleteOnExit();
+    }
+
     protected static final String PATH_ACTUAL = "rttest" + File.separator;
 
     public static TestGroup FailedGroup;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/range-search-open/range-search-open.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/range-search-open/range-search-open.5.query.sqlpp
new file mode 100644
index 0000000..c37d241
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/range-search-open/range-search-open.5.query.sqlpp
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+/*
+ * Description: test range search with non-constant search expressions
+ */
+
+use test;
+
+select value count(*)
+from LineItem as c
+where
+  ((c.l_suppkey < 100 + tobigint(random()/2) /* = 0 */) and
+  (c.l_suppkey > 5 + tobigint(random()/2) /* = 0 */));
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/range-search/range-search.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/range-search/range-search.5.query.sqlpp
new file mode 100644
index 0000000..c37d241
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/range-search/range-search.5.query.sqlpp
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+/*
+ * Description: test range search with non-constant search expressions
+ */
+
+use test;
+
+select value count(*)
+from LineItem as c
+where
+  ((c.l_suppkey < 100 + tobigint(random()/2) /* = 0 */) and
+  (c.l_suppkey > 5 + tobigint(random()/2) /* = 0 */));
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/range-search-open/range-search-open.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/range-search-open/range-search-open.2.adm
new file mode 100644
index 0000000..2aba3a1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/range-search-open/range-search-open.2.adm
@@ -0,0 +1 @@
+2978
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/range-search/range-search.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/range-search/range-search.2.adm
new file mode 100644
index 0000000..2aba3a1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/range-search/range-search.2.adm
@@ -0,0 +1 @@
+2978
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/index-selection/range-search-open/range-search-open.5.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/index-selection/range-search-open/range-search-open.5.ast
new file mode 100644
index 0000000..ecfb9f3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/index-selection/range-search-open/range-search-open.5.ast
@@ -0,0 +1,67 @@
+DataverseUse test
+Query:
+SELECT ELEMENT [
+FunctionCall asterix.sql-count@1[
+  (
+    SELECT ELEMENT [
+    LiteralExpr [LONG] [1]
+    ]
+    FROM [      Variable [ Name=#1 ]
+      AS Variable [ Name=#2 ]
+    ]
+  )
+]
+]
+FROM [  FunctionCall asterix.dataset@1[
+    LiteralExpr [STRING] [test.LineItem]
+  ]
+  AS Variable [ Name=$c ]
+]
+Where
+  OperatorExpr [
+    OperatorExpr [
+      FieldAccessor [
+        Variable [ Name=$c ]
+        Field=l_suppkey
+      ]
+      <
+      OperatorExpr [
+        LiteralExpr [LONG] [100]
+        +
+        FunctionCall test.to-bigint@1[
+          OperatorExpr [
+            FunctionCall test.random@0[
+            ]
+            /
+            LiteralExpr [LONG] [2]
+          ]
+        ]
+      ]
+    ]
+    and
+    OperatorExpr [
+      FieldAccessor [
+        Variable [ Name=$c ]
+        Field=l_suppkey
+      ]
+      >
+      OperatorExpr [
+        LiteralExpr [LONG] [5]
+        +
+        FunctionCall test.to-bigint@1[
+          OperatorExpr [
+            FunctionCall test.random@0[
+            ]
+            /
+            LiteralExpr [LONG] [2]
+          ]
+        ]
+      ]
+    ]
+  ]
+Group All
+  GROUP AS Variable [ Name=#1 ]
+  (
+    c:=Variable [ Name=$c ]
+  )
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/index-selection/range-search/range-search.5.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/index-selection/range-search/range-search.5.ast
new file mode 100644
index 0000000..ecfb9f3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/index-selection/range-search/range-search.5.ast
@@ -0,0 +1,67 @@
+DataverseUse test
+Query:
+SELECT ELEMENT [
+FunctionCall asterix.sql-count@1[
+  (
+    SELECT ELEMENT [
+    LiteralExpr [LONG] [1]
+    ]
+    FROM [      Variable [ Name=#1 ]
+      AS Variable [ Name=#2 ]
+    ]
+  )
+]
+]
+FROM [  FunctionCall asterix.dataset@1[
+    LiteralExpr [STRING] [test.LineItem]
+  ]
+  AS Variable [ Name=$c ]
+]
+Where
+  OperatorExpr [
+    OperatorExpr [
+      FieldAccessor [
+        Variable [ Name=$c ]
+        Field=l_suppkey
+      ]
+      <
+      OperatorExpr [
+        LiteralExpr [LONG] [100]
+        +
+        FunctionCall test.to-bigint@1[
+          OperatorExpr [
+            FunctionCall test.random@0[
+            ]
+            /
+            LiteralExpr [LONG] [2]
+          ]
+        ]
+      ]
+    ]
+    and
+    OperatorExpr [
+      FieldAccessor [
+        Variable [ Name=$c ]
+        Field=l_suppkey
+      ]
+      >
+      OperatorExpr [
+        LiteralExpr [LONG] [5]
+        +
+        FunctionCall test.to-bigint@1[
+          OperatorExpr [
+            FunctionCall test.random@0[
+            ]
+            /
+            LiteralExpr [LONG] [2]
+          ]
+        ]
+      ]
+    ]
+  ]
+Group All
+  GROUP AS Variable [ Name=#1 ]
+  (
+    c:=Variable [ Name=$c ]
+  )
+
diff --git a/asterixdb/src/main/licenses/templates/asterix-notice.ftl b/asterixdb/src/main/licenses/templates/asterix-notice.ftl
index 41cd8a0..c825397 100644
--- a/asterixdb/src/main/licenses/templates/asterix-notice.ftl
+++ b/asterixdb/src/main/licenses/templates/asterix-notice.ftl
@@ -22,7 +22,7 @@
 <#else>
 Apache AsterixDB
 </#if>
-Copyright 2015-2018 The Apache Software Foundation
+Copyright 2015-${.now?string('yyyy')} The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
diff --git a/hyracks-fullstack/NOTICE b/hyracks-fullstack/NOTICE
index 9ca26a7..77f31ad 100644
--- a/hyracks-fullstack/NOTICE
+++ b/hyracks-fullstack/NOTICE
@@ -1,5 +1,5 @@
 Apache Hyracks and Algebricks
-Copyright 2015-2018 The Apache Software Foundation
+Copyright 2015-2019 The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
diff --git a/hyracks-fullstack/hyracks-fullstack-license/src/main/licenses/templates/hyracks-notice.ftl b/hyracks-fullstack/hyracks-fullstack-license/src/main/licenses/templates/hyracks-notice.ftl
index 077e7bc..8a2eb05 100644
--- a/hyracks-fullstack/hyracks-fullstack-license/src/main/licenses/templates/hyracks-notice.ftl
+++ b/hyracks-fullstack/hyracks-fullstack-license/src/main/licenses/templates/hyracks-notice.ftl
@@ -18,7 +18,7 @@
 -->
 <#-- TODO(mblow): share notice file template with asterixdb via maven artifact -->
 Apache Hyracks and Algebricks
-Copyright 2015-2018 The Apache Software Foundation
+Copyright 2015-${.now?string('yyyy')} The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/ControllerConfig.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/ControllerConfig.java
index dc229e0..f7291cb 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/ControllerConfig.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/ControllerConfig.java
@@ -20,6 +20,7 @@
 
 import static org.apache.hyracks.control.common.config.OptionTypes.BOOLEAN;
 
+import java.io.File;
 import java.io.Serializable;
 import java.net.URL;
 import java.util.function.Function;
@@ -123,7 +124,9 @@
     }
 
     public String getLogDir() {
-        return configManager.getAppConfig().getString(ControllerConfig.Option.LOG_DIR);
+        String relPath = configManager.getAppConfig().getString(ControllerConfig.Option.LOG_DIR);
+        String fullPath = new File(relPath).getAbsolutePath();
+        return fullPath;
     }
 
     public boolean isSslEnabled() {
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/NCLogConfigurationFactory.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/NCLogConfigurationFactory.java
index 24c3dee..a657d6b 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/NCLogConfigurationFactory.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/NCLogConfigurationFactory.java
@@ -18,10 +18,10 @@
  */
 package org.apache.hyracks.control.nc;
 
+import java.io.File;
 import java.net.URI;
 
 import org.apache.hyracks.control.common.controllers.NCConfig;
-import org.apache.hyracks.util.file.FileUtil;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.config.Configuration;
@@ -42,7 +42,7 @@
 
     public Configuration createConfiguration(ConfigurationBuilder<BuiltConfiguration> builder) {
         String nodeId = config.getNodeId();
-        String logDir = config.getLogDir();
+        File logDir = new File(config.getLogDir());
         builder.setStatusLevel(Level.WARN);
         builder.setConfigurationName("RollingBuilder");
         // create a rolling file appender
@@ -51,24 +51,22 @@
         ComponentBuilder triggeringPolicy = builder.newComponent("Policies")
                 .addComponent(builder.newComponent("CronTriggeringPolicy").addAttribute("schedule", "0 0 0 * * ?"))
                 .addComponent(builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", "50M"));
-        AppenderComponentBuilder defaultRoll =
-                builder.newAppender("default", "RollingFile")
-                        .addAttribute("fileName", FileUtil.joinPath(logDir, "nc-" + nodeId + ".log"))
-                        .addAttribute("filePattern",
-                                FileUtil.joinPath(logDir, "nc-" + nodeId + "-%d{MM-dd-yy-ss}.log.gz"))
-                        .add(defaultLayout).addComponent(triggeringPolicy);
+        AppenderComponentBuilder defaultRoll = builder.newAppender("default", "RollingFile")
+                .addAttribute("fileName", new File(logDir, "nc-" + nodeId + ".log").getAbsolutePath())
+                .addAttribute("filePattern",
+                        new File(logDir, "nc-" + nodeId + "-%d{MM-dd-yy-ss}.log.gz").getAbsolutePath())
+                .add(defaultLayout).addComponent(triggeringPolicy);
         builder.add(defaultRoll);
 
         // create the new logger
         builder.add(builder.newRootLogger(Level.INFO).add(builder.newAppenderRef("default")));
 
         LayoutComponentBuilder accessLayout = builder.newLayout("PatternLayout").addAttribute("pattern", "%m%n");
-        AppenderComponentBuilder accessRoll =
-                builder.newAppender("access", "RollingFile")
-                        .addAttribute("fileName", FileUtil.joinPath(logDir, "access-" + nodeId + ".log"))
-                        .addAttribute("filePattern",
-                                FileUtil.joinPath(logDir, "access-" + nodeId + "-%d{MM-dd-yy-ss}.log.gz"))
-                        .add(accessLayout).addComponent(triggeringPolicy);
+        AppenderComponentBuilder accessRoll = builder.newAppender("access", "RollingFile")
+                .addAttribute("fileName", new File(logDir, "access-" + nodeId + ".log").getAbsolutePath())
+                .addAttribute("filePattern",
+                        new File(logDir, "access-" + nodeId + "-%d{MM-dd-yy-ss}.log.gz").getAbsolutePath())
+                .add(accessLayout).addComponent(triggeringPolicy);
         builder.add(accessRoll);
         builder.add(builder.newLogger("org.apache.hyracks.http.server.CLFLogger", Level.forName("ACCESS", 550))
                 .add(builder.newAppenderRef("access")).addAttribute("additivity", false));
diff --git a/hyracks-fullstack/hyracks/hyracks-server/pom.xml b/hyracks-fullstack/hyracks/hyracks-server/pom.xml
index 6c5639f..7e566d6 100644
--- a/hyracks-fullstack/hyracks/hyracks-server/pom.xml
+++ b/hyracks-fullstack/hyracks/hyracks-server/pom.xml
@@ -185,5 +185,9 @@
       <groupId>org.apache.logging.log4j</groupId>
       <artifactId>log4j-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+    </dependency>
   </dependencies>
 </project>
diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/NCServiceIT.java b/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/NCServiceIT.java
index 4983ba1..544d414 100644
--- a/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/NCServiceIT.java
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/NCServiceIT.java
@@ -19,16 +19,24 @@
 package org.apache.hyracks.test.server;
 
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.net.InetAddress;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
 import java.util.Iterator;
 
+import org.apache.commons.io.FileUtils;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpStatus;
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.util.EntityUtils;
+import org.apache.hyracks.test.server.process.HyracksCCProcess;
+import org.apache.hyracks.test.server.process.HyracksNCServiceProcess;
 import org.apache.hyracks.test.server.process.HyracksVirtualCluster;
 import org.apache.hyracks.util.file.FileUtil;
 import org.apache.logging.log4j.LogManager;
@@ -55,8 +63,13 @@
     @BeforeClass
     public static void setUp() throws Exception {
         cluster = new HyracksVirtualCluster(new File(APP_HOME), null);
-        cluster.addNCService(new File(RESOURCE_DIR, "nc-red.conf"), new File(LOG_DIR, "nc-red.log"));
-        cluster.addNCService(new File(RESOURCE_DIR, "nc-blue.conf"), new File(LOG_DIR, "nc-blue.log"));
+        File tempConf = new File(TARGET_DIR, "cc.conf");
+        FileUtils.copyFile(new File(RESOURCE_DIR, "cc.conf"), tempConf);
+        Files.write(tempConf.toPath(), ("log.dir: " + LOG_DIR).getBytes(), StandardOpenOption.APPEND);
+        File log4jPath = new File(FileUtil.joinPath("..", "..", "src", "test", "resources", "log4j2-hyracks-test.xml"));
+
+        cluster.addNCService(new File(RESOURCE_DIR, "nc-red.conf"), new File(LOG_DIR, "nc-red.log"), log4jPath);
+        cluster.addNCService(new File(RESOURCE_DIR, "nc-blue.conf"), new File(LOG_DIR, "nc-blue.log"), log4jPath);
 
         try {
             Thread.sleep(2000);
@@ -64,7 +77,7 @@
         }
 
         // Start CC
-        cluster.start(new File(RESOURCE_DIR, "cc.conf"), new File(LOG_DIR, "cc.log"));
+        cluster.start(tempConf, new File(LOG_DIR, "cc.log"), log4jPath);
 
         try {
             Thread.sleep(10000);
diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/process/HyracksCCProcess.java b/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/process/HyracksCCProcess.java
index b39def5..2ac6f2b 100644
--- a/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/process/HyracksCCProcess.java
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/process/HyracksCCProcess.java
@@ -32,6 +32,11 @@
         this.workingDir = workingDir;
     }
 
+    public HyracksCCProcess(File configFile, File logFile, File appHome, File workingDir, File log4jPath) {
+        this(configFile, logFile, appHome, workingDir);
+        args.add("-Dlog4j.configurationFile=file://" + log4jPath.getAbsolutePath());
+    }
+
     @Override
     protected String getMainClassName() {
         return CCDriver.class.getName();
@@ -41,7 +46,8 @@
     @SuppressWarnings("squid:CommentedOutCodeLine")
     protected void addJvmArgs(List<String> cList) {
         // CC needs more than default memory
-        cList.add("-Xmx1024m");
+        args.add("-Xmx1024m");
+        cList.addAll(args);
         // cList.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005");
     }
 }
diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/process/HyracksNCServiceProcess.java b/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/process/HyracksNCServiceProcess.java
index 67fea0f..d0e0244 100644
--- a/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/process/HyracksNCServiceProcess.java
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/process/HyracksNCServiceProcess.java
@@ -32,6 +32,11 @@
         this.workingDir = workingDir;
     }
 
+    public HyracksNCServiceProcess(File configFile, File logFile, File appHome, File workingDir, File log4jPath) {
+        this(configFile, logFile, appHome, workingDir);
+        args.add("-Dlog4j.configurationFile=file://" + log4jPath.getAbsolutePath());
+    }
+
     @Override
     protected String getMainClassName() {
         return NCService.class.getName();
@@ -40,6 +45,8 @@
     @Override
     protected void addJvmArgs(List<String> cList) {
         // NCService needs little memory
-        cList.add("-Xmx128m");
+        args.add("-Xmx128m");
+        cList.addAll(args);
     }
+
 }
diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/process/HyracksServerProcess.java b/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/process/HyracksServerProcess.java
index d157c6f..a8c363b 100644
--- a/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/process/HyracksServerProcess.java
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/process/HyracksServerProcess.java
@@ -37,6 +37,7 @@
     protected File logFile = null;
     protected File appHome = null;
     protected File workingDir = null;
+    protected List<String> args = new ArrayList<>();
 
     public void start() throws IOException {
         String[] cmd = buildCommand();
@@ -114,6 +115,10 @@
     protected void addCmdLineArgs(List<String> cList) {
     }
 
+    public void addArg(String arg) {
+        args.add(arg);
+    }
+
     protected abstract String getMainClassName();
 
     private final String getClasspath() {
diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/process/HyracksVirtualCluster.java b/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/process/HyracksVirtualCluster.java
index fc7bf73..6c77628 100644
--- a/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/process/HyracksVirtualCluster.java
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/test/server/process/HyracksVirtualCluster.java
@@ -35,11 +35,14 @@
 
     /**
      * Construct a Hyracks-based cluster.
-     * @param appHome - path to the installation root of the Hyracks application.
-     *                At least bin/hyracksnc (or the equivalent NC script for
-     *                the application) must exist in this directory.
-     * @param workingDir - directory to use as CWD for all child processes. May
-     *                be null, in which case the CWD of the invoking process is used.
+     *
+     * @param appHome
+     *            - path to the installation root of the Hyracks application.
+     *            At least bin/hyracksnc (or the equivalent NC script for
+     *            the application) must exist in this directory.
+     * @param workingDir
+     *            - directory to use as CWD for all child processes. May
+     *            be null, in which case the CWD of the invoking process is used.
      */
     public HyracksVirtualCluster(File appHome, File workingDir) {
         this.appHome = appHome;
@@ -48,8 +51,11 @@
 
     /**
      * Creates and starts an NCService.
-     * @param configFile - full path to an ncservice.conf. May be null to accept all defaults.
-     * @throws IOException - if there are errors starting the process.
+     *
+     * @param configFile
+     *            - full path to an ncservice.conf. May be null to accept all defaults.
+     * @throws IOException
+     *             - if there are errors starting the process.
      */
     public HyracksNCServiceProcess addNCService(File configFile, File logFile) throws IOException {
         HyracksNCServiceProcess proc = new HyracksNCServiceProcess(configFile, logFile, appHome, workingDir);
@@ -59,11 +65,30 @@
     }
 
     /**
+     * Creates and starts an NCService.
+     *
+     * @param configFile
+     *            - full path to an ncservice.conf. May be null to accept all defaults.
+     * @throws IOException
+     *             - if there are errors starting the process.
+     */
+    public HyracksNCServiceProcess addNCService(File configFile, File logFile, File log4jConfig) throws IOException {
+        HyracksNCServiceProcess proc =
+                new HyracksNCServiceProcess(configFile, logFile, appHome, workingDir, log4jConfig);
+        proc.start();
+        ncProcs.add(proc);
+        return proc;
+    }
+
+    /**
      * Starts the CC, initializing the cluster. Expects that any NCs referenced
      * in the cluster configuration have already been started with addNCService().
-     * @param ccConfigFile - full path to a cluster conf file. May be null to accept all
-     *                     defaults, although this is seldom useful since there are no NCs.
-     * @throws IOException - if there are errors starting the process.
+     *
+     * @param ccConfigFile
+     *            - full path to a cluster conf file. May be null to accept all
+     *            defaults, although this is seldom useful since there are no NCs.
+     * @throws IOException
+     *             - if there are errors starting the process.
      */
     public HyracksCCProcess start(File ccConfigFile, File logFile) throws IOException {
         ccProc = new HyracksCCProcess(ccConfigFile, logFile, appHome, workingDir);
@@ -72,6 +97,22 @@
     }
 
     /**
+     * Starts the CC, initializing the cluster. Expects that any NCs referenced
+     * in the cluster configuration have already been started with addNCService().
+     *
+     * @param ccConfigFile
+     *            - full path to a cluster conf file. May be null to accept all
+     *            defaults, although this is seldom useful since there are no NCs.
+     * @throws IOException
+     *             - if there are errors starting the process.
+     */
+    public HyracksCCProcess start(File ccConfigFile, File logFile, File log4jConfig) throws IOException {
+        ccProc = new HyracksCCProcess(ccConfigFile, logFile, appHome, workingDir, log4jConfig);
+        ccProc.start();
+        return ccProc;
+    }
+
+    /**
      * Stops all processes in the cluster.
      * QQQ Someday this should probably do a graceful stop of NCs rather than
      * killing the NCService.
diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/NCServiceIT/cc.conf b/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/NCServiceIT/cc.conf
index 419b8ca..9c80c7d 100644
--- a/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/NCServiceIT/cc.conf
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/NCServiceIT/cc.conf
@@ -29,3 +29,4 @@
 address = 127.0.0.1
 console.listen.port = 12345
 
+[common]
diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/NCServiceIT/nc-blue.conf b/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/NCServiceIT/nc-blue.conf
index 9ddf4df..6eb38dd 100644
--- a/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/NCServiceIT/nc-blue.conf
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/NCServiceIT/nc-blue.conf
@@ -18,5 +18,3 @@
 [ncservice]
 address=127.0.0.1
 port=9091
-logdir=-
-
diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/NCServiceIT/nc-red.conf b/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/NCServiceIT/nc-red.conf
index f854479..286bd32 100644
--- a/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/NCServiceIT/nc-red.conf
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/NCServiceIT/nc-red.conf
@@ -18,5 +18,3 @@
 [ncservice]
 address=127.0.0.1
 port=9090
-logdir=-
-
diff --git a/hyracks-fullstack/pom.xml b/hyracks-fullstack/pom.xml
index 1fe1784..d3746ee 100644
--- a/hyracks-fullstack/pom.xml
+++ b/hyracks-fullstack/pom.xml
@@ -46,6 +46,7 @@
     <tag>HEAD</tag>
   </scm>
   <properties>
+    <root.dir>${basedir}</root.dir>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <file.encoding>UTF-8</file.encoding>
     <source.jdk.version>1.8</source.jdk.version>
@@ -54,6 +55,7 @@
     <sonar.jacoco.reportPath>${env.PWD}/target/jacoco-merged.exec</sonar.jacoco.reportPath>
     <source-format.goal>format</source-format.goal>
     <source-format.skip>false</source-format.skip>
+    <maven.test.skip>false</maven.test.skip>
     <skipTests>false</skipTests>
     <skip.surefire.tests>${skipTests}</skip.surefire.tests>
     <skip.testResources>${maven.test.skip}</skip.testResources>