Java tutorial
/* * 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.apache.commons.lang.StringUtils; import org.apache.reef.runtime.common.REEFLauncher; import org.apache.reef.util.EnvironmentUtils; import org.apache.reef.util.Optional; 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; /** * Build the launch command for Java REEF processes. */ public final class JavaLaunchCommandBuilder implements LaunchCommandBuilder { private static final Logger LOG = Logger.getLogger(JavaLaunchCommandBuilder.class.getName()); private static final String DEFAULT_JAVA_PATH = "{{JAVA_HOME}}/bin/java"; private static final String[] DEFAULT_OPTIONS = { "-XX:PermSize=128m", "-XX:MaxPermSize=128m" }; private final Map<String, JVMOption> options = new HashMap<>(); private final List<String> commandPrefixList; private final Class launcherClass; private String stderrPath = null; private String stdoutPath = null; private Optional<List<String>> evaluatorConfigurationPaths = Optional.empty(); private String javaPath = null; private String classPath = null; private Boolean assertionsEnabled = null; /** * Constructor that populates default options, using the default Launcher * class {@link REEFLauncher}. */ public JavaLaunchCommandBuilder() { this(REEFLauncher.class, null); } /** * Constructor that uses the default Launcher class, {@link REEFLauncher}. * @param commandPrefixList */ public JavaLaunchCommandBuilder(final List<String> commandPrefixList) { this(REEFLauncher.class, commandPrefixList); } /** * Constructor that populates prefix and uses a custom Launcher class. */ public JavaLaunchCommandBuilder(final Class launcherClass, final List<String> commandPrefixList) { for (final String defaultOption : DEFAULT_OPTIONS) { addOption(defaultOption); } this.launcherClass = launcherClass; this.commandPrefixList = commandPrefixList; } @Override public List<String> build() { return new ArrayList<String>() { { if (commandPrefixList != null) { for (final String cmd : commandPrefixList) { add(cmd); } } if (javaPath == null || javaPath.isEmpty()) { add(DEFAULT_JAVA_PATH); } else { add(javaPath); } if (assertionsEnabled != null && assertionsEnabled || EnvironmentUtils.areAssertionsEnabled()) { addOption("-ea"); } for (final JVMOption jvmOption : options.values()) { add(jvmOption.toString()); } if (classPath != null && !classPath.isEmpty()) { add("-classpath"); add(classPath); } propagateProperties(this, true, "proc_reef"); propagateProperties(this, false, "java.util.logging.config.file", "java.util.logging.config.class"); add(launcherClass.getName()); if (evaluatorConfigurationPaths.isPresent()) { for (final String configurationPath : evaluatorConfigurationPaths.get()) { add(configurationPath); } } if (stdoutPath != null && !stdoutPath.isEmpty()) { add("1>"); add(stdoutPath); } if (stderrPath != null && !stderrPath.isEmpty()) { add("2>"); add(stderrPath); } } }; } @Override @SuppressWarnings("checkstyle:hiddenfield") public JavaLaunchCommandBuilder setMemory(final int megaBytes) { return addOption(JVMOption.parse("-Xmx" + megaBytes + "m")); } @Override public JavaLaunchCommandBuilder setConfigurationFilePaths(final List<String> configurationPaths) { this.evaluatorConfigurationPaths = Optional.of(configurationPaths); return this; } @Override public JavaLaunchCommandBuilder setStandardOut(final String standardOut) { this.stdoutPath = standardOut; return this; } @Override public JavaLaunchCommandBuilder setStandardErr(final String standardErr) { this.stderrPath = standardErr; return this; } /** * Set the path to the java executable. Will default to a heuristic search if not set. * * @param path Path to the java executable. * @return this */ public JavaLaunchCommandBuilder setJavaPath(final String path) { this.javaPath = path; return this; } public JavaLaunchCommandBuilder setClassPath(final String classPath) { this.classPath = classPath; return this; } public JavaLaunchCommandBuilder setClassPath(final Collection<String> classPathElements) { this.classPath = StringUtils.join(classPathElements, File.pathSeparatorChar); return this; } /** * 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)); } /** * Pass values of the properties specified in the propNames array as <code>-D...</code> * command line parameters. Currently used only to pass logging configuration to child JVMs processes. * * @param vargs List of command line parameters to append to. * @param copyNull create an empty parameter if the property is missing in current process. * @param propNames property names. */ private static void propagateProperties(final Collection<String> vargs, final boolean copyNull, final String... propNames) { for (final String propName : propNames) { final String propValue = System.getProperty(propName); if (propValue == null || propValue.isEmpty()) { if (copyNull) { vargs.add("-D" + propName); } } else { vargs.add(String.format("-D%s=%s", propName, propValue)); } } } 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. * * @param assertionsEnabled If true, enable assertions. * @return this */ @SuppressWarnings("checkstyle:hiddenfield") public JavaLaunchCommandBuilder enableAssertions(final boolean assertionsEnabled) { 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; } } }