Solution 1: Timed run: If you want a method to return or throw an exception after a specified amount of time, use the following method to execute the method on a background thread while waiting for it to complete:
public static void timedRun(Runnable r, long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException {
Future<?> task = executor.submit(r);
try {
task.get(timeout, unit);
} catch (ExecutionException e) {
throw launderThrowable(e.getCause());
} finally {
task.cancel(true);
}
}
private static RuntimeException launderThrowable(Throwable t) {
if (t instanceof RuntimeException) return (RuntimeException)t;
else if (t instanceof Error) throw (Error)t;
else throw new IllegalStateException("Not unchecked", t);
}
(Source: Goetz, Brian, Bloch, Joshua, Bowbeer, Joseph, Lea, Doug, Holmes, David and Peierls, Tim. Java Concurrency in Practice. : Addison-Wesley Longman, Amsterdam, 2006. Listing 5.13 and 7.10)
For executor
, you can either create a new one using Executor.newSingleThreadExecutor()
, or reuse an existing one.
But be warned: Although this method is guaranteed to return or throw an exception after the specified timeout, it cannot guarantee that the runnable will really stop! It interrupts the executing thread, but if the runnable does not react to thread interruption (e.g. by internally checking Thread.interrupted()
), it may continue to run in the background - possibly forever - occupying a thread! But at least it does not block.
Solution 2: Timed run with custom threads: If there is any possibility beside thread interruption to cancel your method call, you can still use the approach above, but then you have to use an Executor
with custom ThreadFactory
that creates a special Thread
instance with overridden interrupt
method:
Executor executor = Executor.newSingleThreadExecutor(r -> new WsdlThread(r));
public class WsdlThread extends Thread {
public WsdlThread(Runnable r) { super(r); }
public void interrupt() {
try {
// TODO: do something that will interrupt the wsdl call
// e.g. close connection to server, etc.
// example: ((WsdlRunnable)r).getWsdlConnection().close();
} finally {
super.interrupt();
}
}
}
If this isn't possible too, and Thread.stop()
doesn't work either, this last solution might work:
Solution 3: Start non-cancellable call in another JVM:
Use Runtime.exec
to start another JVM and execute the method call there (See Executing a Java application in a separate process for more info on how to do this). Runtime.exec
will return a Process
object, which represents the running process.
You can kill it by calling destroy()
or destroyForcibly()
.