View Javadoc

1   package cz.cuni.amis.pogamut.base.component.bus.event;
2   
3   import java.util.concurrent.CountDownLatch;
4   import java.util.concurrent.TimeUnit;
5   
6   import cz.cuni.amis.pogamut.base.component.bus.IComponentBus;
7   import cz.cuni.amis.pogamut.base.component.bus.IComponentEvent;
8   import cz.cuni.amis.pogamut.base.component.bus.IComponentEventListener;
9   import cz.cuni.amis.utils.NullCheck;
10  import cz.cuni.amis.utils.exception.PogamutInterruptedException;
11  import cz.cuni.amis.utils.token.Token;
12  
13  /**
14   * Allows you to wait for some event on the bus.
15   * <p><p>
16   * WARNING: if you want to stop using this object (throw away the pointer) call DESTROY(), you will usually use try{}finally{} for this.
17   */
18  public class WaitForEvent {
19  
20  	/**
21  	 * Note that only events that implements {@link IComponentEvent} may be really substitued as T.
22  	 * @author Jimmy
23  	 *
24  	 * @param <T>
25  	 */
26  	public interface IEventFilter<T> {
27  		
28  		/**
29  		 * Must return class of the event that the object may accept.
30  		 * <p><p>
31  		 * Must not return null!
32  		 * 
33  		 * @return
34  		 */
35  		public Class<T> getEventClass(); 
36  		
37  		/**
38  		 * If it does not return null - then only events that happened on this class of component may be accepted (this class or descendants).
39  		 * @return
40  		 */
41  		public Class getComponentClass();
42  		
43  		/**
44  		 * If it does not return null - then only events from the component of this id may be accepted.
45  		 * @return
46  		 */
47  		public Token getComponentId();
48  		
49  		/**
50  		 * Whether the event may be accepted.
51  		 * @param event
52  		 * @return
53  		 */
54  		public boolean accept(T event);
55  		
56  	}
57  		
58  	private final CountDownLatch eventLatch = new CountDownLatch(1);
59  	private final IComponentBus componentBus;
60  	private final IEventFilter acceptEvent;
61  	private final IComponentEventListener listener;
62  	private final IComponentEventListener fatalErrorListener;
63  	private boolean fatalError = false;
64  	
65  	public WaitForEvent(IComponentBus bus, IEventFilter accept) {
66  		this.componentBus = bus;
67  		NullCheck.check(this.componentBus, "bus");
68  		this.acceptEvent = accept;
69  		NullCheck.check(accept.getEventClass(), "accept.getEventClass()");
70  		this.listener = new IComponentEventListener<IComponentEvent>() {
71  
72  			@Override
73  			public void notify(IComponentEvent event) {
74  				if (acceptEvent.accept(acceptEvent.getEventClass().cast(event))) {
75  					try {
76  						componentBus.removeEventListener(acceptEvent.getEventClass(), this);
77  						componentBus.removeEventListener(IFatalErrorEvent.class, fatalErrorListener);
78  					} finally {
79  						eventLatch.countDown();
80  					}
81  				}
82  			}
83  			
84  		};
85  		
86  		this.fatalErrorListener = new IComponentEventListener<IComponentEvent>() {
87  
88  			@Override
89  			public void notify(IComponentEvent event) {
90  				fatalError = true;
91  				try {
92  					componentBus.removeEventListener(acceptEvent.getEventClass(), this);
93  					componentBus.removeEventListener(IFatalErrorEvent.class, fatalErrorListener);
94  				} finally {
95  					eventLatch.countDown();
96  				}
97  			}
98  			
99  		};
100 		
101 		if (!componentBus.isRunning()) {
102 			fatalError = true;
103 			eventLatch.countDown();
104 		} else {		
105 			componentBus.addEventListener(IFatalErrorEvent.class, fatalErrorListener);
106 			componentBus.addEventListener(acceptEvent.getEventClass(), listener);
107 			if (!componentBus.isRunning()) {
108 				fatalError = true;
109 				eventLatch.countDown();
110 				try {
111 					componentBus.removeEventListener(IFatalErrorEvent.class, fatalErrorListener);
112 				} catch (Exception e) {
113 				} finally {
114 					try {
115 						componentBus.removeEventListener(acceptEvent.getEventClass(), listener);
116 					} catch (Exception e) {
117 					}
118 				}
119 			}
120 		}
121 	}
122 	
123 	/**
124 	 * Awaits the event (forever) ... do not use! Always use version with timeout!
125 	 * 
126 	 * @return whether the event has been received (false == fatal error happened)
127 	 */
128 	public boolean await() throws PogamutInterruptedException {
129 		try {
130 			eventLatch.await();
131 			return !fatalError;
132 		} catch (InterruptedException e) {
133 			throw new PogamutInterruptedException(e, this);
134 		}
135 	}
136 	
137 	/**
138 	 * Awaits the event for a specific amount of time.
139 	 * <p><p>
140 	 * WARNING: If the event is not received and you do not plan to use the object further - call destroy()!
141 	 * 
142 	 * @param timeoutMillis
143 	 * @return whether the event has been received (false == timeout or fatal error has happened)
144 	 * @throws InterruptedException
145 	 */
146 	public boolean await(long timeoutMillis) throws PogamutInterruptedException {
147 		try {
148 			eventLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);
149 		} catch (InterruptedException e) {
150 			throw new PogamutInterruptedException(e, this);
151 		}
152 		return !fatalError && eventLatch.getCount() == 0;
153 	}
154 
155 	/**
156 	 * Destroys the object ... any await() call will just go through after this call.
157 	 */
158 	public void destroy() {
159 		componentBus.removeEventListener(acceptEvent.getEventClass(), listener);
160 		eventLatch.countDown();
161 	}
162 	
163 }