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