Browse Source

timeout support for <java>

PR: 5299


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@272237 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 23 years ago
parent
commit
d463563bef
9 changed files with 351 additions and 41 deletions
  1. +3
    -3
      src/main/org/apache/tools/ant/taskdefs/ExecTask.java
  2. +87
    -15
      src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java
  3. +1
    -1
      src/main/org/apache/tools/ant/taskdefs/ExecuteWatchdog.java
  4. +32
    -3
      src/main/org/apache/tools/ant/taskdefs/Java.java
  5. +1
    -1
      src/main/org/apache/tools/ant/util/TimeoutObserver.java
  6. +1
    -1
      src/main/org/apache/tools/ant/util/Watchdog.java
  7. +148
    -0
      src/testcases/org/apache/tools/ant/taskdefs/ExecuteJavaTest.java
  8. +7
    -17
      src/testcases/org/apache/tools/ant/taskdefs/ExecuteWatchdogTest.java
  9. +71
    -0
      src/testcases/org/apache/tools/ant/taskdefs/TimeProcess.java

+ 3
- 3
src/main/org/apache/tools/ant/taskdefs/ExecTask.java View File

@@ -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());
}

/**


+ 87
- 15
src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java View File

@@ -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;
}
}

+ 1
- 1
src/main/org/apache/tools/ant/taskdefs/ExecuteWatchdog.java View File

@@ -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);
}


+ 32
- 3
src/main/org/apache/tools/ant/taskdefs/Java.java View File

@@ -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());
}

}

+ 1
- 1
src/main/org/apache/tools/ant/util/TimeoutObserver.java View File

@@ -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
*


+ 1
- 1
src/main/org/apache/tools/ant/util/Watchdog.java View File

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


+ 148
- 0
src/testcases/org/apache/tools/ant/taskdefs/ExecuteJavaTest.java View File

@@ -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;
}

}

+ 7
- 17
src/testcases/org/apache/tools/ant/taskdefs/ExecuteWatchdogTest.java View File

@@ -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);
}
}
}

+ 71
- 0
src/testcases/org/apache/tools/ant/taskdefs/TimeProcess.java View File

@@ -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);
}
}

Loading…
Cancel
Save