View Javadoc

1   /*
2    * $Id: AbstractLifecycleManager.java 19191 2010-08-25 21:05:23Z tcarlson $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  package org.mule.lifecycle;
11  
12  import org.mule.api.lifecycle.Disposable;
13  import org.mule.api.lifecycle.Initialisable;
14  import org.mule.api.lifecycle.LifecycleCallback;
15  import org.mule.api.lifecycle.LifecycleException;
16  import org.mule.api.lifecycle.LifecycleManager;
17  import org.mule.api.lifecycle.LifecycleState;
18  import org.mule.api.lifecycle.Startable;
19  import org.mule.api.lifecycle.Stoppable;
20  import org.mule.lifecycle.phases.NotInLifecyclePhase;
21  
22  import java.util.HashSet;
23  import java.util.LinkedHashSet;
24  import java.util.Set;
25  import java.util.TreeMap;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  
30  /**
31   * This is a base implementation of the {@link org.mule.api.lifecycle.LifecycleManager} interface and provides
32   * almost all the plumbing required to write a {@link org.mule.api.lifecycle.LifecycleManager} implementation.  This
33   * class handles the tracking ofg the phases, transition validation and checking state.
34   * @param <O> The object type being managed by this {@link org.mule.api.lifecycle.LifecycleManager}
35   *
36   * @since 3.0
37   */
38  public abstract class AbstractLifecycleManager<O> implements LifecycleManager
39  {
40      /**
41       * logger used by this class
42       */
43      protected transient final Log logger = LogFactory.getLog(AbstractLifecycleManager.class);
44  
45      protected String lifecycleManagerId;
46      protected String currentPhase = NotInLifecyclePhase.PHASE_NAME;
47      protected String executingPhase = null;
48      private Set<String> directTransitions = new HashSet<String>();
49      protected Set<String> phaseNames = new LinkedHashSet<String>(4);
50      protected Set<String> completedPhases = new LinkedHashSet<String>(4);
51      protected O object;
52      protected LifecycleState state;
53  
54      private TreeMap<String, LifecycleCallback> callbacks = new TreeMap<String, LifecycleCallback>();
55  
56  
57      public AbstractLifecycleManager(String id, O object)
58      {
59          lifecycleManagerId = id;
60          this.object = object;
61          state = createLifecycleState();
62  
63          currentPhase = NotInLifecyclePhase.PHASE_NAME;
64          completedPhases.add(currentPhase);
65          registerTransitions();
66      }
67  
68      protected abstract void registerTransitions();
69  
70      public void registerLifecycleCallback(String phaseName, LifecycleCallback<O> callback)
71      {
72          callbacks.put(phaseName, callback);
73      }
74  
75      protected LifecycleState createLifecycleState()
76      {
77          return new DefaultLifecycleState(this);
78      }
79  
80      protected void addDirectTransition(String phase1, String phase2)
81      {
82          directTransitions.add(phase1 + "-" + phase2);
83          phaseNames.add(phase1);
84          phaseNames.add(phase2);
85      }
86  
87      public void checkPhase(String name) throws IllegalStateException
88      {
89          if (executingPhase != null)
90          {
91              if (name.equalsIgnoreCase(executingPhase))
92              {
93                  throw new IllegalStateException("Phase '" + name + "' is already currently being executed");
94              }
95              else
96              {
97                  throw new IllegalStateException("Cannot fire phase '" + name + "', currently executing lifecycle phase: " + executingPhase);
98              }
99          }
100 
101         if (name.equalsIgnoreCase(currentPhase))
102         {
103             throw new IllegalStateException("Already in lifecycle phase '" + name + "', cannot fire the same phase twice");
104         }
105 
106 
107         if (!phaseNames.contains(name))
108         {
109             throw new IllegalStateException("Phase does not exist: " + name);
110         }
111         else
112         {
113             if (isDirectTransition(name))
114             {
115                 return;
116             }
117 
118             throw new IllegalStateException("Lifecycle Manager '" + lifecycleManagerId + "' phase '" + currentPhase + "' does not support phase '" + name + "'");
119         }
120     }
121 
122     public O getLifecycleObject()
123     {
124         return object;
125     }
126 
127     public void fireLifecycle(String phase) throws LifecycleException
128     {
129         checkPhase(phase);
130         invokePhase(phase, object, callbacks.get(phase));
131     }
132 
133     protected void invokePhase(String phase, Object object, LifecycleCallback callback) throws LifecycleException
134     {
135         try
136         {
137             setExecutingPhase(phase);
138             callback.onTransition(phase, object);
139             setCurrentPhase(phase);
140         }
141         catch(LifecycleException e)
142         {
143             throw e;
144         }
145         catch(Exception e)
146         {
147             throw new LifecycleException(e, object);
148         }
149         finally
150         {
151             setExecutingPhase(null);
152         }
153 
154     }
155 
156     public boolean isDirectTransition(String destinationPhase)
157     {
158         return isDirectTransition(getCurrentPhase(), destinationPhase);
159     }
160 
161     protected boolean isDirectTransition(String startPhase, String endPhase)
162     {
163         String key = startPhase + "-" + endPhase;
164         return directTransitions.contains(key);
165     }
166 
167     public String getCurrentPhase()
168     {
169         return currentPhase;
170     }
171 
172     protected void setCurrentPhase(String currentPhase)
173     {
174         this.currentPhase = currentPhase;
175         completedPhases.add(currentPhase);
176         //remove irrelevant phases
177         if(currentPhase.equals(Stoppable.PHASE_NAME))
178         {
179             completedPhases.remove(Startable.PHASE_NAME);
180         }
181         else if(currentPhase.equals(Disposable.PHASE_NAME))
182         {
183             completedPhases.remove(Initialisable.PHASE_NAME);
184         }
185 
186         notifyTransition(currentPhase);
187 
188     }
189 
190     public String getExecutingPhase()
191     {
192         return executingPhase;
193     }
194 
195     protected void setExecutingPhase(String executingPhase)
196     {
197         this.executingPhase = executingPhase;
198     }
199 
200     /**
201      * Allows any for any state adjustments in sub classes.  For example, it may be necessary to remove a
202      * state from the 'completedPhases' collection once a transition occurs. This is only necessary for a Lifecycle
203      * Manager that introduces a new phase pair.
204      *
205      * @param currentPhase the currently completed phase
206      */
207     protected void notifyTransition(String currentPhase)
208     {
209         //do nothing
210     }
211 
212     public void reset()
213     {
214         completedPhases.clear();
215         setExecutingPhase(null);
216         setCurrentPhase(NOT_IN_LIFECYCLE_PHASE.getName());
217         completedPhases.add(getCurrentPhase());
218     }
219 
220     public boolean isPhaseComplete(String phaseName)
221     {
222         return completedPhases.contains(phaseName);
223     }
224 
225     public LifecycleState getState()
226     {
227         return state;
228     }
229 
230     
231 }