You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2012/10/31 23:06:26 UTC
svn commit: r1404380 - in /tomcat/tc7.0.x/trunk: ./
java/org/apache/catalina/deploy/WebXml.java
test/org/apache/catalina/deploy/TestWebXmlOrdering.java
webapps/docs/changelog.xml
Author: markt
Date: Wed Oct 31 22:06:26 2012
New Revision: 1404380
URL: http://svn.apache.org/viewvc?rev=1404380&view=rev
Log:
Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=54068
Re-write the fragment ordering algorithm to over come multiple problems. Expand the unit tests to cover the identified issues.
Modified:
tomcat/tc7.0.x/trunk/ (props changed)
tomcat/tc7.0.x/trunk/java/org/apache/catalina/deploy/WebXml.java
tomcat/tc7.0.x/trunk/test/org/apache/catalina/deploy/TestWebXmlOrdering.java
tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml
Propchange: tomcat/tc7.0.x/trunk/
------------------------------------------------------------------------------
Merged /tomcat/trunk:r1404374
Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/deploy/WebXml.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/deploy/WebXml.java?rev=1404380&r1=1404379&r2=1404380&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/deploy/WebXml.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/deploy/WebXml.java Wed Oct 31 22:06:26 2012
@@ -5,9 +5,9 @@
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -26,7 +26,6 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -53,10 +52,10 @@ import org.apache.tomcat.util.res.String
* StandardContext will check validity of values (eg URL formats etc)
*/
public class WebXml {
-
+
protected static final String ORDER_OTHERS =
"org.apache.catalina.order.others";
-
+
private static final StringManager sm =
StringManager.getManager(Constants.Package);
@@ -107,7 +106,7 @@ public class WebXml {
after.add(ORDER_OTHERS);
}
public Set<String> getAfterOrdering() { return after; }
-
+
private Set<String> before = new LinkedHashSet<String>();
public void addBeforeOrdering(String fragmentName) {
before.add(fragmentName);
@@ -122,7 +121,7 @@ public class WebXml {
public Set<String> getBeforeOrdering() { return before; }
// Common elements and attributes
-
+
// Required attribute of web-app element
public String getVersion() {
StringBuilder sb = new StringBuilder(3);
@@ -137,7 +136,7 @@ public class WebXml {
*/
public void setVersion(String version) {
if (version == null) return;
-
+
// Update major and minor version
// Expected format is n.n - allow for any number of digits just in case
String major = null;
@@ -161,7 +160,7 @@ public class WebXml {
majorVersion = 0;
}
}
-
+
if (minor == null || minor.length() == 0) {
minorVersion = 0;
} else {
@@ -218,13 +217,13 @@ public class WebXml {
log.warn(sm.getString("webxml.unrecognisedPublicId", publicId));
}
}
-
+
// Optional metadata-complete attribute
private boolean metadataComplete = false;
public boolean isMetadataComplete() { return metadataComplete; }
public void setMetadataComplete(boolean metadataComplete) {
this.metadataComplete = metadataComplete; }
-
+
// Optional name element
private String name = null;
public String getName() { return name; }
@@ -243,7 +242,7 @@ public class WebXml {
private int minorVersion = 0;
public int getMajorVersion() { return majorVersion; }
public int getMinorVersion() { return minorVersion; }
-
+
// web-app elements
// TODO: Ignored elements:
// - description
@@ -255,14 +254,14 @@ public class WebXml {
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
-
+
// distributable
private boolean distributable = false;
public boolean isDistributable() { return distributable; }
public void setDistributable(boolean distributable) {
this.distributable = distributable;
}
-
+
// context-param
// TODO: description (multiple with language) is ignored
private Map<String,String> contextParams = new HashMap<String,String>();
@@ -270,7 +269,7 @@ public class WebXml {
contextParams.put(param, value);
}
public Map<String,String> getContextParams() { return contextParams; }
-
+
// filter
// TODO: Should support multiple description elements with language
// TODO: Should support multiple display-name elements with language
@@ -288,7 +287,7 @@ public class WebXml {
filters.put(filter.getFilterName(), filter);
}
public Map<String,FilterDef> getFilters() { return filters; }
-
+
// filter-mapping
private Set<FilterMap> filterMaps = new LinkedHashSet<FilterMap>();
private Set<String> filterMappingNames = new HashSet<String>();
@@ -297,7 +296,7 @@ public class WebXml {
filterMappingNames.add(filterMap.getFilterName());
}
public Set<FilterMap> getFilterMappings() { return filterMaps; }
-
+
// listener
// TODO: description (multiple with language) is ignored
// TODO: display-name (multiple with language) is ignored
@@ -307,7 +306,7 @@ public class WebXml {
listeners.add(className);
}
public Set<String> getListeners() { return listeners; }
-
+
// servlet
// TODO: description (multiple with language) is ignored
// TODO: display-name (multiple with language) is ignored
@@ -322,7 +321,7 @@ public class WebXml {
}
}
public Map<String,ServletDef> getServlets() { return servlets; }
-
+
// servlet-mapping
private Map<String,String> servletMappings = new HashMap<String,String>();
private Set<String> servletMappingNames = new HashSet<String>();
@@ -331,7 +330,7 @@ public class WebXml {
servletMappingNames.add(servletName);
}
public Map<String,String> getServletMappings() { return servletMappings; }
-
+
// session-config
// Digester will check there is only one of these
private SessionConfig sessionConfig = new SessionConfig();
@@ -339,14 +338,14 @@ public class WebXml {
this.sessionConfig = sessionConfig;
}
public SessionConfig getSessionConfig() { return sessionConfig; }
-
+
// mime-mapping
private Map<String,String> mimeMappings = new HashMap<String,String>();
public void addMimeMapping(String extension, String mimeType) {
mimeMappings.put(extension, mimeType);
}
public Map<String,String> getMimeMappings() { return mimeMappings; }
-
+
// welcome-file-list merge control
private boolean replaceWelcomeFiles = false;
private boolean alwaysAddWelcomeFiles = true;
@@ -375,14 +374,14 @@ public class WebXml {
welcomeFiles.add(welcomeFile);
}
public Set<String> getWelcomeFiles() { return welcomeFiles; }
-
+
// error-page
private Map<String,ErrorPage> errorPages = new HashMap<String,ErrorPage>();
public void addErrorPage(ErrorPage errorPage) {
errorPages.put(errorPage.getName(), errorPage);
}
public Map<String,ErrorPage> getErrorPages() { return errorPages; }
-
+
// Digester will check there is only one jsp-config
// jsp-config/taglib or taglib (2.3 and earlier)
private Map<String,String> taglibs = new HashMap<String,String>();
@@ -395,7 +394,7 @@ public class WebXml {
taglibs.put(uri, location);
}
public Map<String,String> getTaglibs() { return taglibs; }
-
+
// jsp-config/jsp-property-group
private Set<JspPropertyGroup> jspPropertyGroups =
new LinkedHashSet<JspPropertyGroup>();
@@ -417,7 +416,7 @@ public class WebXml {
public Set<SecurityConstraint> getSecurityConstraints() {
return securityConstraints;
}
-
+
// login-config
// Digester will check there is only one of these
private LoginConfig loginConfig = null;
@@ -425,7 +424,7 @@ public class WebXml {
this.loginConfig = loginConfig;
}
public LoginConfig getLoginConfig() { return loginConfig; }
-
+
// security-role
// TODO: description (multiple with language) is ignored
private Set<String> securityRoles = new HashSet<String>();
@@ -433,7 +432,7 @@ public class WebXml {
securityRoles.add(securityRole);
}
public Set<String> getSecurityRoles() { return securityRoles; }
-
+
// env-entry
// TODO: Should support multiple description elements with language
private Map<String,ContextEnvironment> envEntries =
@@ -448,7 +447,7 @@ public class WebXml {
envEntries.put(envEntry.getName(),envEntry);
}
public Map<String,ContextEnvironment> getEnvEntries() { return envEntries; }
-
+
// ejb-ref
// TODO: Should support multiple description elements with language
private Map<String,ContextEjb> ejbRefs = new HashMap<String,ContextEjb>();
@@ -456,7 +455,7 @@ public class WebXml {
ejbRefs.put(ejbRef.getName(),ejbRef);
}
public Map<String,ContextEjb> getEjbRefs() { return ejbRefs; }
-
+
// ejb-local-ref
// TODO: Should support multiple description elements with language
private Map<String,ContextLocalEjb> ejbLocalRefs =
@@ -467,7 +466,7 @@ public class WebXml {
public Map<String,ContextLocalEjb> getEjbLocalRefs() {
return ejbLocalRefs;
}
-
+
// service-ref
// TODO: Should support multiple description elements with language
// TODO: Should support multiple display-names elements with language
@@ -478,7 +477,7 @@ public class WebXml {
serviceRefs.put(serviceRef.getName(), serviceRef);
}
public Map<String,ContextService> getServiceRefs() { return serviceRefs; }
-
+
// resource-ref
// TODO: Should support multiple description elements with language
private Map<String,ContextResource> resourceRefs =
@@ -495,7 +494,7 @@ public class WebXml {
public Map<String,ContextResource> getResourceRefs() {
return resourceRefs;
}
-
+
// resource-env-ref
// TODO: Should support multiple description elements with language
private Map<String,ContextResourceEnvRef> resourceEnvRefs =
@@ -512,7 +511,7 @@ public class WebXml {
public Map<String,ContextResourceEnvRef> getResourceEnvRefs() {
return resourceEnvRefs;
}
-
+
// message-destination-ref
// TODO: Should support multiple description elements with language
private Map<String,MessageDestinationRef> messageDestinationRefs =
@@ -533,7 +532,7 @@ public class WebXml {
public Map<String,MessageDestinationRef> getMessageDestinationRefs() {
return messageDestinationRefs;
}
-
+
// message-destination
// TODO: Should support multiple description elements with language
// TODO: Should support multiple display-names elements with language
@@ -556,7 +555,7 @@ public class WebXml {
public Map<String,MessageDestination> getMessageDestinations() {
return messageDestinations;
}
-
+
// locale-encoding-mapping-list
private Map<String,String> localeEncodingMappings =
new HashMap<String,String>();
@@ -566,10 +565,10 @@ public class WebXml {
public Map<String,String> getLocalEncodingMappings() {
return localeEncodingMappings;
}
-
+
// Attributes not defined in web.xml or web-fragment.xml
-
+
// URL of JAR / exploded JAR for this web-fragment
private URL uRL = null;
public void setURL(URL url) { this.uRL = url; }
@@ -585,26 +584,26 @@ public class WebXml {
buf.append(getURL());
return buf.toString();
}
-
+
private static final String INDENT2 = " ";
private static final String INDENT4 = " ";
private static final String INDENT6 = " ";
-
+
/**
* Generate a web.xml in String form that matches the representation stored
* in this object.
- *
+ *
* @return The complete contents of web.xml as a String
*/
public String toXml() {
StringBuilder sb = new StringBuilder(2048);
-
+
// TODO - Various, icon, description etc elements are skipped - mainly
// because they are ignored when web.xml is parsed - see above
// Declaration
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
-
+
// Root element
sb.append("<web-app xmlns=\"http://java.sun.com/xml/ns/javaee\"\n");
sb.append(" xmlns:xsi=");
@@ -618,11 +617,11 @@ public class WebXml {
sb.append(" metadata-complete=\"true\">\n\n");
appendElement(sb, INDENT2, "display-name", displayName);
-
+
if (isDistributable()) {
sb.append(" <distributable/>\n\n");
}
-
+
for (Map.Entry<String, String> entry : contextParams.entrySet()) {
sb.append(" <context-param>\n");
appendElement(sb, INDENT4, "param-name", entry.getKey());
@@ -630,7 +629,7 @@ public class WebXml {
sb.append(" </context-param>\n");
}
sb.append('\n');
-
+
for (Map.Entry<String, FilterDef> entry : filters.entrySet()) {
FilterDef filterDef = entry.getValue();
sb.append(" <filter>\n");
@@ -745,7 +744,7 @@ public class WebXml {
sb.append(" </servlet-mapping>\n");
}
sb.append('\n');
-
+
if (sessionConfig != null) {
sb.append(" <session-config>\n");
appendElement(sb, INDENT4, "session-timeout",
@@ -770,7 +769,7 @@ public class WebXml {
}
sb.append(" </session-config>\n\n");
}
-
+
for (Map.Entry<String, String> entry : mimeMappings.entrySet()) {
sb.append(" <mime-mapping>\n");
appendElement(sb, INDENT4, "extension", entry.getKey());
@@ -778,7 +777,7 @@ public class WebXml {
sb.append(" </mime-mapping>\n");
}
sb.append('\n');
-
+
if (welcomeFiles.size() > 0) {
sb.append(" <welcome-file-list>\n");
for (String welcomeFile : welcomeFiles) {
@@ -786,7 +785,7 @@ public class WebXml {
}
sb.append(" </welcome-file-list>\n\n");
}
-
+
for (ErrorPage errorPage : errorPages.values()) {
sb.append(" <error-page>\n");
if (errorPage.getExceptionType() == null) {
@@ -839,7 +838,7 @@ public class WebXml {
}
sb.append(" </jsp-config>\n\n");
}
-
+
for (SecurityConstraint constraint : securityConstraints) {
sb.append(" <security-constraint>\n");
appendElement(sb, INDENT4, "display-name",
@@ -895,13 +894,13 @@ public class WebXml {
}
sb.append(" </login-config>\n\n");
}
-
+
for (String roleName : securityRoles) {
sb.append(" <security-role>\n");
appendElement(sb, INDENT4, "role-name", roleName);
sb.append(" </security-role>\n");
}
-
+
for (ContextEnvironment envEntry : envEntries.values()) {
sb.append(" <env-entry>\n");
appendElement(sb, INDENT4, "description",
@@ -967,7 +966,7 @@ public class WebXml {
sb.append(" </ejb-local-ref>\n");
}
sb.append('\n');
-
+
for (ContextService serviceRef : serviceRefs.values()) {
sb.append(" <service-ref>\n");
appendElement(sb, INDENT4, "description",
@@ -1023,7 +1022,7 @@ public class WebXml {
sb.append(" </service-ref>\n");
}
sb.append('\n');
-
+
for (ContextResource resourceRef : resourceRefs.values()) {
sb.append(" <resource-ref>\n");
appendElement(sb, INDENT4, "description",
@@ -1170,7 +1169,7 @@ public class WebXml {
/**
* Configure a {@link Context} using the stored web.xml representation.
- *
+ *
* @param context The context to be configured
*/
public void configureContext(Context context) {
@@ -1182,7 +1181,7 @@ public class WebXml {
// Everything else in order
context.setEffectiveMajorVersion(getMajorVersion());
context.setEffectiveMinorVersion(getMinorVersion());
-
+
for (Entry<String, String> entry : contextParams.entrySet()) {
context.addParameter(entry.getKey(), entry.getValue());
}
@@ -1231,7 +1230,7 @@ public class WebXml {
}
// messageDestinations were ignored in Tomcat 6, so ignore here
-
+
context.setIgnoreAnnotations(metadataComplete);
for (Entry<String, String> entry : mimeMappings.entrySet()) {
context.addMimeMapping(entry.getKey(), entry.getValue());
@@ -1257,7 +1256,7 @@ public class WebXml {
// Description is ignored
// Display name is ignored
// Icons are ignored
-
+
// jsp-file gets passed to the JSP Servlet as an init-param
if (servlet.getLoadOnStartup() != null) {
@@ -1267,7 +1266,7 @@ public class WebXml {
wrapper.setEnabled(servlet.getEnabled().booleanValue());
}
wrapper.setName(servlet.getServletName());
- Map<String,String> params = servlet.getParameterMap();
+ Map<String,String> params = servlet.getParameterMap();
for (Entry<String, String> entry : params.entrySet()) {
wrapper.addInitParameter(entry.getKey(), entry.getValue());
}
@@ -1334,13 +1333,13 @@ public class WebXml {
entry.getValue(), entry.getKey());
context.getJspConfigDescriptor().getTaglibs().add(descriptor);
}
-
+
// Context doesn't use version directly
-
+
for (String welcomeFile : welcomeFiles) {
/*
* The following will result in a welcome file of "" so don't add
- * that to the context
+ * that to the context
* <welcome-file-list>
* <welcome-file/>
* </welcome-file-list>
@@ -1370,10 +1369,10 @@ public class WebXml {
}
}
}
-
+
/**
* Merge the supplied web fragments into this main web.xml.
- *
+ *
* @param fragments The fragments to merge in
* @return <code>true</code> if merge is successful, else
* <code>false</code>
@@ -1381,7 +1380,7 @@ public class WebXml {
public boolean merge(Set<WebXml> fragments) {
// As far as possible, process in alphabetical order so it is easy to
// check everything is present
-
+
// Merge rules vary from element to element. See SRV.8.2.3
WebXml temp = new WebXml();
@@ -1396,7 +1395,7 @@ public class WebXml {
if (displayName == null) {
for (WebXml fragment : fragments) {
- String value = fragment.getDisplayName();
+ String value = fragment.getDisplayName();
if (value != null) {
if (temp.getDisplayName() == null) {
temp.setDisplayName(value);
@@ -1484,7 +1483,7 @@ public class WebXml {
entry.getKey(),
fragment.getName(),
fragment.getURL()));
-
+
return false;
}
} else {
@@ -1616,7 +1615,7 @@ public class WebXml {
}
}
}
-
+
// Add fragment mappings
for (Map.Entry<String,String> mapping : servletMappingsToAdd) {
addServletMapping(mapping.getKey(), mapping.getValue());
@@ -1637,7 +1636,7 @@ public class WebXml {
entry.getKey(),
fragment.getName(),
fragment.getURL()));
-
+
return false;
}
} else {
@@ -1647,7 +1646,7 @@ public class WebXml {
}
}
servlets.putAll(temp.getServlets());
-
+
if (sessionConfig.getSessionTimeout() == null) {
for (WebXml fragment : fragments) {
Integer value = fragment.getSessionConfig().getSessionTimeout();
@@ -1671,7 +1670,7 @@ public class WebXml {
temp.getSessionConfig().getSessionTimeout().toString());
}
}
-
+
if (sessionConfig.getCookieName() == null) {
for (WebXml fragment : fragments) {
String value = fragment.getSessionConfig().getCookieName();
@@ -1848,7 +1847,7 @@ public class WebXml {
sessionConfig.getSessionTrackingModes().addAll(
temp.getSessionConfig().getSessionTrackingModes());
}
-
+
for (WebXml fragment : fragments) {
if (!mergeMap(fragment.getTaglibs(), taglibs,
temp.getTaglibs(), fragment, "Taglibs")) {
@@ -1867,7 +1866,7 @@ public class WebXml {
return true;
}
-
+
private static <T extends ResourceBase> boolean mergeResourceMap(
Map<String, T> fragmentResources, Map<String, T> mainResources,
Map<String, T> tempResources, WebXml fragment) {
@@ -1890,12 +1889,12 @@ public class WebXml {
}
} else {
tempResources.put(resourceName, resource);
- }
+ }
}
}
return true;
}
-
+
private static <T> boolean mergeMap(Map<String,T> fragmentMap,
Map<String,T> mainMap, Map<String,T> tempMap, WebXml fragment,
String mapName) {
@@ -1922,7 +1921,7 @@ public class WebXml {
}
return true;
}
-
+
private static boolean mergeFilter(FilterDef src, FilterDef dest,
boolean failOnConflict) {
if (dest.getAsyncSupported() == null) {
@@ -1942,7 +1941,7 @@ public class WebXml {
return false;
}
}
-
+
for (Map.Entry<String,String> srcEntry :
src.getParameterMap().entrySet()) {
if (dest.getParameterMap().containsKey(srcEntry.getKey())) {
@@ -1956,7 +1955,7 @@ public class WebXml {
}
return true;
}
-
+
private static boolean mergeServlet(ServletDef src, ServletDef dest,
boolean failOnConflict) {
// These tests should be unnecessary...
@@ -1966,8 +1965,8 @@ public class WebXml {
if (src.getServletClass() != null && src.getJspFile() != null) {
return false;
}
-
-
+
+
if (dest.getServletClass() == null && dest.getJspFile() == null) {
dest.setServletClass(src.getServletClass());
dest.setJspFile(src.getJspFile());
@@ -1983,12 +1982,12 @@ public class WebXml {
return false;
}
}
-
+
// Additive
for (SecurityRoleRef securityRoleRef : src.getSecurityRoleRefs()) {
dest.addSecurityRoleRef(securityRoleRef);
}
-
+
if (dest.getLoadOnStartup() == null) {
if (src.getLoadOnStartup() != null) {
dest.setLoadOnStartup(src.getLoadOnStartup().toString());
@@ -1999,7 +1998,7 @@ public class WebXml {
return false;
}
}
-
+
if (dest.getEnabled() == null) {
if (src.getEnabled() != null) {
dest.setEnabled(src.getEnabled().toString());
@@ -2010,7 +2009,7 @@ public class WebXml {
return false;
}
}
-
+
for (Map.Entry<String,String> srcEntry :
src.getParameterMap().entrySet()) {
if (dest.getParameterMap().containsKey(srcEntry.getKey())) {
@@ -2022,14 +2021,14 @@ public class WebXml {
dest.addInitParameter(srcEntry.getKey(), srcEntry.getValue());
}
}
-
+
if (dest.getMultipartDef() == null) {
dest.setMultipartDef(src.getMultipartDef());
} else if (src.getMultipartDef() != null) {
return mergeMultipartDef(src.getMultipartDef(),
dest.getMultipartDef(), failOnConflict);
}
-
+
if (dest.getAsyncSupported() == null) {
if (src.getAsyncSupported() != null) {
dest.setAsyncSupported(src.getAsyncSupported().toString());
@@ -2040,7 +2039,7 @@ public class WebXml {
return false;
}
}
-
+
return true;
}
@@ -2087,13 +2086,13 @@ public class WebXml {
return true;
}
-
-
+
+
/**
* Generates the sub-set of the web-fragment.xml files to be processed in
* the order that the fragments must be processed as per the rules in the
* Servlet spec.
- *
+ *
* @param application The application web.xml file
* @param fragments The map of fragment names to web fragments
* @return Ordered list of web-fragment.xml files to process
@@ -2102,14 +2101,14 @@ public class WebXml {
Map<String,WebXml> fragments) {
Set<WebXml> orderedFragments = new LinkedHashSet<WebXml>();
-
+
boolean absoluteOrdering =
(application.getAbsoluteOrdering() != null);
-
+
if (absoluteOrdering) {
// Only those fragments listed should be processed
Set<String> requestedOrder = application.getAbsoluteOrdering();
-
+
for (String requestedName : requestedOrder) {
if (WebXml.ORDER_OTHERS.equals(requestedName)) {
// Add all fragments not named explicitly at this point
@@ -2131,75 +2130,137 @@ public class WebXml {
}
}
} else {
- List<String> order = new LinkedList<String>();
- // Start by adding all fragments - order doesn't matter
- order.addAll(fragments.keySet());
-
- // Now go through and move elements to start/end depending on if
- // they specify others
- for (WebXml fragment : fragments.values()) {
- String name = fragment.getName();
- if (fragment.getBeforeOrdering().contains(WebXml.ORDER_OTHERS)) {
- // Move to beginning
- order.remove(name);
- order.add(0, name);
- } else if (fragment.getAfterOrdering().contains(WebXml.ORDER_OTHERS)) {
- // Move to end
- order.remove(name);
- order.add(name);
- }
- }
-
- // Now apply remaining ordering
+ // Stage 1. Make all dependencies bi-directional - this makes the
+ // next stage simpler.
for (WebXml fragment : fragments.values()) {
- String name = fragment.getName();
for (String before : fragment.getBeforeOrdering()) {
- if (!before.equals(WebXml.ORDER_OTHERS) &&
- order.contains(before) &&
- order.indexOf(before) < order.indexOf(name)) {
- order.remove(name);
- order.add(order.indexOf(before), name);
+ if (!before.equals(ORDER_OTHERS)) {
+ fragments.get(before).addAfterOrdering(fragment.getName());
}
}
for (String after : fragment.getAfterOrdering()) {
- if (!after.equals(WebXml.ORDER_OTHERS) &&
- order.contains(after) &&
- order.indexOf(after) > order.indexOf(name)) {
- order.remove(name);
- order.add(order.indexOf(after) + 1, name);
+ if (!after.equals(ORDER_OTHERS)) {
+ fragments.get(after).addBeforeOrdering(fragment.getName());
}
}
}
-
- // Finally check ordering was applied correctly - if there are
- // errors then that indicates circular references
+
+ // Stage 2. Make all fragments that are implicitly before/after
+ // others explicitly so. This is iterative so the next
+ // stage doesn't have to be.
for (WebXml fragment : fragments.values()) {
- String name = fragment.getName();
- for (String before : fragment.getBeforeOrdering()) {
- if (!before.equals(WebXml.ORDER_OTHERS) &&
- order.contains(before) &&
- order.indexOf(before) < order.indexOf(name)) {
- throw new IllegalArgumentException(
- sm.getString("webXml.mergeConflictOrder"));
- }
+ if (fragment.getBeforeOrdering().contains(ORDER_OTHERS)) {
+ makeBeforeOthersExplicit(fragment.getAfterOrdering(), fragments);
}
- for (String after : fragment.getAfterOrdering()) {
- if (!after.equals(WebXml.ORDER_OTHERS) &&
- order.contains(after) &&
- order.indexOf(after) > order.indexOf(name)) {
- throw new IllegalArgumentException(
- sm.getString("webXml.mergeConflictOrder"));
- }
+ if (fragment.getAfterOrdering().contains(ORDER_OTHERS)) {
+ makeAfterOthersExplicit(fragment.getBeforeOrdering(), fragments);
}
}
-
- // Build the ordered list
- for (String name : order) {
- orderedFragments.add(fragments.get(name));
+
+ // Stage 3. Separate into three groups
+ Set<WebXml> beforeSet = new HashSet<WebXml>();
+ Set<WebXml> othersSet = new HashSet<WebXml>();
+ Set<WebXml> afterSet = new HashSet<WebXml>();
+
+ for (WebXml fragment : fragments.values()) {
+ if (fragment.getBeforeOrdering().contains(ORDER_OTHERS)) {
+ beforeSet.add(fragment);
+ fragment.getBeforeOrdering().remove(ORDER_OTHERS);
+ } else if (fragment.getAfterOrdering().contains(ORDER_OTHERS)) {
+ afterSet.add(fragment);
+ fragment.getAfterOrdering().remove(ORDER_OTHERS);
+ } else {
+ othersSet.add(fragment);
+ }
}
+
+ // Stage 4. Decouple the groups so the ordering requirements for
+ // each fragment in the group only refer to other fragments
+ // in the group. Ordering requirements outside the group
+ // will be handled by processing the groups in order.
+ // Note: Only after ordering requirements are considered.
+ // This is OK because of the processing in stage 1.
+ decoupleOtherGroups(beforeSet);
+ decoupleOtherGroups(othersSet);
+ decoupleOtherGroups(afterSet);
+
+ // Stage 5. Order each group
+ // Note: Only after ordering requirements are considered.
+ // This is OK because of the processing in stage 1.
+ orderFragments(orderedFragments, beforeSet);
+ orderFragments(orderedFragments, othersSet);
+ orderFragments(orderedFragments, afterSet);
}
-
+
return orderedFragments;
}
-}
+ private static void decoupleOtherGroups(Set<WebXml> group) {
+ Set<String> names = new HashSet<String>();
+ for (WebXml fragment : group) {
+ names.add(fragment.getName());
+ }
+ for (WebXml fragment : group) {
+ Iterator<String> after = fragment.getAfterOrdering().iterator();
+ while (after.hasNext()) {
+ String entry = after.next();
+ if (!names.contains(entry)) {
+ after.remove();
+ }
+ }
+ }
+ }
+ private static void orderFragments(Set<WebXml> orderedFragments,
+ Set<WebXml> unordered) {
+ Set<WebXml> addedThisRound = new HashSet<WebXml>();
+ Set<WebXml> addedLastRound = new HashSet<WebXml>();
+ while (unordered.size() > 0) {
+ Iterator<WebXml> source = unordered.iterator();
+ while (source.hasNext()) {
+ WebXml fragment = source.next();
+ for (WebXml toRemove : addedLastRound) {
+ fragment.getAfterOrdering().remove(toRemove.getName());
+ }
+ if (fragment.getAfterOrdering().isEmpty()) {
+ addedThisRound.add(fragment);
+ orderedFragments.add(fragment);
+ source.remove();
+ }
+ }
+ if (addedThisRound.size() == 0) {
+ // Circular
+ throw new IllegalArgumentException(
+ sm.getString("webXml.mergeConflictOrder"));
+ }
+ addedLastRound.clear();
+ addedLastRound.addAll(addedThisRound);
+ addedThisRound.clear();
+ }
+ }
+
+ private static void makeBeforeOthersExplicit(Set<String> beforeOrdering,
+ Map<String, WebXml> fragments) {
+ for (String before : beforeOrdering) {
+ if (!before.equals(ORDER_OTHERS)) {
+ WebXml webXml = fragments.get(before);
+ if (!webXml.getBeforeOrdering().contains(ORDER_OTHERS)) {
+ webXml.addBeforeOrderingOthers();
+ makeBeforeOthersExplicit(webXml.getAfterOrdering(), fragments);
+ }
+ }
+ }
+ }
+
+ private static void makeAfterOthersExplicit(Set<String> afterOrdering,
+ Map<String, WebXml> fragments) {
+ for (String after : afterOrdering) {
+ if (!after.equals(ORDER_OTHERS)) {
+ WebXml webXml = fragments.get(after);
+ if (!webXml.getAfterOrdering().contains(ORDER_OTHERS)) {
+ webXml.addAfterOrderingOthers();
+ makeAfterOthersExplicit(webXml.getBeforeOrdering(), fragments);
+ }
+ }
+ }
+ }
+}
Modified: tomcat/tc7.0.x/trunk/test/org/apache/catalina/deploy/TestWebXmlOrdering.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/test/org/apache/catalina/deploy/TestWebXmlOrdering.java?rev=1404380&r1=1404379&r2=1404380&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/test/org/apache/catalina/deploy/TestWebXmlOrdering.java (original)
+++ tomcat/tc7.0.x/trunk/test/org/apache/catalina/deploy/TestWebXmlOrdering.java Wed Oct 31 22:06:26 2012
@@ -17,9 +17,11 @@
package org.apache.catalina.deploy;
-import java.util.HashMap;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -42,9 +44,15 @@ public class TestWebXmlOrdering {
private WebXml e;
private WebXml f;
private Map<String,WebXml> fragments;
+ private int posA;
+ private int posB;
+ private int posC;
+ private int posD;
+ private int posE;
+ private int posF;
@Before
- public void setUp() throws Exception {
+ public void setUp() {
app = new WebXml();
a = new WebXml();
a.setName("a");
@@ -58,7 +66,8 @@ public class TestWebXmlOrdering {
e.setName("e");
f = new WebXml();
f.setName("f");
- fragments = new HashMap<String,WebXml>();
+ // Control the input order
+ fragments = new LinkedHashMap<String,WebXml>();
fragments.put("a",a);
fragments.put("b",b);
fragments.put("c",c);
@@ -185,83 +194,483 @@ public class TestWebXmlOrdering {
assertFalse(iter.hasNext());
}
+ private void doRelativeOrderingTest(RelativeOrderingTestRunner runner) {
+ // Confirm we have all 720 possible input orders
+ // Set<String> orders = new HashSet<>();
+
+ // Test all possible input orders since some bugs were discovered that
+ // depended on input order
+ for (int i = 0; i < 6; i++) {
+ for (int j = 0; j < 5; j++) {
+ for (int k = 0; k < 4; k++) {
+ for (int l = 0; l < 3; l++) {
+ for (int m = 0; m < 2; m++) {
+ setUp();
+ runner.init();
+ ArrayList<WebXml> source = new ArrayList<WebXml>();
+ source.addAll(fragments.values());
+ Map<String,WebXml> input =
+ new LinkedHashMap<String,WebXml>();
+
+ WebXml one = source.remove(i);
+ input.put(one.getName(), one);
+
+ WebXml two = source.remove(j);
+ input.put(two.getName(), two);
+
+ WebXml three = source.remove(k);
+ input.put(three.getName(), three);
+
+ WebXml four = source.remove(l);
+ input.put(four.getName(), four);
+
+ WebXml five = source.remove(m);
+ input.put(five.getName(), five);
+
+ WebXml six = source.remove(0);
+ input.put(six.getName(), six);
+
+ /*
+ String order = one.getName() + two.getName() +
+ three.getName() + four.getName() +
+ five.getName() + six.getName();
+ orders.add(order);
+ */
+
+ Set<WebXml> ordered =
+ WebXml.orderWebFragments(app, input);
+ populatePositions(ordered);
+
+ runner.validate(getOrder(ordered));
+ }
+ }
+ }
+ }
+ }
+ // System.out.println(orders.size());
+ }
+
+ private String getOrder(Set<WebXml> ordered) {
+ StringBuilder sb = new StringBuilder(ordered.size());
+ for (WebXml webXml : ordered) {
+ sb.append(webXml.getName());
+ }
+ return sb.toString();
+ }
+
+ private void populatePositions(Set<WebXml> ordered) {
+ List<WebXml> indexed = new ArrayList<WebXml>();
+ indexed.addAll(ordered);
+
+ posA = indexed.indexOf(a);
+ posB = indexed.indexOf(b);
+ posC = indexed.indexOf(c);
+ posD = indexed.indexOf(d);
+ posE = indexed.indexOf(e);
+ posF = indexed.indexOf(f);
+ }
+
@Test
public void testOrderWebFragmentsRelative1() {
// First example from servlet spec
- a.addAfterOrderingOthers();
- a.addAfterOrdering("c");
- b.addBeforeOrderingOthers();
- c.addAfterOrderingOthers();
- f.addBeforeOrderingOthers();
- f.addBeforeOrdering("b");
-
- Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments);
-
- Iterator<WebXml> iter = ordered.iterator();
- assertEquals(f,iter.next());
- assertEquals(b,iter.next());
- assertEquals(d,iter.next());
- assertEquals(e,iter.next());
- assertEquals(c,iter.next());
- assertEquals(a,iter.next());
+ doRelativeOrderingTest(new RelativeTestRunner1());
}
@Test
public void testOrderWebFragmentsRelative2() {
// Second example - use fragment a for no-id fragment
- a.addAfterOrderingOthers();
- a.addBeforeOrdering("c");
- b.addBeforeOrderingOthers();
- d.addAfterOrderingOthers();
- e.addBeforeOrderingOthers();
+ doRelativeOrderingTest(new RelativeTestRunner2());
+ }
- Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments);
+ @Test
+ public void testOrderWebFragmentsRelative3() {
+ // Third example from spec with e & f added
+ doRelativeOrderingTest(new RelativeTestRunner3());
+ }
- Iterator<WebXml> iter = ordered.iterator();
- // A number of orders are possible but the algorithm is deterministic
- // and this order is valid. If this fails after a change to the
- // algorithm, then check to see if the new order is also valid.
- assertEquals(b,iter.next());
- assertEquals(e,iter.next());
- assertEquals(f,iter.next());
- assertEquals(a,iter.next());
- assertEquals(c,iter.next());
- assertEquals(d,iter.next());
+ @Test
+ public void testOrderWebFragmentsRelative4Bug54068() {
+ // Simple sequence that failed for some inputs
+ doRelativeOrderingTest(new RelativeTestRunner4());
}
@Test
- public void testOrderWebFragmentsRelative3() {
- // Third example from spec
- a.addAfterOrdering("b");
- c.addBeforeOrderingOthers();
- fragments.remove("e");
- fragments.remove("f");
+ public void testOrderWebFragmentsRelative5Bug54068() {
+ // Simple sequence that failed for some inputs
+ doRelativeOrderingTest(new RelativeTestRunner5());
+ }
- Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments);
+ @Test
+ public void testOrderWebFragmentsRelative6Bug54068() {
+ // Simple sequence that failed for some inputs
+ doRelativeOrderingTest(new RelativeTestRunner6());
+ }
- Iterator<WebXml> iter = ordered.iterator();
- // A number of orders are possible but the algorithm is deterministic
- // and this order is valid. If this fails after a change to the
- // algorithm, then check to see if the new order is also valid.
- assertEquals(c,iter.next());
- assertEquals(d,iter.next());
- assertEquals(b,iter.next());
- assertEquals(a,iter.next());
+ @Test
+ public void testOrderWebFragmentsRelative7() {
+ // Reference loop (but not circular dependencies)
+ doRelativeOrderingTest(new RelativeTestRunner7());
}
@Test
- public void testOrderWebFragmentsrelativeCircular() {
+ public void testOrderWebFragmentsRelative8() {
+ // More complex, trying to break the algorithm
+ doRelativeOrderingTest(new RelativeTestRunner8());
+ }
+
+ @Test
+ public void testOrderWebFragmentsRelative9() {
+ // Variation on bug 54068
+ doRelativeOrderingTest(new RelativeTestRunner9());
+ }
+
+ @Test
+ public void testOrderWebFragmentsRelative10() {
+ // Variation on bug 54068
+ doRelativeOrderingTest(new RelativeTestRunner10());
+ }
+
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testOrderWebFragmentsrelativeCircular1() {
a.addBeforeOrdering("b");
b.addBeforeOrdering("a");
- Exception exception = null;
+ WebXml.orderWebFragments(app, fragments);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testOrderWebFragmentsrelativeCircular2() {
+ a.addBeforeOrderingOthers();
+ b.addAfterOrderingOthers();
+ c.addBeforeOrdering("a");
+ c.addAfterOrdering("b");
+
+ WebXml.orderWebFragments(app, fragments);
+ }
+
+ private interface RelativeOrderingTestRunner {
+ void init();
+ void validate(String order);
+ }
+
+ private class RelativeTestRunner1 implements RelativeOrderingTestRunner {
+
+ @Override
+ public void init() {
+ a.addAfterOrderingOthers();
+ a.addAfterOrdering("c");
+ b.addBeforeOrderingOthers();
+ c.addAfterOrderingOthers();
+ f.addBeforeOrderingOthers();
+ f.addBeforeOrdering("b");
+ }
+
+ @Override
+ public void validate(String order) {
+ // There is some duplication in the tests below - it is easier to
+ // check the tests are complete this way.
+
+ //a.addAfterOrderingOthers();
+ assertTrue(order, posA > posB);
+ assertTrue(order, posA > posC);
+ assertTrue(order, posA > posD);
+ assertTrue(order, posA > posE);
+ assertTrue(order, posA > posF);
+
+ // a.addAfterOrdering("c");
+ assertTrue(order, posA > posC);
+
+ // b.addBeforeOrderingOthers();
+ assertTrue(order, posB < posC);
+
+ // c.addAfterOrderingOthers();
+ assertTrue(order, posC > posB);
+ assertTrue(order, posC > posD);
+ assertTrue(order, posC > posE);
+ assertTrue(order, posC > posF);
+
+ // f.addBeforeOrderingOthers();
+ assertTrue(order, posF < posA);
+ assertTrue(order, posF < posB);
+ assertTrue(order, posF < posC);
+ assertTrue(order, posF < posD);
+ assertTrue(order, posF < posE);
+
+ // f.addBeforeOrdering("b");
+ assertTrue(order, posF < posB);
+ }
+ }
+
+ private class RelativeTestRunner2 implements RelativeOrderingTestRunner {
- try {
- WebXml.orderWebFragments(app, fragments);
- } catch (Exception e1) {
- exception = e1;
+ @Override
+ public void init() {
+ a.addAfterOrderingOthers();
+ a.addBeforeOrdering("c");
+ b.addBeforeOrderingOthers();
+ d.addAfterOrderingOthers();
+ e.addBeforeOrderingOthers();
}
- assertTrue(exception instanceof IllegalArgumentException);
+ @Override
+ public void validate(String order) {
+ // There is some duplication in the tests below - it is easier to
+ // check the tests are complete this way.
+
+ // a.addAfterOrderingOthers();
+ assertTrue(order, posA > posB);
+ assertTrue(order, posA > posE);
+ assertTrue(order, posA > posF);
+
+ // a.addBeforeOrdering("c");
+ assertTrue(order, posC > posA);
+ assertTrue(order, posC > posB);
+ assertTrue(order, posC > posE);
+ assertTrue(order, posC > posF);
+
+ // b.addBeforeOrderingOthers();
+ assertTrue(order, posB < posA);
+ assertTrue(order, posB < posC);
+ assertTrue(order, posB < posD);
+ assertTrue(order, posB < posF);
+
+ // d.addAfterOrderingOthers();
+ assertTrue(order, posD > posB);
+ assertTrue(order, posD > posE);
+ assertTrue(order, posD > posF);
+
+ // e.addBeforeOrderingOthers();
+ assertTrue(order, posE < posA);
+ assertTrue(order, posE < posC);
+ assertTrue(order, posE < posD);
+ assertTrue(order, posE < posF);
+ }
+ }
+
+ private class RelativeTestRunner3 implements RelativeOrderingTestRunner {
+
+ @Override
+ public void init() {
+ a.addAfterOrdering("b");
+ c.addBeforeOrderingOthers();
+ }
+
+ @Override
+ public void validate(String order) {
+ // There is some duplication in the tests below - it is easier to
+ // check the tests are complete this way.
+
+ // a.addAfterOrdering("b");
+ assertTrue(order, posA > posB);
+
+ // c.addBeforeOrderingOthers();
+ assertTrue(order, posC < posA);
+ assertTrue(order, posC < posB);
+ assertTrue(order, posC < posD);
+ assertTrue(order, posC < posE);
+ assertTrue(order, posC < posF);
+ }
+ }
+
+ private class RelativeTestRunner4 implements RelativeOrderingTestRunner {
+
+ @Override
+ public void init() {
+ b.addAfterOrdering("a");
+ c.addAfterOrdering("b");
+ }
+
+ @Override
+ public void validate(String order) {
+ // There is some duplication in the tests below - it is easier to
+ // check the tests are complete this way.
+
+ // b.addAfterOrdering("a");
+ assertTrue(order, posB > posA);
+
+ // c.addAfterOrdering("b");
+ assertTrue(order, posC > posB);
+ }
+ }
+
+ private class RelativeTestRunner5 implements RelativeOrderingTestRunner {
+
+ @Override
+ public void init() {
+ b.addBeforeOrdering("a");
+ c.addBeforeOrdering("b");
+ }
+
+ @Override
+ public void validate(String order) {
+ // There is some duplication in the tests below - it is easier to
+ // check the tests are complete this way.
+
+ // b.addBeforeOrdering("a");
+ assertTrue(order, posB < posA);
+
+ // c.addBeforeOrdering("b");
+ assertTrue(order, posC < posB);
+ }
+ }
+
+ private class RelativeTestRunner6 implements RelativeOrderingTestRunner {
+
+ @Override
+ public void init() {
+ b.addBeforeOrdering("a");
+ b.addAfterOrdering("c");
+ }
+
+ @Override
+ public void validate(String order) {
+ // There is some duplication in the tests below - it is easier to
+ // check the tests are complete this way.
+
+ // b.addBeforeOrdering("a");
+ assertTrue(order, posB < posA);
+
+ //b.addAfterOrdering("c");
+ assertTrue(order, posB > posC);
+ }
+ }
+
+ private class RelativeTestRunner7 implements RelativeOrderingTestRunner {
+
+ @Override
+ public void init() {
+ b.addBeforeOrdering("a");
+ c.addBeforeOrdering("b");
+ a.addAfterOrdering("c");
+ }
+
+ @Override
+ public void validate(String order) {
+ // There is some duplication in the tests below - it is easier to
+ // check the tests are complete this way.
+
+ // b.addBeforeOrdering("a");
+ assertTrue(order, posB < posA);
+
+ // c.addBeforeOrdering("b");
+ assertTrue(order, posC < posB);
+
+ // a.addAfterOrdering("c");
+ assertTrue(order, posA > posC);
+ }
+ }
+
+ private class RelativeTestRunner8 implements RelativeOrderingTestRunner {
+
+ @Override
+ public void init() {
+ a.addBeforeOrderingOthers();
+ a.addBeforeOrdering("b");
+ b.addBeforeOrderingOthers();
+ c.addAfterOrdering("b");
+ d.addAfterOrdering("c");
+ e.addAfterOrderingOthers();
+ f.addAfterOrderingOthers();
+ f.addAfterOrdering("e");
+ }
+
+ @Override
+ public void validate(String order) {
+ // There is some duplication in the tests below - it is easier to
+ // check the tests are complete this way.
+
+ // a.addBeforeOrderingOthers();
+ assertTrue(order, posA < posB);
+ assertTrue(order, posA < posC);
+ assertTrue(order, posA < posD);
+ assertTrue(order, posA < posE);
+ assertTrue(order, posA < posF);
+
+ // a.addBeforeOrdering("b");
+ assertTrue(order, posA < posB);
+
+ // b.addBeforeOrderingOthers();
+ assertTrue(order, posB < posC);
+ assertTrue(order, posB < posD);
+ assertTrue(order, posB < posE);
+ assertTrue(order, posB < posF);
+
+ // c.addAfterOrdering("b");
+ assertTrue(order, posC > posB);
+
+ // d.addAfterOrdering("c");
+ assertTrue(order, posD > posC);
+
+ // e.addAfterOrderingOthers();
+ assertTrue(order, posE > posA);
+ assertTrue(order, posE > posB);
+ assertTrue(order, posE > posC);
+ assertTrue(order, posE > posD);
+
+ // f.addAfterOrderingOthers();
+ assertTrue(order, posF > posA);
+ assertTrue(order, posF > posB);
+ assertTrue(order, posF > posC);
+ assertTrue(order, posF > posD);
+ assertTrue(order, posF > posE);
+
+ // f.addAfterOrdering("e");
+ assertTrue(order, posF > posE);
+ }
+ }
+
+ private class RelativeTestRunner9 implements RelativeOrderingTestRunner {
+
+ @Override
+ public void init() {
+ a.addBeforeOrderingOthers();
+ b.addBeforeOrdering("a");
+ c.addBeforeOrdering("b");
+ }
+
+ @Override
+ public void validate(String order) {
+ // There is some duplication in the tests below - it is easier to
+ // check the tests are complete this way.
+
+ // a.addBeforeOrderingOthers();
+ assertTrue(order, posA < posD);
+ assertTrue(order, posA < posE);
+ assertTrue(order, posA < posF);
+
+ // b.addBeforeOrdering("a");
+ assertTrue(order, posB < posA);
+
+ // c.addBeforeOrdering("b");
+ assertTrue(order, posC < posB);
+ }
+ }
+
+ private class RelativeTestRunner10 implements RelativeOrderingTestRunner {
+
+ @Override
+ public void init() {
+ a.addAfterOrderingOthers();
+ b.addAfterOrdering("a");
+ c.addAfterOrdering("b");
+ }
+
+ @Override
+ public void validate(String order) {
+ // There is some duplication in the tests below - it is easier to
+ // check the tests are complete this way.
+
+ // a.addAfterOrderingOthers();
+ assertTrue(order, posA > posD);
+ assertTrue(order, posA > posE);
+ assertTrue(order, posA > posF);
+
+ // b.addAfterOrdering("a");
+ assertTrue(order, posB > posA);
+
+ // c.addAfterOrdering("b");
+ assertTrue(order, posC > posB);
+ }
}
}
Modified: tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml?rev=1404380&r1=1404379&r2=1404380&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml Wed Oct 31 22:06:26 2012
@@ -81,6 +81,11 @@
<bug>54054</bug>: Do not share shell environment variables between
multiple instances of the CGI servlet. (markt)
</fix>
+ <fix>
+ <bug>54068</bug>: Rewrite the web fragment ordering algorithm to resolve
+ multiple issues that resulted in incorrect ordering or failure to find
+ a correct, valid order. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Coyote">
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org