diff --git a/pitest-entry/src/main/java/org/pitest/coverage/execute/CoverageProcess.java b/pitest-entry/src/main/java/org/pitest/coverage/execute/CoverageProcess.java index 22a9d863c..7644446c8 100644 --- a/pitest-entry/src/main/java/org/pitest/coverage/execute/CoverageProcess.java +++ b/pitest-entry/src/main/java/org/pitest/coverage/execute/CoverageProcess.java @@ -20,7 +20,7 @@ public class CoverageProcess { public CoverageProcess(final ProcessArgs processArgs, final CoverageOptions arguments, final ServerSocket socket, final List testClasses, final Consumer handler) { - this.process = new WrappingProcess(socket.getLocalPort(), processArgs, + this.process = WrappingProcess.create(socket.getLocalPort(), processArgs, CoverageMinion.class); this.crt = new CommunicationThread(socket, new SendData(arguments, testClasses), new Receive(handler)); diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/execute/MutationTestProcess.java b/pitest-entry/src/main/java/org/pitest/mutationtest/execute/MutationTestProcess.java index 27b0d50e2..a79667c73 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/execute/MutationTestProcess.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/execute/MutationTestProcess.java @@ -23,7 +23,7 @@ public class MutationTestProcess { public MutationTestProcess(final ServerSocket socket, final ProcessArgs processArgs, final MinionArguments arguments) { - this.process = new WrappingProcess(socket.getLocalPort(), processArgs, + this.process = WrappingProcess.create(socket.getLocalPort(), processArgs, MutationTestMinion.class); this.idMap = new ConcurrentHashMap<>(); diff --git a/pitest-entry/src/main/java/org/pitest/process/Java9Process.java b/pitest-entry/src/main/java/org/pitest/process/Java9Process.java new file mode 100644 index 000000000..51ca17465 --- /dev/null +++ b/pitest-entry/src/main/java/org/pitest/process/Java9Process.java @@ -0,0 +1,152 @@ +package org.pitest.process; + +import static java.util.Arrays.asList; +import static org.pitest.functional.prelude.Prelude.or; +import java.io.File; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Predicate; + +import org.pitest.functional.FCollection; + +/** + * Process for java 9+, using file to pass + * all parameters + */ +public class Java9Process implements WrappingProcess { + + private final int port; + private final ProcessArgs processArgs; + private final Class minionClass; + private JavaProcess process; + + public Java9Process(int port, ProcessArgs args, Class minionClass) { + this.port = port; + this.processArgs = args; + this.minionClass = minionClass; + } + + public void start() throws IOException { + String[] args = { "" + this.port }; + + ProcessBuilder processBuilder = createProcessBuilder( + this.processArgs.getJavaExecutable(), + this.processArgs.getJvmArgs(), + this.minionClass, asList(args), + this.processArgs.getJavaAgentFinder(), + this.processArgs.getLaunchClassPath()); + + + configureProcessBuilder(processBuilder, this.processArgs.getWorkingDir(), + this.processArgs.getEnvironmentVariables()); + + Process process = processBuilder.start(); + this.process = new JavaProcess(process, this.processArgs.getStdout(), + this.processArgs.getStdErr()); + } + + public boolean isAlive() { + return process.isAlive(); + } + + private void configureProcessBuilder(ProcessBuilder processBuilder, + File workingDirectory, Map environmentVariables) { + processBuilder.directory(workingDirectory); + Map environment = processBuilder.environment(); + + for (final Map.Entry entry : environmentVariables.entrySet()) { + environment.put(entry.getKey(), entry.getValue()); + } + } + + public void destroy() { + this.process.destroy(); + } + + private ProcessBuilder createProcessBuilder(String javaProc, + List args, Class mainClass, List programArgs, + JavaAgent javaAgent, String classPath) { + List cmd = createLaunchArgs(javaAgent, args, mainClass, + programArgs, classPath); + + removeJacocoAgent(cmd); + + try { + // all arguments are passed via a temporary file, thereby avoiding command line length limits + Path argsFile = createArgsFile(cmd); + return new ProcessBuilder(asList(javaProc, "@" + argsFile.toFile().getAbsolutePath())); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } + private void removeJacocoAgent(List cmd) { + removeFromClassPath(cmd, line -> line.startsWith("-javaagent") && line.contains("jacoco")); + } + + private static void removeFromClassPath(List cmd, Predicate match) { + for (int i = cmd.size() - 1; i >= 0; i--) { + if (match.test(cmd.get(i))) { + cmd.remove(i); + } + } + } + + private List createLaunchArgs(JavaAgent agentJarLocator, List args, Class mainClass, + List programArgs, String classPath) { + + List cmd = new ArrayList<>(); + + cmd.add("-classpath"); + cmd.add(classPath); + + addPITJavaAgent(agentJarLocator, cmd); + + cmd.addAll(args); + + addLaunchJavaAgents(cmd); + + cmd.add(mainClass.getName()); + cmd.addAll(programArgs); + return cmd; + } + + private Path createArgsFile(List cmd) throws IOException { + Path args = Files.createTempFile("pitest-args", ".args"); + args.toFile().deleteOnExit(); + Files.write(args, cmd); + return args; + } + + private static void addPITJavaAgent(JavaAgent agentJarLocator, + List cmd) { + final Optional jarLocation = agentJarLocator.getJarLocation(); + jarLocation.ifPresent(l -> cmd.add("-javaagent:" + l)); + } + + private static void addLaunchJavaAgents(List cmd) { + RuntimeMXBean rt = ManagementFactory.getRuntimeMXBean(); + List agents = FCollection.filter(rt.getInputArguments(), + or(isJavaAgentParam(), isEnvironmentSetting())); + cmd.addAll(agents); + } + + private static Predicate isEnvironmentSetting() { + return a -> a.startsWith("-D"); + } + + private static Predicate isJavaAgentParam() { + return a -> a.toLowerCase().startsWith("-javaagent"); + } + + public JavaProcess getProcess() { + return this.process; + } +} diff --git a/pitest-entry/src/main/java/org/pitest/process/LegacyProcess.java b/pitest-entry/src/main/java/org/pitest/process/LegacyProcess.java new file mode 100644 index 000000000..066eca800 --- /dev/null +++ b/pitest-entry/src/main/java/org/pitest/process/LegacyProcess.java @@ -0,0 +1,170 @@ +package org.pitest.process; + +import static org.pitest.functional.prelude.Prelude.or; +import java.io.File; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Predicate; + +import org.pitest.functional.FCollection; +import org.pitest.util.ManifestUtils; + +/** + * Process for Java 8, pre module and params file support + */ +public class LegacyProcess implements WrappingProcess { + + private final int port; + private final ProcessArgs processArgs; + private final Class minionClass; + + private JavaProcess process; + + public LegacyProcess(int port, ProcessArgs args, Class minionClass) { + this.port = port; + this.processArgs = args; + this.minionClass = minionClass; + } + + public void start() throws IOException { + final String[] args = { "" + this.port }; + + final ProcessBuilder processBuilder = createProcessBuilder( + this.processArgs.getJavaExecutable(), + this.processArgs.getJvmArgs(), + this.minionClass, Arrays.asList(args), + this.processArgs.getJavaAgentFinder(), + this.processArgs.getLaunchClassPath()); + + + setClassPathInEnvironment(processBuilder); + + configureProcessBuilder(processBuilder, this.processArgs.getWorkingDir(), + this.processArgs.getEnvironmentVariables()); + + final Process process = processBuilder.start(); + this.process = new JavaProcess(process, this.processArgs.getStdout(), + this.processArgs.getStdErr()); + } + + public boolean isAlive() { + return process.isAlive(); + } + + + // Reportedly passing the classpath as an environment variable rather than on the command + // line increases the allowable size of the classpath, but this has not been confirmed + private void setClassPathInEnvironment(final ProcessBuilder processBuilder) { + if (!processArgs.useClasspathJar()) { + processBuilder.environment().put("CLASSPATH", this.processArgs.getLaunchClassPath()); + } + } + + private void configureProcessBuilder(ProcessBuilder processBuilder, + File workingDirectory, Map environmentVariables) { + processBuilder.directory(workingDirectory); + final Map environment = processBuilder.environment(); + + for (final Map.Entry entry : environmentVariables.entrySet()) { + environment.put(entry.getKey(), entry.getValue()); + } + } + + public void destroy() { + this.process.destroy(); + } + + private ProcessBuilder createProcessBuilder(String javaProc, + List args, Class mainClass, List programArgs, + JavaAgent javaAgent, String classPath) { + final List cmd = createLaunchArgs(javaProc, javaAgent, args, mainClass, + programArgs, classPath); + + // IBM jdk adds this, thereby breaking everything + removeClassPathProperties(cmd); + + removeJacocoAgent(cmd); + + return new ProcessBuilder(cmd); + } + + private void removeJacocoAgent(List cmd) { + removeFromClassPath(cmd, line -> line.startsWith("-javaagent") && line.contains("jacoco")); + } + + private static void removeClassPathProperties(List cmd) { + removeFromClassPath(cmd, s -> s.startsWith("-Djava.class.path")); + } + + private static void removeFromClassPath(List cmd, Predicate match) { + for (int i = cmd.size() - 1; i >= 0; i--) { + if (match.test(cmd.get(i))) { + cmd.remove(i); + } + } + } + + private List createLaunchArgs(String javaProcess, + JavaAgent agentJarLocator, List args, Class mainClass, + List programArgs, String classPath) { + + final List cmd = new ArrayList<>(); + cmd.add(javaProcess); + + createClasspathJar(classPath, cmd); + + addPITJavaAgent(agentJarLocator, cmd); + + cmd.addAll(args); + + addLaunchJavaAgents(cmd); + + cmd.add(mainClass.getName()); + cmd.addAll(programArgs); + return cmd; + } + + private void createClasspathJar(String classPath, final List cmd) { + if (this.processArgs.useClasspathJar()) { + try { + cmd.add("-classpath"); + cmd.add( + ManifestUtils.createClasspathJarFile(classPath).getAbsolutePath()); + } catch (Exception e) { + throw new RuntimeException("Unable to create jar to contain classpath", + e); + } + } + } + + private static void addPITJavaAgent(JavaAgent agentJarLocator, + List cmd) { + final Optional jarLocation = agentJarLocator.getJarLocation(); + jarLocation.ifPresent(l -> cmd.add("-javaagent:" + l)); + } + + private static void addLaunchJavaAgents(List cmd) { + final RuntimeMXBean rt = ManagementFactory.getRuntimeMXBean(); + final List agents = FCollection.filter(rt.getInputArguments(), + or(isJavaAgentParam(), isEnvironmentSetting())); + cmd.addAll(agents); + } + + private static Predicate isEnvironmentSetting() { + return a -> a.startsWith("-D"); + } + + private static Predicate isJavaAgentParam() { + return a -> a.toLowerCase().startsWith("-javaagent"); + } + + public JavaProcess getProcess() { + return this.process; + } +} diff --git a/pitest-entry/src/main/java/org/pitest/process/WrappingProcess.java b/pitest-entry/src/main/java/org/pitest/process/WrappingProcess.java index aa6aca8eb..f0ef3f11b 100644 --- a/pitest-entry/src/main/java/org/pitest/process/WrappingProcess.java +++ b/pitest-entry/src/main/java/org/pitest/process/WrappingProcess.java @@ -1,167 +1,24 @@ package org.pitest.process; -import static org.pitest.functional.prelude.Prelude.or; -import java.io.File; import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.RuntimeMXBean; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Predicate; -import org.pitest.functional.FCollection; -import org.pitest.util.ManifestUtils; +public interface WrappingProcess { -public class WrappingProcess { - - private final int port; - private final ProcessArgs processArgs; - private final Class minionClass; - - private JavaProcess process; - - public WrappingProcess(int port, ProcessArgs args, Class minionClass) { - this.port = port; - this.processArgs = args; - this.minionClass = minionClass; - } - - public void start() throws IOException { - final String[] args = { "" + this.port }; - - final ProcessBuilder processBuilder = createProcessBuilder( - this.processArgs.getJavaExecutable(), - this.processArgs.getJvmArgs(), - this.minionClass, Arrays.asList(args), - this.processArgs.getJavaAgentFinder(), - this.processArgs.getLaunchClassPath()); - - - setClassPathInEnvironment(processBuilder); - - configureProcessBuilder(processBuilder, this.processArgs.getWorkingDir(), - this.processArgs.getEnvironmentVariables()); - - final Process process = processBuilder.start(); - this.process = new JavaProcess(process, this.processArgs.getStdout(), - this.processArgs.getStdErr()); - } - - public boolean isAlive() { - return process.isAlive(); - } - - - // Reportedly passing the classpath as an environment variable rather than on the command - // line increases the allowable size of the classpath, but this has not been confirmed - private void setClassPathInEnvironment(final ProcessBuilder processBuilder) { - if (!processArgs.useClasspathJar()) { - processBuilder.environment().put("CLASSPATH", this.processArgs.getLaunchClassPath()); - } - } - - private void configureProcessBuilder(ProcessBuilder processBuilder, - File workingDirectory, Map environmentVariables) { - processBuilder.directory(workingDirectory); - final Map environment = processBuilder.environment(); - - for (final Map.Entry entry : environmentVariables.entrySet()) { - environment.put(entry.getKey(), entry.getValue()); - } - } - - public void destroy() { - this.process.destroy(); - } - - private ProcessBuilder createProcessBuilder(String javaProc, - List args, Class mainClass, List programArgs, - JavaAgent javaAgent, String classPath) { - final List cmd = createLaunchArgs(javaProc, javaAgent, args, mainClass, - programArgs, classPath); - - // IBM jdk adds this, thereby breaking everything - removeClassPathProperties(cmd); - - removeJacocoAgent(cmd); - - return new ProcessBuilder(cmd); - } - - private void removeJacocoAgent(List cmd) { - removeFromClassPath(cmd, line -> line.startsWith("-javaagent") && line.contains("jacoco")); - } - - private static void removeClassPathProperties(List cmd) { - removeFromClassPath(cmd, s -> s.startsWith("-Djava.class.path")); - } - - private static void removeFromClassPath(List cmd, Predicate match) { - for (int i = cmd.size() - 1; i >= 0; i--) { - if (match.test(cmd.get(i))) { - cmd.remove(i); - } + static WrappingProcess create(int port, ProcessArgs args, Class minionClass) { + String javaVersion = System.getProperty("java.version"); + if (javaVersion.startsWith("8") || javaVersion.startsWith("1.8")) { + return new LegacyProcess(port, args, minionClass); } - } - - private List createLaunchArgs(String javaProcess, - JavaAgent agentJarLocator, List args, Class mainClass, - List programArgs, String classPath) { - - final List cmd = new ArrayList<>(); - cmd.add(javaProcess); - - createClasspathJar(classPath, cmd); - addPITJavaAgent(agentJarLocator, cmd); - - cmd.addAll(args); - - addLaunchJavaAgents(cmd); - - cmd.add(mainClass.getName()); - cmd.addAll(programArgs); - return cmd; + return new Java9Process(port, args, minionClass); } - private void createClasspathJar(String classPath, final List cmd) { - if (this.processArgs.useClasspathJar()) { - try { - cmd.add("-classpath"); - cmd.add( - ManifestUtils.createClasspathJarFile(classPath).getAbsolutePath()); - } catch (Exception e) { - throw new RuntimeException("Unable to create jar to contain classpath", - e); - } - } - } - private static void addPITJavaAgent(JavaAgent agentJarLocator, - List cmd) { - final Optional jarLocation = agentJarLocator.getJarLocation(); - jarLocation.ifPresent(l -> cmd.add("-javaagent:" + l)); - } + void start() throws IOException; - private static void addLaunchJavaAgents(List cmd) { - final RuntimeMXBean rt = ManagementFactory.getRuntimeMXBean(); - final List agents = FCollection.filter(rt.getInputArguments(), - or(isJavaAgentParam(), isEnvironmentSetting())); - cmd.addAll(agents); - } + boolean isAlive(); - private static Predicate isEnvironmentSetting() { - return a -> a.startsWith("-D"); - } + void destroy(); - private static Predicate isJavaAgentParam() { - return a -> a.toLowerCase().startsWith("-javaagent"); - } - - public JavaProcess getProcess() { - return this.process; - } + JavaProcess getProcess(); } diff --git a/pitest-entry/src/test/java/org/pitest/process/WrappingProcessTest.java b/pitest-entry/src/test/java/org/pitest/process/WrappingProcessTest.java index 515889bea..2e43788ed 100644 --- a/pitest-entry/src/test/java/org/pitest/process/WrappingProcessTest.java +++ b/pitest-entry/src/test/java/org/pitest/process/WrappingProcessTest.java @@ -42,7 +42,7 @@ public void waitToDieShouldReturnProcessExitCode() throws IOException, .andLaunchOptions(launchOptions).andStdout(nullHandler()) .andStderr(nullHandler()); - final WrappingProcess wrappingProcess = new WrappingProcess(-1, processArgs, + final WrappingProcess wrappingProcess = WrappingProcess.create(-1, processArgs, getClass()); wrappingProcess.start(); final JavaProcess process = wrappingProcess.getProcess();