001: /*
002: * $Id$
003: *
004: * Copyright 2006 Commsen International
005: *
006: * Licensed under the Common Public License, Version 1.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.opensource.org/licenses/cpl1.0.txt
011: *
012: * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013: * EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS
014: * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
015: *
016: */
017: package com.commsen.stopwatch.storages;
018:
019: import java.util.Collections;
020: import java.util.HashMap;
021: import java.util.LinkedList;
022: import java.util.Map;
023:
024: import org.apache.log4j.Logger;
025:
026: import com.commsen.stopwatch.StopwatchStorage;
027: import com.commsen.stopwatch.StopwatchStorageException;
028:
029: /**
030: * TODO Dokumentacja
031: *
032: * @author Milen Dyankov
033: *
034: */
035: public class StorageManager implements Runnable {
036:
037: private static final Logger log = Logger
038: .getLogger(StorageManager.class);
039:
040: private static final int SLEEP_TIME = 10;
041:
042: final static int ADD = 0;
043: final static int UPDATE = 1;
044: final static int REMOVE = 2;
045:
046: /**
047: * Storage manager will insert record into database when "start" method is called and
048: * update it when "stop" method is called. NOTE: in this case the time taken to insert the
049: * record into database is added to measurements.
050: */
051: public static final int NORMAL_MODE = 0;
052:
053: /**
054: * Same as {@link #NORMAL_MODE} but storage manager runs in separate thread, thus resulting
055: * in more correct measurements. NOTE: in this case getReport(...) methods may return null
056: * when called soon after "stop" since storage manager may have not managed to store its data.
057: */
058: public static final int THREAD_MODE = 1;
059:
060: /**
061: * Storage manager keeps in memory data gathered on "start" and stores all together when "end" is called.
062: */
063: public static final int DELAYED_MODE = 2;
064:
065: /**
066: *
067: */
068: private StopwatchStorage storage;
069:
070: /**
071: *
072: */
073: private LinkedList toBeProcessed;
074:
075: /**
076: *
077: */
078: private Map idMapping;
079:
080: long id = 0;
081:
082: boolean running;
083:
084: int mode;
085:
086: private Thread[] threadPool;
087:
088: int runningThreads;
089:
090: /**
091: * @param storage
092: * @param threadMode
093: *
094: */
095: public StorageManager(StopwatchStorage storage, int threadMode) {
096: threadPool = new Thread[1];
097: toBeProcessed = new LinkedList();
098: idMapping = Collections.synchronizedMap(new HashMap());
099: this .storage = storage;
100: this .mode = threadMode;
101: runningThreads = 0;
102: }
103:
104: /**
105: *
106: *
107: */
108: public synchronized void start() {
109: doStart(false);
110: }
111:
112: /**
113: *
114: * @see com.commsen.stopwatch.StopwatchEngine#resume()
115: */
116: public synchronized void resume() {
117: doStart(true);
118: }
119:
120: /**
121: *
122: * @param isRestart
123: */
124: private void doStart(boolean isRestart) {
125: if (running)
126: return;
127:
128: try {
129: if (isRestart)
130: storage.unfreeze();
131: else
132: storage.open();
133: } catch (StopwatchStorageException e) {
134: log.warn("Could not initialize stopwatch storage", e);
135: return;
136: }
137:
138: if (mode == THREAD_MODE) {
139: for (int i = 0; i < threadPool.length; i++) {
140: threadPool[i] = new Thread(this ,
141: "Stopwatch storage manager " + i);
142: threadPool[i].setDaemon(true);
143: threadPool[i].start();
144: }
145: }
146: running = true;
147: }
148:
149: /**
150: *
151: *
152: */
153: public synchronized void stop() {
154: doStop(false);
155: }
156:
157: /**
158: *
159: *
160: */
161: public synchronized void pause() {
162: doStop(true);
163: }
164:
165: /**
166: *
167: * @param isPause
168: */
169: private void doStop(boolean isPause) {
170: running = false;
171:
172: while (runningThreads > 0) {
173: try {
174: Thread.sleep(SLEEP_TIME);
175: } catch (InterruptedException e) {
176: // wake up
177: }
178: }
179:
180: try {
181: if (isPause)
182: storage.freeze();
183: else
184: storage.close();
185: } catch (StopwatchStorageException e) {
186: log.warn("Storage error", e);
187: }
188: }
189:
190: /**
191: * @see java.lang.Runnable#run()
192: */
193: public void run() {
194: runningThreads++;
195: while (running) {
196: try {
197: Thread.sleep(SLEEP_TIME);
198: } catch (InterruptedException e) {
199: // wake up
200: }
201: processItems();
202: }
203: runningThreads--;
204: }
205:
206: /**
207: *
208: * @param parameters
209: * @return
210: */
211: public long newRecord(Object[] parameters) {
212: switch (mode) {
213: case NORMAL_MODE:
214: synchronized (this ) {
215: processSingleItem(++id, ADD, parameters);
216: return id;
217: }
218:
219: case THREAD_MODE:
220: synchronized (toBeProcessed) {
221: toBeProcessed.addLast(new BufferItem(++id, ADD,
222: parameters));
223: return id;
224: }
225:
226: case DELAYED_MODE:
227: synchronized (idMapping) {
228: idMapping.put(new Long(++id), parameters);
229: return id;
230: }
231: }
232: return -1;
233: }
234:
235: /**
236: *
237: * @param id
238: * @param parameters
239: * @return
240: */
241: public boolean completeRecord(long id, Object[] parameters) {
242:
243: switch (mode) {
244: case NORMAL_MODE:
245: synchronized (this ) {
246: return processSingleItem(id, UPDATE, parameters);
247: }
248:
249: case THREAD_MODE:
250: synchronized (toBeProcessed) {
251: toBeProcessed.addLast(new BufferItem(id, UPDATE,
252: parameters));
253: return true;
254: }
255:
256: case DELAYED_MODE:
257: synchronized (idMapping) {
258: Object[] startParameters = (Object[]) idMapping
259: .remove(new Long(id));
260: if (startParameters == null)
261: return false;
262: try {
263: storage.newCompleteRecord(startParameters,
264: parameters);
265: } catch (StopwatchStorageException e) {
266: log.warn("Storage error", e);
267: return false;
268: }
269: return true;
270: }
271: }
272: return false;
273:
274: }
275:
276: /**
277: *
278: * @param id
279: * @return
280: */
281: public boolean removeRecord(long id) {
282:
283: switch (mode) {
284: case NORMAL_MODE:
285: synchronized (this ) {
286: return processSingleItem(id, REMOVE, null);
287: }
288:
289: case THREAD_MODE:
290: synchronized (toBeProcessed) {
291: toBeProcessed.addLast(new BufferItem(id, REMOVE, null));
292: return true;
293: }
294:
295: case DELAYED_MODE:
296: synchronized (idMapping) {
297: return idMapping.remove(new Long(id)) != null;
298: }
299: }
300: return false;
301:
302: }
303:
304: /**
305: *
306: *
307: */
308: private void processItems() {
309: BufferItem bufferItem = null;
310: synchronized (toBeProcessed) {
311: if (!toBeProcessed.isEmpty())
312: bufferItem = (BufferItem) toBeProcessed.removeFirst();
313: }
314:
315: while (bufferItem != null) {
316: processSingleItem(bufferItem.id, bufferItem.operation,
317: bufferItem.parameters);
318:
319: synchronized (toBeProcessed) {
320: if (!toBeProcessed.isEmpty())
321: bufferItem = (BufferItem) toBeProcessed
322: .removeFirst();
323: else
324: bufferItem = null;
325: }
326:
327: }
328:
329: }
330:
331: /**
332: * @param id
333: * @param operation
334: * @param parameters
335: * @param bufferItem
336: */
337: private boolean processSingleItem(long id, int operation,
338: Object[] parameters) {
339: long databaseID;
340: Object dbKey;
341: try {
342: switch (operation) {
343: case ADD:
344: databaseID = storage.newRecord(parameters);
345: idMapping.put(new Long(id), new Long(databaseID));
346: return true;
347:
348: case UPDATE:
349: dbKey = idMapping.remove(new Long(id));
350: if (dbKey == null)
351: return false;
352: databaseID = ((Long) dbKey).longValue();
353: storage.completeRecord(databaseID, parameters);
354: return true;
355:
356: case REMOVE:
357: dbKey = idMapping.remove(new Long(id));
358: if (dbKey == null)
359: return false;
360: databaseID = ((Long) dbKey).longValue();
361: storage.removeRecord(databaseID);
362: return true;
363:
364: }
365: } catch (StopwatchStorageException e) {
366: log.warn("Storage error", e);
367: }
368: return false;
369: }
370:
371: /**
372: *
373: * TODO Dokumentacja
374: *
375: * @author Milen Dyankov
376: *
377: */
378: class BufferItem {
379:
380: long id;
381:
382: int operation;
383:
384: Object[] parameters;
385:
386: /**
387: * @param id
388: * @param operation
389: * @param parameters
390: */
391: public BufferItem(long id, int operation, Object[] parameters) {
392: this.id = id;
393: this.operation = operation;
394: this.parameters = parameters;
395: }
396:
397: }
398:
399: }
|