KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > sape > carbon > core > component > lifecycle > DefaultLifecycleInterceptor


1 /*
2  * The contents of this file are subject to the Sapient Public License
3  * Version 1.0 (the "License"); you may not use this file except in compliance
4  * with the License. You may obtain a copy of the License at
5  * http://carbon.sf.net/License.html.
6  *
7  * Software distributed under the License is distributed on an "AS IS" basis,
8  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
9  * the specific language governing rights and limitations under the License.
10  *
11  * The Original Code is The Carbon Component Framework.
12  *
13  * The Initial Developer of the Original Code is Sapient Corporation
14  *
15  * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
16  */

17
18 package org.sape.carbon.core.component.lifecycle;
19
20 import java.util.Collections JavaDoc;
21 import java.util.HashSet JavaDoc;
22 import java.util.Set JavaDoc;
23 import java.lang.reflect.Proxy JavaDoc;
24 import java.lang.reflect.InvocationHandler JavaDoc;
25 import java.lang.reflect.Method JavaDoc;
26
27 import org.sape.carbon.core.component.Component;
28 import org.sape.carbon.core.component.ComponentConfiguration;
29 import org.sape.carbon.core.component.FunctionalInterface;
30 import org.sape.carbon.core.component.event.ComponentEvent;
31 import org.sape.carbon.core.component.event.EventManager;
32 import org.sape.carbon.core.component.proxy.ComponentProxyInvocationHandler;
33 import org.sape.carbon.core.component.proxy.Interceptor;
34 import org.sape.carbon.core.component.proxy.Invocation;
35 import org.sape.carbon.core.component.proxy.MonitorAcquisitionException;
36 import org.sape.carbon.core.component.proxy.AbstractInterceptor;
37 import org.sape.carbon.core.exception.ExceptionUtility;
38
39 import org.apache.commons.logging.Log;
40 import org.apache.commons.logging.LogFactory;
41
42 /**
43  * This is the default implementation of the lifecycle interceptor interface.
44  * This class takes on the responsibility of delegating
45  *
46  * Copyright 2002 Sapient
47  * @since carbon 1.0
48  * @author Chris Herron, January 2002
49  * @version $Revision: 1.9 $($Author: ghinkl $ / $Date: 2003/10/15 21:40:19 $)
50  */

51 public class DefaultLifecycleInterceptor
52     extends AbstractInterceptor
53     implements LifecycleInterceptor, Interceptor {
54
55     /** Array of interfaces this proxy adds to a component. */
56     protected static final Class JavaDoc[] EXPOSED_INTERFACES =
57         new Class JavaDoc[] {LifecycleInterceptor.class};
58
59     /** Provides handle to Apache-commons logger. */
60     private Log log =
61         LogFactory.getLog(this.getClass());
62
63     /** Name of lifecycle change event. */
64     public static final String JavaDoc LIFECYCLE_CHANGE_EVENT_NAME =
65         "Component.LifecycleChange";
66
67     /**
68      * This method must implement the invocation of any necessary actions and
69      * the chaining the next interceptor.
70      * @param invocation the invocation to execute
71      * @return the results of the invocation's execution
72      *
73      * @throws Throwable indicates an error in the invocation chain
74      */

75     public Object JavaDoc invoke(Invocation invocation) throws Throwable JavaDoc {
76
77         if (invocation.isTargetFunctionalImplementation()) {
78             // If the target is the component's implementation and we're
79
// not it a running state, try and wait until we're ready to go
80
LifecycleStateEnum state = getLifecycleState();
81
82             if (state != LifecycleStateEnum.RUNNING) {
83                 if (state == LifecycleStateEnum.SUSPENDED
84                     || state == LifecycleStateEnum.RESUMING) {
85
86                     waitForResume();
87                 } else {
88                     throw new ComponentUnavailableException(
89                         this.getClass(),
90                         "The component is not in a LifecycleStateEnum.RUNNING "
91                         + "state, FunctionalInterface methods "
92                         + "cannot be called. "
93                         + "Component name [" + getComponentName()
94                         + "], current state [" + state + "]");
95                 }
96             }
97         }
98
99         return callNextInterceptor(invocation);
100     }
101
102     /**
103      * This should return the list of interfaces that a decorator wishes to
104      * expose through the component proxy. This is used by the
105      * component factory to determine what interfaces the component proxy will
106      * implement.
107      *
108      * @return Class[] an array of interfaces
109      */

110     public Class JavaDoc[] getExposedInterfaces() {
111         return DefaultLifecycleInterceptor.EXPOSED_INTERFACES;
112     }
113
114     /**
115      * A reference to the Component instance that is being managed.
116      * final because once the interceptor in constructed, this should
117      * not change.
118      */

119     private final FunctionalInterface functionalImplementation;
120
121
122     /**
123      * The current state associated with the Component. Access to this
124      * property must be synchronized. Access only via get and set methods.
125      */

126     private LifecycleStateEnum currentState = LifecycleStateEnum.CREATING;
127
128     /**
129      * A reference to the component proxy invocation handler.
130      * Used to get a reference to the components monitor.
131      * final because once the interceptor in constructed,
132      * this should not change.
133      */

134     private final ComponentProxyInvocationHandler proxyInvocationHandler;
135
136
137     /** reference to the complete component */
138     private Component componentProxy;
139
140     /**
141      * lock object synchronized upon to block calls while in a suspended
142      * state, used in waitForResume and internalResumeComponent methods
143      */

144     private final Object JavaDoc suspendedLock = new Object JavaDoc();
145
146     /** timeout for blocking calls. */
147     private long blockedCallTimeout;
148
149     /** timeout for the destroyer thread. */
150     private long destroyerThreadTimeout;
151
152     /**
153      * Creates a new DefaultLifecycleInterceptor.
154      * Sets the initial state to CREATING.
155      * It is intended that the constructor does not accept as a parameter
156      * the Component to be managed, since this would circumvent the mutator
157      * method on the LifecycleInterceptor Interface.
158      *
159      * @param componentInstance instance of the component
160      * @param proxyInvocationHandler invocation handler for the component
161      * @param configuration configuration for the interceptor
162      */

163     public DefaultLifecycleInterceptor(
164         FunctionalInterface componentInstance,
165         ComponentProxyInvocationHandler proxyInvocationHandler,
166         DefaultLifecycleInterceptorConfiguration configuration) {
167
168         this.functionalImplementation = componentInstance;
169
170         this.proxyInvocationHandler = proxyInvocationHandler;
171
172         if (configuration == null) {
173             this.blockedCallTimeout =
174                 DefaultLifecycleInterceptorConfiguration.BlockedCallTimeout;
175             this.destroyerThreadTimeout =
176                 DefaultLifecycleInterceptorConfiguration.DestroyerThreadTimeout;
177
178         } else {
179             this.blockedCallTimeout = configuration.getBlockedCallTimeout();
180             this.destroyerThreadTimeout =
181                 configuration.getDestroyerThreadTimeout();
182         }
183     }
184
185
186
187     /**
188      * Gets the current lifecycle state of the component.
189      * Note the protected corresponding mutator method - this property is
190      * read-only to external entities.
191      * @return the lifecycle state of this component
192      */

193     public synchronized LifecycleStateEnum getLifecycleState() {
194         return this.currentState;
195     }
196
197     /**
198      * Returns a string representation of the Lifecycle State
199      *
200      * @return a string representation of the Lifecycle State
201      */

202     public String JavaDoc getLifecycleStateString() {
203         return getLifecycleState().toString();
204     }
205
206     /**
207      * Sets the current lifecycle state
208      * @param state the new state
209      */

210     protected synchronized void setLifecycleState(LifecycleStateEnum state) {
211
212         if (log.isTraceEnabled()) {
213             log.trace(
214                 "Changing ["
215                     + getComponentName()
216                     + "] component's state from ["
217                     + this.currentState
218                     + "] to ["
219                     + state
220                     + "]");
221         }
222
223         // If the component template is so configured to have a decorator
224
// capable of sending events, then send one.
225
if (this.componentProxy instanceof EventManager) {
226             ((EventManager) this.componentProxy).
227                 sendEvent(
228                     new ComponentEvent(
229                         this.componentProxy,
230                         LIFECYCLE_CHANGE_EVENT_NAME,
231                         "Lifecycle state is changing from ["
232                             + this.currentState.getName() + "] to ["
233                             + state.getName() + "]"));
234         }
235
236         this.currentState = state;
237     }
238
239     /**
240      * Run ONLY ONCE during the life of a Component, immediately after the
241      * Component is created. Enforced by having CREATING as the only allowed
242      * entry state.
243      *
244      * <p>Valid Entry States: CREATING </p>
245      * <p>Interim State: INITIALIZING</p>
246      * <p>Exit State: STOPPED</p>
247      *
248      * @param thisComponent component to be initialized
249      *
250      * @throws InvalidStateException When the entry state is not allowed
251      * @throws OperationNotSupportedException When component does not support
252      * the requested operation
253      * @throws StateTransitionException When an error occured while trying to
254      * complete the lifecycle operation
255      */

256     public void initializeComponent(Component thisComponent)
257             throws InvalidStateException,
258
259         OperationNotSupportedException, StateTransitionException {
260
261         startLifecycleMethod();
262
263         validateEntryState(getLifecycleState(),
264             DefaultLifecycleInterceptor.VALID_ENTRY_STATES_INITIALIZE);
265
266         try {
267             if (functionalImplementation instanceof Initializable) {
268                 Initializable subject = null;
269                 //Cast the component to Initializable
270
subject = (Initializable) functionalImplementation;
271                 //Immediately before calling initialize(),
272
//set state to INITIALIZING
273
setLifecycleState(LifecycleStateEnum.INITIALIZING);
274                 //Call the component's intialize() method
275
subject.initialize(thisComponent);
276             } else {
277                 logNotSupported(
278                         Initializable.class);
279             }
280             //Now move the state to STOPPED
281
setLifecycleState(LifecycleStateEnum.STOPPED);
282
283         } catch (Exception JavaDoc e) {
284             throw new StateTransitionException(
285                 this.getClass(),
286                 "An Exception occurred while attempting to initialize", e);
287
288         } finally {
289             stopLifecycleMethod();
290         }
291     }
292
293     /**
294      * Tells the Component to begin providing its service.
295      *
296      * <p>Valid Entry States: STOPPED</p>
297      * <p>Interim State: STARTING</p>
298      * <p>Exit State: RUNNING</p>
299      *
300      * @throws InvalidStateException When the entry state is not allowed
301      * @throws OperationNotSupportedException When component does not support
302      * the requested operation
303      * @throws StateTransitionException When an error occured while trying to
304      * complete the lifecycle operation
305      */

306     public void startComponent() throws InvalidStateException,
307         OperationNotSupportedException, StateTransitionException {
308
309         startLifecycleMethod();
310
311         validateEntryState(getLifecycleState(),
312             DefaultLifecycleInterceptor.VALID_ENTRY_STATES_START);
313
314         try {
315
316             if (functionalImplementation instanceof Startable) {
317                 callComponentStart();
318             } else {
319                 logNotSupported(
320                         Startable.class);
321             }
322
323             //Now move the state to RUNNING
324
setLifecycleState(LifecycleStateEnum.RUNNING);
325
326         } finally {
327             stopLifecycleMethod();
328             // kill the component if it is in an interim state
329
if (getLifecycleState() == LifecycleStateEnum.STARTING) {
330                 killInvalidComponent();
331             }
332         }
333     }
334
335     /**
336      * Tells the Component to cease providing its service.
337      * This is an opportunity for the Component to complete outstanding
338      * work.
339      *
340      * <p>Valid Entry States: RUNNING, SUSPENDED</p>
341      * <p>Interim State: STOPPING</p>
342      * <p>Exit State: STOPPED</p>
343      *
344      * @throws InvalidStateException When the entry state is not allowed
345      * @throws OperationNotSupportedException When component does not support
346      * the requested operation
347      * @throws StateTransitionException When an error occured while trying to
348      * complete the lifecycle operation
349      */

350     public void stopComponent() throws InvalidStateException,
351         OperationNotSupportedException, StateTransitionException {
352
353         startLifecycleMethod();
354
355         validateEntryState(getLifecycleState(),
356             DefaultLifecycleInterceptor.VALID_ENTRY_STATES_STOP);
357
358         try {
359
360             internalStopComponent();
361
362         } finally {
363             stopLifecycleMethod();
364             // kill the component if it is in an interim state
365
if (getLifecycleState() == LifecycleStateEnum.STOPPING) {
366                 killInvalidComponent();
367             }
368         }
369     }
370
371     /**
372      * Tells the Component to suspend its service. This is an opportunity for
373      * the Component to sensibly pause its outstanding work.
374      *
375      * <p>Valid Entry States: RUNNING</p>
376      * <p>Interim State: SUSPENDING</p>
377      * <p>Exit State: SUSPENDED</p>
378      *
379      * @throws InvalidStateException When the entry state is not allowed
380      * @throws OperationNotSupportedException When component does not support
381      * the requested operation
382      * @throws StateTransitionException When an error occured while trying to
383      * complete the lifecycle operation
384      */

385     public void suspendComponent() throws InvalidStateException,
386         OperationNotSupportedException, StateTransitionException {
387
388         startLifecycleMethod();
389
390         validateEntryState(getLifecycleState(),
391             DefaultLifecycleInterceptor.VALID_ENTRY_STATES_SUSPEND);
392
393         try {
394             internalSuspendComponent();
395
396         } finally {
397             stopLifecycleMethod();
398             if (getLifecycleState() == LifecycleStateEnum.SUSPENDING) {
399                 killInvalidComponent();
400             }
401         }
402     }
403
404     /**
405      * Tells the Component to resume its service. The Component can now continue
406      * its outstanding work.
407      *
408      * <p>Valid Entry States: SUSPENDED</p>
409      * <p>Interim State: RESUMING</p>
410      * <p>Exit State: RUNNING</p>
411      *
412      * @throws InvalidStateException When the entry state is not allowed
413      * @throws OperationNotSupportedException When component does not support
414      * the requested operation
415      * @throws StateTransitionException When an error occured while trying to
416      * complete the lifecycle operation
417      */

418     public void resumeComponent() throws InvalidStateException,
419         OperationNotSupportedException, StateTransitionException {
420
421         startLifecycleMethod();
422
423         validateEntryState(getLifecycleState(),
424             DefaultLifecycleInterceptor.VALID_ENTRY_STATES_RESUME);
425
426         try {
427             internalResumeComponent();
428
429             // Wake an who we're waiting while the component was suspended
430
synchronized (this.suspendedLock) {
431                 this.suspendedLock.notifyAll();
432             }
433         } finally {
434             stopLifecycleMethod();
435             if (getLifecycleState() == LifecycleStateEnum.RESUMING) {
436                 killInvalidComponent();
437             }
438         }
439     }
440
441     /**
442      * Provides a Component with its Configuration. The Component should be
443      * SUSPENDED or STOPPED, if not already.
444      * The Component should also be returned to its entry state (RUNNING or
445      * SUSPENDED) after the main Configure operation has completed.
446      *
447      * <p>Valid Entry States: RUNNING, SUSPENDED, STOPPED</p>
448      * <p>Interim State: CONFIGURING</p>
449      * <p>Exit States: RUNNING, SUSPENDED, STOPPED (to match entry state)</p>
450      *
451      * @param configuration Configuration object to be applied to component
452      * @throws InvalidStateException When the entry state is not allowed
453      * @throws OperationNotSupportedException When component does not support
454      * the requested operation
455      * @throws StateTransitionException When an error occured while trying to
456      * complete the lifecycle operation
457      */

458     public void configureComponent(ComponentConfiguration configuration)
459         throws InvalidStateException,
460         OperationNotSupportedException, StateTransitionException {
461
462         startLifecycleMethod();
463
464         validateEntryState(getLifecycleState(),
465             DefaultLifecycleInterceptor.VALID_ENTRY_STATES_CONFIGURE);
466
467         try {
468             if (functionalImplementation instanceof Configurable) {
469                 callComponentConfigure(configuration);
470
471             } else {
472                 logNotSupported(
473                         Configurable.class);
474             }
475
476         } finally {
477             stopLifecycleMethod();
478
479             LifecycleStateEnum exitState = getLifecycleState();
480             if (exitState != LifecycleStateEnum.RUNNING
481                 && exitState != LifecycleStateEnum.SUSPENDED
482                 && exitState != LifecycleStateEnum.STOPPED) {
483
484                 killInvalidComponent();
485             }
486         }
487     }
488
489     /**
490      * Tells the Component prepare to die. This is an opportunity for the
491      * developer to do some sensible housekeeping to aid Garbage Collection,
492      * relinquish resources etc. Once in the DESTROYED state, a Component
493      * cannot be revived. This implementation will wait for
494      * DESTROY_TIMEOUT_MILLIS for the component to be destroyed. If, after
495      * that time the component has not been destroyed, it will be logged as
496      * a warning.
497      *
498      * <p>Valid Entry States: All states are valid for entry. If the
499      * component is in the RUNNING state, it will be stopped first.</p>
500      * <p>Interim State: DESTROYING</p>
501      * <p>Exit State: DESTROYED</p>
502      *
503      * @throws InvalidStateException When the entry state is not allowed
504      * @throws OperationNotSupportedException When component does not support
505      * the requested operation
506      * @throws StateTransitionException When an error occured while trying to
507      * complete the lifecycle operation
508      */

509     public void destroyComponent()
510         throws OperationNotSupportedException, StateTransitionException {
511
512         if (getLifecycleState() == LifecycleStateEnum.DESTROYED) {
513             // component already destroyed
514
return;
515         }
516
517         startLifecycleMethod();
518
519         try {
520             if (functionalImplementation instanceof Destroyable
521                 || (getLifecycleState() == LifecycleStateEnum.RUNNING
522                 && functionalImplementation instanceof Startable)) {
523
524
525                 //Create an anonymous thread that we will use
526
//to timeout on the destroy operation
527
Thread JavaDoc destroyer = new Thread JavaDoc(new Runnable JavaDoc() {
528                     public void run() {
529                         try {
530                             // stop the component if it is running
531
if (getLifecycleState()
532                                     == LifecycleStateEnum.RUNNING) {
533
534                                 internalStopComponent();
535                             }
536
537                             //Call the component's destroy() method
538
if (functionalImplementation
539                                     instanceof Destroyable) {
540
541                                 // Immediately before calling destroy(),
542
// set state to DESTROYING
543
setLifecycleState(
544                                     LifecycleStateEnum.DESTROYING);
545
546                                 ((Destroyable)
547                                     functionalImplementation).destroy();
548                             }
549                         } catch (Exception JavaDoc e) {
550                             log.warn(
551                                 "Exception destroying component ["
552                                 + getComponentName() + "]: " + e + ": "
553                                 + ExceptionUtility.printStackTracesToString(
554                                     e));
555                         }
556                     }
557                 }, "Destroying component " + getComponentName());
558
559                 // set the thread to a daemon so it won't halt shutdown
560
destroyer.setDaemon(true);
561                 destroyer.start();
562
563                 //Join the thread with a specified timeout
564
destroyer.join(this.destroyerThreadTimeout);
565
566                 //On timeout, if the destroyer thread is still running
567
//throw an Exception
568
if (destroyer.isAlive()) {
569                     String JavaDoc message = "Destroy operation timed out after "
570                         + this.destroyerThreadTimeout + " milliseconds";
571
572                     log.warn(message);
573                 }
574             } else {
575                 logNotSupported(
576                         Destroyable.class);
577             }
578
579             //Now move the state to DESTROYED
580
setLifecycleState(LifecycleStateEnum.DESTROYED);
581
582         } catch (InterruptedException JavaDoc ie) {
583             String JavaDoc message = "Thread interrupted during attempt to destroy";
584             throw new StateTransitionException(
585                 this.getClass(),
586                 message, ie);
587
588         } catch (Exception JavaDoc e) {
589             String JavaDoc message = "An error occurred while attempting to destroy";
590             throw new StateTransitionException(
591                 this.getClass(),
592                 message, e);
593
594         } finally {
595             stopLifecycleMethod();
596         }
597     }
598
599     /**
600      * This implementation does not make use of the componentProxy reference
601      *
602      * @param componentProxy a reference to the component that
603      * this interceptor is assisting
604      */

605     public void setComponentReference(Component componentProxy) {
606         this.componentProxy = componentProxy;
607     }
608
609
610     /**
611      * Acquires the component's monitor for writing. If this method is
612      * called, it is imperitive that stopLifecycleMethod() is called.
613      * <p>
614      * Example invocation:<br>
615      * <pre><code>
616      * startLifecycleMethod();
617      * try {
618      * ...
619      * } finally {
620      * stopLifecycleMethod();
621      * }
622      * </code></pre>
623      *
624      * @see ComponentProxyInvocationHandler#getMonitor()
625      *
626      * @throws StateTransitionException if the Thread is interrupted
627      */

628     protected void startLifecycleMethod() {
629         if (log.isTraceEnabled()) {
630             log.trace(
631                 "Acquiring lock to start lifecycle method on component ["
632                     + getComponentName() + "]");
633         }
634
635         try {
636             this.proxyInvocationHandler.getMonitor().writeLock().attempt(1000);
637         } catch (InterruptedException JavaDoc ie) {
638             throw new StateTransitionException(
639                 this.getClass(),
640                 "Caught InterruptedException while trying to acquire the "
641                     + "component's monitor", ie);
642         }
643     }
644
645     /** Releases the component's monitor */
646     protected void stopLifecycleMethod() {
647         if (log.isTraceEnabled()) {
648             log.trace(
649             "Releasing lifecycle method write lock on component ["
650                 + getComponentName() + "]");
651         }
652         this.proxyInvocationHandler.getMonitor().writeLock().release();
653     }
654
655     /**
656      * Helper method to do the work of suspending a component. Called by
657      * suspendComponent and configureComponent. A helper method is used
658      * so that calling methods can handle threading issues as they require
659      * while not duplicating code
660      *
661      * @throws StateTransitionException if an exception is caught in the
662      * functional implementations suspend method
663      * @throws OperationNotSupportedException not thrown by this imlementation,
664      * but provided for overriding methods
665      */

666     protected void internalSuspendComponent()
667         throws OperationNotSupportedException, StateTransitionException {
668
669         if (functionalImplementation instanceof Suspendable) {
670             callComponentSuspend();
671
672         } else {
673             logNotSupported(
674                     Suspendable.class);
675         }
676
677         //Now move the state to SUSPENDED
678
setLifecycleState(LifecycleStateEnum.SUSPENDED);
679     }
680
681     /**
682      * Helper method to do the work of resuming a component. Called by
683      * resumeComponent and configureComponent. A helper method is used
684      * so that calling methods can handle threading issues as they require
685      * while not duplicating code
686      *
687      * @throws StateTransitionException if an exception is caught in the
688      * functional implementations resume method
689      * @throws OperationNotSupportedException not thrown by this imlementation,
690      * but provided for overriding methods
691      */

692     protected void internalResumeComponent()
693         throws OperationNotSupportedException, StateTransitionException {
694
695         if (functionalImplementation instanceof Suspendable) {
696             callComponentResume();
697
698         } else {
699             logNotSupported(
700                     Suspendable.class);
701         }
702
703         //Now move the state to RUNNING
704
setLifecycleState(LifecycleStateEnum.RUNNING);
705     }
706
707     /**
708      * Helper method to do the work of suspending a component. Called by
709      * suspendComponent and configureComponent. A helper method is used
710      * so that calling methods can handle threading issues as they require
711      * while not duplicating code
712      *
713      * @throws StateTransitionException if an exception is caught in the
714      * functional implementations suspend method
715      * @throws OperationNotSupportedException not thrown by this imlementation,
716      * but provided for overriding methods
717      */

718     protected void internalStopComponent()
719         throws OperationNotSupportedException, StateTransitionException {
720
721         if (functionalImplementation instanceof Startable) {
722             callComponentStop();
723
724         } else {
725             logNotSupported(
726                     Startable.class);
727         }
728
729         //Now move the state to SUSPENDED
730
setLifecycleState(LifecycleStateEnum.STOPPED);
731     }
732
733     /**
734      * Waits for the component to return from the LifecycleStateEnum.SUSPENDED
735      * state to the LifecycleStateEnum.RUNNING state.
736      * <p>
737      * Override this method to disable or change this functionality.
738      *
739      * @throws ComponentUnavailableException if it does not change states in a
740      * timely manner or if
741      * @throws InvalidStateException if the entry state is not within
742      * the Set of validStates
743      */

744     protected void waitForResume()
745         throws InvalidStateException, ComponentUnavailableException {
746
747         if (log.isTraceEnabled()) {
748             log.trace("Component ["
749                 + this.getComponentName()
750                 + "] suspended, waiting for it to resume");
751         }
752
753         try {
754             // need to release the read monitor to prevent deadlock
755
// this monitor is claimed within the proxyInvocationHandler
756
this.proxyInvocationHandler.getMonitor().readLock().release();
757
758             synchronized (suspendedLock) {
759                 if (getLifecycleState() == LifecycleStateEnum.SUSPENDED) {
760                     suspendedLock.wait(this.blockedCallTimeout);
761                 }
762             }
763
764         } catch (InterruptedException JavaDoc ie) {
765             Thread.currentThread().interrupt();
766             log.warn(
767                 "Caught InterruptedException: "
768                 + ExceptionUtility.printStackTracesToString(ie));
769
770         } finally {
771             // restore the read monitor
772
try {
773                 this.proxyInvocationHandler.getMonitor().readLock().acquire();
774             } catch (InterruptedException JavaDoc ie) {
775                 Thread.currentThread().interrupt();
776                 throw new MonitorAcquisitionException(this.getClass(),
777                     "Caught InterruptedException reaquiring the monitor", ie);
778             }
779             if (getLifecycleState() != LifecycleStateEnum.RUNNING) {
780                 String JavaDoc message =
781                     "Call to component timed out because it was in a "
782                     + "transient lifecycle state, and did not return to "
783                     + "RUNNING state soon enough to service the call "
784                     + "or another thread changed the state before this one "
785                     + "could execute the method call.";
786                 throw new ComponentUnavailableException(
787                         this.getClass(), message);
788             }
789         }
790     }
791
792     /**
793      * Checks if a given state exists in a set of valid states.
794      * @param entryState the entry state to be validated
795      * @param validStates a set of validStates
796      * @throws InvalidStateException if the entry state is not within
797      * the Set of validStates
798      */

799     protected final void validateEntryState(LifecycleStateEnum entryState,
800                                             Set JavaDoc validStates) {
801
802         if (!validStates.contains(entryState)) {
803             final String JavaDoc message =
804                 "The requested lifecycle operation can not be performed "
805                     + "while in this state: ["
806                     + entryState
807                     + "]";
808
809             throw new InvalidStateException(this.getClass(), message);
810         }
811     }
812
813     /**
814      * Utility method to log trace details when a component does not
815      * support an attempted lifecycle operation.
816      *
817      * @param unImplementedInterface the interface which held the lifecycle
818      * method that was not implemented by the component
819      */

820     protected final void logNotSupported(
821             Class JavaDoc unImplementedInterface) {
822
823         if (log.isTraceEnabled()) {
824             log.trace(
825             "Cannot notify component of lifecycle operation. ["
826                 + getComponentName()
827                 + "] does not implement ["
828                 + unImplementedInterface.getName()
829                 + "]");
830         }
831     }
832
833     /**
834      * Utility method used to get the component's name
835      *
836      * @return String the name of the component
837      */

838     protected final String JavaDoc getComponentName() {
839         if (this.componentProxy == null) {
840             return null;
841         } else {
842             return this.componentProxy.getComponentName();
843         }
844     }
845
846     /**
847      * <p>
848      * This method is called when an exception other than
849      * NonFatalStateTransitionException is thrown from a component's lifecycle
850      * method which signals that the component is corrupt and should be
851      * destroyed.
852      * </p>
853      * <p>
854      * Override this method to change what happens to a component if it
855      * fails to transition state.
856      * </p>
857      */

858     protected void killInvalidComponent() {
859         if (log.isInfoEnabled()) {
860             log.info(
861             "Destroying component ["
862                 + getComponentName()
863                 + "], it is in an unexpected state ["
864                 + getLifecycleState()
865                 + "]");
866         }
867         destroyComponent();
868     }
869
870     /**
871      * Calls component's lifecycle method. Manages state transitions
872      * from entry state to interim state. It will also return the component
873      * to its entry state if a NonFatalStateTransitionException is caught.
874      */

875     private void callComponentStart() {
876         LifecycleStateEnum entryState = getLifecycleState();
877         try {
878             Startable subject = null;
879             //Cast the component to Startable
880
subject = (Startable) functionalImplementation;
881             //Immediately before calling start(), set state to STARTING
882
setLifecycleState(LifecycleStateEnum.STARTING);
883             //Call the component's start() method
884
subject.start();
885
886         } catch (NonFatalStateTransitionException nfste) {
887             log.info(
888                 "Caught NonFatalStateTransitionException, "
889                     + "returning component to its original state. Component: ["
890                     + getComponentName()
891                     + "]");
892
893             setLifecycleState(entryState);
894             // propagate exception
895
throw nfste;
896
897         } catch (Exception JavaDoc e) {
898             String JavaDoc message =
899                 "An Exception occurred while attempting to start ["
900                 + getComponentName() + "]";
901             throw new StateTransitionException(
902                 this.getClass(),
903                 message, e);
904         }
905     }
906
907     /**
908      * Calls component's lifecycle method. Manages state transitions
909      * from entry state to interim state. It will also return the component
910      * to its entry state if a NonFatalStateTransitionException is caught.
911      */

912     private void callComponentStop() {
913         LifecycleStateEnum entryState = getLifecycleState();
914         try {
915             Startable subject = null;
916             //Cast the component to Startable
917
subject = (Startable) functionalImplementation;
918             //Immediately before calling stop(), set state to STOPPING
919
setLifecycleState(LifecycleStateEnum.STOPPING);
920             //Call the component's stop() method
921
subject.stop();
922
923         } catch (NonFatalStateTransitionException nfste) {
924             log.info(
925                 "Caught NonFatalStateTransitionException, "
926                     + "returning component to its original state. Component: ["
927                     + getComponentName()
928                     + "]");
929
930             setLifecycleState(entryState);
931             // propagate exception
932
throw nfste;
933
934         } catch (Exception JavaDoc e) {
935             String JavaDoc message =
936                 "An Exception occurred while attempting to stop ["
937                 + getComponentName() + "]";
938             throw new StateTransitionException(
939                 this.getClass(),
940                 message, e);
941         }
942     }
943
944     /**
945      * Calls component's lifecycle method. Manages state transitions
946      * from entry state to interim state, suspending or resuming as necessary.
947      * It will also return the component
948      * to its last good state (RUNNING or SUSPENDED depending on where the
949      * exception was thrown) if a NonFatalStateTransitionException is caught.
950      *
951      * @param configuration configuration for this component that will be
952      * passed to its lifecycle method
953      */

954     private void callComponentConfigure(ComponentConfiguration configuration) {
955         LifecycleStateEnum entryState = getLifecycleState();
956         LifecycleStateEnum lastGoodState = entryState;
957         try {
958             //Cast the component to Configurable
959
Configurable subject = (Configurable) functionalImplementation;
960
961             //if was RUNNING then suspend it
962
if (entryState == LifecycleStateEnum.RUNNING) {
963                 internalSuspendComponent();
964                 lastGoodState = getLifecycleState();
965             }
966
967
968             //Immediately before calling configure(),
969
//set state to CONFIGURING
970
setLifecycleState(LifecycleStateEnum.CONFIGURING);
971
972             //Call the component's configure method
973
subject.configure(configuration);
974
975             if (entryState == LifecycleStateEnum.RUNNING) {
976                 //if was RUNNING then call resume to move it back
977
internalResumeComponent();
978             } else {
979                 //otherwise, move it back to its original state
980
this.setLifecycleState(entryState);
981             }
982
983         } catch (NonFatalStateTransitionException nfste) {
984             log.info(
985                 "Caught NonFatalStateTransitionException, "
986                     + "returning component to its original state. Component: ["
987                     + getComponentName()
988                     + "]");
989
990             setLifecycleState(lastGoodState);
991             // propagate exception
992
throw nfste;
993
994         } catch (Exception JavaDoc e) {
995             String JavaDoc message =
996                 "An Exception occurred while attempting to configure ["
997                 + getComponentName() + "]";
998             throw new StateTransitionException(
999                 this.getClass(),
1000                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 callComponentSuspend() {
1010        LifecycleStateEnum entryState = getLifecycleState();
1011        try {
1012            Suspendable subject = null;
1013            //Cast the component to Startable
1014
subject = (Suspendable) functionalImplementation;
1015            //Immediately before calling suspend(), set state to SUSPENDING
1016
setLifecycleState(LifecycleStateEnum.SUSPENDING);
1017            //Call the component's suspend() method
1018
subject.suspend();
1019
1020        } catch (NonFatalStateTransitionException nfste) {
1021            log.info(
1022                "Caught NonFatalStateTransitionException, "
1023                    + "returning component to its original state. Component: ["
1024                    + getComponentName()
1025                    + "]");
1026
1027            setLifecycleState(entryState);
1028            // propagate exception
1029
throw nfste;
1030
1031        } catch (Exception JavaDoc e) {
1032            String JavaDoc message =
1033                "An Exception occurred while attempting to suspend ["
1034                + getComponentName() + "]";
1035            throw new StateTransitionException(
1036                getClass(),
1037                message, e);
1038        }
1039    }
1040
1041    /**
1042     * Calls component's lifecycle method. Manages state transitions
1043     * from entry state to interim state. It will also return the component
1044     * to its entry state if a NonFatalStateTransitionException is caught.
1045     */

1046    private void callComponentResume() {
1047        LifecycleStateEnum entryState = getLifecycleState();
1048        try {
1049            Suspendable subject = null;
1050            //Cast the component to Startable
1051
subject = (Suspendable) functionalImplementation;
1052            //Immediately before calling resume(), set state to RESUMING
1053
setLifecycleState(LifecycleStateEnum.RESUMING);
1054            //Call the component's resume() method
1055
subject.resume();
1056
1057        } catch (NonFatalStateTransitionException nfste) {
1058            log.info(
1059                "Caught NonFatalStateTransitionException, "
1060                    + "returning component to its original state. Component: ["
1061                    + getComponentName()
1062                    + "]");
1063
1064            setLifecycleState(entryState);
1065            // propagate exception
1066
throw nfste;
1067
1068        } catch (Exception JavaDoc e) {
1069            String JavaDoc message =
1070                "An Exception occurred while attempting to resume ["
1071                + getComponentName() + "]";
1072            throw new StateTransitionException(
1073                this.getClass(),
1074                message, e);
1075        }
1076    }
1077
1078    /* Static Members, Initializers and Methods below here */
1079
1080    /** Defines the valid initialize states */
1081    private static final Set JavaDoc VALID_ENTRY_STATES_INITIALIZE;
1082    /** Defines the valid start states */
1083    private static final Set JavaDoc VALID_ENTRY_STATES_START;
1084    /** Defines the valid stop states */
1085    private static final Set JavaDoc VALID_ENTRY_STATES_STOP;
1086    /** Defines the valid suspend states */
1087    private static final Set JavaDoc VALID_ENTRY_STATES_SUSPEND;
1088    /** Defines the valid resume states */
1089    private static final Set JavaDoc VALID_ENTRY_STATES_RESUME;
1090    /** Defines the valid configure states */
1091    private static final Set JavaDoc VALID_ENTRY_STATES_CONFIGURE;
1092
1093    static {
1094
1095        /*
1096         * Below, Sets are created, containing the valid entry states for
1097         * each lifecycle operation. When there is only one valid entry state,
1098         * we use a Singleton Set. When there is more than one valid entry state
1099         * we use a new HashSet. I would consider using a TreeSet in place of
1100         * HashSet if storage were crucial, but unless storage is in issue, lets
1101         * stick with HashSet's O(1) performance on Set.contains(Object o)
1102         * operations.
1103         */

1104
1105        /* Set the valid entry states for initialize() */
1106        VALID_ENTRY_STATES_INITIALIZE =
1107            Collections.singleton(LifecycleStateEnum.CREATING);
1108
1109        /* Set the valid entry states for start() */
1110        VALID_ENTRY_STATES_START =
1111            Collections.singleton(LifecycleStateEnum.STOPPED);
1112
1113        /* Set the valid entry states for stop() */
1114        HashSet JavaDoc validStates = new HashSet JavaDoc();
1115        validStates.add(LifecycleStateEnum.RUNNING);
1116        validStates.add(LifecycleStateEnum.SUSPENDED);
1117        VALID_ENTRY_STATES_STOP = validStates;
1118
1119        /* Set the valid entry states for suspend() */
1120        VALID_ENTRY_STATES_SUSPEND =
1121            Collections.singleton(LifecycleStateEnum.RUNNING);
1122
1123        /* Set the valid entry states for resume() */
1124        VALID_ENTRY_STATES_RESUME =
1125            Collections.singleton(LifecycleStateEnum.SUSPENDED);
1126
1127        /* Set the valid entry states for configure() */
1128        validStates = new HashSet JavaDoc();
1129        validStates.add(LifecycleStateEnum.RUNNING);
1130        validStates.add(LifecycleStateEnum.SUSPENDED);
1131        validStates.add(LifecycleStateEnum.STOPPED);
1132        VALID_ENTRY_STATES_CONFIGURE = validStates;
1133    }
1134
1135}
Popular Tags