Updating to conform to jetty-deploy-manager improvements

git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/sandbox/trunk@1077 7e9141cc-0065-0410-87d8-b60c137991c4
diff --git a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/App.java b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/App.java
index 924c8de..6ef27bd 100644
--- a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/App.java
+++ b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/App.java
@@ -16,26 +16,59 @@
 package org.eclipse.jetty.deploy;
 
 import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jetty.deploy.util.FileID;
 import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.AttributesMap;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.xml.XmlConfiguration;
+import org.xml.sax.SAXException;
 
 /**
  * The information about an App that is managed by the {@link DeploymentManager}
  */
-public interface App
+public class App
 {
-    /**
-     * The raw id of the {@link App}
-     * 
-     * @return the generated Id for the App.
-     */
-    String getId();
+    private File archivePath;
+    private ContextHandler handler;
+    private boolean extractWars = false;
+    private boolean parentLoaderPriority = false;
+    private String defaultsDescriptor;
+    private String originId;
 
     /**
-     * Get the internally declared name of the App.
+     * Create an App with specified Origin ID and archivePath
      * 
-     * @return the internal name of the App.
+     * @param originId
+     *            the origin ID (The ID that the {@link AppProvider} knows about)
+     * @param archivePath
+     *            the path to the app (can be a file *.war or *.jar, or a webapp exploded directory)
+     * @see App#getOriginId()
+     * @see App#getContextId()
      */
-    String getName();
+    public App(String originId, File archivePath)
+    {
+        this.originId = originId;
+        this.archivePath = archivePath;
+    }
+
+    /**
+     * Get the archive path to the App.
+     * 
+     * Might exist as a Directory (example: an exploded webapp, or a jetty:run) or a File (example: a WAR file)
+     * 
+     * @return the
+     */
+    public File getArchivePath()
+    {
+        return this.archivePath;
+    }
 
     /**
      * Get ContextHandler for the App.
@@ -46,28 +79,142 @@
      *         when App is in the {@link AppState#STAGED} state}
      * @throws Exception
      */
-    ContextHandler getContextHandler(DeploymentManager deployMgr) throws Exception;
+    public ContextHandler getContextHandler(DeploymentManager deployMgr) throws Exception
+    {
+        if (handler == null)
+        {
+            if (FileID.isXmlFile(archivePath))
+            {
+                this.handler = createContextFromXml(deployMgr);
+            }
+            else if (FileID.isWebArchive(archivePath))
+            {
+                // Treat as a Web Archive.
+                this.handler = createContextDefault(deployMgr);
+            }
+            else
+            {
+                throw new IllegalStateException("Not an XML or Web Archive: " + archivePath.getAbsolutePath());
+            }
+
+            this.handler.setAttributes(new AttributesMap(deployMgr.getContextAttributes()));
+        }
+        return handler;
+    }
+
+    private ContextHandler createContextDefault(DeploymentManager deploymgr)
+    {
+        String context = archivePath.getName();
+
+        // Context Path is the same as the archive.
+        if (FileID.isWebArchiveFile(archivePath))
+        {
+            context = context.substring(0,context.length() - 4);
+        }
+
+        // Context path is "/" in special case of archive (or dir) named "root" 
+        if (context.equalsIgnoreCase("root") || context.equalsIgnoreCase("root/"))
+        {
+            context = URIUtil.SLASH;
+        }
+
+        // Ensure "/" is Prepended to all context paths.
+        if (context.charAt(0) != '/')
+        {
+            context = "/" + context;
+        }
+
+        // Ensure "/" is Not Trailing in context paths.
+        if (context.endsWith("/") && context.length() > 0)
+        {
+            context = context.substring(0,context.length() - 1);
+        }
+
+        WebAppContext wah = new WebAppContext();
+        wah.setContextPath(context);
+        wah.setWar(archivePath.getAbsolutePath());
+        if (defaultsDescriptor != null)
+        {
+            wah.setDefaultsDescriptor(defaultsDescriptor);
+        }
+        wah.setExtractWAR(extractWars);
+        wah.setParentLoaderPriority(parentLoaderPriority);
+
+        return wah;
+    }
+
+    @SuppressWarnings("unchecked")
+    private ContextHandler createContextFromXml(DeploymentManager deploymgr) throws MalformedURLException, IOException, SAXException, Exception
+    {
+        Resource resource = Resource.newResource(this.archivePath.toURI());
+        if (!resource.exists())
+        {
+            return null;
+        }
+
+        XmlConfiguration xmlc = new XmlConfiguration(resource.getURL());
+        Map props = new HashMap();
+        props.put("Server",deploymgr.getServer());
+        if (deploymgr.getConfigurationManager() != null)
+        {
+            props.putAll(deploymgr.getConfigurationManager().getProperties());
+        }
+
+        xmlc.setProperties(props);
+        return (ContextHandler)xmlc.configure();
+    }
+
+    public boolean isExtractWars()
+    {
+        return extractWars;
+    }
+
+    public void setExtractWars(boolean extractWars)
+    {
+        this.extractWars = extractWars;
+    }
+
+    public boolean isParentLoaderPriority()
+    {
+        return parentLoaderPriority;
+    }
+
+    public void setParentLoaderPriority(boolean parentLoaderPriority)
+    {
+        this.parentLoaderPriority = parentLoaderPriority;
+    }
+
+    public String getDefaultsDescriptor()
+    {
+        return defaultsDescriptor;
+    }
+
+    public void setDefaultsDescriptor(String defaultsDescriptor)
+    {
+        this.defaultsDescriptor = defaultsDescriptor;
+    }
 
     /**
-     * Get the work dir for this App, as managed by the {@link DeploymentManager}
+     * The unique id of the {@link App} relating to how it is installed on the jetty server side.
      * 
-     * @return the App's work dir.
+     * @return the generated Id for the App.
      */
-    File getWorkDir();
-
-    /**
-     * Get the archive path to the App.
-     * 
-     * Might exist as a Directory (example: an exploded webapp, or a jetty:run) or a File (example: a WAR file)
-     * 
-     * @return the
-     */
-    File getDir();
+    public String getContextId()
+    {
+        if (this.handler == null)
+        {
+            return null;
+        }
+        return this.handler.getContextPath();
+    }
 
     /**
      * The origin of this {@link App} as specified by the {@link AppProvider}
      * 
      * @return String representing the origin of this app.
      */
-    String getOrigin();
+    public String getOriginId()
+    {
+        return this.originId;
+    }
 }
diff --git a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/AppLifecycle.java b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/AppLifecycle.java
index bf28998..fd81160 100644
--- a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/AppLifecycle.java
+++ b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/AppLifecycle.java
@@ -262,7 +262,7 @@
 
         for (BoundMethod binding : bindings)
         {
-            Log.debug("Calling " + binding.obj.getClass().getName() + "#" + binding.method.getName());
+            Log.info("Calling " + binding.obj.getClass().getName() + "#" + binding.method.getName());
             binding.method.invoke(binding.obj,params);
         }
     }
diff --git a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java
index 8b6fa1a..0912787 100644
--- a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java
+++ b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java
@@ -18,8 +18,12 @@
 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;
@@ -76,11 +80,6 @@
             return app;
         }
 
-        public int getVersion()
-        {
-            return version;
-        }
-
         public Node getLifecyleNode()
         {
             return lifecyleNode;
@@ -91,6 +90,11 @@
             return stateTimestamps;
         }
 
+        public int getVersion()
+        {
+            return version;
+        }
+
         void setLifecycleNode(Node node)
         {
             this.lifecyleNode = node;
@@ -98,12 +102,13 @@
         }
     }
 
-    private Map<String, AppEntry> appmap = new HashMap<String, AppEntry>();
     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 AppLifecycle lifecycle = new AppLifecycle();
+    private String defaultLifeCycleState = "started";
 
     /**
      * Receive an app for processing.
@@ -112,28 +117,17 @@
      */
     public void addApp(App app)
     {
-        AppEntry entry = appmap.get(app.getId());
-        if (entry == null)
-        {
-            // New Entry
-            entry = new AppEntry();
-            entry.version = 0;
-            entry.setLifecycleNode(lifecycle.getNodeByName("undeployed"));
-        }
-        else
-        {
-            // Existing Entry
-            entry.version++;
-
-            // TODO: determine state change
-            // TODO: determine redeploy need?
-        }
-
+        Log.info("App Added: " + app.getOriginId());
+        AppEntry entry = new AppEntry();
         entry.app = app;
-        appmap.put(app.getId(),entry);
+        entry.setLifecycleNode(lifecycle.getNodeByName("undeployed"));
+        apps.add(entry);
 
-        // TODO: Should immediately attempt to start?
-        // this.requestAppGoal(app.getId(),AppState.STARTED);
+        if (defaultLifeCycleState != null)
+        {
+            // Immediately attempt to go to default lifecycle state
+            this.requestAppGoal(entry,defaultLifeCycleState);
+        }
     }
 
     public void addAppProvider(AppProvider provider)
@@ -152,6 +146,11 @@
         }
     }
 
+    public void addLifecycleBinding(Object binding)
+    {
+        lifecycle.addBinding(binding);
+    }
+
     @Override
     protected void doStart() throws Exception
     {
@@ -191,9 +190,53 @@
         super.doStop();
     }
 
-    public App getApp(String appId)
+    private AppEntry findAppByContextId(String contextId)
     {
-        AppEntry entry = appmap.get(appId);
+        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;
@@ -203,12 +246,7 @@
 
     public Collection<AppEntry> getAppEntries()
     {
-        return appmap.values();
-    }
-
-    public AppEntry getAppEntry(String appId)
-    {
-        return appmap.get(appId);
+        return apps;
     }
 
     public Collection<AppProvider> getAppProviders()
@@ -218,12 +256,12 @@
 
     public Collection<App> getApps()
     {
-        List<App> apps = new ArrayList<App>();
-        for (AppEntry entry : appmap.values())
+        List<App> ret = new ArrayList<App>();
+        for (AppEntry entry : apps)
         {
-            apps.add(entry.app);
+            ret.add(entry.app);
         }
-        return apps;
+        return ret;
     }
 
     /**
@@ -235,15 +273,47 @@
      */
     public Collection<App> getApps(Node node)
     {
-        List<App> apps = new ArrayList<App>();
-        for (AppEntry entry : appmap.values())
+        List<App> ret = new ArrayList<App>();
+        for (AppEntry entry : apps)
         {
             if (entry.lifecyleNode == node)
             {
-                apps.add(entry.app);
+                ret.add(entry.app);
             }
         }
-        return apps;
+        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;
     }
 
     /**
@@ -272,6 +342,16 @@
         return contexts;
     }
 
+    public String getDefaultLifeCycleState()
+    {
+        return defaultLifeCycleState;
+    }
+
+    public AppLifecycle getLifecycle()
+    {
+        return lifecycle;
+    }
+
     public Server getServer()
     {
         if (contexts == null)
@@ -281,6 +361,26 @@
         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);
@@ -308,15 +408,36 @@
      * 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 app
+     *            the app to move through the process
      * @param nodeName
      *            the name of the node to attain
      */
-    public void requestAppGoal(String appId, String nodeName)
+    public void requestAppGoal(App app, String nodeName)
     {
-        AppEntry appentry = getAppEntry(appId);
+        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);
@@ -329,10 +450,19 @@
         // Execute each Node binding.  Stopping at any thrown exception.
         try
         {
-            for (Node node : path)
+            Iterator<Node> it = path.iterator();
+            if (it.hasNext()) // Any entries?
             {
-                Log.info("Executing Node: " + node);
-                lifecycle.runBindings(node,appentry.app,this);
+                // 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)
@@ -342,6 +472,29 @@
     }
 
     /**
+     * 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
@@ -367,13 +520,19 @@
         this.contexts = contexts;
     }
 
-    public void addLifecycleBinding(Object binding)
+    public void setDefaultLifeCycleState(String defaultLifeCycleState)
     {
-        lifecycle.addBinding(binding);
+        this.defaultLifeCycleState = defaultLifeCycleState;
     }
 
-    public AppLifecycle getLifecycle()
+    public void undeployAll()
     {
-        return lifecycle;
+        Log.info("Undeploy (All) started");
+        for (AppEntry appentry : apps)
+        {
+            Log.info("Undeploy: " + appentry);
+            requestAppGoal(appentry,"undeployed");
+        }
+        Log.info("Undeploy (All) completed");
     }
 }
diff --git a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/DefaultUndeployer.java b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/DefaultUndeployer.java
deleted file mode 100644
index 25ab5d8..0000000
--- a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/DefaultUndeployer.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// ========================================================================
-// 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.bindings;
-
-import org.eclipse.jetty.deploy.App;
-import org.eclipse.jetty.deploy.DeploymentManager;
-import org.eclipse.jetty.deploy.annotations.DeployLifecycleBinding;
-import org.eclipse.jetty.deploy.graph.Node;
-import org.eclipse.jetty.server.handler.ContextHandler;
-
-public class DefaultUndeployer
-{
-    @DeployLifecycleBinding(nodes = "undeploying")
-    public void handleAppUndeploy(DeploymentManager deployMgr, Node lifecycleNode, App app) throws Exception
-    {
-        ContextHandler handler = app.getContextHandler(deployMgr);
-        deployMgr.getContexts().removeHandler(handler);
-    }
-}
diff --git a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/DefaultDeployer.java b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/StandardDeployer.java
similarity index 89%
rename from jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/DefaultDeployer.java
rename to jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/StandardDeployer.java
index baed599..f790250 100644
--- a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/DefaultDeployer.java
+++ b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/StandardDeployer.java
@@ -21,10 +21,10 @@
 import org.eclipse.jetty.deploy.graph.Node;
 import org.eclipse.jetty.server.handler.ContextHandler;
 
-public class DefaultDeployer
+public class StandardDeployer
 {
     @DeployLifecycleBinding(nodes = "deploying")
-    public void handleAppDeploy(DeploymentManager deployMgr, Node lifecycleNode, App app) throws Exception
+    public void handleAppDeploy(Node lifecycleNode, App app, DeploymentManager deployMgr) throws Exception
     {
         ContextHandler handler = app.getContextHandler(deployMgr);
         deployMgr.getContexts().addHandler(handler);
diff --git a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/DefaultStarter.java b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStarter.java
similarity index 74%
rename from jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/DefaultStarter.java
rename to jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStarter.java
index 3e367cc..2ec33f6 100644
--- a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/DefaultStarter.java
+++ b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStarter.java
@@ -20,16 +20,24 @@
 import org.eclipse.jetty.deploy.annotations.DeployLifecycleBinding;
 import org.eclipse.jetty.deploy.graph.Node;
 import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.log.Log;
 
-public class DefaultStarter
+public class StandardStarter
 {
     @DeployLifecycleBinding(nodes = "starting")
-    public void handleAppStart(DeploymentManager deployMgr, Node lifecycleNode, App app) throws Exception
+    public void handleAppStart(Node lifecycleNode, App app, DeploymentManager deployMgr) throws Exception
     {
         ContextHandler handler = app.getContextHandler(deployMgr);
         if (!handler.isStarted())
         {
             handler.start();
         }
+
+        // Remove other apps at same context
+        for (App other : deployMgr.getAppsWithSameContext(app))
+        {
+            Log.info("Removing apps with same context: " + other);
+            deployMgr.requestAppGoal(other,"undeployed");
+        }
     }
 }
diff --git a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/DefaultStopper.java b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStopper.java
similarity index 90%
rename from jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/DefaultStopper.java
rename to jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStopper.java
index 53470ba..6568b86 100644
--- a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/DefaultStopper.java
+++ b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStopper.java
@@ -21,10 +21,10 @@
 import org.eclipse.jetty.deploy.graph.Node;
 import org.eclipse.jetty.server.handler.ContextHandler;
 
-public class DefaultStopper
+public class StandardStopper
 {
     @DeployLifecycleBinding(nodes = "stopping")
-    public void handleAppStop(DeploymentManager deployMgr, Node lifecycleNode, App app) throws Exception
+    public void handleAppStop(Node lifecycleNode, App app, DeploymentManager deployMgr) throws Exception
     {
         ContextHandler handler = app.getContextHandler(deployMgr);
         if (!handler.isStopped())
diff --git a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java
new file mode 100644
index 0000000..25ae8d7
--- /dev/null
+++ b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java
@@ -0,0 +1,62 @@
+// ========================================================================
+// 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.bindings;
+
+import org.eclipse.jetty.deploy.App;
+import org.eclipse.jetty.deploy.DeploymentManager;
+import org.eclipse.jetty.deploy.annotations.DeployLifecycleBinding;
+import org.eclipse.jetty.deploy.graph.Node;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.eclipse.jetty.server.handler.HandlerCollection;
+import org.eclipse.jetty.util.log.Log;
+
+public class StandardUndeployer
+{
+    @DeployLifecycleBinding(nodes = "undeploying")
+    public void handleAppUndeploy(Node lifecycleNode, App app, DeploymentManager deployMgr) throws Exception
+    {
+        ContextHandler handler = app.getContextHandler(deployMgr);
+        ContextHandlerCollection chcoll = deployMgr.getContexts();
+
+        recursiveRemoveContext(chcoll,handler);
+
+        deployMgr.removeApp(app);
+    }
+
+    private void recursiveRemoveContext(HandlerCollection coll, ContextHandler context)
+    {
+        Handler children[] = coll.getHandlers();
+        int originalCount = children.length;
+
+        for (int i = 0, n = children.length; i < n; i++)
+        {
+            Handler child = children[i];
+            Log.info("Child handler: " + child);
+            if (child.equals(context))
+            {
+                Log.info("Removing handler: " + child);
+                coll.removeHandler(child);
+                Log.info(String.format("After removal: %d (originally %d)",coll.getHandlers().length,originalCount));
+            }
+            else if (child instanceof HandlerCollection)
+            {
+                recursiveRemoveContext((HandlerCollection)child,context);
+            }
+        }
+    }
+}
diff --git a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/providers/ConfiguredContextApp.java b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/providers/ConfiguredContextApp.java
deleted file mode 100644
index 8ffb50d..0000000
--- a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/providers/ConfiguredContextApp.java
+++ /dev/null
@@ -1,94 +0,0 @@
-// ========================================================================
-// 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.providers;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.eclipse.jetty.deploy.App;
-import org.eclipse.jetty.deploy.DeploymentManager;
-import org.eclipse.jetty.server.handler.ContextHandler;
-import org.eclipse.jetty.util.AttributesMap;
-import org.eclipse.jetty.util.resource.Resource;
-import org.eclipse.jetty.xml.XmlConfiguration;
-
-public class ConfiguredContextApp implements App
-{
-    private String filename;
-    private ContextHandler handler;
-
-    public ConfiguredContextApp(String filename)
-    {
-        this.filename = filename;
-    }
-
-    public File getDir()
-    {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @SuppressWarnings("unchecked")
-    public ContextHandler getContextHandler(DeploymentManager deploymgr) throws Exception
-    {
-        if (handler == null)
-        {
-            Resource resource = Resource.newResource(filename);
-            if (!resource.exists())
-            {
-                return null;
-            }
-
-            XmlConfiguration xmlc = new XmlConfiguration(resource.getURL());
-            Map props = new HashMap();
-            props.put("Server",deploymgr.getServer());
-            if (deploymgr.getConfigurationManager() != null)
-            {
-                props.putAll(deploymgr.getConfigurationManager().getProperties());
-            }
-
-            xmlc.setProperties(props);
-            this.handler = (ContextHandler)xmlc.configure();
-            this.handler.setAttributes(new AttributesMap(deploymgr.getContextAttributes()));
-        }
-        return handler;
-    }
-
-    public String getId()
-    {
-        return null;
-    }
-
-    public String getName()
-    {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    public String getOrigin()
-    {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    public File getWorkDir()
-    {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-}
diff --git a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/providers/ContextsDirAppProvider.java b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/providers/ContextsDirAppProvider.java
deleted file mode 100644
index b5acb0e..0000000
--- a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/providers/ContextsDirAppProvider.java
+++ /dev/null
@@ -1,152 +0,0 @@
-// ========================================================================
-// 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.providers;
-
-import java.io.File;
-import java.io.FilenameFilter;
-import java.util.Collections;
-import org.eclipse.jetty.deploy.App;
-import org.eclipse.jetty.deploy.AppProvider;
-import org.eclipse.jetty.deploy.DeploymentManager;
-import org.eclipse.jetty.util.Scanner;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.resource.Resource;
-
-/**
- * Backwards Compatible AppProvider for Monitoring a Contexts directory and deploying All Contexts.
- * 
- * Similar in scope to the original org.eclipse.jetty.deploy.ContextDeployer
- */
-public class ContextsDirAppProvider implements AppProvider, Scanner.DiscreteListener
-{
-    private Resource contextsDir;
-    private Scanner scanner;
-    private int scanInterval = 10;
-    private boolean recursive = false;
-    private DeploymentManager deploymgr;
-
-    class XmlFilenameFilter implements FilenameFilter
-    {
-        public boolean accept(File dir, String name)
-        {
-            if (name.endsWith(".xml"))
-            {
-                return true;
-            }
-
-            return false;
-        }
-    }
-
-    public ContextsDirAppProvider()
-    {
-        scanner = new Scanner();
-    }
-
-    public void startProvider(DeploymentManager deploymentManager) throws Exception
-    {
-        Log.info(this.getClass().getSimpleName() + ".startProvider(" + deploymentManager + ")");
-        if (contextsDir == null)
-        {
-            throw new IllegalStateException("No configuration dir specified");
-        }
-
-        this.deploymgr = deploymentManager;
-
-        File scandir = contextsDir.getFile();
-        Log.info("ScanDir: " + scandir);
-        this.scanner.setScanDirs(Collections.singletonList(scandir));
-        this.scanner.setScanInterval(scanInterval);
-        this.scanner.setRecursive(recursive);
-        this.scanner.setFilenameFilter(new XmlFilenameFilter());
-        this.scanner.addListener(this);
-        this.scanner.scan();
-        this.scanner.start();
-    }
-
-    public Resource getContextsDir()
-    {
-        return contextsDir;
-    }
-
-    public void setContextsDir(Resource contextsDir)
-    {
-        this.contextsDir = contextsDir;
-    }
-
-    /**
-     * @param dir
-     *            Directory to scan for context descriptors
-     */
-    public void setContextsDir(String dir)
-    {
-        try
-        {
-            contextsDir = Resource.newResource(dir);
-        }
-        catch (Exception e)
-        {
-            throw new IllegalArgumentException(e);
-        }
-    }
-
-    public int getScanInterval()
-    {
-        return scanInterval;
-    }
-
-    public void setScanInterval(int scanInterval)
-    {
-        this.scanInterval = scanInterval;
-    }
-
-    public boolean isRecursive()
-    {
-        return recursive;
-    }
-
-    public void setRecursive(boolean recursive)
-    {
-        this.recursive = recursive;
-    }
-
-    public void stopProvider()
-    {
-        this.scanner.removeListener(this);
-        this.scanner.stop();
-    }
-
-    public void fileAdded(String filename) throws Exception
-    {
-        Log.info("fileAdded(" + filename + ")");
-        App app = new ConfiguredContextApp(filename);
-        this.deploymgr.addApp(app);
-    }
-
-    public void fileChanged(String filename) throws Exception
-    {
-        Log.info("fileChanged(" + filename + ")");
-        App app = new ConfiguredContextApp(filename);
-        this.deploymgr.addApp(app);
-    }
-
-    public void fileRemoved(String filename) throws Exception
-    {
-        Log.info("fileRemoved(" + filename + ")");
-        App app = new ConfiguredContextApp(filename);
-        this.deploymgr.addApp(app);
-    }
-}
diff --git a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/providers/MonitoredDirAppProvider.java b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/providers/MonitoredDirAppProvider.java
new file mode 100644
index 0000000..52fccd1
--- /dev/null
+++ b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/providers/MonitoredDirAppProvider.java
@@ -0,0 +1,218 @@
+// ========================================================================
+// 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.providers;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.Collections;
+
+import org.eclipse.jetty.deploy.App;
+import org.eclipse.jetty.deploy.AppProvider;
+import org.eclipse.jetty.deploy.DeploymentManager;
+import org.eclipse.jetty.util.Scanner;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.resource.Resource;
+
+/**
+ * Backwards Compatible AppProvider for Monitoring a Contexts directory and deploying All Contexts.
+ * 
+ * Similar in scope to the original org.eclipse.jetty.deploy.ContextDeployer
+ */
+public class MonitoredDirAppProvider implements AppProvider, Scanner.DiscreteListener
+{
+    class ExtensionFilenameFilter implements FilenameFilter
+    {
+        boolean acceptXml = true;
+        boolean acceptWar = true;
+
+        public boolean accept(File dir, String name)
+        {
+            String lowername = name.toLowerCase();
+
+            if (acceptXml && (lowername.endsWith(".xml")))
+            {
+                return true;
+            }
+
+            if (acceptWar && (lowername.endsWith(".war")))
+            {
+                return true;
+            }
+
+            return false;
+        }
+    }
+
+    private Resource monitoredDir;
+    private Scanner scanner;
+    private int scanInterval = 10;
+    private boolean recursive = false;
+    private boolean extractWars = false;
+    private boolean parentLoaderPriority = false;
+    private String defaultsDescriptor;
+    private DeploymentManager deploymgr;
+    private ExtensionFilenameFilter filenamefilter;
+
+    public MonitoredDirAppProvider()
+    {
+        scanner = new Scanner();
+        filenamefilter = new ExtensionFilenameFilter();
+    }
+
+    private void addConfiguredContextApp(String filename)
+    {
+        String originId = filename;
+        App app = new App(originId,new File(filename));
+        app.setExtractWars(this.extractWars);
+        app.setParentLoaderPriority(this.parentLoaderPriority);
+        app.setDefaultsDescriptor(this.defaultsDescriptor);
+        this.deploymgr.addApp(app);
+    }
+
+    public void fileAdded(String filename) throws Exception
+    {
+        Log.info("fileAdded(" + filename + ")");
+        addConfiguredContextApp(filename);
+    }
+
+    public void fileChanged(String filename) throws Exception
+    {
+        Log.info("fileChanged(" + filename + ")");
+        addConfiguredContextApp(filename);
+    }
+
+    public void fileRemoved(String filename) throws Exception
+    {
+        // TODO: How to determine ID from filename that doesn't exist?
+        /*
+        Log.info("fileRemoved(" + filename + ")");
+        addConfiguredContextApp(filename);
+         */
+    }
+
+    public String getDefaultsDescriptor()
+    {
+        return defaultsDescriptor;
+    }
+
+    public Resource getMonitoredDir()
+    {
+        return monitoredDir;
+    }
+
+    public int getScanInterval()
+    {
+        return scanInterval;
+    }
+
+    public boolean isExtractWars()
+    {
+        return extractWars;
+    }
+
+    public boolean isParentLoaderPriority()
+    {
+        return parentLoaderPriority;
+    }
+
+    public boolean isRecursive()
+    {
+        return recursive;
+    }
+
+    public void setAcceptContextXmlFiles(boolean flag)
+    {
+        filenamefilter.acceptXml = flag;
+    }
+
+    public void setAcceptWarFiles(boolean flag)
+    {
+        filenamefilter.acceptWar = flag;
+    }
+
+    public void setDefaultsDescriptor(String defaultsDescriptor)
+    {
+        this.defaultsDescriptor = defaultsDescriptor;
+    }
+
+    public void setExtractWars(boolean extractWars)
+    {
+        this.extractWars = extractWars;
+    }
+
+    public void setMonitoredDir(Resource contextsDir)
+    {
+        this.monitoredDir = contextsDir;
+    }
+
+    /**
+     * @param dir
+     *            Directory to scan for context descriptors or war files
+     */
+    public void setMonitoredDir(String dir)
+    {
+        try
+        {
+            monitoredDir = Resource.newResource(dir);
+        }
+        catch (Exception e)
+        {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    public void setParentLoaderPriority(boolean parentLoaderPriority)
+    {
+        this.parentLoaderPriority = parentLoaderPriority;
+    }
+
+    public void setRecursive(boolean recursive)
+    {
+        this.recursive = recursive;
+    }
+
+    public void setScanInterval(int scanInterval)
+    {
+        this.scanInterval = scanInterval;
+    }
+
+    public void startProvider(DeploymentManager deploymentManager) throws Exception
+    {
+        Log.info(this.getClass().getSimpleName() + ".startProvider(" + deploymentManager + ")");
+        if (monitoredDir == null)
+        {
+            throw new IllegalStateException("No configuration dir specified");
+        }
+
+        this.deploymgr = deploymentManager;
+
+        File scandir = monitoredDir.getFile();
+        Log.info("ScanDir: " + scandir);
+        this.scanner.setScanDirs(Collections.singletonList(scandir));
+        this.scanner.setScanInterval(scanInterval);
+        this.scanner.setRecursive(recursive);
+        this.scanner.setFilenameFilter(filenamefilter);
+        this.scanner.addListener(this);
+        this.scanner.scan();
+        this.scanner.start();
+    }
+
+    public void stopProvider()
+    {
+        this.scanner.removeListener(this);
+        this.scanner.stop();
+    }
+}
diff --git a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/providers/WebappsDirAppProvider.java b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/providers/WebappsDirAppProvider.java
deleted file mode 100644
index 7e526df..0000000
--- a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/providers/WebappsDirAppProvider.java
+++ /dev/null
@@ -1,41 +0,0 @@
-// ========================================================================
-// 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.providers;
-
-import org.eclipse.jetty.deploy.AppProvider;
-import org.eclipse.jetty.deploy.DeploymentManager;
-
-/**
- * Backwards Compatible AppProvider for Monitoring a Webapps directory and deploying All apps.
- * 
- * Similar in scope to the original org.eclipse.jetty.deploy.WebAppDeployer
- */
-public class WebappsDirAppProvider implements AppProvider
-{
-
-    public void startProvider(DeploymentManager deploymentManager) throws Exception
-    {
-        // TODO Auto-generated method stub
-
-    }
-
-    public void stopProvider()
-    {
-        // TODO Auto-generated method stub
-
-    }
-
-}
diff --git a/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/util/FileID.java b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/util/FileID.java
new file mode 100644
index 0000000..634a14b
--- /dev/null
+++ b/jetty-deploy-manager-graph/src/main/java/org/eclipse/jetty/deploy/util/FileID.java
@@ -0,0 +1,76 @@
+// ========================================================================
+// 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.util;
+
+import java.io.File;
+
+/**
+ * Simple, yet surprisingly common utility methods for identifying various file types commonly seen and worked with in a
+ * deployment scenario.
+ */
+public class FileID
+{
+    /**
+     * Is the path a Web Archive?
+     * 
+     * @param path
+     *            the path to test.
+     * @return True if a .war or .jar or exploded web directory
+     * @see FileID#isWebArchiveFile(File)
+     */
+    public static boolean isWebArchive(File path)
+    {
+        if (path.isFile())
+        {
+            String name = path.getName().toLowerCase();
+            return (name.endsWith(".war") || name.endsWith(".jar"));
+        }
+
+        File webInf = new File(path,"WEB-INF");
+        File webXml = new File(webInf,"web.xml");
+        return webXml.exists() && webXml.isFile();
+    }
+
+    /**
+     * Is the path a Web Archive File (not directory)
+     * 
+     * @param path
+     *            the path to test.
+     * @return True if a .war or .jar file.
+     * @see FileID#isWebArchive(File)
+     */
+    public static boolean isWebArchiveFile(File path)
+    {
+        if (!path.isFile())
+        {
+            return false;
+        }
+
+        String name = path.getName().toLowerCase();
+        return (name.endsWith(".war") || name.endsWith(".jar"));
+    }
+
+    public static boolean isXmlFile(File path)
+    {
+        if (!path.isFile())
+        {
+            return false;
+        }
+
+        String name = path.getName().toLowerCase();
+        return name.endsWith(".xml");
+    }
+}
diff --git a/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/LifecycleOrderTracker.java b/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/AppLifecyclePathCollector.java
similarity index 81%
rename from jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/LifecycleOrderTracker.java
rename to jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/AppLifecyclePathCollector.java
index f8cbd65..ff82f34 100644
--- a/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/LifecycleOrderTracker.java
+++ b/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/AppLifecyclePathCollector.java
@@ -25,7 +25,7 @@
 /**
  * Binds to all lifecycle nodes, and tracks the order of the lifecycle nodes for testing purposes.
  */
-public class LifecycleOrderTracker
+public class AppLifecyclePathCollector
 {
     List<Node> actualOrder = new ArrayList<Node>();
 
@@ -40,32 +40,32 @@
     }
 
     @DeployLifecycleBinding(nodes = "*")
-    public void handleAppDeploy(Node node, App app, DeploymentManager depmgr)
+    public void processNode(Node node, App app, DeploymentManager depmgr)
     {
         actualOrder.add(node);
     }
 
-    public void assertExpected(String msg, List<Node> expectedOrder)
+    public void assertExpected(String msg, List<String> expectedOrder)
     {
         if (expectedOrder.size() != actualOrder.size())
         {
             System.out.println("/* Expected Path */");
-            for (Node path : expectedOrder)
+            for (String path : expectedOrder)
             {
                 System.out.println(path);
             }
             System.out.println("/* Actual Path */");
             for (Node path : actualOrder)
             {
-                System.out.println(path);
+                System.out.println(path.getName());
             }
 
-            Assert.assertEquals(msg + " / count",0,actualOrder.size());
+            Assert.assertEquals(msg + " / count",expectedOrder.size(),actualOrder.size());
         }
 
         for (int i = 0, n = expectedOrder.size(); i < n; i++)
         {
-            Assert.assertEquals(msg + "[" + i + "]",expectedOrder.get(i),actualOrder.get(i));
+            Assert.assertEquals(msg + "[" + i + "]",expectedOrder.get(i),actualOrder.get(i).getName());
         }
     }
 }
diff --git a/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifecyclePathTest.java b/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifecyclePathTest.java
index bebc413..3d5d9c5 100644
--- a/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifecyclePathTest.java
+++ b/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifecyclePathTest.java
@@ -17,50 +17,64 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import org.eclipse.jetty.deploy.graph.Node;
 import org.junit.Test;
 
 public class DeploymentManagerLifecyclePathTest
 {
     @Test
-    public void testStateTransition_NewToDeployed()
+    public void testStateTransition_NewToDeployed() throws Exception
     {
-        LifecycleOrderTracker pathtracker = new LifecycleOrderTracker();
         DeploymentManager depman = new DeploymentManager();
-        depman.addLifecycleBinding(pathtracker);
-        App app = new MockApp();
+        depman.setDefaultLifeCycleState(null); // no default
+        AppLifecyclePathCollector pathtracker = new AppLifecyclePathCollector();
+        MockAppProvider mockProvider = new MockAppProvider();
 
-        // Pretend to AppProvider ...
-        depman.addApp(app);
+        depman.addLifecycleBinding(pathtracker);
+        depman.addAppProvider(mockProvider);
+
+        // Start DepMan
+        depman.start();
+
+        // Trigger new App
+        mockProvider.findWebapp("foo-webapp-1.war");
+
+        App app = depman.getAppByOriginId("mock-foo-webapp-1.war");
 
         // Request Deploy of App
-        depman.requestAppGoal(app.getId(),"deployed");
+        depman.requestAppGoal(app,"deployed");
 
         // Setup Expectations.
-        List<Node> expectedOrder = new ArrayList<Node>();
-        expectedOrder.add(new Node("pre-deploying"));
-        expectedOrder.add(new Node("deploying"));
-        expectedOrder.add(new Node("deployed"));
+        List<String> expected = new ArrayList<String>();
+        // SHOULD NOT SEE THIS NODE VISITED - expected.add("undeployed");
+        expected.add("pre-deploying");
+        expected.add("deploying");
+        expected.add("deployed");
 
-        pathtracker.assertExpected("Test StateTransition / New -> Deployed",expectedOrder);
+        pathtracker.assertExpected("Test StateTransition / New -> Deployed",expected);
     }
 
     @Test
-    public void testStateTransition_Receive()
+    public void testStateTransition_Receive() throws Exception
     {
-        LifecycleOrderTracker pathtracker = new LifecycleOrderTracker();
         DeploymentManager depman = new DeploymentManager();
-        depman.addLifecycleBinding(pathtracker);
-        App app = new MockApp();
+        depman.setDefaultLifeCycleState(null); // no default
+        AppLifecyclePathCollector pathtracker = new AppLifecyclePathCollector();
+        MockAppProvider mockProvider = new MockAppProvider();
 
-        // Pretend to AppProvider
-        depman.addApp(app);
+        depman.addLifecycleBinding(pathtracker);
+        depman.addAppProvider(mockProvider);
+
+        // Start DepMan
+        depman.start();
+
+        // Trigger new App
+        mockProvider.findWebapp("foo-webapp-1.war");
 
         // Perform no goal request.
 
         // Setup Expectations.
-        List<Node> expectedOrder = new ArrayList<Node>();
+        List<String> expected = new ArrayList<String>();
 
-        pathtracker.assertExpected("Test StateTransition / New only",expectedOrder);
+        pathtracker.assertExpected("Test StateTransition / New only",expected);
     }
 }
diff --git a/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java b/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java
index d334fd2..eafa65c 100644
--- a/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java
+++ b/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java
@@ -10,15 +10,21 @@
 public class DeploymentManagerTest
 {
     @Test
-    public void testReceiveApp()
+    public void testReceiveApp() throws Exception
     {
-        LifecycleOrderTracker pathtracker = new LifecycleOrderTracker();
         DeploymentManager depman = new DeploymentManager();
-        depman.addLifecycleBinding(pathtracker);
-        App app = new MockApp();
+        depman.setDefaultLifeCycleState(null); // no default
+        AppLifecyclePathCollector pathtracker = new AppLifecyclePathCollector();
+        MockAppProvider mockProvider = new MockAppProvider();
 
-        // Pretend to be AppProvider ...
-        depman.addApp(app);
+        depman.addLifecycleBinding(pathtracker);
+        depman.addAppProvider(mockProvider);
+
+        // Start DepMan
+        depman.start();
+
+        // Trigger new App
+        mockProvider.findWebapp("foo-webapp-1.war");
 
         // Test app tracking
         Collection<App> apps = depman.getApps();
@@ -26,15 +32,15 @@
         Assert.assertEquals("Expected App Count",1,apps.size());
 
         // Test app get
-        App actual = depman.getApp("mock-app");
+        App actual = depman.getAppByOriginId("mock-foo-webapp-1.war");
         Assert.assertNotNull("Should have gotten app (by id)",actual);
-        Assert.assertEquals("Should have gotten app (by id)",app.getId(),actual.getId());
+        Assert.assertEquals("Should have gotten app (by id)","mock-foo-webapp-1.war",actual.getOriginId());
     }
 
     @Test
     public void testBinding()
     {
-        LifecycleOrderTracker pathtracker = new LifecycleOrderTracker();
+        AppLifecyclePathCollector pathtracker = new AppLifecyclePathCollector();
         DeploymentManager depman = new DeploymentManager();
         depman.addLifecycleBinding(pathtracker);
 
diff --git a/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/MockApp.java b/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/MockApp.java
deleted file mode 100644
index 123b66d..0000000
--- a/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/MockApp.java
+++ /dev/null
@@ -1,53 +0,0 @@
-// ========================================================================
-// 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.io.File;
-
-import org.eclipse.jetty.server.handler.ContextHandler;
-
-public class MockApp implements App
-{
-    public ContextHandler getContextHandler(DeploymentManager deployMgr)
-    {
-        return null;
-    }
-
-    public File getDir()
-    {
-        return null;
-    }
-
-    public String getId()
-    {
-        return "mock-app";
-    }
-
-    public String getName()
-    {
-        return "Mock App";
-    }
-
-    public String getOrigin()
-    {
-        return "Test Cases";
-    }
-
-    public File getWorkDir()
-    {
-        return null;
-    }
-}
diff --git a/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java b/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java
new file mode 100644
index 0000000..2e49846
--- /dev/null
+++ b/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java
@@ -0,0 +1,44 @@
+// ========================================================================
+// 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.io.File;
+
+import org.eclipse.jetty.deploy.test.MavenTestingUtils;
+
+public class MockAppProvider implements AppProvider
+{
+    private DeploymentManager deployMan;
+    private File webappsDir;
+
+    public void startProvider(DeploymentManager deploymentManager) throws Exception
+    {
+        this.deployMan = deploymentManager;
+        this.webappsDir = MavenTestingUtils.getTestResourceDir("webapps");
+    }
+
+    public void findWebapp(String name)
+    {
+        File war = new File(webappsDir,name);
+        App app = new App("mock-" + name,war);
+        this.deployMan.addApp(app);
+    }
+
+    public void stopProvider() throws Exception
+    {
+        /* ignore */
+    }
+}
diff --git a/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/providers/ContextsDirAppProviderTest.java b/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/providers/ContextsDirAppProviderTest.java
deleted file mode 100644
index 743edb9..0000000
--- a/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/providers/ContextsDirAppProviderTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-// ========================================================================
-// 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.providers;
-
-import org.eclipse.jetty.deploy.test.XmlConfiguredJetty;
-import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.handler.HandlerCollection;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-public class ContextsDirAppProviderTest
-{
-    private static XmlConfiguredJetty jetty;
-
-    @BeforeClass
-    public static void setupEnvironment() throws Exception
-    {
-        jetty = new XmlConfiguredJetty();
-        jetty.addConfiguration("jetty.xml");
-        jetty.addConfiguration("jetty-deploymgr-contexts.xml");
-
-        // Should not throw an Exception
-        jetty.load();
-
-        // Start it
-        jetty.start();
-    }
-
-    @AfterClass
-    public static void teardownEnvironment() throws Exception
-    {
-        // Stop jetty.
-        jetty.stop();
-    }
-
-    @Test
-    public void testFindContext()
-    {
-        // Check Server for Handlers
-        Server server = jetty.getServer();
-        Handler handler = server.getHandler();
-        System.out.println("handler: " + handler);
-
-        if (handler instanceof HandlerCollection)
-        {
-            System.out.printf("Handler Collection (child-handlers:%s - handlers:%d)%n",((HandlerCollection)handler).getChildHandlers().length,
-                    ((HandlerCollection)handler).getHandlers().length);
-            System.out.println("is a HandlerCollection: " + ((HandlerCollection)handler).dump());
-        }
-
-        // TODO: verify that handler was created for context.
-    }
-
-    // TODO: write test that adds new app (after startup).
-
-    // TODO: write test that adds new app (after startup), then removes it from filesystem (resulting in removed from handlers).
-
-    // TODO: write test that adds new app (after startup), then updates it (redeploy).
-
-}
diff --git a/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/providers/MonitoredDirAppProviderRuntimeUpdatesTest.java b/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/providers/MonitoredDirAppProviderRuntimeUpdatesTest.java
new file mode 100644
index 0000000..9b71bee
--- /dev/null
+++ b/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/providers/MonitoredDirAppProviderRuntimeUpdatesTest.java
@@ -0,0 +1,131 @@
+// ========================================================================
+// 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.providers;
+
+import java.io.IOException;
+
+import org.eclipse.jetty.deploy.DeploymentManager;
+import org.eclipse.jetty.deploy.test.XmlConfiguredJetty;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Similar in scope to {@link MonitoredDirAppProviderStartupTest}, except is concerned with the modification of existing
+ * deployed webapps due to incoming changes identified by the {@link MonitoredDirAppProvider}.
+ */
+public class MonitoredDirAppProviderRuntimeUpdatesTest
+{
+    private static XmlConfiguredJetty jetty;
+
+    @BeforeClass
+    public static void setupEnvironment() throws Exception
+    {
+        jetty = new XmlConfiguredJetty();
+        jetty.addConfiguration("jetty.xml");
+        jetty.addConfiguration("jetty-deploymgr-contexts.xml");
+
+        // Should not throw an Exception
+        jetty.load();
+
+        // Start it
+        jetty.start();
+    }
+
+    @AfterClass
+    public static void teardownEnvironment() throws Exception
+    {
+        // Stop jetty.
+        jetty.stop();
+    }
+
+    /**
+     * Cleanup / undeploy any handlers currently installed.
+     */
+    @Before
+    public void cleanupHandlers()
+    {
+        DeploymentManager depman = jetty.getActiveDeploymentManager();
+        depman.undeployAll();
+        jetty.assertNoWebAppContexts();
+    }
+
+    /**
+     * Simple webapp deployment after startup of server.
+     */
+    @Test
+    public void testAfterStartupContext() throws IOException
+    {
+        jetty.copyWebapp("foo-webapp-1.war","foo.war");
+        jetty.copyContext("foo.xml","foo.xml");
+
+        jetty.waitForDirectoryScan();
+
+        jetty.assertWebAppContextsExists("/foo");
+    }
+
+    /**
+     * Simple webapp deployment after startup of server, and then removal of the webapp.
+     */
+    @Test
+    @Ignore
+    public void testAfterStartupThenRemoveContext() throws IOException
+    {
+        jetty.copyWebapp("foo-webapp-1.war","foo.war");
+        jetty.copyContext("foo.xml","foo.xml");
+
+        jetty.waitForDirectoryScan();
+
+        jetty.assertWebAppContextsExists("/foo");
+
+        jetty.removeContext("foo.xml");
+
+        jetty.waitForDirectoryScan();
+
+        // FIXME: jetty.assertNoWebAppContexts();
+    }
+
+    /**
+     * Simple webapp deployment after startup of server, and then removal of the webapp.
+     */
+    @Test
+    public void testAfterStartupThenUpdateContext() throws IOException
+    {
+        jetty.copyWebapp("foo-webapp-1.war","foo.war");
+        jetty.copyContext("foo.xml","foo.xml");
+
+        jetty.waitForDirectoryScan();
+
+        jetty.assertWebAppContextsExists("/foo");
+
+        // Test that webapp response contains "-1"
+        jetty.assertResponseContains("/foo/info","FooServlet-1");
+
+        System.out.println("Updating war files");
+        jetty.copyContext("foo.xml","foo.xml"); // essentially "touch" the context xml
+        jetty.copyWebapp("foo-webapp-2.war","foo.war");
+
+        // This should result in the existing foo.war being replaced with the new foo.war
+        jetty.waitForDirectoryScan();
+        jetty.waitForDirectoryScan();
+        // FIXME: jetty.assertWebAppContextsExists("/foo");
+
+        // Test that webapp response contains "-2"
+        // FIXME: jetty.assertResponseContains("/foo/info","FooServlet-2");
+    }
+}
diff --git a/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/providers/MonitoredDirAppProviderStartupTest.java b/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/providers/MonitoredDirAppProviderStartupTest.java
new file mode 100644
index 0000000..aa2abfe
--- /dev/null
+++ b/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/providers/MonitoredDirAppProviderStartupTest.java
@@ -0,0 +1,62 @@
+// ========================================================================
+// 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.providers;
+
+import org.eclipse.jetty.deploy.test.XmlConfiguredJetty;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Tests {@link MonitoredDirAppProvider} as it starts up for the first time.
+ */
+public class MonitoredDirAppProviderStartupTest
+{
+    private static XmlConfiguredJetty jetty;
+
+    @BeforeClass
+    public static void setupEnvironment() throws Exception
+    {
+        jetty = new XmlConfiguredJetty();
+        jetty.addConfiguration("jetty.xml");
+        jetty.addConfiguration("jetty-deploymgr-contexts.xml");
+
+        // Setup initial context
+        jetty.copyContext("foo.xml","foo.xml");
+        jetty.copyWebapp("foo-webapp-1.war","foo.war");
+
+        // Should not throw an Exception
+        jetty.load();
+
+        // Start it
+        jetty.start();
+    }
+
+    @AfterClass
+    public static void teardownEnvironment() throws Exception
+    {
+        // Stop jetty.
+        jetty.stop();
+    }
+
+    @Test
+    public void testStartupContext()
+    {
+        // Check Server for Handlers
+        jetty.printHandlers(System.out);
+        jetty.assertWebAppContextsExists("/foo");
+    }
+}
diff --git a/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java b/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java
deleted file mode 100644
index 90fc9e4..0000000
--- a/jetty-deploy-manager-graph/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java
+++ /dev/null
@@ -1,215 +0,0 @@
-// ========================================================================
-// 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.test;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URL;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Properties;
-
-import org.eclipse.jetty.http.HttpSchemes;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.util.IO;
-import org.eclipse.jetty.xml.XmlConfiguration;
-import org.junit.Assert;
-
-/**
- * Allows for setting up a Jetty server for testing based on XML configuration files.
- */
-public class XmlConfiguredJetty
-{
-    private List<URL> xmlConfigurations;
-    private Properties properties = new Properties();
-    private Server server;
-    private int serverPort;
-    private String scheme = HttpSchemes.HTTP;
-
-    public XmlConfiguredJetty() throws IOException
-    {
-        String testname = new Throwable().getStackTrace()[1].getClassName();
-
-        xmlConfigurations = new ArrayList<URL>();
-        properties = new Properties();
-
-        File jettyHome = MavenTestingUtils.getTargetTestingDir(testname);
-        // Prepare Jetty.Home (Test) dir
-        jettyHome.mkdirs();
-
-        File logsDir = new File(jettyHome,"logs");
-        logsDir.mkdirs();
-
-        File etcDir = new File(jettyHome,"etc");
-        etcDir.mkdirs();
-        IO.copyFile(MavenTestingUtils.getTestResourceFile("etc/realm.properties"),new File(etcDir,"realm.properties"));
-
-        File webappsDir = new File(jettyHome,"webapps");
-        webappsDir.mkdirs();
-        File tmpDir = new File(MavenTestingUtils.getTargetTestingDir(testname),"tmp");
-        tmpDir.mkdirs();
-
-        // Setup properties
-        System.setProperty("java.io.tmpdir",tmpDir.getAbsolutePath());
-        properties.setProperty("jetty.home",jettyHome.getAbsolutePath());
-        System.setProperty("jetty.home",jettyHome.getAbsolutePath());
-        properties.setProperty("test.basedir",MavenTestingUtils.getBasedir().getAbsolutePath());
-        properties.setProperty("test.resourcesdir",MavenTestingUtils.getTestResourcesDir().getAbsolutePath());
-        properties.setProperty("test.webapps",MavenTestingUtils.getTestResourceDir("webapps").getAbsolutePath());
-        properties.setProperty("test.targetdir",MavenTestingUtils.getTargetDir().getAbsolutePath());
-
-        // Write out configuration for use by ConfigurationManager.
-        File testConfig = MavenTestingUtils.getTargetFile("xml-configured-jetty.properties");
-        FileOutputStream out = new FileOutputStream(testConfig);
-        properties.store(out,"Generated by " + XmlConfiguredJetty.class.getName());
-    }
-
-    public void addConfiguration(File xmlConfigFile) throws MalformedURLException
-    {
-        xmlConfigurations.add(xmlConfigFile.toURI().toURL());
-    }
-
-    public void addConfiguration(String testConfigName) throws MalformedURLException
-    {
-        addConfiguration(MavenTestingUtils.getTestResourceFile(testConfigName));
-    }
-
-    public void addConfiguration(URL xmlConfig)
-    {
-        xmlConfigurations.add(xmlConfig);
-    }
-
-    public String getScheme()
-    {
-        return scheme;
-    }
-
-    public Server getServer()
-    {
-        return server;
-    }
-
-    public int getServerPort()
-    {
-        return serverPort;
-    }
-
-    public URI getServerURI() throws UnknownHostException
-    {
-        StringBuffer uri = new StringBuffer();
-        uri.append(this.scheme).append("://");
-        uri.append(InetAddress.getLocalHost().getHostAddress());
-        uri.append(":").append(this.serverPort);
-        return URI.create(uri.toString());
-    }
-
-    @SuppressWarnings("unchecked")
-    public void load() throws Exception
-    {
-        XmlConfiguration last = null;
-        Object[] obj = new Object[this.xmlConfigurations.size()];
-
-        // Configure everything
-        for (int i = 0; i < this.xmlConfigurations.size(); i++)
-        {
-            URL configURL = this.xmlConfigurations.get(i);
-            XmlConfiguration configuration = new XmlConfiguration(configURL);
-            if (last != null)
-            {
-                configuration.getIdMap().putAll(last.getIdMap());
-            }
-            configuration.setProperties(properties);
-            obj[i] = configuration.configure();
-            last = configuration;
-        }
-
-        // Test for Server Instance.
-        Server foundServer = null;
-        int serverCount = 0;
-        for (int i = 0; i < this.xmlConfigurations.size(); i++)
-        {
-            if (obj[i] instanceof Server)
-            {
-                if (obj[i].equals(foundServer))
-                {
-                    // Identical server instance found
-                    break;
-                }
-                foundServer = (Server)obj[i];
-                serverCount++;
-            }
-        }
-
-        if (serverCount <= 0)
-        {
-            throw new Exception("Load failed to configure a " + Server.class.getName());
-        }
-
-        Assert.assertEquals("Server load count",1,serverCount);
-
-        this.server = foundServer;
-        this.server.setGracefulShutdown(10);
-
-    }
-
-    public void setProperty(String key, String value)
-    {
-        properties.setProperty(key,value);
-    }
-
-    public void setScheme(String scheme)
-    {
-        this.scheme = scheme;
-    }
-
-    public void start() throws Exception
-    {
-        Assert.assertNotNull("Server should not be null (failed load?)",server);
-
-        server.start();
-
-        // Find the active server port.
-        this.serverPort = (-1);
-        Connector connectors[] = server.getConnectors();
-        for (int i = 0; i < connectors.length; i++)
-        {
-            Connector connector = connectors[i];
-            if (connector.getLocalPort() > 0)
-            {
-                this.serverPort = connector.getLocalPort();
-                break;
-            }
-        }
-
-        Assert.assertTrue("Server Port is between 1 and 65535. Actually <" + serverPort + ">",(1 <= this.serverPort) && (this.serverPort <= 65535));
-
-        // Uncomment to have server start and continue to run (without exiting)
-        // System.out.printf("Listening to port %d%n",this.serverPort);
-        // server.join();
-    }
-
-    public void stop() throws Exception
-    {
-        server.stop();
-    }
-}
diff --git a/jetty-deploy-manager-graph/src/test/resources/contexts/logcommon.xml b/jetty-deploy-manager-graph/src/test/resources/contexts/foo.xml
similarity index 69%
rename from jetty-deploy-manager-graph/src/test/resources/contexts/logcommon.xml
rename to jetty-deploy-manager-graph/src/test/resources/contexts/foo.xml
index 6da9c12..cb3e7dd 100644
--- a/jetty-deploy-manager-graph/src/test/resources/contexts/logcommon.xml
+++ b/jetty-deploy-manager-graph/src/test/resources/contexts/foo.xml
@@ -1,8 +1,8 @@
 <?xml version="1.0"  encoding="ISO-8859-1"?>
 <!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
 <Configure class="org.eclipse.jetty.webapp.WebAppContext">
-    <Set name="contextPath">/logcommon</Set>
+    <Set name="contextPath">/foo</Set>
     <Set name="war">
-        <Property name="test.webapps" default="." />/logcommon.war
+        <Property name="test.webapps" default="." />/foo.war
     </Set>
 </Configure>
diff --git a/jetty-deploy-manager-graph/src/test/resources/jetty-deploymgr-contexts.xml b/jetty-deploy-manager-graph/src/test/resources/jetty-deploymgr-contexts.xml
index e917ca9..cd778ce 100644
--- a/jetty-deploy-manager-graph/src/test/resources/jetty-deploymgr-contexts.xml
+++ b/jetty-deploy-manager-graph/src/test/resources/jetty-deploymgr-contexts.xml
@@ -20,30 +20,30 @@
         <!-- Lifecycle Bindings -->
         <Call name="addLifecycleBinding">
           <Arg>
-            <New class="org.eclipse.jetty.deploy.bindings.DefaultDeployer" />
+            <New class="org.eclipse.jetty.deploy.bindings.StandardDeployer" />
           </Arg>
         </Call>
         <Call name="addLifecycleBinding">
           <Arg>
-            <New class="org.eclipse.jetty.deploy.bindings.DefaultUndeployer" />
+            <New class="org.eclipse.jetty.deploy.bindings.StandardUndeployer" />
           </Arg>
         </Call>
         <Call name="addLifecycleBinding">
           <Arg>
-            <New class="org.eclipse.jetty.deploy.bindings.DefaultStarter" />
+            <New class="org.eclipse.jetty.deploy.bindings.StandardStarter" />
           </Arg>
         </Call>
         <Call name="addLifecycleBinding">
           <Arg>
-            <New class="org.eclipse.jetty.deploy.bindings.DefaultStopper" />
+            <New class="org.eclipse.jetty.deploy.bindings.StandardStopper" />
           </Arg>
         </Call>
 
         <!-- Providers of Apps -->
         <Call name="addAppProvider">
           <Arg>
-            <New class="org.eclipse.jetty.deploy.providers.ContextsDirAppProvider">
-              <Set name="contextsDir">
+            <New class="org.eclipse.jetty.deploy.providers.MonitoredDirAppProvider">
+              <Set name="monitoredDir">
                 <Property name="test.resourcesdir" default="src/test/resources" />/contexts
               </Set>
               <Set name="scanInterval">0</Set>
diff --git a/jetty-deploy-manager-graph/src/test/resources/webapps/foo-webapp-1.war b/jetty-deploy-manager-graph/src/test/resources/webapps/foo-webapp-1.war
new file mode 100644
index 0000000..e4ed5be
--- /dev/null
+++ b/jetty-deploy-manager-graph/src/test/resources/webapps/foo-webapp-1.war
Binary files differ
diff --git a/jetty-deploy-manager-graph/src/test/resources/webapps/foo-webapp-2.war b/jetty-deploy-manager-graph/src/test/resources/webapps/foo-webapp-2.war
new file mode 100644
index 0000000..df0f023
--- /dev/null
+++ b/jetty-deploy-manager-graph/src/test/resources/webapps/foo-webapp-2.war
Binary files differ
diff --git a/jetty-deploy-manager-graph/src/test/resources/webapps/foo-webapp-3.war b/jetty-deploy-manager-graph/src/test/resources/webapps/foo-webapp-3.war
new file mode 100644
index 0000000..649d81b
--- /dev/null
+++ b/jetty-deploy-manager-graph/src/test/resources/webapps/foo-webapp-3.war
Binary files differ