View Javadoc

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