Repository: incubator-reef Updated Branches: refs/heads/master aee7bebf5 -> 1da62700f
[REEF-327] Allow users to set additional JVM options - Add method addOption / isOptionSet to JVMProcess and JavaLaunchCommandBuilder. - Revise runtimes to use isOptionSet to enforce heap slack only when no user options are set. - Parse a subset of JVM option formats, and prevent duplicates of those options. - Expand and add generics to EvaluatorProcess, EvaluatorProcessFactory, LaunchCommandBuilder interfaces. These should not break existing code. - Provide an example for adding options at HelloJVMOptionsREEF/Driver. JIRA: [REEF-327](https://issues.apache.org/jira/browse/REEF-327) Pull Request: This closes #311 Project: http://git-wip-us.apache.org/repos/asf/incubator-reef/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-reef/commit/1da62700 Tree: http://git-wip-us.apache.org/repos/asf/incubator-reef/tree/1da62700 Diff: http://git-wip-us.apache.org/repos/asf/incubator-reef/diff/1da62700 Branch: refs/heads/master Commit: 1da62700f3dc80fe684a0d7ccba99fe215fb5ca9 Parents: aee7beb Author: Brian Cho <[email protected]> Authored: Wed Jul 22 22:10:21 2015 +0900 Committer: Markus Weimer <[email protected]> Committed: Thu Jul 30 09:42:04 2015 -0700 ---------------------------------------------------------------------- .../reef/driver/evaluator/CLRProcess.java | 15 +- .../driver/evaluator/CLRProcessFactory.java | 4 +- .../reef/driver/evaluator/EvaluatorProcess.java | 12 ++ .../evaluator/EvaluatorProcessFactory.java | 4 +- .../reef/driver/evaluator/JVMProcess.java | 31 +++- .../driver/evaluator/JVMProcessFactory.java | 4 +- .../common/launch/JavaLaunchCommandBuilder.java | 123 +++++++++++++- .../common/launch/LaunchCommandBuilder.java | 5 + .../reef/driver/evaluator/JVMProcessTest.java | 50 ++++++ .../reef/driver/evaluator/package-info.java | 22 +++ .../launch/JavaLaunchCommandBuilderTest.java | 169 +++++++++++++++++++ .../runtime/common/launch/package-info.java | 22 +++ .../examples/hello/HelloJVMOptionsDriver.java | 97 +++++++++++ .../examples/hello/HelloJVMOptionsREEF.java | 88 ++++++++++ .../runtime/local/driver/ResourceManager.java | 21 ++- .../driver/MesosResourceLaunchHandler.java | 22 ++- .../yarn/driver/YARNResourceLaunchHandler.java | 29 +++- 17 files changed, 675 insertions(+), 43 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/1da62700/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/CLRProcess.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/CLRProcess.java b/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/CLRProcess.java index 92704fa..2faff66 100644 --- a/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/CLRProcess.java +++ b/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/CLRProcess.java @@ -27,6 +27,7 @@ import java.util.List; */ public final class CLRProcess implements EvaluatorProcess { private final CLRLaunchCommandBuilder commandBuilder = new CLRLaunchCommandBuilder(); + private boolean optionSet = false; /** * Instantiated via CLRProcessFactory. @@ -46,25 +47,31 @@ public final class CLRProcess implements EvaluatorProcess { } @Override - public EvaluatorProcess setMemory(final int megaBytes) { + public CLRProcess setMemory(final int megaBytes) { commandBuilder.setMemory(megaBytes); + optionSet = true; return this; } @Override - public EvaluatorProcess setConfigurationFileName(final String configurationFileName) { + public boolean isOptionSet() { + return optionSet; + } + + @Override + public CLRProcess setConfigurationFileName(final String configurationFileName) { commandBuilder.setConfigurationFileName(configurationFileName); return this; } @Override - public EvaluatorProcess setStandardOut(final String standardOut) { + public CLRProcess setStandardOut(final String standardOut) { commandBuilder.setStandardOut(standardOut); return this; } @Override - public EvaluatorProcess setStandardErr(final String standardErr) { + public CLRProcess setStandardErr(final String standardErr) { commandBuilder.setStandardErr(standardErr); return this; } http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/1da62700/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/CLRProcessFactory.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/CLRProcessFactory.java b/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/CLRProcessFactory.java index d5d3d3e..d352bcf 100644 --- a/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/CLRProcessFactory.java +++ b/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/CLRProcessFactory.java @@ -28,13 +28,13 @@ import javax.inject.Inject; */ @Private @DriverSide -public final class CLRProcessFactory implements EvaluatorProcessFactory { +public final class CLRProcessFactory implements EvaluatorProcessFactory<CLRProcess> { @Inject private CLRProcessFactory() { } @Override - public EvaluatorProcess newEvaluatorProcess() { + public CLRProcess newEvaluatorProcess() { return new CLRProcess(); } } http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/1da62700/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/EvaluatorProcess.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/EvaluatorProcess.java b/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/EvaluatorProcess.java index ff4b5b1..ef6096a 100644 --- a/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/EvaluatorProcess.java +++ b/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/EvaluatorProcess.java @@ -22,6 +22,9 @@ import java.util.List; /** * Defines the setup of an evaluator's process. + * Users can set custom options via {@link #setMemory(int)} and process-type specific methods. + * Runtimes can also set options, but should not do so if users have set options by + * checking {@link #isOptionSet()}. */ public interface EvaluatorProcess { /** @@ -43,6 +46,15 @@ public interface EvaluatorProcess { EvaluatorProcess setMemory(final int megaBytes); /** + * Check whether custom options have been set for the process. + * Options are memory given via {@link #setMemory(int)} and any other custom options + * supported by the specific type of evaluator process. + * + * @return whether custom options have been set for the process + */ + boolean isOptionSet(); + + /** * Set the name of the configuration file for the Launcher. This file is assumed to exist in the working directory of * the process launched with this command line. * http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/1da62700/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/EvaluatorProcessFactory.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/EvaluatorProcessFactory.java b/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/EvaluatorProcessFactory.java index 2f32f09..057fb34 100644 --- a/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/EvaluatorProcessFactory.java +++ b/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/EvaluatorProcessFactory.java @@ -28,6 +28,6 @@ import org.apache.reef.tang.annotations.DefaultImplementation; @Public @DriverSide @DefaultImplementation(JVMProcessFactory.class) -public interface EvaluatorProcessFactory { - EvaluatorProcess newEvaluatorProcess(); +public interface EvaluatorProcessFactory<T> { + <T extends EvaluatorProcess> T newEvaluatorProcess(); } http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/1da62700/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/JVMProcess.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/JVMProcess.java b/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/JVMProcess.java index 8ed2a14..98f4d14 100644 --- a/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/JVMProcess.java +++ b/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/JVMProcess.java @@ -26,17 +26,21 @@ import java.util.List; /** * Defines the setup of a JVM process. + * Users can set JVM options via {@link #setMemory(int)} and {@link #addOption(String)}. + * Runtimes can also set JVM options, but should not do so if users have set options by + * checking {@link #isOptionSet()}. */ public final class JVMProcess implements EvaluatorProcess { private final JavaLaunchCommandBuilder commandBuilder = new JavaLaunchCommandBuilder(); private final RuntimePathProvider runtimePathProvider; private final ClasspathProvider classpathProvider; + private boolean optionSet = false; /** * Instantiated via JVMProcessFactory. */ JVMProcess(final RuntimePathProvider runtimePathProvider, - final ClasspathProvider classpathProvider) { + final ClasspathProvider classpathProvider) { this.runtimePathProvider = runtimePathProvider; this.classpathProvider = classpathProvider; } @@ -55,26 +59,43 @@ public final class JVMProcess implements EvaluatorProcess { } @Override - public EvaluatorProcess setMemory(final int megaBytes) { + public JVMProcess setMemory(final int megaBytes) { commandBuilder.setMemory(megaBytes); + optionSet = true; return this; } @Override - public EvaluatorProcess setConfigurationFileName(final String configurationFileName) { + public boolean isOptionSet() { + return optionSet; + } + + @Override + public JVMProcess setConfigurationFileName(final String configurationFileName) { commandBuilder.setConfigurationFileName(configurationFileName); return this; } @Override - public EvaluatorProcess setStandardOut(final String standardOut) { + public JVMProcess setStandardOut(final String standardOut) { commandBuilder.setStandardOut(standardOut); return this; } @Override - public EvaluatorProcess setStandardErr(final String standardErr) { + public JVMProcess setStandardErr(final String standardErr) { commandBuilder.setStandardErr(standardErr); return this; } + + /** + * Add a JVM option. + * @param option The full option, e.g. "-XX:+PrintGCDetails", "-Xms500m" + * @return this + */ + public JVMProcess addOption(final String option) { + commandBuilder.addOption(option); + optionSet = true; + return this; + } } http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/1da62700/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/JVMProcessFactory.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/JVMProcessFactory.java b/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/JVMProcessFactory.java index e986fd5..f82d718 100644 --- a/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/JVMProcessFactory.java +++ b/lang/java/reef-common/src/main/java/org/apache/reef/driver/evaluator/JVMProcessFactory.java @@ -28,7 +28,7 @@ import javax.inject.Inject; * Factory to setup new JVM processes. */ @DriverSide -public final class JVMProcessFactory implements EvaluatorProcessFactory { +public final class JVMProcessFactory implements EvaluatorProcessFactory<JVMProcess> { private final RuntimePathProvider pathProvider; private final ClasspathProvider classpathProvider; @@ -40,7 +40,7 @@ public final class JVMProcessFactory implements EvaluatorProcessFactory { } @Override - public EvaluatorProcess newEvaluatorProcess() { + public JVMProcess newEvaluatorProcess() { return new JVMProcess(pathProvider, classpathProvider); } } http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/1da62700/lang/java/reef-common/src/main/java/org/apache/reef/runtime/common/launch/JavaLaunchCommandBuilder.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-common/src/main/java/org/apache/reef/runtime/common/launch/JavaLaunchCommandBuilder.java b/lang/java/reef-common/src/main/java/org/apache/reef/runtime/common/launch/JavaLaunchCommandBuilder.java index 24e36a6..9fdf50f 100644 --- a/lang/java/reef-common/src/main/java/org/apache/reef/runtime/common/launch/JavaLaunchCommandBuilder.java +++ b/lang/java/reef-common/src/main/java/org/apache/reef/runtime/common/launch/JavaLaunchCommandBuilder.java @@ -25,17 +25,34 @@ import org.apache.reef.util.EnvironmentUtils; import java.io.File; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public final class JavaLaunchCommandBuilder implements LaunchCommandBuilder { + private static final Logger LOG = Logger.getLogger(JavaLaunchCommandBuilder.class.getName()); + private static final String DEFAULT_JAVA_PATH = System.getenv("JAVA_HOME") + "/bin/" + "java"; + private static final String[] DEFAULT_OPTIONS = {"-XX:PermSize=128m", "-XX:MaxPermSize=128m"}; private String stderrPath = null; private String stdoutPath = null; - private int megaBytes = 0; private String evaluatorConfigurationPath = null; private String javaPath = null; private String classPath = null; private Boolean assertionsEnabled = null; + private Map<String, JVMOption> options = new HashMap<>(); + + /** + * Constructor that populates default options. + */ + public JavaLaunchCommandBuilder() { + for (final String defaultOption : DEFAULT_OPTIONS) { + addOption(defaultOption); + } + } @Override public List<String> build() { @@ -47,14 +64,13 @@ public final class JavaLaunchCommandBuilder implements LaunchCommandBuilder { add(javaPath); } - add("-XX:PermSize=128m"); - add("-XX:MaxPermSize=128m"); - // Set Xmx based on am memory size - add("-Xmx" + megaBytes + "m"); - if ((assertionsEnabled != null && assertionsEnabled) || EnvironmentUtils.areAssertionsEnabled()) { - add("-ea"); + addOption("-ea"); + } + + for (final JVMOption jvmOption : options.values()) { + add(jvmOption.toString()); } if (classPath != null && !classPath.isEmpty()) { @@ -84,8 +100,7 @@ public final class JavaLaunchCommandBuilder implements LaunchCommandBuilder { @Override @SuppressWarnings("checkstyle:hiddenfield") public JavaLaunchCommandBuilder setMemory(final int megaBytes) { - this.megaBytes = megaBytes; - return this; + return addOption(JVMOption.parse("-Xmx" + megaBytes + "m")); } @Override @@ -128,6 +143,23 @@ public final class JavaLaunchCommandBuilder implements LaunchCommandBuilder { } /** + * Add a JVM option. + * @param option The full option, e.g. "-XX:+PrintGCDetails" + * @return this + */ + public JavaLaunchCommandBuilder addOption(final String option) { + return addOption(JVMOption.parse(option)); + } + + private JavaLaunchCommandBuilder addOption(final JVMOption jvmOption) { + if (options.containsKey(jvmOption.option)) { + LOG.warning("Replaced option " + options.get(jvmOption.option) + " with " + jvmOption); + } + options.put(jvmOption.option, jvmOption); + return this; + } + + /** * Enable or disable assertions on the child process. * If not set, the setting is taken from the JVM that executes the code. * @@ -139,4 +171,77 @@ public final class JavaLaunchCommandBuilder implements LaunchCommandBuilder { this.assertionsEnabled = assertionsEnabled; return this; } + + /** + * Represents the JVM option as a option and value, combined by a separator. + * There are many different JVM option formats. This implementation only recognizes + * equals-separated and -Xm[nsx] memory options. All other option formats are + * represented with an option and empty value and separator. + */ + static final class JVMOption { + static final Pattern EQUALS = Pattern.compile("(.+)=(.+)"); + static final Pattern MEMORY = Pattern.compile("(\\-Xm[nsx])(.+)"); + + public final String option; + public final String value; + public final String separator; + + private JVMOption(final String option, final String value, + final String separator) { + this.option = option; + this.value = value; + this.separator = separator; + } + + static JVMOption parse(final String string) { + + final String trimmed = string.trim(); + + final Matcher equalsMatcher = EQUALS.matcher(trimmed); + if (equalsMatcher.matches()) { + return new JVMOption(equalsMatcher.group(1), equalsMatcher.group(2), "="); + } + + final Matcher memoryMatcher = MEMORY.matcher(trimmed); + if (memoryMatcher.matches()) { + return new JVMOption(memoryMatcher.group(1), memoryMatcher.group(2), ""); + } + + // Unknown options return the entire string as the option + return new JVMOption(trimmed, "", ""); + } + + public String toString() { + return option + separator + value; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final JVMOption jvmOption = (JVMOption) o; + + if (!option.equals(jvmOption.option)) { + return false; + } + if (!value.equals(jvmOption.value)) { + return false; + } + return separator.equals(jvmOption.separator); + + } + + @Override + public int hashCode() { + int result = option.hashCode(); + result = 31 * result + value.hashCode(); + result = 31 * result + separator.hashCode(); + return result; + } + } } http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/1da62700/lang/java/reef-common/src/main/java/org/apache/reef/runtime/common/launch/LaunchCommandBuilder.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-common/src/main/java/org/apache/reef/runtime/common/launch/LaunchCommandBuilder.java b/lang/java/reef-common/src/main/java/org/apache/reef/runtime/common/launch/LaunchCommandBuilder.java index 5726980..0768298 100644 --- a/lang/java/reef-common/src/main/java/org/apache/reef/runtime/common/launch/LaunchCommandBuilder.java +++ b/lang/java/reef-common/src/main/java/org/apache/reef/runtime/common/launch/LaunchCommandBuilder.java @@ -31,6 +31,11 @@ public interface LaunchCommandBuilder { */ List<String> build(); + /** + * Set the size of the launched process in megabytes. + * @param megaBytes + * @return this + */ LaunchCommandBuilder setMemory(final int megaBytes); /** http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/1da62700/lang/java/reef-common/src/test/java/org/apache/reef/driver/evaluator/JVMProcessTest.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-common/src/test/java/org/apache/reef/driver/evaluator/JVMProcessTest.java b/lang/java/reef-common/src/test/java/org/apache/reef/driver/evaluator/JVMProcessTest.java new file mode 100644 index 0000000..0446213 --- /dev/null +++ b/lang/java/reef-common/src/test/java/org/apache/reef/driver/evaluator/JVMProcessTest.java @@ -0,0 +1,50 @@ +/* + * 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.reef.driver.evaluator; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Tests for JVMProcess. + */ +public final class JVMProcessTest { + + @Test + public void testIsOptionSetFalse() { + final JVMProcess jvmProcess = new JVMProcess(null, null); + assertFalse("Option not set", jvmProcess.isOptionSet()); + } + + @Test + public void testIsOptionSet() { + final JVMProcess jvmProcess = new JVMProcess(null, null) + .addOption("-XX:+HeapDumpOnOutOfMemory"); + assertTrue("Option set", jvmProcess.isOptionSet()); + } + + @Test + public void testIsOptionSetOnMemory() { + final JVMProcess jvmProcess = new JVMProcess(null, null) + .setMemory(500); + assertTrue("Option set", jvmProcess.isOptionSet()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/1da62700/lang/java/reef-common/src/test/java/org/apache/reef/driver/evaluator/package-info.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-common/src/test/java/org/apache/reef/driver/evaluator/package-info.java b/lang/java/reef-common/src/test/java/org/apache/reef/driver/evaluator/package-info.java new file mode 100644 index 0000000..47f78e5 --- /dev/null +++ b/lang/java/reef-common/src/test/java/org/apache/reef/driver/evaluator/package-info.java @@ -0,0 +1,22 @@ +/* + * 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. + */ +/** + * Tests for Evaluator management on the Driver. + */ +package org.apache.reef.driver.evaluator; http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/1da62700/lang/java/reef-common/src/test/java/org/apache/reef/runtime/common/launch/JavaLaunchCommandBuilderTest.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-common/src/test/java/org/apache/reef/runtime/common/launch/JavaLaunchCommandBuilderTest.java b/lang/java/reef-common/src/test/java/org/apache/reef/runtime/common/launch/JavaLaunchCommandBuilderTest.java new file mode 100644 index 0000000..8a2f0ae --- /dev/null +++ b/lang/java/reef-common/src/test/java/org/apache/reef/runtime/common/launch/JavaLaunchCommandBuilderTest.java @@ -0,0 +1,169 @@ +/* + * 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.reef.runtime.common.launch; + +import org.junit.Test; + +import java.util.List; + +import static org.apache.reef.runtime.common.launch.JavaLaunchCommandBuilder.JVMOption; +import static org.junit.Assert.*; + +public final class JavaLaunchCommandBuilderTest { + + @Test + public void testParseEqualsOption() { + final JVMOption equals = JVMOption.parse("-XX:PermSize=128m"); + assertEquals("-XX:PermSize", equals.option); + assertEquals("128m", equals.value); + assertEquals("=", equals.separator); + assertEquals("-XX:PermSize=128m", equals.toString()); + } + + @Test + public void testParseMemoryOption() { + final JVMOption xmx = JVMOption.parse("-Xmx500m"); + assertEquals("-Xmx", xmx.option); + assertEquals("500m", xmx.value); + assertEquals("", xmx.separator); + assertEquals("-Xmx500m", xmx.toString()); + } + + @Test + public void testParseUnknownOption() { + final JVMOption unknown = JVMOption.parse("-XX:+HeapDumpOnOutOfMemory"); + assertEquals("-XX:+HeapDumpOnOutOfMemory", unknown.option); + assertEquals("", unknown.value); + assertEquals("", unknown.separator); + assertEquals("-XX:+HeapDumpOnOutOfMemory", unknown.toString()); + } + + @Test + public void testJVMOptionEquals() { + // Option parsed with the EQUALS pattern + final JVMOption permSize1 = JVMOption.parse("-XX:PermSize=128m"); + final JVMOption permSize2 = JVMOption.parse("-XX:PermSize=128m"); + assertTrue(permSize1.equals(permSize2)); + assertTrue(permSize2.equals(permSize1)); + final JVMOption permSize3 = JVMOption.parse("-XX:PermSize=64m"); + assertFalse(permSize1.equals(permSize3)); + assertFalse(permSize3.equals(permSize1)); + + // Option parsed with the MEMORY pattern + final JVMOption memory1 = JVMOption.parse("-Xmx500m"); + final JVMOption memory2 = JVMOption.parse("-Xmx500m"); + assertTrue(memory1.equals(memory2)); + assertTrue(memory2.equals(memory1)); + + final JVMOption memory3 = JVMOption.parse("-Xmx300m"); + assertFalse(memory1.equals(memory3)); + assertFalse(memory3.equals(memory1)); + + // Option with an unknown pattern + final JVMOption unknown1 = JVMOption.parse("-XX:+HeapDumpOnOutOfMemory"); + final JVMOption unknown2 = JVMOption.parse("-XX:+HeapDumpOnOutOfMemory"); + assertTrue(unknown1.equals(unknown2)); + assertTrue(unknown2.equals(unknown1)); + + final JVMOption unknown3 = JVMOption.parse("-XX:+PrintGCDetails"); + assertFalse(unknown1.equals(unknown3)); + assertFalse(unknown3.equals(unknown1)); + } + + @Test + public void testSetMemory() { + final List<String> command = newBuilder() + .setMemory(300) + .build(); + assertMemoryOption(command, "300m"); + } + + @Test + public void testOverrideMemory() { + final List<String> command = newBuilder() + .setMemory(300) + .setMemory(500) + .build(); + assertMemoryOption(command, "500m"); + } + + @Test + public void testAddMemoryOption() { + final List<String> command = newBuilder() + .addOption("-Xmx900m") + .build(); + assertMemoryOption(command, "900m"); + } + + @Test + public void testOverrideMemoryOption() { + final List<String> command = newBuilder() + .setMemory(300) + .addOption("-Xmx900m") + .build(); + assertMemoryOption(command, "900m"); + } + + private static void assertMemoryOption(final List<String> command, final String memoryValue) { + int numMemoryEntries = 0; + for (final String entry : command) { + if (entry.startsWith("-Xmx")) { + numMemoryEntries++; + assertTrue("-Xmx set to value", entry.endsWith(memoryValue)); + } + } + assertEquals(1, numMemoryEntries); + } + + @Test + public void testPermSizeDefaults() { + final List<String> command = newBuilder() + .build(); + assertPermSizeSet(command, "128m", "128m"); + } + + @Test + public void testAddPermSizeOptions() { + final List<String> command = newBuilder() + .addOption(" -XX:PermSize=256m ") + .addOption(" -XX:MaxPermSize=512m ") + .build(); + assertPermSizeSet(command, "256m", "512m"); + } + + private static void assertPermSizeSet(final List<String> command, final String permSize, final String maxPermSize) { + int numPermSizeEntries = 0; + int numMaxPermSizeEntries = 0; + for (final String entry : command) { + if (entry.startsWith("-XX:PermSize")) { + numPermSizeEntries++; + assertTrue("-XX:PermSize set to value", entry.endsWith(permSize)); + } else if (entry.startsWith("-XX:MaxPermSize")) { + numMaxPermSizeEntries++; + assertTrue("-XX:MaxPermSize set to value", entry.endsWith(maxPermSize)); + } + } + assertEquals(1, numPermSizeEntries); + assertEquals(1, numMaxPermSizeEntries); + } + + private static JavaLaunchCommandBuilder newBuilder() { + return new JavaLaunchCommandBuilder().setConfigurationFileName("mockConfigurationFileName"); + } +} http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/1da62700/lang/java/reef-common/src/test/java/org/apache/reef/runtime/common/launch/package-info.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-common/src/test/java/org/apache/reef/runtime/common/launch/package-info.java b/lang/java/reef-common/src/test/java/org/apache/reef/runtime/common/launch/package-info.java new file mode 100644 index 0000000..2b9abf1 --- /dev/null +++ b/lang/java/reef-common/src/test/java/org/apache/reef/runtime/common/launch/package-info.java @@ -0,0 +1,22 @@ +/* + * 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. + */ +/** + * Tests for common launch code between Driver and Evaluator. + */ +package org.apache.reef.runtime.common.launch; http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/1da62700/lang/java/reef-examples/src/main/java/org/apache/reef/examples/hello/HelloJVMOptionsDriver.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-examples/src/main/java/org/apache/reef/examples/hello/HelloJVMOptionsDriver.java b/lang/java/reef-examples/src/main/java/org/apache/reef/examples/hello/HelloJVMOptionsDriver.java new file mode 100644 index 0000000..4c04adf --- /dev/null +++ b/lang/java/reef-examples/src/main/java/org/apache/reef/examples/hello/HelloJVMOptionsDriver.java @@ -0,0 +1,97 @@ +/* + * 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.reef.examples.hello; + +import org.apache.reef.driver.evaluator.AllocatedEvaluator; +import org.apache.reef.driver.evaluator.EvaluatorRequest; +import org.apache.reef.driver.evaluator.EvaluatorRequestor; +import org.apache.reef.driver.evaluator.JVMProcess; +import org.apache.reef.driver.evaluator.JVMProcessFactory; +import org.apache.reef.driver.task.TaskConfiguration; +import org.apache.reef.tang.Configuration; +import org.apache.reef.tang.annotations.Unit; +import org.apache.reef.wake.EventHandler; +import org.apache.reef.wake.time.event.StartTime; + +import javax.inject.Inject; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * The Driver code for the Hello JVM Options Application. + * The additional JVM Options print out diagnostics at the Evaluator. + */ +@Unit +public final class HelloJVMOptionsDriver { + + private static final Logger LOG = Logger.getLogger(HelloJVMOptionsDriver.class.getName()); + + private final EvaluatorRequestor requestor; + private final JVMProcessFactory jvmProcessFactory; + + /** + * Job driver constructor - instantiated via TANG. + * + * @param requestor evaluator requestor object used to create new evaluator containers. + */ + @Inject + public HelloJVMOptionsDriver(final EvaluatorRequestor requestor, + final JVMProcessFactory jvmProcessFactory) { + this.requestor = requestor; + this.jvmProcessFactory = jvmProcessFactory; + LOG.log(Level.FINE, "Instantiated 'HelloDriver'"); + } + + /** + * Handles the StartTime event: Request as single Evaluator. + */ + public final class StartHandler implements EventHandler<StartTime> { + @Override + public void onNext(final StartTime startTime) { + HelloJVMOptionsDriver.this.requestor.submit(EvaluatorRequest.newBuilder() + .setNumber(1) + .setMemory(64) + .setNumberOfCores(1) + .build()); + LOG.log(Level.INFO, "Requested Evaluator."); + } + } + + /** + * Handles AllocatedEvaluator: Submit the HelloTask with additional JVM Options. + * The additional JVM Options print out diagnostics at the Evaluator. + */ + public final class EvaluatorAllocatedHandler implements EventHandler<AllocatedEvaluator> { + @Override + public void onNext(final AllocatedEvaluator allocatedEvaluator) { + LOG.log(Level.INFO, "Submitting HelloREEF task to AllocatedEvaluator: {0}", allocatedEvaluator); + final Configuration taskConfiguration = TaskConfiguration.CONF + .set(TaskConfiguration.IDENTIFIER, "HelloREEFTask") + .set(TaskConfiguration.TASK, HelloTask.class) + .build(); + final JVMProcess jvmProcess = jvmProcessFactory.newEvaluatorProcess() + .setMemory(56) + .addOption("-Xdiag") + .addOption("-XX:+PrintCommandLineFlags") + .addOption("-XX:+PrintGCDetails"); + allocatedEvaluator.setProcess(jvmProcess); + allocatedEvaluator.submitTask(taskConfiguration); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/1da62700/lang/java/reef-examples/src/main/java/org/apache/reef/examples/hello/HelloJVMOptionsREEF.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-examples/src/main/java/org/apache/reef/examples/hello/HelloJVMOptionsREEF.java b/lang/java/reef-examples/src/main/java/org/apache/reef/examples/hello/HelloJVMOptionsREEF.java new file mode 100644 index 0000000..960fd5e --- /dev/null +++ b/lang/java/reef-examples/src/main/java/org/apache/reef/examples/hello/HelloJVMOptionsREEF.java @@ -0,0 +1,88 @@ +/* + * 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.reef.examples.hello; + +import org.apache.reef.client.DriverConfiguration; +import org.apache.reef.client.DriverLauncher; +import org.apache.reef.client.LauncherStatus; +import org.apache.reef.runtime.local.client.LocalRuntimeConfiguration; +import org.apache.reef.tang.Configuration; +import org.apache.reef.tang.exceptions.BindException; +import org.apache.reef.tang.exceptions.InjectionException; +import org.apache.reef.util.EnvironmentUtils; + +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * The Client for Hello JVM Options example. + * Launches HelloJVMOptionsDriver. + */ +public final class HelloJVMOptionsREEF { + private static final Logger LOG = Logger.getLogger(HelloJVMOptionsREEF.class.getName()); + + /** + * The upper limit on the number of Evaluators that the local resourcemanager will hand out concurrently. + */ + private static final int MAX_NUMBER_OF_EVALUATORS = 2; + + /** + * Number of milliseconds to wait for the job to complete. + */ + private static final int JOB_TIMEOUT = 10000; // 10 sec. + + /** + * @return the configuration of the HelloREEF driver. + */ + public static Configuration getDriverConfiguration() { + return DriverConfiguration.CONF + .set(DriverConfiguration.GLOBAL_LIBRARIES, EnvironmentUtils.getClassLocation(HelloJVMOptionsDriver.class)) + .set(DriverConfiguration.DRIVER_IDENTIFIER, "HelloJVMOptions") + .set(DriverConfiguration.ON_DRIVER_STARTED, HelloJVMOptionsDriver.StartHandler.class) + .set(DriverConfiguration.ON_EVALUATOR_ALLOCATED, HelloJVMOptionsDriver.EvaluatorAllocatedHandler.class) + .build(); + } + + public static LauncherStatus runHelloReef(final Configuration runtimeConf, final int timeOut) + throws BindException, InjectionException { + final Configuration driverConf = getDriverConfiguration(); + return DriverLauncher.getLauncher(runtimeConf).run(driverConf, timeOut); + } + + /** + * Start Hello REEF job. Runs method runHelloReef(). + * + * @param args command line parameters. + * @throws BindException configuration error. + * @throws InjectionException configuration error. + */ + public static void main(final String[] args) throws BindException, InjectionException { + final Configuration runtimeConfiguration = LocalRuntimeConfiguration.CONF + .set(LocalRuntimeConfiguration.MAX_NUMBER_OF_EVALUATORS, MAX_NUMBER_OF_EVALUATORS) + .build(); + final LauncherStatus status = runHelloReef(runtimeConfiguration, JOB_TIMEOUT); + LOG.log(Level.INFO, "REEF job completed: {0}", status); + } + + /** + * Empty private constructor to prohibit instantiation of utility class. + */ + private HelloJVMOptionsREEF() { + } +} http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/1da62700/lang/java/reef-runtime-local/src/main/java/org/apache/reef/runtime/local/driver/ResourceManager.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-runtime-local/src/main/java/org/apache/reef/runtime/local/driver/ResourceManager.java b/lang/java/reef-runtime-local/src/main/java/org/apache/reef/runtime/local/driver/ResourceManager.java index 5c26cb9..ba498e2 100644 --- a/lang/java/reef-runtime-local/src/main/java/org/apache/reef/runtime/local/driver/ResourceManager.java +++ b/lang/java/reef-runtime-local/src/main/java/org/apache/reef/runtime/local/driver/ResourceManager.java @@ -20,6 +20,7 @@ package org.apache.reef.runtime.local.driver; import org.apache.reef.annotations.audience.DriverSide; import org.apache.reef.annotations.audience.Private; +import org.apache.reef.driver.evaluator.EvaluatorProcess; import org.apache.reef.proto.ReefServiceProtos; import org.apache.reef.runtime.common.driver.api.ResourceLaunchEvent; import org.apache.reef.runtime.common.driver.api.ResourceReleaseEvent; @@ -179,17 +180,27 @@ public final class ResourceManager { try (final LoggingScope lc = this.loggingScopeFactory .getNewLoggingScope("ResourceManager.onResourceLaunchRequest:runCommand")) { - final List<String> command = launchRequest.getProcess() - .setConfigurationFileName(this.fileNames.getEvaluatorConfigurationPath()) - .setMemory((int) (this.jvmHeapFactor * c.getMemory())) - .getCommandLine(); - + final List<String> command = getLaunchCommand(launchRequest, c.getMemory()); LOG.log(Level.FINEST, "Launching container: {0}", c); c.run(command); } } } + private List<String> getLaunchCommand(final ResourceLaunchEvent launchRequest, + final int containerMemory) { + final EvaluatorProcess process = launchRequest.getProcess() + .setConfigurationFileName(this.fileNames.getEvaluatorConfigurationPath()); + + if (process.isOptionSet()) { + return process.getCommandLine(); + } else { + return process + .setMemory((int) (this.jvmHeapFactor * containerMemory)) + .getCommandLine(); + } + } + /** /** * Checks the allocation queue for new allocations and if there are any http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/1da62700/lang/java/reef-runtime-mesos/src/main/java/org/apache/reef/runtime/mesos/driver/MesosResourceLaunchHandler.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-runtime-mesos/src/main/java/org/apache/reef/runtime/mesos/driver/MesosResourceLaunchHandler.java b/lang/java/reef-runtime-mesos/src/main/java/org/apache/reef/runtime/mesos/driver/MesosResourceLaunchHandler.java index 0ddba98..2aa413e 100644 --- a/lang/java/reef-runtime-mesos/src/main/java/org/apache/reef/runtime/mesos/driver/MesosResourceLaunchHandler.java +++ b/lang/java/reef-runtime-mesos/src/main/java/org/apache/reef/runtime/mesos/driver/MesosResourceLaunchHandler.java @@ -20,6 +20,7 @@ package org.apache.reef.runtime.mesos.driver; import org.apache.reef.annotations.audience.DriverSide; import org.apache.reef.annotations.audience.Private; +import org.apache.reef.driver.evaluator.EvaluatorProcess; import org.apache.reef.io.TempFileCreator; import org.apache.reef.io.WorkingDirectoryTempFileCreator; import org.apache.reef.runtime.common.driver.api.ResourceLaunchEvent; @@ -98,15 +99,26 @@ final class MesosResourceLaunchHandler implements ResourceLaunchHandler { FileUtil.copy(localStagingFolder, fileSystem, hdfsFolder, false, new org.apache.hadoop.conf.Configuration()); // TODO: Replace REEFExecutor with a simple launch command (we only need to launch REEFExecutor) - final List<String> command = resourceLaunchEvent.getProcess() - .setConfigurationFileName(this.fileNames.getEvaluatorConfigurationPath()) - .setMemory((int) (this.jvmHeapFactor * this.executors.getMemory(resourceLaunchEvent.getIdentifier()))) - .getCommandLine(); - + final List<String> command = + getLaunchCommand(resourceLaunchEvent, this.executors.getMemory(resourceLaunchEvent.getIdentifier())); this.executors.launchEvaluator( new EvaluatorLaunch(resourceLaunchEvent.getIdentifier(), StringUtils.join(command, ' '))); } catch (final IOException e) { throw new RuntimeException(e); } } + + private List<String> getLaunchCommand(final ResourceLaunchEvent resourceLaunchEvent, + final int executorMemory) { + final EvaluatorProcess process = resourceLaunchEvent.getProcess() + .setConfigurationFileName(this.fileNames.getEvaluatorConfigurationPath()); + + if (process.isOptionSet()) { + return process.getCommandLine(); + } else { + return process + .setMemory((int) (this.jvmHeapFactor * executorMemory)) + .getCommandLine(); + } + } } http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/1da62700/lang/java/reef-runtime-yarn/src/main/java/org/apache/reef/runtime/yarn/driver/YARNResourceLaunchHandler.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-runtime-yarn/src/main/java/org/apache/reef/runtime/yarn/driver/YARNResourceLaunchHandler.java b/lang/java/reef-runtime-yarn/src/main/java/org/apache/reef/runtime/yarn/driver/YARNResourceLaunchHandler.java index 5dd84c9..551ef34 100644 --- a/lang/java/reef-runtime-yarn/src/main/java/org/apache/reef/runtime/yarn/driver/YARNResourceLaunchHandler.java +++ b/lang/java/reef-runtime-yarn/src/main/java/org/apache/reef/runtime/yarn/driver/YARNResourceLaunchHandler.java @@ -23,6 +23,7 @@ import org.apache.hadoop.yarn.api.ApplicationConstants; import org.apache.hadoop.yarn.api.records.Container; import org.apache.hadoop.yarn.api.records.ContainerLaunchContext; import org.apache.hadoop.yarn.api.records.LocalResource; +import org.apache.reef.driver.evaluator.EvaluatorProcess; import org.apache.reef.runtime.common.driver.api.ResourceLaunchEvent; import org.apache.reef.runtime.common.driver.api.ResourceLaunchHandler; import org.apache.reef.runtime.common.files.REEFFileNames; @@ -76,15 +77,7 @@ public final class YARNResourceLaunchHandler implements ResourceLaunchHandler { final Map<String, LocalResource> localResources = this.evaluatorSetupHelper.getResources(resourceLaunchEvent); - final List<String> command = resourceLaunchEvent.getProcess() - .setConfigurationFileName(this.filenames.getEvaluatorConfigurationPath()) - .setMemory((int) (this.jvmHeapFactor * container.getResource().getMemory())) - .setStandardErr(ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/" + - this.filenames.getEvaluatorStderrFileName()) - .setStandardOut(ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/" + - this.filenames.getEvaluatorStdoutFileName()) - .getCommandLine(); - + final List<String> command = getLaunchCommand(resourceLaunchEvent, container.getResource().getMemory()); if (LOG.isLoggable(Level.FINEST)) { LOG.log(Level.FINEST, "TIME: Run ResourceLaunchProto {0} command: `{1}` with resources: `{2}`", @@ -101,4 +94,22 @@ public final class YARNResourceLaunchHandler implements ResourceLaunchHandler { throw new RuntimeException(e); } } + + private List<String> getLaunchCommand(final ResourceLaunchEvent resourceLaunchEvent, + final int containerMemory) { + final EvaluatorProcess process = resourceLaunchEvent.getProcess() + .setConfigurationFileName(this.filenames.getEvaluatorConfigurationPath()) + .setStandardErr(ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/" + + this.filenames.getEvaluatorStderrFileName()) + .setStandardOut(ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/" + + this.filenames.getEvaluatorStdoutFileName()); + + if (process.isOptionSet()) { + return process.getCommandLine(); + } else { + return process + .setMemory((int) (this.jvmHeapFactor * containerMemory)) + .getCommandLine(); + } + } }
