ASTERIXDB-1482: Added NCServiceExecutionIT, HyracksVirtualCluster.
NCServiceExecutionIT runs all execution tests against a local cluster
managed by the NCService deployment framework.
HyracksVirtualCluster offers programmatic NCService deployment
control along with improved HyracksNCProcess/HyracksCCProcess.
Further fixes and improvements:
1. Fix handling of iodevices/storagedir (ASTERIXDB-1482)
2. Proper handling of [nc] default section in all cases
3. Ensure asterixnc, etc. scripts are executable
4. Consolidate Ini handling
5. Pruned some dead code, including VirtualClusterDriver
6. A bit of refactoring and extended commenting
Change-Id: If3eb450782a595cf85d04a2c2e9cc732564e65e6
Reviewed-on: https://asterix-gerrit.ics.uci.edu/958
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Ian Maxon <imaxon@apache.org>
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixMetadataProperties.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixMetadataProperties.java
index 9a8fba4..677fc78 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixMetadataProperties.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixMetadataProperties.java
@@ -39,7 +39,7 @@
}
public ClusterPartition getMetadataPartition() {
- return accessor.getMetadataPartiton();
+ return accessor.getMetadataPartition();
}
public Map<String, String[]> getStores() {
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixPropertiesAccessor.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixPropertiesAccessor.java
index 507a393..7309f0c 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixPropertiesAccessor.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixPropertiesAccessor.java
@@ -18,6 +18,7 @@
*/
package org.apache.asterix.common.config;
+import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -53,10 +54,12 @@
private final List<String> nodeNames = new ArrayList<>();;
private final Map<String, String[]> stores = new HashMap<>();;
private final Map<String, String> coredumpConfig = new HashMap<>();
+
+ // This can be removed when asterix-configuration.xml is no longer required.
private final Map<String, Property> asterixConfigurationParams;
private final IApplicationConfig cfg;
private final Map<String, String> transactionLogDirs = new HashMap<>();
- private final Map<String, String> asterixBuildProperties;
+ private final Map<String, String> asterixBuildProperties = new HashMap<>();
private final Map<String, ClusterPartition[]> nodePartitionsMap;
private final SortedMap<Integer, ClusterPartition> clusterPartitions = new TreeMap<>();
@@ -94,6 +97,13 @@
List<Store> configuredStores = asterixConfiguration.getStore();
nodePartitionsMap = new HashMap<>();
int uniquePartitionId = 0;
+ // Here we iterate through all <store> elements in asterix-configuration.xml.
+ // For each one, we create an array of ClusterPartitions and store this array
+ // in nodePartitionsMap, keyed by the node name. The array is the same length
+ // as the comma-separated <storeDirs> child element, because Managix will have
+ // arranged for that element to be populated with the full paths to each
+ // partition directory (as formed by appending the <store> subdirectory to
+ // each <iodevices> path from the user's original cluster.xml).
for (Store store : configuredStores) {
String trimmedStoreDirs = store.getStoreDirs().trim();
String[] nodeStores = trimmedStoreDirs.split(",");
@@ -117,35 +127,29 @@
for (TransactionLogDir txnLogDir : asterixConfiguration.getTransactionLogDir()) {
transactionLogDirs.put(txnLogDir.getNcId(), txnLogDir.getTxnLogDirPath());
}
- Properties gitProperties = new Properties();
- try {
- gitProperties.load(getClass().getClassLoader().getResourceAsStream("git.properties"));
- asterixBuildProperties = new HashMap<String, String>();
- for (final String name : gitProperties.stringPropertyNames()) {
- asterixBuildProperties.put(name, gitProperties.getProperty(name));
- }
- } catch (IOException e) {
- throw new AsterixException(e);
- }
+ loadAsterixBuildProperties();
}
/**
* Constructor which wraps an IApplicationConfig.
*/
- public AsterixPropertiesAccessor(IApplicationConfig cfg) {
+ public AsterixPropertiesAccessor(IApplicationConfig cfg) throws AsterixException {
this.cfg = cfg;
instanceName = cfg.getString("asterix", "instance", "DEFAULT_INSTANCE");
String mdNode = null;
nodePartitionsMap = new HashMap<>();
int uniquePartitionId = 0;
+
+ // Iterate through each configured NC.
for (String section : cfg.getSections()) {
if (!section.startsWith("nc/")) {
continue;
}
String ncId = section.substring(3);
+ // Here we figure out which is the metadata node. If any NCs
+ // declare "metadata.port", use that one; otherwise just use the first.
if (mdNode == null) {
- // Default is first node == metadata node
mdNode = ncId;
}
if (cfg.getString(section, "metadata.port") != null) {
@@ -153,27 +157,46 @@
mdNode = ncId;
}
+ // Now we assign the coredump and txnlog directories for this node.
// QQQ Default values? Should they be specified here? Or should there
- // be a default.ini? They can't be inserted by TriggerNCWork except
- // possibly for hyracks-specified values. Certainly wherever they are,
- // they should be platform-dependent.
+ // be a default.ini? Certainly wherever they are, they should be platform-dependent.
coredumpConfig.put(ncId, cfg.getString(section, "coredumpdir", "/var/lib/asterixdb/coredump"));
transactionLogDirs.put(ncId, cfg.getString(section, "txnlogdir", "/var/lib/asterixdb/txn-log"));
- String[] storeDirs = cfg.getString(section, "storagedir", "storage").trim().split(",");
- ClusterPartition[] nodePartitions = new ClusterPartition[storeDirs.length];
+
+ // Now we create an array of ClusterPartitions for all the partitions
+ // on this NC.
+ String[] iodevices = cfg.getString(section, "iodevices", "/var/lib/asterixdb/iodevice").split(",");
+ String storageSubdir = cfg.getString(section, "storagedir", "storage");
+ String[] nodeStores = new String[iodevices.length];
+ ClusterPartition[] nodePartitions = new ClusterPartition[iodevices.length];
for (int i = 0; i < nodePartitions.length; i++) {
+ // Construct final storage path from iodevice dir + storage subdir.
+ nodeStores[i] = iodevices[i] + File.separator + storageSubdir;
+ // Create ClusterPartition instances for this NC.
ClusterPartition partition = new ClusterPartition(uniquePartitionId++, ncId, i);
clusterPartitions.put(partition.getPartitionId(), partition);
nodePartitions[i] = partition;
}
- stores.put(ncId, storeDirs);
+ stores.put(ncId, nodeStores);
nodePartitionsMap.put(ncId, nodePartitions);
nodeNames.add(ncId);
}
metadataNodeName = mdNode;
asterixConfigurationParams = null;
- asterixBuildProperties = null;
+ loadAsterixBuildProperties();
+ }
+
+ private void loadAsterixBuildProperties() throws AsterixException {
+ Properties gitProperties = new Properties();
+ try {
+ gitProperties.load(getClass().getClassLoader().getResourceAsStream("git.properties"));
+ for (final String name : gitProperties.stringPropertyNames()) {
+ asterixBuildProperties.put(name, gitProperties.getProperty(name));
+ }
+ } catch (IOException e) {
+ throw new AsterixException(e);
+ }
}
public String getMetadataNodeName() {
@@ -204,20 +227,6 @@
return asterixBuildProperties;
}
- public void putCoredumpPaths(String nodeId, String coredumpPath) {
- if (coredumpConfig.containsKey(nodeId)) {
- throw new IllegalStateException("Cannot override value for coredump path");
- }
- coredumpConfig.put(nodeId, coredumpPath);
- }
-
- public void putTransactionLogDir(String nodeId, String txnLogDir) {
- if (transactionLogDirs.containsKey(nodeId)) {
- throw new IllegalStateException("Cannot override value for txnLogDir");
- }
- transactionLogDirs.put(nodeId, txnLogDir);
- }
-
public <T> T getProperty(String property, T defaultValue, IPropertyInterpreter<T> interpreter) {
String value;
Property p = null;
@@ -246,18 +255,11 @@
}
}
- private static <T> void logConfigurationError(Property p, T defaultValue) {
- if (LOGGER.isLoggable(Level.SEVERE)) {
- LOGGER.severe("Invalid property value '" + p.getValue() + "' for property '" + p.getName()
- + "'.\n See the description: \n" + p.getDescription() + "\nDefault = " + defaultValue);
- }
- }
-
public String getInstanceName() {
return instanceName;
}
- public ClusterPartition getMetadataPartiton() {
+ public ClusterPartition getMetadataPartition() {
// metadata partition is always the first partition on the metadata node
return nodePartitionsMap.get(metadataNodeName)[0];
}
diff --git a/asterixdb/asterix-server/pom.xml b/asterixdb/asterix-server/pom.xml
index 812fd59..92fde73 100644
--- a/asterixdb/asterix-server/pom.xml
+++ b/asterixdb/asterix-server/pom.xml
@@ -125,6 +125,24 @@
</executions>
</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>
+ <plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
<executions>
@@ -181,11 +199,37 @@
<scope>compile</scope>
</dependency>
<dependency>
+ <groupId>org.apache.hyracks</groupId>
+ <artifactId>hyracks-server</artifactId>
+ <type>jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.apache.asterix</groupId>
<artifactId>asterix-app</artifactId>
<version>0.8.9-SNAPSHOT</version>
</dependency>
<dependency>
+ <groupId>org.apache.asterix</groupId>
+ <artifactId>asterix-app</artifactId>
+ <version>0.8.9-SNAPSHOT</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.asterix</groupId>
+ <artifactId>asterix-common</artifactId>
+ <version>0.8.9-SNAPSHOT</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.asterix</groupId>
+ <artifactId>asterix-test-framework</artifactId>
+ <version>0.8.9-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.codehaus.mojo.appassembler</groupId>
<artifactId>appassembler-booter</artifactId>
<version>1.3.1</version>
diff --git a/asterixdb/asterix-server/src/test/java/org/apache/asterix/server/test/NCServiceExecutionIT.java b/asterixdb/asterix-server/src/test/java/org/apache/asterix/server/test/NCServiceExecutionIT.java
new file mode 100644
index 0000000..c179103
--- /dev/null
+++ b/asterixdb/asterix-server/src/test/java/org/apache/asterix/server/test/NCServiceExecutionIT.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.server.test;
+
+import org.apache.asterix.test.aql.TestExecutor;
+import org.apache.asterix.test.runtime.HDFSCluster;
+import org.apache.asterix.testframework.context.TestCaseContext;
+import org.apache.asterix.testframework.xml.TestGroup;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hyracks.server.process.HyracksVirtualCluster;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.logging.Logger;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)
+public class NCServiceExecutionIT {
+
+ // Important paths and files for this test.
+
+ // The "target" subdirectory of asterix-server. All outputs go here.
+ private static final String TARGET_DIR = StringUtils
+ .join(new String[] { System.getProperty("basedir"), "target" }, File.separator);
+
+ // Directory where the NCs create and store all data, as configured by
+ // src/test/resources/NCServiceExecutionIT/cc.conf.
+ private static final String INSTANCE_DIR = StringUtils
+ .join(new String[] { TARGET_DIR, "tmp" }, File.separator);
+
+ // The log directory, where all CC, NCService, and NC logs are written. CC and
+ // NCService logs are configured on the HyracksVirtualCluster below. NC logs
+ // are configured in src/test/resources/NCServiceExecutionIT/ncservice*.conf.
+ private static final String LOG_DIR = StringUtils
+ .join(new String[] { TARGET_DIR, "failsafe-reports" }, File.separator);
+
+ // Directory where *.conf files are located.
+ private static final String CONF_DIR = StringUtils
+ .join(new String[] { TARGET_DIR, "test-classes", "NCServiceExecutionIT" },
+ File.separator);
+
+ // The app.home specified for HyracksVirtualCluster. The NCService expects
+ // to find the NC startup script in ${app.home}/bin.
+ private static final String APP_HOME = StringUtils
+ .join(new String[] { TARGET_DIR, "appassembler" }, File.separator);
+
+ // Path to the asterix-app directory. This is used as the current working
+ // directory for the CC and NCService processes, which allows relative file
+ // paths in "load" statements in test queries to find the right data. It is
+ // also used for HDFSCluster.
+ private static final String ASTERIX_APP_DIR = StringUtils
+ .join(new String[] { System.getProperty("basedir"), "..", "asterix-app" },
+ File.separator);
+
+ // Path to the actual AQL test files, which we borrow from asterix-app. This is
+ // passed to TestExecutor.
+ protected static final String TESTS_DIR = StringUtils
+ .join(new String[] { ASTERIX_APP_DIR, "src", "test", "resources", "runtimets" },
+ File.separator);
+
+ // Path that actual results are written to. We create and clean this directory
+ // here, and also pass it to TestExecutor which writes the test output there.
+ private static final String ACTUAL_RESULTS_DIR = StringUtils
+ .join(new String[] { TARGET_DIR, "ittest" }, File.separator);
+
+ private static final Logger LOGGER = Logger.getLogger(NCServiceExecutionIT.class.getName());
+
+ private final TestCaseContext tcCtx;
+ private static final TestExecutor testExecutor = new TestExecutor();
+ private static HyracksVirtualCluster cluster;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ // Create actual-results output directory.
+ File outDir = new File(ACTUAL_RESULTS_DIR);
+ outDir.mkdirs();
+
+ // Remove any instance data from previous runs.
+ File instanceDir = new File(INSTANCE_DIR);
+ if (instanceDir.isDirectory()) {
+ FileUtils.deleteDirectory(instanceDir);
+ }
+
+ // HDFSCluster requires the input directory to end with a file separator.
+ HDFSCluster.getInstance().setup(ASTERIX_APP_DIR + File.separator);
+
+ cluster = new HyracksVirtualCluster(new File(APP_HOME), new File(ASTERIX_APP_DIR));
+ cluster.addNC(
+ new File(CONF_DIR, "ncservice1.conf"),
+ new File(LOG_DIR, "ncservice1.log")
+ );
+ cluster.addNC(
+ new File(CONF_DIR, "ncservice2.conf"),
+ new File(LOG_DIR, "ncservice2.log")
+ );
+
+ try {
+ Thread.sleep(2000);
+ }
+ catch (InterruptedException ignored) {
+ }
+
+ // Start CC
+ cluster.start(
+ new File(CONF_DIR, "cc.conf"),
+ new File(LOG_DIR, "cc.log")
+ );
+
+ LOGGER.info("Sleeping while cluster comes online...");
+ try {
+ Thread.sleep(6000);
+ }
+ catch (InterruptedException ignored) {
+ }
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ File outdir = new File(ACTUAL_RESULTS_DIR);
+ File[] files = outdir.listFiles();
+ if (files == null || files.length == 0) {
+ outdir.delete();
+ }
+ cluster.stop();
+ HDFSCluster.getInstance().cleanup();
+ }
+
+ @Parameters
+ public static Collection<Object[]> tests() throws Exception {
+ Collection<Object[]> testArgs = new ArrayList<Object[]>();
+ TestCaseContext.Builder b = new TestCaseContext.Builder();
+ for (TestCaseContext ctx : b.build(new File(TESTS_DIR))) {
+ if (!skip(ctx)) {
+ testArgs.add(new Object[]{ctx});
+ }
+ }
+ return testArgs;
+ }
+
+ private static boolean skip(TestCaseContext tcCtx) {
+ // For now we skip feeds tests and external-library tests.
+ for (TestGroup group : tcCtx.getTestGroups()) {
+ if (group.getName().startsWith("external-") || group.getName().equals("feeds")) {
+ LOGGER.info("Skipping test: " + tcCtx.toString());
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ public NCServiceExecutionIT(TestCaseContext ctx) {
+ this.tcCtx = ctx;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testExecutor.executeTest(ACTUAL_RESULTS_DIR, tcCtx, null, false);
+ }
+}
diff --git a/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/cc.conf b/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/cc.conf
new file mode 100644
index 0000000..c4c76e6
--- /dev/null
+++ b/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/cc.conf
@@ -0,0 +1,25 @@
+[nc/asterix_nc1]
+txnlogdir=../asterix-server/target/tmp/asterix_nc1/txnlog
+coredumpdir=../asterix-server/target/tmp/asterix_nc1/coredump
+iodevices=../asterix-server/target/tmp/asterix_nc1/iodevice1,../asterix-server/target/tmp/asterix_nc1/iodevice2
+
+[nc/asterix_nc2]
+port=9091
+txnlogdir=../asterix-server/target/tmp/asterix_nc2/txnlog
+coredumpdir=../asterix-server/target/tmp/asterix_nc2/coredump
+iodevices=../asterix-server/target/tmp/asterix_nc2/iodevice1,../asterix-server/target/tmp/asterix_nc2/iodevice2
+
+[nc]
+address=127.0.0.1
+command=asterixnc
+app.class=org.apache.asterix.hyracks.bootstrap.NCApplicationEntryPoint
+jvm.args=-Xmx4096m -Dnode.Resolver="org.apache.asterix.external.util.IdentitiyResolverFactory"
+storagedir=test_storage
+
+[cc]
+cluster.address = 127.0.0.1
+app.class=org.apache.asterix.hyracks.bootstrap.CCApplicationEntryPoint
+
+[asterix]
+storage.memorycomponent.globalbudget = 1073741824
+
diff --git a/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/ncservice1.conf b/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/ncservice1.conf
new file mode 100644
index 0000000..fa44fa2
--- /dev/null
+++ b/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/ncservice1.conf
@@ -0,0 +1,3 @@
+[ncservice]
+logdir=../asterix-server/target/failsafe-reports
+
diff --git a/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/ncservice2.conf b/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/ncservice2.conf
new file mode 100644
index 0000000..53d8d9b
--- /dev/null
+++ b/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/ncservice2.conf
@@ -0,0 +1,4 @@
+[ncservice]
+logdir=../asterix-server/target/failsafe-reports
+port=9091
+
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 2aa3f37..4cddef1 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
@@ -276,7 +276,7 @@
continue;
}
String ncid = section.substring(3);
- String address = ini.get(section, "address");
+ String address = IniUtils.getString(ini, section, "address", null);
int port = IniUtils.getInt(ini, section, "port", 9090);
if (address == null) {
address = InetAddress.getLoopbackAddress().getHostAddress();
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
index ee79d38..7d2ff25 100644
--- 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
@@ -87,37 +87,15 @@
}
/**
- * 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();
-
- // First copy the global [nc] section to a new section named for
- // *this* NC, so that those values serve as defaults.
- String ncsection = "nc/" + ncId;
- copyIniSection(ccini, "nc", ini, ncsection);
- // Now copy all sections to their same name in the derived config.
- for (String section : ccini.keySet()) {
- copyIniSection(ccini, section, ini, section);
- }
+ StringWriter iniString = new StringWriter();
+ ccini.store(iniString);
// Finally insert *this* NC's name into localnc section - this is a fixed
// entry point so that NCs can determine where all their config is.
- ini.put("localnc", "id", ncId);
- StringWriter iniString = new StringWriter();
- ini.store(iniString);
+ iniString.append("\n[localnc]\nid=" + ncId + "\n");
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Returning Ini file:\n" + iniString.toString());
}
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
index 3a8a2de..22fe318 100644
--- 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
@@ -19,6 +19,7 @@
package org.apache.hyracks.control.common.application;
import org.apache.hyracks.api.application.IApplicationConfig;
+import org.apache.hyracks.control.common.controllers.IniUtils;
import org.ini4j.Ini;
import java.util.Set;
@@ -37,39 +38,34 @@
}
}
- 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);
+ return IniUtils.getString(ini, section, key, null);
}
@Override
public String getString(String section, String key, String defaultValue) {
- return getIniValue(section, key, defaultValue, String.class);
+ return IniUtils.getString(ini, section, key, defaultValue);
}
@Override
public int getInt(String section, String key) {
- return getIniValue(section, key, 0, Integer.class);
+ return IniUtils.getInt(ini, section, key, 0);
}
@Override
public int getInt(String section, String key, int defaultValue) {
- return getIniValue(section, key, defaultValue, Integer.class);
+ return IniUtils.getInt(ini, section, key, defaultValue);
}
@Override
public long getLong(String section, String key) {
- return getIniValue(section, key, (long) 0, Long.class);
+ return IniUtils.getLong(ini, section, key, (long) 0);
}
@Override
public long getLong(String section, String key, long defaultValue) {
- return getIniValue(section, key, defaultValue, Long.class);
+ return IniUtils.getLong(ini, section, key, defaultValue);
}
@Override
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 a04d750..64bd7d1 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
@@ -152,47 +152,4 @@
public IApplicationConfig getAppConfig() {
return new IniApplicationConfig(ini);
}
-
- public void toCommandLine(List<String> cList) {
- cList.add("-client-net-ip-address");
- cList.add(clientNetIpAddress);
- cList.add("-client-net-port");
- cList.add(String.valueOf(clientNetPort));
- cList.add("-cluster-net-ip-address");
- cList.add(clusterNetIpAddress);
- cList.add("-cluster-net-port");
- cList.add(String.valueOf(clusterNetPort));
- cList.add("-http-port");
- cList.add(String.valueOf(httpPort));
- cList.add("-heartbeat-period");
- cList.add(String.valueOf(heartbeatPeriod));
- cList.add("-max-heartbeat-lapse-periods");
- cList.add(String.valueOf(maxHeartbeatLapsePeriods));
- cList.add("-profile-dump-period");
- cList.add(String.valueOf(profileDumpPeriod));
- cList.add("-default-max-job-attempts");
- cList.add(String.valueOf(defaultMaxJobAttempts));
- cList.add("-job-history-size");
- cList.add(String.valueOf(jobHistorySize));
- cList.add("-result-time-to-live");
- cList.add(String.valueOf(resultTTL));
- cList.add("-result-sweep-threshold");
- cList.add(String.valueOf(resultSweepThreshold));
- cList.add("-cc-root");
- cList.add(ccRoot);
- if (clusterTopologyDefinition != null) {
- cList.add("-cluster-topology");
- cList.add(clusterTopologyDefinition.getAbsolutePath());
- }
- if (appCCMainClass != null) {
- cList.add("-app-cc-main-class");
- cList.add(appCCMainClass);
- }
- if (appArgs != null && !appArgs.isEmpty()) {
- cList.add("--");
- for (String appArg : appArgs) {
- cList.add(appArg);
- }
- }
- }
}
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/IniUtils.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/IniUtils.java
index 9a5c9a0..538bb0b 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/IniUtils.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/IniUtils.java
@@ -26,21 +26,39 @@
/**
* Some utility functions for reading Ini4j objects with default values.
+ * For all getXxx() methods: if the 'section' contains a slash, and the 'key'
+ * is not found in that section, we will search for the key in the section named
+ * by stripping the leaf of the section name (final slash and anything following).
+ * eg. getInt(ini, "nc/red", "dir", null) will first look for the key "dir" in
+ * the section "nc/red", but if it is not found, will look in the section "nc".
*/
public class IniUtils {
+ private static <T> T getIniValue(Ini ini, String section, String key, T default_value, Class<T> clazz) {
+ T value;
+ while (true) {
+ value = ini.get(section, key, clazz);
+ if (value == null) {
+ int idx = section.lastIndexOf('/');
+ if (idx > -1) {
+ section = section.substring(0, idx);
+ continue;
+ }
+ }
+ break;
+ }
+ return (value != null) ? value : default_value;
+ }
+
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;
+ return getIniValue(ini, section, key, defaultValue, String.class);
}
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;
+ return getIniValue(ini, section, key, defaultValue, Integer.class);
}
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;
+ return getIniValue(ini, section, key, defaultValue, Long.class);
}
public static Ini loadINIFile(String configFile) throws IOException {
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 b408083..d08df60 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
@@ -191,66 +191,6 @@
return new IniApplicationConfig(ini);
}
- public void toCommandLine(List<String> cList) {
- cList.add("-cc-host");
- cList.add(ccHost);
- cList.add("-cc-port");
- cList.add(String.valueOf(ccPort));
- cList.add("-cluster-net-ip-address");
- cList.add(clusterNetIPAddress);
- cList.add("-cluster-net-port");
- cList.add(String.valueOf(clusterNetPort));
- cList.add("-cluster-net-public-ip-address");
- cList.add(clusterNetPublicIPAddress);
- cList.add("-cluster-net-public-port");
- cList.add(String.valueOf(clusterNetPublicPort));
- cList.add("-node-id");
- cList.add(nodeId);
- cList.add("-data-ip-address");
- cList.add(dataIPAddress);
- cList.add("-data-port");
- cList.add(String.valueOf(dataPort));
- cList.add("-data-public-ip-address");
- cList.add(dataPublicIPAddress);
- cList.add("-data-public-port");
- cList.add(String.valueOf(dataPublicPort));
- cList.add("-result-ip-address");
- cList.add(resultIPAddress);
- cList.add("-result-port");
- cList.add(String.valueOf(resultPort));
- cList.add("-result-public-ip-address");
- 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");
- cList.add(String.valueOf(nNetThreads));
- cList.add("-net-buffer-count");
- cList.add(String.valueOf(nNetBuffers));
- cList.add("-max-memory");
- cList.add(String.valueOf(maxMemory));
- cList.add("-result-time-to-live");
- cList.add(String.valueOf(resultTTL));
- cList.add("-result-sweep-threshold");
- cList.add(String.valueOf(resultSweepThreshold));
- cList.add("-result-manager-memory");
- cList.add(String.valueOf(resultManagerMemory));
-
- if (appNCMainClass != null) {
- cList.add("-app-nc-main-class");
- cList.add(appNCMainClass);
- }
- if (appArgs != null && !appArgs.isEmpty()) {
- cList.add("--");
- for (String appArg : appArgs) {
- cList.add(appArg);
- }
- }
- }
-
public void toMap(Map<String, String> configuration) {
configuration.put("cc-host", ccHost);
configuration.put("cc-port", (String.valueOf(ccPort)));
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCService.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCService.java
index e3fe959..4102b4c 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCService.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCService.java
@@ -19,6 +19,7 @@
package org.apache.hyracks.control.nc.service;
import org.apache.commons.lang3.SystemUtils;
+import org.apache.hyracks.control.common.controllers.IniUtils;
import org.ini4j.Ini;
import org.kohsuke.args4j.CmdLineParser;
@@ -71,23 +72,13 @@
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>();
// Find the command to run. For now, we allow overriding the name, but
// still assume it's located in the bin/ directory of the deployment.
// Even this is likely more configurability than we need.
- String command = getStringINIOpt(ini, nodeSection, "command", "hyracksnc");
+ String command = IniUtils.getString(ini, nodeSection, "command", "hyracksnc");
// app.home is specified by the Maven appassembler plugin. If it isn't set,
// fall back to user's home dir. Again this is likely more flexibility
// than we need.
@@ -110,10 +101,16 @@
private static void configEnvironment(Map<String,String> env) {
if (env.containsKey("JAVA_OPTS")) {
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.info("Keeping JAVA_OPTS from environment");
+ }
return;
}
- String jvmargs = getStringINIOpt(ini, nodeSection, "jvm.args", "-Xmx1536m");
+ String jvmargs = IniUtils.getString(ini, nodeSection, "jvm.args", "-Xmx1536m");
env.put("JAVA_OPTS", jvmargs);
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.info("Setting JAVA_OPTS to " + jvmargs);
+ }
}
/**
@@ -146,6 +143,8 @@
// If the directory IS there, all is well
}
File logfile = new File(config.logdir, "nc-" + ncId + ".log");
+ // Don't care if this succeeds or fails:
+ logfile.delete();
pb.redirectOutput(ProcessBuilder.Redirect.appendTo(logfile));
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Logging to " + logfile.getCanonicalPath());
@@ -192,7 +191,7 @@
}
String iniString = ois.readUTF();
ini = new Ini(new StringReader(iniString));
- ncId = getStringINIOpt(ini, "localnc", "id", "");
+ ncId = IniUtils.getString(ini, "localnc", "id", "");
nodeSection = "nc/" + ncId;
return launchNCProcess();
} catch (Exception e) {
diff --git a/hyracks-fullstack/hyracks/hyracks-server/pom.xml b/hyracks-fullstack/hyracks/hyracks-server/pom.xml
index 3bda1d3..52958f8 100644
--- a/hyracks-fullstack/hyracks/hyracks-server/pom.xml
+++ b/hyracks-fullstack/hyracks/hyracks-server/pom.xml
@@ -86,10 +86,6 @@
<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>
</programs>
<repositoryLayout>flat</repositoryLayout>
<repositoryName>lib</repositoryName>
diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/drivers/VirtualClusterDriver.java b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/drivers/VirtualClusterDriver.java
deleted file mode 100644
index 41c14a7..0000000
--- a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/drivers/VirtualClusterDriver.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.drivers;
-
-import org.kohsuke.args4j.CmdLineParser;
-import org.kohsuke.args4j.Option;
-
-import org.apache.hyracks.control.common.controllers.CCConfig;
-import org.apache.hyracks.control.common.controllers.NCConfig;
-import org.apache.hyracks.server.process.HyracksCCProcess;
-import org.apache.hyracks.server.process.HyracksNCProcess;
-
-public class VirtualClusterDriver {
- private static class Options {
- @Option(name = "-n", required = false, usage = "Number of node controllers (default: 2)")
- public int n = 2;
-
- @Option(name = "-cc-client-net-port", required = false, usage = "CC Port (default: 1098)")
- public int ccClientNetPort = 1098;
-
- @Option(name = "-cc-cluster-net-port", required = false, usage = "CC Port (default: 1099)")
- public int ccClusterNetPort = 1099;
-
- @Option(name = "-cc-http-port", required = false, usage = "CC Port (default: 16001)")
- public int ccHttpPort = 16001;
- }
-
- public static void main(String[] args) throws Exception {
- Options options = new Options();
- CmdLineParser cp = new CmdLineParser(options);
- try {
- cp.parseArgument(args);
- } catch (Exception e) {
- System.err.println(e.getMessage());
- cp.printUsage(System.err);
- return;
- }
-
- CCConfig ccConfig = new CCConfig();
- ccConfig.clusterNetIpAddress = "127.0.0.1";
- ccConfig.clusterNetPort = options.ccClusterNetPort;
- ccConfig.clientNetIpAddress = "127.0.0.1";
- ccConfig.clientNetPort = options.ccClientNetPort;
- ccConfig.httpPort = options.ccHttpPort;
- HyracksCCProcess ccp = new HyracksCCProcess(ccConfig);
- ccp.start();
-
- Thread.sleep(5000);
-
- HyracksNCProcess ncps[] = new HyracksNCProcess[options.n];
- for (int i = 0; i < options.n; ++i) {
- NCConfig ncConfig = new NCConfig();
- ncConfig.ccHost = "127.0.0.1";
- ncConfig.ccPort = options.ccClusterNetPort;
- ncConfig.clusterNetIPAddress = "127.0.0.1";
- ncConfig.nodeId = "nc" + i;
- ncConfig.dataIPAddress = "127.0.0.1";
- ncps[i] = new HyracksNCProcess(ncConfig);
- ncps[i].start();
- }
-
- while (true) {
- Thread.sleep(10000);
- }
- }
-}
diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksCCProcess.java b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksCCProcess.java
index d0d8d63..4a70120 100644
--- a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksCCProcess.java
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksCCProcess.java
@@ -18,25 +18,28 @@
*/
package org.apache.hyracks.server.process;
+import org.apache.hyracks.control.cc.CCDriver;
+
+import java.io.File;
import java.util.List;
-import org.apache.hyracks.control.cc.CCDriver;
-import org.apache.hyracks.control.common.controllers.CCConfig;
-
public class HyracksCCProcess extends HyracksServerProcess {
- private CCConfig config;
- public HyracksCCProcess(CCConfig config) {
- this.config = config;
- }
-
- @Override
- protected void addCmdLineArgs(List<String> cList) {
- config.toCommandLine(cList);
+ public HyracksCCProcess(File configFile, File logFile, File appHome, File workingDir) {
+ this.configFile = configFile;
+ this.logFile = logFile;
+ this.appHome = appHome;
+ this.workingDir = workingDir;
}
@Override
protected String getMainClassName() {
return CCDriver.class.getName();
}
+
+ @Override
+ protected void addJvmArgs(List<String> cList) {
+ // CC needs more than default memory
+ cList.add("-Xmx1024m");
+ }
}
diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksNCProcess.java b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksNCProcess.java
index c4517e6..8bc1694 100644
--- a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksNCProcess.java
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksNCProcess.java
@@ -18,25 +18,28 @@
*/
package org.apache.hyracks.server.process;
+import org.apache.hyracks.control.nc.service.NCService;
+
+import java.io.File;
import java.util.List;
-import org.apache.hyracks.control.common.controllers.NCConfig;
-import org.apache.hyracks.control.nc.NCDriver;
-
public class HyracksNCProcess extends HyracksServerProcess {
- private NCConfig config;
- public HyracksNCProcess(NCConfig config) {
- this.config = config;
- }
-
- @Override
- protected void addCmdLineArgs(List<String> cList) {
- config.toCommandLine(cList);
+ public HyracksNCProcess(File configFile, File logFile, File appHome, File workingDir) {
+ this.configFile = configFile;
+ this.logFile = logFile;
+ this.appHome = appHome;
+ this.workingDir = workingDir;
}
@Override
protected String getMainClassName() {
- return NCDriver.class.getName();
+ return NCService.class.getName();
+ }
+
+ @Override
+ protected void addJvmArgs(List<String> cList) {
+ // NCService needs little memory
+ cList.add("-Xmx128m");
}
}
diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksServerProcess.java b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksServerProcess.java
index 9dec0ec..13cb445 100644
--- a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksServerProcess.java
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksServerProcess.java
@@ -29,53 +29,69 @@
import java.util.logging.Level;
import java.util.logging.Logger;
-public abstract class HyracksServerProcess {
+abstract class HyracksServerProcess {
private static final Logger LOGGER = Logger.getLogger(HyracksServerProcess.class.getName());
protected Process process;
+ protected File configFile = null;
+ protected File logFile = null;
+ protected File appHome = null;
+ protected File workingDir = null;
public void start() throws IOException {
String[] cmd = buildCommand();
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Starting command: " + Arrays.toString(cmd));
}
- process = Runtime.getRuntime().exec(cmd, null, null);
- dump(process.getInputStream());
- dump(process.getErrorStream());
+
+ ProcessBuilder pb = new ProcessBuilder(cmd);
+ pb.redirectErrorStream(true);
+ if (logFile != null) {
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.info("Logging to: " + logFile.getCanonicalPath());
+ }
+ logFile.getParentFile().mkdirs();
+ logFile.delete();
+ pb.redirectOutput(ProcessBuilder.Redirect.appendTo(logFile));
+ } else {
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.info("Logfile not set, subprocess will output to stdout");
+ }
+ }
+ pb.directory(workingDir);
+ process = pb.start();
}
- private void dump(InputStream input) {
- final int streamBufferSize = 1000;
- final Reader in = new InputStreamReader(input);
- new Thread(new Runnable() {
- public void run() {
- try {
- char[] chars = new char[streamBufferSize];
- int c;
- while ((c = in.read(chars)) != -1) {
- if (c > 0) {
- System.out.print(String.valueOf(chars, 0, c));
- }
- }
- } catch (IOException e) {
- }
- }
- }).start();
+ public void stop() {
+ process.destroy();
+ try {
+ process.waitFor();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
}
private String[] buildCommand() {
List<String> cList = new ArrayList<String>();
cList.add(getJavaCommand());
- cList.add("-Dbasedir=" + System.getProperty("basedir"));
- cList.add("-Djava.rmi.server.hostname=127.0.0.1");
+ addJvmArgs(cList);
+ cList.add("-Dapp.home=" + appHome.getAbsolutePath());
cList.add("-classpath");
cList.add(getClasspath());
cList.add(getMainClassName());
+ if (configFile != null) {
+ cList.add("-config-file");
+ cList.add(configFile.getAbsolutePath());
+ }
addCmdLineArgs(cList);
return cList.toArray(new String[cList.size()]);
}
- protected abstract void addCmdLineArgs(List<String> cList);
+ protected void addJvmArgs(List<String> cList) {
+ }
+
+ protected void addCmdLineArgs(List<String> cList) {
+ }
protected abstract String getMainClassName();
@@ -83,7 +99,7 @@
return System.getProperty("java.class.path");
}
- protected final String getJavaCommand() {
+ private final String getJavaCommand() {
return System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
}
}
diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksVirtualCluster.java b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksVirtualCluster.java
new file mode 100644
index 0000000..f08bb43
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksVirtualCluster.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.server.process;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Starts a local hyracks-based cluster (NC and CC child processes).
+ */
+public class HyracksVirtualCluster {
+ private final File appHome;
+ private final File workingDir;
+ private List<HyracksNCProcess> ncProcs = new ArrayList<>(3);
+ private HyracksCCProcess ccProc = null;
+
+ /**
+ * 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.
+ */
+ public HyracksVirtualCluster(File appHome, File workingDir) {
+ this.appHome = appHome;
+ this.workingDir = workingDir;
+ }
+
+ /**
+ * 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 void addNC(File configFile, File logFile) throws IOException {
+ HyracksNCProcess proc = new HyracksNCProcess(configFile, logFile, appHome, workingDir);
+ proc.start();
+ ncProcs.add(proc);
+ }
+
+ /**
+ * Starts the CC, initializing the cluster. Expects that any NCs referenced
+ * in the cluster configuration have already been started with addNC().
+ * @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 void start(File ccConfigFile, File logFile) throws IOException {
+ ccProc = new HyracksCCProcess(ccConfigFile, logFile, appHome, workingDir);
+ ccProc.start();
+ }
+
+ /**
+ * Stops all processes in the cluster.
+ * QQQ Someday this should probably do a graceful stop of NCs rather than
+ * killing the NCService.
+ */
+ public void stop() {
+ ccProc.stop();
+ for (HyracksNCProcess proc : ncProcs) {
+ proc.stop();
+ }
+ }
+}
+
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
index bd99c8c..9a231a0 100644
--- 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
@@ -23,6 +23,7 @@
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.lang3.StringUtils;
+import org.apache.hyracks.server.process.HyracksVirtualCluster;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.AfterClass;
@@ -30,6 +31,7 @@
import org.junit.Test;
import java.io.File;
+import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
@@ -38,23 +40,29 @@
public class NCServiceIT {
private static final String TARGET_DIR = StringUtils
- .join(new String[]{System.getProperty("basedir"), "target"}, File.separator);
+ .join(new String[] { System.getProperty("basedir"), "target" }, File.separator);
private static final String LOG_DIR = StringUtils
- .join(new String[]{TARGET_DIR, "surefire-reports"}, File.separator);
+ .join(new String[] { TARGET_DIR, "failsafe-reports" }, File.separator);
private static final String RESOURCE_DIR = StringUtils
- .join(new String[]{TARGET_DIR, "test-classes", "NCServiceIT"}, File.separator);
- private static final String APP_DIR = StringUtils
- .join(new String[]{TARGET_DIR, "appassembler", "bin"}, File.separator);
+ .join(new String[] { TARGET_DIR, "test-classes", "NCServiceIT" }, File.separator);
+ private static final String APP_HOME = StringUtils
+ .join(new String[] { TARGET_DIR, "appassembler" }, File.separator);
private static final Logger LOGGER = Logger.getLogger(NCServiceIT.class.getName());
- private static List<Process> procs = new ArrayList<>();
+
+ private static HyracksVirtualCluster cluster = null;
@BeforeClass
public static void setUp() throws Exception {
- // Start two NC Services - don't read their output as they don't terminate
- procs.add(invoke("nc-red.log", APP_DIR + File.separator + "hyracksncservice",
- "-config-file", RESOURCE_DIR + File.separator + "nc-red.conf"));
- procs.add(invoke("nc-blue.log", APP_DIR + File.separator + "hyracksncservice",
- "-config-file", RESOURCE_DIR + File.separator + "nc-blue.conf"));
+ cluster = new HyracksVirtualCluster(new File(APP_HOME), null);
+ cluster.addNC(
+ new File(RESOURCE_DIR, "nc-red.conf"),
+ new File(LOG_DIR, "nc-red.log")
+ );
+ cluster.addNC(
+ new File(RESOURCE_DIR, "nc-blue.conf"),
+ new File(LOG_DIR, "nc-blue.log")
+ );
+
try {
Thread.sleep(2000);
}
@@ -62,8 +70,11 @@
}
// Start CC
- procs.add(invoke("cc.log", APP_DIR + File.separator + "hyrackscc",
- "-config-file", RESOURCE_DIR + File.separator + "cc.conf"));
+ cluster.start(
+ new File(RESOURCE_DIR, "cc.conf"),
+ new File(LOG_DIR, "cc.log")
+ );
+
try {
Thread.sleep(10000);
}
@@ -72,11 +83,8 @@
}
@AfterClass
- public static void tearDown() throws Exception {
- for (Process p : procs) {
- p.destroy();
- p.waitFor();
- }
+ public static void tearDown() throws IOException {
+ cluster.stop();
}
private static String getHttp(String url) throws Exception {
@@ -97,18 +105,6 @@
}
}
- private static Process invoke(String logfile, String... args) throws Exception {
- ProcessBuilder pb = new ProcessBuilder(args);
- pb.redirectErrorStream(true);
- File logDir = new File(LOG_DIR);
- logDir.mkdirs();
- File log = new File(logDir, logfile);
- log.delete();
- pb.redirectOutput(ProcessBuilder.Redirect.appendTo(log));
- Process p = pb.start();
- return p;
- }
-
@Test
public void IsNodelistCorrect() throws Exception {
// Ping the nodelist HTTP API
@@ -138,5 +134,4 @@
tearDown();
}
}
-
}
diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/logging.properties b/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/logging.properties
index c888bb1..e9f84796 100644
--- a/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/logging.properties
+++ b/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/logging.properties
@@ -46,8 +46,8 @@
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
-.level= WARNING
-# .level= INFO
+# .level= WARNING
+.level= INFO
# .level= FINE
# .level = FINEST