View Javadoc

1   package cz.cuni.amis.pogamut.base.utils.future;
2   
3   import java.util.concurrent.CountDownLatch;
4   import java.util.concurrent.TimeUnit;
5   
6   import cz.cuni.amis.pogamut.base.component.IComponent;
7   import cz.cuni.amis.pogamut.base.component.bus.IComponentBus;
8   import cz.cuni.amis.pogamut.base.component.bus.event.BusAwareCountDownLatch;
9   import cz.cuni.amis.pogamut.base.component.bus.exception.ComponentNotRunningException;
10  import cz.cuni.amis.utils.future.FutureWithListeners;
11  
12  /**
13   * Future that depends on the running state of the {@link IComponent}. This future guarantees 
14   * that it unblock all threads waiting for the result if the world view (or other component(s) specified
15   * via {@link ComponentFuture#ComponentFuture(IComponentBus, IComponent...)}) dies.
16   * 
17   * @author Jimmy
18   *
19   * @param <RESULT>
20   */
21  public class ComponentFuture<RESULT> extends FutureWithListeners<RESULT> {
22  
23  	private IComponent[] dependants;
24  	private IComponentBus bus;
25  
26  	/**
27  	 * Constructor where you have to specify components on which the result depends + its component bus. Note
28  	 * that all components must be registered at 'bus'.
29  	 * @param bus may be null - in this case a simple {@link CountDownLatch} is created instead of {@link BusAwareCountDownLatch}
30  	 * @param dependants may be null or zero-length - in this case a simple {@link CountDownLatch} is created instead of {@link BusAwareCountDownLatch}
31  	 */
32  	public ComponentFuture(IComponentBus bus, IComponent... dependants) {
33  		this.bus = bus;
34  		this.dependants = dependants;
35  	}
36  	
37  	@Override
38  	protected CountDownLatch createLatch() {
39  		if (bus != null && dependants != null && dependants.length > 0) {
40  			return new BusAwareCountDownLatch(1, bus, dependants);
41  		} else {
42  			return new CountDownLatch(1);
43  		}
44  	}
45  	
46  	/**
47  	 * Blocks until the future is computed and then returns the result of the computation.
48  	 * <p><p>
49  	 * If the result can't be computed (computation is cancelled, exception happens or some component working on the future
50  	 * result stops), throws an {@link ComponentFutureException}.
51  	 * <p><p>
52  	 * For additional info, see {@link FutureWithListeners#get()}.
53  	 * 
54  	 * @return
55  	 */
56  	@Override
57  	public RESULT get() throws ComponentFutureException {
58  		super.get();
59  		synchronized(mutex) {
60  			switch (getStatus()) {
61  			case FUTURE_IS_READY: return super.get();
62  			case CANCELED: throw new ComponentFutureException("The computation has been canceled.", this);
63  			case COMPUTATION_EXCEPTION: throw new ComponentFutureException("Computation exception.", getException());
64  			case FUTURE_IS_BEING_COMPUTED: 
65  				computationException(new ComponentNotRunningException("One of the component dealing with the future computation has stopped.", this));
66  				throw new ComponentFutureException("One of the component has stopped, future can't be computer.", getException());
67  			}
68  		}
69  		return null;
70  	}
71  	
72  	/**
73  	 * Blocks until the future is computed (or timeout) and then returns the result of the computation. If the result
74  	 * is not computed until timeout, null is returned (check status of the future, whether the 'null' is truly the result
75  	 * of the computation).
76  	 * <p><p>
77  	 * If the result can't be computed (computation is cancelled, exception happens or some component working on the future
78  	 * result stops), throws an {@link ComponentFutureException}.
79  	 * <p><p>
80  	 * For additional info, see {@link FutureWithListeners#get(long, TimeUnit)}.
81  	 * 
82  	 * @param timeout
83  	 * @param unit
84  	 */
85  	@Override
86  	public RESULT get(long timeout, TimeUnit unit) throws ComponentFutureException {
87  		super.get(timeout, unit);
88  		synchronized(mutex) {
89  			switch (getStatus()) {
90  			case FUTURE_IS_READY: return super.get();
91  			case CANCELED: throw new ComponentFutureException("The computation has been canceled.", this);
92  			case COMPUTATION_EXCEPTION: throw new ComponentFutureException("Computation exception.", getException());
93  			case FUTURE_IS_BEING_COMPUTED: 
94  				if (latch.getCount() == 0) {
95  					// latch has been risen without the setting the result
96  					computationException(new ComponentNotRunningException("One of the component dealing with the future computation has stopped.", this));				
97  					throw new ComponentFutureException("One of the component has stopped, future can't be computer.", getException());
98  				}
99  			}
100 		}
101 		return null;
102 	}
103 	
104 }