001: /*
002: * $Id: Manageable.java,v 1.33 2007/03/15 17:08:27 agoubard Exp $
003: *
004: * Copyright 2003-2007 Orange Nederland Breedband B.V.
005: * See the COPYRIGHT file for redistribution and use restrictions.
006: */
007: package org.xins.common.manageable;
008:
009: import org.xins.common.MandatoryArgumentChecker;
010: import org.xins.common.collections.InvalidPropertyValueException;
011: import org.xins.common.collections.MissingRequiredPropertyException;
012: import org.xins.common.collections.PropertyReader;
013: import org.xins.common.collections.PropertyReaderUtils;
014:
015: /**
016: * Abstraction of a manageable object. Abstract base class for classes that
017: * support bootstrap, initialization and deinitialization functions.
018: *
019: * <p>In environments where <code>Manageable</code> instances are constructed
020: * dynamically, they are typically expected to have a public no-argument
021: * constructor.
022: *
023: * <p>Initially the state of a manageable object is {@link #UNUSABLE}. In this
024: * state, the object should be considered unusable. To change to the
025: * {@link #USABLE} state, the {@link #bootstrap(PropertyReader)} and
026: * {@link #init(PropertyReader)} methods should be called first, as described
027: * below.
028: *
029: * <p>The {@link #bootstrap(PropertyReader)} method can only be called if the
030: * state of this object is {@link #UNUSABLE}. If it finishes successfully, the
031: * state then changes to {@link #BOOTSTRAPPED}.
032: *
033: * <p>After that the {@link #init(PropertyReader)} method should be called to
034: * initialize or re-initialize this object. This method can only be called
035: * successfully if the current state is either {@link #BOOTSTRAPPED} or even
036: * {@link #USABLE}.
037: *
038: * <p>The {@link #deinit()} method is called when this object is no
039: * longer needed. That changes the state back to {@link #UNUSABLE}. After
040: * that, {@link #bootstrap(PropertyReader)} could be called again, though.
041: *
042: * @version $Revision: 1.33 $ $Date: 2007/03/15 17:08:27 $
043: * @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
044: *
045: * @since XINS 1.0.0
046: */
047: public abstract class Manageable {
048:
049: /**
050: * The <em>UNUSABLE</em> state.
051: */
052: public static final State UNUSABLE = new State(0, "UNUSABLE");
053:
054: /**
055: * The <em>BOOTSTRAPPING</em> state.
056: */
057: public static final State BOOTSTRAPPING = new State(2,
058: "BOOTSTRAPPING");
059:
060: /**
061: * The <em>BOOTSTRAPPED</em> state.
062: */
063: public static final State BOOTSTRAPPED = new State(3,
064: "BOOTSTRAPPED");
065:
066: /**
067: * The <em>INITIALIZING</em> state.
068: */
069: public static final State INITIALIZING = new State(4,
070: "INITIALIZING");
071:
072: /**
073: * The <em>USABLE</em> state.
074: */
075: public static final State USABLE = new State(5, "USABLE");
076:
077: /**
078: * The <em>DEINITIALIZING</em> state.
079: */
080: public static final State DEINITIALIZING = new State(1,
081: "DEINITIALIZING");
082:
083: /**
084: * The state of this manageable object.
085: */
086: private State _state;
087:
088: /**
089: * The lock for the state object.
090: */
091: private Object _stateLock;
092:
093: /**
094: * Constructs a new <code>Manageable</code>.
095: */
096: protected Manageable() {
097: _state = UNUSABLE;
098: _stateLock = new Object();
099: }
100:
101: /**
102: * Gets the current state of this object.
103: *
104: * @return
105: * the current state, never <code>null</code>.
106: */
107: public final State getState() {
108: return _state;
109: }
110:
111: /**
112: * Performs the bootstrap procedure (wrapper method).
113: *
114: * <p>If the state of this object is valid (it must be {@link #UNUSABLE})
115: * and the argument is not <code>null</code>, then
116: * {@link #bootstrapImpl(PropertyReader)} will be called. If that method
117: * succeeds, then this object will be left in the {@link #BOOTSTRAPPED}
118: * state.
119: *
120: * <p>If {@link #bootstrapImpl(PropertyReader)} throws any exception (even
121: * {@link Error}s), it is wrapped in an {@link BootstrapException} and then
122: * the latter is thrown instead.
123: *
124: * @param properties
125: * the bootstrap properties, can be <code>null</code>.
126: *
127: * @throws IllegalStateException
128: * if the current state is not {@link #UNUSABLE}.
129: *
130: * @throws MissingRequiredPropertyException
131: * if a required property is not given.
132: *
133: * @throws InvalidPropertyValueException
134: * if the value of a certain property is invalid.
135: *
136: * @throws BootstrapException
137: * if the bootstrapping failed for any other reason.
138: */
139: public final void bootstrap(PropertyReader properties)
140: throws IllegalStateException,
141: MissingRequiredPropertyException,
142: InvalidPropertyValueException, BootstrapException {
143:
144: State erroneousState = null;
145:
146: // Get the current state and change to BOOTSTRAPPING if it is valid
147: synchronized (_stateLock) {
148: if (_state != UNUSABLE) {
149: erroneousState = _state;
150: } else {
151: _state = BOOTSTRAPPING;
152: }
153: }
154:
155: // If the state was invalid, then fail
156: if (erroneousState != null) {
157: final String MESSAGE = "The current state is "
158: + erroneousState + " instead of UNUSABLE.";
159: throw new IllegalStateException(MESSAGE);
160: }
161:
162: // If no properties are passed, then use an empty set
163: if (properties == null) {
164: properties = PropertyReaderUtils.EMPTY_PROPERTY_READER;
165: }
166:
167: // Delegate to subclass
168: State newState = UNUSABLE;
169: try {
170: bootstrapImpl(properties);
171: newState = BOOTSTRAPPED;
172:
173: // Catch expected exceptions
174: } catch (MissingRequiredPropertyException exception) {
175: throw exception;
176: } catch (InvalidPropertyValueException exception) {
177: throw exception;
178: } catch (BootstrapException exception) {
179: throw exception;
180:
181: // Wrap other exceptions in an InitializationException
182: } catch (Throwable exception) {
183: throw new BootstrapException(exception);
184:
185: // Always set the state before returning
186: } finally {
187: synchronized (_stateLock) {
188: _state = newState;
189: }
190: }
191: }
192:
193: /**
194: * Performs the bootstrap procedure (actual implementation). When this
195: * method is called from {@link #bootstrap(PropertyReader)}, the state and
196: * the argument will have been checked and the state will have been set to
197: * {@link #BOOTSTRAPPING}.
198: *
199: * <p>The implementation of this method in class {@link Manageable} is
200: * empty.
201: *
202: * @param properties
203: * the bootstrap properties, not <code>null</code>.
204: *
205: * @throws MissingRequiredPropertyException
206: * if a required property is not given.
207: *
208: * @throws InvalidPropertyValueException
209: * if the value of a certain property is invalid.
210: *
211: * @throws BootstrapException
212: * if the bootstrapping failed for any other reason.
213: */
214: protected void bootstrapImpl(PropertyReader properties)
215: throws MissingRequiredPropertyException,
216: InvalidPropertyValueException, BootstrapException {
217: // empty
218: }
219:
220: /**
221: * Performs the initialization procedure (wrapper method).
222: *
223: * <p>If the state of this object is valid (it must be either
224: * {@link #BOOTSTRAPPED} or {@link #USABLE}) and the argument is not
225: * <code>null</code>, then {@link #initImpl(PropertyReader)} will be
226: * called. If that method succeeds, then this object will be left in the
227: * {@link #USABLE} state. If an exception is thrown, then this object will
228: * be left in the {@link #BOOTSTRAPPED} state instead.
229: *
230: * <p>If {@link #initImpl(PropertyReader)} throws any exception (even
231: * {@link Error}s), it is wrapped in an {@link InitializationException} and
232: * then the latter is thrown instead.
233: *
234: * @param properties
235: * the initialization properties, can be <code>null</code>.
236: *
237: * @throws IllegalStateException
238: * if the current state is not {@link #BOOTSTRAPPED} or {@link #USABLE}.
239: *
240: * @throws MissingRequiredPropertyException
241: * if a required property is not given.
242: *
243: * @throws InvalidPropertyValueException
244: * if the value of a certain property is invalid.
245: *
246: * @throws InitializationException
247: * if the initialization failed for any other reason.
248: */
249: public final void init(PropertyReader properties)
250: throws IllegalStateException,
251: MissingRequiredPropertyException,
252: InvalidPropertyValueException, InitializationException {
253:
254: State erroneousState = null;
255:
256: // Get the current state and change to INITIALIZING if it is valid
257: synchronized (_stateLock) {
258: if (_state != BOOTSTRAPPED && _state != USABLE) {
259: erroneousState = _state;
260: } else {
261: _state = INITIALIZING;
262: }
263: }
264:
265: // If the state was invalid, then fail
266: if (erroneousState != null) {
267: String message = "The current state is " + erroneousState
268: + " instead of either " + BOOTSTRAPPED + " or "
269: + USABLE + '.';
270: throw new IllegalStateException(message);
271: }
272:
273: // If no properties are passed, then use an empty set
274: if (properties == null) {
275: properties = PropertyReaderUtils.EMPTY_PROPERTY_READER;
276: }
277:
278: // Delegate to subclass
279: State newState = BOOTSTRAPPED;
280: try {
281: initImpl(properties);
282: newState = USABLE;
283:
284: // Catch expected exceptions
285: } catch (MissingRequiredPropertyException exception) {
286: throw exception;
287: } catch (InvalidPropertyValueException exception) {
288: throw exception;
289: } catch (InitializationException exception) {
290: throw exception;
291:
292: // Wrap other exceptions in an InitializationException
293: } catch (Throwable exception) {
294: throw new InitializationException(exception);
295:
296: // Always set the state before returning
297: } finally {
298: synchronized (_stateLock) {
299: _state = newState;
300: }
301: }
302: }
303:
304: /**
305: * Performs the initialization procedure (actual implementation). When this
306: * method is called from {@link #init(PropertyReader)}, the state and the
307: * argument will have been checked and the state will have been set to
308: * {@link #INITIALIZING}.
309: *
310: * <p>The implementation of this method in class {@link Manageable} is
311: * empty.
312: *
313: * @param properties
314: * the initialization properties, not <code>null</code>.
315: *
316: * @throws MissingRequiredPropertyException
317: * if a required property is not given.
318: *
319: * @throws InvalidPropertyValueException
320: * if the value of a certain property is invalid.
321: *
322: * @throws InitializationException
323: * if the initialization failed, for any other reason.
324: */
325: protected void initImpl(PropertyReader properties)
326: throws MissingRequiredPropertyException,
327: InvalidPropertyValueException, InitializationException {
328: // empty
329: }
330:
331: /**
332: * Deinitializes this instance (wrapper method). This method relies on
333: * {@link #deinitImpl()} to actually perform the deinitialization.
334: *
335: * <p>The current state of this object must be either {@link #BOOTSTRAPPED}
336: * or {@link #USABLE}.
337: *
338: * <p>When this method returns, the state has been set to
339: * {@link #UNUSABLE}, even if {@link #deinitImpl()} threw an exception.
340: *
341: * <p>If {@link #deinitImpl()} throws any exception, it is wrapped in a
342: * {@link DeinitializationException} and
343: * then the latter is thrown instead.
344: *
345: * @throws IllegalStateException
346: * if the state is not {@link #BOOTSTRAPPED} nor {@link #USABLE}.
347: *
348: * @throws DeinitializationException
349: * if the deinitialization caused an exception in
350: * {@link #deinitImpl()}.
351: */
352: public final void deinit() throws IllegalStateException,
353: DeinitializationException {
354:
355: State erroneousState = null;
356:
357: // Get the current state and change to DEINITIALIZING if it is valid
358: synchronized (_stateLock) {
359: if (_state != BOOTSTRAPPED && _state != USABLE) {
360: erroneousState = _state;
361: } else {
362: _state = DEINITIALIZING;
363: }
364: }
365:
366: // If the state was invalid, then fail
367: if (erroneousState != null) {
368: String message = "The current state is " + erroneousState
369: + " instead of either " + BOOTSTRAPPED + " or "
370: + USABLE + '.';
371: throw new IllegalStateException(message);
372: }
373:
374: // Delegate to subclass
375: State newState = BOOTSTRAPPED;
376: try {
377: deinitImpl();
378: newState = UNUSABLE;
379:
380: // Catch and wrap all caught exceptions
381: } catch (Throwable exception) {
382: throw new DeinitializationException(exception);
383:
384: // Always set the state before returning
385: } finally {
386: synchronized (_stateLock) {
387: _state = newState;
388: }
389: }
390: }
391:
392: /**
393: * Deinitializes this instance (actual implementation). This method will be
394: * called from {@link #deinit()} each time the latter is called and it
395: * finds that the state is correct. The state will have been set to
396: * {@link #DEINITIALIZING}.
397: *
398: * @throws Throwable
399: * if the deinitialization caused an exception.
400: */
401: protected void deinitImpl() throws Throwable {
402: // empty
403: }
404:
405: /**
406: * Determines if this object is currently bootstrapped. Even if this object
407: * is already initialized, then it is still considered bootstrapped.
408: *
409: * @return
410: * <code>true</code> if this object is bootstrapped,
411: * <code>false</code> if it is not.
412: *
413: * @since XINS 1.5.0
414: */
415: public final boolean isBootstrapped() {
416: State state;
417: synchronized (_stateLock) {
418: state = _state;
419: }
420: return state.getLevel() >= BOOTSTRAPPED.getLevel();
421: }
422:
423: /**
424: * Determines if this object is currently usable.
425: *
426: * @return
427: * <code>true</code> if this object is usable,
428: * <code>false</code> if it is not.
429: */
430: public final boolean isUsable() {
431: State state;
432: synchronized (_stateLock) {
433: state = _state;
434: }
435: return state == USABLE;
436: }
437:
438: /**
439: * Asserts that this object is currently usable. If it is not, then an
440: * {@link IllegalStateException} is thrown.
441: *
442: * @throws IllegalStateException
443: * if this object is not in the {@link #USABLE} state.
444: */
445: protected final void assertUsable() throws IllegalStateException {
446:
447: // Minimize the time the lock is held
448: State state;
449: synchronized (_stateLock) {
450: state = _state;
451: }
452:
453: // Construct and throw an exception, if appropriate
454: if (state != USABLE) {
455: String message = "The current state is " + state
456: + " instead of " + USABLE + '.';
457: throw new IllegalStateException(message);
458: }
459: }
460:
461: /**
462: * State of a <code>Manageable</code> object.
463: *
464: * @version $Revision: 1.33 $ $Date: 2007/03/15 17:08:27 $
465: * @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
466: *
467: * @since XINS 1.0.0
468: */
469: public static final class State {
470: /**
471: * Constructs a new <code>State</code> object.
472: *
473: * @param level
474: * the level of this state.
475: *
476: * @param name
477: * the name of this state, cannot be <code>null</code>.
478: *
479: * @throws IllegalArgumentException
480: * if <code>name == null</code>.
481: */
482: private State(int level, String name)
483: throws IllegalArgumentException {
484:
485: // Check preconditions
486: MandatoryArgumentChecker.check("name", name);
487:
488: _level = level;
489: _name = name;
490: }
491:
492: /**
493: * The level of this state.
494: */
495: private final int _level;
496:
497: /**
498: * The name of this state. Cannot be <code>null</code>.
499: */
500: private final String _name;
501:
502: /**
503: * Returns the level of this state.
504: *
505: * @return
506: * the level of this state, cannot be <code>null</code>.
507: */
508: int getLevel() {
509: return _level;
510: }
511:
512: /**
513: * Returns the name of this state.
514: *
515: * @return
516: * the name of this state, cannot be <code>null</code>.
517: */
518: public String getName() {
519: return _name;
520: }
521:
522: /**
523: * Returns a textual representation of this object.
524: *
525: * @return
526: * the name of this state, never <code>null</code>.
527: */
528: public String toString() {
529: return _name;
530: }
531: }
532: }
|