/*
* org.openmicroscopy.shoola.util.concur.tasks.Future
*
*------------------------------------------------------------------------------
* Copyright (C) 2006 University of Dundee. All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*------------------------------------------------------------------------------
*/
package org.openmicroscopy.shoola.util.concur.tasks;
//Java imports
//Third-party libraries
//Application-internal dependencies
import org.openmicroscopy.shoola.util.concur.ControlFlowObserver;
/**
* Extends {@link ExecHandle} to allow clients to retrieve the result of a
* computation performed by a service.
* After triggering the asynchronous execution of a service that computes a
* result, the invoker (client) is given back a <code>Future</code> object.
* The client calls one of the <code>getResult</code> methods to retrieve the
* computed result, possibly waiting until the computation is finished or until
* a given timeout elapses.
*
* @author Jean-Marie Burel
* <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @author <br>Andrea Falconi
* <a href="mailto:a.falconi@dundee.ac.uk">
* a.falconi@dundee.ac.uk</a>
* @version 2.2
* <small>
* (<b>Internal version:</b> $Revision$ $Date$)
* </small>
* @since OME2.2
*/
public class Future
extends ExecHandle
{
/** State flag. */
static final int WAITING_FOR_RESULT = 0;
/** State flag. */
static final int HAS_RESULT = 1;
/** State flag. */
static final int HAS_EXCEPTION = 2;
//NOTE: the above state flags have package visibility to enable testing.
/** The result of the computation, if any. */
private Object result;
/** Wraps any exception occurred during the computation. */
private ExecException failureWrapper;
/** Keeps track of the current state.*/
private int state;
/**
* Sets the result or makes a new {@link ExecException} to wrap the service
* exception.
* Also wakes up any thread blocked on one of the <code>getResult</code>
* methods.
* At least one of the passed arguments must be <code>null</code>.
*
* @param result The result of the computation.
* @param t Any exception encountered by the service.
*/
private void set(Object result, Throwable t)
{
if (state != WAITING_FOR_RESULT)
throw new Error("Can't reuse a future.");
this.result = result;
if (t != null) failureWrapper = new ExecException(t);
notifyAll();
}
/**
* Creates a new instance.
* Recall that this class extends {@link ExecHandle}, so the state is
* invalid until you call {@link ExecHandle#setCommand(ExecCommand)}.
*/
Future()
{
result = null;
failureWrapper = null;
state = WAITING_FOR_RESULT;
}
/**
* Stores the result of the service.
* Can only be invoked once and only if {@link #setException(Throwable)}
* hasn't already been invoked.
*
* @param r The service result.
*/
synchronized void setResult(Object r)
{
if (flowObs != null) flowObs.update(LOCK_ACQUIRED);
set(r, null);
state = HAS_RESULT;
}
/**
* Stores any exception occurred during the service execution.
* Can only be invoked once and only if {@link #setResult(Object)}
* hasn't already been invoked.
*
* @param t Any exception occurred during the service execution. Mustn't
* be <code>null</code>.
*/
synchronized void setException(Throwable t)
{
if (t == null) //See NOTE below.
t = new Error("Future.setException(null) is not a valid call."+
"(Bug in the execution workflow code.)");
if (flowObs != null) flowObs.update(LOCK_ACQUIRED);
set(null, t);
state = HAS_EXCEPTION;
}
/* NOTE: This can't happen if the execution workflow is correctly implemented;
* in fact, setException() is only called after catching an exception and
* passing along that exception. However, in the case t is null, this is the
* best strategy: if a client is blocked on getResult(), it will be woken up
* and no deadlock is possible. The alternative would be to throw a
* NullPointerException which is likely to percolate all the way up in the
* executor thread's call stack and eventually appear on stderr. In this case,
* no call to setException() (or setResult() for that matter) would follow and
* the state would still be WAITING_FOR_RESULT, which could result in a
* deadlock.
*/
/**
* Retrieves the result computed by the service.
* This method blocks until the computation is finished. If the service
* executed normally then the result of the computation is returned.
* Otherwise this method throws an <code>ExecException</code> that wraps
* the original exception which caused the service to terminate abnormally.
*
* @return The result computed by the service.
* @throws ExecException Wraps any exception encountered by the service.
* @throws InterruptedException If you interrupt a thread blocked on this
* method. Bear in mind that this has nothing
* to do with service cancellation.
*/
public Object getResult()
throws ExecException, InterruptedException
{
synchronized (this) {
if (flowObs != null) flowObs.update(LOCK_ACQUIRED);
while (state == WAITING_FOR_RESULT) wait();
if (state == HAS_EXCEPTION) throw failureWrapper;
return result;
}
}
/**
* Not implemented yet.
* @param msec
* @return See above.
* @throws ExecException
* @throws InterruptedException
*/
public Object getResult(long msec)
throws ExecException, InterruptedException
{
if (true) throw new NoSuchMethodError();
//TODO: implement. The following code is just to avoid compilers
//warnings.
if (msec == 0) msec = 0;
if (false) throw new ExecException(null);
if (false) throw new InterruptedException();
return null;
}
/*
* ==============================================================
* Follows code to enable testing.
* ==============================================================
*/
static final int LOCK_ACQUIRED = 100;
private ControlFlowObserver flowObs;
void register(ControlFlowObserver obs) { flowObs = obs; }
synchronized int getState() { return state; }
}