From eca527f22bfff2af0e9b57e1c10e6f25424fe653 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Wed, 2 Feb 2005 12:52:50 +0000 Subject: [PATCH] Simplify forking of new VMs by providing a fork method in ExecuteJava git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@277571 13f79535-47bb-0310-9956-ffa450edef68 --- .../tools/ant/taskdefs/ExecuteJava.java | 80 ++++++++++++++++++- .../org/apache/tools/ant/taskdefs/Java.java | 18 +---- .../tools/ant/taskdefs/LogOutputStream.java | 20 ++++- .../tools/ant/taskdefs/LogStreamHandler.java | 18 ++++- .../apache/tools/ant/taskdefs/Redirector.java | 24 ++++-- .../tools/ant/types/CommandlineJava.java | 20 +++++ .../tools/ant/util/ConcatFileInputStream.java | 20 +++-- .../tools/ant/util/LeadPipeInputStream.java | 18 ++++- .../tools/ant/taskdefs/ExecuteJavaTest.java | 30 ++++++- 9 files changed, 205 insertions(+), 43 deletions(-) diff --git a/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java b/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java index b2b327382..60d874281 100644 --- a/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java +++ b/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2004 The Apache Software Foundation + * Copyright 2000-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +15,10 @@ * */ - package org.apache.tools.ant.taskdefs; +import java.io.File; +import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -25,11 +26,14 @@ import java.lang.reflect.Modifier; import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectComponent; import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.condition.Os; import org.apache.tools.ant.types.Commandline; import org.apache.tools.ant.types.CommandlineJava; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.Permissions; +import org.apache.tools.ant.util.JavaEnvUtils; import org.apache.tools.ant.util.TimeoutObserver; import org.apache.tools.ant.util.Watchdog; @@ -226,4 +230,76 @@ public class ExecuteJava implements Runnable, TimeoutObserver { public synchronized boolean killedProcess() { return timedOut; } + + /** + * Runs the Java command in a separate VM, this does not give you + * the full flexibility of the Java task, but may be enough for + * simple needs. + * + * @since Ant 1.6.3 + */ + public int fork(ProjectComponent pc) throws BuildException { + CommandlineJava cmdl = new CommandlineJava(); + cmdl.setClassname(javaCommand.getExecutable()); + String[] args = javaCommand.getArguments(); + for (int i = 0; i < args.length; i++) { + cmdl.createArgument().setValue(args[i]); + } + if (classpath != null) { + cmdl.createClasspath(pc.getProject()).append(classpath); + } + if (sysProperties != null) { + cmdl.addSysproperties(sysProperties); + } + + Redirector redirector = new Redirector(pc); + Execute exe + = new Execute(redirector.createHandler(), + timeout == null + ? null + : new ExecuteWatchdog(timeout.longValue())); + exe.setAntRun(pc.getProject()); + if (Os.isFamily("openvms")) { + setupCommandLineForVMS(exe, cmdl.getCommandline()); + } else { + exe.setCommandline(cmdl.getCommandline()); + } + try { + int rc = exe.execute(); + redirector.complete(); + timedOut = exe.killedProcess(); + return rc; + } catch (IOException e) { + throw new BuildException(e); + } + } + + /** + * On VMS platform, we need to create a special java options file + * containing the arguments and classpath for the java command. + * The special file is supported by the "-V" switch on the VMS JVM. + * + * @param exe + * @param command + */ + public static void setupCommandLineForVMS(Execute exe, String[] command) { + //Use the VM launcher instead of shell launcher on VMS + exe.setVMLauncher(true); + File vmsJavaOptionFile = null; + try { + String [] args = new String[command.length - 1]; + System.arraycopy(command, 1, args, 0, command.length - 1); + vmsJavaOptionFile = JavaEnvUtils.createVmsJavaOptionFile(args); + //we mark the file to be deleted on exit. + //the alternative would be to cache the filename and delete + //after execution finished, which is much better for long-lived runtimes + //though spawning complicates things... + vmsJavaOptionFile.deleteOnExit(); + String [] vmsCmd = {command[0], "-V", vmsJavaOptionFile.getPath()}; + exe.setCommandline(vmsCmd); + } catch (IOException e) { + throw new BuildException("Failed to create a temporary file for \"-V\" switch"); + } + } + } diff --git a/src/main/org/apache/tools/ant/taskdefs/Java.java b/src/main/org/apache/tools/ant/taskdefs/Java.java index 68b5f5f35..3cea9c1ae 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Java.java +++ b/src/main/org/apache/tools/ant/taskdefs/Java.java @@ -840,23 +840,7 @@ public class Java extends Task { * @param command */ private void setupCommandLineForVMS(Execute exe, String[] command) { - //Use the VM launcher instead of shell launcher on VMS - exe.setVMLauncher(true); - File vmsJavaOptionFile = null; - try { - String [] args = new String[command.length - 1]; - System.arraycopy(command, 1, args, 0, command.length - 1); - vmsJavaOptionFile = JavaEnvUtils.createVmsJavaOptionFile(args); - //we mark the file to be deleted on exit. - //the alternative would be to cache the filename and delete - //after execution finished, which is much better for long-lived runtimes - //though spawning complicates things... - vmsJavaOptionFile.deleteOnExit(); - String [] vmsCmd = {command[0], "-V", vmsJavaOptionFile.getPath()}; - exe.setCommandline(vmsCmd); - } catch (IOException e) { - throw new BuildException("Failed to create a temporary file for \"-V\" switch"); - } + ExecuteJava.setupCommandLineForVMS(exe, command); } /** diff --git a/src/main/org/apache/tools/ant/taskdefs/LogOutputStream.java b/src/main/org/apache/tools/ant/taskdefs/LogOutputStream.java index 8b78e5f2e..263ba531a 100644 --- a/src/main/org/apache/tools/ant/taskdefs/LogOutputStream.java +++ b/src/main/org/apache/tools/ant/taskdefs/LogOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2004 The Apache Software Foundation + * Copyright 2000-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectComponent; import org.apache.tools.ant.Task; @@ -48,7 +49,7 @@ public class LogOutputStream extends OutputStream { = new ByteArrayOutputStream(INTIAL_SIZE); private boolean skip = false; - private Task task; + private ProjectComponent pc; private int level = Project.MSG_INFO; /** @@ -58,7 +59,18 @@ public class LogOutputStream extends OutputStream { * @param level loglevel used to log data written to this stream. */ public LogOutputStream(Task task, int level) { - this.task = task; + this((ProjectComponent) task, level); + } + + /** + * Creates a new instance of this class. + * + * @param task the task for whom to log + * @param level loglevel used to log data written to this stream. + * @since Ant 1.6.3 + */ + public LogOutputStream(ProjectComponent pc, int level) { + this.pc = pc; this.level = level; } @@ -114,7 +126,7 @@ public class LogOutputStream extends OutputStream { * @param line the line to log. */ protected void processLine(String line, int level) { - task.log(line, level); + pc.log(line, level); } diff --git a/src/main/org/apache/tools/ant/taskdefs/LogStreamHandler.java b/src/main/org/apache/tools/ant/taskdefs/LogStreamHandler.java index 5d5074ff1..36e8738bf 100644 --- a/src/main/org/apache/tools/ant/taskdefs/LogStreamHandler.java +++ b/src/main/org/apache/tools/ant/taskdefs/LogStreamHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2000,2002,2004 The Apache Software Foundation + * Copyright 2000,2002,2004-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package org.apache.tools.ant.taskdefs; import java.io.IOException; import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.ProjectComponent; import org.apache.tools.ant.Task; /** @@ -36,8 +37,19 @@ public class LogStreamHandler extends PumpStreamHandler { * @param errlevel the loglevel used to log standard error */ public LogStreamHandler(Task task, int outlevel, int errlevel) { - super(new LogOutputStream(task, outlevel), - new LogOutputStream(task, errlevel)); + this((ProjectComponent) task, outlevel, errlevel); + } + + /** + * Creates log stream handler + * + * @param pc the project component for whom to log + * @param outlevel the loglevel used to log standard output + * @param errlevel the loglevel used to log standard error + */ + public LogStreamHandler(ProjectComponent pc, int outlevel, int errlevel) { + super(new LogOutputStream(pc, outlevel), + new LogOutputStream(pc, errlevel)); } /** diff --git a/src/main/org/apache/tools/ant/taskdefs/Redirector.java b/src/main/org/apache/tools/ant/taskdefs/Redirector.java index 4ba4a6257..54b234ee7 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Redirector.java +++ b/src/main/org/apache/tools/ant/taskdefs/Redirector.java @@ -31,8 +31,9 @@ import java.io.ByteArrayOutputStream; import java.util.Arrays; import java.util.Vector; -import org.apache.tools.ant.Task; import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectComponent; +import org.apache.tools.ant.Task; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.filters.util.ChainReaderHelper; import org.apache.tools.ant.util.StringUtils; @@ -46,7 +47,7 @@ import org.apache.tools.ant.util.KeepAliveOutputStream; /** * The Redirector class manages the setup and connection of - * input and output redirection for an Ant task. + * input and output redirection for an Ant project component. * * @since Ant 1.6 */ @@ -125,7 +126,7 @@ public class Redirector { private boolean createEmptyFiles = true; /** The task for which this redirector is working */ - private Task managingTask; + private ProjectComponent managingTask; /** The stream for output data */ private OutputStream outputStream = null; @@ -172,6 +173,17 @@ public class Redirector { * @param managingTask the task for which the redirector is to work */ public Redirector(Task managingTask) { + this((ProjectComponent) managingTask); + } + + /** + * Create a redirector instance for the given task + * + * @param managingTask the project component for which the + * redirector is to work + * @since Ant 1.6.3 + */ + public Redirector(ProjectComponent managingTask) { this.managingTask = managingTask; } @@ -498,7 +510,7 @@ public class Redirector { || !(outputEncoding.equalsIgnoreCase(inputEncoding))) { try { LeadPipeInputStream snk = new LeadPipeInputStream(); - snk.setManagingTask(managingTask); + snk.setManagingComponent(managingTask); InputStream outPumpIn = snk; @@ -527,7 +539,7 @@ public class Redirector { || !(errorEncoding.equalsIgnoreCase(inputEncoding))) { try { LeadPipeInputStream snk = new LeadPipeInputStream(); - snk.setManagingTask(managingTask); + snk.setManagingComponent(managingTask); InputStream errPumpIn = snk; @@ -563,7 +575,7 @@ public class Redirector { } catch (IOException eyeOhEx) { throw new BuildException(eyeOhEx); } - ((ConcatFileInputStream) inputStream).setManagingTask(managingTask); + ((ConcatFileInputStream) inputStream).setManagingComponent(managingTask); } else if (inputString != null) { managingTask.log("Using input \"" + inputString + "\"", Project.MSG_VERBOSE); diff --git a/src/main/org/apache/tools/ant/types/CommandlineJava.java b/src/main/org/apache/tools/ant/types/CommandlineJava.java index 81734e6fe..8e0f7b6f6 100644 --- a/src/main/org/apache/tools/ant/types/CommandlineJava.java +++ b/src/main/org/apache/tools/ant/types/CommandlineJava.java @@ -191,6 +191,16 @@ public class CommandlineJava implements Cloneable { propertySets.addElement(ps); } + /** + * add a propertyset to the total set + * @param ps the new property set + * @since Ant 1.6.3 + */ + public void addSysproperties(SysProperties ps) { + variables.addAll(ps.variables); + propertySets.addAll(ps.propertySets); + } + /** * merge all property sets into a single Properties object * @return the merged object @@ -204,6 +214,7 @@ public class CommandlineJava implements Cloneable { } return p; } + } /** @@ -246,6 +257,15 @@ public class CommandlineJava implements Cloneable { sysProperties.addSyspropertyset(sysp); } + /** + * add a set of system properties + * @param sysp a set of properties + * @since Ant 1.6.3 + */ + public void addSysproperties(SysProperties sysp) { + sysProperties.addSysproperties(sysp); + } + /** * Set the executable used to start the new JVM. * @param vm the executable to use diff --git a/src/main/org/apache/tools/ant/util/ConcatFileInputStream.java b/src/main/org/apache/tools/ant/util/ConcatFileInputStream.java index aa66ce868..f401424e7 100755 --- a/src/main/org/apache/tools/ant/util/ConcatFileInputStream.java +++ b/src/main/org/apache/tools/ant/util/ConcatFileInputStream.java @@ -23,8 +23,9 @@ import java.io.BufferedInputStream; import java.io.IOException; import java.io.FileInputStream; -import org.apache.tools.ant.Task; import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectComponent; +import org.apache.tools.ant.Task; /** * Special InputStream that will @@ -37,7 +38,7 @@ public class ConcatFileInputStream extends InputStream { private boolean eof = false; private File[] file; private InputStream currentStream; - private Task managingTask; + private ProjectComponent managingPc; /** * Construct a new ConcatFileInputStream @@ -71,7 +72,16 @@ public class ConcatFileInputStream extends InputStream { * @param task the managing Task. */ public void setManagingTask(Task task) { - this.managingTask = task; + setManagingComponent(task); + } + + /** + * Set a managing Task for + * this ConcatFileInputStream. + * @param task the managing Task. + */ + public void setManagingComponent(ProjectComponent pc) { + this.managingPc = pc; } /** @@ -80,8 +90,8 @@ public class ConcatFileInputStream extends InputStream { * @param loglevel the int logging level. */ public void log(String message, int loglevel) { - if (managingTask != null) { - managingTask.log(message, loglevel); + if (managingPc != null) { + managingPc.log(message, loglevel); } else { if (loglevel > Project.MSG_WARN) { System.out.println(message); diff --git a/src/main/org/apache/tools/ant/util/LeadPipeInputStream.java b/src/main/org/apache/tools/ant/util/LeadPipeInputStream.java index 3a0187966..b6675d438 100755 --- a/src/main/org/apache/tools/ant/util/LeadPipeInputStream.java +++ b/src/main/org/apache/tools/ant/util/LeadPipeInputStream.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; +import org.apache.tools.ant.ProjectComponent; import org.apache.tools.ant.Task; import org.apache.tools.ant.Project; @@ -29,7 +30,7 @@ import org.apache.tools.ant.Project; * when the writing Thread is no longer alive. */ public class LeadPipeInputStream extends PipedInputStream { - private Task managingTask; + private ProjectComponent managingPc; /** * Construct a new LeadPipeInputStream. @@ -73,7 +74,16 @@ public class LeadPipeInputStream extends PipedInputStream { * @param task the managing Task. */ public void setManagingTask(Task task) { - this.managingTask = task; + setManagingComponent(task); + } + + /** + * Set a managing ProjectComponent for + * this LeadPipeInputStream. + * @param pc the managing ProjectComponent. + */ + public void setManagingComponent(ProjectComponent pc) { + this.managingPc = pc; } /** @@ -82,8 +92,8 @@ public class LeadPipeInputStream extends PipedInputStream { * @param loglevel the int logging level. */ public void log(String message, int loglevel) { - if (managingTask != null) { - managingTask.log(message, loglevel); + if (managingPc != null) { + managingPc.log(message, loglevel); } else { if (loglevel > Project.MSG_WARN) { System.out.println(message); diff --git a/src/testcases/org/apache/tools/ant/taskdefs/ExecuteJavaTest.java b/src/testcases/org/apache/tools/ant/taskdefs/ExecuteJavaTest.java index ca60bb07f..8829b9e5f 100644 --- a/src/testcases/org/apache/tools/ant/taskdefs/ExecuteJavaTest.java +++ b/src/testcases/org/apache/tools/ant/taskdefs/ExecuteJavaTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002,2004 The Apache Software Foundation + * Copyright 2002,2004-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,6 +40,7 @@ public class ExecuteJavaTest extends TestCase { private ExecuteJava ej; private Project project; + private Path cp; public ExecuteJavaTest(String name) { super(name); @@ -50,7 +51,8 @@ public class ExecuteJavaTest extends TestCase { ej.setTimeout(new Long(TIME_OUT)); project = new Project(); project.setBasedir("."); - ej.setClasspath(new Path(project, getTestClassPath())); + cp = new Path(project, getTestClassPath()); + ej.setClasspath(cp); } private Commandline getCommandline(int timetorun) throws Exception { @@ -85,6 +87,30 @@ public class ExecuteJavaTest extends TestCase { } + public void testNoTimeOutForked() throws Exception { + Commandline cmd = getCommandline(TIME_OUT/2); + ej.setJavaCommand(cmd); + ej.fork(cp); + assertTrue("process should not have been killed", !ej.killedProcess()); + } + + // test that the watchdog ends the process + public void testTimeOutForked() throws Exception { + Commandline cmd = getCommandline(TIME_OUT*2); + ej.setJavaCommand(cmd); + long now = System.currentTimeMillis(); + ej.fork(cp); + long elapsed = System.currentTimeMillis() - now; + assertTrue("process should have been killed", ej.killedProcess()); + + assertTrue("elapse time of "+elapsed + +" ms is less than timeout value of "+TIME_OUT_TEST+" ms", + elapsed >= TIME_OUT_TEST); + assertTrue("elapse time of "+elapsed + +" ms is greater than run value of "+(TIME_OUT*2)+" ms", + elapsed < TIME_OUT*2); + } + /** * Dangerous method to obtain the classpath for the test. This is * severely tighted to the build.xml properties.