View Javadoc

1   /*
2    * $Id$
3    *
4    * Copyright 2006 Commsen International
5    * 
6    * Licensed under the Common Public License, Version 1.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    * 
10   *      http://www.opensource.org/licenses/cpl1.0.txt
11   * 
12   * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
13   * EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS 
14   * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
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 				// wake up
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 				// wake up
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 }