View Javadoc

1   package cz.cuni.amis.pogamut.base3d.worldview.impl;
2   
3   import java.util.ArrayList;
4   import java.util.LinkedList;
5   import java.util.List;
6   import java.util.Queue;
7   import java.util.concurrent.CountDownLatch;
8   
9   import com.google.inject.Inject;
10  import com.google.inject.name.Named;
11  
12  import cz.cuni.amis.pogamut.base.communication.translator.event.IWorldChangeEvent;
13  import cz.cuni.amis.pogamut.base.communication.worldview.ILockableWorldView;
14  import cz.cuni.amis.pogamut.base.component.bus.IComponentBus;
15  import cz.cuni.amis.pogamut.base.component.controller.ComponentDependencies;
16  import cz.cuni.amis.pogamut.base.utils.logging.IAgentLogger;
17  import cz.cuni.amis.utils.exception.PogamutInterruptedException;
18  import java.util.logging.Level;
19  
20  public abstract class SyncLockableBatchAwareWorldView extends BatchAwareWorldView implements ILockableWorldView {
21  	
22  	public static final String WORLDVIEW_DEPENDENCY = "SyncLockableBatchAwareWorldView";
23  
24  	/**
25  	 * Here we store batches that are complete (ends with the EndMessage).
26  	 */
27  	private Queue<List<IWorldChangeEvent>> batches = new LinkedList<List<IWorldChangeEvent>>();
28  
29  	/**
30  	 * Here we store new events that are coming from the Mediator.
31  	 */
32  	private List<IWorldChangeEvent> currentBatch = new ArrayList<IWorldChangeEvent>();
33  
34  	/**
35  	 * Whether the world view is locked.
36  	 */
37  	private boolean locked = false;
38  
39  	/**
40  	 * First the world view will become locked, when the next END message is
41  	 * received, it will raise this flag meaning the lock() succeeded (the
42  	 * lockLatch has been raised) and we have to buffer all new messages.
43  	 */
44  	private boolean inLock = false;
45  
46  	/**
47  	 * First BEG message
48  	 */
49  	private boolean beginCame = false;
50  
51  	/**
52  	 * Synchronization mutex for this class.
53  	 */
54  	private final Object objectMutex = new Object();
55  
56  	/**
57  	 * lock() waits on this latch to continue...
58  	 */
59  	private CountDownLatch lockLatch = new CountDownLatch(1);
60  
61  	/**
62  	 * Whether the stop() method has been called.
63  	 */
64  	private boolean stopRequested = false;
65  
66  	@Inject
67  	public SyncLockableBatchAwareWorldView(@Named(WORLDVIEW_DEPENDENCY) ComponentDependencies dependencies, IComponentBus bus, IAgentLogger log) {
68          super(dependencies, bus, log);
69  	}
70  
71  	/**
72  	 * When the world view is locked - no batches are processes until unlocked.
73  	 * 
74  	 * @throws InterruptedException
75  	 */
76  	public void lock() throws PogamutInterruptedException {
77  		synchronized (objectMutex) {
78  			if (isLocked())
79  				return;
80  			locked = true;
81  			if (log.isLoggable(Level.FINER)) log.finer("World view locked.");
82  		}
83  		try {
84  			lockLatch.await();
85  		} catch (InterruptedException e) {
86  			throw new PogamutInterruptedException(e.getMessage(), e, log, this);
87  		}
88  		if (stopRequested) {
89  			throw new PogamutInterruptedException(
90  					"lock() interrupted with the request to stop the work", log, this);
91  		}
92  	}
93  
94  	/**
95  	 * Unlocks the world view - triggers processing of all events till the last
96  	 * EndMessage that came between lock() / unlock() calls.
97  	 */
98  	public void unlock() {
99  		synchronized (objectMutex) {
100 			if (!isLocked())
101 				return;
102 			if (log.isLoggable(Level.FINER)) log.finer("World view is being unlocked.");
103 			locked = false;
104 			inLock = false;
105 			processBatches();
106 			if (log.isLoggable(Level.FINER)) log.finer("World view unlocked.");
107 			// reinitialize the lock latch so the next lock() blocks as well
108 			lockLatch = new CountDownLatch(1);
109 		}
110 	}
111 
112 	public boolean isLocked() {
113 		return locked;
114 	}
115 
116 	public boolean isInLock() {
117 		return inLock;
118 	}
119 
120 	/**
121 	 * Process all messages that are stored inside the batches and cleares them.
122 	 * <p>
123 	 * <p>
124 	 * <b>Unsync!</b>
125 	 */
126 	private void processBatches() {
127 		// process old batches
128 		for (List<IWorldChangeEvent> batch : batches) {
129 			processBatch(batch);
130 		}
131 		batches.clear();
132 		// process current opened batch
133 		processBatch(currentBatch);
134 	}
135 
136 	/**
137 	 * Does super.notifyEvent(event) for each event in the batch.
138 	 * <p>
139 	 * <p>
140 	 * <b>Unsync!</b>
141 	 * 
142 	 * @param batch
143 	 */
144 	private void processBatch(List<IWorldChangeEvent> batch) {
145 		for (IWorldChangeEvent event : batch) {
146 			super.notify(event);
147 		}
148 		batch.clear();
149 	}
150 
151 	/**
152 	 * Implements locking logic.
153 	 */
154 	@Override
155 	public void notify(IWorldChangeEvent event) {
156 		synchronized (objectMutex) {
157 			if (!beginCame) {
158 				if (isBatchBeginEvent(event)) {
159 					beginCame = true;
160 				} else {
161 					super.notify(event);
162 					return;
163 				}
164 			}
165 			if (isLocked()) {
166 				if (isInLock()) {
167 					// we're IN LOCK - logic is running, do not process any new
168 					// message
169 					if (isBatchEndEvent(event)) {
170 						currentBatch.add(event);
171 						batches.add(currentBatch);
172 						currentBatch = new ArrayList<IWorldChangeEvent>(
173 							currentBatch.size() + 10
174 						);
175 					} else {
176 						currentBatch.add(event);
177 					}
178 				} else {
179 					// we're waiting for the next EndMessage
180 					if (isBatchEndEvent(event)) {
181 						// EndMessage came! Notify...
182 						super.notify(event);
183 						// ... raise the latch and let the logic continue!
184 						if (log.isLoggable(Level.FINER)) log.finer("World view in-locked state, raising the lock() latch.");
185 						lockLatch.countDown();
186 						inLock = true;
187 					} else {
188 						// not an EndMessage, process as usual
189 						super.notify(event);
190 					}
191 				}
192 			} else {
193 				super.notify(event);
194 			}
195 		}
196 	}
197 
198 	@Override
199 	public void stop() {
200 		super.stop();
201 		synchronized (objectMutex) {
202 			stopRequested = true;
203 			lockLatch.countDown();
204 		}
205 	}
206 
207 }