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 PathContext VersionContext NameBase filename
/fooNone/foofoo
/foo/barNone/foo/barfoo#bar
Empty StringNoneEmpty StringROOT
/foo42/foo##42foo##42
/foo/bar42/foo/bar##42foo#bar##42
Empty String42##42ROOT##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