diff --git a/VERSION.txt b/VERSION.txt
index 3bfbdbab33b..4df8f5c87d8 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -27,6 +27,7 @@ jetty-7.4.0.RC0
+ 341692 Fixed deadlock if stopped while starting
+ 341736 Split jetty-nested out of war module
+ 341726 JSONPojoConverter handles characters
+ + 341992 Overlayed context deployer
+ JETTY-1245 Pooled Buffers implementation
+ JETTY-1354 Added jetty-nested
+ Ensure generated fragment names are unique
diff --git a/jetty-overlay-deployer/logs/jtrac.log b/jetty-overlay-deployer/logs/jtrac.log
new file mode 100644
index 00000000000..8c4c974e455
--- /dev/null
+++ b/jetty-overlay-deployer/logs/jtrac.log
@@ -0,0 +1,24 @@
+2010-11-17 17:49:57,785 [main] INFO [info.jtrac.config.JtracConfigurer] - found 'jtrac-init.properties' on classpath, processing...
+2010-11-17 17:49:57,785 [main] INFO [info.jtrac.config.JtracConfigurer] - 'jtrac.home' property initialized from 'jtrac-init.properties' as '/tmp/jtrac-red'
+2010-11-17 17:49:57,785 [main] INFO [info.jtrac.config.JtracConfigurer] - locales available configured are 'en'
+2010-11-17 17:49:57,785 [main] INFO [info.jtrac.config.JtracConfigurer] - directory already exists: '/tmp/jtrac-red'
+2010-11-17 17:49:57,785 [main] INFO [info.jtrac.config.JtracConfigurer] - directory already exists: '/tmp/jtrac-red/attachments'
+2010-11-17 17:49:57,785 [main] INFO [info.jtrac.config.JtracConfigurer] - directory already exists: '/tmp/jtrac-red/indexes'
+2010-11-17 17:49:57,785 [main] INFO [info.jtrac.config.JtracConfigurer] - 'jtrac.properties' file exists: '/tmp/jtrac-red/jtrac.properties'
+2010-11-17 17:49:57,786 [main] INFO [info.jtrac.config.JtracConfigurer] - found 'jtrac-version.properties' on classpath, processing...
+2010-11-17 17:49:57,786 [main] INFO [info.jtrac.config.JtracConfigurer] - jtrac.version = '2.1.0'
+2010-11-17 17:49:57,786 [main] INFO [info.jtrac.config.JtracConfigurer] - jtrac.timestamp = '200803022120'
+2010-11-17 17:49:57,786 [main] INFO [info.jtrac.config.JtracConfigurer] - Loading properties file from file [/tmp/jtrac-red/jtrac.properties]
+2010-11-17 17:49:57,804 [main] INFO [info.jtrac.config.DataSourceFactoryBean] - embedded HSQLDB mode detected, switching on spring single connection data source
+2010-11-17 17:49:57,950 [main] INFO [info.jtrac.hibernate.HibernateJtracDao] - database schema exists, normal startup
+2010-11-17 17:49:57,952 [main] INFO [info.jtrac.JtracImpl] - available locales configured {en=en - English}
+2010-11-17 17:49:57,954 [main] WARN [info.jtrac.mail.MailSender] - 'mail.server.host' config is null, mail sender not initialized
+2010-11-17 17:49:57,954 [main] WARN [info.jtrac.JtracImpl] - invalid default locale configured = 'null', using en
+2010-11-17 17:49:57,954 [main] INFO [info.jtrac.JtracImpl] - default locale set to 'en'
+2010-11-17 17:49:57,954 [main] WARN [info.jtrac.JtracImpl] - invalid attachment max size 'null', using 5
+2010-11-17 17:49:57,954 [main] INFO [info.jtrac.JtracImpl] - attachment max size set to 5 MB
+2010-11-17 17:49:57,954 [main] WARN [info.jtrac.JtracImpl] - invalid session timeout 'null', using 30
+2010-11-17 17:49:57,954 [main] INFO [info.jtrac.JtracImpl] - session timeout set to 30 minutes
+2010-11-17 17:49:57,975 [main] INFO [info.jtrac.wicket.JtracApplication] - casProxyTicketValidator not found in application context, CAS single-sign-on is not being used
+2010-11-17 17:53:44,141 [Scanner-0] INFO [info.jtrac.config.DataSourceFactoryBean] - attempting to shut down embedded HSQLDB database
+2010-11-17 17:53:44,253 [Scanner-0] INFO [info.jtrac.config.DataSourceFactoryBean] - embedded HSQLDB database shut down successfully
diff --git a/jetty-overlay-deployer/pom.xml b/jetty-overlay-deployer/pom.xml
new file mode 100644
index 00000000000..43bc76f14f1
--- /dev/null
+++ b/jetty-overlay-deployer/pom.xml
@@ -0,0 +1,65 @@
+
+
+ org.eclipse.jetty
+ jetty-project
+ 7.4.0-SNAPSHOT
+
+ 4.0.0
+ jetty-overlay-deployer
+ Jetty :: Overlay Deployer
+ Overlayed deployer
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ package
+
+ single
+
+
+
+ ${basedir}/src/main/assembly/config.xml
+
+
+
+
+
+
+
+
+
+ org.eclipse.jetty
+ jetty-deploy
+ ${project.version}
+
+
+ org.eclipse.jetty
+ jetty-server
+ ${project.version}
+
+
+ org.eclipse.jetty
+ jetty-plus
+ ${project.version}
+
+
+ javax.transaction
+ jta
+ 1.1
+ test
+
+
+ junit
+ junit
+ test
+
+
+
+
+
diff --git a/jetty-overlay-deployer/src/main/assembly/config.xml b/jetty-overlay-deployer/src/main/assembly/config.xml
new file mode 100644
index 00000000000..b9b398e630e
--- /dev/null
+++ b/jetty-overlay-deployer/src/main/assembly/config.xml
@@ -0,0 +1,18 @@
+
+
+ config
+ false
+
+ jar
+
+
+
+ src/main/config
+
+
+ etc/**
+ overlays/**
+
+
+
+
diff --git a/jetty-overlay-deployer/src/main/config/etc/jetty-cloudtide-deployer.xml b/jetty-overlay-deployer/src/main/config/etc/jetty-cloudtide-deployer.xml
new file mode 100644
index 00000000000..135548dd026
--- /dev/null
+++ b/jetty-overlay-deployer/src/main/config/etc/jetty-cloudtide-deployer.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ [
+
+
+
+ /cloudtide
+
+ 1
+
+
+
+ ]
+
diff --git a/jetty-overlay-deployer/src/main/config/overlays/instances/README.TXT b/jetty-overlay-deployer/src/main/config/overlays/instances/README.TXT
new file mode 100644
index 00000000000..013fe90eeb6
--- /dev/null
+++ b/jetty-overlay-deployer/src/main/config/overlays/instances/README.TXT
@@ -0,0 +1,23 @@
+
+
+Cloudtide instance
+===================
+
+This directory contains overlays (as directories or jars) that
+are define instances of templates to be deployed.
+
+An instance filename consists of a template name, the '=' character and
+then an instance identifier. For example:
+
+ mytemplate=redInstance
+
+defines a instance called redInstance using the template called mytemplate.
+
+An overlay may contain:
+ * A context.xml file, which is an XmlConfiguration format file to
+ inject the ContextHandler instance.
+ * A web.xml fragment that is applied as an override descriptor
+ * A cache.xml file which is an XmlConfiguration form file to inject
+ the ResourceCache
+ * A webapp directory that contains static content that overlays the
+ static content of the context.
diff --git a/jetty-overlay-deployer/src/main/config/overlays/nodes/README.TXT b/jetty-overlay-deployer/src/main/config/overlays/nodes/README.TXT
new file mode 100644
index 00000000000..188d8e12242
--- /dev/null
+++ b/jetty-overlay-deployer/src/main/config/overlays/nodes/README.TXT
@@ -0,0 +1,16 @@
+
+
+Cloudtide nodes
+===============
+
+This directory contains overlays (as directories or jars) that
+are applied to all instances for a given node.
+
+An overlay may contain:
+ * A context.xml file, which is an XmlConfiguration format file to
+ inject the ContextHandler instance.
+ * A web.xml fragment that is applied as an override descriptor
+ * A cache.xml file which is an XmlConfiguration form file to inject
+ the ResourceCache
+ * A webapp directory that contains static content that overlays the
+ static content of the context.
diff --git a/jetty-overlay-deployer/src/main/config/overlays/templates/README.TXT b/jetty-overlay-deployer/src/main/config/overlays/templates/README.TXT
new file mode 100644
index 00000000000..ea554204c4a
--- /dev/null
+++ b/jetty-overlay-deployer/src/main/config/overlays/templates/README.TXT
@@ -0,0 +1,24 @@
+
+Cloudtide templates
+===================
+
+This directory contains overlays (as directories or jars) that
+are applied to webapps to customize them. Templates are not directly
+deployed, but are used by overlays in the instances directory.
+
+A Template filename consists of a template name, the '=' character and
+then the webapp name. For example:
+
+ mytemplate=mywebapp-1.2.3
+
+defines a template called mytemplate that overlays either the mywebapp-1.2.3.war
+file or the mywebapp-1.2.3 directory in cloudtide/webapps.
+
+An overlay may contain:
+ * A context.xml file, which is an XmlConfiguration format file to
+ inject the ContextHandler instance.
+ * A web.xml fragment that is applied as an override descriptor
+ * A cache.xml file which is an XmlConfiguration form file to inject
+ the ResourceCache
+ * A webapp directory that contains static content that overlays the
+ static content of the context.
diff --git a/jetty-overlay-deployer/src/main/config/overlays/tmp/README.TXT b/jetty-overlay-deployer/src/main/config/overlays/tmp/README.TXT
new file mode 100644
index 00000000000..675410eb689
--- /dev/null
+++ b/jetty-overlay-deployer/src/main/config/overlays/tmp/README.TXT
@@ -0,0 +1 @@
+This directory is for temporary cloudtide files
diff --git a/jetty-overlay-deployer/src/main/config/overlays/webapps/README.TXT b/jetty-overlay-deployer/src/main/config/overlays/webapps/README.TXT
new file mode 100644
index 00000000000..f709dce94d7
--- /dev/null
+++ b/jetty-overlay-deployer/src/main/config/overlays/webapps/README.TXT
@@ -0,0 +1,6 @@
+
+Cloudtide webapplication
+------------------------
+
+This directory contains webapplication directories or war files that are used by the
+cloudtide templates
diff --git a/jetty-overlay-deployer/src/main/java/org/eclipse/jetty/overlays/OverlayedAppProvider.java b/jetty-overlay-deployer/src/main/java/org/eclipse/jetty/overlays/OverlayedAppProvider.java
new file mode 100644
index 00000000000..5bac82655ab
--- /dev/null
+++ b/jetty-overlay-deployer/src/main/java/org/eclipse/jetty/overlays/OverlayedAppProvider.java
@@ -0,0 +1,1335 @@
+// ========================================================================
+// Copyright (c) 2010-2011 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// 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.opensource.org/licenses/apache2.0.php
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+
+package org.eclipse.jetty.overlays;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.regex.Pattern;
+
+import org.eclipse.jetty.deploy.App;
+import org.eclipse.jetty.deploy.AppProvider;
+import org.eclipse.jetty.deploy.ConfigurationManager;
+import org.eclipse.jetty.deploy.DeploymentManager;
+import org.eclipse.jetty.jndi.java.javaRootURLContext;
+import org.eclipse.jetty.jndi.local.localContextRoot;
+import org.eclipse.jetty.server.ResourceCache;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.Scanner;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.JarResource;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.util.resource.ResourceCollection;
+import org.eclipse.jetty.webapp.WebAppClassLoader;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.xml.XmlConfiguration;
+import org.xml.sax.SAXException;
+
+/**
+ * Overlayed AppProvider
+ *
+ * This {@link AppProvider} implementation can deploy either {@link WebAppContext}s or plain
+ * {@link ContextHandler}s that are assembled from a series of overlays:
+ *
+ * - webapp
- The webapp overlay is a WAR file or docroot directory. The intent is that
+ * the WAR should be deployed to this AppProvider unchanged from how it was delivered. All configuration
+ * and extension should be able to be done in an overlay.
+ * - template
- A template overlay is applied to a WAR file to configure it for all instances of
+ * the webapp to be deployed in the server(s)
+ * - node
- A node overlay is applied to a template to configure it all instances of the template
+ * with node specific information (eg IP address, DB servers etc.).
+ * - instance
- An instance overlay is applied to a node and/or template to configure it
+ * for a specific instance of the template (eg per tenant configuration).
+ *
+ *
+ * Each overlays may provide the following files and subdirectories:
+ * - lib
+ * - The lib directory can contain jars that are applied to a {@link URLClassLoader} that is
+ * available before any context.xml files are executed, so that classes from these jars may be used by the
+ * context.xml.
+ *
+ * - context.xml
+ * - This {@link XmlConfiguration} formatted file may exist in a template, node or instance overlay and is applied to the {@link ContextHandler} or
+ * {@link WebAppContext} so that it may be configured. The context.xml from the template overlay can be used to
+ * create the ContextHandler instance, so a derived class maybe used.
+ *
+ * - template.xml
+ * - This {@link XmlConfiguration} formatted file if it exists in a template or node overlay, is applied to a shared instance of {@link TemplateContext}.
+ * Any ID's created in a template are available as ID's in context.xml for an instance.
+ *
+ * - webdefaul.xml
+ * - If present in an overlay, then the most specific version is passed to
+ * {@link WebAppContext#setDefaultsDescriptor(String)}. Typically this is set in the template overlay.
+ *
+ * - web.xml
+ * - The web.xml file of an overlay is applied to a web application as
+ * with {@link WebAppContext#addOverrideDescriptor(String)}. This allows incremental changes to web.xml without
+ * totally replacing it (see webapp). Typically this is used to set init parameters.
+ *
+ * - webapp
+ * - This directory contains static content that overlays the static content of the webapp
+ * or earlier overlays. Using this directory, files like index.html or logo.png can be added or replaced. It can
+ * also be used to replace files within WEB-INF including web.xml classes and libs.
+ *
+ *
+ *
+ * The OverlayedAppProvider will scan the "webapps", "templates", "nodes" and "instances" subdirectories of
+ * the directory configured with {@link #setScanDir(File)}. New webapps and overlays and modified files within
+ * the overlays will trigger hot deployment, redeployment or undeployment. The scan for modified files is
+ * restricted to only top level files (eg context.xml) and the files matching WEB-INF/*.xml WEB-INF/lib/*
+ * and WEB-INF/classes/*. The webapps/overlays may be directory structures or war/jar archives.
+ *
+ * The filenames of the templates and instances are used to match them together and with a webapplication.
+ * A webapp may be named anyway, but it is good practise to include a version number (eg webapps/foo-1.2.3.war
+ * or webapps/foo-1.2.3/). A template for that webapplication must have a name that includes the template name
+ * and the war name separated by '=' (eg templates/myFoo=foo-1.2.3.jar or templates/myFoo=foo-1.2.3/).
+ * An instance overlay is named with the template name and an arbitrary instance name separated by '='
+ * (eg instances/myFoo=instance1.jar instances/myFoo=instance2/ etc.).
+ *
+ * If a template name does not include a webapp name, then the template is created as a ContextHandler
+ * instead of a WebAppContext (with the exact type being determined by context.xml).
+ */
+public class OverlayedAppProvider extends AbstractLifeCycle implements AppProvider
+{
+ private final static Logger __log=org.eclipse.jetty.util.log.Log.getLogger("OverlayedAppProvider");
+ /**
+ * Property set for context.xml and template.xml files that gives the root cloudtide directory as a canoncial file name.
+ */
+ public final static String CLOUDTIDE_DIR="cloudtide.dir";
+ /**
+ * Property set for context.xml and template.xml files that gives the current webapp name, as {@link Webapp#getName()}.
+ */
+ public final static String CLOUDTIDE_WEBAPP="cloudtide.webapp";
+ /**
+ * Property set for context.xml and template.xml files that gives the current template name, as {@link Template#getTemplateName()}.
+ */
+ public final static String CLOUDTIDE_TEMPLATE="cloudtide.template";
+ /**
+ * Property set for context.xml and template.xml files that gives the current node name, as {@link Node#getName()}.
+ */
+ public final static String CLOUDTIDE_NODE="cloudtide.node";
+ /**
+ * Property set for context.xml and template.xml files that gives the current instance name, {@link Instance#getName()}.
+ */
+ public final static String CLOUDTIDE_INSTANCE="cloudtide.instance";
+
+ public final static String WEBAPPS="webapps";
+ public final static String TEMPLATES="templates";
+ public final static String NODES="nodes";
+ public final static String INSTANCES="instances";
+
+ enum Monitor { WEBAPPS,TEMPLATES,NODES,INSTANCES} ;
+
+ public final static List __scanPatterns = new ArrayList();
+
+ static
+ {
+ List regexes = new ArrayList();
+
+ regexes.add(WEBAPPS+"/[^/]*/");
+ regexes.add(TEMPLATES+"/[^/]*/");
+ regexes.add(NODES+"/[^/]*/");
+ regexes.add(INSTANCES+"/[^/]*/");
+
+ regexes.add(WEBAPPS+"/[^/]*");
+ regexes.add(TEMPLATES+"/[^/]*");
+ regexes.add(NODES+"/[^/]*");
+ regexes.add(INSTANCES+"/[^/]*");
+
+ regexes.add(TEMPLATES+"/[^/]*/[^/]+");
+ regexes.add(NODES+"/[^/]*/[^/]+");
+ regexes.add(INSTANCES+"/[^/]*/[^/]+");
+
+ regexes.add(TEMPLATES+"/[^/]*/lib/[^/]+");
+ regexes.add(NODES+"/[^/]*/lib/[^/]+");
+ regexes.add(INSTANCES+"/[^/]*/lib/[^/]+");
+
+ for (String s:new String[] {"/WEB-INF/lib/[^/]*","/WEB-INF/classes/[^/]*","/WEB-INF/[^/]*\\.xml",})
+ {
+ regexes.add(WEBAPPS+"/[^/]*"+s);
+ regexes.add(TEMPLATES+"/[^/]*/webapp"+s);
+ regexes.add(NODES+"/[^/]*/webapp"+s);
+ regexes.add(INSTANCES+"/[^/]*/webapp"+s);
+ }
+
+ for (String s: regexes)
+ __scanPatterns.add(Pattern.compile(s,Pattern.CASE_INSENSITIVE));
+ };
+
+ private String _nodeName;
+ private File _scanDir;
+ private File _tmpDir;
+ private String _scanDirURI;
+ private long _loading;
+ private Node _node;
+ private final Map _webapps = new HashMap();
+ private final Map _templates = new HashMap();
+ private final Map _instances = new HashMap();
+ private final Map _deployed = new HashMap();
+ private final Map _shared = new HashMap();
+ private boolean _copydir=false;
+ private DeploymentManager _deploymentManager;
+ private ConfigurationManager _configurationManager;
+ private String _serverID="Server";
+ private final Set _removedLayers = new HashSet();
+ private Timer _sessionScavenger = new Timer();
+
+ private final Scanner _scanner = new Scanner();
+ private final Scanner.BulkListener _listener = new Scanner.BulkListener()
+ {
+ public void filesChanged(List filenames) throws Exception
+ {
+ __log.debug("Changed {}",filenames);
+
+ Set changes = new HashSet();
+ for (String filename:filenames)
+ {
+ File file=new File(filename);
+ if (file.getName().startsWith(".") || file.getName().endsWith(".swp"))
+ continue;
+
+ String relname=file.toURI().getPath().substring(_scanDirURI.length());
+
+ File rel = new File(relname);
+
+ String dir=null;
+ String name=null;
+ String parent=rel.getParent();
+ while (parent!=null)
+ {
+ name=rel.getName();
+ dir=parent;
+ rel=rel.getParentFile();
+ parent=rel.getParent();
+ }
+
+ String uri=dir+"/"+name;
+
+ for (Pattern p : __scanPatterns)
+ {
+ if (p.matcher(relname).matches())
+ {
+ __log.debug("{} == {}",relname,p.pattern());
+ changes.add(uri);
+ }
+ else
+ __log.debug("{} != {}",relname,p.pattern());
+ }
+ }
+
+ if (changes.size()>0)
+ OverlayedAppProvider.this.updateLayers(changes);
+ }
+ };
+
+
+ /* ------------------------------------------------------------ */
+ public OverlayedAppProvider()
+ {
+ try
+ {
+ _nodeName=InetAddress.getLocalHost().getHostName();
+ }
+ catch(UnknownHostException e)
+ {
+ __log.debug(e);
+ _nodeName="unknown";
+ }
+ }
+
+
+
+ /* ------------------------------------------------------------ */
+ public void setDeploymentManager(DeploymentManager deploymentManager)
+ {
+ _deploymentManager=deploymentManager;
+ }
+
+ /* ------------------------------------------------------------ */
+ public DeploymentManager getDeploymentManager()
+ {
+ return _deploymentManager;
+ }
+
+ /* ------------------------------------------------------------ */
+ public ConfigurationManager getConfigurationManager()
+ {
+ return _configurationManager;
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Set the configurationManager.
+ * @param configurationManager the configurationManager to set
+ */
+ public void setConfigurationManager(ConfigurationManager configurationManager)
+ {
+ _configurationManager = configurationManager;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return The name in {@link XmlConfiguration#getIdMap()} of the {@link Server} instance. Default "Server".
+ */
+ public String getServerID()
+ {
+ return _serverID;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param serverID The name in {@link XmlConfiguration#getIdMap()} of the {@link Server} instance.
+ */
+ public void setServerID(String serverID)
+ {
+ _serverID = serverID;
+ }
+
+
+ /**
+ * Create Context Handler.
+ *
+ * Callback from the deployment manager to create a context handler instance.
+ * @see org.eclipse.jetty.deploy.AppProvider#createContextHandler(org.eclipse.jetty.deploy.App)
+ */
+ public synchronized ContextHandler createContextHandler(App app) throws Exception
+ {
+ final OverlayedApp overlayed = (OverlayedApp)app;
+ final String origin = overlayed.getOriginId();
+ final Instance instance = overlayed.getInstance();
+ final Template template = instance.getTemplate();
+ final Webapp webapp = template.getWebapp();
+ final Node node = _node;
+
+ // remember the original loader
+ ClassLoader orig_loader = Thread.currentThread().getContextClassLoader();
+ try
+ {
+ // Look for existing shared resources
+ String key=(node==null?"":node.getLoadedKey())+template.getLoadedKey()+(webapp==null?"":webapp.getLoadedKey());
+ instance.setSharedKey(key);
+
+ TemplateContext shared=_shared.get(key);
+ // Create shared resourced
+ if (shared==null)
+ shared=createTemplateContext(key,webapp,template,node,orig_loader);
+
+ // Build the instance lib loader
+ ClassLoader shared_loader = shared.getWebappLoader()!=null?shared.getWebappLoader():(shared.getLibLoader()!=null?shared.getLibLoader():orig_loader);
+ ClassLoader loader = shared_loader;
+ Resource instance_lib = instance.getResource("lib");
+ if (instance_lib.exists())
+ {
+ List libs = new ArrayList();
+ for (String jar :instance_lib.list())
+ {
+ if (!jar.toLowerCase().endsWith(".jar"))
+ continue;
+ libs.add(instance_lib.addPath(jar).getURL());
+ }
+
+ __log.debug("{}: libs={}",origin,libs);
+ loader = URLClassLoader.newInstance(libs.toArray(new URL[]{}),loader);
+ }
+
+ // set the thread loader
+ Thread.currentThread().setContextClassLoader(loader);
+
+ // Create properties to be shared by context.xmls
+ Map idMap = new HashMap();
+ idMap.putAll(shared.getIdMap());
+ idMap.put(_serverID,getDeploymentManager().getServer());
+
+ // Create the instance context for the template
+ ContextHandler context=null;
+
+ Resource template_context_xml = template.getResource("context.xml");
+ if (template_context_xml.exists())
+ {
+ __log.debug("{}: context.xml={}",origin,template_context_xml);
+ XmlConfiguration xmlc = newXmlConfiguration(template_context_xml.getURL(),idMap,template,instance);
+ context=(ContextHandler)xmlc.configure();
+ idMap=xmlc.getIdMap();
+ }
+ else if (webapp==null)
+ // If there is no webapp, this is a plain context
+ context=new ContextHandler();
+ else
+ // It is a webapp context
+ context=new WebAppContext();
+
+ // Set the resource base
+ final Resource instance_webapp = instance.getResource("webapp");
+ if (instance_webapp.exists())
+ {
+ context.setBaseResource(new ResourceCollection(instance_webapp,shared.getBaseResource()));
+
+ // Create the resource cache
+ ResourceCache cache = new ResourceCache(shared.getResourceCache(),instance_webapp,context.getMimeTypes());
+ context.setAttribute(ResourceCache.class.getCanonicalName(),cache);
+ }
+ else
+ {
+ context.setBaseResource(shared.getBaseResource());
+ context.setAttribute(ResourceCache.class.getCanonicalName(),shared.getResourceCache());
+ }
+ __log.debug("{}: baseResource={}",origin,context.getResourceBase());
+
+ // Set the shared session scavenger timer
+ context.setAttribute("org.eclipse.jetty.server.session.timer", _sessionScavenger);
+
+ // Apply any node or instance context.xml
+ for (Resource context_xml : getLayeredResources("context.xml",node,instance))
+ {
+ __log.debug("{}: context.xml={}",origin,context_xml);
+ XmlConfiguration xmlc = newXmlConfiguration(context_xml.getURL(),idMap,template,instance);
+ xmlc.getIdMap().put("Cache",context.getAttribute(ResourceCache.class.getCanonicalName()));
+ xmlc.configure(context);
+ idMap=xmlc.getIdMap();
+ }
+
+ // Is it a webapp?
+ if (context instanceof WebAppContext)
+ {
+ final WebAppContext webappcontext = (WebAppContext)context;
+
+ if (Arrays.asList(((WebAppContext)context).getServerClasses()).toString().equals(Arrays.asList(WebAppContext.__dftServerClasses).toString()))
+ {
+ __log.debug("clear server classes");
+ webappcontext.setServerClasses(null);
+ }
+
+ // set classloader
+ webappcontext.setCopyWebDir(false);
+ webappcontext.setCopyWebInf(false);
+ webappcontext.setExtractWAR(false);
+
+ if (instance_webapp.exists())
+ {
+ final Resource classes=instance_webapp.addPath("WEB-INF/classes");
+ final Resource lib=instance_webapp.addPath("WEB-INF/lib");
+
+ if (classes.exists()||lib.exists())
+ {
+ final AtomicBoolean locked =new AtomicBoolean(false);
+
+ WebAppClassLoader webapp_loader=new WebAppClassLoader(loader,webappcontext)
+ {
+ @Override
+ public void addClassPath(Resource resource) throws IOException
+ {
+ if (!locked.get())
+ super.addClassPath(resource);
+ }
+
+ @Override
+ public void addClassPath(String classPath) throws IOException
+ {
+ if (!locked.get())
+ super.addClassPath(classPath);
+ }
+
+ @Override
+ public void addJars(Resource lib)
+ {
+ if (!locked.get())
+ super.addJars(lib);
+ }
+ };
+
+ if (classes.exists())
+ webapp_loader.addClassPath(classes);
+ if (lib.exists())
+ webapp_loader.addJars(lib);
+ locked.set(true);
+
+ loader=webapp_loader;
+ }
+ }
+
+ // Make sure loader is unique for JNDI
+ if (loader==shared_loader)
+ loader = new URLClassLoader(new URL[]{},shared_loader);
+
+ // add default descriptor
+ List webdefaults=getLayeredResources("webdefault.xml",instance,node,template);
+ if (webdefaults.size()>0)
+ {
+ Resource webdefault = webdefaults.get(0);
+ __log.debug("{}: defaultweb={}",origin,webdefault);
+ webappcontext.setDefaultsDescriptor(webdefault.toString());
+ }
+
+ // add overlay descriptors
+ for (Resource override : getLayeredResources("web.xml",template,node,instance))
+ {
+ __log.debug("{}: web override={}",origin,override);
+ webappcontext.addOverrideDescriptor(override.toString());
+ }
+ }
+
+ context.setClassLoader(loader);
+
+ __log.debug("{}: baseResource={}",origin,context.getBaseResource());
+
+
+ System.err.println("created:\n"+context.dump());
+
+ return context;
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(orig_loader);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ private XmlConfiguration newXmlConfiguration(URL url, Map idMap, Template template, Instance instance) throws SAXException, IOException
+ {
+ XmlConfiguration xmlc = new XmlConfiguration(url);
+ xmlc.getProperties().put(CLOUDTIDE_DIR,_scanDir.getCanonicalPath());
+ if (template!=null)
+ {
+ xmlc.getProperties().put(CLOUDTIDE_TEMPLATE,template.getTemplateName());
+ xmlc.getProperties().put(CLOUDTIDE_WEBAPP,template.getWebapp()==null?null:template.getWebapp().getName());
+ }
+ if (_node!=null)
+ xmlc.getProperties().put(CLOUDTIDE_NODE,_node.getName());
+ if (instance!=null)
+ xmlc.getProperties().put(CLOUDTIDE_INSTANCE,instance.getName());
+ if (getConfigurationManager()!=null)
+ xmlc.getProperties().putAll(getConfigurationManager().getProperties());
+ xmlc.getIdMap().putAll(idMap);
+
+ return xmlc;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ private TemplateContext createTemplateContext(final String key, Webapp webapp, Template template, Node node, ClassLoader parent) throws Exception
+ {
+ __log.info("created {}",key);
+
+ // look for libs
+ // If we have libs directories, create classloader and make it available to
+ // the XMLconfiguration
+ List libs = new ArrayList();
+ for (Resource lib : getLayeredResources("lib",node,template))
+ {
+ for (String jar :lib.list())
+ {
+ if (!jar.toLowerCase().endsWith(".jar"))
+ continue;
+ libs.add(lib.addPath(jar).getURL());
+ }
+ }
+ final ClassLoader libLoader;
+ if (libs.size()>0)
+ {
+ __log.debug("{}: libs={}",key,libs);
+ libLoader=new URLClassLoader(libs.toArray(new URL[]{}),parent)
+ {
+ public String toString() {return "libLoader@"+Long.toHexString(hashCode())+"-lib-"+key;}
+ };
+
+ }
+ else
+ libLoader=parent;
+
+ Thread.currentThread().setContextClassLoader(libLoader);
+
+
+ // Make the shared resourceBase
+ List bases = new ArrayList();
+ for (Resource wa : getLayeredResources("webapp",node,template))
+ bases.add(wa);
+ if (webapp!=null)
+ bases.add(webapp.getBaseResource());
+ Resource baseResource = bases.size()==1?bases.get(0):new ResourceCollection(bases.toArray(new Resource[bases.size()]));
+ __log.debug("{}: baseResource={}",key,baseResource);
+
+
+ // Make the shared context
+ TemplateContext shared = new TemplateContext(key,getDeploymentManager().getServer(),baseResource,libLoader);
+ _shared.put(key,shared);
+
+
+ // Create properties to be shared by context.xmls
+ Map idMap = new HashMap();
+ idMap.put(_serverID,getDeploymentManager().getServer());
+
+
+ // Create the shared context for the template
+ // This instance will never be start, but is used to capture the
+ // shared results of running the template and node context.xml files.
+ // If there is a template context.xml, give it the chance to create the ContextHandler instance
+ // otherwise create an instance ourselves
+ for (Resource template_xml : getLayeredResources("template.xml",template,node))
+ {
+ __log.debug("{}: template.xml={}",key,template_xml);
+ XmlConfiguration xmlc = newXmlConfiguration(template_xml.getURL(),idMap,template,null);
+ xmlc.getIdMap().putAll(idMap);
+ xmlc.configure(shared);
+ idMap=xmlc.getIdMap();
+ }
+
+ shared.setIdMap(idMap);
+ shared.start();
+
+ return shared;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return The node name (defaults to hostname)
+ */
+ public String getNodeName()
+ {
+ return _nodeName;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param nodeName Set the node name
+ */
+ public void setNodeName(String nodeName)
+ {
+ _nodeName = nodeName;
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Get the scanDir.
+ * @return the scanDir
+ */
+ public File getScanDir()
+ {
+ return _scanDir;
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Set the scanDir.
+ * @param scanDir the scanDir to set
+ */
+ public void setScanDir(File scanDir)
+ {
+ _scanDir = scanDir;
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Set the temporary directory.
+ * @param tmpDir the directory for temporary files. If null, then getScanDir()+"/tmp" is used if it exists, else the system default is used.
+ */
+ public void setTmpDir(File tmpDir)
+ {
+ _tmpDir=tmpDir;
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Get the temporary directory.
+ * return the tmpDir. If null, then getScanDir()+"/tmp" is used if it exists, else the system default is used.
+ */
+ public File getTmpDir()
+ {
+ return _tmpDir;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return The scan interval
+ * @see org.eclipse.jetty.util.Scanner#getScanInterval()
+ */
+ public int getScanInterval()
+ {
+ return _scanner.getScanInterval();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param scanInterval The scan interval
+ * @see org.eclipse.jetty.util.Scanner#setScanInterval(int)
+ */
+ public void setScanInterval(int scanInterval)
+ {
+ _scanner.setScanInterval(scanInterval);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.util.Scanner#scan()
+ */
+ public void scan()
+ {
+ _scanner.scan();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
+ */
+ @Override
+ protected void doStart() throws Exception
+ {
+ __log.info("Node={} Scan=",_nodeName,_scanDir);
+ if (_scanDir==null || !_scanDir.exists())
+ throw new IllegalStateException("!scandir");
+
+ _scanDirURI=_scanDir.toURI().getPath();
+ _scanner.setScanDepth(6); // enough for templates/name/webapps/WEB-INF/lib/foo.jar
+ List dirs = Arrays.asList(new File[]
+ {
+ new File(_scanDir,WEBAPPS),
+ new File(_scanDir,TEMPLATES),
+ new File(_scanDir,NODES),
+ new File(_scanDir,INSTANCES)
+ });
+ for (File file : dirs)
+ {
+ if (!file.exists() && !file.isDirectory())
+ __log.warn("No directory: "+file.getAbsolutePath());
+ }
+ _scanner.setScanDirs(dirs);
+ _scanner.addListener(_listener);
+ _scanner.start();
+
+ super.doStart();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
+ */
+ @Override
+ protected void doStop() throws Exception
+ {
+ _scanner.removeListener(_listener);
+ _scanner.stop();
+
+ if (_deploymentManager.isRunning())
+ {
+ for (App app: _deployed.values())
+ _deploymentManager.removeApp(app);
+ }
+ _deployed.clear();
+
+ for (Layer layer : _webapps.values())
+ layer.release();
+ _webapps.clear();
+ for (Layer layer : _templates.values())
+ layer.release();
+ _templates.clear();
+ if (_node!=null)
+ _node.release();
+ for (Layer layer : _instances.values())
+ layer.release();
+ _instances.clear();
+
+ super.doStop();
+ }
+
+ /* ------------------------------------------------------------ */
+ protected synchronized void updateLayers(Set layerURIs)
+ {
+ _loading=System.currentTimeMillis();
+ for (String ruri: layerURIs)
+ {
+ try
+ {
+ // Decompose the name
+ File directory;
+ File archive;
+ File origin = new File(new URI(_scanDir.toURI()+ruri));
+ String name=origin.getName();
+
+ Monitor monitor = Monitor.valueOf(origin.getParentFile().getName().toUpperCase());
+
+ String ext=monitor==Monitor.WEBAPPS?".war":".jar";
+
+ // check directory vs archive
+ if (origin.isDirectory() || !origin.exists() && !ruri.toLowerCase().endsWith(ext))
+ {
+ // directories have priority over archives
+ directory=origin;
+ archive=new File(directory.toString()+ext);
+ }
+ else
+ {
+ // check extension name
+ if (!ruri.toLowerCase().endsWith(ext))
+ continue;
+
+ name=name.substring(0,name.length()-4);
+ archive=origin;
+ directory=new File(new URI(_scanDir.toURI()+ruri.substring(0,ruri.length()-4)));
+
+ // Look to see if directory exists
+ if (directory.exists())
+ {
+ __log.info("Directory exists, ignoring change to {}",ruri);
+ continue;
+ }
+ }
+
+ Layer layer=null;
+
+ switch(monitor)
+ {
+ case WEBAPPS:
+ if (origin.exists())
+ layer=loadWebapp(name,origin);
+ else
+ {
+ removeWebapp(name);
+ if (origin==directory && archive.exists())
+ layer=loadWebapp(name,archive);
+ }
+
+ break;
+
+ case TEMPLATES:
+ if (origin.exists())
+ layer=loadTemplate(name,origin);
+ else
+ {
+ removeTemplate(name);
+ if (origin==directory && archive.exists())
+ layer=loadTemplate(name,archive);
+ }
+ break;
+
+ case NODES:
+ if (name.equalsIgnoreCase(_nodeName))
+ {
+ if (origin.exists())
+ layer=loadNode(origin);
+ else
+ {
+ removeNode();
+ if (origin==directory && archive.exists())
+ layer=loadNode(archive);
+ }
+ }
+ break;
+
+ case INSTANCES:
+ if (origin.exists())
+ layer=loadInstance(name,origin);
+ else
+ {
+ removeInstance(name);
+ if (origin==directory && archive.exists())
+ layer=loadInstance(name,archive);
+ }
+ break;
+
+ }
+
+ if (layer!=null)
+ __log.info("loaded {}",layer.getLoadedKey());
+ }
+ catch(Exception e)
+ {
+ __log.warn(e);
+ }
+ }
+
+ redeploy();
+
+ // Release removed layers
+ for (Layer layer : _removedLayers)
+ {
+ if (layer!=null)
+ {
+ __log.info("unload {}",layer.getLoadedKey());
+ layer.release();
+ }
+ }
+ _removedLayers.clear();
+
+ if (__log.isDebugEnabled())
+ {
+ System.err.println("updated:");
+ System.err.println("java:"+javaRootURLContext.getRoot().dump());
+ System.err.println("local:"+localContextRoot.getRoot().dump());
+ if (getDeploymentManager()!=null && getDeploymentManager().getServer()!=null)
+ System.err.println(getDeploymentManager().getServer().dump());
+ }
+ }
+
+ protected File tmpdir(String name,String suffix) throws IOException
+ {
+ File dir=_tmpDir;
+ if (dir==null || !dir.isDirectory() || !dir.canWrite())
+ {
+ dir=new File(_scanDir,"tmp");
+ if (!dir.isDirectory() || !dir.canWrite())
+ dir=null;
+ }
+
+ File tmp = File.createTempFile(name+"_","."+suffix,dir);
+ tmp=tmp.getCanonicalFile();
+ if (tmp.exists())
+ IO.delete(tmp);
+ tmp.mkdir();
+ tmp.deleteOnExit();
+ return tmp;
+ }
+
+ /**
+ * Walks the defined webapps, templates, nodes and instances to
+ * determine what should be deployed, then adjust reality to match.
+ */
+ protected void redeploy()
+ {
+ Map templates = new ConcurrentHashMap();
+
+ // Check for duplicate templates
+ for (Template template : _templates.values())
+ {
+ Template other=templates.get(template.getTemplateName());
+ if (other!=null)
+ {
+ __log.warn("Multiple Templates: {} & {}",template.getName(),other.getName());
+ if (other.getName().compareToIgnoreCase(template.getName())<=0)
+ continue;
+ }
+ templates.put(template.getTemplateName(),template);
+ }
+
+ // Match webapps to templates
+ for (Template template : templates.values())
+ {
+ String webappname=template.getClassifier();
+
+ if (webappname==null)
+ continue;
+
+ Webapp webapp = _webapps.get(webappname);
+
+ if (webapp==null)
+ {
+ __log.warn("No webapp found for template: {}",template.getName());
+ templates.remove(template.getTemplateName());
+ }
+ else
+ {
+ template.setWebapp(webapp);
+ }
+ }
+
+ // Match instance to templates and check if what needs to be deployed or undeployed.
+ Set deployed = new HashSet();
+ List deploy = new ArrayList();
+
+ for (Instance instance : _instances.values())
+ {
+ Template template=templates.get(instance.getTemplateName());
+ instance.setTemplate(template);
+ if (template!=null)
+ {
+ String key=instance.getInstanceKey();
+ App app = _deployed.get(key);
+ if (app==null)
+ deploy.add(instance);
+ else
+ deployed.add(key);
+ }
+ }
+
+ // Look for deployed apps that need to be undeployed
+ List undeploy = new ArrayList();
+ for (String key : _deployed.keySet())
+ {
+ if (!deployed.contains(key))
+ undeploy.add(key);
+ }
+
+ // Do the undeploys
+ for (String key : undeploy)
+ {
+ App app = _deployed.remove(key);
+ if (app!=null)
+ {
+ __log.info("Undeploy {}",key);
+ _deploymentManager.removeApp(app);
+ }
+ }
+
+ // ready the deploys
+ for (Instance instance : deploy)
+ {
+ String key=instance.getInstanceKey();
+ OverlayedApp app = new OverlayedApp(_deploymentManager,this,key,instance);
+ _deployed.put(key,app);
+ }
+
+ // Remove unused Shared stuff
+ Set sharedKeys = new HashSet(_shared.keySet());
+ for (OverlayedApp app : _deployed.values())
+ {
+ Instance instance = app.getInstance();
+ sharedKeys.remove(instance.getSharedKey());
+ }
+ for (String sharedKey: sharedKeys)
+ {
+ __log.debug("Remove "+sharedKey);
+ TemplateContext shared=_shared.remove(sharedKey);
+ if (shared!=null)
+ {
+ try
+ {
+ shared.stop();
+ }
+ catch(Exception e)
+ {
+ __log.warn(e);
+ }
+ shared.destroy();
+ }
+ }
+
+ // Do the deploys
+ for (Instance instance : deploy)
+ {
+ String key=instance.getInstanceKey();
+ OverlayedApp app = _deployed.get(key);
+ __log.info("Deploy {}",key);
+ _deploymentManager.addApp(app);
+ }
+
+
+ }
+
+ protected void removeInstance(String name)
+ {
+ _removedLayers.add(_instances.remove(name));
+ }
+
+ protected Instance loadInstance(String name, File origin)
+ throws IOException
+ {
+ Instance instance=new Instance(name,origin);
+ _removedLayers.add(_instances.put(name,instance));
+ return instance;
+ }
+
+ protected void removeNode()
+ {
+ if (_node!=null)
+ _removedLayers.add(_node);
+ _node=null;
+ }
+
+ protected Node loadNode(File origin)
+ throws IOException
+ {
+ if (_node!=null)
+ _removedLayers.add(_node);
+ _node=new Node(_nodeName,origin);
+ return _node;
+ }
+
+ protected void removeTemplate(String name)
+ {
+ _removedLayers.add(_templates.remove(name));
+ }
+
+ protected Template loadTemplate(String name, File origin)
+ throws IOException
+ {
+ Template template=new Template(name,origin);
+ _removedLayers.add(_templates.put(name,template));
+ return template;
+ }
+
+ protected void removeWebapp(String name)
+ {
+ _removedLayers.add(_webapps.remove(name));
+ }
+
+ protected Webapp loadWebapp(String name, File origin)
+ throws IOException
+ {
+ Webapp webapp = new Webapp(name,origin);
+ _removedLayers.add(_webapps.put(name,webapp));
+ return webapp;
+ }
+
+ private static List getLayeredResources(String path, Layer... layers)
+ {
+ List resources = new ArrayList();
+ for (Layer layer: layers)
+ {
+ if (layer==null)
+ continue;
+ Resource resource = layer.getResource(path);
+ if (resource.exists())
+ resources.add(resource);
+ }
+ return resources;
+ }
+
+ class Layer
+ {
+ private final String _name;
+ private final File _origin;
+ private final long _loaded=_loading;
+ private final Resource _resourceBase;
+ private final boolean _resourceBaseIsCopy;
+
+ public Layer(String name, File origin)
+ throws IOException
+ {
+ super();
+ _name = name;
+ _origin = origin;
+
+ Resource resource = Resource.newResource(origin.toURI());
+
+ if (resource.isDirectory())
+ {
+ if (_copydir)
+ {
+ File tmp=tmpdir(name,"extract");
+ __log.info("Extract {} to {}",origin,tmp);
+ IO.copyDir(origin,tmp);
+ _resourceBase=Resource.newResource(tmp.toURI());
+ _resourceBaseIsCopy=true;
+ }
+ else
+ {
+ _resourceBase=resource;
+ _resourceBaseIsCopy=false;
+ }
+ }
+ else
+ {
+ Resource jar = JarResource.newJarResource(resource);
+ File tmp=tmpdir(name,"extract");
+ __log.info("Extract {} to {}",jar,tmp);
+ jar.copyTo(tmp);
+ _resourceBase=Resource.newResource(tmp.toURI());
+ _resourceBaseIsCopy=true;
+ }
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public File getOrigin()
+ {
+ return _origin;
+ }
+
+ public long getLoaded()
+ {
+ return _loaded;
+ }
+
+ public Resource getBaseResource()
+ {
+ return _resourceBase;
+ }
+
+ public Resource getResource(String path)
+ {
+ try
+ {
+ return getBaseResource().addPath(path);
+ }
+ catch(Exception e)
+ {
+ __log.warn(e);
+ }
+ return null;
+ }
+
+ public String getLoadedKey()
+ {
+ return _name+"@"+_loaded;
+ }
+
+ public void release()
+ {
+ if (_resourceBaseIsCopy)
+ {
+ try
+ {
+ File file = _resourceBase.getFile();
+ if (file!=null)
+ IO.delete(file);
+ }
+ catch(Exception e)
+ {
+ __log.warn(e);
+ }
+ }
+ }
+
+ public String toString()
+ {
+ return getLoadedKey();
+ }
+ }
+
+ class Webapp extends Layer
+ {
+ public Webapp(String name, File origin) throws IOException
+ {
+ super(name,origin);
+ }
+ }
+
+ class Overlay extends Layer
+ {
+ public Overlay(String name, File origin) throws IOException
+ {
+ super(name,origin);
+ }
+
+ public Resource getContext()
+ {
+ return getResource("context.xml");
+ }
+ }
+
+ class Node extends Overlay
+ {
+ public Node(String name, File origin) throws IOException
+ {
+ super(name,origin);
+ }
+ }
+
+
+ class ClassifiedOverlay extends Overlay
+ {
+ private final String _templateName;
+ private final String _classifier;
+
+ public ClassifiedOverlay(String name, File origin) throws IOException
+ {
+ super(name,origin);
+ int e=name.indexOf('=');
+ _templateName=e>=0?name.substring(0,e):name;
+ _classifier=e>=0?name.substring(e+1):null;
+ }
+
+ public String getTemplateName()
+ {
+ return _templateName;
+ }
+
+ public String getClassifier()
+ {
+ return _classifier;
+ }
+ }
+
+ class Template extends ClassifiedOverlay
+ {
+ private Webapp _webapp;
+
+ public Webapp getWebapp()
+ {
+ return _webapp;
+ }
+
+ public void setWebapp(Webapp webapp)
+ {
+ _webapp = webapp;
+ }
+
+ public Template(String name, File origin) throws IOException
+ {
+ super(name,origin);
+ }
+ }
+
+ class Instance extends ClassifiedOverlay
+ {
+ Template _template;
+ String _sharedKey;
+
+ public Instance(String name, File origin) throws IOException
+ {
+ super(name,origin);
+ if (getClassifier()==null)
+ throw new IllegalArgumentException("Instance without '=':"+name);
+ }
+
+ public void setSharedKey(String key)
+ {
+ _sharedKey=key;
+ }
+
+ public String getSharedKey()
+ {
+ return _sharedKey;
+ }
+
+ public void setTemplate(Template template)
+ {
+ _template=template;
+ }
+
+ public Template getTemplate()
+ {
+ return _template;
+ }
+
+ public String getInstanceKey()
+ {
+ return
+ (_template.getWebapp()==null?"":_template.getWebapp().getLoadedKey())+"|"+
+ _template.getLoadedKey()+"|"+
+ (_node==null?"":_node.getLoadedKey())+"|"+
+ getLoadedKey();
+ }
+ }
+
+ static class OverlayedApp extends App
+ {
+ final Instance _instance;
+
+ public OverlayedApp(DeploymentManager manager, AppProvider provider, String originId, Instance instance)
+ {
+ super(manager,provider,originId);
+ _instance=instance;
+ }
+
+ public Instance getInstance()
+ {
+ return _instance;
+ }
+ }
+
+}
diff --git a/jetty-overlay-deployer/src/main/java/org/eclipse/jetty/overlays/TemplateContext.java b/jetty-overlay-deployer/src/main/java/org/eclipse/jetty/overlays/TemplateContext.java
new file mode 100644
index 00000000000..8925bfb07b8
--- /dev/null
+++ b/jetty-overlay-deployer/src/main/java/org/eclipse/jetty/overlays/TemplateContext.java
@@ -0,0 +1,285 @@
+// ========================================================================
+// Copyright (c) 2010-2011 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// 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.opensource.org/licenses/apache2.0.php
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+
+package org.eclipse.jetty.overlays;
+
+import java.io.IOException;
+import java.security.PermissionCollection;
+import java.util.Map;
+
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.server.ResourceCache;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.component.AggregateLifeCycle;
+import org.eclipse.jetty.util.component.Destroyable;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.webapp.ClasspathPattern;
+import org.eclipse.jetty.webapp.WebAppClassLoader;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.xml.XmlConfiguration;
+
+/**
+ * A Cloudtide template context.
+ *
+ * This class is configured by the template.xml files and is used to control the
+ * shared resource cache and classloader.
+ *
+ * This class is an AggregateLifeCycle, so dependent beans may be added to the template and will be started, stopped and destroyed with the template.
+ * The template is started after the template.xml file have been applied. It is stopped and destroyed after the last instance using the template is undeployed.
+ */
+public class TemplateContext extends AggregateLifeCycle implements WebAppClassLoader.Context, Destroyable
+{
+ private final ClassLoader _libLoader;
+
+ private final Resource _baseResource;
+ private final ResourceCache _resourceCache;
+ private final Server _server;
+ private final MimeTypes _mimeTypes;
+ private final WebAppClassLoader _webappLoader;
+
+ private ClasspathPattern _systemClasses;
+ private ClasspathPattern _serverClasses;
+ private PermissionCollection _permissions;
+
+ private boolean _parentLoaderPriority;
+
+ private String _extraClasspath;
+
+ private Map _idMap;
+
+
+ public ClassLoader getLibLoader()
+ {
+ return _libLoader;
+ }
+
+ public TemplateContext()
+ {
+ _server=null;
+ _baseResource=null;
+ _mimeTypes=new MimeTypes();
+ _resourceCache=null;
+ _webappLoader=null;
+ _libLoader=null;
+ }
+
+ public TemplateContext(String key, Server server,Resource baseResource, ClassLoader libLoader) throws IOException
+ {
+ _server=server;
+ _baseResource=baseResource;
+ _mimeTypes=new MimeTypes();
+ _resourceCache=new ResourceCache(null,baseResource,_mimeTypes);
+
+ String[] patterns = (String[])_server.getAttribute(WebAppContext.SERVER_SRV_CLASSES);
+ _serverClasses=new ClasspathPattern(patterns==null?WebAppContext.__dftServerClasses:patterns);
+ patterns = (String[])_server.getAttribute(WebAppContext.SERVER_SYS_CLASSES);
+ _systemClasses=new ClasspathPattern(patterns==null?WebAppContext.__dftSystemClasses:patterns);
+ _libLoader=libLoader;
+
+
+ // Is this a webapp or a normal context
+ Resource classes=getBaseResource().addPath("WEB-INF/classes/");
+ Resource lib=getBaseResource().addPath("WEB-INF/lib/");
+ if (classes.exists() && classes.isDirectory() || lib.exists() && lib.isDirectory())
+ {
+ _webappLoader=new WebAppClassLoader(_libLoader,this);
+ _webappLoader.setName(key);
+ if (classes.exists())
+ _webappLoader.addClassPath(classes);
+ if (lib.exists())
+ _webappLoader.addJars(lib);
+ }
+ else
+ _webappLoader=null;
+
+ }
+
+ /* ------------------------------------------------------------ */
+ public Resource getBaseResource()
+ {
+ return _baseResource;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return Comma or semicolon separated path of filenames or URLs
+ * pointing to directories or jar files. Directories should end
+ * with '/'.
+ */
+ public String getExtraClasspath()
+ {
+ return _extraClasspath;
+ }
+
+ /* ------------------------------------------------------------ */
+ public MimeTypes getMimeTypes()
+ {
+ return _mimeTypes;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ public PermissionCollection getPermissions()
+ {
+ return _permissions;
+ }
+
+ /* ------------------------------------------------------------ */
+ public ResourceCache getResourceCache()
+ {
+ return _resourceCache;
+ }
+
+ /* ------------------------------------------------------------ */
+ public Server getServer()
+ {
+ return _server;
+ }
+
+ /* ------------------------------------------------------------ */
+ WebAppClassLoader getWebappLoader()
+ {
+ return _webappLoader;
+ }
+
+ /* ------------------------------------------------------------ */
+ public boolean isParentLoaderPriority()
+ {
+ return _parentLoaderPriority;
+ }
+
+ /* ------------------------------------------------------------ */
+ public boolean isServerClass(String clazz)
+ {
+ return _serverClasses.match(clazz);
+ }
+
+ /* ------------------------------------------------------------ */
+ public boolean isSystemClass(String clazz)
+ {
+ return _systemClasses.match(clazz);
+ }
+
+ /* ------------------------------------------------------------ */
+ public Resource newResource(String urlOrPath) throws IOException
+ {
+ return Resource.newResource(urlOrPath);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param extraClasspath Comma or semicolon separated path of filenames or URLs
+ * pointing to directories or jar files. Directories should end
+ * with '/'.
+ */
+ public void setExtraClasspath(String extraClasspath)
+ {
+ _extraClasspath=extraClasspath;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param java2compliant The java2compliant to set.
+ */
+ public void setParentLoaderPriority(boolean java2compliant)
+ {
+ _parentLoaderPriority = java2compliant;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param permissions The permissions to set.
+ */
+ public void setPermissions(PermissionCollection permissions)
+ {
+ _permissions = permissions;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Set the server classes patterns.
+ *
+ * Server classes/packages are classes used to implement the server and are hidden
+ * from the context. If the context needs to load these classes, it must have its
+ * own copy of them in WEB-INF/lib or WEB-INF/classes.
+ * A class pattern is a string of one of the forms:
+ * - org.package.Classname
- Match a specific class
+ * - org.package.
- Match a specific package hierarchy
+ * - -org.package.Classname
- Exclude a specific class
+ * - -org.package.
- Exclude a specific package hierarchy
+ *
+ * @param serverClasses The serverClasses to set.
+ */
+ public void setServerClasses(String[] serverClasses)
+ {
+ _serverClasses = new ClasspathPattern(serverClasses);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Set the system classes patterns.
+ *
+ * System classes/packages are classes provided by the JVM and that
+ * cannot be replaced by classes of the same name from WEB-INF,
+ * regardless of the value of {@link #setParentLoaderPriority(boolean)}.
+ * A class pattern is a string of one of the forms:
+ * - org.package.Classname
- Match a specific class
+ * - org.package.
- Match a specific package hierarchy
+ * - -org.package.Classname
- Exclude a specific class
+ * - -org.package.
- Exclude a specific package hierarchy
+ *
+ * @param systemClasses The systemClasses to set.
+ */
+ public void setSystemClasses(String[] systemClasses)
+ {
+ _systemClasses = new ClasspathPattern(systemClasses);
+ }
+
+ /* ------------------------------------------------------------ */
+ public void addSystemClass(String classname)
+ {
+ _systemClasses.addPattern(classname);
+ }
+
+ /* ------------------------------------------------------------ */
+ public void addServerClass(String classname)
+ {
+ _serverClasses.addPattern(classname);
+ }
+
+ /* ------------------------------------------------------------ */
+ public void destroy()
+ {
+ if (_baseResource!=null)
+ _baseResource.release();
+ if (_resourceCache!=null)
+ _resourceCache.flushCache();
+ if(_idMap!=null)
+ _idMap.clear();
+ }
+
+ /* ------------------------------------------------------------ */
+ public void setIdMap(Map idMap)
+ {
+ _idMap=idMap;
+ }
+
+ /* ------------------------------------------------------------ */
+ public Map getIdMap()
+ {
+ return _idMap;
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/jetty-overlay-deployer/src/test/java/org/eclipse/jetty/overlays/OverlayServer.java b/jetty-overlay-deployer/src/test/java/org/eclipse/jetty/overlays/OverlayServer.java
new file mode 100644
index 00000000000..b5933e5c2c2
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/java/org/eclipse/jetty/overlays/OverlayServer.java
@@ -0,0 +1,95 @@
+// ========================================================================
+// Copyright (c) 2010-2011 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// 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.opensource.org/licenses/apache2.0.php
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+
+package org.eclipse.jetty.overlays;
+
+import java.io.File;
+
+import org.eclipse.jetty.deploy.DeploymentManager;
+import org.eclipse.jetty.jndi.NamingUtil;
+import org.eclipse.jetty.overlays.OverlayedAppProvider;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.eclipse.jetty.server.handler.DefaultHandler;
+import org.eclipse.jetty.server.handler.HandlerCollection;
+import org.eclipse.jetty.server.handler.RequestLogHandler;
+import org.eclipse.jetty.server.handler.StatisticsHandler;
+import org.eclipse.jetty.server.nio.SelectChannelConnector;
+
+public class OverlayServer
+{
+ public static void main(String[] args) throws Exception
+ {
+ // NamingUtil.__log.setDebugEnabled(true);
+ String jetty_home = System.getProperty("jetty.home","target/test-classes/home");
+ System.setProperty("jetty.home",jetty_home);
+
+ Server server = new Server();
+ server.setAttribute("org.eclipse.jetty.webapp.configuration",
+ new String[]
+ {
+ org.eclipse.jetty.webapp.WebInfConfiguration.class.getCanonicalName(),
+ org.eclipse.jetty.webapp.WebXmlConfiguration.class.getCanonicalName(),
+ org.eclipse.jetty.webapp.MetaInfConfiguration.class.getCanonicalName(),
+ org.eclipse.jetty.webapp.FragmentConfiguration.class.getCanonicalName(),
+ org.eclipse.jetty.plus.webapp.EnvConfiguration.class.getCanonicalName(),
+ org.eclipse.jetty.plus.webapp.PlusConfiguration.class.getCanonicalName(),
+ org.eclipse.jetty.webapp.JettyWebXmlConfiguration.class.getCanonicalName(),
+ org.eclipse.jetty.webapp.TagLibConfiguration.class.getCanonicalName()
+ }
+ );
+
+ // Setup Connectors
+ SelectChannelConnector connector = new SelectChannelConnector();
+ connector.setPort(8080);
+ server.addConnector(connector);
+
+ HandlerCollection handlers = new HandlerCollection();
+ ContextHandlerCollection contexts = new ContextHandlerCollection();
+ RequestLogHandler requestLogHandler = new RequestLogHandler();
+ handlers.setHandlers(new Handler[]
+ { contexts, new DefaultHandler(), requestLogHandler });
+
+ StatisticsHandler stats = new StatisticsHandler();
+ stats.setHandler(handlers);
+
+ server.setHandler(stats);
+
+ // Setup deployers
+ DeploymentManager deployer = new DeploymentManager();
+ deployer.setContexts(contexts);
+ server.addBean(deployer);
+
+ OverlayedAppProvider provider = new OverlayedAppProvider();
+
+ provider.setNodeName("nodeA");
+ provider.setScanDir(new File(jetty_home + "/overlays"));
+ provider.setScanInterval(2);
+
+ deployer.addAppProvider(provider);
+
+ server.setStopAtShutdown(true);
+ server.setSendServerVersion(true);
+
+ // Uncomment to work with JNDI examples
+ // new org.eclipse.jetty.plus.jndi.Transaction(new com.atomikos.icatch.jta.UserTransactionImp());
+
+
+
+
+ server.start();
+ server.join();
+ }
+
+}
diff --git a/jetty-overlay-deployer/src/test/java/org/eclipse/jetty/overlays/OverlayedAppProviderTest.java b/jetty-overlay-deployer/src/test/java/org/eclipse/jetty/overlays/OverlayedAppProviderTest.java
new file mode 100644
index 00000000000..ffcb5a37a28
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/java/org/eclipse/jetty/overlays/OverlayedAppProviderTest.java
@@ -0,0 +1,582 @@
+// ========================================================================
+// Copyright (c) 2010-2011 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// 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.opensource.org/licenses/apache2.0.php
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+
+package org.eclipse.jetty.overlays;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.overlays.OverlayedAppProvider;
+import org.eclipse.jetty.util.IO;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
+
+
+public class OverlayedAppProviderTest
+{
+ File _tmp;
+ File _scan;
+ File _webapps;
+ File _templates;
+ File _nodes;
+ File _instances;
+
+ @Before
+ public void before() throws Exception
+ {
+ _tmp=File.createTempFile("OAPTest",null);
+ if (_tmp.exists())
+ IO.delete(_tmp);
+ _tmp.mkdir();
+
+ _scan = new File(_tmp,"scan").getCanonicalFile();
+ _webapps = new File(_scan,OverlayedAppProvider.WEBAPPS);
+ _templates = new File(_scan,OverlayedAppProvider.TEMPLATES);
+ _nodes = new File(_scan,OverlayedAppProvider.NODES);
+ _instances = new File(_scan,OverlayedAppProvider.INSTANCES);
+ _webapps.mkdirs();
+ _templates.mkdir();
+ _nodes.mkdir();
+ _instances.mkdir();
+ }
+
+ @After
+ public void after() throws Exception
+ {
+ if (_tmp.exists())
+ IO.delete(_tmp);
+ }
+
+ @Test
+ public void testScanForJars() throws Exception
+ {
+ final ConcurrentLinkedQueue> scanned = new ConcurrentLinkedQueue>();
+ OverlayedAppProvider provider = new OverlayedAppProvider()
+ {
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.overlays.OverlayedAppProvider#updateLayers(java.util.List)
+ */
+ @Override
+ protected void updateLayers(Set filenames)
+ {
+ scanned.offer(filenames);
+ }
+ };
+ provider.setScanInterval(0);
+
+
+ provider.setScanDir(_scan);
+ provider.start();
+ provider.scan();
+ provider.scan();
+
+ assertTrue(scanned.isEmpty());
+
+
+ // Check scanning for archives
+ File war = new File(_webapps,"foo-1.2.3.war");
+ touch(war);
+ File template = new File(_templates,"foo=foo-1.2.3.jar");
+ touch(template);
+ File node = new File(_nodes,"nodeA.jar");
+ touch(node);
+ File instance = new File(_instances,"foo=instance.jar");
+ touch(instance);
+
+ provider.scan();
+ provider.scan();
+
+ Set results = scanned.poll();
+ assertTrue(results!=null);
+ assertEquals(4,results.size());
+ assertTrue(results.contains("webapps/foo-1.2.3.war"));
+ assertTrue(results.contains("templates/foo=foo-1.2.3.jar"));
+ assertTrue(results.contains("nodes/nodeA.jar"));
+ assertTrue(results.contains("instances/foo=instance.jar"));
+
+ provider.scan();
+ provider.scan();
+ assertTrue(scanned.isEmpty());
+
+ IO.delete(war);
+ IO.delete(template);
+ IO.delete(node);
+ IO.delete(instance);
+
+ provider.scan();
+ provider.scan();
+ results = scanned.poll();
+ assertTrue(results!=null);
+ assertEquals(4,results.size());
+ assertTrue(results.contains("webapps/foo-1.2.3.war"));
+ assertTrue(results.contains("templates/foo=foo-1.2.3.jar"));
+ assertTrue(results.contains("nodes/nodeA.jar"));
+ assertTrue(results.contains("instances/foo=instance.jar"));
+
+ }
+
+ @Test
+ public void testScanForDirs() throws Exception
+ {
+ final ConcurrentLinkedQueue> scanned = new ConcurrentLinkedQueue>();
+ OverlayedAppProvider provider = new OverlayedAppProvider()
+ {
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.overlays.OverlayedAppProvider#updateLayers(java.util.List)
+ */
+ @Override
+ protected void updateLayers(Set filenames)
+ {
+ scanned.offer(filenames);
+ }
+ };
+ provider.setScanInterval(0);
+
+
+ provider.setScanDir(_scan);
+ provider.start();
+ provider.scan();
+
+ assertTrue(scanned.isEmpty());
+
+
+ // Check scanning for archives
+ File war = new File(_webapps,"foo-1.2.3");
+ war.mkdir();
+ File template = new File(_templates,"foo=foo-1.2.3");
+ template.mkdir();
+ File node = new File(_nodes,"nodeA");
+ node.mkdir();
+ File instance = new File(_instances,"foo=instance");
+ instance.mkdir();
+
+ provider.scan();
+ provider.scan();
+
+ Set results = scanned.poll();
+ assertTrue(results!=null);
+ assertEquals(4,results.size());
+ assertTrue(results.contains("webapps/foo-1.2.3"));
+ assertTrue(results.contains("templates/foo=foo-1.2.3"));
+ assertTrue(results.contains("nodes/nodeA"));
+ assertTrue(results.contains("instances/foo=instance"));
+
+ provider.scan();
+ provider.scan();
+ assertTrue(scanned.isEmpty());
+
+
+ // Touch everything
+ touch(war,"WEB-INF/web.xml");
+ touch(war,"WEB-INF/spring.XML");
+ touch(war,"WEB-INF/other");
+ touch(war,"WEB-INF/lib/bar.jar");
+ touch(war,"WEB-INF/classes/bar.class");
+
+ for (File d : new File[]{template,node,instance})
+ {
+ touch(d,"web.xml");
+ touch(d,"context.xml");
+ touch(d,"other");
+ touch(d,"webapp/WEB-INF/lib/bar.jar");
+ }
+
+ provider.scan();
+ provider.scan();
+ results = scanned.poll();
+ assertTrue(results!=null);
+ assertEquals(4,results.size());
+ assertTrue(results.contains("webapps/foo-1.2.3"));
+ assertTrue(results.contains("templates/foo=foo-1.2.3"));
+ assertTrue(results.contains("nodes/nodeA"));
+ assertTrue(results.contains("instances/foo=instance"));
+
+
+ // Touch xml
+ Thread.sleep(1000); // needed so last modified is different
+ touch(war,"WEB-INF/web.xml");
+ for (File d : new File[]{template,node,instance})
+ touch(d,"context.xml");
+ provider.scan();
+ provider.scan();
+ results = scanned.poll();
+ assertTrue(results!=null);
+ assertEquals(4,results.size());
+ assertTrue(results.contains("webapps/foo-1.2.3"));
+ assertTrue(results.contains("templates/foo=foo-1.2.3"));
+ assertTrue(results.contains("nodes/nodeA"));
+ assertTrue(results.contains("instances/foo=instance"));
+
+ // Touch XML
+ Thread.sleep(1000);
+ touch(war,"WEB-INF/spring.XML");
+ for (File d : new File[]{template,node,instance})
+ touch(d,"webapp/WEB-INF/spring.XML");
+ provider.scan();
+ provider.scan();
+ results = scanned.poll();
+ assertTrue(results!=null);
+ assertEquals(4,results.size());
+ assertTrue(results.contains("webapps/foo-1.2.3"));
+ assertTrue(results.contains("templates/foo=foo-1.2.3"));
+ assertTrue(results.contains("nodes/nodeA"));
+ assertTrue(results.contains("instances/foo=instance"));
+
+
+ // Touch unrelated
+ touch(war,"index.html");
+ for (File d : new File[]{template,node,instance})
+ touch(d,"webapp/index.html");
+ provider.scan();
+ provider.scan();
+ results = scanned.poll();
+ assertEquals(1,results.size());
+ assertTrue(results.contains("webapps/foo-1.2.3"));
+
+
+ // Touch jar
+ Thread.sleep(1000);
+ touch(war,"WEB-INF/lib/bar.jar");
+ for (File d : new File[]{template,node,instance})
+ touch(d,"webapp/WEB-INF/lib/bar.jar");
+ provider.scan();
+ provider.scan();
+ results = scanned.poll();
+ assertTrue(results!=null);
+ assertEquals(4,results.size());
+ assertTrue(results.contains("webapps/foo-1.2.3"));
+ assertTrue(results.contains("templates/foo=foo-1.2.3"));
+ assertTrue(results.contains("nodes/nodeA"));
+ assertTrue(results.contains("instances/foo=instance"));
+
+ // touch other class
+ Thread.sleep(1000);
+ touch(war,"WEB-INF/other.txt");
+ touch(war,"index.html");
+ for (File d : new File[]{template,node,instance})
+ touch(d,"index.html");
+ provider.scan();
+ provider.scan();
+ results = scanned.poll();
+ assertTrue(scanned.isEmpty());
+
+
+ // delete all
+ IO.delete(war);
+ IO.delete(template);
+ IO.delete(node);
+ IO.delete(instance);
+
+ provider.scan();
+ provider.scan();
+ results = scanned.poll();
+ assertTrue(results!=null);
+ assertEquals(4,results.size());
+ assertTrue(results.contains("webapps/foo-1.2.3"));
+ assertTrue(results.contains("templates/foo=foo-1.2.3"));
+ assertTrue(results.contains("nodes/nodeA"));
+ assertTrue(results.contains("instances/foo=instance"));
+
+ }
+
+
+ @Test
+ public void testTriageURI() throws Exception
+ {
+ final BlockingQueue scanned = new LinkedBlockingQueue();
+ OverlayedAppProvider provider = new OverlayedAppProvider()
+ {
+ protected void removeInstance(String name)
+ {
+ scanned.add("removeInstance "+name);
+ }
+ protected Instance loadInstance(String name, File origin)
+ {
+ scanned.add("loadInstance "+name);
+ scanned.add(origin.getAbsolutePath());
+ return null;
+ }
+ protected void removeNode()
+ {
+ scanned.add("removeNode");
+ }
+ protected Node loadNode(File origin)
+ {
+ scanned.add("loadNode");
+ scanned.add(origin.getAbsolutePath());
+ return null;
+ }
+ protected void removeTemplate(String name)
+ {
+ scanned.add("removeTemplate "+name);
+ }
+ protected Template loadTemplate(String name, File origin)
+ {
+ scanned.add("loadTemplate "+name);
+ scanned.add(origin.getAbsolutePath());
+ return null;
+ }
+ protected void removeWebapp(String name)
+ {
+ scanned.add("removeWebapp "+name);
+ }
+ protected Webapp loadWebapp(String name, File origin)
+ {
+ scanned.add("loadWebapp "+name);
+ scanned.add(origin.getAbsolutePath());
+ return null;
+ }
+
+ protected void redeploy()
+ {
+ }
+
+ };
+ provider.setScanInterval(0);
+ provider.setNodeName("nodeA");
+
+
+ provider.setScanDir(_scan);
+ provider.start();
+ provider.scan();
+
+ assertTrue(scanned.isEmpty());
+
+ // Add a war
+ File war = new File(_webapps,"foo-1.2.3.war");
+ touch(war);
+ provider.scan();
+ provider.scan();
+ assertEquals("loadWebapp foo-1.2.3",scanned.poll(1,TimeUnit.SECONDS));
+ assertEquals(war.getAbsolutePath(),scanned.poll(1,TimeUnit.SECONDS));
+
+
+ // Add a template
+ File template = new File(_templates,"foo=foo-1.2.3.jar");
+ touch(template);
+ provider.scan();
+ provider.scan();
+ assertEquals("loadTemplate foo=foo-1.2.3",scanned.poll(1,TimeUnit.SECONDS));
+ assertEquals(template.getAbsolutePath(),scanned.poll(1,TimeUnit.SECONDS));
+
+ // Add a node
+ File nodeA = new File(_nodes,"nodeA.jar");
+ touch(nodeA);
+ provider.scan();
+ provider.scan();
+ assertEquals("loadNode",scanned.poll(1,TimeUnit.SECONDS));
+ assertEquals(nodeA.getAbsolutePath(),scanned.poll(1,TimeUnit.SECONDS));
+
+ // Add another node
+ File nodeB = new File(_nodes,"nodeB.jar");
+ provider.scan();
+ provider.scan();
+ assertTrue(scanned.isEmpty());
+
+ // Add an instance
+ File instance = new File(_instances,"foo=instance.jar");
+ touch(instance);
+ provider.scan();
+ provider.scan();
+ assertEquals("loadInstance foo=instance",scanned.poll(1,TimeUnit.SECONDS));
+ assertEquals(instance.getAbsolutePath(),scanned.poll(1,TimeUnit.SECONDS));
+
+
+ // Add a war dir
+ File warDir = new File(_webapps,"foo-1.2.3");
+ warDir.mkdir();
+ provider.scan();
+ provider.scan();
+ assertEquals("loadWebapp foo-1.2.3",scanned.poll(1,TimeUnit.SECONDS));
+ assertEquals(warDir.getAbsolutePath(),scanned.poll(1,TimeUnit.SECONDS));
+
+
+ // Add a template dir
+ File templateDir = new File(_templates,"foo=foo-1.2.3");
+ templateDir.mkdir();
+ provider.scan();
+ provider.scan();
+ assertEquals("loadTemplate foo=foo-1.2.3",scanned.poll(1,TimeUnit.SECONDS));
+ assertEquals(templateDir.getAbsolutePath(),scanned.poll(1,TimeUnit.SECONDS));
+
+ // Add a node dir
+ File nodeADir = new File(_nodes,"nodeA");
+ nodeADir.mkdir();
+ provider.scan();
+ provider.scan();
+ assertEquals("loadNode",scanned.poll(1,TimeUnit.SECONDS));
+ assertEquals(nodeADir.getAbsolutePath(),scanned.poll(1,TimeUnit.SECONDS));
+
+ // Add another node dir
+ File nodeBDir = new File(_nodes,"nodeB");
+ nodeBDir.mkdir();
+ provider.scan();
+ provider.scan();
+ assertTrue(scanned.isEmpty());
+
+ // Add an instance dir
+ File instanceDir = new File(_instances,"foo=instance");
+ instanceDir.mkdir();
+ provider.scan();
+ provider.scan();
+ assertEquals("loadInstance foo=instance",scanned.poll(1,TimeUnit.SECONDS));
+ assertEquals(instanceDir.getAbsolutePath(),scanned.poll(1,TimeUnit.SECONDS));
+
+
+ // touch archives will be ignored.
+ Thread.sleep(1000);
+ touch(war);
+ touch(template);
+ touch(nodeA);
+ touch(nodeB);
+ touch(instance);
+ provider.scan();
+ provider.scan();
+ assertTrue(scanned.isEmpty());
+
+ // Touch directories
+ touch(warDir,"WEB-INF/web.xml");
+ touch(templateDir,"context.xml");
+ touch(nodeADir,"context.xml");
+ touch(nodeBDir,"context.xml");
+ touch(instanceDir,"context.xml");
+ provider.scan();
+ provider.scan();
+ assertEquals(8,scanned.size());
+ scanned.clear();
+
+ // Remove web dir
+ IO.delete(warDir);
+ provider.scan();
+ provider.scan();
+ assertEquals("removeWebapp foo-1.2.3",scanned.poll(1,TimeUnit.SECONDS));
+ assertEquals("loadWebapp foo-1.2.3",scanned.poll(1,TimeUnit.SECONDS));
+ assertEquals(war.getAbsolutePath(),scanned.poll(1,TimeUnit.SECONDS));
+
+ // Remove template dir
+ IO.delete(templateDir);
+ provider.scan();
+ provider.scan();
+ assertEquals("removeTemplate foo=foo-1.2.3",scanned.poll(1,TimeUnit.SECONDS));
+ assertEquals("loadTemplate foo=foo-1.2.3",scanned.poll(1,TimeUnit.SECONDS));
+ assertEquals(template.getAbsolutePath(),scanned.poll(1,TimeUnit.SECONDS));
+
+ // Remove nodeA dir
+ IO.delete(nodeADir);
+ provider.scan();
+ provider.scan();
+ assertEquals("removeNode",scanned.poll(1,TimeUnit.SECONDS));
+ assertEquals("loadNode",scanned.poll(1,TimeUnit.SECONDS));
+ assertEquals(nodeA.getAbsolutePath(),scanned.poll(1,TimeUnit.SECONDS));
+
+ // Remove nodeB dir
+ IO.delete(nodeBDir);
+ provider.scan();
+ provider.scan();
+ assertTrue(scanned.isEmpty());
+
+
+ // Remove instance dir
+ IO.delete(instanceDir);
+ provider.scan();
+ provider.scan();
+ assertEquals("removeInstance foo=instance",scanned.poll(1,TimeUnit.SECONDS));
+ assertEquals("loadInstance foo=instance",scanned.poll(1,TimeUnit.SECONDS));
+ assertEquals(instance.getAbsolutePath(),scanned.poll(1,TimeUnit.SECONDS));
+
+ // Remove web
+ IO.delete(war);
+ provider.scan();
+ provider.scan();
+ assertEquals("removeWebapp foo-1.2.3",scanned.poll(1,TimeUnit.SECONDS));
+
+ // Remove template
+ IO.delete(template);
+ provider.scan();
+ provider.scan();
+ assertEquals("removeTemplate foo=foo-1.2.3",scanned.poll(1,TimeUnit.SECONDS));
+
+ // Remove nodeA dir
+ IO.delete(nodeA);
+ provider.scan();
+ provider.scan();
+ assertEquals("removeNode",scanned.poll(1,TimeUnit.SECONDS));
+
+ // Remove nodeB dir
+ IO.delete(nodeB);
+ provider.scan();
+ provider.scan();
+ assertTrue(scanned.isEmpty());
+
+ // Remove instance dir
+ IO.delete(instance);
+ provider.scan();
+ provider.scan();
+ assertEquals("removeInstance foo=instance",scanned.poll(1,TimeUnit.SECONDS));
+
+ provider.scan();
+ provider.scan();
+ assertTrue(scanned.isEmpty());
+
+ }
+
+
+ private void touch(File base,String path)
+ {
+ try
+ {
+ File target = new File(new URI(base.toURI().toString()+path));
+ target.getParentFile().mkdirs();
+ touch(target);
+ }
+ catch (URISyntaxException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+
+ private void touch(File file)
+ {
+ try
+ {
+ IO.delete(file);
+ FileOutputStream out = new FileOutputStream(file,false);
+ out.write("Hello
".getBytes());
+ out.close();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/context.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/context.xml
new file mode 100644
index 00000000000..9d5eaea4563
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/context.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ /blue
+
+
+
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/lib/META-INF/MANIFEST.MF b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/lib/META-INF/MANIFEST.MF
new file mode 100644
index 00000000000..e7374eeb7dc
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/lib/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Created-By: 1.6.0_20 (Sun Microsystems Inc.)
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/lib/resources.jar b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/lib/resources.jar
new file mode 100644
index 00000000000..a94e69bb0ea
Binary files /dev/null and b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/lib/resources.jar differ
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/web.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/web.xml
new file mode 100644
index 00000000000..8499944f90b
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/web.xml
@@ -0,0 +1,21 @@
+
+
+
+ Blue Test WebApp
+
+
+ overlay
+ instances/myfoo=blue/web.xml
+
+
+ instance
+ instances/myfoo=blue/web.xml
+
+
+
+
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/webapp/WEB-INF/classes/resourceA.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/webapp/WEB-INF/classes/resourceA.txt
new file mode 100644
index 00000000000..9c74ff39523
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/webapp/WEB-INF/classes/resourceA.txt
@@ -0,0 +1 @@
+instance myfoo=blue WEB-INF classes
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/webapp/WEB-INF/classes/resourceB.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/webapp/WEB-INF/classes/resourceB.txt
new file mode 100644
index 00000000000..9c74ff39523
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/webapp/WEB-INF/classes/resourceB.txt
@@ -0,0 +1 @@
+instance myfoo=blue WEB-INF classes
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/webapp/logo.png b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/webapp/logo.png
new file mode 100644
index 00000000000..21e0d3024a4
Binary files /dev/null and b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/webapp/logo.png differ
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/context.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/context.xml
new file mode 100644
index 00000000000..08ab9458bcb
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/context.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ /green
+
+
+
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/lib/META-INF/MANIFEST.MF b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/lib/META-INF/MANIFEST.MF
new file mode 100644
index 00000000000..e7374eeb7dc
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/lib/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Created-By: 1.6.0_20 (Sun Microsystems Inc.)
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/lib/resources.jar b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/lib/resources.jar
new file mode 100644
index 00000000000..bfce59a00ba
Binary files /dev/null and b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/lib/resources.jar differ
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/web.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/web.xml
new file mode 100644
index 00000000000..ddda5385c62
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/web.xml
@@ -0,0 +1,21 @@
+
+
+
+ Green Test WebApp
+
+
+ overlay
+ instances/myfoo=green/web.xml
+
+
+ instance
+ instances/myfoo=green/web.xml
+
+
+
+
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/webapp/WEB-INF/classes/resourceA.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/webapp/WEB-INF/classes/resourceA.txt
new file mode 100644
index 00000000000..4817b3f04de
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/webapp/WEB-INF/classes/resourceA.txt
@@ -0,0 +1 @@
+instance myfoo=green WEB-INF classes
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/webapp/WEB-INF/classes/resourceB.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/webapp/WEB-INF/classes/resourceB.txt
new file mode 100644
index 00000000000..4817b3f04de
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/webapp/WEB-INF/classes/resourceB.txt
@@ -0,0 +1 @@
+instance myfoo=green WEB-INF classes
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/webapp/logo.png b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/webapp/logo.png
new file mode 100644
index 00000000000..67b04b96b80
Binary files /dev/null and b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/webapp/logo.png differ
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/context.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/context.xml
new file mode 100644
index 00000000000..6494f38bed5
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/context.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ /red
+
+
+
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/lib/META-INF/MANIFEST.MF b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/lib/META-INF/MANIFEST.MF
new file mode 100644
index 00000000000..e7374eeb7dc
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/lib/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Created-By: 1.6.0_20 (Sun Microsystems Inc.)
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/lib/resources.jar b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/lib/resources.jar
new file mode 100644
index 00000000000..994a698f899
Binary files /dev/null and b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/lib/resources.jar differ
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/web.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/web.xml
new file mode 100644
index 00000000000..40927895b9c
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/web.xml
@@ -0,0 +1,21 @@
+
+
+
+ Red Test WebApp
+
+
+ overlay
+ instances/myfoo=red/web.xml
+
+
+ instance
+ instances/myfoo=red/web.xml
+
+
+
+
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/webapp/WEB-INF/classes/resourceA.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/webapp/WEB-INF/classes/resourceA.txt
new file mode 100644
index 00000000000..47d45478c5d
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/webapp/WEB-INF/classes/resourceA.txt
@@ -0,0 +1 @@
+instance myfoo=red WEB-INF classes
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/webapp/WEB-INF/classes/resourceB.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/webapp/WEB-INF/classes/resourceB.txt
new file mode 100644
index 00000000000..47d45478c5d
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/webapp/WEB-INF/classes/resourceB.txt
@@ -0,0 +1 @@
+instance myfoo=red WEB-INF classes
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/webapp/logo.png b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/webapp/logo.png
new file mode 100644
index 00000000000..a121e4a6c11
Binary files /dev/null and b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/webapp/logo.png differ
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/instances/root=root/context.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/root=root/context.xml
new file mode 100644
index 00000000000..47ad8a422f4
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/root=root/context.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/lib/META-INF/MANIFEST.MF b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/lib/META-INF/MANIFEST.MF
new file mode 100644
index 00000000000..e7374eeb7dc
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/lib/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Created-By: 1.6.0_20 (Sun Microsystems Inc.)
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/lib/nodeResources.jar b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/lib/nodeResources.jar
new file mode 100644
index 00000000000..61f42e6bba8
Binary files /dev/null and b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/lib/nodeResources.jar differ
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/web.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/web.xml
new file mode 100644
index 00000000000..27c9960ab99
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/web.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ overlay
+ nodes/nodeA/web.xml
+
+
+ node
+ nodes/nodeA/web.xml
+
+
+
+
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/META-INF/MANIFEST.MF b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/META-INF/MANIFEST.MF
new file mode 100644
index 00000000000..e7374eeb7dc
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Created-By: 1.6.0_20 (Sun Microsystems Inc.)
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/resourceA.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/resourceA.txt
new file mode 100644
index 00000000000..170b5badf10
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/resourceA.txt
@@ -0,0 +1 @@
+node WEB-INF lib
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/resourceB.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/resourceB.txt
new file mode 100644
index 00000000000..170b5badf10
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/resourceB.txt
@@ -0,0 +1 @@
+node WEB-INF lib
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/resourceC.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/resourceC.txt
new file mode 100644
index 00000000000..170b5badf10
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/resourceC.txt
@@ -0,0 +1 @@
+node WEB-INF lib
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/resourceD.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/resourceD.txt
new file mode 100644
index 00000000000..170b5badf10
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/resourceD.txt
@@ -0,0 +1 @@
+node WEB-INF lib
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeB/web.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeB/web.xml
new file mode 100644
index 00000000000..d43dd8b52de
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeB/web.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ overlay
+ nodes/nodeB/web.xml
+
+
+ node
+ nodes/nodeB/web.xml
+
+
+
+
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/lib/META-INF/MANIFEST.MF b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/lib/META-INF/MANIFEST.MF
new file mode 100644
index 00000000000..e7374eeb7dc
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/lib/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Created-By: 1.6.0_20 (Sun Microsystems Inc.)
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/lib/templateResources.jar b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/lib/templateResources.jar
new file mode 100644
index 00000000000..4fb379cb222
Binary files /dev/null and b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/lib/templateResources.jar differ
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/template.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/template.xml
new file mode 100644
index 00000000000..ec8d822e962
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/template.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ false
+
+ true
+ 10000000
+ 1000
+ 64000000
+
+
+
+ bogus
+ application/bogon
+
+
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/web.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/web.xml
new file mode 100644
index 00000000000..44c7878918b
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/web.xml
@@ -0,0 +1,21 @@
+
+
+
+ Templated Test WebApp
+
+
+ overlay
+ templates/myfoo/web.xml
+
+
+ template
+ templates/myfoo/web.xml
+
+
+
+
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceA.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceA.txt
new file mode 100644
index 00000000000..9e00184c94d
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceA.txt
@@ -0,0 +1 @@
+template WEB-INF classes
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceB.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceB.txt
new file mode 100644
index 00000000000..9e00184c94d
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceB.txt
@@ -0,0 +1 @@
+template WEB-INF classes
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceC.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceC.txt
new file mode 100644
index 00000000000..9e00184c94d
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceC.txt
@@ -0,0 +1 @@
+template WEB-INF classes
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceD.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceD.txt
new file mode 100644
index 00000000000..9e00184c94d
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceD.txt
@@ -0,0 +1 @@
+template WEB-INF classes
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceE.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceE.txt
new file mode 100644
index 00000000000..9e00184c94d
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceE.txt
@@ -0,0 +1 @@
+template WEB-INF classes
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceF.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceF.txt
new file mode 100644
index 00000000000..9e00184c94d
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceF.txt
@@ -0,0 +1 @@
+template WEB-INF classes
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/index.html b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/index.html
new file mode 100644
index 00000000000..afebff7ad8f
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/index.html
@@ -0,0 +1,10 @@
+
+Template foo webapp
+
+
+Should see index.jsp instead of this!!!
+
+
+Red,
+Blue,
+Green
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/index.jsp b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/index.jsp
new file mode 100644
index 00000000000..569338d2448
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/index.jsp
@@ -0,0 +1,54 @@
+
+<%@page import="java.io.BufferedReader"%>
+<%@page import="java.io.InputStreamReader"%>
+<%@page import="java.util.Enumeration"%>
+
<%=application.getServletContextName()%>
+
+
+
+Red,
+Blue,
+Green
+
+
+
Overlays
+webapp=<%=application.getInitParameter("webapp")%>
+template=<%=application.getInitParameter("template")%>
+node=<%=application.getInitParameter("node")%>
+instance=<%=application.getInitParameter("instance")%>
+
+Init Parameters
+<%
+Enumeration e=application.getInitParameterNames();
+while (e.hasMoreElements())
+{
+ String name=e.nextElement().toString();
+ String value=application.getInitParameter(name);
+ out.println(name+": "+value+"
");
+}
+%>
+Attributes
+<%
+e=application.getAttributeNames();
+while (e.hasMoreElements())
+{
+ String name=e.nextElement().toString();
+ String value=String.valueOf(application.getAttribute(name));
+ out.println(name+": "+value+"
");
+}
+%>
+Resources
+<%
+ClassLoader loader = Thread.currentThread().getContextClassLoader();
+%>
+resourceA.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceA.txt").openStream())).readLine()%>
+resourceB.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceB.txt").openStream())).readLine()%>
+resourceC.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceC.txt").openStream())).readLine()%>
+resourceD.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceD.txt").openStream())).readLine()%>
+resourceE.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceE.txt").openStream())).readLine()%>
+resourceF.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceF.txt").openStream())).readLine()%>
+resourceG.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceG.txt").openStream())).readLine()%>
+resourceH.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceH.txt").openStream())).readLine()%>
+resourceI.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceI.txt").openStream())).readLine()%>
+resourceJ.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceJ.txt").openStream())).readLine()%>
+resourceK.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceK.txt").openStream())).readLine()%>
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/logo.png b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/logo.png
new file mode 100644
index 00000000000..b633447fff5
Binary files /dev/null and b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/logo.png differ
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webdefault.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webdefault.xml
new file mode 100644
index 00000000000..6a116536993
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webdefault.xml
@@ -0,0 +1,482 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Default web.xml file.
+ This file is applied to a Web application before it's own WEB_INF/web.xml file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ default
+ org.eclipse.jetty.servlet.DefaultServlet
+
+ aliases
+ false
+
+
+ acceptRanges
+ true
+
+
+ dirAllowed
+ true
+
+
+ welcomeServlets
+ false
+
+
+ redirectWelcome
+ false
+
+
+ maxCacheSize
+ 256000000
+
+
+ maxCachedFileSize
+ 10000000
+
+
+ maxCachedFiles
+ 2048
+
+
+ gzip
+ true
+
+
+ useFileMappedBuffer
+ true
+
+
+ resourceCache
+ org.eclipse.jetty.server.ResourceCache
+
+
+ 0
+
+
+
+ default
+ /
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ jsp
+ org.apache.jasper.servlet.JspServlet
+
+ logVerbosityLevel
+ DEBUG
+
+
+ fork
+ false
+
+
+ xpoweredBy
+ false
+
+
+ 0
+
+
+
+ jsp
+ *.jsp
+ *.jspf
+ *.jspx
+ *.xsp
+ *.JSP
+ *.JSPF
+ *.JSPX
+ *.XSP
+
+
+
+
+
+ 30
+
+
+
+
+
+
+
+
+
+
+
+
+ index.jsp
+ index.html
+ index.htm
+
+
+
+
+
+ ar
+ ISO-8859-6
+
+
+ be
+ ISO-8859-5
+
+
+ bg
+ ISO-8859-5
+
+
+ ca
+ ISO-8859-1
+
+
+ cs
+ ISO-8859-2
+
+
+ da
+ ISO-8859-1
+
+
+ de
+ ISO-8859-1
+
+
+ el
+ ISO-8859-7
+
+
+ en
+ ISO-8859-1
+
+
+ es
+ ISO-8859-1
+
+
+ et
+ ISO-8859-1
+
+
+ fi
+ ISO-8859-1
+
+
+ fr
+ ISO-8859-1
+
+
+ hr
+ ISO-8859-2
+
+
+ hu
+ ISO-8859-2
+
+
+ is
+ ISO-8859-1
+
+
+ it
+ ISO-8859-1
+
+
+ iw
+ ISO-8859-8
+
+
+ ja
+ Shift_JIS
+
+
+ ko
+ EUC-KR
+
+
+ lt
+ ISO-8859-2
+
+
+ lv
+ ISO-8859-2
+
+
+ mk
+ ISO-8859-5
+
+
+ nl
+ ISO-8859-1
+
+
+ no
+ ISO-8859-1
+
+
+ pl
+ ISO-8859-2
+
+
+ pt
+ ISO-8859-1
+
+
+ ro
+ ISO-8859-2
+
+
+ ru
+ ISO-8859-5
+
+
+ sh
+ ISO-8859-5
+
+
+ sk
+ ISO-8859-2
+
+
+ sl
+ ISO-8859-2
+
+
+ sq
+ ISO-8859-2
+
+
+ sr
+ ISO-8859-5
+
+
+ sv
+ ISO-8859-1
+
+
+ tr
+ ISO-8859-9
+
+
+ uk
+ ISO-8859-5
+
+
+ zh
+ GB2312
+
+
+ zh_TW
+ Big5
+
+
+
+
+
+ Disable TRACE
+ /
+ TRACE
+
+
+
+
+
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/root/context.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/root/context.xml
new file mode 100644
index 00000000000..2ed2154da5f
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/root/context.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ /
+
+
+
+
+ - index.html
+
+
+ max-age=3600,public
+
+
+
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/templates/root/webapp/index.html b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/root/webapp/index.html
new file mode 100644
index 00000000000..67ed39608fc
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/root/webapp/index.html
@@ -0,0 +1,7 @@
+
+Cloudtide Deployer Demo
+
+
+Red,
+Blue,
+Green
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/classes/resourceA.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/classes/resourceA.txt
new file mode 100644
index 00000000000..c8ca259d738
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/classes/resourceA.txt
@@ -0,0 +1 @@
+webapp WEB-INF classes
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/classes/resourceB.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/classes/resourceB.txt
new file mode 100644
index 00000000000..c8ca259d738
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/classes/resourceB.txt
@@ -0,0 +1 @@
+webapp WEB-INF classes
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/classes/resourceC.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/classes/resourceC.txt
new file mode 100644
index 00000000000..c8ca259d738
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/classes/resourceC.txt
@@ -0,0 +1 @@
+webapp WEB-INF classes
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/classes/resourceD.txt b/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/classes/resourceD.txt
new file mode 100644
index 00000000000..c8ca259d738
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/classes/resourceD.txt
@@ -0,0 +1 @@
+webapp WEB-INF classes
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/lib/webappResources.jar b/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/lib/webappResources.jar
new file mode 100644
index 00000000000..4c450a43e30
Binary files /dev/null and b/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/lib/webappResources.jar differ
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/web.xml b/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/web.xml
new file mode 100644
index 00000000000..5be798c0b25
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/web.xml
@@ -0,0 +1,21 @@
+
+
+
+ Test WebApp
+
+
+ overlay
+ webapps/foo/WEB-INF/web.xml
+
+
+ webapp
+ webapps/foo-INF/web.xml
+
+
+
+
+
diff --git a/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/index.html b/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/index.html
new file mode 100644
index 00000000000..a98e28bfce9
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/index.html
@@ -0,0 +1,2 @@
+
+
foo webapp
diff --git a/pom.xml b/pom.xml
index 8b361b9fc21..162d4396c86 100644
--- a/pom.xml
+++ b/pom.xml
@@ -310,6 +310,7 @@
jetty-policy
jetty-start
jetty-nested
+ jetty-overlay-deployer
test-continuation
test-continuation-jetty6
test-jetty-servlet