0001: /*
0002: * The contents of this file are subject to the Sapient Public License
0003: * Version 1.0 (the "License"); you may not use this file except in compliance
0004: * with the License. You may obtain a copy of the License at
0005: * http://carbon.sf.net/License.html.
0006: *
0007: * Software distributed under the License is distributed on an "AS IS" basis,
0008: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
0009: * the specific language governing rights and limitations under the License.
0010: *
0011: * The Original Code is The Carbon Component Framework.
0012: *
0013: * The Initial Developer of the Original Code is Sapient Corporation
0014: *
0015: * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
0016: */
0017:
0018: package org.sape.carbon.core.component.lifecycle;
0019:
0020: import java.util.Collections;
0021: import java.util.HashSet;
0022: import java.util.Set;
0023: import java.lang.reflect.Proxy;
0024: import java.lang.reflect.InvocationHandler;
0025: import java.lang.reflect.Method;
0026:
0027: import org.sape.carbon.core.component.Component;
0028: import org.sape.carbon.core.component.ComponentConfiguration;
0029: import org.sape.carbon.core.component.FunctionalInterface;
0030: import org.sape.carbon.core.component.event.ComponentEvent;
0031: import org.sape.carbon.core.component.event.EventManager;
0032: import org.sape.carbon.core.component.proxy.ComponentProxyInvocationHandler;
0033: import org.sape.carbon.core.component.proxy.Interceptor;
0034: import org.sape.carbon.core.component.proxy.Invocation;
0035: import org.sape.carbon.core.component.proxy.MonitorAcquisitionException;
0036: import org.sape.carbon.core.component.proxy.AbstractInterceptor;
0037: import org.sape.carbon.core.exception.ExceptionUtility;
0038:
0039: import org.apache.commons.logging.Log;
0040: import org.apache.commons.logging.LogFactory;
0041:
0042: /**
0043: * This is the default implementation of the lifecycle interceptor interface.
0044: * This class takes on the responsibility of delegating
0045: *
0046: * Copyright 2002 Sapient
0047: * @since carbon 1.0
0048: * @author Chris Herron, January 2002
0049: * @version $Revision: 1.9 $($Author: ghinkl $ / $Date: 2003/10/15 21:40:19 $)
0050: */
0051: public class DefaultLifecycleInterceptor extends AbstractInterceptor
0052: implements LifecycleInterceptor, Interceptor {
0053:
0054: /** Array of interfaces this proxy adds to a component. */
0055: protected static final Class[] EXPOSED_INTERFACES = new Class[] { LifecycleInterceptor.class };
0056:
0057: /** Provides handle to Apache-commons logger. */
0058: private Log log = LogFactory.getLog(this .getClass());
0059:
0060: /** Name of lifecycle change event. */
0061: public static final String LIFECYCLE_CHANGE_EVENT_NAME = "Component.LifecycleChange";
0062:
0063: /**
0064: * This method must implement the invocation of any necessary actions and
0065: * the chaining the next interceptor.
0066: * @param invocation the invocation to execute
0067: * @return the results of the invocation's execution
0068: *
0069: * @throws Throwable indicates an error in the invocation chain
0070: */
0071: public Object invoke(Invocation invocation) throws Throwable {
0072:
0073: if (invocation.isTargetFunctionalImplementation()) {
0074: // If the target is the component's implementation and we're
0075: // not it a running state, try and wait until we're ready to go
0076: LifecycleStateEnum state = getLifecycleState();
0077:
0078: if (state != LifecycleStateEnum.RUNNING) {
0079: if (state == LifecycleStateEnum.SUSPENDED
0080: || state == LifecycleStateEnum.RESUMING) {
0081:
0082: waitForResume();
0083: } else {
0084: throw new ComponentUnavailableException(
0085: this .getClass(),
0086: "The component is not in a LifecycleStateEnum.RUNNING "
0087: + "state, FunctionalInterface methods "
0088: + "cannot be called. "
0089: + "Component name ["
0090: + getComponentName()
0091: + "], current state [" + state
0092: + "]");
0093: }
0094: }
0095: }
0096:
0097: return callNextInterceptor(invocation);
0098: }
0099:
0100: /**
0101: * This should return the list of interfaces that a decorator wishes to
0102: * expose through the component proxy. This is used by the
0103: * component factory to determine what interfaces the component proxy will
0104: * implement.
0105: *
0106: * @return Class[] an array of interfaces
0107: */
0108: public Class[] getExposedInterfaces() {
0109: return DefaultLifecycleInterceptor.EXPOSED_INTERFACES;
0110: }
0111:
0112: /**
0113: * A reference to the Component instance that is being managed.
0114: * final because once the interceptor in constructed, this should
0115: * not change.
0116: */
0117: private final FunctionalInterface functionalImplementation;
0118:
0119: /**
0120: * The current state associated with the Component. Access to this
0121: * property must be synchronized. Access only via get and set methods.
0122: */
0123: private LifecycleStateEnum currentState = LifecycleStateEnum.CREATING;
0124:
0125: /**
0126: * A reference to the component proxy invocation handler.
0127: * Used to get a reference to the components monitor.
0128: * final because once the interceptor in constructed,
0129: * this should not change.
0130: */
0131: private final ComponentProxyInvocationHandler proxyInvocationHandler;
0132:
0133: /** reference to the complete component */
0134: private Component componentProxy;
0135:
0136: /**
0137: * lock object synchronized upon to block calls while in a suspended
0138: * state, used in waitForResume and internalResumeComponent methods
0139: */
0140: private final Object suspendedLock = new Object();
0141:
0142: /** timeout for blocking calls. */
0143: private long blockedCallTimeout;
0144:
0145: /** timeout for the destroyer thread. */
0146: private long destroyerThreadTimeout;
0147:
0148: /**
0149: * Creates a new DefaultLifecycleInterceptor.
0150: * Sets the initial state to CREATING.
0151: * It is intended that the constructor does not accept as a parameter
0152: * the Component to be managed, since this would circumvent the mutator
0153: * method on the LifecycleInterceptor Interface.
0154: *
0155: * @param componentInstance instance of the component
0156: * @param proxyInvocationHandler invocation handler for the component
0157: * @param configuration configuration for the interceptor
0158: */
0159: public DefaultLifecycleInterceptor(
0160: FunctionalInterface componentInstance,
0161: ComponentProxyInvocationHandler proxyInvocationHandler,
0162: DefaultLifecycleInterceptorConfiguration configuration) {
0163:
0164: this .functionalImplementation = componentInstance;
0165:
0166: this .proxyInvocationHandler = proxyInvocationHandler;
0167:
0168: if (configuration == null) {
0169: this .blockedCallTimeout = DefaultLifecycleInterceptorConfiguration.BlockedCallTimeout;
0170: this .destroyerThreadTimeout = DefaultLifecycleInterceptorConfiguration.DestroyerThreadTimeout;
0171:
0172: } else {
0173: this .blockedCallTimeout = configuration
0174: .getBlockedCallTimeout();
0175: this .destroyerThreadTimeout = configuration
0176: .getDestroyerThreadTimeout();
0177: }
0178: }
0179:
0180: /**
0181: * Gets the current lifecycle state of the component.
0182: * Note the protected corresponding mutator method - this property is
0183: * read-only to external entities.
0184: * @return the lifecycle state of this component
0185: */
0186: public synchronized LifecycleStateEnum getLifecycleState() {
0187: return this .currentState;
0188: }
0189:
0190: /**
0191: * Returns a string representation of the Lifecycle State
0192: *
0193: * @return a string representation of the Lifecycle State
0194: */
0195: public String getLifecycleStateString() {
0196: return getLifecycleState().toString();
0197: }
0198:
0199: /**
0200: * Sets the current lifecycle state
0201: * @param state the new state
0202: */
0203: protected synchronized void setLifecycleState(
0204: LifecycleStateEnum state) {
0205:
0206: if (log.isTraceEnabled()) {
0207: log.trace("Changing [" + getComponentName()
0208: + "] component's state from [" + this .currentState
0209: + "] to [" + state + "]");
0210: }
0211:
0212: // If the component template is so configured to have a decorator
0213: // capable of sending events, then send one.
0214: if (this .componentProxy instanceof EventManager) {
0215: ((EventManager) this .componentProxy)
0216: .sendEvent(new ComponentEvent(this .componentProxy,
0217: LIFECYCLE_CHANGE_EVENT_NAME,
0218: "Lifecycle state is changing from ["
0219: + this .currentState.getName()
0220: + "] to [" + state.getName() + "]"));
0221: }
0222:
0223: this .currentState = state;
0224: }
0225:
0226: /**
0227: * Run ONLY ONCE during the life of a Component, immediately after the
0228: * Component is created. Enforced by having CREATING as the only allowed
0229: * entry state.
0230: *
0231: * <p>Valid Entry States: CREATING </p>
0232: * <p>Interim State: INITIALIZING</p>
0233: * <p>Exit State: STOPPED</p>
0234: *
0235: * @param thisComponent component to be initialized
0236: *
0237: * @throws InvalidStateException When the entry state is not allowed
0238: * @throws OperationNotSupportedException When component does not support
0239: * the requested operation
0240: * @throws StateTransitionException When an error occured while trying to
0241: * complete the lifecycle operation
0242: */
0243: public void initializeComponent(Component this Component)
0244: throws InvalidStateException,
0245:
0246: OperationNotSupportedException, StateTransitionException {
0247:
0248: startLifecycleMethod();
0249:
0250: validateEntryState(
0251: getLifecycleState(),
0252: DefaultLifecycleInterceptor.VALID_ENTRY_STATES_INITIALIZE);
0253:
0254: try {
0255: if (functionalImplementation instanceof Initializable) {
0256: Initializable subject = null;
0257: //Cast the component to Initializable
0258: subject = (Initializable) functionalImplementation;
0259: //Immediately before calling initialize(),
0260: //set state to INITIALIZING
0261: setLifecycleState(LifecycleStateEnum.INITIALIZING);
0262: //Call the component's intialize() method
0263: subject.initialize(this Component);
0264: } else {
0265: logNotSupported(Initializable.class);
0266: }
0267: //Now move the state to STOPPED
0268: setLifecycleState(LifecycleStateEnum.STOPPED);
0269:
0270: } catch (Exception e) {
0271: throw new StateTransitionException(
0272: this .getClass(),
0273: "An Exception occurred while attempting to initialize",
0274: e);
0275:
0276: } finally {
0277: stopLifecycleMethod();
0278: }
0279: }
0280:
0281: /**
0282: * Tells the Component to begin providing its service.
0283: *
0284: * <p>Valid Entry States: STOPPED</p>
0285: * <p>Interim State: STARTING</p>
0286: * <p>Exit State: RUNNING</p>
0287: *
0288: * @throws InvalidStateException When the entry state is not allowed
0289: * @throws OperationNotSupportedException When component does not support
0290: * the requested operation
0291: * @throws StateTransitionException When an error occured while trying to
0292: * complete the lifecycle operation
0293: */
0294: public void startComponent() throws InvalidStateException,
0295: OperationNotSupportedException, StateTransitionException {
0296:
0297: startLifecycleMethod();
0298:
0299: validateEntryState(getLifecycleState(),
0300: DefaultLifecycleInterceptor.VALID_ENTRY_STATES_START);
0301:
0302: try {
0303:
0304: if (functionalImplementation instanceof Startable) {
0305: callComponentStart();
0306: } else {
0307: logNotSupported(Startable.class);
0308: }
0309:
0310: //Now move the state to RUNNING
0311: setLifecycleState(LifecycleStateEnum.RUNNING);
0312:
0313: } finally {
0314: stopLifecycleMethod();
0315: // kill the component if it is in an interim state
0316: if (getLifecycleState() == LifecycleStateEnum.STARTING) {
0317: killInvalidComponent();
0318: }
0319: }
0320: }
0321:
0322: /**
0323: * Tells the Component to cease providing its service.
0324: * This is an opportunity for the Component to complete outstanding
0325: * work.
0326: *
0327: * <p>Valid Entry States: RUNNING, SUSPENDED</p>
0328: * <p>Interim State: STOPPING</p>
0329: * <p>Exit State: STOPPED</p>
0330: *
0331: * @throws InvalidStateException When the entry state is not allowed
0332: * @throws OperationNotSupportedException When component does not support
0333: * the requested operation
0334: * @throws StateTransitionException When an error occured while trying to
0335: * complete the lifecycle operation
0336: */
0337: public void stopComponent() throws InvalidStateException,
0338: OperationNotSupportedException, StateTransitionException {
0339:
0340: startLifecycleMethod();
0341:
0342: validateEntryState(getLifecycleState(),
0343: DefaultLifecycleInterceptor.VALID_ENTRY_STATES_STOP);
0344:
0345: try {
0346:
0347: internalStopComponent();
0348:
0349: } finally {
0350: stopLifecycleMethod();
0351: // kill the component if it is in an interim state
0352: if (getLifecycleState() == LifecycleStateEnum.STOPPING) {
0353: killInvalidComponent();
0354: }
0355: }
0356: }
0357:
0358: /**
0359: * Tells the Component to suspend its service. This is an opportunity for
0360: * the Component to sensibly pause its outstanding work.
0361: *
0362: * <p>Valid Entry States: RUNNING</p>
0363: * <p>Interim State: SUSPENDING</p>
0364: * <p>Exit State: SUSPENDED</p>
0365: *
0366: * @throws InvalidStateException When the entry state is not allowed
0367: * @throws OperationNotSupportedException When component does not support
0368: * the requested operation
0369: * @throws StateTransitionException When an error occured while trying to
0370: * complete the lifecycle operation
0371: */
0372: public void suspendComponent() throws InvalidStateException,
0373: OperationNotSupportedException, StateTransitionException {
0374:
0375: startLifecycleMethod();
0376:
0377: validateEntryState(getLifecycleState(),
0378: DefaultLifecycleInterceptor.VALID_ENTRY_STATES_SUSPEND);
0379:
0380: try {
0381: internalSuspendComponent();
0382:
0383: } finally {
0384: stopLifecycleMethod();
0385: if (getLifecycleState() == LifecycleStateEnum.SUSPENDING) {
0386: killInvalidComponent();
0387: }
0388: }
0389: }
0390:
0391: /**
0392: * Tells the Component to resume its service. The Component can now continue
0393: * its outstanding work.
0394: *
0395: * <p>Valid Entry States: SUSPENDED</p>
0396: * <p>Interim State: RESUMING</p>
0397: * <p>Exit State: RUNNING</p>
0398: *
0399: * @throws InvalidStateException When the entry state is not allowed
0400: * @throws OperationNotSupportedException When component does not support
0401: * the requested operation
0402: * @throws StateTransitionException When an error occured while trying to
0403: * complete the lifecycle operation
0404: */
0405: public void resumeComponent() throws InvalidStateException,
0406: OperationNotSupportedException, StateTransitionException {
0407:
0408: startLifecycleMethod();
0409:
0410: validateEntryState(getLifecycleState(),
0411: DefaultLifecycleInterceptor.VALID_ENTRY_STATES_RESUME);
0412:
0413: try {
0414: internalResumeComponent();
0415:
0416: // Wake an who we're waiting while the component was suspended
0417: synchronized (this .suspendedLock) {
0418: this .suspendedLock.notifyAll();
0419: }
0420: } finally {
0421: stopLifecycleMethod();
0422: if (getLifecycleState() == LifecycleStateEnum.RESUMING) {
0423: killInvalidComponent();
0424: }
0425: }
0426: }
0427:
0428: /**
0429: * Provides a Component with its Configuration. The Component should be
0430: * SUSPENDED or STOPPED, if not already.
0431: * The Component should also be returned to its entry state (RUNNING or
0432: * SUSPENDED) after the main Configure operation has completed.
0433: *
0434: * <p>Valid Entry States: RUNNING, SUSPENDED, STOPPED</p>
0435: * <p>Interim State: CONFIGURING</p>
0436: * <p>Exit States: RUNNING, SUSPENDED, STOPPED (to match entry state)</p>
0437: *
0438: * @param configuration Configuration object to be applied to component
0439: * @throws InvalidStateException When the entry state is not allowed
0440: * @throws OperationNotSupportedException When component does not support
0441: * the requested operation
0442: * @throws StateTransitionException When an error occured while trying to
0443: * complete the lifecycle operation
0444: */
0445: public void configureComponent(ComponentConfiguration configuration)
0446: throws InvalidStateException,
0447: OperationNotSupportedException, StateTransitionException {
0448:
0449: startLifecycleMethod();
0450:
0451: validateEntryState(
0452: getLifecycleState(),
0453: DefaultLifecycleInterceptor.VALID_ENTRY_STATES_CONFIGURE);
0454:
0455: try {
0456: if (functionalImplementation instanceof Configurable) {
0457: callComponentConfigure(configuration);
0458:
0459: } else {
0460: logNotSupported(Configurable.class);
0461: }
0462:
0463: } finally {
0464: stopLifecycleMethod();
0465:
0466: LifecycleStateEnum exitState = getLifecycleState();
0467: if (exitState != LifecycleStateEnum.RUNNING
0468: && exitState != LifecycleStateEnum.SUSPENDED
0469: && exitState != LifecycleStateEnum.STOPPED) {
0470:
0471: killInvalidComponent();
0472: }
0473: }
0474: }
0475:
0476: /**
0477: * Tells the Component prepare to die. This is an opportunity for the
0478: * developer to do some sensible housekeeping to aid Garbage Collection,
0479: * relinquish resources etc. Once in the DESTROYED state, a Component
0480: * cannot be revived. This implementation will wait for
0481: * DESTROY_TIMEOUT_MILLIS for the component to be destroyed. If, after
0482: * that time the component has not been destroyed, it will be logged as
0483: * a warning.
0484: *
0485: * <p>Valid Entry States: All states are valid for entry. If the
0486: * component is in the RUNNING state, it will be stopped first.</p>
0487: * <p>Interim State: DESTROYING</p>
0488: * <p>Exit State: DESTROYED</p>
0489: *
0490: * @throws InvalidStateException When the entry state is not allowed
0491: * @throws OperationNotSupportedException When component does not support
0492: * the requested operation
0493: * @throws StateTransitionException When an error occured while trying to
0494: * complete the lifecycle operation
0495: */
0496: public void destroyComponent()
0497: throws OperationNotSupportedException,
0498: StateTransitionException {
0499:
0500: if (getLifecycleState() == LifecycleStateEnum.DESTROYED) {
0501: // component already destroyed
0502: return;
0503: }
0504:
0505: startLifecycleMethod();
0506:
0507: try {
0508: if (functionalImplementation instanceof Destroyable
0509: || (getLifecycleState() == LifecycleStateEnum.RUNNING && functionalImplementation instanceof Startable)) {
0510:
0511: //Create an anonymous thread that we will use
0512: //to timeout on the destroy operation
0513: Thread destroyer = new Thread(new Runnable() {
0514: public void run() {
0515: try {
0516: // stop the component if it is running
0517: if (getLifecycleState() == LifecycleStateEnum.RUNNING) {
0518:
0519: internalStopComponent();
0520: }
0521:
0522: //Call the component's destroy() method
0523: if (functionalImplementation instanceof Destroyable) {
0524:
0525: // Immediately before calling destroy(),
0526: // set state to DESTROYING
0527: setLifecycleState(LifecycleStateEnum.DESTROYING);
0528:
0529: ((Destroyable) functionalImplementation)
0530: .destroy();
0531: }
0532: } catch (Exception e) {
0533: log
0534: .warn("Exception destroying component ["
0535: + getComponentName()
0536: + "]: "
0537: + e
0538: + ": "
0539: + ExceptionUtility
0540: .printStackTracesToString(e));
0541: }
0542: }
0543: }, "Destroying component " + getComponentName());
0544:
0545: // set the thread to a daemon so it won't halt shutdown
0546: destroyer.setDaemon(true);
0547: destroyer.start();
0548:
0549: //Join the thread with a specified timeout
0550: destroyer.join(this .destroyerThreadTimeout);
0551:
0552: //On timeout, if the destroyer thread is still running
0553: //throw an Exception
0554: if (destroyer.isAlive()) {
0555: String message = "Destroy operation timed out after "
0556: + this .destroyerThreadTimeout
0557: + " milliseconds";
0558:
0559: log.warn(message);
0560: }
0561: } else {
0562: logNotSupported(Destroyable.class);
0563: }
0564:
0565: //Now move the state to DESTROYED
0566: setLifecycleState(LifecycleStateEnum.DESTROYED);
0567:
0568: } catch (InterruptedException ie) {
0569: String message = "Thread interrupted during attempt to destroy";
0570: throw new StateTransitionException(this .getClass(),
0571: message, ie);
0572:
0573: } catch (Exception e) {
0574: String message = "An error occurred while attempting to destroy";
0575: throw new StateTransitionException(this .getClass(),
0576: message, e);
0577:
0578: } finally {
0579: stopLifecycleMethod();
0580: }
0581: }
0582:
0583: /**
0584: * This implementation does not make use of the componentProxy reference
0585: *
0586: * @param componentProxy a reference to the component that
0587: * this interceptor is assisting
0588: */
0589: public void setComponentReference(Component componentProxy) {
0590: this .componentProxy = componentProxy;
0591: }
0592:
0593: /**
0594: * Acquires the component's monitor for writing. If this method is
0595: * called, it is imperitive that stopLifecycleMethod() is called.
0596: * <p>
0597: * Example invocation:<br>
0598: * <pre><code>
0599: * startLifecycleMethod();
0600: * try {
0601: * ...
0602: * } finally {
0603: * stopLifecycleMethod();
0604: * }
0605: * </code></pre>
0606: *
0607: * @see ComponentProxyInvocationHandler#getMonitor()
0608: *
0609: * @throws StateTransitionException if the Thread is interrupted
0610: */
0611: protected void startLifecycleMethod() {
0612: if (log.isTraceEnabled()) {
0613: log
0614: .trace("Acquiring lock to start lifecycle method on component ["
0615: + getComponentName() + "]");
0616: }
0617:
0618: try {
0619: this .proxyInvocationHandler.getMonitor().writeLock()
0620: .attempt(1000);
0621: } catch (InterruptedException ie) {
0622: throw new StateTransitionException(this .getClass(),
0623: "Caught InterruptedException while trying to acquire the "
0624: + "component's monitor", ie);
0625: }
0626: }
0627:
0628: /** Releases the component's monitor */
0629: protected void stopLifecycleMethod() {
0630: if (log.isTraceEnabled()) {
0631: log
0632: .trace("Releasing lifecycle method write lock on component ["
0633: + getComponentName() + "]");
0634: }
0635: this .proxyInvocationHandler.getMonitor().writeLock().release();
0636: }
0637:
0638: /**
0639: * Helper method to do the work of suspending a component. Called by
0640: * suspendComponent and configureComponent. A helper method is used
0641: * so that calling methods can handle threading issues as they require
0642: * while not duplicating code
0643: *
0644: * @throws StateTransitionException if an exception is caught in the
0645: * functional implementations suspend method
0646: * @throws OperationNotSupportedException not thrown by this imlementation,
0647: * but provided for overriding methods
0648: */
0649: protected void internalSuspendComponent()
0650: throws OperationNotSupportedException,
0651: StateTransitionException {
0652:
0653: if (functionalImplementation instanceof Suspendable) {
0654: callComponentSuspend();
0655:
0656: } else {
0657: logNotSupported(Suspendable.class);
0658: }
0659:
0660: //Now move the state to SUSPENDED
0661: setLifecycleState(LifecycleStateEnum.SUSPENDED);
0662: }
0663:
0664: /**
0665: * Helper method to do the work of resuming a component. Called by
0666: * resumeComponent and configureComponent. A helper method is used
0667: * so that calling methods can handle threading issues as they require
0668: * while not duplicating code
0669: *
0670: * @throws StateTransitionException if an exception is caught in the
0671: * functional implementations resume method
0672: * @throws OperationNotSupportedException not thrown by this imlementation,
0673: * but provided for overriding methods
0674: */
0675: protected void internalResumeComponent()
0676: throws OperationNotSupportedException,
0677: StateTransitionException {
0678:
0679: if (functionalImplementation instanceof Suspendable) {
0680: callComponentResume();
0681:
0682: } else {
0683: logNotSupported(Suspendable.class);
0684: }
0685:
0686: //Now move the state to RUNNING
0687: setLifecycleState(LifecycleStateEnum.RUNNING);
0688: }
0689:
0690: /**
0691: * Helper method to do the work of suspending a component. Called by
0692: * suspendComponent and configureComponent. A helper method is used
0693: * so that calling methods can handle threading issues as they require
0694: * while not duplicating code
0695: *
0696: * @throws StateTransitionException if an exception is caught in the
0697: * functional implementations suspend method
0698: * @throws OperationNotSupportedException not thrown by this imlementation,
0699: * but provided for overriding methods
0700: */
0701: protected void internalStopComponent()
0702: throws OperationNotSupportedException,
0703: StateTransitionException {
0704:
0705: if (functionalImplementation instanceof Startable) {
0706: callComponentStop();
0707:
0708: } else {
0709: logNotSupported(Startable.class);
0710: }
0711:
0712: //Now move the state to SUSPENDED
0713: setLifecycleState(LifecycleStateEnum.STOPPED);
0714: }
0715:
0716: /**
0717: * Waits for the component to return from the LifecycleStateEnum.SUSPENDED
0718: * state to the LifecycleStateEnum.RUNNING state.
0719: * <p>
0720: * Override this method to disable or change this functionality.
0721: *
0722: * @throws ComponentUnavailableException if it does not change states in a
0723: * timely manner or if
0724: * @throws InvalidStateException if the entry state is not within
0725: * the Set of validStates
0726: */
0727: protected void waitForResume() throws InvalidStateException,
0728: ComponentUnavailableException {
0729:
0730: if (log.isTraceEnabled()) {
0731: log.trace("Component [" + this .getComponentName()
0732: + "] suspended, waiting for it to resume");
0733: }
0734:
0735: try {
0736: // need to release the read monitor to prevent deadlock
0737: // this monitor is claimed within the proxyInvocationHandler
0738: this .proxyInvocationHandler.getMonitor().readLock()
0739: .release();
0740:
0741: synchronized (suspendedLock) {
0742: if (getLifecycleState() == LifecycleStateEnum.SUSPENDED) {
0743: suspendedLock.wait(this .blockedCallTimeout);
0744: }
0745: }
0746:
0747: } catch (InterruptedException ie) {
0748: Thread.currentThread().interrupt();
0749: log.warn("Caught InterruptedException: "
0750: + ExceptionUtility.printStackTracesToString(ie));
0751:
0752: } finally {
0753: // restore the read monitor
0754: try {
0755: this .proxyInvocationHandler.getMonitor().readLock()
0756: .acquire();
0757: } catch (InterruptedException ie) {
0758: Thread.currentThread().interrupt();
0759: throw new MonitorAcquisitionException(
0760: this .getClass(),
0761: "Caught InterruptedException reaquiring the monitor",
0762: ie);
0763: }
0764: if (getLifecycleState() != LifecycleStateEnum.RUNNING) {
0765: String message = "Call to component timed out because it was in a "
0766: + "transient lifecycle state, and did not return to "
0767: + "RUNNING state soon enough to service the call "
0768: + "or another thread changed the state before this one "
0769: + "could execute the method call.";
0770: throw new ComponentUnavailableException(
0771: this .getClass(), message);
0772: }
0773: }
0774: }
0775:
0776: /**
0777: * Checks if a given state exists in a set of valid states.
0778: * @param entryState the entry state to be validated
0779: * @param validStates a set of validStates
0780: * @throws InvalidStateException if the entry state is not within
0781: * the Set of validStates
0782: */
0783: protected final void validateEntryState(
0784: LifecycleStateEnum entryState, Set validStates) {
0785:
0786: if (!validStates.contains(entryState)) {
0787: final String message = "The requested lifecycle operation can not be performed "
0788: + "while in this state: [" + entryState + "]";
0789:
0790: throw new InvalidStateException(this .getClass(), message);
0791: }
0792: }
0793:
0794: /**
0795: * Utility method to log trace details when a component does not
0796: * support an attempted lifecycle operation.
0797: *
0798: * @param unImplementedInterface the interface which held the lifecycle
0799: * method that was not implemented by the component
0800: */
0801: protected final void logNotSupported(Class unImplementedInterface) {
0802:
0803: if (log.isTraceEnabled()) {
0804: log
0805: .trace("Cannot notify component of lifecycle operation. ["
0806: + getComponentName()
0807: + "] does not implement ["
0808: + unImplementedInterface.getName() + "]");
0809: }
0810: }
0811:
0812: /**
0813: * Utility method used to get the component's name
0814: *
0815: * @return String the name of the component
0816: */
0817: protected final String getComponentName() {
0818: if (this .componentProxy == null) {
0819: return null;
0820: } else {
0821: return this .componentProxy.getComponentName();
0822: }
0823: }
0824:
0825: /**
0826: * <p>
0827: * This method is called when an exception other than
0828: * NonFatalStateTransitionException is thrown from a component's lifecycle
0829: * method which signals that the component is corrupt and should be
0830: * destroyed.
0831: * </p>
0832: * <p>
0833: * Override this method to change what happens to a component if it
0834: * fails to transition state.
0835: * </p>
0836: */
0837: protected void killInvalidComponent() {
0838: if (log.isInfoEnabled()) {
0839: log.info("Destroying component [" + getComponentName()
0840: + "], it is in an unexpected state ["
0841: + getLifecycleState() + "]");
0842: }
0843: destroyComponent();
0844: }
0845:
0846: /**
0847: * Calls component's lifecycle method. Manages state transitions
0848: * from entry state to interim state. It will also return the component
0849: * to its entry state if a NonFatalStateTransitionException is caught.
0850: */
0851: private void callComponentStart() {
0852: LifecycleStateEnum entryState = getLifecycleState();
0853: try {
0854: Startable subject = null;
0855: //Cast the component to Startable
0856: subject = (Startable) functionalImplementation;
0857: //Immediately before calling start(), set state to STARTING
0858: setLifecycleState(LifecycleStateEnum.STARTING);
0859: //Call the component's start() method
0860: subject.start();
0861:
0862: } catch (NonFatalStateTransitionException nfste) {
0863: log
0864: .info("Caught NonFatalStateTransitionException, "
0865: + "returning component to its original state. Component: ["
0866: + getComponentName() + "]");
0867:
0868: setLifecycleState(entryState);
0869: // propagate exception
0870: throw nfste;
0871:
0872: } catch (Exception e) {
0873: String message = "An Exception occurred while attempting to start ["
0874: + getComponentName() + "]";
0875: throw new StateTransitionException(this .getClass(),
0876: message, e);
0877: }
0878: }
0879:
0880: /**
0881: * Calls component's lifecycle method. Manages state transitions
0882: * from entry state to interim state. It will also return the component
0883: * to its entry state if a NonFatalStateTransitionException is caught.
0884: */
0885: private void callComponentStop() {
0886: LifecycleStateEnum entryState = getLifecycleState();
0887: try {
0888: Startable subject = null;
0889: //Cast the component to Startable
0890: subject = (Startable) functionalImplementation;
0891: //Immediately before calling stop(), set state to STOPPING
0892: setLifecycleState(LifecycleStateEnum.STOPPING);
0893: //Call the component's stop() method
0894: subject.stop();
0895:
0896: } catch (NonFatalStateTransitionException nfste) {
0897: log
0898: .info("Caught NonFatalStateTransitionException, "
0899: + "returning component to its original state. Component: ["
0900: + getComponentName() + "]");
0901:
0902: setLifecycleState(entryState);
0903: // propagate exception
0904: throw nfste;
0905:
0906: } catch (Exception e) {
0907: String message = "An Exception occurred while attempting to stop ["
0908: + getComponentName() + "]";
0909: throw new StateTransitionException(this .getClass(),
0910: message, e);
0911: }
0912: }
0913:
0914: /**
0915: * Calls component's lifecycle method. Manages state transitions
0916: * from entry state to interim state, suspending or resuming as necessary.
0917: * It will also return the component
0918: * to its last good state (RUNNING or SUSPENDED depending on where the
0919: * exception was thrown) if a NonFatalStateTransitionException is caught.
0920: *
0921: * @param configuration configuration for this component that will be
0922: * passed to its lifecycle method
0923: */
0924: private void callComponentConfigure(
0925: ComponentConfiguration configuration) {
0926: LifecycleStateEnum entryState = getLifecycleState();
0927: LifecycleStateEnum lastGoodState = entryState;
0928: try {
0929: //Cast the component to Configurable
0930: Configurable subject = (Configurable) functionalImplementation;
0931:
0932: //if was RUNNING then suspend it
0933: if (entryState == LifecycleStateEnum.RUNNING) {
0934: internalSuspendComponent();
0935: lastGoodState = getLifecycleState();
0936: }
0937:
0938: //Immediately before calling configure(),
0939: //set state to CONFIGURING
0940: setLifecycleState(LifecycleStateEnum.CONFIGURING);
0941:
0942: //Call the component's configure method
0943: subject.configure(configuration);
0944:
0945: if (entryState == LifecycleStateEnum.RUNNING) {
0946: //if was RUNNING then call resume to move it back
0947: internalResumeComponent();
0948: } else {
0949: //otherwise, move it back to its original state
0950: this .setLifecycleState(entryState);
0951: }
0952:
0953: } catch (NonFatalStateTransitionException nfste) {
0954: log
0955: .info("Caught NonFatalStateTransitionException, "
0956: + "returning component to its original state. Component: ["
0957: + getComponentName() + "]");
0958:
0959: setLifecycleState(lastGoodState);
0960: // propagate exception
0961: throw nfste;
0962:
0963: } catch (Exception e) {
0964: String message = "An Exception occurred while attempting to configure ["
0965: + getComponentName() + "]";
0966: throw new StateTransitionException(this .getClass(),
0967: message, e);
0968: }
0969: }
0970:
0971: /**
0972: * Calls component's lifecycle method. Manages state transitions
0973: * from entry state to interim state. It will also return the component
0974: * to its entry state if a NonFatalStateTransitionException is caught.
0975: */
0976: private void callComponentSuspend() {
0977: LifecycleStateEnum entryState = getLifecycleState();
0978: try {
0979: Suspendable subject = null;
0980: //Cast the component to Startable
0981: subject = (Suspendable) functionalImplementation;
0982: //Immediately before calling suspend(), set state to SUSPENDING
0983: setLifecycleState(LifecycleStateEnum.SUSPENDING);
0984: //Call the component's suspend() method
0985: subject.suspend();
0986:
0987: } catch (NonFatalStateTransitionException nfste) {
0988: log
0989: .info("Caught NonFatalStateTransitionException, "
0990: + "returning component to its original state. Component: ["
0991: + getComponentName() + "]");
0992:
0993: setLifecycleState(entryState);
0994: // propagate exception
0995: throw nfste;
0996:
0997: } catch (Exception e) {
0998: String message = "An Exception occurred while attempting to suspend ["
0999: + getComponentName() + "]";
1000: throw new StateTransitionException(getClass(), message, e);
1001: }
1002: }
1003:
1004: /**
1005: * Calls component's lifecycle method. Manages state transitions
1006: * from entry state to interim state. It will also return the component
1007: * to its entry state if a NonFatalStateTransitionException is caught.
1008: */
1009: private void callComponentResume() {
1010: LifecycleStateEnum entryState = getLifecycleState();
1011: try {
1012: Suspendable subject = null;
1013: //Cast the component to Startable
1014: subject = (Suspendable) functionalImplementation;
1015: //Immediately before calling resume(), set state to RESUMING
1016: setLifecycleState(LifecycleStateEnum.RESUMING);
1017: //Call the component's resume() method
1018: subject.resume();
1019:
1020: } catch (NonFatalStateTransitionException nfste) {
1021: log
1022: .info("Caught NonFatalStateTransitionException, "
1023: + "returning component to its original state. Component: ["
1024: + getComponentName() + "]");
1025:
1026: setLifecycleState(entryState);
1027: // propagate exception
1028: throw nfste;
1029:
1030: } catch (Exception e) {
1031: String message = "An Exception occurred while attempting to resume ["
1032: + getComponentName() + "]";
1033: throw new StateTransitionException(this .getClass(),
1034: message, e);
1035: }
1036: }
1037:
1038: /* Static Members, Initializers and Methods below here */
1039:
1040: /** Defines the valid initialize states */
1041: private static final Set VALID_ENTRY_STATES_INITIALIZE;
1042: /** Defines the valid start states */
1043: private static final Set VALID_ENTRY_STATES_START;
1044: /** Defines the valid stop states */
1045: private static final Set VALID_ENTRY_STATES_STOP;
1046: /** Defines the valid suspend states */
1047: private static final Set VALID_ENTRY_STATES_SUSPEND;
1048: /** Defines the valid resume states */
1049: private static final Set VALID_ENTRY_STATES_RESUME;
1050: /** Defines the valid configure states */
1051: private static final Set VALID_ENTRY_STATES_CONFIGURE;
1052:
1053: static {
1054:
1055: /*
1056: * Below, Sets are created, containing the valid entry states for
1057: * each lifecycle operation. When there is only one valid entry state,
1058: * we use a Singleton Set. When there is more than one valid entry state
1059: * we use a new HashSet. I would consider using a TreeSet in place of
1060: * HashSet if storage were crucial, but unless storage is in issue, lets
1061: * stick with HashSet's O(1) performance on Set.contains(Object o)
1062: * operations.
1063: */
1064:
1065: /* Set the valid entry states for initialize() */
1066: VALID_ENTRY_STATES_INITIALIZE = Collections
1067: .singleton(LifecycleStateEnum.CREATING);
1068:
1069: /* Set the valid entry states for start() */
1070: VALID_ENTRY_STATES_START = Collections
1071: .singleton(LifecycleStateEnum.STOPPED);
1072:
1073: /* Set the valid entry states for stop() */
1074: HashSet validStates = new HashSet();
1075: validStates.add(LifecycleStateEnum.RUNNING);
1076: validStates.add(LifecycleStateEnum.SUSPENDED);
1077: VALID_ENTRY_STATES_STOP = validStates;
1078:
1079: /* Set the valid entry states for suspend() */
1080: VALID_ENTRY_STATES_SUSPEND = Collections
1081: .singleton(LifecycleStateEnum.RUNNING);
1082:
1083: /* Set the valid entry states for resume() */
1084: VALID_ENTRY_STATES_RESUME = Collections
1085: .singleton(LifecycleStateEnum.SUSPENDED);
1086:
1087: /* Set the valid entry states for configure() */
1088: validStates = new HashSet();
1089: validStates.add(LifecycleStateEnum.RUNNING);
1090: validStates.add(LifecycleStateEnum.SUSPENDED);
1091: validStates.add(LifecycleStateEnum.STOPPED);
1092: VALID_ENTRY_STATES_CONFIGURE = validStates;
1093: }
1094:
1095: }
|