1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.commsen.stopwatch.storages;
18
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.LinkedList;
22 import java.util.Map;
23
24 import org.apache.log4j.Logger;
25
26 import com.commsen.stopwatch.StopwatchStorage;
27 import com.commsen.stopwatch.StopwatchStorageException;
28
29 /***
30 * TODO Dokumentacja
31 *
32 * @author Milen Dyankov
33 *
34 */
35 public class StorageManager implements Runnable {
36
37 private static final Logger log = Logger.getLogger(StorageManager.class);
38
39 private static final int SLEEP_TIME = 10;
40
41 final static int ADD = 0;
42 final static int UPDATE = 1;
43 final static int REMOVE = 2;
44
45 /***
46 * Storage manager will insert record into database when "start" method is called and update it
47 * when "stop" method is called. NOTE: in this case the time taken to insert the record into
48 * database is added to measurements.
49 */
50 public static final int NORMAL_MODE = 0;
51
52 /***
53 * Same as {@link #NORMAL_MODE} but storage manager runs in separate thread, thus resulting in
54 * more correct measurements. NOTE: in this case getReport(...) methods may return null when
55 * called soon after "stop" since storage manager may have not managed to store its data.
56 */
57 public static final int THREAD_MODE = 1;
58
59 /***
60 * Storage manager keeps in memory data gathered on "start" and stores all together when "end"
61 * is called.
62 */
63 public static final int DELAYED_MODE = 2;
64
65 /***
66 *
67 */
68 private StopwatchStorage storage;
69
70 /***
71 *
72 */
73 private LinkedList toBeProcessed;
74
75 /***
76 *
77 */
78 private Map idMapping;
79
80 long id = 0;
81
82 boolean running;
83
84 int mode;
85
86 private Thread[] threadPool;
87
88 int runningThreads;
89
90
91 /***
92 * @param storage
93 * @param threadMode
94 *
95 */
96 public StorageManager(StopwatchStorage storage, int threadMode) {
97 threadPool = new Thread[1];
98 toBeProcessed = new LinkedList();
99 idMapping = Collections.synchronizedMap(new HashMap());
100 this.storage = storage;
101 this.mode = threadMode;
102 runningThreads = 0;
103 }
104
105
106 /***
107 *
108 *
109 */
110 public synchronized void start() {
111 doStart(false);
112 }
113
114
115 /***
116 *
117 * @see com.commsen.stopwatch.StopwatchEngine#resume()
118 */
119 public synchronized void resume() {
120 doStart(true);
121 }
122
123
124 /***
125 *
126 * @param isRestart
127 */
128 private void doStart(boolean isRestart) {
129 if (running) return;
130
131 try {
132 if (isRestart)
133 storage.unfreeze();
134 else
135 storage.open();
136 } catch (StopwatchStorageException e) {
137 log.warn("Could not initialize stopwatch storage", e);
138 return;
139 }
140
141 running = true;
142
143 if (mode == THREAD_MODE) {
144 for (int i = 0; i < threadPool.length; i++) {
145 threadPool[i] = new Thread(this, "Stopwatch storage manager " + i);
146 threadPool[i].setDaemon(true);
147 threadPool[i].start();
148 }
149 }
150 }
151
152
153 /***
154 *
155 *
156 */
157 public synchronized void stop() {
158 doStop(false);
159 }
160
161
162 /***
163 *
164 *
165 */
166 public synchronized void pause() {
167 doStop(true);
168 }
169
170
171 /***
172 *
173 * @param isPause
174 */
175 private void doStop(boolean isPause) {
176 running = false;
177
178 while (runningThreads > 0) {
179 try {
180 Thread.sleep(SLEEP_TIME);
181 } catch (InterruptedException e) {
182
183 }
184 }
185
186 try {
187 if (isPause)
188 storage.freeze();
189 else
190 storage.close();
191 } catch (StopwatchStorageException e) {
192 log.warn("Storage error", e);
193 }
194 }
195
196
197 /***
198 * @see java.lang.Runnable#run()
199 */
200 public void run() {
201 runningThreads++;
202 while (running) {
203 try {
204 Thread.sleep(SLEEP_TIME);
205 } catch (InterruptedException e) {
206
207 }
208 processItems();
209 }
210 runningThreads--;
211 }
212
213
214 /***
215 *
216 * @param parameters
217 * @return
218 */
219 public long newRecord(Object[] parameters) {
220 switch (mode) {
221 case NORMAL_MODE:
222 synchronized (this) {
223 processSingleItem(++id, ADD, parameters);
224 return id;
225 }
226
227 case THREAD_MODE:
228 synchronized (toBeProcessed) {
229 toBeProcessed.addLast(new BufferItem(++id, ADD, parameters));
230 return id;
231 }
232
233 case DELAYED_MODE:
234 synchronized (idMapping) {
235 idMapping.put(new Long(++id), parameters);
236 return id;
237 }
238 }
239 return -1;
240 }
241
242
243 /***
244 *
245 * @param id
246 * @param parameters
247 * @return
248 */
249 public boolean completeRecord(long id, Object[] parameters) {
250
251 switch (mode) {
252 case NORMAL_MODE:
253 synchronized (this) {
254 return processSingleItem(id, UPDATE, parameters);
255 }
256
257 case THREAD_MODE:
258 synchronized (toBeProcessed) {
259 toBeProcessed.addLast(new BufferItem(id, UPDATE, parameters));
260 return true;
261 }
262
263 case DELAYED_MODE:
264 synchronized (idMapping) {
265 Object[] startParameters = (Object[]) idMapping.remove(new Long(id));
266 if (startParameters == null) return false;
267 try {
268 storage.newCompleteRecord(startParameters, parameters);
269 } catch (StopwatchStorageException e) {
270 log.warn("Storage error", e);
271 return false;
272 }
273 return true;
274 }
275 }
276 return false;
277
278 }
279
280
281 /***
282 *
283 * @param id
284 * @return
285 */
286 public boolean removeRecord(long id) {
287
288 switch (mode) {
289 case NORMAL_MODE:
290 synchronized (this) {
291 return processSingleItem(id, REMOVE, null);
292 }
293
294 case THREAD_MODE:
295 synchronized (toBeProcessed) {
296 toBeProcessed.addLast(new BufferItem(id, REMOVE, null));
297 return true;
298 }
299
300 case DELAYED_MODE:
301 synchronized (idMapping) {
302 return idMapping.remove(new Long(id)) != null;
303 }
304 }
305 return false;
306
307 }
308
309
310 /***
311 *
312 *
313 */
314 private void processItems() {
315 BufferItem bufferItem = null;
316 synchronized (toBeProcessed) {
317 if (!toBeProcessed.isEmpty()) bufferItem = (BufferItem) toBeProcessed.removeFirst();
318 }
319
320 while (bufferItem != null) {
321 processSingleItem(bufferItem.id, bufferItem.operation, bufferItem.parameters);
322
323 synchronized (toBeProcessed) {
324 if (!toBeProcessed.isEmpty())
325 bufferItem = (BufferItem) toBeProcessed.removeFirst();
326 else
327 bufferItem = null;
328 }
329
330 }
331
332 }
333
334
335 /***
336 * @param id
337 * @param operation
338 * @param parameters
339 * @param bufferItem
340 */
341 private boolean processSingleItem(long id, int operation, Object[] parameters) {
342 long databaseID;
343 Object dbKey;
344 try {
345 switch (operation) {
346 case ADD:
347 databaseID = storage.newRecord(parameters);
348 idMapping.put(new Long(id), new Long(databaseID));
349 return true;
350
351 case UPDATE:
352 dbKey = idMapping.remove(new Long(id));
353 if (dbKey == null) return false;
354 databaseID = ((Long) dbKey).longValue();
355 storage.completeRecord(databaseID, parameters);
356 return true;
357
358 case REMOVE:
359 dbKey = idMapping.remove(new Long(id));
360 if (dbKey == null) return false;
361 databaseID = ((Long) dbKey).longValue();
362 storage.removeRecord(databaseID);
363 return true;
364
365 }
366 } catch (StopwatchStorageException e) {
367 log.warn("Storage error", e);
368 }
369 return false;
370 }
371
372 /***
373 *
374 * TODO Dokumentacja
375 *
376 * @author Milen Dyankov
377 *
378 */
379 static class BufferItem {
380
381 long id;
382
383 int operation;
384
385 Object[] parameters;
386
387
388 /***
389 * @param id
390 * @param operation
391 * @param parameters
392 */
393 public BufferItem(long id, int operation, Object[] parameters) {
394 this.id = id;
395 this.operation = operation;
396 this.parameters = parameters;
397 }
398
399 }
400
401 }