diff --git a/src/helma/framework/FutureResult.java b/src/helma/framework/FutureResult.java new file mode 100644 index 00000000..f5d42869 --- /dev/null +++ b/src/helma/framework/FutureResult.java @@ -0,0 +1,59 @@ +/* + * Copyright 2006 Hannes Wallnoefer + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package helma.framework; + +/** + * A handle for an asynchronous request execution. This allows to wait for + * request termination, get the result or the exception of the execution. + */ +public interface FutureResult { + /** + * Get the result of the execution. If the execution is still active, + * or if the invocation threw an exception, this method immediately returns null. + * @return the result, or null + */ + Object getResult(); + + /** + * Get the exception of the execution, if one was thrown. If the execution + * is still active, or if no exception was thrown, this method immediately returns null. + * @return the exception, or null + */ + Exception getException(); + + /** + * Returns true if the execution is still active, and false if not. + * @return true if the execution is still active + */ + boolean getRunning(); + + /** + * Wait for execution to terminat, returning the execution result, if one is available. + * @return the execution result, or null + * @throws InterruptedException if we were interrupted by some other thread + */ + Object waitForResult() throws InterruptedException; + + /** + * Wait for a specific ammount of thime for the execution to terminate, returning + * the execution result, if one is available. + * @param timeout the number of milliseconds to wait + * @return the execution result, or null + * @throws InterruptedException if we were interrupted by some other thread + */ + Object waitForResult(long timeout) throws InterruptedException; +} diff --git a/src/helma/framework/core/ApplicationBean.java b/src/helma/framework/core/ApplicationBean.java index 01c9375f..3cbcc8bc 100644 --- a/src/helma/framework/core/ApplicationBean.java +++ b/src/helma/framework/core/ApplicationBean.java @@ -22,6 +22,7 @@ import helma.util.CronJob; import helma.util.SystemMap; import helma.util.WrappedMap; import helma.framework.repository.*; +import helma.framework.FutureResult; import helma.main.Server; import java.io.File; @@ -33,7 +34,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** - * + * Application bean that provides a handle to the scripting environment to + * application specific functionality. */ public class ApplicationBean implements Serializable { Application app; @@ -682,11 +684,11 @@ public class ApplicationBean implements Serializable { * this long, we will try to interrupt the invocation * @return an object with the properties described above */ - public Object invokeAsync(Object thisObject, + public FutureResult invokeAsync(Object thisObject, final Object function, final Object[] args) { // default timeout of 15 minutes - return invokeAsync(thisObject, function, args, 60000L * 15); + return new AsyncInvoker(thisObject, function, args, 60000L * 15); } /** @@ -712,76 +714,9 @@ public class ApplicationBean implements Serializable { * this long, we will try to interrupt the invocation * @return an object with the properties described above */ - public Object invokeAsync(final Object thisObject, - final Object function, - final Object[] args, - final long timeout) { - Thread thread = new Thread() { - - private Object result; - private Exception exception; - private boolean running = true; - - public void run() { - RequestEvaluator reval = null; - try { - reval = app.getEvaluator(); - setResult(reval.invokeInternal(thisObject, function, args, timeout)); - } catch (Exception x) { - setException(x); - } finally { - running = false; - app.releaseEvaluator(reval); - } - } - - public synchronized boolean getRunning() { - return running; - } - - private synchronized void setResult(Object obj) { - result = obj; - running = false; - notifyAll(); - } - - public synchronized Object getResult() { - return result; - } - - public synchronized Object waitForResult() throws InterruptedException { - if (!running) - return result; - wait(); - return result; - } - - public synchronized Object waitForResult(long timeout) - throws InterruptedException { - if (!running) - return result; - wait(timeout); - return result; - } - - private synchronized void setException(Exception x) { - exception = x; - running = false; - notifyAll(); - } - - public synchronized Exception getException() { - return exception; - } - - public String toString() { - return new StringBuffer("AsyncInvokeThread{running: ").append(running) - .append(", result: ").append(result).append(", exception: ") - .append(exception).append("}").toString(); - } - }; - thread.start(); - return thread; + public FutureResult invokeAsync(Object thisObject, Object function, + Object[] args, long timeout) { + return new AsyncInvoker(thisObject, function, args, timeout); } /** @@ -791,4 +726,83 @@ public class ApplicationBean implements Serializable { public String toString() { return "[Application " + app.getName() + "]"; } + + class AsyncInvoker extends Thread implements FutureResult { + + private Object thisObject; + private Object function; + private Object[] args; + private long timeout; + + private Object result; + private Exception exception; + private boolean running = true; + + private AsyncInvoker(Object thisObj, Object func, Object[] args, long timeout) { + thisObject = thisObj; + function = func; + this.args = args; + this.timeout = timeout; + start(); + } + + public void run() { + RequestEvaluator reval = null; + try { + reval = app.getEvaluator(); + setResult(reval.invokeInternal(thisObject, function, args, timeout)); + } catch (Exception x) { + setException(x); + } finally { + running = false; + app.releaseEvaluator(reval); + } + } + + public synchronized boolean getRunning() { + return running; + } + + private synchronized void setResult(Object obj) { + result = obj; + running = false; + notifyAll(); + } + + public synchronized Object getResult() { + return result; + } + + public synchronized Object waitForResult() throws InterruptedException { + if (!running) + return result; + wait(); + return result; + } + + public synchronized Object waitForResult(long timeout) + throws InterruptedException { + if (!running) + return result; + wait(timeout); + return result; + } + + private synchronized void setException(Exception x) { + exception = x; + running = false; + notifyAll(); + } + + public synchronized Exception getException() { + return exception; + } + + public String toString() { + return new StringBuffer("AsyncInvokeThread{running: ").append(running) + .append(", result: ").append(result).append(", exception: ") + .append(exception).append("}").toString(); + } + + } }