PR: 5907 git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@274992 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -530,6 +530,9 @@ Other changes: | |||||
| * <exec> will now have a new attribute spawn (default false). | * <exec> will now have a new attribute spawn (default false). | ||||
| If set to true, the process will be spawned. Bugzilla Report 5907. | If set to true, the process will be spawned. Bugzilla Report 5907. | ||||
| * <java> will now have a new attribute spawn (default false). | |||||
| If set to true, the process will be spawned. Bugzilla Report 5907. | |||||
| * <parallel> now supports a timeout which can be used to recover | * <parallel> now supports a timeout which can be used to recover | ||||
| from deadlocks, etc in the parallel threads. <parallel> also | from deadlocks, etc in the parallel threads. <parallel> also | ||||
| now supports a <daemons> nested element. This can be used to | now supports a <daemons> nested element. This can be used to | ||||
| @@ -56,6 +56,14 @@ JVM. | |||||
| (disabled by default)</td> | (disabled by default)</td> | ||||
| <td align="center" valign="top">No</td> | <td align="center" valign="top">No</td> | ||||
| </tr> | </tr> | ||||
| <tr> | |||||
| <td valign="top">spawn</td> | |||||
| <td valign="top">if enabled allows to start a process which will outlive ant.<br/> | |||||
| Requires fork=true, and not compatible | |||||
| with timeout, input, output, error, result attributes.<br/> | |||||
| (disabled by default)</td> | |||||
| <td align="center" valign="top">No</td> | |||||
| </tr> | |||||
| <tr> | <tr> | ||||
| <td valign="top">jvm</td> | <td valign="top">jvm</td> | ||||
| <td valign="top">the command used to invoke the Java Virtual Machine, | <td valign="top">the command used to invoke the Java Virtual Machine, | ||||
| @@ -2,41 +2,44 @@ | |||||
| <project name="java-test" basedir="." default="foo"> | <project name="java-test" basedir="." default="foo"> | ||||
| <property name="app" | |||||
| <property name="app" | |||||
| value="org.apache.tools.ant.taskdefs.JavaTest$$EntryPoint" /> | value="org.apache.tools.ant.taskdefs.JavaTest$$EntryPoint" /> | ||||
| <property name="app2" | |||||
| <property name="app2" | |||||
| value="org.apache.tools.ant.taskdefs.JavaTest$$ExceptingEntryPoint" /> | value="org.apache.tools.ant.taskdefs.JavaTest$$ExceptingEntryPoint" /> | ||||
| <property name="spawnapp" | |||||
| value="org.apache.tools.ant.taskdefs.JavaTest$$SpawnEntryPoint" /> | |||||
| <path id="test.classpath"> | <path id="test.classpath"> | ||||
| <pathelement location="${build.tests}"/> | <pathelement location="${build.tests}"/> | ||||
| </path> | </path> | ||||
| <target name="testNoJarNoClassname"> | <target name="testNoJarNoClassname"> | ||||
| <java/> | <java/> | ||||
| </target> | </target> | ||||
| <target name="testJarNoFork"> | <target name="testJarNoFork"> | ||||
| <java jar="test.jar" fork="false"/> | <java jar="test.jar" fork="false"/> | ||||
| </target> | |||||
| </target> | |||||
| <target name="testJarAndClassName"> | <target name="testJarAndClassName"> | ||||
| <java jar="test.jar" classname="${app}" /> | <java jar="test.jar" classname="${app}" /> | ||||
| </target> | </target> | ||||
| <target name="testClassnameAndJar"> | <target name="testClassnameAndJar"> | ||||
| <java classname="${app}" jar="test.jar" /> | <java classname="${app}" jar="test.jar" /> | ||||
| </target> | |||||
| </target> | |||||
| <target name="testRun"> | <target name="testRun"> | ||||
| <fail unless="tests-classpath.value" /> | |||||
| <fail unless="tests-classpath.value" /> | |||||
| <java classname="${app}" | <java classname="${app}" | ||||
| classpath="${tests-classpath.value}"/> | classpath="${tests-classpath.value}"/> | ||||
| </target> | </target> | ||||
| <target name="testRunFail"> | <target name="testRunFail"> | ||||
| <java classname="${app}" | |||||
| <java classname="${app}" | |||||
| classpath="${tests-classpath.value}" | classpath="${tests-classpath.value}" | ||||
| > | > | ||||
| <arg value="-1"/> | <arg value="-1"/> | ||||
| @@ -88,7 +91,7 @@ | |||||
| fork="true"> | fork="true"> | ||||
| </java> | </java> | ||||
| </target> | </target> | ||||
| <target name="testResultPropertyZero"> | <target name="testResultPropertyZero"> | ||||
| <java classname="${app}" | <java classname="${app}" | ||||
| classpath="${tests-classpath.value}" | classpath="${tests-classpath.value}" | ||||
| @@ -97,7 +100,7 @@ | |||||
| </java> | </java> | ||||
| <echo message="exitcode = ${exitcode}"/> | <echo message="exitcode = ${exitcode}"/> | ||||
| </target> | </target> | ||||
| <target name="testResultPropertyNonZero"> | <target name="testResultPropertyNonZero"> | ||||
| <java classname="${app}" | <java classname="${app}" | ||||
| classpath="${tests-classpath.value}" | classpath="${tests-classpath.value}" | ||||
| @@ -109,6 +112,13 @@ | |||||
| </java> | </java> | ||||
| <echo message="exitcode = ${exitcode}"/> | <echo message="exitcode = ${exitcode}"/> | ||||
| </target> | </target> | ||||
| <target name="testSpawn"> | |||||
| <java classname="${spawnapp}" fork="true" spawn="true" classpath="${tests-classpath.value}"> | |||||
| <arg value="${timeToWait}"/> | |||||
| <arg value="${logFile}" /> | |||||
| </java> | |||||
| </target> | |||||
| <target name="foo" /> | <target name="foo" /> | ||||
| </project> | </project> | ||||
| @@ -94,6 +94,8 @@ public class Java extends Task { | |||||
| private Long timeout = null; | private Long timeout = null; | ||||
| private Redirector redirector = new Redirector(this); | private Redirector redirector = new Redirector(this); | ||||
| private String resultProperty; | private String resultProperty; | ||||
| private boolean spawn = false; | |||||
| private boolean incompatibleWithSpawn = false; | |||||
| /** | /** | ||||
| * Do the execution. | * Do the execution. | ||||
| * @throws BuildException if failOnError is set to true and the application | * @throws BuildException if failOnError is set to true and the application | ||||
| @@ -136,7 +138,17 @@ public class Java extends Task { | |||||
| throw new BuildException("Cannot execute a jar in non-forked mode." | throw new BuildException("Cannot execute a jar in non-forked mode." | ||||
| + " Please set fork='true'. "); | + " Please set fork='true'. "); | ||||
| } | } | ||||
| if (spawn && !fork) { | |||||
| throw new BuildException("Cannot spawn a java process in non-forked mode." | |||||
| + " Please set fork='true'. "); | |||||
| } | |||||
| if (spawn && incompatibleWithSpawn) { | |||||
| getProject().log("spawn does not allow attributes related to input, " | |||||
| + "output, error, result", Project.MSG_ERR); | |||||
| getProject().log("spawn does not also not allow timeout", Project.MSG_ERR); | |||||
| throw new BuildException("You have used an attribute which is " | |||||
| + "not compatible with spawn"); | |||||
| } | |||||
| if (fork) { | if (fork) { | ||||
| log(cmdl.describeCommand(), Project.MSG_VERBOSE); | log(cmdl.describeCommand(), Project.MSG_VERBOSE); | ||||
| } else { | } else { | ||||
| @@ -165,7 +177,12 @@ public class Java extends Task { | |||||
| try { | try { | ||||
| if (fork) { | if (fork) { | ||||
| return run(cmdl.getCommandline()); | |||||
| if (!spawn) { | |||||
| return run(cmdl.getCommandline()); | |||||
| } else { | |||||
| spawn(cmdl.getCommandline()); | |||||
| return 0; | |||||
| } | |||||
| } else { | } else { | ||||
| try { | try { | ||||
| run(cmdl); | run(cmdl); | ||||
| @@ -191,6 +208,16 @@ public class Java extends Task { | |||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * set whether or not you want the process to be spawned | |||||
| * default is not spawned | |||||
| * @param spawn if true you do not want ant to wait for the end of the process | |||||
| * @since ant 1.6 | |||||
| */ | |||||
| public void setSpawn(boolean spawn) { | |||||
| this.spawn = spawn; | |||||
| } | |||||
| /** | /** | ||||
| * Set the classpath to be used when running the Java class | * Set the classpath to be used when running the Java class | ||||
| * | * | ||||
| @@ -373,6 +400,7 @@ public class Java extends Task { | |||||
| */ | */ | ||||
| public void setFailonerror(boolean fail) { | public void setFailonerror(boolean fail) { | ||||
| failOnError = fail; | failOnError = fail; | ||||
| incompatibleWithSpawn = true; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -392,6 +420,7 @@ public class Java extends Task { | |||||
| */ | */ | ||||
| public void setOutput(File out) { | public void setOutput(File out) { | ||||
| redirector.setOutput(out); | redirector.setOutput(out); | ||||
| incompatibleWithSpawn = true; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -401,6 +430,7 @@ public class Java extends Task { | |||||
| */ | */ | ||||
| public void setInput(File input) { | public void setInput(File input) { | ||||
| redirector.setInput(input); | redirector.setInput(input); | ||||
| incompatibleWithSpawn = true; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -410,6 +440,7 @@ public class Java extends Task { | |||||
| */ | */ | ||||
| public void setInputString(String inputString) { | public void setInputString(String inputString) { | ||||
| redirector.setInputString(inputString); | redirector.setInputString(inputString); | ||||
| incompatibleWithSpawn = true; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -422,6 +453,7 @@ public class Java extends Task { | |||||
| */ | */ | ||||
| public void setLogError(boolean logError) { | public void setLogError(boolean logError) { | ||||
| redirector.setLogError(logError); | redirector.setLogError(logError); | ||||
| incompatibleWithSpawn = true; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -433,6 +465,7 @@ public class Java extends Task { | |||||
| */ | */ | ||||
| public void setError(File error) { | public void setError(File error) { | ||||
| redirector.setError(error); | redirector.setError(error); | ||||
| incompatibleWithSpawn = true; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -444,6 +477,7 @@ public class Java extends Task { | |||||
| */ | */ | ||||
| public void setOutputproperty(String outputProp) { | public void setOutputproperty(String outputProp) { | ||||
| redirector.setOutputProperty(outputProp); | redirector.setOutputProperty(outputProp); | ||||
| incompatibleWithSpawn = true; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -456,6 +490,7 @@ public class Java extends Task { | |||||
| */ | */ | ||||
| public void setErrorProperty(String errorProperty) { | public void setErrorProperty(String errorProperty) { | ||||
| redirector.setErrorProperty(errorProperty); | redirector.setErrorProperty(errorProperty); | ||||
| incompatibleWithSpawn = true; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -510,6 +545,7 @@ public class Java extends Task { | |||||
| */ | */ | ||||
| public void setAppend(boolean append) { | public void setAppend(boolean append) { | ||||
| this.append = append; | this.append = append; | ||||
| incompatibleWithSpawn = true; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -521,6 +557,7 @@ public class Java extends Task { | |||||
| */ | */ | ||||
| public void setTimeout(Long value) { | public void setTimeout(Long value) { | ||||
| timeout = value; | timeout = value; | ||||
| incompatibleWithSpawn = true; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -665,6 +702,42 @@ public class Java extends Task { | |||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Executes the given classname with the given arguments in a separate VM. | |||||
| */ | |||||
| private void spawn(String[] command) throws BuildException { | |||||
| Execute exe | |||||
| = new Execute(); | |||||
| exe.setAntRun(getProject()); | |||||
| if (dir == null) { | |||||
| dir = getProject().getBaseDir(); | |||||
| } else if (!dir.exists() || !dir.isDirectory()) { | |||||
| throw new BuildException(dir.getAbsolutePath() | |||||
| + " is not a valid directory", | |||||
| getLocation()); | |||||
| } | |||||
| exe.setWorkingDirectory(dir); | |||||
| String[] environment = env.getVariables(); | |||||
| if (environment != null) { | |||||
| for (int i = 0; i < environment.length; i++) { | |||||
| log("Setting environment variable: " + environment[i], | |||||
| Project.MSG_VERBOSE); | |||||
| } | |||||
| } | |||||
| exe.setNewenvironment(newEnvironment); | |||||
| exe.setEnvironment(environment); | |||||
| exe.setCommandline(command); | |||||
| try { | |||||
| exe.spawn(); | |||||
| } catch (IOException e) { | |||||
| throw new BuildException(e, getLocation()); | |||||
| } | |||||
| } | |||||
| /** | /** | ||||
| * Executes the given classname with the given arguments as it | * Executes the given classname with the given arguments as it | ||||
| * was a command line application. | * was a command line application. | ||||
| @@ -57,6 +57,7 @@ package org.apache.tools.ant.taskdefs; | |||||
| import junit.framework.*; | import junit.framework.*; | ||||
| import java.io.*; | import java.io.*; | ||||
| import org.apache.tools.ant.*; | import org.apache.tools.ant.*; | ||||
| import org.apache.tools.ant.util.FileUtils; | |||||
| /** | /** | ||||
| * stress out java task | * stress out java task | ||||
| @@ -65,7 +66,8 @@ import org.apache.tools.ant.*; | |||||
| * @author <a href="mailto:donal@savvion.com">Donal Quinlan</a> | * @author <a href="mailto:donal@savvion.com">Donal Quinlan</a> | ||||
| * */ | * */ | ||||
| public class JavaTest extends BuildFileTest { | public class JavaTest extends BuildFileTest { | ||||
| private static final int TIME_TO_WAIT = 4; | |||||
| private boolean runFatalTests=false; | private boolean runFatalTests=false; | ||||
| public JavaTest(String name) { | public JavaTest(String name) { | ||||
| @@ -176,7 +178,23 @@ public class JavaTest extends BuildFileTest { | |||||
| executeTarget("testResultPropertyNonZero"); | executeTarget("testResultPropertyNonZero"); | ||||
| assertEquals("-1",project.getProperty("exitcode")); | assertEquals("-1",project.getProperty("exitcode")); | ||||
| } | } | ||||
| public void testSpawn() { | |||||
| FileUtils fileutils = FileUtils.newFileUtils(); | |||||
| File logFile = fileutils.createTempFile("spawn","log", project.getBaseDir()); | |||||
| // this is guaranteed by FileUtils#createTempFile | |||||
| assertTrue("log file not existing", !logFile.exists()); | |||||
| project.setProperty("logFile", logFile.getAbsolutePath()); | |||||
| project.setProperty("timeToWait", Long.toString(TIME_TO_WAIT)); | |||||
| project.executeTarget("testSpawn"); | |||||
| try { | |||||
| Thread.sleep(TIME_TO_WAIT * 1000 + 400); | |||||
| } catch (Exception ex) { | |||||
| System.out.println("my sleep was interrupted"); | |||||
| } | |||||
| assertTrue("log file exists", logFile.exists()); | |||||
| } | |||||
| /** | /** | ||||
| * entry point class with no dependencies other | * entry point class with no dependencies other | ||||
| * than normal JRE runtime | * than normal JRE runtime | ||||
| @@ -225,4 +243,36 @@ public class JavaTest extends BuildFileTest { | |||||
| throw new NullPointerException("Exception raised inside called program"); | throw new NullPointerException("Exception raised inside called program"); | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * test class for spawn | |||||
| */ | |||||
| public static class SpawnEntryPoint { | |||||
| public static void main(String [] argv) { | |||||
| int sleepTime = 10; | |||||
| String logFile = "spawn.log"; | |||||
| if (argv.length >= 1) { | |||||
| sleepTime = Integer.parseInt(argv[0]); | |||||
| } | |||||
| if (argv.length >= 2) | |||||
| { | |||||
| logFile = argv[1]; | |||||
| } | |||||
| OutputStreamWriter out = null; | |||||
| try { | |||||
| Thread.sleep(sleepTime * 1000); | |||||
| } catch (InterruptedException ex) { | |||||
| System.out.println("my sleep was interrupted"); | |||||
| } | |||||
| try { | |||||
| File dest = new File(logFile); | |||||
| FileOutputStream fos = new FileOutputStream(dest); | |||||
| out = new OutputStreamWriter(fos); | |||||
| out.write("bye bye\n"); | |||||
| } catch (Exception ex) {} | |||||
| finally { | |||||
| try {out.close();} catch (IOException ioe) {}} | |||||
| } | |||||
| } | |||||
| } | } | ||||