View Javadoc

1   package cz.cuni.amis.pogamut.udk.communication.worldview;
2   
3   import java.util.ArrayList;
4   import java.util.LinkedList;
5   import java.util.List;
6   import java.util.Queue;
7   
8   import com.google.inject.Inject;
9   import com.google.inject.name.Named;
10  
11  import cz.cuni.amis.pogamut.base.communication.mediator.IMediator;
12  import cz.cuni.amis.pogamut.base.communication.translator.event.IWorldChangeEvent;
13  import cz.cuni.amis.pogamut.base.component.bus.IComponentBus;
14  import cz.cuni.amis.pogamut.base.component.bus.event.BusAwareCountDownLatch;
15  import cz.cuni.amis.pogamut.base.component.bus.exception.ComponentNotRunningException;
16  import cz.cuni.amis.pogamut.base.component.bus.exception.ComponentPausedException;
17  import cz.cuni.amis.pogamut.base.component.controller.ComponentDependencies;
18  import cz.cuni.amis.pogamut.base.utils.guice.AgentScoped;
19  import cz.cuni.amis.pogamut.base.utils.logging.IAgentLogger;
20  import cz.cuni.amis.pogamut.base3d.ILockableVisionWorldView;
21  import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.BeginMessage;
22  import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.EndMessage;
23  import cz.cuni.amis.utils.exception.PogamutInterruptedException;
24  import java.util.logging.Level;
25  
26  /**
27   * Lockable word view.
28   * <p>
29   * <p>
30   * Contains GameBotsUDK correct locking of the worldview.
31   * <p>
32   * <p>
33   * All messages are processed always in batches (all messages between
34   * EndMessages are one batch) meaning that the world view is always correct!
35   * <p>
36   * <p>
37   * When worldview is lock()ed it postpones the events until unlock()ed, which is
38   * triggering raising all events that came from the lock().
39   * <p>
40   * <p>
41   * <b>lock() method here blocks</b> until the END message of the batch is hit,
42   * then the world view is considered to be fully locked and let the lock()
43   * continue. You may use it to create correct sync bot. (just lock() the world
44   * view before your logic and unlock() the world view after the logic finishes)
45   * <p>
46   * <p>
47   * The world view is unlocked from the beginning.
48   * <p>
49   * <p>
50   * The locking mechanism starts to work with the first BeginMessage. (To let all
51   * other events to be processed automatically during the handshake.)
52   * 
53   * @author Jimmy
54   *         <p>
55   *         <p>
56   * 
57   * 
58   * @author Jimmy
59   * @see UDKLockableWorldView
60   */
61  @AgentScoped
62  public class UDKSyncLockableWorldView extends UDKWorldView
63  		implements ILockableVisionWorldView {
64  
65  	public static final String WORLDVIEW_DEPENDENCY = "UDKSyncLockableWorldViewDependency";
66  
67  	/**
68  	 * Here we store batches that are complete (ends with the EndMessage).
69  	 */
70  	private Queue<List<IWorldChangeEvent>> batches = new LinkedList<List<IWorldChangeEvent>>();
71  
72  	/**
73  	 * Here we store new events that are coming from the Mediator.
74  	 */
75  	private List<IWorldChangeEvent> currentBatch = new ArrayList<IWorldChangeEvent>();
76  
77  	/**
78  	 * Whether the world view is locked.
79  	 */
80  	private boolean locked = false;
81  
82  	/**
83  	 * First the world view will become locked, when the next END message is
84  	 * received, it will raise this flag meaning the lock() succeeded (the
85  	 * lockLatch has been raised) and we have to buffer all new messages.
86  	 */
87  	private boolean inLock = false;
88  
89  	/**
90  	 * First BEG message
91  	 */
92  	private boolean beginCame = false;
93  
94  	/**
95  	 * Synchronization mutex for this class.
96  	 */
97  	private final Object objectMutex = new Object();
98  
99  	/**
100 	 * lock() waits on this latch to continue...
101 	 */
102 	private BusAwareCountDownLatch lockLatch;
103 
104 	/**
105 	 * Whether the stop() method has been called.
106 	 */
107 	private boolean stopRequested = false;
108 	
109 	/**
110 	 * Whether the pause() method has been called.
111 	 */
112 	private boolean pauseRequested = false;
113 
114 	@Inject
115 	public UDKSyncLockableWorldView(
116 			@Named(WORLDVIEW_DEPENDENCY) ComponentDependencies dependencies,
117 			IMediator mediator,
118 			IComponentBus bus, IAgentLogger log) {
119 		super(dependencies, mediator, bus, log);		
120 	}
121 
122 	/**
123 	 * When the world view is locked - no batches are processes until unlocked.
124 	 * 
125 	 * @throws PogamutInterruptedException
126 	 */
127 	public void lock() throws PogamutInterruptedException, ComponentNotRunningException {
128 		if (isPaused()) throw new ComponentPausedException(controller.getState().getFlag(), this);
129 		if (!isRunning()) throw new ComponentNotRunningException(controller.getState().getFlag(), this);
130 		synchronized (objectMutex) {
131 			if (isLocked())
132 				return;
133 			locked = true;
134 			if (log.isLoggable(Level.FINER)) log.finer("World view is being locked.");
135 		}
136 		if (isPaused()) {
137 			if (log.isLoggable(Level.FINER)) log.finer("World view paused, unlocking.");
138 			locked = false;
139 			throw new ComponentPausedException(controller.getState().getFlag(), this);
140 		}
141 		if (!isRunning()) {
142 			if (log.isLoggable(Level.FINER)) log.finer("World view not running, unlocking.");
143 			locked = false;
144 			throw new ComponentNotRunningException(controller.getState().getFlag(), this);
145 		}
146 		lockLatch.await();
147 		if (log.isLoggable(Level.FINER)) log.finer("World view locked.");
148 		if (pauseRequested) { 
149 			throw new ComponentPausedException("Component pause requested.", this);
150 		}
151 		if (stopRequested) {
152 			throw new ComponentNotRunningException("Component stop requested.", this);
153 		}
154 	}
155 
156 	/**
157 	 * Unlocks the world view - triggers processing of all events till the last
158 	 * EndMessage that came between lock() / unlock() calls.
159 	 */
160 	public void unlock() throws ComponentNotRunningException {
161 		synchronized (objectMutex) {
162 			if (!isLocked())
163 				return;
164 			locked = false;
165 			if (log.isLoggable(Level.FINER)) log.finer("World view is being unlocked.");
166 			inLock = false;
167 			processBatches();
168 			if (log.isLoggable(Level.FINER)) log.finer("World view unlocked.");
169 			// reinitialize the lock latch so the next lock() blocks as well
170 			lockLatch = new BusAwareCountDownLatch(1, eventBus, this);
171 		}
172 	}
173 
174 	public boolean isLocked() {
175 		return locked;
176 	}
177 
178 	public boolean isInLock() {
179 		return inLock;
180 	}
181 
182 	/**
183 	 * Process all messages that are stored inside the batches and clears them.
184 	 * <p><p>
185 	 * <b>Unsync!</b>
186 	 */
187 	private void processBatches() {
188 		// process old batches
189 		for (List<IWorldChangeEvent> batch : batches) {
190 			processBatch(batch);
191 		}
192 		batches.clear();
193 		// process current opened batch
194 		processBatch(currentBatch);
195 	}
196 
197 	/**
198 	 * Does super.notifyEvent(event) for each event in the batch.
199 	 * <p>
200 	 * <p>
201 	 * <b>Unsync!</b>
202 	 * 
203 	 * @param batch
204 	 */
205 	private void processBatch(List<IWorldChangeEvent> batch) {
206 		for (IWorldChangeEvent event : batch) {
207 			super.notify(event);
208 		}
209 		batch.clear();
210 	}
211 
212 	/**
213 	 * Implements locking logic.
214 	 */
215 	@Override
216 	public void notify(IWorldChangeEvent event) {
217 		synchronized (objectMutex) {
218 			if (!beginCame) {
219 				if (event instanceof BeginMessage) {
220 					beginCame = true;
221 				} else {
222 					super.notify(event);
223 					return;
224 				}
225 			}
226 			if (isLocked()) {
227 				if (isInLock()) {
228 					// we're IN LOCK - logic is running, do not process any new
229 					// message
230 					if (event instanceof EndMessage) {
231 						currentBatch.add(event);
232 						batches.add(currentBatch);
233 						currentBatch = new ArrayList<IWorldChangeEvent>(
234 								currentBatch.size() + 10);
235 					} else {
236 						currentBatch.add(event);
237 					}
238 				} else {
239 					// we're waiting for the next EndMessage
240 					if (event instanceof EndMessage) {
241 						// EndMessage came! Notify...
242 						super.notify(event);
243 						// ... raise the latch and let the logic continue!
244 						if (log.isLoggable(Level.FINER)) log.finer("World view in-locked state, raising the lock() latch.");
245 						lockLatch.countDown();
246 						inLock = true;
247 					} else {
248 						// not an EndMessage, process as usual
249 						super.notify(event);
250 					}
251 				}
252 			} else {
253 				super.notify(event);
254 			}
255 		}
256 	}
257 	
258 	@Override
259 	protected void start(boolean startPaused) {
260 		super.start(startPaused);
261 		lockLatch = new BusAwareCountDownLatch(1, eventBus, this);
262 		stopRequested = false;
263 		pauseRequested = false;
264 	}
265 
266 	@Override
267 	protected void preStop() {
268 		super.preStop();
269 		synchronized (objectMutex) {
270 			stopRequested = true;
271 			lockLatch.countDown();
272 		}
273 	}
274 	
275 	@Override
276 	protected void prePause() {
277 		super.preStop();
278 		synchronized (objectMutex) {
279 			pauseRequested = true;
280 			lockLatch.countDown();
281 		}
282 	}
283 	
284 	@Override
285 	protected void resume() {
286 		super.resume();
287 		synchronized(objectMutex) {
288 			lockLatch.countDown();
289 			lockLatch = new BusAwareCountDownLatch(1, eventBus, this);
290 			pauseRequested = false;
291 		}
292 	}
293 	
294 	@Override
295 	protected void stop() {
296 		super.stop();
297 		synchronized (objectMutex) {
298 			stopRequested = true;
299 			lockLatch.countDown();
300 		}
301 	}
302 
303 }