View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.lifecycle;
8   
9   import org.mule.api.MuleContext;
10  import org.mule.api.lifecycle.Disposable;
11  import org.mule.api.lifecycle.Initialisable;
12  import org.mule.api.lifecycle.LifecycleCallback;
13  import org.mule.api.lifecycle.LifecycleException;
14  import org.mule.api.lifecycle.LifecycleManager;
15  import org.mule.api.lifecycle.LifecycleState;
16  import org.mule.api.lifecycle.Startable;
17  import org.mule.api.lifecycle.Stoppable;
18  import org.mule.lifecycle.phases.NotInLifecyclePhase;
19  import org.mule.transport.AbstractConnector;
20  import org.mule.transport.ConnectException;
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
32   * and provides almost all the plumbing required to write a {@link org.mule.api.lifecycle.LifecycleManager}
33   * implementation. This class handles the tracking ofg the phases, transition validation and
34   * checking state.
35   *
36   * @param <O> The object type being managed by this {@link org.mule.api.lifecycle.LifecycleManager}
37   * @since 3.0
38   */
39  public abstract class AbstractLifecycleManager<O> implements LifecycleManager
40  {
41      /**
42       * logger used by this class
43       */
44      protected transient final Log logger = LogFactory.getLog(AbstractLifecycleManager.class);
45  
46      protected String lifecycleManagerId;
47      protected String currentPhase = NotInLifecyclePhase.PHASE_NAME;
48      protected String executingPhase = null;
49      private Set<String> directTransitions = new HashSet<String>();
50      protected Set<String> phaseNames = new LinkedHashSet<String>(4);
51      protected Set<String> completedPhases = new LinkedHashSet<String>(4);
52      protected O object;
53      protected LifecycleState state;
54  
55      private TreeMap<String, LifecycleCallback> callbacks = new TreeMap<String, LifecycleCallback>();
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         // In the case of a connection exception, trigger the reconnection strategy.
142         catch (ConnectException ce)
143         {
144             MuleContext muleContext = ((AbstractConnector) ce.getFailed()).getMuleContext();
145             muleContext.getExceptionListener().handleException(ce);
146         }
147         catch (LifecycleException le)
148         {
149             throw le;
150         }
151         catch (Exception e)
152         {
153             throw new LifecycleException(e, object);
154         }
155         finally
156         {
157             setExecutingPhase(null);
158         }
159 
160     }
161 
162     public boolean isDirectTransition(String destinationPhase)
163     {
164         return isDirectTransition(getCurrentPhase(), destinationPhase);
165     }
166 
167     protected boolean isDirectTransition(String startPhase, String endPhase)
168     {
169         String key = startPhase + "-" + endPhase;
170         return directTransitions.contains(key);
171     }
172 
173     public String getCurrentPhase()
174     {
175         return currentPhase;
176     }
177 
178     protected void setCurrentPhase(String currentPhase)
179     {
180         this.currentPhase = currentPhase;
181         completedPhases.add(currentPhase);
182         //remove irrelevant phases
183         if (currentPhase.equals(Stoppable.PHASE_NAME))
184         {
185             completedPhases.remove(Startable.PHASE_NAME);
186         }
187         else  if (currentPhase.equals(Startable.PHASE_NAME))
188         {
189             completedPhases.remove(Stoppable.PHASE_NAME);
190         }
191         else if (currentPhase.equals(Disposable.PHASE_NAME))
192         {
193             completedPhases.remove(Initialisable.PHASE_NAME);
194         }
195 
196         notifyTransition(currentPhase);
197 
198     }
199 
200     public String getExecutingPhase()
201     {
202         return executingPhase;
203     }
204 
205     protected void setExecutingPhase(String executingPhase)
206     {
207         this.executingPhase = executingPhase;
208     }
209 
210     /**
211      * Allows any for any state adjustments in sub classes.  For example, it may be necessary to remove a
212      * state from the 'completedPhases' collection once a transition occurs. This is only necessary for a Lifecycle
213      * Manager that introduces a new phase pair.
214      *
215      * @param phase the currently completed phase
216      */
217     protected void notifyTransition(String phase)
218     {
219         //do nothing
220     }
221 
222     public void reset()
223     {
224         completedPhases.clear();
225         setExecutingPhase(null);
226         setCurrentPhase(NOT_IN_LIFECYCLE_PHASE.getName());
227         completedPhases.add(getCurrentPhase());
228     }
229 
230     public boolean isPhaseComplete(String phaseName)
231     {
232         return completedPhases.contains(phaseName);
233     }
234 
235     public LifecycleState getState()
236     {
237         return state;
238     }
239 }