View Javadoc

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