Author: markt
Date: Thu Nov 12 17:29:00 2009
New Revision: 835460

URL: http://svn.apache.org/viewvc?rev=835460&view=rev
Log:
Servlet 3 implementation.
- Add support for relative fragment ordering and some test cases
- Re-order fragment and annotation processing to match spec
- Implement login/logout
- Provide a method to retrieve the Authenticator in use by a Context
- Add methods to Authenticator interface to facilitate the new login/login 
methods
- Enable Authenticator.register() to be used for logout as well as login

Added:
    tomcat/trunk/test/org/apache/catalina/startup/TestWebXml.java
      - copied, changed from r834808, 
tomcat/trunk/test/org/apache/catalina/startup/TestContextConfig.java
Removed:
    tomcat/trunk/test/org/apache/catalina/startup/TestContextConfig.java
Modified:
    tomcat/trunk/TOMCAT-7-RELEASE-PLAN.txt
    tomcat/trunk/java/org/apache/catalina/Authenticator.java
    tomcat/trunk/java/org/apache/catalina/Context.java
    tomcat/trunk/java/org/apache/catalina/authenticator/AuthenticatorBase.java
    tomcat/trunk/java/org/apache/catalina/connector/LocalStrings.properties
    tomcat/trunk/java/org/apache/catalina/connector/Request.java
    tomcat/trunk/java/org/apache/catalina/connector/RequestFacade.java
    tomcat/trunk/java/org/apache/catalina/core/StandardContext.java
    tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java
    tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties
    tomcat/trunk/java/org/apache/catalina/startup/WebRuleSet.java
    tomcat/trunk/java/org/apache/catalina/startup/WebXml.java
    tomcat/trunk/test/org/apache/catalina/connector/TestKeepAliveCount.java
    tomcat/trunk/test/org/apache/catalina/connector/TestRequest.java
    tomcat/trunk/test/org/apache/catalina/startup/TestTomcat.java

Modified: tomcat/trunk/TOMCAT-7-RELEASE-PLAN.txt
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/TOMCAT-7-RELEASE-PLAN.txt?rev=835460&r1=835459&r2=835460&view=diff
==============================================================================
--- tomcat/trunk/TOMCAT-7-RELEASE-PLAN.txt (original)
+++ tomcat/trunk/TOMCAT-7-RELEASE-PLAN.txt Thu Nov 12 17:29:00 2009
@@ -15,13 +15,11 @@
   limitations under the License.
 
================================================================================
 
-$Id: $
-
             =====================================================
             An outline plan for the first stable Tomcat 7 release
             =====================================================
 
-1. Update trunk with new API from Servlet Spec 3.0 PR
+1. Update trunk with new API from Servlet Spec 3.0 Final Draft 2009-11-05
    - Done
 
 2. Provide NOOP implementations with TODO SRV3 markers so it will build
@@ -36,33 +34,29 @@
      2.3.3.3, 2.3.3.4 - In progress
      2.3.4 - Compliant
    - Sections 3 to 6 - not checked
-   - Section 7 - in progress
-     7.1, 7.2, 7.3, 7.4, 7.5, 7.6 - Compliant
-     7.7.1 - Compliant
-     7.7.2 - When is IAE thrown?
-     7.7.3 - Compliant
+   - Section 7 - Compliant
    - Section 8 - in progress
      8.1 - not checked
-     8.2 - in progress, plan as follows
-           - modify digester to parse to new classes
-           - configure Context & Jasper from new classes
-           - fragment scanning / parsing
-           - fragment ordering
-           - web(-fragment).xml merging code
-           - annotation scanning
+     8.2 - in progress, 'just' annotation scanning left
      8.3 - not checked
-   - Sections 9 onwards - not checked
+     8.4 - not checked
+   - Sections 9 to 12 - not checked
+   - Section 13 - In progess
+     13.1 to 13.3 - Compliant
+     13.4 - In progress
+     13.5 to 13.10 - Compliant
+     Section 14 to 15 - not checked
    - Java EE spec requirements - not checked
+   - JSR 196 - Recommended - Not yet implemented. Copy from Geronimo?
 
 4. Do an alpha release (from trunk)
    - Create tc7.0.x\tags to hold release tags
-   - Create Bugzilla project
    - Add to web site
    - Update Wiki version status page
 
 5. Fix issues as they get reported
 
-6. Update for next public draft(s) of the spec if any.
+6. Update for final release of the spec
 
 7. Aim for first stable TC7 release with final release of Servlet 3 spec
    - Create tc7.0.x\trunk from trunk at first stable release

Modified: tomcat/trunk/java/org/apache/catalina/Authenticator.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/Authenticator.java?rev=835460&r1=835459&r2=835460&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/Authenticator.java (original)
+++ tomcat/trunk/java/org/apache/catalina/Authenticator.java Thu Nov 12 
17:29:00 2009
@@ -18,18 +18,55 @@
 
 package org.apache.catalina;
 
+import java.io.IOException;
+import java.security.Principal;
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.deploy.LoginConfig;
+
 
 /**
  * An <b>Authenticator</b> is a component (usually a Valve or Container) that
- * provides some sort of authentication service.  The interface itself has no
- * functional significance,  but is used as a tagging mechanism so that other
- * components can detect the presence (via an "instanceof Authenticator" test)
- * of an already configured authentication service.
+ * provides some sort of authentication service.
  *
  * @author Craig R. McClanahan
  * @version $Revision$ $Date$
  */
 
 public interface Authenticator {
-    // No methods
+    
+    /**
+     * Authenticate the user making this request, based on the specified
+     * login configuration.  Return <code>true</code> if any specified
+     * constraint has been satisfied, or <code>false</code> if we have
+     * created a response challenge already.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param config    Login configuration describing how authentication
+     *              should be performed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public boolean authenticate(Request request, Response response,
+            LoginConfig config) throws IOException;
+    
+    /**
+     * Register an authenticated Principal and authentication type in our
+     * request, in the current session (if there is one), and with our
+     * SingleSignOn valve, if there is one.  Set the appropriate cookie
+     * to be returned. Passing in a null principal will de-register any
+     * SSO sessions.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are generating
+     * @param principal The authenticated Principal to be registered
+     * @param authType The authentication type to be registered
+     * @param username Username used to authenticate (if any)
+     * @param password Password used to authenticate (if any)
+     */
+    public void register(Request request, Response response,
+            Principal principal, String authType,
+            String username, String password);
 }

Modified: tomcat/trunk/java/org/apache/catalina/Context.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/Context.java?rev=835460&r1=835459&r2=835460&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/Context.java (original)
+++ tomcat/trunk/java/org/apache/catalina/Context.java Thu Nov 12 17:29:00 2009
@@ -1096,5 +1096,10 @@
      */
     public void setJarScanner(JarScanner jarScanner);
 
+    /**
+     * Obtain the {...@link Authenticator} that is used by this context or
+     * <code>null</code> if none is used.
+     */
+    public Authenticator getAuthenticator();
 }
 

Modified: 
tomcat/trunk/java/org/apache/catalina/authenticator/AuthenticatorBase.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/authenticator/AuthenticatorBase.java?rev=835460&r1=835459&r2=835460&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/authenticator/AuthenticatorBase.java 
(original)
+++ tomcat/trunk/java/org/apache/catalina/authenticator/AuthenticatorBase.java 
Thu Nov 12 17:29:00 2009
@@ -570,7 +570,7 @@
      *
      * @exception IOException if an input/output error occurs
      */
-    protected abstract boolean authenticate(Request request,
+    public abstract boolean authenticate(Request request,
                                             Response response,
                                             LoginConfig config)
         throws IOException;
@@ -708,7 +708,7 @@
      * @param username Username used to authenticate (if any)
      * @param password Password used to authenticate (if any)
      */
-    protected void register(Request request, Response response,
+    public void register(Request request, Response response,
                             Principal principal, String authType,
                             String username, String password) {
 
@@ -768,8 +768,14 @@
             request.setNote(Constants.REQ_SSOID_NOTE, ssoId);
 
         } else {
-            // Update the SSO session with the latest authentication data
-            sso.update(ssoId, principal, authType, username, password);
+            if (principal == null) {
+                // Registering a programmatic logout
+                sso.deregister(ssoId);
+                return;
+            } else {
+                // Update the SSO session with the latest authentication data
+                sso.update(ssoId, principal, authType, username, password);
+            }
         }
 
         // Fix for Bug 10040

Modified: 
tomcat/trunk/java/org/apache/catalina/connector/LocalStrings.properties
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/LocalStrings.properties?rev=835460&r1=835459&r2=835460&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/connector/LocalStrings.properties 
(original)
+++ tomcat/trunk/java/org/apache/catalina/connector/LocalStrings.properties Thu 
Nov 12 17:29:00 2009
@@ -61,6 +61,11 @@
 coyoteRequest.parseParameters=Exception thrown whilst processing POSTed 
parameters
 coyoteRequest.postTooLarge=Parameters were not parsed because the size of the 
posted data was too big. Use the maxPostSize attribute of the connector to 
resolve this if the application should accept large POSTs.
 coyoteRequest.chunkedPostTooLarge=Parameters were not parsed because the size 
of the posted data was too big. Because this request was a chunked request, it 
could not be processed further. Use the maxPostSize attribute of the connector 
to resolve this if the application should accept large POSTs.
+coyoteRequest.alreadyAuthenticated=This is request has already been 
authenticated
+coyoteRequest.noLoginConfig=No authentication mechanism has been configured 
for this context
+coyoteRequest.noPasswordLogin=The authentication mechanism configured for this 
context does not support user name and password authentication
+coyoteRequest.authFail=The authentication of user {0} was not successful
+coyoteRequest.authenticate.ise=Cannot call authenticate() after the reponse 
has been committed
 
 requestFacade.nullRequest=The request object has been recycled and is no 
longer associated with this facade
 

Modified: tomcat/trunk/java/org/apache/catalina/connector/Request.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/Request.java?rev=835460&r1=835459&r2=835460&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/connector/Request.java (original)
+++ tomcat/trunk/java/org/apache/catalina/connector/Request.java Thu Nov 12 
17:29:00 2009
@@ -54,6 +54,7 @@
 import javax.servlet.http.HttpSession;
 import javax.servlet.http.Part;
 
+import org.apache.catalina.Authenticator;
 import org.apache.catalina.Context;
 import org.apache.catalina.Globals;
 import org.apache.catalina.Host;
@@ -63,6 +64,7 @@
 import org.apache.catalina.Wrapper;
 import org.apache.catalina.core.ApplicationSessionCookieConfig;
 import org.apache.catalina.core.AsyncContextImpl;
+import org.apache.catalina.deploy.LoginConfig;
 import org.apache.catalina.realm.GenericPrincipal;
 import org.apache.catalina.util.Enumerator;
 import org.apache.catalina.util.ParameterMap;
@@ -2307,18 +2309,70 @@
         return requestedSessionSSL;
     }
     
-    public boolean authenticate(HttpServletResponse response) throws 
IOException {
-        // TODO Servlet 3 - authentication
+    /**
+     * @throws IOException If an I/O error occurs
+     * @throws IllegalStateException If the response has been committed
+     * @throws ServletException If the caller is responsible for handling the
+     *         error and the container has NOT set the HTTP response code etc.
+     */
+    public boolean authenticate(HttpServletResponse response) 
+    throws IOException, ServletException {
+        if (response.isCommitted()) {
+            throw new IllegalStateException(
+                    sm.getString("coyoteRequest.authenticate.ise"));
+        }
+
+        // TODO SERVLET 3
         return false;
     }
     
+    /**
+     * @throws ServletException If any of {...@link #getRemoteUser()},
+     *         {...@link #getUserPrincipal()} or {...@link #getAuthType()} are
+     *         non-null, if the configured authenticator does not support
+     *         user name and password authentication or if the authentication
+     *         fails
+     */
     public void login(String username, String password)
     throws ServletException {
-        // TODO Servlet 3 - authentication
+        if (getAuthType() != null || getRemoteUser() != null ||
+                getUserPrincipal() != null) {
+            throw new ServletException(
+                    sm.getString("coyoteRequest.alreadyAuthenticated"));
+        }
+        
+        if (context.getLoginConfig() == null) {
+            throw new ServletException(
+                    sm.getString("coyoteRequest.noLoginConfig"));
+        }
+        
+        String authMethod = context.getLoginConfig().getAuthMethod();
+        if (BASIC_AUTH.equals(authMethod) || FORM_AUTH.equals(authMethod) ||
+                DIGEST_AUTH.equals(authMethod)) {
+            // Methods support user name and password authentication
+            Realm realm = context.getRealm();
+            
+            Principal principal = realm.authenticate(username, password);
+
+            if (principal == null) {
+                throw new ServletException(
+                        sm.getString("coyoteRequest.authFail", username));
+            }
+            // Assume if we have a non-null LogonConfig then we must have an
+            // authenticator
+            context.getAuthenticator().register(this, getResponse(), principal,
+                    authMethod, username, password);
+        } else {
+            throw new ServletException("coyoteRequest.noPasswordLogin");
+        }
     }
-    
+
+    /**
+     * @throws ServletException If the logout fails
+     */
     public void logout() throws ServletException {
-        // TODO Servlet 3 - authentication
+        context.getAuthenticator().register(this, getResponse(), null,
+                null, null, null);
     }
     
     public Collection<Part> getParts() {

Modified: tomcat/trunk/java/org/apache/catalina/connector/RequestFacade.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/RequestFacade.java?rev=835460&r1=835459&r2=835460&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/connector/RequestFacade.java 
(original)
+++ tomcat/trunk/java/org/apache/catalina/connector/RequestFacade.java Thu Nov 
12 17:29:00 2009
@@ -982,7 +982,8 @@
         return request.getDispatcherType();
     }
     
-    public boolean authenticate(HttpServletResponse response) throws 
IOException {
+    public boolean authenticate(HttpServletResponse response)
+    throws IOException, ServletException {
         return request.authenticate(response);
     }
 

Modified: tomcat/trunk/java/org/apache/catalina/core/StandardContext.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardContext.java?rev=835460&r1=835459&r2=835460&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/StandardContext.java (original)
+++ tomcat/trunk/java/org/apache/catalina/core/StandardContext.java Thu Nov 12 
17:29:00 2009
@@ -57,6 +57,7 @@
 import javax.servlet.http.HttpSessionAttributeListener;
 import javax.servlet.http.HttpSessionListener;
 
+import org.apache.catalina.Authenticator;
 import org.apache.catalina.Container;
 import org.apache.catalina.ContainerListener;
 import org.apache.catalina.Context;
@@ -69,6 +70,8 @@
 import org.apache.catalina.LifecycleListener;
 import org.apache.catalina.Loader;
 import org.apache.catalina.Manager;
+import org.apache.catalina.Pipeline;
+import org.apache.catalina.Valve;
 import org.apache.catalina.Wrapper;
 import org.apache.catalina.deploy.ApplicationParameter;
 import org.apache.catalina.deploy.ErrorPage;
@@ -738,6 +741,24 @@
     // ----------------------------------------------------- Context Properties
 
 
+    public Authenticator getAuthenticator() {
+        if (this instanceof Authenticator)
+            return (Authenticator) this;
+        
+        Pipeline pipeline = getPipeline();
+        if (pipeline != null) {
+            Valve basic = pipeline.getBasic();
+            if ((basic != null) && (basic instanceof Authenticator))
+                return (Authenticator) basic;
+            Valve valves[] = pipeline.getValves();
+            for (int i = 0; i < valves.length; i++) {
+                if (valves[i] instanceof Authenticator)
+                    return (Authenticator) valves[i];
+            }
+        }
+        return null;
+    }
+    
     public JarScanner getJarScanner() {
         if (jarScanner == null) {
             jarScanner = new DefaultJarScanner();

Modified: tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java?rev=835460&r1=835459&r2=835460&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java (original)
+++ tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java Thu Nov 12 
17:29:00 2009
@@ -29,7 +29,6 @@
 import java.net.URL;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.LinkedHashSet;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
@@ -388,21 +387,10 @@
         }
 
         // Has an authenticator been configured already?
-        if (context instanceof Authenticator)
+        if (context.getAuthenticator() != null)
             return;
-        if (context instanceof ContainerBase) {
-            Pipeline pipeline = ((ContainerBase) context).getPipeline();
-            if (pipeline != null) {
-                Valve basic = pipeline.getBasic();
-                if ((basic != null) && (basic instanceof Authenticator))
-                    return;
-                Valve valves[] = pipeline.getValves();
-                for (int i = 0; i < valves.length; i++) {
-                    if (valves[i] instanceof Authenticator)
-                        return;
-                }
-            }
-        } else {
+        
+        if (!(context instanceof ContainerBase)) {
             return;     // Cannot install a Valve even if it would be needed
         }
 
@@ -1237,23 +1225,25 @@
         parseWebXml(contextWebXml, webXml, false);
         
         if (!webXml.isMetadataComplete()) {
+            // Process /WEB-INF/classes for annotations
+            // TODO SERVLET3
+
             // Have to process JARs for fragments
             Map<String,WebXml> fragments = processJarsForWebFragments();
             
             // Merge the fragments into the main web.xml
-            Set<WebXml> orderedFragments = orderWebFragments(webXml, 
fragments);
+            Set<WebXml> orderedFragments =
+                WebXml.orderWebFragments(webXml, fragments);
+
+            // Process JARs for annotations - only need to process those
+            // fragments we are going to use
+            processAnnotationsInJars(orderedFragments);
 
             // Merge fragment into application
             if (ok) {
                 ok = webXml.merge(orderedFragments);
             }
 
-            // Process JARs for annotations
-            processAnnotationsInJars(fragments);
-
-            // Process /WEB-INF/classes for annotations
-            // TODO SERVLET3
-
             // Apply merged web.xml to Context
             webXml.configureContext(context);
         } else {
@@ -1566,58 +1556,11 @@
         }
     }
 
-    /**
-     * 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
-     */
-    protected static Set<WebXml> orderWebFragments(WebXml application,
-            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
-                    for (String name : fragments.keySet()) {
-                        if (!requestedOrder.contains(name)) {
-                            WebXml fragment = fragments.get(name);
-                            if (fragment != null) {
-                                orderedFragments.add(fragment);
-                            }
-                        }
-                    }
-                } else {
-                    WebXml fragment = fragments.get(requestedName);
-                    if (fragment != null) {
-                        orderedFragments.add(fragment);
-                    }
-                }
-            }
-        } else {
-            // TODO SERVLET3 Relative ordering
-        }
-        
-        return orderedFragments;
-    }
-
-    
-    protected void processAnnotationsInJars(Map<String,WebXml> fragments) {
-        for(String name : fragments.keySet()) {
-            WebXml fragment = fragments.get(name);
-            if (fragment == null || !fragment.isMetadataComplete()) {
-                // Scan jar for annotations
+    protected void processAnnotationsInJars(Set<WebXml> fragments) {
+        for(WebXml fragment : fragments) {
+            if (!fragment.isMetadataComplete()) {
+                // Scan jar for annotations, add to fragment
                 // TODO SERVLET3
             }
         }

Modified: tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties?rev=835460&r1=835459&r2=835460&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties 
(original)
+++ tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties Thu 
Nov 12 17:29:00 2009
@@ -118,6 +118,7 @@
 webXml.mergeConflictErrorPage=The Error Page for [{0}] was defined 
inconsistently in multiple fragments including fragment with name [{1}] located 
at [{2}]
 webXml.mergeConflictFilter=The Filter [{0}] was defined inconsistently in 
multiple fragments including fragment with name [{1}] located at [{2}]
 webXml.mergeConflictLoginConfig=A LoginConfig was defined inconsistently in 
multiple fragments including fragment with name [{1}] located at [{2}]
+webXml.mergeConflictOrder=Fragment relative ordering contains circular 
references. Thsi can be resolved by using absolute ordering in web.xml.
 webXml.mergeConflictResource=The Resource [{0}] was defined inconsistently in 
multiple fragments including fragment with name [{1}] located at [{2}]
 webXml.mergeConflictFilter=The Servlet [{0}] was defined inconsistently in 
multiple fragments including fragment with name [{1}] located at [{2}]
 webXml.mergeConflictSessionTimeout=The session timeout was defined 
inconsistently in multiple fragments with different values including fragment 
with name [{0}] located at [{1}]

Modified: tomcat/trunk/java/org/apache/catalina/startup/WebRuleSet.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/WebRuleSet.java?rev=835460&r1=835459&r2=835460&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/startup/WebRuleSet.java (original)
+++ tomcat/trunk/java/org/apache/catalina/startup/WebRuleSet.java Thu Nov 12 
17:29:00 2009
@@ -161,11 +161,10 @@
         digester.addRule(fullPrefix,
                          new IgnoreAnnotationsRule());
 
-        digester.addCallMethod(fullPrefix + "/name",
-                "setName", 0);
-        
         if (fragment) {
             // web-fragment.xml
+            digester.addCallMethod(fullPrefix + "/name",
+                    "setName", 0);
             digester.addRule(fullPrefix + "/absolute-ordering",
                     new AbsoluteOrderingRule());
             digester.addCallMethod(fullPrefix + "/ordering/after/name",

Modified: tomcat/trunk/java/org/apache/catalina/startup/WebXml.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/WebXml.java?rev=835460&r1=835459&r2=835460&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/startup/WebXml.java (original)
+++ tomcat/trunk/java/org/apache/catalina/startup/WebXml.java Thu Nov 12 
17:29:00 2009
@@ -23,6 +23,8 @@
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -88,28 +90,31 @@
     // web-fragment.xml only elements
     // Relative ordering
     private Set<String> after = new LinkedHashSet<String>();
-    public void addAfterOrdering(String fragmentName) {
+    public void addAfterOrder(String fragmentName) {
         after.add(fragmentName);
     }
-    public void addAfterOrderingOthers() {
+    public void addAfterOrderOthers() {
         if (before.contains(ORDER_OTHERS)) {
             throw new IllegalArgumentException(sm.getString(
                     "webXml.multipleOther"));
         }
         after.add(ORDER_OTHERS);
     }
+    public Set<String> getAfterOrder() { return after; }
+    
     private Set<String> before = new LinkedHashSet<String>();
-    public void addBeforeOrdering(String fragmentName) {
+    public void addBeforeOrder(String fragmentName) {
         before.add(fragmentName);
     }
-    public void addBeforeOrderingOthers() {
+    public void addBeforeOrderOthers() {
         if (after.contains(ORDER_OTHERS)) {
             throw new IllegalArgumentException(sm.getString(
                     "webXml.multipleOther"));
         }
         before.add(ORDER_OTHERS);
     }
-    
+    public Set<String> getBeforeOrder() { return before; }
+
     // Common elements and attributes
     
     // Required attribute of web-app element
@@ -441,6 +446,19 @@
     public void setURL(URL url) { this.uRL = url; }
     public URL getURL() { return uRL; }
     
+    
+    
+    @Override
+    public String toString() {
+        StringBuilder buf = new StringBuilder(32);
+        buf.append("Name: ");
+        buf.append(getName());
+        buf.append(", URL: ");
+        buf.append(getURL());
+        return buf.toString();
+    }
+    
+    
     /**
      * Configure a {...@link Context} using the stored web.xml representation.
      *  
@@ -1004,4 +1022,115 @@
         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
+     */
+    protected static Set<WebXml> orderWebFragments(WebXml application,
+            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
+                    for (String name : fragments.keySet()) {
+                        if (!requestedOrder.contains(name)) {
+                            WebXml fragment = fragments.get(name);
+                            if (fragment != null) {
+                                orderedFragments.add(fragment);
+                            }
+                        }
+                    }
+                } else {
+                    WebXml fragment = fragments.get(requestedName);
+                    if (fragment != null) {
+                        orderedFragments.add(fragment);
+                    }
+                }
+            }
+        } 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.getBeforeOrder().contains(WebXml.ORDER_OTHERS)) {
+                    // Move to beginning
+                    order.remove(name);
+                    order.add(0, name);
+                } else if 
(fragment.getAfterOrder().contains(WebXml.ORDER_OTHERS)) {
+                    // Move to end
+                    order.remove(name);
+                    order.add(name);
+                }
+            }
+            
+            // Now apply remaining ordering
+            for (WebXml fragment : fragments.values()) {
+                String name = fragment.getName();
+                for (String before : fragment.getBeforeOrder()) {
+                    if (!before.equals(WebXml.ORDER_OTHERS) &&
+                            order.contains(before) &&
+                            order.indexOf(before) < order.indexOf(name)) {
+                        order.remove(name);
+                        order.add(order.indexOf(before), name);
+                    }
+                }
+                for (String after : fragment.getAfterOrder()) {
+                    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);
+                    }
+                }
+            }
+            
+            // Finally check ordering was applied correctly - if there are
+            // errors then that indicates circular references
+            for (WebXml fragment : fragments.values()) {
+                String name = fragment.getName();
+                for (String before : fragment.getBeforeOrder()) {
+                    if (!before.equals(WebXml.ORDER_OTHERS) &&
+                            order.contains(before) &&
+                            order.indexOf(before) < order.indexOf(name)) {
+                        throw new IllegalArgumentException(sm.getString(""));
+                    }
+                }
+                for (String after : fragment.getAfterOrder()) {
+                    if (!after.equals(WebXml.ORDER_OTHERS) &&
+                            order.contains(after) &&
+                            order.indexOf(after) > order.indexOf(name)) {
+                        throw new IllegalArgumentException();
+                    }
+                }
+            }
+            
+            // Build the ordered list
+            for (String name : order) {
+                orderedFragments.add(fragments.get(name));
+            }
+        }
+        
+        return orderedFragments;
+    }
+
+}    
+

Modified: 
tomcat/trunk/test/org/apache/catalina/connector/TestKeepAliveCount.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/connector/TestKeepAliveCount.java?rev=835460&r1=835459&r2=835460&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/connector/TestKeepAliveCount.java 
(original)
+++ tomcat/trunk/test/org/apache/catalina/connector/TestKeepAliveCount.java Thu 
Nov 12 17:29:00 2009
@@ -120,6 +120,8 @@
     
     private static class SimpleServlet extends HttpServlet {
 
+        private static final long serialVersionUID = 1L;
+
         @Override
         protected void service(HttpServletRequest req, HttpServletResponse 
resp) throws ServletException, IOException {
             resp.setContentLength(0);

Modified: tomcat/trunk/test/org/apache/catalina/connector/TestRequest.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/connector/TestRequest.java?rev=835460&r1=835459&r2=835460&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/connector/TestRequest.java (original)
+++ tomcat/trunk/test/org/apache/catalina/connector/TestRequest.java Thu Nov 12 
17:29:00 2009
@@ -30,6 +30,8 @@
 import org.apache.catalina.startup.SimpleHttpClient;
 import org.apache.catalina.startup.TomcatBaseTest;
 import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TestTomcat.MapRealm;
+import org.apache.tomcat.util.buf.ByteChunk;
 
 /**
  * Test case for {...@link Request}. 
@@ -192,4 +194,61 @@
         }
         
     }
+
+    /**
+     * Test case for {...@link Request#login(String, String)} and
+     * {...@link Request#logout()}.
+     */
+    public void testLoginLogout() throws Exception{
+        // Setup Tomcat instance
+        Tomcat tomcat = getTomcatInstance();
+        
+        // Must have a real docBase - just use temp
+        StandardContext ctx = 
+            tomcat.addContext("/", System.getProperty("java.io.tmpdir"));
+        // You can customize the context by calling 
+        // its API
+        
+        Tomcat.addServlet(ctx, "servlet", new LoginLogoutServlet());
+        ctx.addServletMapping("/", "servlet");
+        
+        MapRealm realm = new MapRealm();
+        realm.addUser(LoginLogoutServlet.USER, LoginLogoutServlet.PWD);
+        ctx.setRealm(realm);
+        
+        tomcat.start();
+        
+        ByteChunk res = getUrl("http://localhost:"; + getPort() + "/");
+
+        assertEquals(LoginLogoutServlet.OK, res.toString());
+    }
+    
+    private static final class LoginLogoutServlet extends HttpServlet {
+        private static final long serialVersionUID = 1L;
+        private static final String USER = "user";
+        private static final String PWD = "pwd";
+        private static final String OK = "OK";
+        
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+                throws ServletException, IOException {
+            
+            req.login(USER, PWD);
+            
+            if (!req.getRemoteUser().equals(USER))
+                throw new ServletException();
+            if (!req.getUserPrincipal().getName().equals(USER))
+                throw new ServletException();
+            
+            req.logout();
+            
+            if (req.getRemoteUser() != null)
+                throw new ServletException();
+            if (req.getUserPrincipal() != null)
+                throw new ServletException();
+            
+            resp.getWriter().write(OK);
+        }
+        
+    }
 }

Modified: tomcat/trunk/test/org/apache/catalina/startup/TestTomcat.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/startup/TestTomcat.java?rev=835460&r1=835459&r2=835460&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/startup/TestTomcat.java (original)
+++ tomcat/trunk/test/org/apache/catalina/startup/TestTomcat.java Thu Nov 12 
17:29:00 2009
@@ -19,6 +19,9 @@
 import java.io.File;
 import java.io.IOException;
 import java.net.URL;
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.Map;
 
 import javax.naming.Context;
 import javax.naming.InitialContext;
@@ -29,6 +32,8 @@
 
 import org.apache.catalina.core.StandardContext;
 import org.apache.catalina.deploy.ContextEnvironment;
+import org.apache.catalina.realm.GenericPrincipal;
+import org.apache.catalina.realm.RealmBase;
 import org.apache.tomcat.util.buf.ByteChunk;
 
 public class TestTomcat extends TomcatBaseTest {
@@ -95,6 +100,34 @@
         }
     }
     
+    /**
+     * Simple Realm that uses a configurable {...@link Map} to link user names 
and
+     * passwords. No roles are supported at this stage.
+     */
+    public static final class MapRealm extends RealmBase {
+        private Map<String,String> users = new HashMap<String,String>();
+        
+        public void addUser(String username, String password) {
+            users.put(username, password);
+        }
+
+        @Override
+        protected String getName() {
+            return "MapRealm";
+        }
+
+        @Override
+        protected String getPassword(String username) {
+            return users.get(username);
+        }
+
+        @Override
+        protected Principal getPrincipal(String username) {
+            return new GenericPrincipal(username, getPassword(username));
+        }
+        
+    }
+
     /** 
      * Start tomcat with a single context and one 
      * servlet - all programmatic, no server.xml or 

Copied: tomcat/trunk/test/org/apache/catalina/startup/TestWebXml.java (from 
r834808, tomcat/trunk/test/org/apache/catalina/startup/TestContextConfig.java)
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/startup/TestWebXml.java?p2=tomcat/trunk/test/org/apache/catalina/startup/TestWebXml.java&p1=tomcat/trunk/test/org/apache/catalina/startup/TestContextConfig.java&r1=834808&r2=835460&rev=835460&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/startup/TestContextConfig.java 
(original)
+++ tomcat/trunk/test/org/apache/catalina/startup/TestWebXml.java Thu Nov 12 
17:29:00 2009
@@ -11,137 +11,218 @@
 /**
  * Test case for {...@link ContextConfig}.
  */
-public class TestContextConfig extends TestCase {
+public class TestWebXml extends TestCase {
     private WebXml app;
-    private WebXml f1;
-    private WebXml f2;
-    private WebXml f3;
-    private WebXml f4;
-    private WebXml f5;
+    private WebXml a;
+    private WebXml b;
+    private WebXml c;
+    private WebXml d;
+    private WebXml e;
+    private WebXml f;
     private Map<String,WebXml> fragments;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         app = new WebXml();
-        f1 = new WebXml();
-        f1.setName("f1");
-        f2 = new WebXml();
-        f2.setName("f2");
-        f3 = new WebXml();
-        f3.setName("f3");
-        f4 = new WebXml();
-        f4.setName("f4");
-        f5 = new WebXml();
-        f5.setName("f5");
+        a = new WebXml();
+        a.setName("a");
+        b = new WebXml();
+        b.setName("b");
+        c = new WebXml();
+        c.setName("c");
+        d = new WebXml();
+        d.setName("d");
+        e = new WebXml();
+        e.setName("e");
+        f = new WebXml();
+        f.setName("f");
         fragments = new HashMap<String,WebXml>();
-        fragments.put("f1",f1);
-        fragments.put("f2",f2);
-        fragments.put("f3",f3);
-        fragments.put("f4",f4);
-        fragments.put("f5",f5);
+        fragments.put("a",a);
+        fragments.put("b",b);
+        fragments.put("c",c);
+        fragments.put("d",d);
+        fragments.put("e",e);
+        fragments.put("f",f);
     }
 
-
     public void testOrderWebFragmentsAbsolute() {
-        app.addAbsoluteOrdering("f3");
-        app.addAbsoluteOrdering("f1");
-        app.addAbsoluteOrdering("f2");
-        app.addAbsoluteOrdering("f5");
-        app.addAbsoluteOrdering("f4");
-        
-        Set<WebXml> ordered = ContextConfig.orderWebFragments(app, fragments);
+        app.addAbsoluteOrdering("c");
+        app.addAbsoluteOrdering("a");
+        app.addAbsoluteOrdering("b");
+        app.addAbsoluteOrdering("e");
+        app.addAbsoluteOrdering("d");
+        
+        Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments);
         
         Iterator<WebXml> iter = ordered.iterator();
-        assertEquals(f3,iter.next());
-        assertEquals(f1,iter.next());
-        assertEquals(f2,iter.next());
-        assertEquals(f5,iter.next());
-        assertEquals(f4,iter.next());
+        assertEquals(c,iter.next());
+        assertEquals(a,iter.next());
+        assertEquals(b,iter.next());
+        assertEquals(e,iter.next());
+        assertEquals(d,iter.next());
         assertFalse(iter.hasNext());
     }
 
     public void testOrderWebFragmentsAbsolutePartial() {
-        app.addAbsoluteOrdering("f3");
-        app.addAbsoluteOrdering("f1");
+        app.addAbsoluteOrdering("c");
+        app.addAbsoluteOrdering("a");
         
-        Set<WebXml> ordered = ContextConfig.orderWebFragments(app, fragments);
+        Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments);
         
         Iterator<WebXml> iter = ordered.iterator();
-        assertEquals(f3,iter.next());
-        assertEquals(f1,iter.next());
+        assertEquals(c,iter.next());
+        assertEquals(a,iter.next());
         assertFalse(iter.hasNext());
     }
 
     public void testOrderWebFragmentsAbsoluteOthersStart() {
         app.addAbsoluteOrdering(WebXml.ORDER_OTHERS);
-        app.addAbsoluteOrdering("f2");
-        app.addAbsoluteOrdering("f4");
+        app.addAbsoluteOrdering("b");
+        app.addAbsoluteOrdering("d");
         
         Set<WebXml> others = new HashSet<WebXml>();
-        others.add(f1);
-        others.add(f3);
-        others.add(f5);
+        others.add(a);
+        others.add(c);
+        others.add(e);
+        others.add(f);
         
-        Set<WebXml> ordered = ContextConfig.orderWebFragments(app, fragments);
+        Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments);
         
         Iterator<WebXml> iter = ordered.iterator();
         while (others.size() > 0) {
-            WebXml f = iter.next();
-            assertTrue(others.contains(f));
-            others.remove(f);
+            WebXml o = iter.next();
+            assertTrue(others.contains(o));
+            others.remove(o);
         }
-        assertEquals(f2,iter.next());
-        assertEquals(f4,iter.next());
+        assertEquals(b,iter.next());
+        assertEquals(d,iter.next());
         assertFalse(iter.hasNext());
     }
 
     public void testOrderWebFragmentsAbsoluteOthersMiddle() {
-        app.addAbsoluteOrdering("f2");
+        app.addAbsoluteOrdering("b");
         app.addAbsoluteOrdering(WebXml.ORDER_OTHERS);
-        app.addAbsoluteOrdering("f4");
+        app.addAbsoluteOrdering("d");
         
         Set<WebXml> others = new HashSet<WebXml>();
-        others.add(f1);
-        others.add(f3);
-        others.add(f5);
+        others.add(a);
+        others.add(c);
+        others.add(e);
+        others.add(f);
         
-        Set<WebXml> ordered = ContextConfig.orderWebFragments(app, fragments);
+        Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments);
 
         Iterator<WebXml> iter = ordered.iterator();
-        assertEquals(f2,iter.next());
+        assertEquals(b,iter.next());
 
         while (others.size() > 0) {
-            WebXml f = iter.next();
-            assertTrue(others.contains(f));
-            others.remove(f);
+            WebXml o = iter.next();
+            assertTrue(others.contains(o));
+            others.remove(o);
         }
-        assertEquals(f4,iter.next());
+        assertEquals(d,iter.next());
         assertFalse(iter.hasNext());
     }
 
     public void testOrderWebFragmentsAbsoluteOthersEnd() {
-        app.addAbsoluteOrdering("f2");
-        app.addAbsoluteOrdering("f4");
+        app.addAbsoluteOrdering("b");
+        app.addAbsoluteOrdering("d");
         app.addAbsoluteOrdering(WebXml.ORDER_OTHERS);
         
         Set<WebXml> others = new HashSet<WebXml>();
-        others.add(f1);
-        others.add(f3);
-        others.add(f5);
+        others.add(a);
+        others.add(c);
+        others.add(e);
+        others.add(f);
         
-        Set<WebXml> ordered = ContextConfig.orderWebFragments(app, fragments);
+        Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments);
 
         Iterator<WebXml> iter = ordered.iterator();
-        assertEquals(f2,iter.next());
-        assertEquals(f4,iter.next());
+        assertEquals(b,iter.next());
+        assertEquals(d,iter.next());
 
         while (others.size() > 0) {
-            WebXml f = iter.next();
-            assertTrue(others.contains(f));
-            others.remove(f);
+            WebXml o = iter.next();
+            assertTrue(others.contains(o));
+            others.remove(o);
         }
         assertFalse(iter.hasNext());
     }
 
+    public void testOrderWebFragmentsRelative1() {
+        // First example from servlet spec
+        a.addAfterOrderOthers();
+        a.addAfterOrder("c");
+        b.addBeforeOrderOthers();
+        c.addAfterOrderOthers();
+        f.addBeforeOrderOthers();
+        f.addBeforeOrder("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());
+    }
+    
+    public void testOrderWebFragmentsRelative2() {
+        // Second example - use fragment a for no-id fragment
+        a.addAfterOrderOthers();
+        a.addBeforeOrder("c");
+        b.addBeforeOrderOthers();
+        d.addAfterOrderOthers();
+        e.addBeforeOrderOthers();
+        
+        Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments);
+        
+        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());
+    }
+    
+    public void testOrderWebFragmentsRelative3() {
+        // Third example from spec
+        a.addAfterOrder("b");
+        c.addBeforeOrderOthers();
+        fragments.remove("e");
+        fragments.remove("f");
+
+        Set<WebXml> ordered = WebXml.orderWebFragments(app, fragments);
+        
+        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());
+    }
+    
+    public void testOrderWebFragmentsrelativeCircular() {
+        a.addBeforeOrder("b");
+        b.addBeforeOrder("a");
+
+        Exception e = null;
+        
+        try {
+            WebXml.orderWebFragments(app, fragments);
+        } catch (Exception e1) {
+            e = e1;
+        }
+        
+        assertTrue(e instanceof IllegalArgumentException);
+    }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to