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