| // ======================================================================== |
| // Copyright (c) Webtide LLC |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.apache.org/licenses/LICENSE-2.0.txt |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| package org.eclipse.jetty.deploy; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Map; |
| |
| import org.eclipse.jetty.deploy.graph.Node; |
| import org.eclipse.jetty.deploy.support.ConfigurationManager; |
| import org.eclipse.jetty.server.Server; |
| import org.eclipse.jetty.server.handler.ContextHandlerCollection; |
| import org.eclipse.jetty.util.AttributesMap; |
| import org.eclipse.jetty.util.component.AbstractLifeCycle; |
| import org.eclipse.jetty.util.log.Log; |
| |
| /** |
| * The Deployment Manager. |
| * <p> |
| * Responsibilities: |
| * <p> |
| * <img src="doc-files/DeploymentManager_Roles.png"> |
| * <ol> |
| * <li>Tracking Apps and their Lifecycle Location</li> |
| * <li>Managing AppProviders and the Apps that they provide.</li> |
| * <li>Executing AppLifecycle on App based on current and desired Lifecycle Location.</li> |
| * </ol> |
| * <p> |
| * <img src="doc-files/DeploymentManager.png"> |
| */ |
| public class DeploymentManager extends AbstractLifeCycle |
| { |
| /** |
| * Represents a single tracked app within the deployment manager. |
| */ |
| public class AppEntry |
| { |
| /** |
| * Version of the app. |
| * |
| * Note: Auto-increments on each {@link DeploymentManager#addApp(App)} |
| */ |
| private int version; |
| |
| /** |
| * The app being tracked. |
| */ |
| private App app; |
| |
| /** |
| * The lifecycle node location of this App |
| */ |
| private Node lifecyleNode; |
| |
| /** |
| * Tracking the various AppState timestamps (in system milliseconds) |
| */ |
| private Map<Node, Long> stateTimestamps = new HashMap<Node, Long>(); |
| |
| public App getApp() |
| { |
| return app; |
| } |
| |
| public Node getLifecyleNode() |
| { |
| return lifecyleNode; |
| } |
| |
| public Map<Node, Long> getStateTimestamps() |
| { |
| return stateTimestamps; |
| } |
| |
| public int getVersion() |
| { |
| return version; |
| } |
| |
| void setLifecycleNode(Node node) |
| { |
| this.lifecyleNode = node; |
| this.stateTimestamps.put(node,Long.valueOf(System.currentTimeMillis())); |
| } |
| } |
| |
| private List<AppProvider> providers = new ArrayList<AppProvider>(); |
| private AppLifecycle lifecycle = new AppLifecycle(); |
| private LinkedList<AppEntry> apps = new LinkedList<AppEntry>(); |
| private AttributesMap contextAttributes = new AttributesMap(); |
| private ConfigurationManager configurationManager; |
| private ContextHandlerCollection contexts; |
| private String defaultLifeCycleState = "started"; |
| |
| /** |
| * Receive an app for processing. |
| * |
| * Most commonly used by the various {@link AppProvider} implementations. |
| */ |
| public void addApp(App app) |
| { |
| Log.info("App Added: " + app.getOriginId()); |
| AppEntry entry = new AppEntry(); |
| entry.app = app; |
| entry.setLifecycleNode(lifecycle.getNodeByName("undeployed")); |
| apps.add(entry); |
| |
| if (defaultLifeCycleState != null) |
| { |
| // Immediately attempt to go to default lifecycle state |
| this.requestAppGoal(entry,defaultLifeCycleState); |
| } |
| } |
| |
| public void addAppProvider(AppProvider provider) |
| { |
| providers.add(provider); |
| if (isStarted() || isStarting()) |
| { |
| try |
| { |
| provider.startProvider(this); |
| } |
| catch (Exception e) |
| { |
| Log.warn("Unable to start AppProvider",e); |
| } |
| } |
| } |
| |
| public void addLifecycleBinding(Object binding) |
| { |
| lifecycle.addBinding(binding); |
| } |
| |
| @Override |
| protected void doStart() throws Exception |
| { |
| Log.info("Starting all Providers: " + providers.size()); |
| // Start all of the AppProviders |
| for (AppProvider provider : providers) |
| { |
| try |
| { |
| provider.startProvider(this); |
| } |
| catch (Exception e) |
| { |
| Log.warn("Unable to start AppProvider",e); |
| } |
| } |
| super.doStart(); |
| } |
| |
| @Override |
| protected void doStop() throws Exception |
| { |
| Log.info("Stopping all Providers: " + providers.size()); |
| |
| // Stop all of the AppProviders |
| for (AppProvider provider : providers) |
| { |
| try |
| { |
| provider.stopProvider(); |
| } |
| catch (Exception e) |
| { |
| Log.warn("Unable to start AppProvider",e); |
| } |
| } |
| super.doStop(); |
| } |
| |
| private AppEntry findAppByContextId(String contextId) |
| { |
| if (contextId == null) |
| { |
| return null; |
| } |
| |
| for (AppEntry entry : apps) |
| { |
| if (contextId.equals(entry.app.getContextId())) |
| { |
| return entry; |
| } |
| } |
| return null; |
| } |
| |
| private AppEntry findAppByOriginId(String originId) |
| { |
| if (originId == null) |
| { |
| return null; |
| } |
| |
| for (AppEntry entry : apps) |
| { |
| if (originId.equals(entry.app.getOriginId())) |
| { |
| return entry; |
| } |
| } |
| return null; |
| } |
| |
| public App getAppByContextId(String contextId) |
| { |
| AppEntry entry = findAppByContextId(contextId); |
| if (entry == null) |
| { |
| return null; |
| } |
| return entry.app; |
| } |
| |
| public App getAppByOriginId(String originId) |
| { |
| AppEntry entry = findAppByOriginId(originId); |
| if (entry == null) |
| { |
| return null; |
| } |
| return entry.app; |
| } |
| |
| public Collection<AppEntry> getAppEntries() |
| { |
| return apps; |
| } |
| |
| public Collection<AppProvider> getAppProviders() |
| { |
| return providers; |
| } |
| |
| public Collection<App> getApps() |
| { |
| List<App> ret = new ArrayList<App>(); |
| for (AppEntry entry : apps) |
| { |
| ret.add(entry.app); |
| } |
| return ret; |
| } |
| |
| /** |
| * Get Set of {@link App}s by {@link Node} |
| * |
| * @param state |
| * the state to look for. |
| * @return |
| */ |
| public Collection<App> getApps(Node node) |
| { |
| List<App> ret = new ArrayList<App>(); |
| for (AppEntry entry : apps) |
| { |
| if (entry.lifecyleNode == node) |
| { |
| ret.add(entry.app); |
| } |
| } |
| return ret; |
| } |
| |
| public List<App> getAppsWithSameContext(App app) |
| { |
| List<App> ret = new ArrayList<App>(); |
| if (app == null) |
| { |
| return ret; |
| } |
| |
| String contextId = app.getContextId(); |
| if (contextId == null) |
| { |
| // No context? Likely not deployed or started yet. |
| return ret; |
| } |
| |
| for (AppEntry entry : apps) |
| { |
| if (entry.app.equals(app)) |
| { |
| // Its the input app. skip it. |
| // TODO: is this filter needed? |
| continue; |
| } |
| |
| if (contextId.equals(entry.app.getContextId())) |
| { |
| ret.add(entry.app); |
| } |
| } |
| return ret; |
| } |
| |
| /** |
| * Get a contextAttribute that will be set for every Context deployed by this provider. |
| * |
| * @param name |
| * @return |
| */ |
| public Object getAttribute(String name) |
| { |
| return contextAttributes.getAttribute(name); |
| } |
| |
| public ConfigurationManager getConfigurationManager() |
| { |
| return configurationManager; |
| } |
| |
| public AttributesMap getContextAttributes() |
| { |
| return contextAttributes; |
| } |
| |
| public ContextHandlerCollection getContexts() |
| { |
| return contexts; |
| } |
| |
| public String getDefaultLifeCycleState() |
| { |
| return defaultLifeCycleState; |
| } |
| |
| public AppLifecycle getLifecycle() |
| { |
| return lifecycle; |
| } |
| |
| public Server getServer() |
| { |
| if (contexts == null) |
| { |
| return null; |
| } |
| return contexts.getServer(); |
| } |
| |
| /** |
| * Remove the app from the tracking of the DeploymentManager |
| * |
| * @param app |
| * if the app is Unavailable remove it from the deployment manager. |
| */ |
| public void removeApp(App app) |
| { |
| ListIterator<AppEntry> it = apps.listIterator(); |
| while (it.hasNext()) |
| { |
| AppEntry entry = it.next(); |
| if (entry.app.equals(app) && "undeployed".equals(entry.lifecyleNode.getName())) |
| { |
| Log.info("Remove App: " + entry.app); |
| it.remove(); |
| } |
| } |
| } |
| |
| public void removeAppProvider(AppProvider provider) |
| { |
| providers.remove(provider); |
| try |
| { |
| provider.stopProvider(); |
| } |
| catch (Exception e) |
| { |
| Log.warn("Unable to stop Provider",e); |
| } |
| } |
| |
| /** |
| * Remove a contextAttribute that will be set for every Context deployed by this provider. |
| * |
| * @param name |
| */ |
| public void removeAttribute(String name) |
| { |
| contextAttributes.removeAttribute(name); |
| } |
| |
| /** |
| * Move an {@link App} through the {@link AppLifecycle} to the desired {@link Node}, executing each lifecycle step |
| * in the process to reach the desired state. |
| * |
| * @param app |
| * the app to move through the process |
| * @param nodeName |
| * the name of the node to attain |
| */ |
| public void requestAppGoal(App app, String nodeName) |
| { |
| AppEntry appentry = findAppByContextId(app.getContextId()); |
| if (appentry == null) |
| { |
| appentry = findAppByOriginId(app.getOriginId()); |
| if (appentry == null) |
| { |
| throw new IllegalStateException("App not being tracked by Deployment Manager: " + app); |
| } |
| } |
| requestAppGoal(appentry,nodeName); |
| } |
| |
| /** |
| * Move an {@link App} through the {@link AppLifecycle} to the desired {@link Node}, executing each lifecycle step |
| * in the process to reach the desired state. |
| * |
| * @param appentry |
| * the internal appentry to move through the process |
| * @param nodeName |
| * the name of the node to attain |
| */ |
| private void requestAppGoal(AppEntry appentry, String nodeName) |
| { |
| Node destinationNode = lifecycle.getNodeByName(nodeName); |
| // Compute lifecycle steps |
| List<Node> path = lifecycle.findPath(appentry.lifecyleNode,destinationNode); |
| if (path.isEmpty()) |
| { |
| // nothing to do. already there. |
| return; |
| } |
| |
| // Execute each Node binding. Stopping at any thrown exception. |
| try |
| { |
| Iterator<Node> it = path.iterator(); |
| if (it.hasNext()) // Any entries? |
| { |
| // The first entry in the path is always the start node |
| // We don't want to run bindings on that entry (again) |
| it.next(); // skip first entry |
| while (it.hasNext()) |
| { |
| Node node = it.next(); |
| Log.info("Executing Node: " + node); |
| lifecycle.runBindings(node,appentry.app,this); |
| appentry.setLifecycleNode(node); |
| } |
| } |
| } |
| catch (Throwable t) |
| { |
| Log.warn("Unable to reach node goal: " + nodeName,t); |
| } |
| } |
| |
| /** |
| * Move an {@link App} through the {@link AppLifecycle} to the desired {@link Node}, executing each lifecycle step |
| * in the process to reach the desired state. |
| * |
| * @param appId |
| * the id of the app to move through the process |
| * @param nodeName |
| * the name of the node to attain |
| */ |
| public void requestAppGoal(String appId, String nodeName) |
| { |
| AppEntry appentry = findAppByContextId(appId); |
| if (appentry == null) |
| { |
| appentry = findAppByOriginId(appId); |
| if (appentry == null) |
| { |
| throw new IllegalStateException("App not being tracked by Deployment Manager: " + appId); |
| } |
| } |
| requestAppGoal(appentry,nodeName); |
| } |
| |
| /** |
| * Set a contextAttribute that will be set for every Context deployed by this provider. |
| * |
| * @param name |
| * @param value |
| */ |
| public void setAttribute(String name, Object value) |
| { |
| contextAttributes.setAttribute(name,value); |
| } |
| |
| public void setConfigurationManager(ConfigurationManager configurationManager) |
| { |
| this.configurationManager = configurationManager; |
| } |
| |
| public void setContextAttributes(AttributesMap contextAttributes) |
| { |
| this.contextAttributes = contextAttributes; |
| } |
| |
| public void setContexts(ContextHandlerCollection contexts) |
| { |
| this.contexts = contexts; |
| } |
| |
| public void setDefaultLifeCycleState(String defaultLifeCycleState) |
| { |
| this.defaultLifeCycleState = defaultLifeCycleState; |
| } |
| |
| public void undeployAll() |
| { |
| Log.info("Undeploy (All) started"); |
| for (AppEntry appentry : apps) |
| { |
| Log.info("Undeploy: " + appentry); |
| requestAppGoal(appentry,"undeployed"); |
| } |
| Log.info("Undeploy (All) completed"); |
| } |
| } |