Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Wilkins2011-04-06 09:30:00 +0000
committerGreg Wilkins2011-04-06 09:30:00 +0000
commit9a415c93f4bc98b7fec1189f6d1283a813244e40 (patch)
tree93becfb4bd2cc5a96ddb631cc11be4d6e20447d9
parent9fe29756a7d465563d4c9cab101bfa5d664075c7 (diff)
downloadorg.eclipse.jetty.project-9a415c93f4bc98b7fec1189f6d1283a813244e40.tar.gz
org.eclipse.jetty.project-9a415c93f4bc98b7fec1189f6d1283a813244e40.tar.xz
org.eclipse.jetty.project-9a415c93f4bc98b7fec1189f6d1283a813244e40.zip
341992 Overlayed context deployer
git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@2983 7e9141cc-0065-0410-87d8-b60c137991c4
-rw-r--r--VERSION.txt1
-rw-r--r--jetty-overlay-deployer/logs/jtrac.log24
-rw-r--r--jetty-overlay-deployer/pom.xml65
-rw-r--r--jetty-overlay-deployer/src/main/assembly/config.xml18
-rw-r--r--jetty-overlay-deployer/src/main/config/etc/jetty-cloudtide-deployer.xml24
-rw-r--r--jetty-overlay-deployer/src/main/config/overlays/instances/README.TXT23
-rw-r--r--jetty-overlay-deployer/src/main/config/overlays/nodes/README.TXT16
-rw-r--r--jetty-overlay-deployer/src/main/config/overlays/templates/README.TXT24
-rw-r--r--jetty-overlay-deployer/src/main/config/overlays/tmp/README.TXT1
-rw-r--r--jetty-overlay-deployer/src/main/config/overlays/webapps/README.TXT6
-rw-r--r--jetty-overlay-deployer/src/main/java/org/eclipse/jetty/overlays/OverlayedAppProvider.java1335
-rw-r--r--jetty-overlay-deployer/src/main/java/org/eclipse/jetty/overlays/TemplateContext.java285
-rw-r--r--jetty-overlay-deployer/src/test/java/org/eclipse/jetty/overlays/OverlayServer.java95
-rw-r--r--jetty-overlay-deployer/src/test/java/org/eclipse/jetty/overlays/OverlayedAppProviderTest.java582
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/context.xml16
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/lib/META-INF/MANIFEST.MF3
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/lib/resources.jarbin0 -> 921 bytes
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/web.xml21
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/webapp/WEB-INF/classes/resourceA.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/webapp/WEB-INF/classes/resourceB.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/webapp/logo.pngbin0 -> 2128 bytes
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/context.xml16
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/lib/META-INF/MANIFEST.MF3
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/lib/resources.jarbin0 -> 925 bytes
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/web.xml21
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/webapp/WEB-INF/classes/resourceA.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/webapp/WEB-INF/classes/resourceB.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/webapp/logo.pngbin0 -> 11230 bytes
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/context.xml16
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/lib/META-INF/MANIFEST.MF3
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/lib/resources.jarbin0 -> 917 bytes
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/web.xml21
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/webapp/WEB-INF/classes/resourceA.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/webapp/WEB-INF/classes/resourceB.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/webapp/logo.pngbin0 -> 4499 bytes
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/instances/root=root/context.xml12
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/lib/META-INF/MANIFEST.MF3
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/lib/nodeResources.jarbin0 -> 1119 bytes
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/web.xml19
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/META-INF/MANIFEST.MF3
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/resourceA.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/resourceB.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/resourceC.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/webapp/WEB-INF/classes/resourceD.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeB/web.xml19
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/lib/META-INF/MANIFEST.MF3
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/lib/templateResources.jarbin0 -> 1409 bytes
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/template.xml18
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/web.xml21
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceA.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceB.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceC.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceD.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceE.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/WEB-INF/classes/resourceF.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/index.html10
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/index.jsp54
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/logo.pngbin0 -> 2001 bytes
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webdefault.xml482
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/templates/root/context.xml18
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/templates/root/webapp/index.html7
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/classes/resourceA.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/classes/resourceB.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/classes/resourceC.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/classes/resourceD.txt1
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/lib/webappResources.jarbin0 -> 901 bytes
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/web.xml21
-rw-r--r--jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/index.html2
-rw-r--r--pom.xml1
69 files changed, 3332 insertions, 0 deletions
diff --git a/VERSION.txt b/VERSION.txt
index 3bfbdbab33..4df8f5c87d 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 0000000000..8c4c974e45
--- /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 0000000000..43bc76f14f
--- /dev/null
+++ b/jetty-overlay-deployer/pom.xml
@@ -0,0 +1,65 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-project</artifactId>
+ <version>7.4.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>jetty-overlay-deployer</artifactId>
+ <name>Jetty :: Overlay Deployer</name>
+ <description>Overlayed deployer</description>
+ <properties>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>${basedir}/src/main/assembly/config.xml</descriptor>
+ </descriptors>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-deploy</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-plus</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.transaction</groupId>
+ <artifactId>jta</artifactId>
+ <version>1.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <reporting>
+ </reporting>
+</project>
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 0000000000..b9b398e630
--- /dev/null
+++ b/jetty-overlay-deployer/src/main/assembly/config.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<assembly>
+ <id>config</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>jar</format>
+ </formats>
+ <fileSets>
+ <fileSet>
+ <directory>src/main/config</directory>
+ <outputDirectory></outputDirectory>
+ <includes>
+ <include>etc/**</include>
+ <include>overlays/**</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+</assembly>
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 0000000000..135548dd02
--- /dev/null
+++ b/jetty-overlay-deployer/src/main/config/etc/jetty-cloudtide-deployer.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+
+<!-- =============================================================== -->
+<!-- Add a ContextProvider to the deployment manager -->
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+<!-- This scans the webapps directory for war files and directories -->
+<!-- to deploy. -->
+<!-- This configuration must be used with jetty-deploy.xml, which -->
+<!-- creates the deployment manager instance -->
+<!-- =============================================================== -->
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+ <Ref id="DeploymentManager">
+ <Call name="addAppProvider">
+ <Arg>
+ <New id="OverlayedAppProvider" class="org.eclipse.jetty.overlays.OverlayedAppProvider">
+ <Set name="scanDir"><Property name="jetty.home" default="." />/cloudtide</Set>
+ <Set name="nodeName"><Property name="node.name" default="nonameNode"/></Set>
+ <Set name="scanInterval">1</Set>
+ </New>
+ </Arg>
+ </Call>
+ </Ref>
+</Configure>
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 0000000000..013fe90eeb
--- /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 0000000000..188d8e1224
--- /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 0000000000..ea554204c4
--- /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 0000000000..675410eb68
--- /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 0000000000..f709dce94d
--- /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 0000000000..5bac82655a
--- /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
+ * <p>
+ * This {@link AppProvider} implementation can deploy either {@link WebAppContext}s or plain
+ * {@link ContextHandler}s that are assembled from a series of overlays:
+ * <dl>
+ * <dt>webapp</dt><dd>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.</dd>
+ * <dt>template</dt><dd>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)</dd>
+ * <dt>node</dt><dd>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.).</dd>
+ * <dt>instance</dt><dd>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).</dd>
+ * </dl>
+ * <p>
+ * Each overlays may provide the following files and subdirectories:<dl>
+ * <dt>lib</dt>
+ * <dd>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.</dd>
+ *
+ * <dt>context.xml</dt>
+ * <dd>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.</dd>
+ *
+ * <dt>template.xml</dt>
+ * <dd>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.</dd>
+ *
+ * <dt>webdefaul.xml</dt>
+ * <dd>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.</dd>
+ *
+ * <dt>web.xml</dt>
+ * <dd>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.</dd>
+ *
+ * <dt>webapp</dt>
+ * <dd>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.</dd>
+ *
+ * </dl>
+ * <p>
+ * 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.
+ * <p>
+ * 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.).
+ * <p>
+ * 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<Pattern> __scanPatterns = new ArrayList<Pattern>();
+
+ static
+ {
+ List<String> regexes = new ArrayList<String>();
+
+ 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<String,Webapp> _webapps = new HashMap<String,Webapp>();
+ private final Map<String,Template> _templates = new HashMap<String,Template>();
+ private final Map<String,Instance> _instances = new HashMap<String,Instance>();
+ private final Map<String,OverlayedApp> _deployed = new HashMap<String,OverlayedApp>();
+ private final Map<String,TemplateContext> _shared = new HashMap<String, TemplateContext>();
+ private boolean _copydir=false;
+ private DeploymentManager _deploymentManager;
+ private ConfigurationManager _configurationManager;
+ private String _serverID="Server";
+ private final Set<Layer> _removedLayers = new HashSet<Layer>();
+ private Timer _sessionScavenger = new Timer();
+
+ private final Scanner _scanner = new Scanner();
+ private final Scanner.BulkListener _listener = new Scanner.BulkListener()
+ {
+ public void filesChanged(List<String> filenames) throws Exception
+ {
+ __log.debug("Changed {}",filenames);
+
+ Set<String> changes = new HashSet<String>();
+ 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.
+ * <p>
+ * 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<URL> libs = new ArrayList<URL>();
+ 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<String,Object> idMap = new HashMap<String,Object>();
+ 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<Resource> 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<String, Object> 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<URL> libs = new ArrayList<URL>();
+ 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<Resource> bases = new ArrayList<Resource>();
+ 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<String,Object> idMap = new HashMap<String,Object>();
+ 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<File> 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<String> 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<String,Template> templates = new ConcurrentHashMap<String,Template>();
+
+ // 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<String> deployed = new HashSet<String>();
+ List<Instance> deploy = new ArrayList<Instance>();
+
+ 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<String> undeploy = new ArrayList<String>();
+ 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<String> sharedKeys = new HashSet<String>(_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<Resource> getLayeredResources(String path, Layer... layers)
+ {
+ List<Resource> resources = new ArrayList<Resource>();
+ 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 0000000000..8925bfb07b
--- /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.
+ * <p>
+ * This class is configured by the template.xml files and is used to control the
+ * shared resource cache and classloader.
+ * <p>
+ * 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<String, Object> _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.
+ * <p>
+ * 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:<dl>
+ * <dt>org.package.Classname</dt><dd>Match a specific class</dd>
+ * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd>
+ * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd>
+ * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd>
+ * </dl>
+ * @param serverClasses The serverClasses to set.
+ */
+ public void setServerClasses(String[] serverClasses)
+ {
+ _serverClasses = new ClasspathPattern(serverClasses);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Set the system classes patterns.
+ * <p>
+ * 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:<dl>
+ * <dt>org.package.Classname</dt><dd>Match a specific class</dd>
+ * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd>
+ * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd>
+ * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd>
+ * </dl>
+ * @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<String, Object> idMap)
+ {
+ _idMap=idMap;
+ }
+
+ /* ------------------------------------------------------------ */
+ public Map<String, Object> 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 0000000000..b5933e5c2c
--- /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 0000000000..ffcb5a37a2
--- /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<Set<String>> scanned = new ConcurrentLinkedQueue<Set<String>>();
+ OverlayedAppProvider provider = new OverlayedAppProvider()
+ {
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.overlays.OverlayedAppProvider#updateLayers(java.util.List)
+ */
+ @Override
+ protected void updateLayers(Set<String> 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<String> 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<Set<String>> scanned = new ConcurrentLinkedQueue<Set<String>>();
+ OverlayedAppProvider provider = new OverlayedAppProvider()
+ {
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.overlays.OverlayedAppProvider#updateLayers(java.util.List)
+ */
+ @Override
+ protected void updateLayers(Set<String> 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<String> 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<String> scanned = new LinkedBlockingQueue<String>();
+ 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("<h1>Hello</h1>".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 0000000000..9d5eaea456
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/context.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+
+ <Set name="contextPath">/blue</Set>
+
+ <!--
+ <Set name="virtualHosts">
+ <Array type="String">
+ <Item>blue.myVirtualDomain.com</Item>
+ </Array>
+ </Set>
+ -->
+
+</Configure>
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 0000000000..e7374eeb7d
--- /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 0000000000..a94e69bb0e
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/lib/resources.jar
Binary files 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 0000000000..8499944f90
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/web.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app
+ xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ version="2.5">
+
+ <display-name>Blue Test WebApp</display-name>
+
+ <context-param>
+ <param-name>overlay</param-name>
+ <param-value>instances/myfoo=blue/web.xml</param-value>
+ </context-param>
+ <context-param>
+ <param-name>instance</param-name>
+ <param-value>instances/myfoo=blue/web.xml</param-value>
+ </context-param>
+
+</web-app>
+
+
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 0000000000..9c74ff3952
--- /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 0000000000..9c74ff3952
--- /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 0000000000..21e0d3024a
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=blue/webapp/logo.png
Binary files 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 0000000000..08ab9458bc
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/context.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+
+ <Set name="contextPath">/green</Set>
+
+ <!--
+ <Set name="virtualHosts">
+ <Array type="String">
+ <Item>green.myVirtualDomain.com</Item>
+ </Array>
+ </Set>
+ -->
+
+</Configure>
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 0000000000..e7374eeb7d
--- /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 0000000000..bfce59a00b
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/lib/resources.jar
Binary files 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 0000000000..ddda5385c6
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/web.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app
+ xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ version="2.5">
+
+ <display-name>Green Test WebApp</display-name>
+
+ <context-param>
+ <param-name>overlay</param-name>
+ <param-value>instances/myfoo=green/web.xml</param-value>
+ </context-param>
+ <context-param>
+ <param-name>instance</param-name>
+ <param-value>instances/myfoo=green/web.xml</param-value>
+ </context-param>
+
+</web-app>
+
+
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 0000000000..4817b3f04d
--- /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 0000000000..4817b3f04d
--- /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 0000000000..67b04b96b8
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=green/webapp/logo.png
Binary files 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 0000000000..6494f38bed
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/context.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+
+ <Set name="contextPath">/red</Set>
+
+ <!--
+ <Set name="virtualHosts">
+ <Array type="String">
+ <Item>red.myVirtualDomain.com</Item>
+ </Array>
+ </Set>
+ -->
+
+</Configure>
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 0000000000..e7374eeb7d
--- /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 0000000000..994a698f89
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/lib/resources.jar
Binary files 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 0000000000..40927895b9
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/web.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app
+ xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ version="2.5">
+
+ <display-name>Red Test WebApp</display-name>
+
+ <context-param>
+ <param-name>overlay</param-name>
+ <param-value>instances/myfoo=red/web.xml</param-value>
+ </context-param>
+ <context-param>
+ <param-name>instance</param-name>
+ <param-value>instances/myfoo=red/web.xml</param-value>
+ </context-param>
+
+</web-app>
+
+
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 0000000000..47d45478c5
--- /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 0000000000..47d45478c5
--- /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 0000000000..a121e4a6c1
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/myfoo=red/webapp/logo.png
Binary files 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 0000000000..47ad8a422f
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/instances/root=root/context.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+
+<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
+ <!--
+ <Set name="virtualHosts">
+ <Array type="String">
+ <Item>red.myVirtualDomain.com</Item>
+ </Array>
+ </Set>
+ -->
+</Configure>
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 0000000000..e7374eeb7d
--- /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 0000000000..61f42e6bba
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/lib/nodeResources.jar
Binary files 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 0000000000..27c9960ab9
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeA/web.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app
+ xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ version="2.5">
+
+ <context-param>
+ <param-name>overlay</param-name>
+ <param-value>nodes/nodeA/web.xml</param-value>
+ </context-param>
+ <context-param>
+ <param-name>node</param-name>
+ <param-value>nodes/nodeA/web.xml</param-value>
+ </context-param>
+
+</web-app>
+
+
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 0000000000..e7374eeb7d
--- /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 0000000000..170b5badf1
--- /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 0000000000..170b5badf1
--- /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 0000000000..170b5badf1
--- /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 0000000000..170b5badf1
--- /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 0000000000..d43dd8b52d
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/nodes/nodeB/web.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app
+ xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ version="2.5">
+
+ <context-param>
+ <param-name>overlay</param-name>
+ <param-value>nodes/nodeB/web.xml</param-value>
+ </context-param>
+ <context-param>
+ <param-name>node</param-name>
+ <param-value>nodes/nodeB/web.xml</param-value>
+ </context-param>
+
+</web-app>
+
+
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 0000000000..e7374eeb7d
--- /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 0000000000..4fb379cb22
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/lib/templateResources.jar
Binary files 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 0000000000..ec8d822e96
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/template.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+
+<Configure class="org.eclipse.jetty.overlays.TemplateContext">
+ <Set name="parentLoaderPriority" type="boolean">false</Set>
+ <Get name="resourceCache">
+ <Set name="useFileMappedBuffer">true</Set>
+ <Set name="maxCachedFileSize">10000000</Set>
+ <Set name="maxCachedFiles">1000</Set>
+ <Set name="maxCacheSize">64000000</Set>
+ </Get>
+ <Get name="mimeTypes">
+ <Call name="addMimeMapping">
+ <Arg>bogus</Arg>
+ <Arg>application/bogon</Arg>
+ </Call>
+ </Get>
+</Configure>
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 0000000000..44c7878918
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/web.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app
+ xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ version="2.5">
+
+ <display-name>Templated Test WebApp</display-name>
+
+ <context-param>
+ <param-name>overlay</param-name>
+ <param-value>templates/myfoo/web.xml</param-value>
+ </context-param>
+ <context-param>
+ <param-name>template</param-name>
+ <param-value>templates/myfoo/web.xml</param-value>
+ </context-param>
+
+</web-app>
+
+
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 0000000000..9e00184c94
--- /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 0000000000..9e00184c94
--- /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 0000000000..9e00184c94
--- /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 0000000000..9e00184c94
--- /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 0000000000..9e00184c94
--- /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 0000000000..9e00184c94
--- /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 0000000000..afebff7ad8
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/index.html
@@ -0,0 +1,10 @@
+
+<h1>Template foo webapp</h1>
+<img src=logo.png></img>
+
+Should see index.jsp instead of this!!!
+
+<p>
+<a href="/red">Red</a>,
+<a href="/blue">Blue</a>,
+<a href="/green">Green</a>
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 0000000000..569338d244
--- /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"%>
+<h1><%=application.getServletContextName()%></h1>
+<img src=logo.png></img>
+
+<p>
+<a href="/red">Red</a>,
+<a href="/blue">Blue</a>,
+<a href="/green">Green</a>
+<p>
+
+<h3>Overlays</h3>
+webapp=<%=application.getInitParameter("webapp")%><br/>
+template=<%=application.getInitParameter("template")%><br/>
+node=<%=application.getInitParameter("node")%><br/>
+instance=<%=application.getInitParameter("instance")%><br/>
+
+<h3>Init Parameters</h3>
+<%
+Enumeration e=application.getInitParameterNames();
+while (e.hasMoreElements())
+{
+ String name=e.nextElement().toString();
+ String value=application.getInitParameter(name);
+ out.println(name+": "+value+"<br/>");
+}
+%>
+<h3>Attributes</h3>
+<%
+e=application.getAttributeNames();
+while (e.hasMoreElements())
+{
+ String name=e.nextElement().toString();
+ String value=String.valueOf(application.getAttribute(name));
+ out.println(name+": "+value+"<br/>");
+}
+%>
+<h3>Resources</h3>
+<%
+ClassLoader loader = Thread.currentThread().getContextClassLoader();
+%>
+resourceA.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceA.txt").openStream())).readLine()%><br/>
+resourceB.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceB.txt").openStream())).readLine()%><br/>
+resourceC.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceC.txt").openStream())).readLine()%><br/>
+resourceD.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceD.txt").openStream())).readLine()%><br/>
+resourceE.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceE.txt").openStream())).readLine()%><br/>
+resourceF.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceF.txt").openStream())).readLine()%><br/>
+resourceG.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceG.txt").openStream())).readLine()%><br/>
+resourceH.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceH.txt").openStream())).readLine()%><br/>
+resourceI.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceI.txt").openStream())).readLine()%><br/>
+resourceJ.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceJ.txt").openStream())).readLine()%><br/>
+resourceK.txt=<%=new BufferedReader(new InputStreamReader(loader.getResource("resourceK.txt").openStream())).readLine()%><br/>
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 0000000000..b633447fff
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webapp/logo.png
Binary files 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 0000000000..6a11653699
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/myfoo=foo/webdefault.xml
@@ -0,0 +1,482 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+ <!-- ===================================================================== -->
+ <!-- This file contains the default descriptor for web applications. -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- The intent of this descriptor is to include jetty specific or common -->
+ <!-- configuration for all webapps. If a context has a webdefault.xml -->
+ <!-- descriptor, it is applied before the contexts own web.xml file -->
+ <!-- -->
+ <!-- A context may be assigned a default descriptor by: -->
+ <!-- + Calling WebApplicationContext.setDefaultsDescriptor -->
+ <!-- + Passed an arg to addWebApplications -->
+ <!-- -->
+ <!-- This file is used both as the resource within the jetty.jar (which is -->
+ <!-- used as the default if no explicit defaults descriptor is set) and it -->
+ <!-- is copied to the etc directory of the Jetty distro and explicitly -->
+ <!-- by the jetty.xml file. -->
+ <!-- -->
+ <!-- ===================================================================== -->
+<web-app
+ xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ metadata-complete="true"
+ version="2.5"
+>
+
+ <description>
+ Default web.xml file.
+ This file is applied to a Web application before it's own WEB_INF/web.xml file
+ </description>
+
+
+ <!-- ==================================================================== -->
+ <!-- Context params to control Session Cookies -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!--
+ UNCOMMENT TO ACTIVATE <context-param> <param-name>org.eclipse.jetty.servlet.SessionDomain</param-name> <param-value>127.0.0.1</param-value> </context-param> <context-param>
+ <param-name>org.eclipse.jetty.servlet.SessionPath</param-name> <param-value>/</param-value> </context-param> <context-param> <param-name>org.eclipse.jetty.servlet.MaxAge</param-name>
+ <param-value>-1</param-value> </context-param>
+ -->
+
+ <!-- ==================================================================== -->
+ <!-- The default servlet. -->
+ <!-- This servlet, normally mapped to /, provides the handling for static -->
+ <!-- content, OPTIONS and TRACE methods for the context. -->
+ <!-- The following initParameters are supported: -->
+ <!--
+ * acceptRanges If true, range requests and responses are
+ * supported
+ *
+ * dirAllowed If true, directory listings are returned if no
+ * welcome file is found. Else 403 Forbidden.
+ *
+ * welcomeServlets If true, attempt to dispatch to welcome files
+ * that are servlets, but only after no matching static
+ * resources could be found. If false, then a welcome
+ * file must exist on disk. If "exact", then exact
+ * servlet matches are supported without an existing file.
+ * Default is true.
+ *
+ * This must be false if you want directory listings,
+ * but have index.jsp in your welcome file list.
+ *
+ * redirectWelcome If true, welcome files are redirected rather than
+ * forwarded to.
+ *
+ * gzip If set to true, then static content will be served as
+ * gzip content encoded if a matching resource is
+ * found ending with ".gz"
+ *
+ * resourceBase Set to replace the context resource base
+ *
+ * resourceCache If set, this is a context attribute name, which the servlet
+ * will use to look for a shared ResourceCache instance.
+ *
+ * relativeResourceBase
+ * Set with a pathname relative to the base of the
+ * servlet context root. Useful for only serving static content out
+ * of only specific subdirectories.
+ *
+ * aliases If True, aliases of resources are allowed (eg. symbolic
+ * links and caps variations). May bypass security constraints.
+ *
+ * maxCacheSize The maximum total size of the cache or 0 for no cache.
+ * maxCachedFileSize The maximum size of a file to cache
+ * maxCachedFiles The maximum number of files to cache
+ *
+ * useFileMappedBuffer
+ * If set to true, it will use mapped file buffer to serve static content
+ * when using NIO connector. Setting this value to false means that
+ * a direct buffer will be used instead of a mapped file buffer.
+ * By default, this is set to true.
+ *
+ * cacheControl If set, all static content will have this value set as the cache-control
+ * header.
+ -->
+
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <servlet>
+ <servlet-name>default</servlet-name>
+ <servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
+ <init-param>
+ <param-name>aliases</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ <init-param>
+ <param-name>acceptRanges</param-name>
+ <param-value>true</param-value>
+ </init-param>
+ <init-param>
+ <param-name>dirAllowed</param-name>
+ <param-value>true</param-value>
+ </init-param>
+ <init-param>
+ <param-name>welcomeServlets</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ <init-param>
+ <param-name>redirectWelcome</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ <init-param>
+ <param-name>maxCacheSize</param-name>
+ <param-value>256000000</param-value>
+ </init-param>
+ <init-param>
+ <param-name>maxCachedFileSize</param-name>
+ <param-value>10000000</param-value>
+ </init-param>
+ <init-param>
+ <param-name>maxCachedFiles</param-name>
+ <param-value>2048</param-value>
+ </init-param>
+ <init-param>
+ <param-name>gzip</param-name>
+ <param-value>true</param-value>
+ </init-param>
+ <init-param>
+ <param-name>useFileMappedBuffer</param-name>
+ <param-value>true</param-value>
+ </init-param>
+ <init-param>
+ <param-name>resourceCache</param-name>
+ <param-value>org.eclipse.jetty.server.ResourceCache</param-value>
+ </init-param>
+ <!--
+ <init-param>
+ <param-name>cacheControl</param-name>
+ <param-value>max-age=3600,public</param-value>
+ </init-param>
+ -->
+ <load-on-startup>0</load-on-startup>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>default</servlet-name>
+ <url-pattern>/</url-pattern>
+ </servlet-mapping>
+
+
+ <!-- ==================================================================== -->
+ <!-- JSP Servlet -->
+ <!-- This is the jasper JSP servlet from the jakarta project -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- The JSP page compiler and execution servlet, which is the mechanism -->
+ <!-- used by Glassfish to support JSP pages. Traditionally, this servlet -->
+ <!-- is mapped to URL patterh "*.jsp". This servlet supports the -->
+ <!-- following initialization parameters (default values are in square -->
+ <!-- brackets): -->
+ <!-- -->
+ <!-- checkInterval If development is false and reloading is true, -->
+ <!-- background compiles are enabled. checkInterval -->
+ <!-- is the time in seconds between checks to see -->
+ <!-- if a JSP page needs to be recompiled. [300] -->
+ <!-- -->
+ <!-- compiler Which compiler Ant should use to compile JSP -->
+ <!-- pages. See the Ant documenation for more -->
+ <!-- information. [javac] -->
+ <!-- -->
+ <!-- classdebuginfo Should the class file be compiled with -->
+ <!-- debugging information? [true] -->
+ <!-- -->
+ <!-- classpath What class path should I use while compiling -->
+ <!-- generated servlets? [Created dynamically -->
+ <!-- based on the current web application] -->
+ <!-- Set to ? to make the container explicitly set -->
+ <!-- this parameter. -->
+ <!-- -->
+ <!-- development Is Jasper used in development mode (will check -->
+ <!-- for JSP modification on every access)? [true] -->
+ <!-- -->
+ <!-- enablePooling Determines whether tag handler pooling is -->
+ <!-- enabled [true] -->
+ <!-- -->
+ <!-- fork Tell Ant to fork compiles of JSP pages so that -->
+ <!-- a separate JVM is used for JSP page compiles -->
+ <!-- from the one Tomcat is running in. [true] -->
+ <!-- -->
+ <!-- ieClassId The class-id value to be sent to Internet -->
+ <!-- Explorer when using <jsp:plugin> tags. -->
+ <!-- [clsid:8AD9C840-044E-11D1-B3E9-00805F499D93] -->
+ <!-- -->
+ <!-- javaEncoding Java file encoding to use for generating java -->
+ <!-- source files. [UTF-8] -->
+ <!-- -->
+ <!-- keepgenerated Should we keep the generated Java source code -->
+ <!-- for each page instead of deleting it? [true] -->
+ <!-- -->
+ <!-- logVerbosityLevel The level of detailed messages to be produced -->
+ <!-- by this servlet. Increasing levels cause the -->
+ <!-- generation of more messages. Valid values are -->
+ <!-- FATAL, ERROR, WARNING, INFORMATION, and DEBUG. -->
+ <!-- [WARNING] -->
+ <!-- -->
+ <!-- mappedfile Should we generate static content with one -->
+ <!-- print statement per input line, to ease -->
+ <!-- debugging? [false] -->
+ <!-- -->
+ <!-- -->
+ <!-- reloading Should Jasper check for modified JSPs? [true] -->
+ <!-- -->
+ <!-- suppressSmap Should the generation of SMAP info for JSR45 -->
+ <!-- debugging be suppressed? [false] -->
+ <!-- -->
+ <!-- dumpSmap Should the SMAP info for JSR45 debugging be -->
+ <!-- dumped to a file? [false] -->
+ <!-- False if suppressSmap is true -->
+ <!-- -->
+ <!-- scratchdir What scratch directory should we use when -->
+ <!-- compiling JSP pages? [default work directory -->
+ <!-- for the current web application] -->
+ <!-- -->
+ <!-- tagpoolMaxSize The maximum tag handler pool size [5] -->
+ <!-- -->
+ <!-- xpoweredBy Determines whether X-Powered-By response -->
+ <!-- header is added by generated servlet [false] -->
+ <!-- -->
+ <!-- If you wish to use Jikes to compile JSP pages: -->
+ <!-- Set the init parameter "compiler" to "jikes". Define -->
+ <!-- the property "-Dbuild.compiler.emacs=true" when starting Jetty -->
+ <!-- to cause Jikes to emit error messages in a format compatible with -->
+ <!-- Jasper. -->
+ <!-- If you get an error reporting that jikes can't use UTF-8 encoding, -->
+ <!-- try setting the init parameter "javaEncoding" to "ISO-8859-1". -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <servlet
+ id="jsp"
+ >
+ <servlet-name>jsp</servlet-name>
+ <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
+ <init-param>
+ <param-name>logVerbosityLevel</param-name>
+ <param-value>DEBUG</param-value>
+ </init-param>
+ <init-param>
+ <param-name>fork</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ <init-param>
+ <param-name>xpoweredBy</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ <!--
+ <init-param>
+ <param-name>classpath</param-name>
+ <param-value>?</param-value>
+ </init-param>
+ -->
+ <load-on-startup>0</load-on-startup>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>jsp</servlet-name>
+ <url-pattern>*.jsp</url-pattern>
+ <url-pattern>*.jspf</url-pattern>
+ <url-pattern>*.jspx</url-pattern>
+ <url-pattern>*.xsp</url-pattern>
+ <url-pattern>*.JSP</url-pattern>
+ <url-pattern>*.JSPF</url-pattern>
+ <url-pattern>*.JSPX</url-pattern>
+ <url-pattern>*.XSP</url-pattern>
+ </servlet-mapping>
+
+
+ <!-- ==================================================================== -->
+ <session-config>
+ <session-timeout>30</session-timeout>
+ </session-config>
+
+ <!-- ==================================================================== -->
+ <!-- Default MIME mappings -->
+ <!-- The default MIME mappings are provided by the mime.properties -->
+ <!-- resource in the org.eclipse.jetty.server.jar file. Additional or modified -->
+ <!-- mappings may be specified here -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- UNCOMMENT TO ACTIVATE
+ <mime-mapping>
+ <extension>mysuffix</extension>
+ <mime-type>mymime/type</mime-type>
+ </mime-mapping>
+ -->
+
+ <!-- ==================================================================== -->
+ <welcome-file-list>
+ <welcome-file>index.jsp</welcome-file>
+ <welcome-file>index.html</welcome-file>
+ <welcome-file>index.htm</welcome-file>
+ </welcome-file-list>
+
+ <!-- ==================================================================== -->
+ <locale-encoding-mapping-list>
+ <locale-encoding-mapping>
+ <locale>ar</locale>
+ <encoding>ISO-8859-6</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>be</locale>
+ <encoding>ISO-8859-5</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>bg</locale>
+ <encoding>ISO-8859-5</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>ca</locale>
+ <encoding>ISO-8859-1</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>cs</locale>
+ <encoding>ISO-8859-2</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>da</locale>
+ <encoding>ISO-8859-1</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>de</locale>
+ <encoding>ISO-8859-1</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>el</locale>
+ <encoding>ISO-8859-7</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>en</locale>
+ <encoding>ISO-8859-1</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>es</locale>
+ <encoding>ISO-8859-1</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>et</locale>
+ <encoding>ISO-8859-1</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>fi</locale>
+ <encoding>ISO-8859-1</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>fr</locale>
+ <encoding>ISO-8859-1</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>hr</locale>
+ <encoding>ISO-8859-2</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>hu</locale>
+ <encoding>ISO-8859-2</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>is</locale>
+ <encoding>ISO-8859-1</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>it</locale>
+ <encoding>ISO-8859-1</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>iw</locale>
+ <encoding>ISO-8859-8</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>ja</locale>
+ <encoding>Shift_JIS</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>ko</locale>
+ <encoding>EUC-KR</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>lt</locale>
+ <encoding>ISO-8859-2</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>lv</locale>
+ <encoding>ISO-8859-2</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>mk</locale>
+ <encoding>ISO-8859-5</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>nl</locale>
+ <encoding>ISO-8859-1</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>no</locale>
+ <encoding>ISO-8859-1</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>pl</locale>
+ <encoding>ISO-8859-2</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>pt</locale>
+ <encoding>ISO-8859-1</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>ro</locale>
+ <encoding>ISO-8859-2</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>ru</locale>
+ <encoding>ISO-8859-5</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>sh</locale>
+ <encoding>ISO-8859-5</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>sk</locale>
+ <encoding>ISO-8859-2</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>sl</locale>
+ <encoding>ISO-8859-2</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>sq</locale>
+ <encoding>ISO-8859-2</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>sr</locale>
+ <encoding>ISO-8859-5</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>sv</locale>
+ <encoding>ISO-8859-1</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>tr</locale>
+ <encoding>ISO-8859-9</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>uk</locale>
+ <encoding>ISO-8859-5</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>zh</locale>
+ <encoding>GB2312</encoding>
+ </locale-encoding-mapping>
+ <locale-encoding-mapping>
+ <locale>zh_TW</locale>
+ <encoding>Big5</encoding>
+ </locale-encoding-mapping>
+ </locale-encoding-mapping-list>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Disable TRACE</web-resource-name>
+ <url-pattern>/</url-pattern>
+ <http-method>TRACE</http-method>
+ </web-resource-collection>
+ <auth-constraint/>
+ </security-constraint>
+
+</web-app>
+
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 0000000000..2ed2154da5
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/root/context.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.eclipse.org/configure.dtd">
+
+
+<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
+ <Set name="contextPath">/</Set>
+ <Set name="handler">
+ <New class="org.eclipse.jetty.server.handler.ResourceHandler">
+ <Set name="welcomeFiles">
+ <Array type="String">
+ <Item>index.html</Item>
+ </Array>
+ </Set>
+ <Set name="cacheControl">max-age=3600,public</Set>
+ </New>
+ </Set>
+</Configure>
+
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 0000000000..67ed39608f
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/templates/root/webapp/index.html
@@ -0,0 +1,7 @@
+
+<h1>Cloudtide Deployer Demo</h1>
+
+<p>
+<a href="/red">Red</a>,
+<a href="/blue">Blue</a>,
+<a href="/green">Green</a>
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 0000000000..c8ca259d73
--- /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 0000000000..c8ca259d73
--- /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 0000000000..c8ca259d73
--- /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 0000000000..c8ca259d73
--- /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 0000000000..4c450a43e3
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/lib/webappResources.jar
Binary files 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 0000000000..5be798c0b2
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/WEB-INF/web.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app
+ xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ version="2.5">
+
+ <display-name>Test WebApp</display-name>
+
+ <context-param>
+ <param-name>overlay</param-name>
+ <param-value>webapps/foo/WEB-INF/web.xml</param-value>
+ </context-param>
+ <context-param>
+ <param-name>webapp</param-name>
+ <param-value>webapps/foo-INF/web.xml</param-value>
+ </context-param>
+
+</web-app>
+
+
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 0000000000..a98e28bfce
--- /dev/null
+++ b/jetty-overlay-deployer/src/test/resources/home/overlays/webapps/foo/index.html
@@ -0,0 +1,2 @@
+
+<h1>foo webapp</h1>
diff --git a/pom.xml b/pom.xml
index 8b361b9fc2..162d4396c8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -310,6 +310,7 @@
<module>jetty-policy</module>
<module>jetty-start</module>
<module>jetty-nested</module>
+ <module>jetty-overlay-deployer</module>
<module>test-continuation</module>
<module>test-continuation-jetty6</module>
<module>test-jetty-servlet</module>

Back to the top