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