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