From 8146aa7e7902b5d9c3ac97836c2f325c339792e2 Mon Sep 17 00:00:00 2001
From: markt
Date: Fri, 19 Nov 2010 17:18:04 +0000
Subject: [PATCH] Add the final component of parallel deployment
git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1036949 13f79535-47bb-0310-9956-ffa450edef68
---
.../apache/catalina/connector/CoyoteAdapter.java | 159 +++++++----
.../apache/catalina/connector/MapperListener.java | 29 +-
.../org/apache/tomcat/util/http/mapper/Mapper.java | 300 ++++++++++++++-------
.../tomcat/util/http/mapper/MappingData.java | 2 +
.../apache/tomcat/util/http/mapper/TestMapper.java | 38 +--
webapps/docs/changelog.xml | 8 +
webapps/docs/config/context.xml | 100 +++++--
webapps/docs/tomcat-docs.xsl | 22 ++
8 files changed, 454 insertions(+), 204 deletions(-)
diff --git a/java/org/apache/catalina/connector/CoyoteAdapter.java b/java/org/apache/catalina/connector/CoyoteAdapter.java
index bd9972615..082b9c4d0 100644
--- a/java/org/apache/catalina/connector/CoyoteAdapter.java
+++ b/java/org/apache/catalina/connector/CoyoteAdapter.java
@@ -570,62 +570,89 @@ public class CoyoteAdapter implements Adapter {
//reset mapping data, should prolly be done elsewhere
request.getMappingData().recycle();
}
- connector.getMapper().map(serverName, decodedURI,
- request.getMappingData());
- request.setContext((Context) request.getMappingData().context);
- request.setWrapper((Wrapper) request.getMappingData().wrapper);
+
+ boolean mapRequired = true;
+ String version = null;
+
+ while (mapRequired) {
+ if (version != null) {
+ // Once we have a version - that is it
+ mapRequired = false;
+ }
+ // This will map the the latest version by default
+ connector.getMapper().map(serverName, decodedURI, version,
+ request.getMappingData());
+ request.setContext((Context) request.getMappingData().context);
+ request.setWrapper((Wrapper) request.getMappingData().wrapper);
+
+ // Single contextVersion therefore no possibility of remap
+ if (request.getMappingData().contexts == null) {
+ mapRequired = false;
+ }
- // Filter trace method
- if (!connector.getAllowTrace()
- && req.method().equalsIgnoreCase("TRACE")) {
- Wrapper wrapper = request.getWrapper();
- String header = null;
- if (wrapper != null) {
- String[] methods = wrapper.getServletMethods();
- if (methods != null) {
- for (int i=0; i 0; i--) {
+ Context ctxt = (Context) objs[i - 1];
+ if (ctxt.getManager().findSession(sessionID) != null) {
+ // Was the correct context already mapped?
+ if (ctxt.equals(request.getMappingData().context)) {
+ mapRequired = false;
+ } else {
+ // Set version so second time through mapping the
+ // correct context is found
+ version = ctxt.getWebappVersion();
+ // Reset mapping
+ request.getMappingData().recycle();
+ break;
+ }
+ }
+ }
+ if (version == null) {
+ // No matching context found. No need to re-map
+ mapRequired = false;
+ }
+ }
}
+
}
// Possible redirect
@@ -651,9 +678,33 @@ public class CoyoteAdapter implements Adapter {
return false;
}
- // Finally look for session ID in cookies and SSL session
- parseSessionCookiesId(req, request);
- parseSessionSslId(request);
+ // Filter trace method
+ if (!connector.getAllowTrace()
+ && req.method().equalsIgnoreCase("TRACE")) {
+ Wrapper wrapper = request.getWrapper();
+ String header = null;
+ if (wrapper != null) {
+ String[] methods = wrapper.getServletMethods();
+ if (methods != null) {
+ for (int i=0; i mappedHost.contextList.nesting) {
mappedHost.contextList.nesting = slashCount;
}
- Context[] newContexts = new Context[contexts.length + 1];
- Context newContext = new Context();
- newContext.name = path;
- newContext.object = context;
- newContext.welcomeResources = welcomeResources;
- newContext.resources = resources;
- if (insertMap(contexts, newContexts, newContext)) {
- mappedHost.contextList.contexts = newContexts;
+ int pos2 = find(contexts, path);
+ if (pos2 < 0 || !path.equals(contexts[pos2].name)) {
+ Context newContext = new Context();
+ newContext.name = path;
+ Context[] newContexts = new Context[contexts.length + 1];
+ if (insertMap(contexts, newContexts, newContext)) {
+ mappedHost.contextList.contexts = newContexts;
+ }
+ pos2 = find(newContexts, path);
+ }
+
+ Context mappedContext = mappedHost.contextList.contexts[pos2];
+
+ ContextVersion[] contextVersions = mappedContext.versions;
+ ContextVersion[] newContextVersions =
+ new ContextVersion[contextVersions.length + 1];
+ ContextVersion newContextVersion = new ContextVersion();
+ newContextVersion.path = path;
+ newContextVersion.name = version;
+ newContextVersion.object = context;
+ newContextVersion.welcomeResources = welcomeResources;
+ newContextVersion.resources = resources;
+ if (insertMap(contextVersions, newContextVersions, newContextVersion)) {
+ mappedContext.versions = newContextVersions;
}
}
}
@@ -248,8 +265,10 @@ public final class Mapper {
*
* @param hostName Virtual host name this context belongs to
* @param path Context path
+ * @param version Context version
*/
- public void removeContext(String hostName, String path) {
+ public void removeContextVersion(String hostName, String path,
+ String version) {
Host[] hosts = this.hosts;
int pos = find(hosts, hostName);
if (pos < 0) {
@@ -259,18 +278,35 @@ public final class Mapper {
if (host.name.equals(hostName)) {
synchronized (host) {
Context[] contexts = host.contextList.contexts;
- if( contexts.length == 0 ){
+ if (contexts.length == 0 ){
return;
}
- Context[] newContexts = new Context[contexts.length - 1];
- if (removeMap(contexts, newContexts, path)) {
- host.contextList.contexts = newContexts;
- // Recalculate nesting
- host.contextList.nesting = 0;
- for (int i = 0; i < newContexts.length; i++) {
- int slashCount = slashCount(newContexts[i].name);
- if (slashCount > host.contextList.nesting) {
- host.contextList.nesting = slashCount;
+
+ int pos2 = find(contexts, path);
+ if (pos2 < 0 || !path.equals(contexts[pos2].name)) {
+ return;
+ }
+ Context context = contexts[pos2];
+
+ ContextVersion[] contextVersions = context.versions;
+ ContextVersion[] newContextVersions =
+ new ContextVersion[contextVersions.length - 1];
+ if (removeMap(contextVersions, newContextVersions, version)) {
+ context.versions = newContextVersions;
+
+ if (context.versions.length == 0) {
+ // Remove the context
+ Context[] newContexts = new Context[contexts.length -1];
+ if (removeMap(contexts, newContexts, path)) {
+ host.contextList.contexts = newContexts;
+ // Recalculate nesting
+ host.contextList.nesting = 0;
+ for (int i = 0; i < newContexts.length; i++) {
+ int slashCount = slashCount(newContexts[i].name);
+ if (slashCount > host.contextList.nesting) {
+ host.contextList.nesting = slashCount;
+ }
+ }
}
}
}
@@ -279,8 +315,8 @@ public final class Mapper {
}
- public void addWrapper(String hostName, String contextPath, String path,
- Object wrapper, boolean jspWildCard,
+ public void addWrapper(String hostName, String contextPath, String version,
+ String path, Object wrapper, boolean jspWildCard,
boolean resourceOnly) {
Host[] hosts = this.hosts;
int pos = find(hosts, hostName);
@@ -291,13 +327,24 @@ public final class Mapper {
if (host.name.equals(hostName)) {
Context[] contexts = host.contextList.contexts;
int pos2 = find(contexts, contextPath);
- if( pos2<0 ) {
+ if (pos2 < 0) {
log.error("No context found: " + contextPath );
return;
}
Context context = contexts[pos2];
if (context.name.equals(contextPath)) {
- addWrapper(context, path, wrapper, jspWildCard, resourceOnly);
+ ContextVersion[] contextVersions = context.versions;
+ int pos3 = find(contextVersions, version);
+ if( pos3<0 ) {
+ log.error("No context version found: " + contextPath + " " +
+ version);
+ return;
+ }
+ ContextVersion contextVersion = contextVersions[pos3];
+ if (contextVersion.name.equals(version)) {
+ addWrapper(contextVersion, path, wrapper, jspWildCard,
+ resourceOnly);
+ }
}
}
}
@@ -320,8 +367,8 @@ public final class Mapper {
* resource to be present (such as a JSP)
* and the mapping path contains a wildcard; false otherwise
*/
- protected void addWrapper(Context context, String path, Object wrapper,
- boolean jspWildCard, boolean resourceOnly) {
+ protected void addWrapper(ContextVersion context, String path,
+ Object wrapper, boolean jspWildCard, boolean resourceOnly) {
synchronized (context) {
Wrapper newWrapper = new Wrapper();
@@ -386,7 +433,7 @@ public final class Mapper {
* @param path Wrapper mapping
*/
public void removeWrapper
- (String hostName, String contextPath, String path) {
+ (String hostName, String contextPath, String version, String path) {
Host[] hosts = this.hosts;
int pos = find(hosts, hostName);
if (pos < 0) {
@@ -401,12 +448,20 @@ public final class Mapper {
}
Context context = contexts[pos2];
if (context.name.equals(contextPath)) {
- removeWrapper(context, path);
+ ContextVersion[] contextVersions = context.versions;
+ int pos3 = find(contextVersions, version);
+ if( pos3<0 ) {
+ return;
+ }
+ ContextVersion contextVersion = contextVersions[pos3];
+ if (contextVersion.name.equals(version)) {
+ removeWrapper(contextVersion, path);
+ }
}
}
}
- protected void removeWrapper(Context context, String path) {
+ protected void removeWrapper(ContextVersion context, String path) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("mapper.removeWrapper", context.name, path));
@@ -473,7 +528,7 @@ public final class Mapper {
* @param welcomeFile
*/
public void addWelcomeFile(String hostName, String contextPath,
- String welcomeFile) {
+ String version, String welcomeFile) {
Host[] hosts = this.hosts;
int pos = find(hosts, hostName);
if (pos < 0) {
@@ -483,18 +538,28 @@ public final class Mapper {
if (host.name.equals(hostName)) {
Context[] contexts = host.contextList.contexts;
int pos2 = find(contexts, contextPath);
- if( pos2<0 ) {
+ if (pos2 < 0) {
log.error("No context found: " + contextPath );
return;
}
Context context = contexts[pos2];
if (context.name.equals(contextPath)) {
- int len = context.welcomeResources.length + 1;
- String[] newWelcomeResources = new String[len];
- System.arraycopy(context.welcomeResources, 0,
- newWelcomeResources, 0, len - 1);
- newWelcomeResources[len - 1] = welcomeFile;
- context.welcomeResources = newWelcomeResources;
+ ContextVersion[] contextVersions = context.versions;
+ int pos3 = find(contextVersions, version);
+ if( pos3<0 ) {
+ log.error("No context version found: " + contextPath + " " +
+ version);
+ return;
+ }
+ ContextVersion contextVersion = contextVersions[pos3];
+ if (contextVersion.name.equals(version)) {
+ int len = contextVersion.welcomeResources.length + 1;
+ String[] newWelcomeResources = new String[len];
+ System.arraycopy(contextVersion.welcomeResources, 0,
+ newWelcomeResources, 0, len - 1);
+ newWelcomeResources[len - 1] = welcomeFile;
+ contextVersion.welcomeResources = newWelcomeResources;
+ }
}
}
}
@@ -508,7 +573,7 @@ public final class Mapper {
* @param welcomeFile
*/
public void removeWelcomeFile(String hostName, String contextPath,
- String welcomeFile) {
+ String version, String welcomeFile) {
Host[] hosts = this.hosts;
int pos = find(hosts, hostName);
if (pos < 0) {
@@ -518,29 +583,39 @@ public final class Mapper {
if (host.name.equals(hostName)) {
Context[] contexts = host.contextList.contexts;
int pos2 = find(contexts, contextPath);
- if( pos2<0 ) {
+ if (pos2 < 0) {
log.error("No context found: " + contextPath );
return;
}
Context context = contexts[pos2];
if (context.name.equals(contextPath)) {
- int match = -1;
- for (int i = 0; i < context.welcomeResources.length; i++) {
- if (welcomeFile.equals(context.welcomeResources[i])) {
- match = i;
- break;
- }
+ ContextVersion[] contextVersions = context.versions;
+ int pos3 = find(contextVersions, version);
+ if( pos3<0 ) {
+ log.error("No context version found: " + contextPath + " " +
+ version);
+ return;
}
- if (match > -1) {
- int len = context.welcomeResources.length - 1;
- String[] newWelcomeResources = new String[len];
- System.arraycopy(context.welcomeResources, 0,
- newWelcomeResources, 0, match);
- if (match < len) {
- System.arraycopy(context.welcomeResources, match + 1,
- newWelcomeResources, match, len - match);
+ ContextVersion contextVersion = contextVersions[pos3];
+ if (contextVersion.name.equals(version)) {
+ int match = -1;
+ for (int i = 0; i < contextVersion.welcomeResources.length; i++) {
+ if (welcomeFile.equals(contextVersion.welcomeResources[i])) {
+ match = i;
+ break;
+ }
+ }
+ if (match > -1) {
+ int len = contextVersion.welcomeResources.length - 1;
+ String[] newWelcomeResources = new String[len];
+ System.arraycopy(contextVersion.welcomeResources, 0,
+ newWelcomeResources, 0, match);
+ if (match < len) {
+ System.arraycopy(contextVersion.welcomeResources, match + 1,
+ newWelcomeResources, match, len - match);
+ }
+ contextVersion.welcomeResources = newWelcomeResources;
}
- context.welcomeResources = newWelcomeResources;
}
}
}
@@ -553,7 +628,8 @@ public final class Mapper {
* @param hostName
* @param contextPath
*/
- public void clearWelcomeFiles(String hostName, String contextPath) {
+ public void clearWelcomeFiles(String hostName, String contextPath,
+ String version) {
Host[] hosts = this.hosts;
int pos = find(hosts, hostName);
if (pos < 0) {
@@ -563,13 +639,23 @@ public final class Mapper {
if (host.name.equals(hostName)) {
Context[] contexts = host.contextList.contexts;
int pos2 = find(contexts, contextPath);
- if( pos2<0 ) {
+ if (pos2 < 0) {
log.error("No context found: " + contextPath );
return;
}
Context context = contexts[pos2];
if (context.name.equals(contextPath)) {
- context.welcomeResources = new String[0];
+ ContextVersion[] contextVersions = context.versions;
+ int pos3 = find(contextVersions, version);
+ if( pos3<0 ) {
+ log.error("No context version found: " + contextPath + " " +
+ version);
+ return;
+ }
+ ContextVersion contextVersion = contextVersions[pos3];
+ if (contextVersion.name.equals(version)) {
+ contextVersion.welcomeResources = new String[0];
+ }
}
}
}
@@ -583,7 +669,7 @@ public final class Mapper {
* @param mappingData This structure will contain the result of the mapping
* operation
*/
- public void map(MessageBytes host, MessageBytes uri,
+ public void map(MessageBytes host, MessageBytes uri, String version,
MappingData mappingData)
throws Exception {
@@ -592,7 +678,8 @@ public final class Mapper {
}
host.toChars();
uri.toChars();
- internalMap(host.getCharChunk(), uri.getCharChunk(), mappingData);
+ internalMap(host.getCharChunk(), uri.getCharChunk(), version,
+ mappingData);
}
@@ -623,13 +710,14 @@ public final class Mapper {
* Map the specified URI.
*/
private final void internalMap(CharChunk host, CharChunk uri,
- MappingData mappingData)
- throws Exception {
+ String version, MappingData mappingData) throws Exception {
uri.setLimit(-1);
Context[] contexts = null;
Context context = null;
+ ContextVersion contextVersion = null;
+
int nesting = 0;
// Virtual host mapping
@@ -695,14 +783,39 @@ public final class Mapper {
context = contexts[pos];
}
if (context != null) {
- mappingData.context = context.object;
mappingData.contextPath.setString(context.name);
}
}
+ if (context != null) {
+ ContextVersion[] contextVersions = context.versions;
+ int versionCount = contextVersions.length;
+ if (versionCount > 1) {
+ Object[] contextObjects = new Object[contextVersions.length];
+ for (int i = 0; i < contextObjects.length; i++) {
+ contextObjects[i] = contextVersions[i].object;
+ }
+ mappingData.contexts = contextObjects;
+ }
+
+ if (version == null) {
+ // Return the latest version
+ contextVersion = contextVersions[versionCount - 1];
+ } else {
+ int pos = find(contextVersions, version);
+ if (pos < 0 || !contextVersions[pos].name.equals(version)) {
+ // Return the latest version
+ contextVersion = contextVersions[versionCount - 1];
+ } else {
+ contextVersion = contextVersions[pos];
+ }
+ }
+ mappingData.context = contextVersion.object;
+ }
+
// Wrapper mapping
- if ((context != null) && (mappingData.wrapper == null)) {
- internalMapWrapper(context, uri, mappingData);
+ if ((contextVersion != null) && (mappingData.wrapper == null)) {
+ internalMapWrapper(contextVersion, uri, mappingData);
}
}
@@ -711,7 +824,8 @@ public final class Mapper {
/**
* Wrapper mapping.
*/
- private final void internalMapWrapper(Context context, CharChunk path,
+ private final void internalMapWrapper(ContextVersion contextVersion,
+ CharChunk path,
MappingData mappingData)
throws Exception {
@@ -720,7 +834,7 @@ public final class Mapper {
int servletPath = pathOffset;
boolean noServletPath = false;
- int length = context.name.length();
+ int length = contextVersion.path.length();
if (length != (pathEnd - pathOffset)) {
servletPath = pathOffset + length;
} else {
@@ -734,14 +848,14 @@ public final class Mapper {
path.setOffset(servletPath);
// Rule 1 -- Exact Match
- Wrapper[] exactWrappers = context.exactWrappers;
+ Wrapper[] exactWrappers = contextVersion.exactWrappers;
internalMapExactWrapper(exactWrappers, path, mappingData);
// Rule 2 -- Prefix Match
boolean checkJspWelcomeFiles = false;
- Wrapper[] wildcardWrappers = context.wildcardWrappers;
+ Wrapper[] wildcardWrappers = contextVersion.wildcardWrappers;
if (mappingData.wrapper == null) {
- internalMapWildcardWrapper(wildcardWrappers, context.nesting,
+ internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting,
path, mappingData);
if (mappingData.wrapper != null && mappingData.jspWildCard) {
char[] buf = path.getBuffer();
@@ -774,7 +888,7 @@ public final class Mapper {
}
// Rule 3 -- Extension Match
- Wrapper[] extensionWrappers = context.extensionWrappers;
+ Wrapper[] extensionWrappers = contextVersion.extensionWrappers;
if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
internalMapExtensionWrapper(extensionWrappers, path, mappingData,
true);
@@ -788,12 +902,12 @@ public final class Mapper {
checkWelcomeFiles = (buf[pathEnd - 1] == '/');
}
if (checkWelcomeFiles) {
- for (int i = 0; (i < context.welcomeResources.length)
+ for (int i = 0; (i < contextVersion.welcomeResources.length)
&& (mappingData.wrapper == null); i++) {
path.setOffset(pathOffset);
path.setEnd(pathEnd);
- path.append(context.welcomeResources[i], 0,
- context.welcomeResources[i].length());
+ path.append(contextVersion.welcomeResources[i], 0,
+ contextVersion.welcomeResources[i].length());
path.setOffset(servletPath);
// Rule 4a -- Welcome resources processing for exact macth
@@ -802,18 +916,18 @@ public final class Mapper {
// Rule 4b -- Welcome resources processing for prefix match
if (mappingData.wrapper == null) {
internalMapWildcardWrapper
- (wildcardWrappers, context.nesting,
+ (wildcardWrappers, contextVersion.nesting,
path, mappingData);
}
// Rule 4c -- Welcome resources processing
// for physical folder
if (mappingData.wrapper == null
- && context.resources != null) {
+ && contextVersion.resources != null) {
Object file = null;
String pathStr = path.toString();
try {
- file = context.resources.lookup(pathStr);
+ file = contextVersion.resources.lookup(pathStr);
} catch(NamingException nex) {
// Swallow not found, since this is normal
}
@@ -821,9 +935,9 @@ public final class Mapper {
internalMapExtensionWrapper(extensionWrappers, path,
mappingData, true);
if (mappingData.wrapper == null
- && context.defaultWrapper != null) {
+ && contextVersion.defaultWrapper != null) {
mappingData.wrapper =
- context.defaultWrapper.object;
+ contextVersion.defaultWrapper.object;
mappingData.requestPath.setChars
(path.getBuffer(), path.getStart(),
path.getLength());
@@ -857,12 +971,12 @@ public final class Mapper {
checkWelcomeFiles = (buf[pathEnd - 1] == '/');
}
if (checkWelcomeFiles) {
- for (int i = 0; (i < context.welcomeResources.length)
+ for (int i = 0; (i < contextVersion.welcomeResources.length)
&& (mappingData.wrapper == null); i++) {
path.setOffset(pathOffset);
path.setEnd(pathEnd);
- path.append(context.welcomeResources[i], 0,
- context.welcomeResources[i].length());
+ path.append(contextVersion.welcomeResources[i], 0,
+ contextVersion.welcomeResources[i].length());
path.setOffset(servletPath);
internalMapExtensionWrapper(extensionWrappers, path,
mappingData, false);
@@ -876,8 +990,8 @@ public final class Mapper {
// Rule 7 -- Default servlet
if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
- if (context.defaultWrapper != null) {
- mappingData.wrapper = context.defaultWrapper.object;
+ if (contextVersion.defaultWrapper != null) {
+ mappingData.wrapper = contextVersion.defaultWrapper.object;
mappingData.requestPath.setChars
(path.getBuffer(), path.getStart(), path.getLength());
mappingData.wrapperPath.setChars
@@ -885,11 +999,11 @@ public final class Mapper {
}
// Redirection to a folder
char[] buf = path.getBuffer();
- if (context.resources != null && buf[pathEnd -1 ] != '/') {
+ if (contextVersion.resources != null && buf[pathEnd -1 ] != '/') {
Object file = null;
String pathStr = path.toString();
try {
- file = context.resources.lookup(pathStr);
+ file = contextVersion.resources.lookup(pathStr);
} catch(NamingException nex) {
// Swallow, since someone else handles the 404
}
@@ -1371,9 +1485,13 @@ public final class Mapper {
// ---------------------------------------------------- Context Inner Class
- protected static final class Context
- extends MapElement {
+ protected static final class Context extends MapElement {
+ public ContextVersion[] versions = new ContextVersion[0];
+ }
+
+ protected static final class ContextVersion extends MapElement {
+ public String path = null;
public String[] welcomeResources = new String[0];
public javax.naming.Context resources = null;
public Wrapper defaultWrapper = null;
diff --git a/java/org/apache/tomcat/util/http/mapper/MappingData.java b/java/org/apache/tomcat/util/http/mapper/MappingData.java
index b3304340c..5d8ab74aa 100644
--- a/java/org/apache/tomcat/util/http/mapper/MappingData.java
+++ b/java/org/apache/tomcat/util/http/mapper/MappingData.java
@@ -28,6 +28,7 @@ public class MappingData {
public Object host = null;
public Object context = null;
+ public Object[] contexts = null;
public Object wrapper = null;
public boolean jspWildCard = false;
@@ -41,6 +42,7 @@ public class MappingData {
public void recycle() {
host = null;
context = null;
+ contexts = null;
wrapper = null;
jspWildCard = false;
contextPath.recycle();
diff --git a/test/org/apache/tomcat/util/http/mapper/TestMapper.java b/test/org/apache/tomcat/util/http/mapper/TestMapper.java
index db5347d2f..e2a4c3584 100644
--- a/test/org/apache/tomcat/util/http/mapper/TestMapper.java
+++ b/test/org/apache/tomcat/util/http/mapper/TestMapper.java
@@ -51,30 +51,30 @@ public class TestMapper extends TestCase {
welcomes[0] = "boo/baba";
welcomes[1] = "bobou";
- mapper.addContext("iowejoiejfoiew", "blah7", "",
- "context0", new String[0], null);
- mapper.addContext("iowejoiejfoiew", "blah7", "/foo",
- "context1", new String[0], null);
- mapper.addContext("iowejoiejfoiew", "blah7", "/foo/bar",
- "context2", welcomes, null);
- mapper.addContext("iowejoiejfoiew", "blah7", "/foo/bar/bla",
- "context3", new String[0], null);
+ mapper.addContextVersion("iowejoiejfoiew", "blah7", "",
+ "0", "context0", new String[0], null);
+ mapper.addContextVersion("iowejoiejfoiew", "blah7", "/foo",
+ "0", "context1", new String[0], null);
+ mapper.addContextVersion("iowejoiejfoiew", "blah7", "/foo/bar",
+ "0", "context2", welcomes, null);
+ mapper.addContextVersion("iowejoiejfoiew", "blah7", "/foo/bar/bla",
+ "0", "context3", new String[0], null);
- mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/fo/*",
+ mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "0", "/fo/*",
"wrapper0", false, false);
- mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/",
+ mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "0", "/",
"wrapper1", false, false);
- mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/blh",
+ mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "0", "/blh",
"wrapper2", false, false);
- mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "*.jsp",
+ mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "0", "*.jsp",
"wrapper3", false, false);
- mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/blah/bou/*",
+ mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "0", "/blah/bou/*",
"wrapper4", false, false);
- mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/blah/bobou/*",
+ mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "0", "/blah/bobou/*",
"wrapper5", false, false);
- mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "*.htm",
+ mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "0", "*.htm",
"wrapper6", false, false);
- mapper.addWrapper("iowejoiejfoiew", "/foo/bar/bla", "/bobou/*",
+ mapper.addWrapper("iowejoiejfoiew", "/foo/bar/bla", "0", "/bobou/*",
"wrapper7", false, false);
}
@@ -106,7 +106,7 @@ public class TestMapper extends TestCase {
uri.toChars();
uri.getCharChunk().setLimit(-1);
- mapper.map(host, uri, mappingData);
+ mapper.map(host, uri, null, mappingData);
assertEquals("blah7", mappingData.host);
assertEquals("context2", mappingData.context);
assertEquals("wrapper5", mappingData.wrapper);
@@ -120,7 +120,7 @@ public class TestMapper extends TestCase {
uri.setString("/foo/bar/bla/bobou/foo");
uri.toChars();
uri.getCharChunk().setLimit(-1);
- mapper.map(host, uri, mappingData);
+ mapper.map(host, uri, null, mappingData);
assertEquals("blah7", mappingData.host);
assertEquals("context3", mappingData.context);
assertEquals("wrapper7", mappingData.wrapper);
@@ -143,7 +143,7 @@ public class TestMapper extends TestCase {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
mappingData.recycle();
- mapper.map(host, uri, mappingData);
+ mapper.map(host, uri, null, mappingData);
}
long time = System.currentTimeMillis() - start;
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 806cd4ae7..8dc11d6fd 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -146,6 +146,14 @@
of children whilst the new child is being started since this can block
other threads and cause issues such as lost cluster messages. (markt)
+
+ Implement support for parallel deployment. This allows multiple versions
+ of the same web application to be deployed to the same context path at
+ the same time. Users without a current session will be mapped to the
+ latest version of the web application. Users with a current session will
+ continue to use the version of the web application with which the
+ session is associated until the session expires. (markt)
+
diff --git a/webapps/docs/config/context.xml b/webapps/docs/config/context.xml
index 7bef4d403..c1a28008f 100644
--- a/webapps/docs/config/context.xml
+++ b/webapps/docs/config/context.xml
@@ -59,25 +59,85 @@
Request URI against the context path of each defined Context.
Once selected, that Context will select an appropriate servlet to
process the incoming request, according to the servlet mappings defined
- in the web application deployment descriptor file (which MUST
- be located at /WEB-INF/web.xml within the web app's
- directory hierarchy).
+ by the web application deployment.
You may define as many Context elements as you
- wish. Each such Context MUST have a unique context path. In
+ wish. Each such Context MUST have a unique context name. In
addition, a Context must be present with a context path equal to
a zero-length string. This Context becomes the default
web application for this virtual host, and is used to process all
requests that do not match any other Context's context path.
- For the current versions of Tomcat, unlike Tomcat 4.x,
- it is NOT recommended to place
- <Context> elements directly in the server.xml file. This
- is because it makes modifying the Context configuration
- more invasive since the main conf/server.xml file cannot be
- reloaded without restarting Tomcat.
+ You may deploy multiple versions of a web application with the same context
+ path at the same time. The rules used to match requests to a context version
+ are as follows:
+
+ - If no session information is present in the request, use the latest
+ version.
+ - If session information is present in the request, check the session
+ manager of each version for a matching session and if one is found, use that
+ version.
+ - If session information is present in the request but no matching session
+ can be found, use the latest version.
+
+
+
+ There is a close relationship between the context name,
+ context path, context version and the base file
+ name used for the WAR and/or directory that contains the web application.
+ When no version is specified, the rules are:
+
+ - contextName = contextPath
+ - If the contextPath is a zero length string, the base name is ROOT
+ - If the contextPath is not a zero length string, the base name is the
+ contextPath with the leading '/' removed and any remaining '/'
+ characters in the path replaced with '#'.
+
+ When a version is specified, ##version is added to the contextName and base
+ name. To help clarify these rules, some examples are given in the following
+ table.
+
+
+ | Context Path | Context Version | Context Name | Base filename |
+ | /foo | None | /foo | foo |
+ | /foo/bar | None | /foo/bar | foo#bar |
+ | Empty String | None | Empty String | ROOT |
+ | /foo | 42 | /foo##42 | foo##42 |
+ | /foo/bar | 42 | /foo/bar##42 | foo#bar##42 |
+ | Empty String | 42 | ##42 | ROOT##42 |
+
+
+ It is NOT recommended to place <Context> elements directly in the
+ server.xml file. This is because it makes modifying the
+ Context configuration more invasive since the main
+ conf/server.xml file cannot be reloaded without restarting
+ Tomcat.
+
+ Individual Context elements may be explicitly defined:
+
+ - In an individual file at
/META-INF/context.xml inside the
+ application files. Optionally (based on the Host's copyXML attribute)
+ this may be copied to
+ $CATALINA_BASE/conf/[enginename]/[hostname]/ and renamed to
+ application's base file name plus a ".xml" extension.
+ - In individual files (with a ".xml" extension) in the
+
$CATALINA_BASE/conf/[enginename]/[hostname]/ directory.
+ The context path and version will be derived from the base name of the file
+ (the file name less the .xml extension). This file will always take precedence
+ over any context.xml file packaged in the web application's META-INF
+ directory.
+ - Inside a Host element in the main
+
conf/server.xml.
+
+
- Context elements may be explicitly defined:
+
Default Context elements may be defined that apply to
+ multiple web applications. Configuration for an individual web application
+ will override anything configured in one of these defaults. Any nested
+ elements, e.g. <Resource> elements, that are defined in a default
+ Context will be created once for each
+ Context to which the default applies. They will not be
+ shared between Context elements.
- In the
$CATALINA_BASE/conf/context.xml file:
the Context element information will be loaded by all webapps.
@@ -85,24 +145,6 @@
$CATALINA_BASE/conf/[enginename]/[hostname]/context.xml.default
file: the Context element information will be loaded by all webapps of that
host.
- - In individual files (with a ".xml" extension) in the
-
$CATALINA_BASE/conf/[enginename]/[hostname]/ directory.
- The name of the file (less the .xml extension) will be used as the
- context path. Multi-level context paths may be defined using #, e.g.
- foo#bar.xml for a context path of /foo/bar. The
- default web application may be defined by using a file called
- ROOT.xml.
- - Only if a context file does not exist for the application in the
-
$CATALINA_BASE/conf/[enginename]/[hostname]/, in an individual
- file at /META-INF/context.xml inside the application files. If
- the web application is packaged as a WAR then
- /META-INF/context.xml will be copied to
- $CATALINA_BASE/conf/[enginename]/[hostname]/ and renamed to
- match the application's context path. Once this file exists, it will not be
- replaced if a new WAR with a newer /META-INF/context.xml is
- placed in the host's appBase.
- - Inside a Host element in the main
-
conf/server.xml.
diff --git a/webapps/docs/tomcat-docs.xsl b/webapps/docs/tomcat-docs.xsl
index f5551baf8..baa70f47a 100644
--- a/webapps/docs/tomcat-docs.xsl
+++ b/webapps/docs/tomcat-docs.xsl
@@ -521,6 +521,28 @@
r
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+ |
+
+
--
2.11.0