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