View Javadoc

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