blob: bf289987b71feb0e09c6d2f55a6d279f921037e6 [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.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jetty.deploy.annotations.DeployLifecycleBinding;
import org.eclipse.jetty.deploy.graph.Graph;
import org.eclipse.jetty.deploy.graph.Node;
import org.eclipse.jetty.util.log.Log;
/**
* The lifecycle of an App in the {@link DeploymentManager}.
*
* Setups a the default {@link Graph}, and manages the bindings to the life cycle via the {@link DeployLifecycleBinding}
* annotation.
* <p>
* <img src="doc-files/AppLifecycle.png">
*/
public class AppLifecycle extends Graph
{
private static class BoundMethod
{
Object obj;
@SuppressWarnings("unused")
Node node;
Method method;
}
// Private string constants defined to avoid typos on repeatedly used strings
private static final String NODE_UNDEPLOYED = "undeployed";
private static final String NODE_PRE_DEPLOYING = "pre-deploying";
private static final String NODE_DEPLOYING = "deploying";
private static final String NODE_DEPLOYED = "deployed";
private static final String NODE_PRE_STARTING = "pre-starting";
private static final String NODE_STARTING = "starting";
private static final String NODE_STARTED = "started";
private static final String NODE_PRE_STOPPING = "pre-stopping";
private static final String NODE_STOPPING = "stopping";
private static final String NODE_PRE_UNDEPLOYING = "pre-undeploying";
private static final String NODE_UNDEPLOYING = "undeploying";
public static void main(String[] args)
{
// Dump All Paths
AppLifecycle lifecycle = new AppLifecycle();
Set<Node> nodes = lifecycle.getNodes();
for (Node from : nodes)
{
for (Node to : nodes)
{
System.out.println();
System.out.printf("Paths %s -> %s:%n",from,to);
List<Node> path = lifecycle.findPath(from,to);
if (path.isEmpty())
{
System.out.printf(" (no steps needed)%n");
continue;
}
for (Node step : path)
{
System.out.printf(" %s%n",step.getName());
}
}
}
}
private Map<Node, List<BoundMethod>> lifecyclebindings = new HashMap<Node, List<BoundMethod>>();
public AppLifecycle()
{
// Define Default Graph
// undeployed -> deployed
addEdge(NODE_UNDEPLOYED,NODE_PRE_DEPLOYING);
addEdge(NODE_PRE_DEPLOYING,NODE_DEPLOYING);
addEdge(NODE_DEPLOYING,NODE_DEPLOYED);
// deployed -> started
addEdge(NODE_DEPLOYED,NODE_PRE_STARTING);
addEdge(NODE_PRE_STARTING,NODE_STARTING);
addEdge(NODE_STARTING,NODE_STARTED);
// started -> deployed
addEdge(NODE_STARTED,NODE_PRE_STOPPING);
addEdge(NODE_PRE_STOPPING,NODE_STOPPING);
addEdge(NODE_STOPPING,NODE_DEPLOYED);
// deployed -> undeployed
addEdge(NODE_DEPLOYED,NODE_PRE_UNDEPLOYING);
addEdge(NODE_PRE_UNDEPLOYING,NODE_UNDEPLOYING);
addEdge(NODE_UNDEPLOYING,NODE_UNDEPLOYED);
}
public void addBinding(Object obj)
{
Class<?>[] expectedParams =
{ Node.class, App.class, DeploymentManager.class };
for (Method method : obj.getClass().getDeclaredMethods())
{
// Does it have annotation?
if (!method.isAnnotationPresent(DeployLifecycleBinding.class))
{
continue; // skip
}
// Is it public?
if (!Modifier.isPublic(method.getModifiers()))
{
continue; // skip
}
// Does it return void?
if (method.getReturnType() != Void.TYPE)
{
continue; // skip
}
// Does it have specified parameters?
Class<?> params[] = method.getParameterTypes();
if (params == null)
{
continue; // skip
}
if (params.length != expectedParams.length)
{
continue; // skip
}
for (int i = 0; i < expectedParams.length; i++)
{
if (params[i].isAssignableFrom(expectedParams[i]) == false)
{
continue; // Not a match. skip it
}
}
// Has to be a match at this point.
DeployLifecycleBinding bindingAnno = method.getAnnotation(DeployLifecycleBinding.class);
for (String nodeName : bindingAnno.nodes())
{
if (nodeName.equals("*"))
{
// Special Case: Bind to all Nodes
for (Node node : getNodes())
{
bindToNode(node,obj,method);
}
}
else
{
// Bind to specific node
Node node = getNodeByName(nodeName);
bindToNode(node,obj,method);
}
}
}
}
private void bindToNode(Node node, Object obj, Method method)
{
// Create Method -> Object binding reference.
BoundMethod bound = new BoundMethod();
bound.obj = obj;
bound.method = method;
bound.node = node;
// Assign to map
List<BoundMethod> bindings = lifecyclebindings.get(node);
if (bindings == null)
{
bindings = new ArrayList<BoundMethod>();
}
bindings.add(bound);
lifecyclebindings.put(node,bindings);
}
/**
* Get all {@link Node} bound objects.
*
* @return Set of Object(s) for all lifecycle bindings. never null.
*/
public Set<Object> getBindings()
{
Set<Object> boundset = new HashSet<Object>();
for (List<BoundMethod> bindings : lifecyclebindings.values())
{
for (BoundMethod bound : bindings)
{
boundset.add(bound.obj);
}
}
return boundset;
}
/**
* Get all objects bound to a specific {@link Node}
*
* @return Set of Object(s) for specific lifecycle bindings. never null.
*/
public Set<Object> getBindings(Node node)
{
Set<Object> boundset = new HashSet<Object>();
List<BoundMethod> bindings = lifecyclebindings.get(node);
if (bindings == null)
{
return boundset;
}
for (BoundMethod bound : bindings)
{
boundset.add(bound.obj);
}
return boundset;
}
public Set<Object> getBindings(String nodeName)
{
Node node = getNodeByName(nodeName);
return getBindings(node);
}
public void runBindings(Node node, App app, DeploymentManager deploymentManager) throws Throwable
{
List<BoundMethod> bindings = this.lifecyclebindings.get(node);
if (bindings == null)
{
return;
}
Object[] params =
{ node, app, deploymentManager };
for (BoundMethod binding : bindings)
{
Log.debug("Calling " + binding.obj.getClass().getName() + "#" + binding.method.getName());
binding.method.invoke(binding.obj,params);
}
}
}