| // ======================================================================== |
| // 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); |
| } |
| } |
| } |