This is an automated email from the ASF dual-hosted git repository. jensdeppe pushed a commit to branch feature/GEODE-5787-refactor-dunit in repository https://gitbox.apache.org/repos/asf/geode.git
commit 3a2082e31f433b127f15fe3a3a17e927dfee863b Author: Sai Boorlagadda <sboorlaga...@pivotal.io> AuthorDate: Mon Oct 1 15:55:46 2018 -0700 wip Signed-off-by: Jens Deppe <jde...@pivotal.io> --- .../org/apache/geode/test/dunit/RMIException.java | 20 +++ .../test/dunit/standalone/ProcessManager.java | 1 + .../apache/geode/test/dunit2/ChildVMLauncher.java | 192 +++++++++++++++++++++ .../apache/geode/test/dunit2/DUnitEnvConfig.java | 27 +++ .../apache/geode/test/dunit2/DUnitEnvironment.java | 7 + .../main/java/org/apache/geode/test/dunit2/VM.java | 127 ++++++++++++++ .../geode/test/dunit2/DUnitEnvironmentTest.java | 21 +++ .../java/org/apache/geode/test/dunit2/VMTest.java | 41 +++++ 8 files changed, 436 insertions(+) diff --git a/geode-dunit/src/main/java/org/apache/geode/test/dunit/RMIException.java b/geode-dunit/src/main/java/org/apache/geode/test/dunit/RMIException.java index 1d59bcc..72005eb 100644 --- a/geode-dunit/src/main/java/org/apache/geode/test/dunit/RMIException.java +++ b/geode-dunit/src/main/java/org/apache/geode/test/dunit/RMIException.java @@ -77,6 +77,14 @@ public class RMIException extends GemFireException { this.vm = vm; } + public RMIException(org.apache.geode.test.dunit2.VM vm, String className, String methodName, Throwable cause) { + super("While invoking " + className + "." + methodName + " in " + vm, cause); + this.cause = cause; + this.className = className; + this.methodName = methodName; + //this.vm = vm; + } + /** * Creates a new <code>RMIException</code> to indicate that an exception of a given type was * thrown while invoking a given method. @@ -99,6 +107,18 @@ public class RMIException extends GemFireException { this.stackTrace = stackTrace; } + public RMIException(org.apache.geode.test.dunit2.VM vm, String className, String methodName, Throwable cause, + String stackTrace) { + super("While invoking " + className + "." + methodName + " in " + vm, + new HokeyException(cause, stackTrace)); +// this.vm = vm; + this.cause = cause; + this.className = className; + this.methodName = methodName; + // this.exceptionClassName = exceptionClassName; assignment has no effect + this.stackTrace = stackTrace; + } + /** * Returns the class name of the exception that was thrown in a remote method invocation. */ diff --git a/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/ProcessManager.java b/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/ProcessManager.java index a200f14..ee0a3c5 100755 --- a/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/ProcessManager.java +++ b/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/ProcessManager.java @@ -63,6 +63,7 @@ public class ProcessManager { public synchronized void launchVM(String version, int vmNum, boolean bouncedVM) throws IOException { + //TODO: why do we need this gaurd? May be vm.launch should handle it if (processes.containsKey(vmNum)) { throw new IllegalStateException("VM " + vmNum + " is already running."); } diff --git a/geode-dunit/src/main/java/org/apache/geode/test/dunit2/ChildVMLauncher.java b/geode-dunit/src/main/java/org/apache/geode/test/dunit2/ChildVMLauncher.java new file mode 100644 index 0000000..6a19cab --- /dev/null +++ b/geode-dunit/src/main/java/org/apache/geode/test/dunit2/ChildVMLauncher.java @@ -0,0 +1,192 @@ +package org.apache.geode.test.dunit2; + +import static org.apache.geode.distributed.ConfigurationProperties.ENABLE_NETWORK_PARTITION_DETECTION; +import static org.apache.geode.distributed.ConfigurationProperties.LOG_LEVEL; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; + +import org.apache.geode.distributed.ConfigurationProperties; +import org.apache.geode.distributed.internal.DistributionConfig; +import org.apache.geode.distributed.internal.InternalLocator; +import org.apache.geode.test.dunit.VM; +import org.apache.geode.test.dunit.standalone.ChildVM; +import org.apache.geode.test.dunit.standalone.DUnitLauncher; +import org.apache.geode.test.dunit.standalone.VersionManager; + +public class ChildVMLauncher { + + private VersionManager versionManager; + + private int debugPort = Integer.getInteger("dunit.debug.basePort", 0); + private int suspendVM = Integer.getInteger("dunit.debug.suspendVM", -100); + + public ChildVMLauncher(VersionManager versionManager) { + this.versionManager = versionManager; + } + + public Process launch(int vmId, String workingDirPath, String geodeVersion, String registryHost, int registryPort) + throws IOException { + File workingDir = new File(workingDirPath); + + if (!workingDir.exists()) { + workingDir.mkdirs(); + } + + String[] cmd = buildJavaCommand(vmId, geodeVersion, registryHost, registryPort); + System.out.println("Executing " + Arrays.toString(cmd)); + +// if (log4jConfig != null) { +// FileUtils.copyFileToDirectory(log4jConfig, workingDir); +// } + + // TODO - delete directory contents, preferably with commons io FileUtils + Process process = Runtime.getRuntime().exec(cmd, null, workingDir); + linkStreams(geodeVersion, vmId, process, process.getErrorStream(), System.err); + linkStreams(geodeVersion, vmId, process, process.getInputStream(), System.out); + return process; + } + + private String[] buildJavaCommand(int vmId, String geodeVersion, String registryHost, int registryPort) { + + String cmd = Paths.get(System.getProperty("java.home"), "bin", "java").toString(); + + String dunitClasspath = System.getProperty("java.class.path"); + String separator = File.separator; + String classPath; + if (VersionManager.isCurrentVersion(geodeVersion)) { + classPath = dunitClasspath; + } else { + // remove current-version product classes and resources from the classpath + String buildDir = separator + "geode-core" + separator + "build" + separator; + + String mainClasses = buildDir + "classes" + separator + "main"; + dunitClasspath = removeFromPath(dunitClasspath, mainClasses); + + dunitClasspath = removeFromPath(dunitClasspath, "geode-core/out/production"); + + String mainResources = buildDir + "resources" + separator + "main"; + dunitClasspath = removeFromPath(dunitClasspath, mainResources); + + String generatedResources = buildDir + "generated-resources" + separator + "main"; + dunitClasspath = removeFromPath(dunitClasspath, generatedResources); + + buildDir = separator + "geode-common" + separator + "build" + separator + "classes" + + separator + "main"; + dunitClasspath = removeFromPath(dunitClasspath, buildDir); + + buildDir = separator + "geode-json" + separator + "build" + separator + "classes" + separator + + "main"; + dunitClasspath = removeFromPath(dunitClasspath, buildDir); + + classPath = versionManager.getClasspath(geodeVersion) + File.pathSeparator + dunitClasspath; + } + + String jdkDebug = ""; + if (debugPort > 0) { + jdkDebug += ",address=" + debugPort; + debugPort++; + } + + String jdkSuspend = vmId == suspendVM ? "y" : "n"; // ignore version + ArrayList<String> cmds = new ArrayList<String>(); + cmds.add(cmd); + cmds.add("-classpath"); + String jreLib = separator + "jre" + separator + "lib" + separator; + classPath = removeFromPath(classPath, jreLib); + cmds.add(classPath); + cmds.add("-D" + DUnitLauncher.RMI_HOST_PARAM + "=" + registryHost); + cmds.add("-D" + DUnitLauncher.RMI_PORT_PARAM + "=" + registryPort); + cmds.add("-D" + DUnitLauncher.VM_NUM_PARAM + "=" + vmId); + cmds.add("-D" + DUnitLauncher.VM_VERSION_PARAM + "=" + geodeVersion); + cmds.add("-D" + DUnitLauncher.WORKSPACE_DIR_PARAM + "=" + new File(".").getAbsolutePath()); + if (vmId >= 0) { // let the locator print a banner + if (geodeVersion.equals(VersionManager.CURRENT_VERSION)) { // enable the banner for older versions + cmds.add("-D" + InternalLocator.INHIBIT_DM_BANNER + "=true"); + } + } else { + // most distributed unit tests were written under the assumption that network partition + // detection is disabled, so we turn it off in the locator. Tests for network partition + // detection should create a separate locator that has it enabled + cmds.add( + "-D" + DistributionConfig.GEMFIRE_PREFIX + ENABLE_NETWORK_PARTITION_DETECTION + "=false"); + cmds.add( + "-D" + DistributionConfig.GEMFIRE_PREFIX + "allow_old_members_to_join_for_testing=true"); + } + cmds.add("-D" + LOG_LEVEL + "=" + DUnitLauncher.logLevel); + if (DUnitLauncher.LOG4J != null) { + cmds.add("-Dlog4j.configurationFile=" + DUnitLauncher.LOG4J); + } + cmds.add("-Djava.library.path=" + System.getProperty("java.library.path")); + cmds.add("-Xrunjdwp:transport=dt_socket,server=y,suspend=" + jdkSuspend + jdkDebug); + cmds.add("-XX:+HeapDumpOnOutOfMemoryError"); + cmds.add("-Xmx512m"); + cmds.add("-D" + DistributionConfig.GEMFIRE_PREFIX + "DEFAULT_MAX_OPLOG_SIZE=10"); + cmds.add("-D" + DistributionConfig.GEMFIRE_PREFIX + "disallowMcastDefaults=true"); + cmds.add("-D" + DistributionConfig.RESTRICT_MEMBERSHIP_PORT_RANGE + "=true"); + cmds.add("-D" + DistributionConfig.GEMFIRE_PREFIX + + ConfigurationProperties.VALIDATE_SERIALIZABLE_OBJECTS + "=true"); + cmds.add("-ea"); + cmds.add("-XX:MetaspaceSize=512m"); + cmds.add("-XX:SoftRefLRUPolicyMSPerMB=1"); + cmds.add(ChildVM.class.getName()); + String[] rst = new String[cmds.size()]; + cmds.toArray(rst); + + return rst; + } + + private String removeFromPath(String classpath, String partialPath) { + String[] jars = classpath.split(File.pathSeparator); + StringBuilder sb = new StringBuilder(classpath.length()); + Boolean firstjar = true; + for (String jar : jars) { + if (!jar.contains(partialPath)) { + if (!firstjar) { + sb.append(File.pathSeparator); + } + sb.append(jar); + firstjar = false; + } + } + return sb.toString(); + } + + private void linkStreams(final String version, final int vmId, final Process process, + final InputStream in, final PrintStream out) { + final String vmName = "[" + VM.getVMName(version, vmId) + "] "; + System.out.println("linking IO streams for " + vmName); + Thread ioTransport = new Thread() { + public void run() { + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + try { + String line = reader.readLine(); + while (line != null) { + if (line.length() == 0) { + out.println(); + } else { + out.print(vmName); + out.println(line); + } + line = reader.readLine(); + } + } catch (Exception e) { + if (!process.isAlive()) { + out.println("Error transporting IO from child process"); + e.printStackTrace(out); + } + } + } + }; + + ioTransport.setDaemon(true); + ioTransport.start(); + } +} diff --git a/geode-dunit/src/main/java/org/apache/geode/test/dunit2/DUnitEnvConfig.java b/geode-dunit/src/main/java/org/apache/geode/test/dunit2/DUnitEnvConfig.java new file mode 100644 index 0000000..1a1465e --- /dev/null +++ b/geode-dunit/src/main/java/org/apache/geode/test/dunit2/DUnitEnvConfig.java @@ -0,0 +1,27 @@ +package org.apache.geode.test.dunit2; + +public class DUnitEnvConfig { + private String registryHost; + private int registryPort; + + public DUnitEnvConfig(String registryHost, int registryPort) { + this.registryHost = registryHost; + this.registryPort = registryPort; + } + + public String getRegistryHost() { + return registryHost; + } + + public void setRegistryHost(String registryHost) { + this.registryHost = registryHost; + } + + public int getRegistryPort() { + return registryPort; + } + + public void setRegistryPort(int registryPort) { + this.registryPort = registryPort; + } +} diff --git a/geode-dunit/src/main/java/org/apache/geode/test/dunit2/DUnitEnvironment.java b/geode-dunit/src/main/java/org/apache/geode/test/dunit2/DUnitEnvironment.java new file mode 100644 index 0000000..62a3e72 --- /dev/null +++ b/geode-dunit/src/main/java/org/apache/geode/test/dunit2/DUnitEnvironment.java @@ -0,0 +1,7 @@ +package org.apache.geode.test.dunit2; + +public class DUnitEnvironment { + public VM getVM(int i) { + return new VM(); + } +} diff --git a/geode-dunit/src/main/java/org/apache/geode/test/dunit2/VM.java b/geode-dunit/src/main/java/org/apache/geode/test/dunit2/VM.java new file mode 100644 index 0000000..85ffcc5 --- /dev/null +++ b/geode-dunit/src/main/java/org/apache/geode/test/dunit2/VM.java @@ -0,0 +1,127 @@ +package org.apache.geode.test.dunit2; + +import java.io.IOException; +import java.rmi.RemoteException; +import java.util.concurrent.Callable; + +import hydra.MethExecutorResult; + +import org.apache.geode.test.dunit.NamedCallable; +import org.apache.geode.test.dunit.NamedRunnable; +import org.apache.geode.test.dunit.RMIException; +import org.apache.geode.test.dunit.SerializableCallable; +import org.apache.geode.test.dunit.SerializableCallableIF; +import org.apache.geode.test.dunit.SerializableRunnable; +import org.apache.geode.test.dunit.SerializableRunnableIF; +import org.apache.geode.test.dunit.standalone.RemoteDUnitVMIF; + +public class VM { + private ChildVMLauncher launcher; + private int id = 1; + private boolean alive = false; + + private Process process; + private RemoteDUnitVMIF stub; + + public VM() { + + } + public VM(DUnitEnvConfig config, ChildVMLauncher launcher) { + this.launcher = launcher; + } + + public int getId() { + return id; + } + + public VM launch() throws IOException { + alive = true; + process = launcher.launch(1, "0", "0", "", 0); + return this; + } + + public void stop() { + alive = false; + } + + public boolean isAlive() { + return alive; + } + + /** + * Invokes the {@code run} method of a {@link Runnable} in this {@code VM}. Recall that + * {@code run} takes no arguments and has no return value. + * + * @param runnable The {@code Runnable} to be run + * @param name The name of the {@code Runnable}, which will be logged in DUnit output + * + * @see SerializableRunnable + */ + public void invoke(final String name, final SerializableRunnableIF runnable) { + invoke(new NamedRunnable(name, runnable), "run"); + } + + /** + * Invokes the {@code run} method of a {@link Runnable} in this {@code VM}. Recall that + * {@code run} takes no arguments and has no return value. + * + * @param runnable The {@code Runnable} to be run + * + * @see SerializableRunnable + */ + public void invoke(final SerializableRunnableIF runnable) { + invoke(runnable, "run"); + } + + /** + * Invokes the {@code call} method of a {@link Callable} in this {@code VM}. + * + * @param callable The {@code Callable} to be run + * @param name The name of the {@code Callable}, which will be logged in DUnit output + * + * @see SerializableCallable + */ + public <V> V invoke(final String name, final SerializableCallableIF<V> callable) { + return invoke(new NamedCallable<>(name, callable), "call"); + } + + /** + * Invokes the {@code call} method of a {@link Callable} in this {@code VM}. + * + * @param callable The {@code Callable} to be run + * + * @see SerializableCallable + */ + public <V> V invoke(final SerializableCallableIF<V> callable) { + return invoke(callable, "call"); + } + + private <V> V invoke(final Object targetObject, final String methodName) { + return invoke(targetObject, methodName, new Object[0]); + } + + private <V> V invoke(final Object targetObject, final String methodName, final Object[] args) { + if (!alive) { + throw new RMIException(this, targetObject.getClass().getName(), methodName, + new IllegalStateException("VM not available: " + this)); + } + + try { + MethExecutorResult result; + if (args == null) { + result = stub.executeMethodOnObject(targetObject, methodName); + } else { + result = stub.executeMethodOnObject(targetObject, methodName, args); + } + if (!result.exceptionOccurred()) { + return (V) result.getResult(); + + } else { + throw new RMIException(this, targetObject.getClass().getName(), methodName, + result.getException(), result.getStackTrace()); + } + } catch (RemoteException exception) { + throw new RMIException(this, targetObject.getClass().getName(), methodName, exception); + } + } +} diff --git a/geode-dunit/src/test/java/org/apache/geode/test/dunit2/DUnitEnvironmentTest.java b/geode-dunit/src/test/java/org/apache/geode/test/dunit2/DUnitEnvironmentTest.java new file mode 100644 index 0000000..a5ee344 --- /dev/null +++ b/geode-dunit/src/test/java/org/apache/geode/test/dunit2/DUnitEnvironmentTest.java @@ -0,0 +1,21 @@ +package org.apache.geode.test.dunit2; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DUnitEnvironmentTest { + + @Test + public void getVMGetsRequestedVM() { + DUnitEnvironment environment = new DUnitEnvironment(); + assertThat(environment.getVM(1)).isNotNull(); + assertThat(environment.getVM(1).getId()).isEqualTo(1); + } + + @Test + public void launchVMRunsJavaProcess() { + + } + +} \ No newline at end of file diff --git a/geode-dunit/src/test/java/org/apache/geode/test/dunit2/VMTest.java b/geode-dunit/src/test/java/org/apache/geode/test/dunit2/VMTest.java new file mode 100644 index 0000000..47e8fac --- /dev/null +++ b/geode-dunit/src/test/java/org/apache/geode/test/dunit2/VMTest.java @@ -0,0 +1,41 @@ +package org.apache.geode.test.dunit2; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.SystemOutRule; + +public class VMTest { + + @Rule + public SystemOutRule systemOutRule = new SystemOutRule(); + @Test + public void testLaunchVM() throws IOException { + VM newVM = new VM(); + VM launchedVM = new VM().launch(); + + assertThat(launchedVM.isAlive()).isTrue(); + assertThat(newVM.isAlive()).isFalse(); + } + + @Test + public void testStopVMStopsIt() throws IOException { + VM runningVM = new VM().launch(); + runningVM.stop(); + assertThat(runningVM.isAlive()).isFalse(); + } + + @Test + public void testInvokeReturnsValueFromCallable() throws IOException { + final String message = "Hello! World"; + + VM vm = new VM().launch(); + String result = vm.invoke(() -> message); + + assertThat(result).isEqualTo(message); + } + +} \ No newline at end of file