View Javadoc

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