Overhaul of Hyracks configuration management.
Includes Asterix changes to make use of new conf management as a
Hyracks application.
Change-Id: Ie3027c8c839f25ea858790bd3340187f4b11f212
Reviewed-on: https://asterix-gerrit.ics.uci.edu/336
Tested-by: Chris Hillery <ceej@lambda.nu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Michael Blow <michael.blow@couchbase.com>
Reviewed-by: Ian Maxon <imaxon@apache.org>
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/application/IApplicationConfig.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/application/IApplicationConfig.java
new file mode 100644
index 0000000..bd40813
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/application/IApplicationConfig.java
@@ -0,0 +1,35 @@
+/*
+ * 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.api.application;
+
+import java.util.Set;
+
+/**
+ * Accessor for the data contained in the global application configuration file.
+ */
+public interface IApplicationConfig {
+ String getString(String section, String key);
+ String getString(String section, String key, String defaultValue);
+ int getInt(String section, String key);
+ int getInt(String section, String key, int defaultValue);
+ long getLong(String section, String key);
+ long getLong(String section, String key, long defaultValue);
+ Set<String> getSections();
+ Set<String> getKeys(String section);
+}
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/application/IApplicationContext.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/application/IApplicationContext.java
index 6c79abd..7b07174 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/application/IApplicationContext.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/application/IApplicationContext.java
@@ -49,6 +49,8 @@
public void setThreadFactory(ThreadFactory threadFactory);
+ public IApplicationConfig getAppConfig();
+
/**
* @return The controller service which the application context belongs to.
*/
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/CCDriver.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/CCDriver.java
index d2e7cb5..786a89f 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/CCDriver.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/CCDriver.java
@@ -34,6 +34,8 @@
cp.printUsage(System.err);
return;
}
+ ccConfig.loadConfigAndApplyDefaults();
+
ClusterControllerService ccService = new ClusterControllerService(ccConfig);
ccService.start();
while (true) {
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 e8b2c27..8fb83d1 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
@@ -34,7 +34,10 @@
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
-
+import org.apache.hyracks.control.cc.work.TriggerNCWork;
+import org.apache.hyracks.control.common.controllers.IniUtils;
+import org.ini4j.Ini;
+import org.xml.sax.InputSource;
import org.apache.hyracks.api.application.ICCApplicationEntryPoint;
import org.apache.hyracks.api.client.ClusterControllerInfo;
import org.apache.hyracks.api.client.HyracksClientInterfaceFunctions;
@@ -244,6 +247,7 @@
datasetDirectoryService.init(executor);
workQueue.start();
+ connectNCs();
LOGGER.log(Level.INFO, "Started ClusterControllerService");
if (aep != null) {
// Sometimes, there is no application entry point. Check hyracks-client project
@@ -252,7 +256,7 @@
}
private void startApplication() throws Exception {
- appCtx = new CCApplicationContext(this, serverCtx, ccContext);
+ appCtx = new CCApplicationContext(this, serverCtx, ccContext, ccConfig.getAppConfig());
appCtx.addJobLifecycleListener(datasetDirectoryService);
String className = ccConfig.appCCMainClass;
if (className != null) {
@@ -265,6 +269,25 @@
executor = Executors.newCachedThreadPool(appCtx.getThreadFactory());
}
+ private void connectNCs() throws Exception {
+ Ini ini = ccConfig.getIni();
+ if (ini == null) {
+ return;
+ }
+ for (String section : ini.keySet()) {
+ if (!section.startsWith("nc/")) {
+ continue;
+ }
+ String ncid = section.substring(3);
+ String address = ini.get(section, "address");
+ int port = IniUtils.getInt(ini, section, "port", 9090);
+ if (address == null) {
+ address = InetAddress.getLoopbackAddress().getHostAddress();
+ }
+ workQueue.schedule(new TriggerNCWork(this, address, port, ncid));
+ }
+ }
+
@Override
public void stop() throws Exception {
LOGGER.log(Level.INFO, "Stopping ClusterControllerService");
@@ -317,7 +340,6 @@
public Map<String, NodeControllerState> getNodeMap() {
return nodeRegistry;
}
-
public CCConfig getConfig() {
return ccConfig;
}
@@ -614,7 +636,7 @@
* Add a deployment run
*
* @param deploymentKey
- * @param nodeControllerIds
+ * @param dRun
*/
public synchronized void addDeploymentRun(DeploymentId deploymentKey, DeploymentRun dRun) {
deploymentRunMap.put(deploymentKey, dRun);
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/application/CCApplicationContext.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/application/CCApplicationContext.java
index 61505eb..7917e4a 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/application/CCApplicationContext.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/application/CCApplicationContext.java
@@ -26,6 +26,7 @@
import java.util.Map;
import java.util.Set;
+import org.apache.hyracks.api.application.IApplicationConfig;
import org.apache.hyracks.api.application.ICCApplicationContext;
import org.apache.hyracks.api.application.IClusterLifecycleListener;
import org.apache.hyracks.api.context.ICCContext;
@@ -52,9 +53,9 @@
private List<IClusterLifecycleListener> clusterLifecycleListeners;
private final ClusterControllerService ccs;
- public CCApplicationContext(ClusterControllerService ccs, ServerContext serverCtx, ICCContext ccContext)
- throws IOException {
- super(serverCtx);
+ public CCApplicationContext(ClusterControllerService ccs, ServerContext serverCtx, ICCContext ccContext,
+ IApplicationConfig appConfig) throws IOException {
+ super(serverCtx, appConfig);
this.ccContext = ccContext;
this.ccs = ccs;
initPendingNodeIds = new HashSet<String>();
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/web/WebServer.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/web/WebServer.java
index 71ae37d..ac98b646 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/web/WebServer.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/web/WebServer.java
@@ -18,11 +18,10 @@
*/
package org.apache.hyracks.control.cc.web;
-import java.util.EnumSet;
-import java.util.logging.Logger;
-
-import javax.servlet.DispatcherType;
-
+import org.apache.hyracks.control.cc.ClusterControllerService;
+import org.apache.hyracks.control.cc.adminconsole.HyracksAdminConsoleApplication;
+import org.apache.hyracks.control.cc.web.util.JSONOutputRequestHandler;
+import org.apache.hyracks.control.cc.web.util.RoutingHandler;
import org.apache.wicket.Application;
import org.apache.wicket.RuntimeConfigurationType;
import org.apache.wicket.protocol.http.ContextParamWebApplicationFactory;
@@ -38,10 +37,9 @@
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.apache.hyracks.control.cc.ClusterControllerService;
-import org.apache.hyracks.control.cc.adminconsole.HyracksAdminConsoleApplication;
-import org.apache.hyracks.control.cc.web.util.JSONOutputRequestHandler;
-import org.apache.hyracks.control.cc.web.util.RoutingHandler;
+import javax.servlet.DispatcherType;
+import java.util.EnumSet;
+import java.util.logging.Logger;
public class WebServer {
private final static Logger LOGGER = Logger.getLogger(WebServer.class.getName());
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/RemoveDeadNodesWork.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/RemoveDeadNodesWork.java
index dca6bc2..b3a3065 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/RemoveDeadNodesWork.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/RemoveDeadNodesWork.java
@@ -45,7 +45,7 @@
Map<String, NodeControllerState> nodeMap = ccs.getNodeMap();
for (Map.Entry<String, NodeControllerState> e : nodeMap.entrySet()) {
NodeControllerState state = e.getValue();
- if (state.incrementLastHeartbeatDuration() >= ccs.getConfig().maxHeartbeatLapsePeriods) {
+ if (state.incrementLastHeartbeatDuration() >= ccs.getCCConfig().maxHeartbeatLapsePeriods) {
deadNodes.add(e.getKey());
LOGGER.info(e.getKey() + " considered dead");
}
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/TriggerNCWork.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/TriggerNCWork.java
new file mode 100644
index 0000000..75d4dd2
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/TriggerNCWork.java
@@ -0,0 +1,122 @@
+/*
+ * 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.control.cc.work;
+
+import org.apache.hyracks.control.cc.ClusterControllerService;
+import org.apache.hyracks.control.common.work.AbstractWork;
+import org.ini4j.Ini;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.StringWriter;
+import java.net.Socket;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A work which is run at CC startup for each NC specified in the configuration file.
+ * It contacts the NC service on each node and passes in the NC-specific configuration.
+ */
+public class TriggerNCWork extends AbstractWork {
+
+ // This constant must match the corresponding constant in
+ // hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCService.java
+ // I didn't want to introduce a Maven-level dependency on the
+ // hyracks-nc-service package (or vice-versa).
+ public static final String NC_MAGIC_COOKIE = "hyncmagic";
+ private static final Logger LOGGER = Logger.getLogger(TriggerNCWork.class.getName());
+
+ private final ClusterControllerService ccs;
+ private final String ncHost;
+ private final int ncPort;
+ private final String ncId;
+
+ public TriggerNCWork(ClusterControllerService ccs, String ncHost, int ncPort, String ncId) {
+ this.ccs = ccs;
+ this.ncHost = ncHost;
+ this.ncPort = ncPort;
+ this.ncId = ncId;
+ }
+ @Override
+ public final void run() {
+ ccs.getExecutor().execute(new Runnable() {
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.info("Connecting NC service '" + ncId + "' at " + ncHost + ":" + ncPort);
+ }
+ Socket s = new Socket(ncHost, ncPort);
+ ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
+ oos.writeUTF(NC_MAGIC_COOKIE);
+ oos.writeUTF(serializeIni(ccs.getCCConfig().getIni()));
+ oos.close();
+ break;
+ // QQQ Should probably have an ACK here
+ } catch (IOException e) {
+ if (LOGGER.isLoggable(Level.WARNING)) {
+ LOGGER.log(Level.WARNING, "Failed to contact NC service at " + ncHost +
+ ":" + ncPort + "; will retry", e);
+ }
+ }
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Utility routine to copy all keys from a named section in Ini a
+ * to a named section in Ini b. We need to do this the hard way
+ * because Ini4j reacts inscrutably when attempting to copy
+ * Ini.Sections directly from one Ini to another.
+ */
+ private void copyIniSection(Ini a, String asect, Ini b, String bsect) {
+ Ini.Section source = a.get(asect);
+ for (String key : source.keySet()) {
+ b.put(bsect, key, source.get(key));
+ }
+ }
+ /**
+ * Given an Ini object, serialize it to String with some enhancements.
+ * @param ccini
+ */
+ String serializeIni(Ini ccini) throws IOException {
+ Ini ini = new Ini();
+ String ncsection = "nc/" + ncId;
+ for (String section : ccini.keySet()) {
+ if (section.equals(ncsection)) {
+ copyIniSection(ccini, ncsection, ini, "localnc");
+ ini.put("localnc", "id", ncId);
+ }
+ copyIniSection(ccini, section, ini, section);
+ }
+ StringWriter iniString = new StringWriter();
+ ini.store(iniString);
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.fine("Returning Ini file:\n" + iniString.toString());
+ }
+ return iniString.toString();
+ }
+}
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/pom.xml b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/pom.xml
index 912e447..d885b38 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/pom.xml
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/pom.xml
@@ -44,5 +44,10 @@
<type>jar</type>
<scope>compile</scope>
</dependency>
+ <dependency>
+ <groupId>org.ini4j</groupId>
+ <artifactId>ini4j</artifactId>
+ <version>0.5.4</version>
+ </dependency>
</dependencies>
</project>
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/application/ApplicationContext.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/application/ApplicationContext.java
index c4e9ea2..23d287c 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/application/ApplicationContext.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/application/ApplicationContext.java
@@ -22,6 +22,7 @@
import java.io.Serializable;
import java.util.concurrent.ThreadFactory;
+import org.apache.hyracks.api.application.IApplicationConfig;
import org.apache.hyracks.api.application.IApplicationContext;
import org.apache.hyracks.api.job.IJobSerializerDeserializerContainer;
import org.apache.hyracks.api.job.JobSerializerDeserializerContainer;
@@ -32,6 +33,7 @@
protected ServerContext serverCtx;
protected Serializable distributedState;
protected IMessageBroker messageBroker;
+ protected final IApplicationConfig appConfig;
protected IJobSerializerDeserializerContainer jobSerDeContainer = new JobSerializerDeserializerContainer();
protected ThreadFactory threadFactory = new ThreadFactory() {
public Thread newThread(Runnable r) {
@@ -41,8 +43,9 @@
}
};
- public ApplicationContext(ServerContext serverCtx) throws IOException {
+ public ApplicationContext(ServerContext serverCtx, IApplicationConfig appConfig) {
this.serverCtx = serverCtx;
+ this.appConfig = appConfig;
}
@Override
@@ -74,4 +77,9 @@
public void setThreadFactory(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
}
-}
+
+ @Override
+ public IApplicationConfig getAppConfig() {
+ return appConfig;
+ }
+}
\ No newline at end of file
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/application/IniApplicationConfig.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/application/IniApplicationConfig.java
new file mode 100644
index 0000000..3a8a2de
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/application/IniApplicationConfig.java
@@ -0,0 +1,84 @@
+/*
+ * 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.control.common.application;
+
+import org.apache.hyracks.api.application.IApplicationConfig;
+import org.ini4j.Ini;
+
+import java.util.Set;
+
+/**
+ * An implementation of IApplicationConfig which is backed by Ini4j.
+ */
+public class IniApplicationConfig implements IApplicationConfig {
+ private final Ini ini;
+
+ public IniApplicationConfig(Ini ini) {
+ if (ini != null) {
+ this.ini = ini;
+ } else {
+ this.ini = new Ini();
+ }
+ }
+
+ private <T> T getIniValue(String section, String key, T default_value, Class<T> clazz) {
+ T value = ini.get(section, key, clazz);
+ return (value != null) ? value : default_value;
+ }
+
+ @Override
+ public String getString(String section, String key) {
+ return getIniValue(section, key, null, String.class);
+ }
+
+ @Override
+ public String getString(String section, String key, String defaultValue) {
+ return getIniValue(section, key, defaultValue, String.class);
+ }
+
+ @Override
+ public int getInt(String section, String key) {
+ return getIniValue(section, key, 0, Integer.class);
+ }
+
+ @Override
+ public int getInt(String section, String key, int defaultValue) {
+ return getIniValue(section, key, defaultValue, Integer.class);
+ }
+
+ @Override
+ public long getLong(String section, String key) {
+ return getIniValue(section, key, (long) 0, Long.class);
+ }
+
+ @Override
+ public long getLong(String section, String key, long defaultValue) {
+ return getIniValue(section, key, defaultValue, Long.class);
+ }
+
+ @Override
+ public Set<String> getSections() {
+ return ini.keySet();
+ }
+
+ @Override
+ public Set<String> getKeys(String section) {
+ return ini.get(section).keySet();
+ }
+}
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 4e7c394..a04d750 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
@@ -19,20 +19,31 @@
package org.apache.hyracks.control.common.controllers;
import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
import java.util.List;
+import org.apache.hyracks.api.application.IApplicationConfig;
+import org.apache.hyracks.control.common.application.IniApplicationConfig;
+import org.ini4j.Ini;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
import org.kohsuke.args4j.spi.StopOptionHandler;
public class CCConfig {
- @Option(name = "-client-net-ip-address", usage = "Sets the IP Address to listen for connections from clients", required = true)
+ @Option(name = "-address", usage = "IP Address for CC (default: localhost)", required = false)
+ public String ipAddress = InetAddress.getLoopbackAddress().getHostAddress();
+
+ @Option(name = "-client-net-ip-address", usage = "Sets the IP Address to listen for connections from clients (default: same as -address)", required = false)
public String clientNetIpAddress;
@Option(name = "-client-net-port", usage = "Sets the port to listen for connections from clients (default 1098)")
public int clientNetPort = 1098;
- @Option(name = "-cluster-net-ip-address", usage = "Sets the IP Address to listen for connections from", required = true)
+ // QQQ Note that clusterNetIpAddress is *not directly used* yet. Both
+ // the cluster listener and the web server listen on "all interfaces".
+ // This IP address is only used to instruct the NC on which IP to call in.
+ @Option(name = "-cluster-net-ip-address", usage = "Sets the IP Address to listen for connections from NCs (default: same as -address)", required = false)
public String clusterNetIpAddress;
@Option(name = "-cluster-net-port", usage = "Sets the port to listen for connections from node controllers (default 1099)")
@@ -71,10 +82,77 @@
@Option(name = "-app-cc-main-class", required = false, usage = "Application CC Main Class")
public String appCCMainClass = null;
+ @Option(name = "-config-file", usage = "Specify path to master configuration file (default: none)", required = false)
+ public String configFile = null;
+
@Argument
@Option(name = "--", handler = StopOptionHandler.class)
public List<String> appArgs;
+ 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);
+
+ ipAddress = IniUtils.getString(ini, "cc", "address", ipAddress);
+ clientNetIpAddress = IniUtils.getString(ini, "cc", "client.address", clientNetIpAddress);
+ clientNetPort = IniUtils.getInt(ini, "cc", "client.port", clientNetPort);
+ clusterNetIpAddress = IniUtils.getString(ini, "cc", "cluster.address", clusterNetIpAddress);
+ clusterNetPort = IniUtils.getInt(ini, "cc", "cluster.port", clusterNetPort);
+ httpPort = IniUtils.getInt(ini, "cc", "http.port", httpPort);
+ heartbeatPeriod = IniUtils.getInt(ini, "cc", "heartbeat.period", heartbeatPeriod);
+ maxHeartbeatLapsePeriods = IniUtils.getInt(ini, "cc", "heartbeat.maxlapse", maxHeartbeatLapsePeriods);
+ profileDumpPeriod = IniUtils.getInt(ini, "cc", "profiledump.period", profileDumpPeriod);
+ defaultMaxJobAttempts = IniUtils.getInt(ini, "cc", "job.defaultattempts", defaultMaxJobAttempts);
+ jobHistorySize = IniUtils.getInt(ini, "cc", "job.historysize", jobHistorySize);
+ resultTTL = IniUtils.getLong(ini, "cc", "results.ttl", resultTTL);
+ resultSweepThreshold = IniUtils.getLong(ini, "cc", "results.sweepthreshold", resultSweepThreshold);
+ ccRoot = IniUtils.getString(ini, "cc", "rootfolder", ccRoot);
+ // QQQ clusterTopologyDefinition is a "File"; should support verifying that the file
+ // exists, as @Option likely does
+ appCCMainClass = IniUtils.getString(ini, "cc", "app.class", appCCMainClass);
+ }
+
+ /**
+ * Once all @Option fields have been loaded from command-line or otherwise
+ * specified programmatically, call this method to:
+ * 1. Load options from a config file (as specified by -config-file)
+ * 2. Set default values for certain derived values, such as setting
+ * clusterNetIpAddress to ipAddress
+ */
+ public void loadConfigAndApplyDefaults() throws IOException {
+ if (configFile != null) {
+ loadINIFile();
+ // 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);
+ ini.add("cc", "client.address", clientNetIpAddress);
+ }
+
+ // "address" is the default for all IP addresses
+ if (clusterNetIpAddress == null) clusterNetIpAddress = ipAddress;
+ if (clientNetIpAddress == null) clientNetIpAddress = ipAddress;
+ }
+
+ /**
+ * Returns the global config Ini file. Note: this will be null
+ * if -config-file wasn't specified.
+ */
+ public Ini getIni() {
+ return ini;
+ }
+
+ /**
+ * @return An IApplicationConfig representing this NCConfig.
+ * Note: Currently this only includes the values from the configuration
+ * file, not anything specified on the command-line. QQQ
+ */
+ public IApplicationConfig getAppConfig() {
+ return new IniApplicationConfig(ini);
+ }
+
public void toCommandLine(List<String> cList) {
cList.add("-client-net-ip-address");
cList.add(clientNetIpAddress);
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
new file mode 100644
index 0000000..9a5c9a0
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/IniUtils.java
@@ -0,0 +1,55 @@
+/*
+ * 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.control.common.controllers;
+
+import org.ini4j.Ini;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * Some utility functions for reading Ini4j objects with default values.
+ */
+public class IniUtils {
+ public static String getString(Ini ini, String section, String key, String defaultValue) {
+ String value = ini.get(section, key, String.class);
+ return (value != null) ? value : defaultValue;
+ }
+
+ public static int getInt(Ini ini, String section, String key, int defaultValue) {
+ Integer value = ini.get(section, key, Integer.class);
+ return (value != null) ? value : defaultValue;
+ }
+
+ public static long getLong(Ini ini, String section, String key, long defaultValue) {
+ Long value = ini.get(section, key, Long.class);
+ return (value != null) ? value : defaultValue;
+ }
+
+ public static Ini loadINIFile(String configFile) throws IOException {
+ Ini ini = new Ini();
+ File conffile = new File(configFile);
+ if (!conffile.exists()) {
+ throw new FileNotFoundException(configFile);
+ }
+ ini.load(conffile);
+ 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 547ffe7..4240e3a 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
@@ -18,24 +18,32 @@
*/
package org.apache.hyracks.control.common.controllers;
-import java.io.Serializable;
-import java.util.List;
-import java.util.Map;
-
+import org.apache.hyracks.api.application.IApplicationConfig;
+import org.apache.hyracks.control.common.application.IniApplicationConfig;
+import org.ini4j.Ini;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
import org.kohsuke.args4j.spi.StopOptionHandler;
-public class NCConfig implements Serializable {
- private static final long serialVersionUID = 1L;
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.InetAddress;
+import java.util.List;
+import java.util.Map;
- @Option(name = "-cc-host", usage = "Cluster Controller host name", required = true)
- public String ccHost;
+public class NCConfig implements Serializable {
+ private static final long serialVersionUID = 2L;
+
+ @Option(name = "-cc-host", usage = "Cluster Controller host name (required unless specified in config file)", required = false)
+ public String ccHost = null;
@Option(name = "-cc-port", usage = "Cluster Controller port (default: 1099)", required = false)
public int ccPort = 1099;
- @Option(name = "-cluster-net-ip-address", usage = "IP Address to bind cluster listener", required = true)
+ @Option(name = "-address", usage = "IP Address for NC (default: localhost)", required = false)
+ public String ipAddress = InetAddress.getLoopbackAddress().getHostAddress();
+
+ @Option(name = "-cluster-net-ip-address", usage = "IP Address to bind cluster listener (default: same as -address)", required = false)
public String clusterNetIPAddress;
@Option(name = "-cluster-net-port", usage = "IP port to bind cluster listener (default: random port)", required = false)
@@ -47,10 +55,10 @@
@Option(name = "-cluster-net-public-port", usage = "Public IP port to announce cluster listener (default: same as -cluster-net-port; must set -cluser-net-public-ip-address also)", required = false)
public int clusterNetPublicPort = 0;
- @Option(name = "-node-id", usage = "Logical name of node controller unique within the cluster", required = true)
- public String nodeId;
+ @Option(name = "-node-id", usage = "Logical name of node controller unique within the cluster (required unless specified in config file)", required = false)
+ public String nodeId = null;
- @Option(name = "-data-ip-address", usage = "IP Address to bind data listener", required = true)
+ @Option(name = "-data-ip-address", usage = "IP Address to bind data listener (default: same as -address)", required = false)
public String dataIPAddress;
@Option(name = "-data-port", usage = "IP port to bind data listener (default: random port)", required = false)
@@ -62,7 +70,7 @@
@Option(name = "-data-public-port", usage = "Public IP port to announce data listener (default: same as -data-port; must set -data-public-ip-address also)", required = false)
public int dataPublicPort = 0;
- @Option(name = "-result-ip-address", usage = "IP Address to bind dataset result distribution listener", required = true)
+ @Option(name = "-result-ip-address", usage = "IP Address to bind dataset result distribution listener (default: same as -address)", required = false)
public String resultIPAddress;
@Option(name = "-result-port", usage = "IP port to bind dataset result distribution listener (default: random port)", required = false)
@@ -74,6 +82,9 @@
@Option(name = "-result-public-port", usage = "Public IP port to announce dataset result distribution listener (default: same as -result-port; must set -result-public-ip-address also)", required = false)
public int resultPublicPort = 0;
+ @Option(name = "-retries", usage ="Number of attempts to contact CC before giving up (default = 5)")
+ public int retries = 5;
+
@Option(name = "-iodevices", usage = "Comma separated list of IO Device mount points (default: One device in default temp folder)", required = false)
public String ioDevices = System.getProperty("java.io.tmpdir");
@@ -98,10 +109,85 @@
@Option(name = "-app-nc-main-class", usage = "Application NC Main Class")
public String appNCMainClass;
+ @Option(name = "-config-file", usage = "Specify path to local configuration file (default: no local config)", required = false)
+ public String configFile = null;
+
@Argument
@Option(name = "--", handler = StopOptionHandler.class)
public List<String> appArgs;
+ private transient Ini ini = null;
+
+ private void loadINIFile() throws IOException {
+ ini = IniUtils.loadINIFile(configFile);
+ // 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);
+ ccPort = IniUtils.getInt(ini, "cc", "cluster.port", ccPort);
+ nodeId = IniUtils.getString(ini, "localnc", "id", nodeId);
+
+ // Network ports
+
+ ipAddress = IniUtils.getString(ini, "localnc", "address", ipAddress);
+
+ clusterNetIPAddress = IniUtils.getString(ini, "localnc", "cluster.address", clusterNetIPAddress);
+ clusterNetPort = IniUtils.getInt(ini, "localnc", "cluster.port", clusterNetPort);
+ dataIPAddress = IniUtils.getString(ini, "localnc", "data.address", dataIPAddress);
+ dataPort = IniUtils.getInt(ini, "localnc", "data.port", dataPort);
+ resultIPAddress = IniUtils.getString(ini, "localnc", "result.address", resultIPAddress);
+ resultPort = IniUtils.getInt(ini, "localnc", "result.port", resultPort);
+
+ clusterNetPublicIPAddress = IniUtils.getString(ini, "localnc", "public.cluster.address", clusterNetPublicIPAddress);
+ clusterNetPublicPort = IniUtils.getInt(ini, "localnc", "public.cluster.port", clusterNetPublicPort);
+ dataPublicIPAddress = IniUtils.getString(ini, "localnc", "public.data.address", dataPublicIPAddress);
+ dataPublicPort = IniUtils.getInt(ini, "localnc", "public.data.port", dataPublicPort);
+ resultPublicIPAddress = IniUtils.getString(ini, "localnc", "public.result.address", resultPublicIPAddress);
+ resultPublicPort = IniUtils.getInt(ini, "localnc", "public.result.port", resultPublicPort);
+
+ retries = IniUtils.getInt(ini, "localnc", "retries", retries);
+
+ // Directories
+ ioDevices = IniUtils.getString(ini, "localnc", "iodevices", ioDevices);
+
+ // Hyracks client entrypoint
+ appNCMainClass = IniUtils.getString(ini, "localnc", "app.class", appNCMainClass);
+ }
+
+ /*
+ * Once all @Option fields have been loaded from command-line or otherwise
+ * specified programmatically, call this method to:
+ * 1. Load options from a config file (as specified by -config-file)
+ * 2. Set default values for certain derived values, such as setting
+ * clusterNetIpAddress to ipAddress
+ */
+ public void loadConfigAndApplyDefaults() throws IOException {
+ if (configFile != null) {
+ loadINIFile();
+ }
+
+ // "address" is the default for all IP addresses
+ if (clusterNetIPAddress == null) clusterNetIPAddress = ipAddress;
+ if (dataIPAddress == null) dataIPAddress = ipAddress;
+ if (resultIPAddress == null) resultIPAddress = ipAddress;
+
+ // All "public" options default to their "non-public" versions
+ if (clusterNetPublicIPAddress == null) clusterNetPublicIPAddress = clusterNetIPAddress;
+ if (clusterNetPublicPort == 0) clusterNetPublicPort = clusterNetPort;
+ if (dataPublicIPAddress == null) dataPublicIPAddress = dataIPAddress;
+ if (dataPublicPort == 0) dataPublicPort = dataPort;
+ if (resultPublicIPAddress == null) resultPublicIPAddress = resultIPAddress;
+ if (resultPublicPort == 0) resultPublicPort = resultPort;
+ }
+
+ /**
+ * @return An IApplicationConfig representing this NCConfig.
+ * Note: Currently this only includes the values from the configuration
+ * file, not anything specified on the command-line. QQQ
+ */
+ public IApplicationConfig getAppConfig() {
+ return new IniApplicationConfig(ini);
+ }
+
public void toCommandLine(List<String> cList) {
cList.add("-cc-host");
cList.add(ccHost);
@@ -133,6 +219,8 @@
cList.add(resultPublicIPAddress);
cList.add("-result-public-port");
cList.add(String.valueOf(resultPublicPort));
+ cList.add("-retries");
+ cList.add(String.valueOf(retries));
cList.add("-iodevices");
cList.add(ioDevices);
cList.add("-net-thread-count");
@@ -176,6 +264,7 @@
configuration.put("result-port", String.valueOf(resultPort));
configuration.put("result-public-ip-address", resultPublicIPAddress);
configuration.put("result-public-port", String.valueOf(resultPublicPort));
+ configuration.put("retries", String.valueOf(retries));
configuration.put("iodevices", ioDevices);
configuration.put("net-thread-count", String.valueOf(nNetThreads));
configuration.put("net-buffer-count", String.valueOf(nNetBuffers));
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 db759cb..b015e3d 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
@@ -18,12 +18,11 @@
*/
package org.apache.hyracks.control.nc;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
+import org.apache.hyracks.control.common.controllers.NCConfig;
import org.kohsuke.args4j.CmdLineParser;
-import org.apache.hyracks.control.common.controllers.NCConfig;
+import java.util.logging.Level;
+import java.util.logging.Logger;
public class NCDriver {
private static final Logger LOGGER = Logger.getLogger(NCDriver.class.getName());
@@ -35,13 +34,14 @@
try {
cp.parseArgument(args);
} catch (Exception e) {
- System.err.println(e.getMessage());
+ e.printStackTrace();
cp.printUsage(System.err);
- return;
+ System.exit(1);
}
+ ncConfig.loadConfigAndApplyDefaults();
final NodeControllerService nService = new NodeControllerService(ncConfig);
- if (LOGGER.isLoggable(Level.INFO)) {
+ if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.severe("Setting uncaught exception handler " + nService.getLifeCycleComponentManager());
}
Thread.currentThread().setUncaughtExceptionHandler(nService.getLifeCycleComponentManager());
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 598d6db..2f8def1 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
@@ -260,7 +260,7 @@
init();
datasetNetworkManager.start();
- IIPCHandle ccIPCHandle = ipc.getHandle(new InetSocketAddress(ncConfig.ccHost, ncConfig.ccPort), -1);
+ IIPCHandle ccIPCHandle = ipc.getHandle(new InetSocketAddress(ncConfig.ccHost, ncConfig.ccPort), ncConfig.retries);
this.ccs = new ClusterControllerRemoteProxy(ccIPCHandle);
HeartbeatSchema.GarbageCollectorInfo[] gcInfos = new HeartbeatSchema.GarbageCollectorInfo[gcMXBeans.size()];
for (int i = 0; i < gcInfos.length; ++i) {
@@ -270,14 +270,12 @@
// Use "public" versions of network addresses and ports
NetworkAddress datasetAddress = datasetNetworkManager.getPublicNetworkAddress();
NetworkAddress netAddress = netManager.getPublicNetworkAddress();
- if (ncConfig.dataPublicIPAddress != null) {
- netAddress = new NetworkAddress(ncConfig.dataPublicIPAddress, ncConfig.dataPublicPort);
- }
- ccs.registerNode(new NodeRegistration(ipc.getSocketAddress(), id, ncConfig, netAddress, datasetAddress,
- osMXBean.getName(), osMXBean.getArch(), osMXBean.getVersion(), osMXBean.getAvailableProcessors(),
- runtimeMXBean.getVmName(), runtimeMXBean.getVmVersion(), runtimeMXBean.getVmVendor(),
- runtimeMXBean.getClassPath(), runtimeMXBean.getLibraryPath(), runtimeMXBean.getBootClassPath(),
- runtimeMXBean.getInputArguments(), runtimeMXBean.getSystemProperties(), hbSchema));
+ ccs.registerNode(new NodeRegistration(ipc.getSocketAddress(), id, ncConfig, netAddress,
+ datasetAddress, osMXBean.getName(), osMXBean.getArch(), osMXBean
+ .getVersion(), osMXBean.getAvailableProcessors(), runtimeMXBean.getVmName(), runtimeMXBean
+ .getVmVersion(), runtimeMXBean.getVmVendor(), runtimeMXBean.getClassPath(), runtimeMXBean
+ .getLibraryPath(), runtimeMXBean.getBootClassPath(), runtimeMXBean.getInputArguments(),
+ runtimeMXBean.getSystemProperties(), hbSchema));
synchronized (this) {
while (registrationPending) {
@@ -316,7 +314,7 @@
}
private void startApplication() throws Exception {
- appCtx = new NCApplicationContext(this, serverCtx, ctx, id, memoryManager, lccm);
+ appCtx = new NCApplicationContext(this, serverCtx, ctx, id, memoryManager, lccm, ncConfig.getAppConfig());
String className = ncConfig.appNCMainClass;
if (className != null) {
Class<?> c = Class.forName(className);
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/application/NCApplicationContext.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/application/NCApplicationContext.java
index d0fd524..d23c701 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/application/NCApplicationContext.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/application/NCApplicationContext.java
@@ -22,6 +22,7 @@
import java.io.OutputStream;
import java.io.Serializable;
+import org.apache.hyracks.api.application.IApplicationConfig;
import org.apache.hyracks.api.application.INCApplicationContext;
import org.apache.hyracks.api.application.IStateDumpHandler;
import org.apache.hyracks.api.context.IHyracksRootContext;
@@ -42,10 +43,10 @@
private IStateDumpHandler sdh;
private final NodeControllerService ncs;
- public NCApplicationContext(NodeControllerService ncs, ServerContext serverCtx, IHyracksRootContext rootCtx,
- String nodeId, MemoryManager memoryManager, ILifeCycleComponentManager lifeCyclecomponentManager)
- throws IOException {
- super(serverCtx);
+ public NCApplicationContext(NodeControllerService ncs, ServerContext serverCtx, IHyracksRootContext rootCtx, String nodeId,
+ MemoryManager memoryManager, ILifeCycleComponentManager lifeCyclecomponentManager,
+ IApplicationConfig appConfig) throws IOException {
+ super(serverCtx, appConfig);
this.lccm = lifeCyclecomponentManager;
this.nodeId = nodeId;
this.rootCtx = rootCtx;
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/pom.xml b/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/pom.xml
new file mode 100644
index 0000000..66d4d24
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ! 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.
+ !-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>hyracks-control</artifactId>
+ <groupId>org.apache.hyracks</groupId>
+ <version>0.2.18-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>hyracks-nc-service</artifactId>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.0.2</version>
+ <configuration>
+ <source>1.7</source>
+ <target>1.7</target>
+ <fork>true</fork>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>args4j</groupId>
+ <artifactId>args4j</artifactId>
+ <version>2.0.12</version>
+ <type>jar</type>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ini4j</groupId>
+ <artifactId>ini4j</artifactId>
+ <version>0.5.4</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hyracks</groupId>
+ <artifactId>hyracks-control-nc</artifactId>
+ <version>0.2.18-SNAPSHOT</version>
+ <type>jar</type>
+ <scope>compile</scope>
+ </dependency>
+ </dependencies>
+
+</project>
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
new file mode 100644
index 0000000..df92d1a
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCService.java
@@ -0,0 +1,220 @@
+/*
+ * 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.control.nc.service;
+
+import org.apache.commons.lang3.SystemUtils;
+import org.ini4j.Ini;
+import org.kohsuke.args4j.CmdLineParser;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.StringReader;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Stand-alone process which listens for configuration information from the
+ * CC and starts an NC. Intended to be a constantly-running service.
+ */
+public class NCService {
+
+ private static final Logger LOGGER = Logger.getLogger(NCService.class.getName());
+
+ /**
+ * The .ini read from the CC (*not* the ncservice.ini file)
+ */
+ private static Ini ini = new Ini();
+
+ /**
+ * The NCServiceConfig
+ */
+ private static NCServiceConfig config;
+
+ /**
+ * The child Process, if one is active
+ */
+ private static Process proc = null;
+
+ private static final String MAGIC_COOKIE = "hyncmagic";
+
+ private static String getStringINIOpt(Ini ini, String section, String key, String default_value) {
+ String value = ini.get(section, key, String.class);
+ return (value != null) ? value : default_value;
+ }
+
+ private static int getIntINIOpt(Ini ini, String section, String key, int default_value) {
+ Integer value = ini.get(section, key, Integer.class);
+ return (value != null) ? value : default_value;
+ }
+
+ private static List<String> buildCommand() throws IOException {
+ List<String> cList = new ArrayList<String>();
+ if (SystemUtils.IS_OS_WINDOWS) {
+ cList.add(config.command + ".bat");
+ }
+ else {
+ cList.add(config.command);
+ }
+ cList.add("-config-file");
+ // Store the Ini file from the CC locally so NCConfig can read it.
+ // QQQ should arrange to delete this when done
+ File tempIni = File.createTempFile("ncconf", ".conf");
+ ini.store(tempIni);
+ cList.add(tempIni.getCanonicalPath());
+ return cList;
+ }
+
+ private static void configEnvironment(Map<String,String> env) {
+ if (env.containsKey("JAVA_OPTS")) {
+ return;
+ }
+ String jvmargs = getStringINIOpt(ini, "localnc", "jvm.args", "-Xmx1536m");
+ env.put("JAVA_OPTS", jvmargs);
+ }
+
+ /**
+ * Attempts to launch the "real" NCDriver, based on the configuration
+ * information gathered so far.
+ * @return true if the process was successfully launched and has now
+ * exited with a 0 (normal) exit code. false if some configuration error
+ * prevented the process from being launched or the process returned
+ * a non-0 (abnormal) exit code.
+ */
+ private static boolean launchNCProcess() {
+ try {
+ ProcessBuilder pb = new ProcessBuilder(buildCommand());
+ configEnvironment(pb.environment());
+ // QQQ inheriting probably isn't right
+ pb.inheritIO();
+
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.info("Launching NCDriver process");
+ }
+ proc = pb.start();
+
+ boolean waiting = true;
+ int retval = 0;
+ while (waiting) {
+ try {
+ retval = proc.waitFor();
+ waiting = false;
+ } catch (InterruptedException ignored) {
+ }
+ }
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.info("NCDriver exited with return value " + retval);
+ }
+ return (retval == 0);
+ } catch (Exception e) {
+ if (LOGGER.isLoggable(Level.SEVERE)) {
+ LOGGER.log(Level.SEVERE, "Configuration from CC broken", e);
+ }
+ return false;
+ }
+ }
+
+ private static boolean acceptConnection(InputStream is) {
+ // Simple on-wire protocol: magic cookie (string), CC address (string),
+ // port (string), as encoded on CC by ObjectOutputStream. If we see
+ // anything else or have any error, crap out and await a different
+ // connection.
+ // QQQ This should probably be changed to directly accept the full
+ // config file from the CC, rather than calling back to the CC's
+ // "config" webservice to retrieve it. Revisit when the CC is fully
+ // parsing and validating the master config file.
+ try {
+ ObjectInputStream ois = new ObjectInputStream(is);
+ String magic = ois.readUTF();
+ if (! MAGIC_COOKIE.equals(magic)) {
+ LOGGER.severe("Connection used incorrect magic cookie");
+ return false;
+ }
+ String iniString = ois.readUTF();
+ ini = new Ini(new StringReader(iniString));
+ return launchNCProcess();
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, "Error decoding connection from server", e);
+ }
+ return false;
+ }
+
+ public static void main(String[] args) throws Exception {
+ // Register a shutdown hook which will kill the NC if the NC Service is killed.
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ if (proc != null) {
+ proc.destroy();
+ }
+ }
+ });
+ config = new NCServiceConfig();
+ CmdLineParser cp = new CmdLineParser(config);
+ try {
+ cp.parseArgument(args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ cp.printUsage(System.err);
+ System.exit(1);
+ }
+ config.loadConfigAndApplyDefaults();
+
+ // For now we implement a trivial listener which just
+ // accepts an IP/port combination from the CC. This could
+ // be made more advanced in several ways depending on whether
+ // we want to expand the functionality of this service.
+ // For now this gets the job done, without radically changing
+ // the NC itself so that Managix can continue to function.
+ InetAddress addr = config.address == null ? null : InetAddress.getByName(config.address);
+ int port = config.port;
+
+ // Loop forever - the NCService will always return to "waiting for CC" state
+ // when the child NC terminates for any reason.
+ while (true) {
+ ServerSocket listener = new ServerSocket(port, 5, addr);
+ try {
+ boolean launched = false;
+ while (!launched) {
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.info("Waiting for connection from CC on " + addr + ":" + port);
+ }
+ Socket socket = listener.accept();
+ try {
+ // QQQ Because acceptConnection() doesn't return if the
+ // service is started appropriately, the socket remains
+ // open but non-responsive.
+ launched = acceptConnection(socket.getInputStream());
+ } finally {
+ socket.close();
+ }
+ }
+ } finally {
+ listener.close();
+ }
+ }
+ }
+}
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCServiceConfig.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCServiceConfig.java
new file mode 100644
index 0000000..af80b33
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCServiceConfig.java
@@ -0,0 +1,74 @@
+/*
+ * 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.control.nc.service;
+
+import org.apache.hyracks.control.common.controllers.IniUtils;
+import org.ini4j.Ini;
+import org.kohsuke.args4j.Option;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+/**
+ * Command-line arguments for NC Service.
+ */
+public class NCServiceConfig {
+
+ /**
+ * Normally one should only pass this argument. Other arguments are for debugging and test purposes.
+ * If an option is specified both in the config file and on the command line, the config file
+ * version will take precedence.
+ */
+ @Option(name = "-config-file", usage = "Local NC configuration file (default: none)", required = false)
+ public String configFile = null;
+
+ @Option(name = "-address", usage = "Address to listen on for connections from CC (default: localhost)", required = false)
+ public String address = InetAddress.getLoopbackAddress().getHostAddress();
+
+ @Option(name = "-port", usage = "Port to listen on for connections from CC (default: 9090)", required = false)
+ public int port = 9090;
+
+ @Option(name = "-command", usage = "NC command to run (default: 'hyracksnc' on PATH)", required = false)
+ public String command = "hyracksnc";
+
+ private Ini ini = null;
+
+ /**
+ * This method simply maps from the ini parameters to the NCServiceConfig's fields.
+ * It does not apply defaults or any logic.
+ */
+ private void loadINIFile() throws IOException {
+ ini = IniUtils.loadINIFile(configFile);
+ address = IniUtils.getString(ini, "ncservice", "address", address);
+ port = IniUtils.getInt(ini, "ncservice", "port", port);
+ }
+
+ /**
+ * Once all @Option fields have been loaded from command-line or otherwise
+ * specified programmatically, call this method to:
+ * 1. Load options from a config file (as specified by -config-file)
+ * 2. Set default values for certain derived values
+ */
+ public void loadConfigAndApplyDefaults() throws IOException {
+ if (configFile != null) {
+ loadINIFile();
+ }
+ // No defaults necessary beyond the static ones for this config
+ }
+}
diff --git a/hyracks-fullstack/hyracks/hyracks-control/pom.xml b/hyracks-fullstack/hyracks/hyracks-control/pom.xml
index ddb4e89..bd8d739 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/pom.xml
+++ b/hyracks-fullstack/hyracks/hyracks-control/pom.xml
@@ -46,5 +46,6 @@
<module>hyracks-control-common</module>
<module>hyracks-control-cc</module>
<module>hyracks-control-nc</module>
+ <module>hyracks-nc-service</module>
</modules>
</project>
diff --git a/hyracks-fullstack/hyracks/hyracks-dist/pom.xml b/hyracks-fullstack/hyracks/hyracks-dist/pom.xml
index 99f8eca..a7c7a15 100644
--- a/hyracks-fullstack/hyracks/hyracks-dist/pom.xml
+++ b/hyracks-fullstack/hyracks/hyracks-dist/pom.xml
@@ -76,7 +76,7 @@
<phase>package</phase>
<configuration>
<target>
- <chmod file="target/appassembler/bin/*)" perm="755"/>
+ <chmod file="target/appassembler/bin/*" perm="755"/>
</target>
</configuration>
<goals>
diff --git a/hyracks-fullstack/hyracks/hyracks-server/pom.xml b/hyracks-fullstack/hyracks/hyracks-server/pom.xml
index facaae8..3bda1d3 100644
--- a/hyracks-fullstack/hyracks/hyracks-server/pom.xml
+++ b/hyracks-fullstack/hyracks/hyracks-server/pom.xml
@@ -43,6 +43,30 @@
<build>
<plugins>
<plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <version>2.6</version>
+ <configuration>
+ <runOrder>alphabetical</runOrder>
+ <forkMode>pertest</forkMode>
+ <systemProperties>
+ <property>
+ <name>java.util.logging.config.file</name>
+ <value>src/test/resources/logging.properties</value>
+ </property>
+ </systemProperties>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>integration-test</goal>
+ <goal>verify</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>appassembler-maven-plugin</artifactId>
<version>1.3</version>
@@ -59,6 +83,10 @@
<name>hyracksnc</name>
</program>
<program>
+ <mainClass>org.apache.hyracks.control.nc.service.NCService</mainClass>
+ <name>hyracksncservice</name>
+ </program>
+ <program>
<mainClass>org.apache.hyracks.server.drivers.VirtualClusterDriver</mainClass>
<name>hyracks-virtual-cluster</name>
</program>
@@ -80,6 +108,24 @@
<skip>true</skip>
</configuration>
</plugin>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.6</version>
+ <executions>
+ <execution>
+ <id>process-test-classes</id>
+ <phase>package</phase>
+ <configuration>
+ <target>
+ <chmod file="target/appassembler/bin/*" perm="755" />
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
<dependencies>
@@ -97,5 +143,18 @@
<type>jar</type>
<scope>compile</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.hyracks</groupId>
+ <artifactId>hyracks-nc-service</artifactId>
+ <version>0.2.18-SNAPSHOT</version>
+ <type>jar</type>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-httpclient</groupId>
+ <artifactId>commons-httpclient</artifactId>
+ <version>3.0.1</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/server/test/NCServiceIT.java b/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/server/test/NCServiceIT.java
new file mode 100644
index 0000000..7f431f6
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/server/test/NCServiceIT.java
@@ -0,0 +1,137 @@
+/*
+ * 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.server.test;
+
+import junit.framework.Assert;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.lang3.StringUtils;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.File;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+public class NCServiceIT {
+
+ private static final String RESOURCE_DIR = StringUtils
+ .join(new String[]{System.getProperty("user.dir"), "src", "test", "resources", "NCServiceIT"},
+ File.separator);
+ private static final String APP_DIR = StringUtils
+ .join(new String[]{System.getProperty("user.dir"), "target", "appassembler", "bin"},
+ File.separator);
+ private static final Logger LOGGER = Logger.getLogger(NCServiceIT.class.getName());
+ private static List<Process> procs = new ArrayList<>();
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ // Start two NC Services - don't read their output as they don't terminate
+ procs.add(invoke(APP_DIR + File.separator + "hyracksncservice",
+ "-config-file", RESOURCE_DIR + File.separator + "nc-red.conf",
+ "-command", APP_DIR + File.separator + "hyracksnc"));
+ procs.add(invoke(APP_DIR + File.separator + "hyracksncservice",
+ "-config-file", RESOURCE_DIR + File.separator + "nc-blue.conf",
+ "-command", APP_DIR + File.separator + "hyracksnc"));
+ try {
+ Thread.sleep(2000);
+ }
+ catch (InterruptedException ignored) {
+ }
+
+ // Start CC
+ procs.add(invoke(APP_DIR + File.separator + "hyrackscc",
+ "-config-file", RESOURCE_DIR + File.separator + "cc.conf"));
+ try {
+ Thread.sleep(10000);
+ }
+ catch (InterruptedException ignored) {
+ }
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ for (Process p : procs) {
+ p.destroy();
+ p.waitFor();
+ }
+ }
+
+ private static String getHttp(String url) throws Exception {
+ HttpClient client = new HttpClient();
+ GetMethod get = new GetMethod(url);
+ int statusCode;
+ try {
+ statusCode = client.executeMethod(get);
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw e;
+ }
+ String response = get.getResponseBodyAsString();
+ if (statusCode == HttpStatus.SC_OK) {
+ return response;
+ } else {
+ throw new Exception("HTTP error " + statusCode + ":\n" + response);
+ }
+ }
+
+ private static Process invoke(String... args) throws Exception {
+ ProcessBuilder pb = new ProcessBuilder(args);
+ pb.redirectErrorStream(true);
+ Process p = pb.start();
+ return p;
+ }
+
+ @Test
+ public void IsNodelistCorrect() throws Exception {
+ // Ping the nodelist HTTP API
+ String localhost = InetAddress.getLoopbackAddress().getHostAddress();
+ String response = getHttp("http://" + localhost + ":12345/rest/nodes");
+ JSONObject result = new JSONObject(response);
+ JSONArray nodes = result.getJSONArray("result");
+ int numNodes = nodes.length();
+ Assert.assertEquals("Wrong number of nodes!", numNodes, 2);
+ for (int i = 0; i < nodes.length(); i++) {
+ JSONObject node = nodes.getJSONObject(i);
+ String id = node.getString("node-id");
+ if (id.equals("red") || id.equals("blue")) {
+ continue;
+ }
+ Assert.fail("Unexpected node ID '" + id + "'!");
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ try {
+ setUp();
+ } catch (Exception e) {
+ e.printStackTrace();
+ LOGGER.severe("TEST CASE(S) FAILED");
+ } finally {
+ tearDown();
+ }
+ }
+
+}
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
new file mode 100644
index 0000000..25ac530
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/NCServiceIT/cc.conf
@@ -0,0 +1,11 @@
+[nc/red]
+address=127.0.0.1
+
+[nc/blue]
+address=127.0.0.1
+port=9091
+
+[cc]
+cluster.address = 127.0.0.1
+http.port = 12345
+
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
new file mode 100644
index 0000000..d070b59
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/NCServiceIT/nc-blue.conf
@@ -0,0 +1,3 @@
+[ncservice]
+address=127.0.0.1
+port=9091
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
new file mode 100644
index 0000000..58a8f1d
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/NCServiceIT/nc-red.conf
@@ -0,0 +1,4 @@
+[ncservice]
+address=127.0.0.1
+port=9090
+
diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/logging.properties b/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/logging.properties
new file mode 100644
index 0000000..c888bb1
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/logging.properties
@@ -0,0 +1,76 @@
+#/*
+# 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.
+############################################################
+# Default Logging Configuration File
+#
+# You can use a different file by specifying a filename
+# with the java.util.logging.config.file system property.
+# For example java -Djava.util.logging.config.file=myfile
+############################################################
+
+############################################################
+# Global properties
+############################################################
+
+# "handlers" specifies a comma separated list of log Handler
+# classes. These handlers will be installed during VM startup.
+# Note that these classes must be on the system classpath.
+# By default we only configure a ConsoleHandler, which will only
+# show messages at the INFO and above levels.
+
+handlers= java.util.logging.ConsoleHandler
+
+# To also add the FileHandler, use the following line instead.
+
+# handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
+
+# Default global logging level.
+# This specifies which kinds of events are logged across
+# all loggers. For any given facility this global level
+# can be overriden by a facility specific level
+# Note that the ConsoleHandler also has a separate level
+# setting to limit messages printed to the console.
+
+.level= WARNING
+# .level= INFO
+# .level= FINE
+# .level = FINEST
+
+############################################################
+# Handler specific properties.
+# Describes specific configuration info for Handlers.
+############################################################
+
+# default file output is in user's home directory.
+
+# java.util.logging.FileHandler.pattern = %h/java%u.log
+# java.util.logging.FileHandler.limit = 50000
+# java.util.logging.FileHandler.count = 1
+# java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
+
+# Limit the message that are printed on the console to FINE and above.
+
+java.util.logging.ConsoleHandler.level = FINE
+java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
+
+
+############################################################
+# Facility specific properties.
+# Provides extra control for each logger.
+############################################################
+
diff --git a/hyracks-fullstack/hyracks/hyracks-test-support/src/main/java/org/apache/hyracks/test/support/TestNCApplicationContext.java b/hyracks-fullstack/hyracks/hyracks-test-support/src/main/java/org/apache/hyracks/test/support/TestNCApplicationContext.java
index f619be0..c6a4430 100644
--- a/hyracks-fullstack/hyracks/hyracks-test-support/src/main/java/org/apache/hyracks/test/support/TestNCApplicationContext.java
+++ b/hyracks-fullstack/hyracks/hyracks-test-support/src/main/java/org/apache/hyracks/test/support/TestNCApplicationContext.java
@@ -21,6 +21,7 @@
import java.io.Serializable;
import java.util.concurrent.ThreadFactory;
+import org.apache.hyracks.api.application.IApplicationConfig;
import org.apache.hyracks.api.application.INCApplicationContext;
import org.apache.hyracks.api.application.IStateDumpHandler;
import org.apache.hyracks.api.context.IHyracksRootContext;
@@ -122,6 +123,11 @@
}
@Override
+ public IApplicationConfig getAppConfig() {
+ return null;
+ }
+
+ @Override
public ILifeCycleComponentManager getLifeCycleComponentManager() {
return lccm;
}