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) {}} | |||||
} | |||||
} | |||||
} | } |