View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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 (_useStandardBindings)
221         {
222             LOG.debug("DeploymentManager using standard bindings");
223             addLifeCycleBinding(new StandardDeployer());
224             addLifeCycleBinding(new StandardStarter());
225             addLifeCycleBinding(new StandardStopper());
226             addLifeCycleBinding(new StandardUndeployer());
227         }
228 
229         // Start all of the AppProviders
230         for (AppProvider provider : _providers)
231         {
232             startAppProvider(provider);
233         }
234         super.doStart();
235     }
236 
237     @Override
238     protected void doStop() throws Exception
239     {
240         // Stop all of the AppProviders
241         for (AppProvider provider : _providers)
242         {
243             try
244             {
245                 provider.stop();
246             }
247             catch (Exception e)
248             {
249                 LOG.warn("Unable to start AppProvider",e);
250             }
251         }
252         super.doStop();
253     }
254 
255     private AppEntry findAppByOriginId(String originId)
256     {
257         if (originId == null)
258         {
259             return null;
260         }
261 
262         for (AppEntry entry : _apps)
263         {
264             if (originId.equals(entry.app.getOriginId()))
265             {
266                 return entry;
267             }
268         }
269         return null;
270     }
271     
272     public App getAppByOriginId(String originId)
273     {
274         AppEntry entry = findAppByOriginId(originId);
275         if (entry == null)
276         {
277             return null;
278         }
279         return entry.app;
280     }
281 
282     public Collection<AppEntry> getAppEntries()
283     {
284         return _apps;
285     }
286 
287     @ManagedAttribute("Deployed Apps")
288     public Collection<App> getApps()
289     {
290         List<App> ret = new ArrayList<App>();
291         for (AppEntry entry : _apps)
292         {
293             ret.add(entry.app);
294         }
295         return ret;
296     }
297 
298     /**
299      * Get Set of {@link App}s by {@link Node}
300      * 
301      * @param node
302      *            the node to look for.
303      * @return the collection of apps for the node
304      */
305     public Collection<App> getApps(Node node)
306     {
307         List<App> ret = new ArrayList<App>();
308         for (AppEntry entry : _apps)
309         {
310             if (entry.lifecyleNode == node)
311             {
312                 ret.add(entry.app);
313             }
314         }
315         return ret;
316     }
317 
318     public List<App> getAppsWithSameContext(App app)
319     {
320         List<App> ret = new ArrayList<App>();
321         if (app == null)
322         {
323             return ret;
324         }
325 
326         String contextId = app.getContextPath();
327         if (contextId == null)
328         {
329             // No context? Likely not deployed or started yet.
330             return ret;
331         }
332 
333         for (AppEntry entry : _apps)
334         {
335             if (entry.app.equals(app))
336             {
337                 // Its the input app. skip it.
338                 // TODO: is this filter needed?
339                 continue;
340             }
341 
342             if (contextId.equals(entry.app.getContextPath()))
343             {
344                 ret.add(entry.app);
345             }
346         }
347         return ret;
348     }
349 
350     /**
351      * Get a contextAttribute that will be set for every Context deployed by this provider.
352      * 
353      * @param name
354      * @return the context attribute value
355      */
356     public Object getContextAttribute(String name)
357     {
358         return _contextAttributes.getAttribute(name);
359     }
360 
361     public AttributesMap getContextAttributes()
362     {
363         return _contextAttributes;
364     }
365 
366     @ManagedAttribute("Deployed Contexts")
367     public ContextHandlerCollection getContexts()
368     {
369         return _contexts;
370     }
371 
372     public String getDefaultLifeCycleGoal()
373     {
374         return _defaultLifeCycleGoal;
375     }
376 
377     public AppLifeCycle getLifeCycle()
378     {
379         return _lifecycle;
380     }
381 
382     public Server getServer()
383     {
384         if (_contexts == null)
385         {
386             return null;
387         }
388         return _contexts.getServer();
389     }
390 
391     /**
392      * Remove the app from the tracking of the DeploymentManager
393      * 
394      * @param app
395      *            if the app is Unavailable remove it from the deployment manager.
396      */
397     public void removeApp(App app)
398     {
399         Iterator<AppEntry> it = _apps.iterator();
400         while (it.hasNext())
401         {
402             AppEntry entry = it.next();
403             if (entry.app.equals(app))
404             {
405                 if (! AppLifeCycle.UNDEPLOYED.equals(entry.lifecyleNode.getName()))
406                     requestAppGoal(entry.app,AppLifeCycle.UNDEPLOYED);
407                 it.remove();
408                 LOG.debug("Deployable removed: {}",entry.app);
409             }
410         }
411     }
412 
413     public void removeAppProvider(AppProvider provider)
414     {
415         if(_providers.remove(provider))
416             removeBean(provider);
417         
418         try
419         {
420             provider.stop();
421         }
422         catch (Exception e)
423         {
424             LOG.warn("Unable to stop Provider",e);
425         }
426     }
427 
428     /**
429      * Remove a contextAttribute that will be set for every Context deployed by this provider.
430      * 
431      * @param name
432      */
433     public void removeContextAttribute(String name)
434     {
435         _contextAttributes.removeAttribute(name);
436     }
437 
438     /**
439      * Move an {@link App} through the {@link AppLifeCycle} to the desired {@link Node}, executing each lifecycle step
440      * in the process to reach the desired state.
441      * 
442      * @param app
443      *            the app to move through the process
444      * @param nodeName
445      *            the name of the node to attain
446      */
447     public void requestAppGoal(App app, String nodeName)
448     {
449         AppEntry appentry = findAppByOriginId(app.getOriginId());
450         if (appentry == null)
451         {
452             throw new IllegalStateException("App not being tracked by Deployment Manager: " + app);
453         }
454         
455         requestAppGoal(appentry,nodeName);
456     }
457 
458     /**
459      * Move an {@link App} through the {@link AppLifeCycle} to the desired {@link Node}, executing each lifecycle step
460      * in the process to reach the desired state.
461      * 
462      * @param appentry
463      *            the internal appentry to move through the process
464      * @param nodeName
465      *            the name of the node to attain
466      */
467     private void requestAppGoal(AppEntry appentry, String nodeName)
468     {
469         Node destinationNode = _lifecycle.getNodeByName(nodeName);
470         if (destinationNode == null)
471         {
472             throw new IllegalStateException("Node not present in Deployment Manager: " + nodeName);
473         }
474         // Compute lifecycle steps
475         Path path = _lifecycle.getPath(appentry.lifecyleNode,destinationNode);
476         if (path.isEmpty())
477         {
478             // nothing to do. already there.
479             return;
480         }
481 
482         // Execute each Node binding.  Stopping at any thrown exception.
483         try
484         {
485             Iterator<Node> it = path.getNodes().iterator();
486             if (it.hasNext()) // Any entries?
487             {
488                 // The first entry in the path is always the start node
489                 // We don't want to run bindings on that entry (again)
490                 it.next(); // skip first entry
491                 while (it.hasNext())
492                 {
493                     Node node = it.next();
494                     LOG.debug("Executing Node {}",node);
495                     _lifecycle.runBindings(node,appentry.app,this);
496                     appentry.setLifeCycleNode(node);
497                 }
498             }
499         }
500         catch (Throwable t)
501         {
502             LOG.warn("Unable to reach node goal: " + nodeName,t);
503         }
504     }
505 
506     /**
507      * Move an {@link App} through the {@link AppLifeCycle} to the desired {@link Node}, executing each lifecycle step
508      * in the process to reach the desired state.
509      * 
510      * @param appId
511      *            the id of the app to move through the process
512      * @param nodeName
513      *            the name of the node to attain
514      */
515     @ManagedOperation(value="request the app to be moved to the specified lifecycle node", impact="ACTION")
516     public void requestAppGoal(@Name("appId") String appId, @Name("nodeName") String nodeName)
517     {
518         AppEntry appentry = findAppByOriginId(appId);
519         if (appentry == null)
520         {
521             throw new IllegalStateException("App not being tracked by Deployment Manager: " + appId);
522         }
523         requestAppGoal(appentry,nodeName);
524     }
525 
526     /**
527      * Set a contextAttribute that will be set for every Context deployed by this provider.
528      * 
529      * @param name
530      * @param value
531      */
532     public void setContextAttribute(String name, Object value)
533     {
534         _contextAttributes.setAttribute(name,value);
535     }
536 
537     public void setContextAttributes(AttributesMap contextAttributes)
538     {
539         this._contextAttributes = contextAttributes;
540     }
541 
542     public void setContexts(ContextHandlerCollection contexts)
543     {
544         this._contexts = contexts;
545     }
546 
547     public void setDefaultLifeCycleGoal(String defaultLifeCycleState)
548     {
549         this._defaultLifeCycleGoal = defaultLifeCycleState;
550     }
551 
552     private void startAppProvider(AppProvider provider)
553     {
554         try
555         {
556             provider.setDeploymentManager(this);
557             provider.start();
558         }
559         catch (Exception e)
560         {
561             LOG.warn("Unable to start AppProvider",e);
562         }
563     }
564 
565     public void undeployAll()
566     {
567         LOG.debug("Undeploy All");
568         for (AppEntry appentry : _apps)
569         {
570             requestAppGoal(appentry,"undeployed");
571         }
572     }
573 
574     public boolean isUseStandardBindings()
575     {
576         return _useStandardBindings;
577     }
578 
579     public void setUseStandardBindings(boolean useStandardBindings)
580     {
581         this._useStandardBindings = useStandardBindings;
582     }
583 
584     public Collection<Node> getNodes()
585     {
586         return _lifecycle.getNodes();
587     }
588     
589     @ManagedOperation(value="list apps that are located at specified App LifeCycle nodes", impact="ACTION")
590     public Collection<App> getApps(@Name("nodeName") String nodeName)
591     {
592         return getApps(_lifecycle.getNodeByName(nodeName));
593     }
594 }