blob: 6a7941c0614d44050c2d7591383feb1e2ca0b98c [file] [log] [blame]
// ========================================================================
// 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.bindings.StandardDeployer;
import org.eclipse.jetty.deploy.bindings.StandardStarter;
import org.eclipse.jetty.deploy.bindings.StandardStopper;
import org.eclipse.jetty.deploy.bindings.StandardUndeployer;
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 boolean useStandardBindings = true;
private String defaultLifeCycleGoal = "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 (defaultLifeCycleGoal != null)
{
// Immediately attempt to go to default lifecycle state
this.requestAppGoal(entry,defaultLifeCycleGoal);
}
}
public void addAppProvider(AppProvider provider)
{
providers.add(provider);
if (isStarted() || isStarting())
{
startAppProvider(provider);
}
}
public void addLifeCycleBinding(AppLifeCycle.Binding binding)
{
lifecycle.addBinding(binding);
}
@Override
protected void doStart() throws Exception
{
if (useStandardBindings)
{
Log.info("Using standard bindings");
addLifeCycleBinding(new StandardDeployer());
addLifeCycleBinding(new StandardStarter());
addLifeCycleBinding(new StandardStopper());
addLifeCycleBinding(new StandardUndeployer());
}
Log.info("Starting all Providers: " + providers.size());
// Start all of the AppProviders
for (AppProvider provider : providers)
{
startAppProvider(provider);
}
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.stop();
}
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 getDefaultLifeCycleGoal()
{
return defaultLifeCycleGoal;
}
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.stop();
}
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 setDefaultLifeCycleGoal(String defaultLifeCycleState)
{
this.defaultLifeCycleGoal = defaultLifeCycleState;
}
private void startAppProvider(AppProvider provider)
{
try
{
provider.setDeploymentManager(this);
provider.start();
}
catch (Exception e)
{
Log.warn("Unable to start AppProvider",e);
}
}
public void undeployAll()
{
Log.info("Undeploy (All) started");
for (AppEntry appentry : apps)
{
Log.info("Undeploy: " + appentry);
requestAppGoal(appentry,"undeployed");
}
Log.info("Undeploy (All) completed");
}
public boolean isUseStandardBindings()
{
return useStandardBindings;
}
public void setUseStandardBindings(boolean useStandardBindings)
{
this.useStandardBindings = useStandardBindings;
}
}