001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.lifecycle;
031:
032: import com.caucho.util.Alarm;
033:
034: import java.lang.ref.WeakReference;
035: import java.util.ArrayList;
036: import java.util.logging.Level;
037: import java.util.logging.Logger;
038:
039: /**
040: * Lifecycle class.
041: */
042: public final class Lifecycle implements LifecycleState {
043: private final Logger _log;
044: private String _name;
045:
046: private Level _level = Level.FINE;
047: private Level _lowLevel = Level.FINER;
048:
049: private int _state;
050:
051: private long _activeCount;
052: private long _failCount;
053:
054: private long _lastFailTime;
055: private long _lastChangeTime;
056:
057: private ArrayList<WeakReference<LifecycleListener>> _listeners;
058:
059: /**
060: * Creates an anonymous lifecycle.
061: */
062: public Lifecycle() {
063: _log = null;
064: }
065:
066: /**
067: * Creates an lifecycle with logger and name.
068: */
069: public Lifecycle(Logger log) {
070: _log = log;
071: }
072:
073: /**
074: * Creates an lifecycle with logger and name.
075: */
076: public Lifecycle(Logger log, String name) {
077: _log = log;
078: _name = name;
079: }
080:
081: /**
082: * Creates an lifecycle with logger, a name, and a level.
083: */
084: public Lifecycle(Logger log, String name, Level level) {
085: _log = log;
086: _name = name;
087:
088: setLevel(level);
089: }
090:
091: /**
092: * Gets the lifecycle name.
093: */
094: public String getName() {
095: return _name;
096: }
097:
098: /**
099: * Sets the lifecycle name, and the level to Level.INFO.
100: */
101: public void setName(String name) {
102: _name = name;
103: }
104:
105: /**
106: * Gets the lifecycle logging level.
107: */
108: public Level getLevel() {
109: return _level;
110: }
111:
112: /**
113: * Sets the lifecycle logging level.
114: */
115: public void setLevel(Level level) {
116: _level = level;
117:
118: if (level.intValue() < _lowLevel.intValue())
119: _lowLevel = level;
120: }
121:
122: /**
123: * Adds a listener to detect lifecycle changes.
124: */
125: public void addListener(LifecycleListener listener) {
126: synchronized (this ) {
127: if (isDestroyed()) {
128: IllegalStateException e = new IllegalStateException(
129: "attempted to add listener to a destroyed lifecyle "
130: + this );
131:
132: if (_log != null)
133: _log.log(Level.WARNING, e.toString(), e);
134: else
135: Logger.getLogger(Lifecycle.class.getName()).log(
136: Level.WARNING, e.toString(), e);
137:
138: return;
139: }
140:
141: if (_listeners == null)
142: _listeners = new ArrayList<WeakReference<LifecycleListener>>();
143:
144: for (int i = _listeners.size() - 1; i >= 0; i--) {
145: LifecycleListener oldListener = _listeners.get(i).get();
146:
147: if (listener == oldListener)
148: return;
149: else if (oldListener == null)
150: _listeners.remove(i);
151: }
152:
153: _listeners.add(new WeakReference<LifecycleListener>(
154: listener));
155: }
156: }
157:
158: /**
159: * Removes a listener.
160: */
161: public void removeListener(LifecycleListener listener) {
162: synchronized (this ) {
163:
164: if (_listeners == null)
165: return;
166:
167: for (int i = _listeners.size() - 1; i >= 0; i--) {
168: LifecycleListener oldListener = _listeners.get(i).get();
169:
170: if (listener == oldListener) {
171: _listeners.remove(i);
172:
173: return;
174: } else if (oldListener == null)
175: _listeners.remove(i);
176: }
177: }
178: }
179:
180: /**
181: * Returns the listeners.
182: */
183: private void notifyListeners(int oldState, int newState) {
184: synchronized (this ) {
185: if (_listeners == null) {
186: return;
187: } else {
188: for (int i = 0; i < _listeners.size(); i++) {
189: LifecycleListener listener = _listeners.get(i)
190: .get();
191:
192: if (listener != null) {
193: listener.lifecycleEvent(oldState, newState);
194: } else {
195: _listeners.remove(i);
196: i--;
197: }
198: }
199: }
200: }
201: }
202:
203: /**
204: * Returns the current state.
205: */
206: public int getState() {
207: return _state;
208: }
209:
210: /**
211: * Returns the state name for the passed state.
212: */
213: public static String getStateName(int state) {
214: switch (state) {
215: case IS_NEW:
216: return "new";
217: case IS_INITIALIZING:
218: return "initializing";
219: case IS_INIT:
220: return "init";
221: case IS_STARTING:
222: return "starting";
223: case IS_ACTIVE:
224: return "active";
225: case IS_FAILED:
226: return "failed";
227: case IS_STOPPING:
228: return "stopping";
229: case IS_STOPPED:
230: return "stopped";
231: case IS_DESTROYING:
232: return "destroying";
233: case IS_DESTROYED:
234: return "destroyed";
235: default:
236: return "unknown";
237: }
238: }
239:
240: /**
241: * Returns the current state name.
242: */
243: public String getStateName() {
244: return getStateName(_state);
245: }
246:
247: /**
248: * Returns the last lifecycle change time.
249: */
250: public long getLastChangeTime() {
251: return _lastChangeTime;
252: }
253:
254: /**
255: * Returns the last failure time.
256: */
257: public long getLastFailTime() {
258: return _lastFailTime;
259: }
260:
261: /**
262: * Returns the number of times the lifecycle has switched to active.
263: */
264: public long getActiveCount() {
265: return _activeCount;
266: }
267:
268: /**
269: * Returns the number of times the lifecycle has switched to failing.
270: */
271: public long getFailCount() {
272: return _failCount;
273: }
274:
275: /**
276: * Returns true for the initializing state.
277: */
278: public boolean isInitializing() {
279: return _state == IS_INITIALIZING;
280: }
281:
282: /**
283: * Returns true for the init state.
284: */
285: public boolean isInit() {
286: return _state == IS_INIT;
287: }
288:
289: /**
290: * Returns true for the init state.
291: */
292: public boolean isBeforeInit() {
293: return _state < IS_INIT;
294: }
295:
296: /**
297: * Returns true for the init state.
298: */
299: public boolean isAfterInit() {
300: return _state >= IS_INIT;
301: }
302:
303: /**
304: * Returns true if the service is starting.
305: */
306: public boolean isStarting() {
307: return _state == IS_STARTING;
308: }
309:
310: /**
311: * Returns true for the warmup state.
312: */
313: public boolean isWarmup() {
314: return _state == IS_WARMUP;
315: }
316:
317: /**
318: * Returns true for the initializing state.
319: */
320: public boolean isBeforeActive() {
321: return _state < IS_ACTIVE;
322: }
323:
324: /**
325: * Returns true for the closing states
326: */
327: public boolean isAfterActive() {
328: return IS_ACTIVE < _state;
329: }
330:
331: /**
332: * Wait for a period of time until the service starts.
333: */
334: public boolean waitForActive(long timeout) {
335: if (_state == IS_ACTIVE)
336: return true;
337:
338: long waitEnd = Alarm.getCurrentTime() + timeout;
339:
340: synchronized (this ) {
341: while (Alarm.getCurrentTime() < waitEnd) {
342: if (_state == IS_ACTIVE)
343: return true;
344: else if (IS_ACTIVE < _state)
345: return false;
346: else if (Alarm.isTest())
347: return false;
348:
349: try {
350: wait(waitEnd - Alarm.getCurrentTime());
351: } catch (InterruptedException e) {
352: }
353: }
354: }
355:
356: return _state == IS_ACTIVE;
357: }
358:
359: /**
360: * Returns true for the active state.
361: */
362: public boolean isActive() {
363: return _state == IS_ACTIVE;
364: }
365:
366: /**
367: * Returns true for the a runnable state, including warmup
368: */
369: public boolean isRunnable() {
370: return IS_WARMUP <= _state && _state <= IS_ACTIVE;
371: }
372:
373: /**
374: * Returns true for the failed state.
375: */
376: public boolean isError() {
377: return isFailed();
378: }
379:
380: /**
381: * Returns true for the failed state.
382: */
383: public boolean isFailed() {
384: return _state == IS_FAILED;
385: }
386:
387: /**
388: * Returns true if the state is stopping.
389: */
390: public boolean isStopping() {
391: return IS_STOPPING <= _state;
392: }
393:
394: /**
395: * Returns true if the state is stopping.
396: */
397: public boolean isStopped() {
398: return IS_STOPPING <= _state;
399: }
400:
401: /**
402: * Returns true if the state is closed
403: */
404: public boolean isDestroying() {
405: return IS_DESTROYING <= _state;
406: }
407:
408: /**
409: * Returns true if the state is closed
410: */
411: public boolean isDestroyed() {
412: return IS_DESTROYED <= _state;
413: }
414:
415: /**
416: * Changes to the initializing state.
417: *
418: * @return true if the transition is allowed
419: */
420: public synchronized boolean toInitializing() {
421: if (IS_INITIALIZING <= _state)
422: return false;
423:
424: int oldState = _state;
425:
426: _state = IS_INITIALIZING;
427: _lastChangeTime = Alarm.getCurrentTime();
428:
429: if (_log != null && _log.isLoggable(_lowLevel))
430: _log.log(_lowLevel, _name + " initializing");
431:
432: notifyListeners(oldState, _state);
433:
434: return true;
435: }
436:
437: /**
438: * Changes to the init state.
439: *
440: * @return true if the transition is allowed
441: */
442: public synchronized boolean toInit() {
443: if (IS_INIT <= _state)
444: return false;
445:
446: int oldState = _state;
447:
448: _state = IS_INIT;
449: _lastChangeTime = Alarm.getCurrentTime();
450:
451: if (_log != null && _log.isLoggable(_lowLevel))
452: _log.log(_lowLevel, _name + " initialized");
453:
454: notifyListeners(oldState, _state);
455:
456: return true;
457: }
458:
459: /**
460: * Changes to the init from the stopped state.
461: *
462: * @return true if the transition is allowed
463: */
464: public synchronized boolean toPostInit() {
465: if (IS_STOPPED == _state) {
466: int oldState = _state;
467:
468: _state = IS_INIT;
469: _lastChangeTime = Alarm.getCurrentTime();
470:
471: notifyListeners(oldState, _state);
472:
473: return true;
474: } else if (IS_INIT == _state) {
475: return true;
476: }
477:
478: return false;
479: }
480:
481: /**
482: * Changes to the starting state.
483: *
484: * @return true if the transition is allowed
485: */
486: public synchronized boolean toStarting() {
487: if (_state < IS_STARTING || _state == IS_STOPPED) {
488: int oldState = _state;
489:
490: _state = IS_STARTING;
491: _lastChangeTime = Alarm.getCurrentTime();
492:
493: if (_log != null && _log.isLoggable(Level.FINE)
494: && _log.isLoggable(_level))
495: _log.fine(_name + " starting");
496:
497: notifyListeners(oldState, _state);
498:
499: return true;
500: } else
501: return false;
502: }
503:
504: /**
505: * Changes to the active state.
506: *
507: * @return true if the transition is allowed
508: */
509: public synchronized boolean toActive() {
510: if (_state < IS_ACTIVE || IS_FAILED <= _state
511: && _state <= IS_STOPPED) {
512: int oldState = _state;
513:
514: _state = IS_ACTIVE;
515: _lastChangeTime = Alarm.getCurrentTime();
516: _activeCount++;
517:
518: if (_log != null && _log.isLoggable(_level))
519: _log.log(_level, _name + " active");
520:
521: notifyListeners(oldState, _state);
522:
523: notifyAll();
524:
525: return true;
526: } else
527: return false;
528: }
529:
530: /**
531: * Changes to the error state.
532: *
533: * @return true if the transition is allowed
534: */
535: public boolean toError() {
536: return toFail();
537: }
538:
539: /**
540: * Changes to the failed state.
541: *
542: * @return true if the transition is allowed
543: */
544: public synchronized boolean toFail() {
545: if (_state < IS_DESTROYING && _state != IS_FAILED) {
546: int oldState = _state;
547:
548: _state = IS_FAILED;
549: _lastChangeTime = Alarm.getCurrentTime();
550: _failCount++;
551:
552: if (_log != null && _log.isLoggable(_level))
553: _log.log(_level, _name + " error");
554:
555: notifyListeners(oldState, _state);
556:
557: notifyAll();
558:
559: return true;
560: } else
561: return false;
562: }
563:
564: /**
565: * Changes to the stopping state.
566: *
567: * @return true if the transition is allowed
568: */
569: public synchronized boolean toStopping() {
570: if (_state < IS_STOPPING && _state != IS_STARTING) {
571: int oldState = _state;
572:
573: _state = IS_STOPPING;
574: _lastChangeTime = Alarm.getCurrentTime();
575:
576: if (_log != null && _log.isLoggable(_level))
577: _log.log(_level, _name + " stopping");
578:
579: notifyListeners(oldState, _state);
580:
581: return true;
582: } else
583: return false;
584: }
585:
586: /**
587: * Changes to the stopped state.
588: *
589: * @return true if the transition is allowed
590: */
591: public synchronized boolean toStop() {
592: if (_state < IS_STOPPED) {
593: if (_log == null) {
594: } else if (_state < IS_STOPPING && _log.isLoggable(_level))
595: _log.log(_level, _name + " stopped");
596: else if (_log.isLoggable(_lowLevel))
597: _log.log(_lowLevel, _name + " stopped");
598:
599: int oldState = _state;
600:
601: _state = IS_STOPPED;
602: _lastChangeTime = Alarm.getCurrentTime();
603:
604: notifyListeners(oldState, _state);
605:
606: notifyAll();
607:
608: return true;
609: } else
610: return false;
611: }
612:
613: /**
614: * Changes to the destroying state.
615: *
616: * @return true if the transition is allowed
617: */
618: public synchronized boolean toDestroying() {
619: if (_state < IS_DESTROYING) {
620: int oldState = _state;
621:
622: _state = IS_DESTROYING;
623: _lastChangeTime = Alarm.getCurrentTime();
624:
625: if (_log != null && _log.isLoggable(_lowLevel))
626: _log.log(_lowLevel, _name + " destroying");
627:
628: notifyListeners(oldState, _state);
629:
630: return true;
631: } else
632: return false;
633: }
634:
635: /**
636: * Changes to the closed state.
637: *
638: * @return true if the transition is allowed
639: */
640: public synchronized boolean toDestroy() {
641: if (_state < IS_DESTROYED) {
642: int oldState = _state;
643:
644: _state = IS_DESTROYED;
645: _lastChangeTime = Alarm.getCurrentTime();
646:
647: if (_log != null && _log.isLoggable(_lowLevel))
648: _log.log(_lowLevel, _name + " destroyed");
649:
650: notifyListeners(oldState, _state);
651:
652: notifyAll();
653:
654: return true;
655: } else
656: return false;
657: }
658:
659: /**
660: * Copies from a target state.
661: *
662: * @return true if the transition is allowed
663: */
664: public void copyState(Lifecycle source) {
665: _state = source._state;
666: }
667:
668: /**
669: * Debug string value.
670: */
671: public String toString() {
672: if (_name != null)
673: return "Lifecycle[" + _name + ", " + getStateName() + "]";
674: else
675: return "Lifecycle[" + getStateName() + "]";
676: }
677: }
|