Author: markt
Date: Fri Nov 19 17:18:04 2010
New Revision: 1036949
URL: http://svn.apache.org/viewvc?rev=1036949&view=rev
Log:
Add the final component of parallel deployment
Modified:
tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java
tomcat/trunk/java/org/apache/catalina/connector/MapperListener.java
tomcat/trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java
tomcat/trunk/java/org/apache/tomcat/util/http/mapper/MappingData.java
tomcat/trunk/test/org/apache/tomcat/util/http/mapper/TestMapper.java
tomcat/trunk/webapps/docs/changelog.xml
tomcat/trunk/webapps/docs/config/context.xml
tomcat/trunk/webapps/docs/tomcat-docs.xsl
Modified: tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java?rev=1036949&r1=1036948&r2=1036949&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java
(original)
+++ tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java Fri Nov
19 17:18:04 2010
@@ -570,62 +570,89 @@ public class CoyoteAdapter implements Ad
//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);
-
- // 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<methods.length; i++) {
- if ("TRACE".equals(methods[i])) {
- continue;
- }
- if (header == null) {
- header = methods[i];
- } else {
- header += ", " + methods[i];
- }
- }
+
+ 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;
+ }
+
+ // If there is no context at this point, it is likely no ROOT
context
+ // has been deployed
+ if (request.getContext() == null) {
+ res.setStatus(404);
+ res.setMessage("Not found");
+ // No context, so use host
+ request.getHost().logAccess(request, response, 0, true);
+ return false;
+ }
+
+ // Now we have the context, we can parse the session ID from the
URL
+ // (if any). Need to do this before we redirect in case we need to
+ // include the session id in the redirect
+ String sessionID = null;
+ if (request.getServletContext().getEffectiveSessionTrackingModes()
+ .contains(SessionTrackingMode.URL)) {
+
+ // Get the session ID if there was one
+ sessionID = request.getPathParameter(
+ ApplicationSessionCookieConfig.getSessionUriParamName(
+ request.getContext()));
+ if (sessionID != null) {
+ request.setRequestedSessionId(sessionID);
+ request.setRequestedSessionURL(true);
}
- }
- res.setStatus(405);
- res.addHeader("Allow", header);
- res.setMessage("TRACE method is not allowed");
- request.getContext().logAccess(request, response, 0, true);
- return false;
- }
+ }
- // If there is no context at this point, it is likely no ROOT context
- // has been deployed
- if (request.getContext() == null) {
- res.setStatus(404);
- res.setMessage("Not found");
- // No context, so use host
- request.getHost().logAccess(request, response, 0, true);
- return false;
- }
-
- // Now we have the context, we can parse the session ID from the URL
- // (if any). Need to do this before we redirect in case we need to
- // include the session id in the redirect
- if (request.getServletContext().getEffectiveSessionTrackingModes()
- .contains(SessionTrackingMode.URL)) {
+ // Look for session ID in cookies and SSL session
+ parseSessionCookiesId(req, request);
+ parseSessionSslId(request);
- // Get the session ID if there was one
- String sessionID = request.getPathParameter(
- ApplicationSessionCookieConfig.getSessionUriParamName(
- request.getContext()));
- if (sessionID != null) {
- request.setRequestedSessionId(sessionID);
- request.setRequestedSessionURL(true);
+ sessionID = request.getRequestedSessionId();
+
+ if (mapRequired) {
+ if (sessionID == null) {
+ // No session means no possibility of needing to remap
+ mapRequired = false;
+ } else {
+ // Find the context associated with the session
+ Object[] objs = request.getMappingData().contexts;
+ for (int i = (objs.length); 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 Ad
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<methods.length; i++) {
+ if ("TRACE".equals(methods[i])) {
+ continue;
+ }
+ if (header == null) {
+ header = methods[i];
+ } else {
+ header += ", " + methods[i];
+ }
+ }
+ }
+ }
+ res.setStatus(405);
+ res.addHeader("Allow", header);
+ res.setMessage("TRACE method is not allowed");
+ request.getContext().logAccess(request, response, 0, true);
+ return false;
+ }
+
return true;
}
Modified: tomcat/trunk/java/org/apache/catalina/connector/MapperListener.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/MapperListener.java?rev=1036949&r1=1036948&r2=1036949&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/connector/MapperListener.java
(original)
+++ tomcat/trunk/java/org/apache/catalina/connector/MapperListener.java Fri Nov
19 17:18:04 2010
@@ -161,12 +161,13 @@ public class MapperListener implements C
if ("/".equals(contextPath)) {
contextPath = "";
}
+ String version = ((Context)
wrapper.getParent()).getWebappVersion();
String hostName = context.getParent().getName();
String wrapperName = wrapper.getName();
String mapping = (String) event.getData();
boolean jspWildCard = ("jsp".equals(wrapperName)
&& mapping.endsWith("/*"));
- mapper.addWrapper(hostName, contextPath, mapping, wrapper,
+ mapper.addWrapper(hostName, contextPath, version, mapping, wrapper,
jspWildCard, context.isResourceOnlyServlet(wrapperName));
} else if (event.getType() == Wrapper.REMOVE_MAPPING_EVENT) {
// Handle dynamically removing wrappers
@@ -176,11 +177,12 @@ public class MapperListener implements C
if ("/".equals(contextPath)) {
contextPath = "";
}
+ String version = ((Context)
wrapper.getParent()).getWebappVersion();
String hostName = wrapper.getParent().getParent().getName();
String mapping = (String) event.getData();
- mapper.removeWrapper(hostName, contextPath, mapping);
+ mapper.removeWrapper(hostName, contextPath, version, mapping);
} else if (event.getType() == Context.ADD_WELCOME_FILE_EVENT) {
// Handle dynamically adding welcome files
Context context = (Context) event.getSource();
@@ -194,7 +196,8 @@ public class MapperListener implements C
String welcomeFile = (String) event.getData();
- mapper.addWelcomeFile(hostName, contextPath, welcomeFile);
+ mapper.addWelcomeFile(hostName, contextPath,
+ context.getWebappVersion(), welcomeFile);
} else if (event.getType() == Context.REMOVE_WELCOME_FILE_EVENT) {
// Handle dynamically removing welcome files
Context context = (Context) event.getSource();
@@ -208,7 +211,8 @@ public class MapperListener implements C
String welcomeFile = (String) event.getData();
- mapper.removeWelcomeFile(hostName, contextPath, welcomeFile);
+ mapper.removeWelcomeFile(hostName, contextPath,
+ context.getWebappVersion(), welcomeFile);
} else if (event.getType() == Context.CLEAR_WELCOME_FILES_EVENT) {
// Handle dynamically clearing welcome files
Context context = (Context) event.getSource();
@@ -220,7 +224,8 @@ public class MapperListener implements C
contextPath = "";
}
- mapper.clearWelcomeFiles(hostName, contextPath);
+ mapper.clearWelcomeFiles(hostName, contextPath,
+ context.getWebappVersion());
}
}
@@ -307,12 +312,13 @@ public class MapperListener implements C
if ("/".equals(contextPath)) {
contextPath = "";
}
+ String version = ((Context) wrapper.getParent()).getWebappVersion();
String hostName = wrapper.getParent().getParent().getName();
String[] mappings = wrapper.findMappings();
for (String mapping : mappings) {
- mapper.removeWrapper(hostName, contextPath, mapping);
+ mapper.removeWrapper(hostName, contextPath, version, mapping);
}
if(log.isDebugEnabled()) {
@@ -336,8 +342,8 @@ public class MapperListener implements C
javax.naming.Context resources = context.getResources();
String[] welcomeFiles = context.findWelcomeFiles();
- mapper.addContext(host.getName(), host, contextPath, context,
- welcomeFiles, resources);
+ mapper.addContextVersion(host.getName(), host, contextPath,
+ context.getWebappVersion(), context, welcomeFiles, resources);
for (Container container : context.findChildren()) {
registerWrapper((Wrapper) container);
@@ -370,7 +376,8 @@ public class MapperListener implements C
log.debug(sm.getString("mapperListener.unregisterContext",
contextPath, connector));
- mapper.removeContext(hostName, contextPath);
+ mapper.removeContextVersion(hostName, contextPath,
+ context.getWebappVersion());
}
@@ -385,7 +392,7 @@ public class MapperListener implements C
if ("/".equals(contextPath)) {
contextPath = "";
}
-
+ String version = ((Context) wrapper.getParent()).getWebappVersion();
String hostName = context.getParent().getName();
String[] mappings = wrapper.findMappings();
@@ -393,7 +400,7 @@ public class MapperListener implements C
for (String mapping : mappings) {
boolean jspWildCard = (wrapperName.equals("jsp")
&& mapping.endsWith("/*"));
- mapper.addWrapper(hostName, contextPath, mapping, wrapper,
+ mapper.addWrapper(hostName, contextPath, version, mapping, wrapper,
jspWildCard,
context.isResourceOnlyServlet(wrapperName));
}
Modified: tomcat/trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java?rev=1036949&r1=1036948&r2=1036949&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java Fri Nov 19
17:18:04 2010
@@ -57,7 +57,7 @@ public final class Mapper {
/**
* Context associated with this wrapper, used for wrapper mapping.
*/
- protected Context context = new Context();
+ protected ContextVersion context = new ContextVersion();
// --------------------------------------------------------- Public Methods
@@ -189,7 +189,7 @@ public final class Mapper {
*/
public void setContext(String path, String[] welcomeResources,
javax.naming.Context resources) {
- context.name = path;
+ context.path = path;
context.welcomeResources = welcomeResources;
context.resources = resources;
}
@@ -201,13 +201,14 @@ public final class Mapper {
* @param hostName Virtual host name this context belongs to
* @param host Host object
* @param path Context path
+ * @param version Context version
* @param context Context object
* @param welcomeResources Welcome files defined for this context
* @param resources Static resources of the context
*/
- public void addContext
- (String hostName, Object host, String path, Object context,
- String[] welcomeResources, javax.naming.Context resources) {
+ public void addContextVersion(String hostName, Object host, String path,
+ String version, Object context, String[] welcomeResources,
+ javax.naming.Context resources) {
Host[] hosts = this.hosts;
int pos = find(hosts, hostName);
@@ -228,14 +229,30 @@ public final class Mapper {
if (slashCount > 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;
Modified: tomcat/trunk/java/org/apache/tomcat/util/http/mapper/MappingData.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/http/mapper/MappingData.java?rev=1036949&r1=1036948&r2=1036949&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/http/mapper/MappingData.java
(original)
+++ tomcat/trunk/java/org/apache/tomcat/util/http/mapper/MappingData.java Fri
Nov 19 17:18:04 2010
@@ -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();
Modified: tomcat/trunk/test/org/apache/tomcat/util/http/mapper/TestMapper.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/util/http/mapper/TestMapper.java?rev=1036949&r1=1036948&r2=1036949&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/tomcat/util/http/mapper/TestMapper.java
(original)
+++ tomcat/trunk/test/org/apache/tomcat/util/http/mapper/TestMapper.java Fri
Nov 19 17:18:04 2010
@@ -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;
Modified: tomcat/trunk/webapps/docs/changelog.xml
URL:
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1036949&r1=1036948&r2=1036949&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Fri Nov 19 17:18:04 2010
@@ -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)
</fix>
+ <add>
+ 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)
+ </add>
</changelog>
</subsection>
<subsection name="Coyote">
Modified: tomcat/trunk/webapps/docs/config/context.xml
URL:
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/context.xml?rev=1036949&r1=1036948&r2=1036949&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/context.xml (original)
+++ tomcat/trunk/webapps/docs/config/context.xml Fri Nov 19 17:18:04 2010
@@ -59,25 +59,85 @@
Request URI against the <em>context path</em> 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 <em>web application deployment descriptor</em> file (which MUST
- be located at <code>/WEB-INF/web.xml</code> within the web app's
- directory hierarchy).</p>
+ by the web application deployment.</p>
<p>You may define as many <strong>Context</strong> 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 <em>default</em>
web application for this virtual host, and is used to process all
requests that do not match any other Context's context path.</p>
- <p><b>For the current versions of Tomcat, unlike Tomcat 4.x,
- it is NOT recommended to place
- <Context> elements directly in the server.xml file.</b> This
- is because it makes modifying the <strong>Context</strong> configuration
- more invasive since the main <code>conf/server.xml</code> file cannot be
- reloaded without restarting Tomcat.</p>
+ <p>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:
+ <ul>
+ <li>If no session information is present in the request, use the latest
+ version.</li>
+ <li>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.</li>
+ <li>If session information is present in the request but no matching session
+ can be found, use the latest version.</li>
+ </ul>
+ </p>
+
+ <p>There is a close relationship between the <em>context name</em>,
+ <em>context path</em>, <em>context version</em> and the <em>base file
+ name</em> used for the WAR and/or directory that contains the web
application.
+ When no version is specified, the rules are:
+ <ul>
+ <li>contextName = contextPath</li>
+ <li>If the contextPath is a zero length string, the base name is ROOT</li>
+ <li>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 '#'.</li>
+ </ul>
+ 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.</p>
+
+ <table class="detail-table">
+ <tr><th>Context Path</th><th>Context Version</th><th>Context
Name</th><th>Base filename</th></tr>
+ <tr><td>/foo</td><td><i>None</i></td><td>/foo</td><td>foo</td></tr>
+
<tr><td>/foo/bar</td><td><i>None</i></td><td>/foo/bar</td><td>foo#bar</td></tr>
+ <tr><td><i>Empty String</i></td><td><i>None</i></td><td><i>Empty
String</i></td><td>ROOT</td></tr>
+ <tr><td>/foo</td><td>42</td><td>/foo##42</td><td>foo##42</td></tr>
+
<tr><td>/foo/bar</td><td>42</td><td>/foo/bar##42</td><td>foo#bar##42</td></tr>
+ <tr><td><i>Empty
String</i></td><td>42</td><td>##42</td><td>ROOT##42</td></tr>
+ </table>
+
+ <p><b>It is NOT recommended to place <Context> elements directly in the
+ server.xml file.</b> This is because it makes modifying the
+ <strong>Context</strong> configuration more invasive since the main
+ <code>conf/server.xml</code> file cannot be reloaded without restarting
+ Tomcat.</p>
- <p><strong>Context</strong> elements may be explicitly defined:
+ <p>Individual <strong>Context</strong> elements may be explicitly defined:
+ <ul>
+ <li>In an individual file at <code>/META-INF/context.xml</code> inside the
+ application files. Optionally (based on the Host's copyXML attribute)
+ this may be copied to
+ <code>$CATALINA_BASE/conf/[enginename]/[hostname]/</code> and renamed to
+ application's base file name plus a ".xml" extension.</li>
+ <li>In individual files (with a ".xml" extension) in the
+ <code>$CATALINA_BASE/conf/[enginename]/[hostname]/</code> 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.</li>
+ <li>Inside a <a href="host.html">Host</a> element in the main
+ <code>conf/server.xml</code>.</li>
+ </ul>
+ </p>
+
+ <p>Default <strong>Context</strong> 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
+ <strong>Context</strong> will be created once for each
+ <strong>Context</strong> to which the default applies. They will <b>not</b>
be
+ shared between <strong>Context</strong> elements.
<ul>
<li>In the <code>$CATALINA_BASE/conf/context.xml</code> file:
the Context element information will be loaded by all webapps.</li>
@@ -85,24 +145,6 @@
<code>$CATALINA_BASE/conf/[enginename]/[hostname]/context.xml.default</code>
file: the Context element information will be loaded by all webapps of that
host.</li>
- <li>In individual files (with a ".xml" extension) in the
- <code>$CATALINA_BASE/conf/[enginename]/[hostname]/</code> 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.
- <code>foo#bar.xml</code> for a context path of <code>/foo/bar</code>. The
- default web application may be defined by using a file called
- <code>ROOT.xml</code>.</li>
- <li>Only if a context file does not exist for the application in the
- <code>$CATALINA_BASE/conf/[enginename]/[hostname]/</code>, in an individual
- file at <code>/META-INF/context.xml</code> inside the application files. If
- the web application is packaged as a WAR then
- <code>/META-INF/context.xml</code> will be copied to
- <code>$CATALINA_BASE/conf/[enginename]/[hostname]/</code> 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 <code>/META-INF/context.xml</code> is
- placed in the host's appBase.</li>
- <li>Inside a <a href="host.html">Host</a> element in the main
- <code>conf/server.xml</code>.</li>
</ul>
</p>
Modified: tomcat/trunk/webapps/docs/tomcat-docs.xsl
URL:
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/tomcat-docs.xsl?rev=1036949&r1=1036948&r2=1036949&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/tomcat-docs.xsl (original)
+++ tomcat/trunk/webapps/docs/tomcat-docs.xsl Fri Nov 19 17:18:04 2010
@@ -521,6 +521,28 @@
<a href="{$link}">r<xsl:apply-templates/></a>
</xsl:template>
+ <!-- specially process td tags ala site.vsl -->
+ <xsl:template match="tab...@class='detail-table']/tr/td">
+ <td bgcolor="{$table-td-bg}" valign="top" align="left">
+ <xsl:if test="@colspan"><xsl:attribute name="colspan"><xsl:value-of
select="@colspan"/></xsl:attribute></xsl:if>
+ <xsl:if test="@rowspan"><xsl:attribute name="rowspan"><xsl:value-of
select="@rowspan"/></xsl:attribute></xsl:if>
+ <font color="#000000" size="-1" face="arial,helvetica,sanserif">
+ <xsl:apply-templates/>
+ </font>
+ </td>
+ </xsl:template>
+
+ <!-- handle th ala site.vsl -->
+ <xsl:template match="tab...@class='detail-table']/tr/th">
+ <td bgcolor="{$table-th-bg}" valign="top">
+ <xsl:if test="@colspan"><xsl:attribute name="colspan"><xsl:value-of
select="@colspan"/></xsl:attribute></xsl:if>
+ <xsl:if test="@rowspan"><xsl:attribute name="rowspan"><xsl:value-of
select="@rowspan"/></xsl:attribute></xsl:if>
+ <font color="#000000" size="-1" face="arial,helvetica,sanserif">
+ <xsl:apply-templates />
+ </font>
+ </td>
+ </xsl:template>
+
<!-- Process everything else by just passing it through -->
<xsl:template match="*|@*">
<xsl:copy>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]