PR: 5299 git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@272237 13f79535-47bb-0310-9956-ffa450edef68master
@@ -88,7 +88,7 @@ public class ExecTask extends Task { | |||
private File dir; | |||
protected boolean failOnError = false; | |||
protected boolean newEnvironment = false; | |||
private Integer timeout = null; | |||
private Long timeout = null; | |||
private Environment env = new Environment(); | |||
protected Commandline cmdl = new Commandline(); | |||
private FileOutputStream fos = null; | |||
@@ -104,7 +104,7 @@ public class ExecTask extends Task { | |||
/** | |||
* Timeout in milliseconds after which the process will be killed. | |||
*/ | |||
public void setTimeout(Integer value) { | |||
public void setTimeout(Long value) { | |||
timeout = value; | |||
} | |||
@@ -377,7 +377,7 @@ public class ExecTask extends Task { | |||
if (timeout == null) { | |||
return null; | |||
} | |||
return new ExecuteWatchdog(timeout.intValue()); | |||
return new ExecuteWatchdog(timeout.longValue()); | |||
} | |||
/** | |||
@@ -1,7 +1,7 @@ | |||
/* | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2000-2001 The Apache Software Foundation. All rights | |||
* Copyright (c) 2000-2002 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
@@ -62,21 +62,28 @@ import org.apache.tools.ant.Project; | |||
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.util.TimeoutObserver; | |||
import org.apache.tools.ant.util.Watchdog; | |||
import java.lang.reflect.InvocationTargetException; | |||
import java.lang.reflect.Method; | |||
import java.io.PrintStream; | |||
/* | |||
/** | |||
* | |||
* @author thomas.haas@softwired-inc.com | |||
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||
*/ | |||
public class ExecuteJava { | |||
public class ExecuteJava implements Runnable, TimeoutObserver { | |||
private Commandline javaCommand = null; | |||
private Path classpath = null; | |||
private CommandlineJava.SysProperties sysProperties = null; | |||
private Method main = null; | |||
private Long timeout = null; | |||
private Throwable caught = null; | |||
private boolean timedOut = false; | |||
private Thread thread = null; | |||
public void setJavaCommand(Commandline javaCommand) { | |||
this.javaCommand = javaCommand; | |||
@@ -99,9 +106,15 @@ public class ExecuteJava { | |||
public void setOutput(PrintStream out) { | |||
} | |||
/** | |||
* @since 1.19, Ant 1.5 | |||
*/ | |||
public void setTimeout(Long timeout) { | |||
this.timeout = timeout; | |||
} | |||
public void execute(Project project) throws BuildException{ | |||
final String classname = javaCommand.getExecutable(); | |||
final Object[] argument = { javaCommand.getArguments() }; | |||
AntClassLoader loader = null; | |||
try { | |||
@@ -120,21 +133,41 @@ public class ExecuteJava { | |||
target = loader.forceLoadClass(classname); | |||
AntClassLoader.initializeClass(target); | |||
} | |||
final Method main = target.getMethod("main", param); | |||
main.invoke(null, argument); | |||
main = target.getMethod("main", param); | |||
if (timeout == null) { | |||
run(); | |||
} else { | |||
thread = new Thread(this, "ExecuteJava"); | |||
Watchdog w = new Watchdog(timeout.longValue()); | |||
w.addTimeoutObserver(this); | |||
synchronized (this) { | |||
thread.start(); | |||
w.start(); | |||
try { | |||
wait(); | |||
} catch (InterruptedException e) {} | |||
if (timedOut) { | |||
project.log("Timeout: killed the sub-process", | |||
Project.MSG_WARN); | |||
} else { | |||
thread = null; | |||
w.stop(); | |||
} | |||
} | |||
} | |||
if (caught != null) { | |||
throw caught; | |||
} | |||
} catch (NullPointerException e) { | |||
throw new BuildException("Could not find main() method in " + classname); | |||
} catch (ClassNotFoundException e) { | |||
throw new BuildException("Could not find " + classname + ". Make sure you have it in your classpath"); | |||
} catch (InvocationTargetException e) { | |||
Throwable t = e.getTargetException(); | |||
if (!(t instanceof SecurityException)) { | |||
throw new BuildException(t); | |||
} | |||
else { | |||
throw (SecurityException)t; | |||
} | |||
} catch (Exception e) { | |||
} catch (SecurityException e) { | |||
throw e; | |||
} catch (Throwable e) { | |||
throw new BuildException(e); | |||
} finally { | |||
if (loader != null) { | |||
@@ -146,4 +179,43 @@ public class ExecuteJava { | |||
} | |||
} | |||
} | |||
/** | |||
* @since 1.19, Ant 1.5 | |||
*/ | |||
public void run() { | |||
final Object[] argument = { javaCommand.getArguments() }; | |||
try { | |||
main.invoke(null, argument); | |||
} catch (InvocationTargetException e) { | |||
Throwable t = e.getTargetException(); | |||
if (!(t instanceof InterruptedException)) { | |||
caught = t; | |||
} /* else { swallow, probably due to timeout } */ | |||
} catch (Throwable t) { | |||
caught = t; | |||
} finally { | |||
synchronized (this) { | |||
notifyAll(); | |||
} | |||
} | |||
} | |||
/** | |||
* @since 1.19, Ant 1.5 | |||
*/ | |||
public synchronized void timeoutOccured(Watchdog w) { | |||
if (thread != null) { | |||
timedOut = true; | |||
thread.interrupt(); | |||
} | |||
notifyAll(); | |||
} | |||
/** | |||
* @since 1.19, Ant 1.5 | |||
*/ | |||
public boolean killedProcess() { | |||
return timedOut; | |||
} | |||
} |
@@ -98,7 +98,7 @@ public class ExecuteWatchdog implements TimeoutObserver { | |||
* | |||
* @param timeout the timeout for the process in milliseconds. It must be greather than 0. | |||
*/ | |||
public ExecuteWatchdog(int timeout) { | |||
public ExecuteWatchdog(long timeout) { | |||
watchdog = new Watchdog(timeout); | |||
watchdog.addTimeoutObserver(this); | |||
} | |||
@@ -91,6 +91,7 @@ public class Java extends Task { | |||
private PrintStream outStream = null; | |||
private boolean failOnError = false; | |||
private boolean append = false; | |||
private Long timeout = null; | |||
/** | |||
* Do the execution. | |||
@@ -309,6 +310,15 @@ public class Java extends Task { | |||
this.append = append; | |||
} | |||
/** | |||
* Timeout in milliseconds after which the process will be killed. | |||
* | |||
* @since 1.37, Ant 1.5 | |||
*/ | |||
public void setTimeout(Long value) { | |||
timeout = value; | |||
} | |||
protected void handleOutput(String line) { | |||
if (outStream != null) { | |||
outStream.println(line); | |||
@@ -336,6 +346,7 @@ public class Java extends Task { | |||
exe.setJavaCommand(command.getJavaCommand()); | |||
exe.setClasspath(command.getClasspath()); | |||
exe.setSystemProperties(command.getSystemProperties()); | |||
exe.setTimeout(timeout); | |||
if (out != null) { | |||
try { | |||
outStream = | |||
@@ -366,10 +377,11 @@ public class Java extends Task { | |||
if (out == null) { | |||
exe = new Execute(new LogStreamHandler(this, Project.MSG_INFO, | |||
Project.MSG_WARN), | |||
null); | |||
createWatchdog()); | |||
} else { | |||
fos = new FileOutputStream(out.getAbsolutePath(), append); | |||
exe = new Execute(new PumpStreamHandler(fos), null); | |||
exe = new Execute(new PumpStreamHandler(fos), | |||
createWatchdog()); | |||
} | |||
exe.setAntRun(project); | |||
@@ -395,7 +407,11 @@ public class Java extends Task { | |||
exe.setCommandline(command); | |||
try { | |||
return exe.execute(); | |||
int rc = exe.execute(); | |||
if(exe.killedProcess()) { | |||
log("Timeout: killed the sub-process",Project.MSG_WARN); | |||
} | |||
return rc; | |||
} catch (IOException e) { | |||
throw new BuildException(e, location); | |||
} | |||
@@ -427,4 +443,17 @@ public class Java extends Task { | |||
public void clearArgs() { | |||
cmdl.clearJavaArgs(); | |||
} | |||
/** | |||
* Create the Watchdog to kill a runaway process. | |||
* | |||
* @since 1.37, Ant 1.5 | |||
*/ | |||
protected ExecuteWatchdog createWatchdog() throws BuildException { | |||
if (timeout == null) { | |||
return null; | |||
} | |||
return new ExecuteWatchdog(timeout.longValue()); | |||
} | |||
} |
@@ -57,7 +57,7 @@ package org.apache.tools.ant.util; | |||
/** | |||
* Interface for classes that want to be notified by Watchdog. | |||
* | |||
* @since 1.5 | |||
* @since Ant 1.5 | |||
* | |||
* @see org.apache.tools.ant.util.Watchdog | |||
* | |||
@@ -113,7 +113,7 @@ public class Watchdog implements Runnable { | |||
long now; | |||
while (!stopped && until > (now = System.currentTimeMillis())) { | |||
try { | |||
wait(until - now); | |||
wait(until - now); | |||
} catch (InterruptedException e) {} | |||
} | |||
if (!stopped) { | |||
@@ -0,0 +1,148 @@ | |||
/* | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2002 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, if | |||
* any, must include the following acknowlegement: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowlegement may appear in the software itself, | |||
* if and wherever such third-party acknowlegements normally appear. | |||
* | |||
* 4. The names "The Jakarta Project", "Ant", and "Apache Software | |||
* Foundation" must not be used to endorse or promote products derived | |||
* from this software without prior written permission. For written | |||
* permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache" | |||
* nor may "Apache" appear in their names without prior written | |||
* permission of the Apache Group. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.tools.ant.taskdefs; | |||
import org.apache.tools.ant.Project; | |||
import org.apache.tools.ant.types.Path; | |||
import org.apache.tools.ant.types.Commandline; | |||
import java.io.File; | |||
import junit.framework.TestCase; | |||
/** | |||
* Simple testcase for the ExecuteJava class - mostly stolen from | |||
* ExecuteWatchdogTest. | |||
* | |||
* @author <a href="mailto:sbailliez@imediation.com">Stephane Bailliez</a> | |||
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||
*/ | |||
public class ExecuteJavaTest extends TestCase { | |||
private final static int TIME_OUT = 5000; | |||
private final static int CLOCK_ERROR=200; | |||
private final static int TIME_OUT_TEST=TIME_OUT-CLOCK_ERROR; | |||
private ExecuteJava ej; | |||
private Project project; | |||
public ExecuteJavaTest(String name) { | |||
super(name); | |||
} | |||
protected void setUp(){ | |||
ej = new ExecuteJava(); | |||
ej.setTimeout(new Long(TIME_OUT)); | |||
project = new Project(); | |||
project.setBasedir("."); | |||
ej.setClasspath(new Path(project, getTestClassPath())); | |||
} | |||
private Commandline getCommandline(int timetorun) throws Exception { | |||
Commandline cmd = new Commandline(); | |||
cmd.setExecutable(TimeProcess.class.getName()); | |||
cmd.createArgument().setValue(String.valueOf(timetorun)); | |||
return cmd; | |||
} | |||
public void testNoTimeOut() throws Exception { | |||
Commandline cmd = getCommandline(TIME_OUT/2); | |||
ej.setJavaCommand(cmd); | |||
ej.execute(project); | |||
assertTrue("process should not have been killed", !ej.killedProcess()); | |||
} | |||
// test that the watchdog ends the process | |||
public void testTimeOut() throws Exception { | |||
Commandline cmd = getCommandline(TIME_OUT*2); | |||
ej.setJavaCommand(cmd); | |||
long now = System.currentTimeMillis(); | |||
ej.execute(project); | |||
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. | |||
*/ | |||
private static String getTestClassPath(){ | |||
String classpath = System.getProperty("build.tests"); | |||
if (classpath == null) { | |||
System.err.println("WARNING: 'build.tests' property is not available !"); | |||
classpath = System.getProperty("java.class.path"); | |||
} | |||
// JDK 1.1 needs classes.zip in -classpath argument | |||
if (Project.getJavaVersion() == Project.JAVA_1_1) { | |||
classpath += File.pathSeparator | |||
+ System.getProperty("java.home") | |||
+ File.separator + "lib" | |||
+ File.separator + "classes.zip"; | |||
} | |||
return classpath; | |||
} | |||
} |
@@ -95,13 +95,13 @@ public class ExecuteWatchdogTest extends TestCase { | |||
classpath = System.getProperty("java.class.path"); | |||
} | |||
// JDK 1.1 needs classes.zip in -classpath argument | |||
if (Project.getJavaVersion() == Project.JAVA_1_1) { | |||
classpath += File.pathSeparator | |||
+ System.getProperty("java.home") | |||
+ File.separator + "lib" | |||
+ File.separator + "classes.zip"; | |||
} | |||
// JDK 1.1 needs classes.zip in -classpath argument | |||
if (Project.getJavaVersion() == Project.JAVA_1_1) { | |||
classpath += File.pathSeparator | |||
+ System.getProperty("java.home") | |||
+ File.separator + "lib" | |||
+ File.separator + "classes.zip"; | |||
} | |||
return classpath; | |||
} | |||
@@ -197,14 +197,4 @@ public class ExecuteWatchdogTest extends TestCase { | |||
assertEquals(0, process.exitValue()); | |||
assertTrue("process should not have been killed", !watchdog.killedProcess()); | |||
} | |||
public static class TimeProcess { | |||
public static void main(String[] args) throws Exception { | |||
int time = Integer.parseInt(args[0]); | |||
if (time < 1) { | |||
throw new IllegalArgumentException("Invalid time: " + time); | |||
} | |||
Thread.sleep(time); | |||
} | |||
} | |||
} |
@@ -0,0 +1,71 @@ | |||
/* | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2000-2002 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, if | |||
* any, must include the following acknowlegement: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowlegement may appear in the software itself, | |||
* if and wherever such third-party acknowlegements normally appear. | |||
* | |||
* 4. The names "The Jakarta Project", "Ant", and "Apache Software | |||
* Foundation" must not be used to endorse or promote products derived | |||
* from this software without prior written permission. For written | |||
* permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache" | |||
* nor may "Apache" appear in their names without prior written | |||
* permission of the Apache Group. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.tools.ant.taskdefs; | |||
/** | |||
* Helper class for ExecuteWatchdogTest and ExecuteJavaTest. | |||
* | |||
* <p>Used to be an inner class of ExecuteWatchdogTest. | |||
* | |||
* @author <a href="mailto:sbailliez@imediation.com">Stephane Bailliez</a> | |||
*/ | |||
public class TimeProcess { | |||
public static void main(String[] args) throws Exception { | |||
int time = Integer.parseInt(args[0]); | |||
if (time < 1) { | |||
throw new IllegalArgumentException("Invalid time: " + time); | |||
} | |||
Thread.sleep(time); | |||
} | |||
} |