View Javadoc

1   // ========================================================================
2   // Copyright (c) Webtide LLC
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   //
8   // The Eclipse Public License is available at 
9   // http://www.eclipse.org/legal/epl-v10.html
10  //
11  // The Apache License v2.0 is available at
12  // http://www.apache.org/licenses/LICENSE-2.0.txt
13  //
14  // You may elect to redistribute this code under either of these licenses. 
15  // ========================================================================
16  package org.eclipse.jetty.deploy;
17  
18  import java.util.ArrayList;
19  import java.util.Collection;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Queue;
26  import java.util.concurrent.ConcurrentLinkedQueue;
27  
28  import org.eclipse.jetty.deploy.bindings.StandardDeployer;
29  import org.eclipse.jetty.deploy.bindings.StandardStarter;
30  import org.eclipse.jetty.deploy.bindings.StandardStopper;
31  import org.eclipse.jetty.deploy.bindings.StandardUndeployer;
32  import org.eclipse.jetty.deploy.graph.Edge;
33  import org.eclipse.jetty.deploy.graph.Node;
34  import org.eclipse.jetty.deploy.graph.Path;
35  import org.eclipse.jetty.server.Server;
36  import org.eclipse.jetty.server.handler.ContextHandlerCollection;
37  import org.eclipse.jetty.util.AttributesMap;
38  import org.eclipse.jetty.util.component.AbstractLifeCycle;
39  import org.eclipse.jetty.util.component.AggregateLifeCycle;
40  import org.eclipse.jetty.util.log.Log;
41  
42  /**
43   * The Deployment Manager.
44   * <p>
45   * Responsibilities:
46   * <p>
47   * <img src="doc-files/DeploymentManager_Roles.png">
48   * <ol>
49   * <li>Tracking Apps and their LifeCycle Location</li>
50   * <li>Managing AppProviders and the Apps that they provide.</li>
51   * <li>Executing AppLifeCycle on App based on current and desired LifeCycle Location.</li>
52   * </ol>
53   * <p>
54   * <img src="doc-files/DeploymentManager.png">
55   */
56  public class DeploymentManager extends AggregateLifeCycle
57  {
58      /**
59       * Represents a single tracked app within the deployment manager.
60       */
61      public class AppEntry
62      {
63          /**
64           * Version of the app.
65           * 
66           * Note: Auto-increments on each {@link DeploymentManager#addApp(App)}
67           */
68          private int version;
69  
70          /**
71           * The app being tracked.
72           */
73          private App app;
74  
75          /**
76           * The lifecycle node location of this App
77           */
78          private Node lifecyleNode;
79  
80          /**
81           * Tracking the various AppState timestamps (in system milliseconds)
82           */
83          private Map<Node, Long> stateTimestamps = new HashMap<Node, Long>();
84  
85          public App getApp()
86          {
87              return app;
88          }
89  
90          public Node getLifecyleNode()
91          {
92              return lifecyleNode;
93          }
94  
95          public Map<Node, Long> getStateTimestamps()
96          {
97              return stateTimestamps;
98          }
99  
100         public int getVersion()
101         {
102             return version;
103         }
104 
105         void setLifeCycleNode(Node node)
106         {
107             this.lifecyleNode = node;
108             this.stateTimestamps.put(node,Long.valueOf(System.currentTimeMillis()));
109         }
110     }
111 
112     private final List<AppProvider> _providers = new ArrayList<AppProvider>();
113     private final AppLifeCycle _lifecycle = new AppLifeCycle();
114     private final Queue<AppEntry> _apps = new ConcurrentLinkedQueue<AppEntry>();
115     private AttributesMap _contextAttributes = new AttributesMap();
116     private ContextHandlerCollection _contexts;
117     private boolean _useStandardBindings = true;
118     private String _defaultLifeCycleGoal = AppLifeCycle.STARTED;
119 
120     /**
121      * Receive an app for processing.
122      * 
123      * Most commonly used by the various {@link AppProvider} implementations.
124      */
125     public void addApp(App app)
126     {
127         Log.info("Deployable added: " + app.getOriginId());
128         AppEntry entry = new AppEntry();
129         entry.app = app;
130         entry.setLifeCycleNode(_lifecycle.getNodeByName("undeployed"));
131         _apps.add(entry);
132 
133         if (isRunning() && _defaultLifeCycleGoal != null)
134         {
135             // Immediately attempt to go to default lifecycle state
136             this.requestAppGoal(entry,_defaultLifeCycleGoal);
137         }
138     }
139 
140     public void setAppProviders(Collection<AppProvider> providers)
141     {
142         if (isRunning())
143             throw new IllegalStateException();
144         
145         _providers.clear();
146         removeBeans();
147         for (AppProvider provider:providers)
148             if (_providers.add(provider))
149                 addBean(provider);
150     }
151 
152     public Collection<AppProvider> getAppProviders()
153     {
154         return Collections.unmodifiableList(_providers);
155     }
156     
157     public void addAppProvider(AppProvider provider)
158     {
159         if (isRunning())
160             throw new IllegalStateException();
161         
162         List<AppProvider> old = new ArrayList<AppProvider>(_providers);
163         if (_providers.add(provider) && getServer()!=null)
164             getServer().getContainer().update(this, null, provider, "provider");
165             
166         addBean(provider);        
167     }
168 
169     public void setLifeCycleBindings(Collection<AppLifeCycle.Binding> bindings)
170     {
171         if (isRunning())
172             throw new IllegalStateException();
173         for (AppLifeCycle.Binding b : _lifecycle.getBindings())
174             _lifecycle.removeBinding(b);
175         for (AppLifeCycle.Binding b : bindings)
176             _lifecycle.addBinding(b);
177     }
178 
179     public Collection<AppLifeCycle.Binding> getLifeCycleBindings()
180     {
181         return Collections.unmodifiableSet(_lifecycle.getBindings());
182     }
183     
184     public void addLifeCycleBinding(AppLifeCycle.Binding binding)
185     {
186         _lifecycle.addBinding(binding);
187     }
188 
189     /**
190      * Convenience method to allow for insertion of nodes into the lifecycle.
191      * 
192      * @param existingFromNodeName
193      * @param existingToNodeName
194      * @param insertedNodeName
195      */
196     public void insertLifeCycleNode(String existingFromNodeName, String existingToNodeName, String insertedNodeName)
197     {
198         Node fromNode = _lifecycle.getNodeByName(existingFromNodeName);
199         Node toNode = _lifecycle.getNodeByName(existingToNodeName);
200         Edge edge = new Edge(fromNode,toNode);
201         _lifecycle.insertNode(edge,insertedNodeName);
202     }
203 
204     @Override
205     protected void doStart() throws Exception
206     {
207         if (_useStandardBindings)
208         {
209             Log.debug("DeploymentManager using standard bindings");
210             addLifeCycleBinding(new StandardDeployer());
211             addLifeCycleBinding(new StandardStarter());
212             addLifeCycleBinding(new StandardStopper());
213             addLifeCycleBinding(new StandardUndeployer());
214         }
215 
216         // Start all of the AppProviders
217         for (AppProvider provider : _providers)
218         {
219             startAppProvider(provider);
220         }
221         super.doStart();
222     }
223 
224     @Override
225     protected void doStop() throws Exception
226     {
227         // Stop all of the AppProviders
228         for (AppProvider provider : _providers)
229         {
230             try
231             {
232                 provider.stop();
233             }
234             catch (Exception e)
235             {
236                 Log.warn("Unable to start AppProvider",e);
237             }
238         }
239         super.doStop();
240     }
241 
242     private AppEntry findAppByOriginId(String originId)
243     {
244         if (originId == null)
245         {
246             return null;
247         }
248 
249         for (AppEntry entry : _apps)
250         {
251             if (originId.equals(entry.app.getOriginId()))
252             {
253                 return entry;
254             }
255         }
256         return null;
257     }
258     
259     public App getAppByOriginId(String originId)
260     {
261         AppEntry entry = findAppByOriginId(originId);
262         if (entry == null)
263         {
264             return null;
265         }
266         return entry.app;
267     }
268 
269     public Collection<AppEntry> getAppEntries()
270     {
271         return _apps;
272     }
273 
274     public Collection<App> getApps()
275     {
276         List<App> ret = new ArrayList<App>();
277         for (AppEntry entry : _apps)
278         {
279             ret.add(entry.app);
280         }
281         return ret;
282     }
283 
284     /**
285      * Get Set of {@link App}s by {@link Node}
286      * 
287      * @param node
288      *            the node to look for.
289      * @return the collection of apps for the node
290      */
291     public Collection<App> getApps(Node node)
292     {
293         List<App> ret = new ArrayList<App>();
294         for (AppEntry entry : _apps)
295         {
296             if (entry.lifecyleNode == node)
297             {
298                 ret.add(entry.app);
299             }
300         }
301         return ret;
302     }
303 
304     public List<App> getAppsWithSameContext(App app)
305     {
306         List<App> ret = new ArrayList<App>();
307         if (app == null)
308         {
309             return ret;
310         }
311 
312         String contextId = app.getContextPath();
313         if (contextId == null)
314         {
315             // No context? Likely not deployed or started yet.
316             return ret;
317         }
318 
319         for (AppEntry entry : _apps)
320         {
321             if (entry.app.equals(app))
322             {
323                 // Its the input app. skip it.
324                 // TODO: is this filter needed?
325                 continue;
326             }
327 
328             if (contextId.equals(entry.app.getContextPath()))
329             {
330                 ret.add(entry.app);
331             }
332         }
333         return ret;
334     }
335 
336     /**
337      * Get a contextAttribute that will be set for every Context deployed by this provider.
338      * 
339      * @param name
340      * @return the context attribute value
341      */
342     public Object getContextAttribute(String name)
343     {
344         return _contextAttributes.getAttribute(name);
345     }
346 
347     public AttributesMap getContextAttributes()
348     {
349         return _contextAttributes;
350     }
351 
352     public ContextHandlerCollection getContexts()
353     {
354         return _contexts;
355     }
356 
357     public String getDefaultLifeCycleGoal()
358     {
359         return _defaultLifeCycleGoal;
360     }
361 
362     public AppLifeCycle getLifeCycle()
363     {
364         return _lifecycle;
365     }
366 
367     public Server getServer()
368     {
369         if (_contexts == null)
370         {
371             return null;
372         }
373         return _contexts.getServer();
374     }
375 
376     /**
377      * Remove the app from the tracking of the DeploymentManager
378      * 
379      * @param app
380      *            if the app is Unavailable remove it from the deployment manager.
381      */
382     public void removeApp(App app)
383     {
384         Iterator<AppEntry> it = _apps.iterator();
385         while (it.hasNext())
386         {
387             AppEntry entry = it.next();
388             if (entry.app.equals(app))
389             {
390                 if (! AppLifeCycle.UNDEPLOYED.equals(entry.lifecyleNode.getName()))
391                     requestAppGoal(entry.app,AppLifeCycle.UNDEPLOYED);
392                 it.remove();
393                 Log.info("Deployable removed: " + entry.app);
394             }
395         }
396     }
397 
398     public void removeAppProvider(AppProvider provider)
399     {
400         if(_providers.remove(provider))
401         {
402             removeBean(provider);
403             if (getServer()!=null)
404                 getServer().getContainer().update(this, provider,null, "provider");
405         }
406         try
407         {
408             provider.stop();
409         }
410         catch (Exception e)
411         {
412             Log.warn("Unable to stop Provider",e);
413         }
414     }
415 
416     /**
417      * Remove a contextAttribute that will be set for every Context deployed by this provider.
418      * 
419      * @param name
420      */
421     public void removeContextAttribute(String name)
422     {
423         _contextAttributes.removeAttribute(name);
424     }
425 
426     /**
427      * Move an {@link App} through the {@link AppLifeCycle} to the desired {@link Node}, executing each lifecycle step
428      * in the process to reach the desired state.
429      * 
430      * @param app
431      *            the app to move through the process
432      * @param nodeName
433      *            the name of the node to attain
434      */
435     public void requestAppGoal(App app, String nodeName)
436     {
437         AppEntry appentry = findAppByOriginId(app.getOriginId());
438             if (appentry == null)
439             {
440                 throw new IllegalStateException("App not being tracked by Deployment Manager: " + app);
441             }
442         
443         requestAppGoal(appentry,nodeName);
444     }
445 
446     /**
447      * Move an {@link App} through the {@link AppLifeCycle} to the desired {@link Node}, executing each lifecycle step
448      * in the process to reach the desired state.
449      * 
450      * @param appentry
451      *            the internal appentry to move through the process
452      * @param nodeName
453      *            the name of the node to attain
454      */
455     private void requestAppGoal(AppEntry appentry, String nodeName)
456     {
457         Node destinationNode = _lifecycle.getNodeByName(nodeName);
458         // Compute lifecycle steps
459         Path path = _lifecycle.getPath(appentry.lifecyleNode,destinationNode);
460         if (path.isEmpty())
461         {
462             // nothing to do. already there.
463             return;
464         }
465 
466         // Execute each Node binding.  Stopping at any thrown exception.
467         try
468         {
469             Iterator<Node> it = path.getNodes().iterator();
470             if (it.hasNext()) // Any entries?
471             {
472                 // The first entry in the path is always the start node
473                 // We don't want to run bindings on that entry (again)
474                 it.next(); // skip first entry
475                 while (it.hasNext())
476                 {
477                     Node node = it.next();
478                     Log.debug("Executing Node: " + node);
479                     _lifecycle.runBindings(node,appentry.app,this);
480                     appentry.setLifeCycleNode(node);
481                 }
482             }
483         }
484         catch (Throwable t)
485         {
486             Log.warn("Unable to reach node goal: " + nodeName,t);
487         }
488     }
489 
490     /**
491      * Move an {@link App} through the {@link AppLifeCycle} to the desired {@link Node}, executing each lifecycle step
492      * in the process to reach the desired state.
493      * 
494      * @param appId
495      *            the id of the app to move through the process
496      * @param nodeName
497      *            the name of the node to attain
498      */
499     public void requestAppGoal(String appId, String nodeName)
500     {
501         AppEntry appentry = findAppByOriginId(appId);
502         if (appentry == null)
503         {
504             throw new IllegalStateException("App not being tracked by Deployment Manager: " + appId);
505         }
506         requestAppGoal(appentry,nodeName);
507     }
508 
509     /**
510      * Set a contextAttribute that will be set for every Context deployed by this provider.
511      * 
512      * @param name
513      * @param value
514      */
515     public void setContextAttribute(String name, Object value)
516     {
517         _contextAttributes.setAttribute(name,value);
518     }
519 
520     public void setContextAttributes(AttributesMap contextAttributes)
521     {
522         this._contextAttributes = contextAttributes;
523     }
524 
525     public void setContexts(ContextHandlerCollection contexts)
526     {
527         this._contexts = contexts;
528     }
529 
530     public void setDefaultLifeCycleGoal(String defaultLifeCycleState)
531     {
532         this._defaultLifeCycleGoal = defaultLifeCycleState;
533     }
534 
535     private void startAppProvider(AppProvider provider)
536     {
537         try
538         {
539             provider.setDeploymentManager(this);
540             provider.start();
541         }
542         catch (Exception e)
543         {
544             Log.warn("Unable to start AppProvider",e);
545         }
546     }
547 
548     public void undeployAll()
549     {
550         Log.info("Undeploy All");
551         for (AppEntry appentry : _apps)
552         {
553             requestAppGoal(appentry,"undeployed");
554         }
555     }
556 
557     public boolean isUseStandardBindings()
558     {
559         return _useStandardBindings;
560     }
561 
562     public void setUseStandardBindings(boolean useStandardBindings)
563     {
564         this._useStandardBindings = useStandardBindings;
565     }
566 }