390163 Implement ServletRegistration.Dynamic.setServletSecurity
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java
index ee2c45c..bfbef9b 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java
@@ -21,6 +21,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.servlet.ServletSecurityElement;
 import javax.servlet.annotation.HttpConstraint;
 import javax.servlet.annotation.HttpMethodConstraint;
 import javax.servlet.annotation.ServletSecurity;
@@ -30,12 +31,13 @@
 import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
 import org.eclipse.jetty.security.ConstraintAware;
 import org.eclipse.jetty.security.ConstraintMapping;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.eclipse.jetty.servlet.ServletMapping;
-import org.eclipse.jetty.util.LazyList;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.security.Constraint;
+import org.eclipse.jetty.webapp.Origin;
 import org.eclipse.jetty.webapp.WebAppContext;
 
 /**
@@ -82,7 +84,7 @@
        if (servletSecurity == null)
            return;
        
-       //If there are already constraints defined (ie from web.xml or programmatically(?)) that match any 
+       //If there are already constraints defined (ie from web.xml) that match any 
        //of the url patterns defined for this servlet, then skip the security annotation.
       
        List<ServletMapping> servletMappings = getServletMappings(clazz.getCanonicalName());
@@ -97,19 +99,15 @@
        //Make a fresh list
        constraintMappings = new ArrayList<ConstraintMapping>();
        
-       //Get the values that form the constraints that will apply unless there are HttpMethodConstraints to augment them
-       HttpConstraint defaults = servletSecurity.value();
-
-       //Make a Constraint for the <auth-constraint> and <user-data-constraint> specified by the HttpConstraint
-       Constraint defaultConstraint = makeConstraint (clazz, 
-                                                      defaults.rolesAllowed(), 
-                                                      defaults.value(), 
-                                                      defaults.transportGuarantee());
-
-       constraintMappings.addAll(makeMethodMappings(clazz, 
-                                                    defaultConstraint, 
-                                                    servletMappings, 
-                                                    servletSecurity.httpMethodConstraints()));
+       ServletSecurityElement securityElement = new ServletSecurityElement(servletSecurity);
+       for (ServletMapping sm : servletMappings)
+       {
+           for (String url : sm.getPathSpecs())
+           {
+               _context.getMetaData().setOrigin("constraint.url."+url, Origin.Annotation);
+               constraintMappings.addAll(ConstraintSecurityHandler.createConstraintsWithMappingsForPath(clazz.getName(), url, securityElement));
+           }
+       }
 
        //set up the security constraints produced by the annotation
        ConstraintAware securityHandler = (ConstraintAware)_context.getSecurityHandler();
@@ -131,111 +129,10 @@
      */
     protected Constraint makeConstraint (Class servlet, String[] rolesAllowed, EmptyRoleSemantic permitOrDeny, TransportGuarantee transport)
     {  
-        Constraint constraint = new Constraint();
-        if (rolesAllowed == null || rolesAllowed.length==0)
-        {           
-           if (permitOrDeny.equals(EmptyRoleSemantic.DENY))
-           {
-               //Equivalent to <auth-constraint> with no roles
-               constraint.setName(servlet.getName()+"-Deny");
-               constraint.setAuthenticate(true);
-           }
-           else
-           {
-               //Equivalent to no <auth-constraint>
-               constraint.setAuthenticate(false);
-               constraint.setName(servlet.getName()+"-Permit");
-           }
-        }
-        else
-        {
-            //Equivalent to <auth-constraint> with list of <security-role-name>s
-            constraint.setAuthenticate(true);
-            constraint.setRoles(rolesAllowed);
-            constraint.setName(servlet.getName()+"-RolesAllowed");           
-        } 
-        
-      //Equivalent to //<user-data-constraint><transport-guarantee>CONFIDENTIAL</transport-guarantee></user-data-constraint>
-      constraint.setDataConstraint((transport.equals(TransportGuarantee.CONFIDENTIAL)?Constraint.DC_CONFIDENTIAL:Constraint.DC_NONE));
-      return constraint;
+        return ConstraintSecurityHandler.createConstraint(servlet.getName(), rolesAllowed, permitOrDeny, transport);
     }
     
     
-    /**
-     * Make a ConstraintMapping which captures the <http-method> or <http-method-omission> elements for a particular url pattern,
-     * and relates it to a Constraint object (<auth-constraint> and <user-data-constraint>).
-     * @param constraint
-     * @param url
-     * @param method
-     * @param omissions
-     * @return
-     */
-    protected ConstraintMapping makeConstraintMapping (Constraint constraint, String url, String method, String[] omissions)
-    {
-        ConstraintMapping mapping = new ConstraintMapping();
-        mapping.setConstraint(constraint);
-        mapping.setPathSpec(url);
-        if (method != null)
-            mapping.setMethod(method);
-        if (omissions != null)
-            mapping.setMethodOmissions(omissions);
-        return mapping;
-    }
-  
-    /**
-     * Make the Jetty Constraints and ConstraintMapping objects that correspond to the HttpMethodConstraint
-     * annotations for each url pattern for the servlet.
-     * @param servlet
-     * @param defaultConstraint
-     * @param servletMappings
-     * @param annotations
-     * @return
-     */
-    protected List<ConstraintMapping> makeMethodMappings (Class servlet, Constraint defaultConstraint, List<ServletMapping> servletMappings, HttpMethodConstraint[] annotations)
-    {
-        List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
-        
-        //for each url-pattern existing for the servlet make a ConstraintMapping for the HttpConstraint, and ConstraintMappings for
-        //each HttpMethodConstraint
-        for (ServletMapping sm : servletMappings)
-        {
-            for (String url : sm.getPathSpecs())
-            {
-                //Make a ConstraintMapping that matches the defaultConstraint
-                ConstraintMapping defaultMapping = makeConstraintMapping(defaultConstraint, url, null, null);
-                
-                //If there are HttpMethodConstraint annotations, make a Constraint and a ConstraintMapping for it
-                if (annotations != null && annotations.length>0)
-                {    
-                    List<String> omissions = new ArrayList<String>();
-                    
-                    //for each HttpMethodConstraint annotation, make a new Constraint and ConstraintMappings for this url
-                    for (int i=0;  i < annotations.length;i++)
-                    {
-                        //Make a Constraint that captures the <auth-constraint> and <user-data-constraint> elements
-                        Constraint methodConstraint = makeConstraint(servlet, 
-                                                                     annotations[i].rolesAllowed(), 
-                                                                     annotations[i].emptyRoleSemantic(),
-                                                                     annotations[i].transportGuarantee());
-
-                        //Make ConstraintMapping that captures the <http-method> elements                        
-                        ConstraintMapping methodConstraintMapping = makeConstraintMapping (methodConstraint,
-                                                                                           url,annotations[i].value(), 
-                                                                                           null);
-                        mappings.add(methodConstraintMapping);
-                        omissions.add(annotations[i].value()); 
-                    }   
-                    defaultMapping.setMethodOmissions(omissions.toArray(new String[0]));
-                }
-
-                //add the constraint mapping containing the http-method-omissions, if there are any
-                mappings.add(defaultMapping);
-            }
-        }             
-        return mappings;
-    }
-    
-   
     
     /**
      * Get the ServletMappings for the servlet's class.
@@ -284,6 +181,7 @@
            {
                for (int j=0; j < pathSpecs.length; j++)
                {
+                   //TODO decide if we need to check the origin
                    if (pathSpecs[j].equals(constraintMappings.get(i).getPathSpec()))
                    {
                        exists = true;
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
index 94f70f5..5712c1d 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
@@ -24,7 +24,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -51,7 +50,7 @@
 /* ------------------------------------------------------------ */
 /**
  * Handler to enforce SecurityConstraints. This implementation is servlet spec
- * 2.4 compliant and precomputes the constraint combinations for runtime
+ * 3.0 compliant and precomputes the constraint combinations for runtime
  * efficiency.
  *
  */
@@ -191,9 +190,11 @@
     
     
     /* ------------------------------------------------------------ */
-    /**
+    /** Take out of the constraint mappings those that match the 
+     * given path.
+     * 
      * @param pathSpec
-     * @param constraintMappings
+     * @param constraintMappings a new list minus the matching constraints
      * @return
      */
     public static List<ConstraintMapping> removeConstraintMappingsForPath(String pathSpec, List<ConstraintMapping> constraintMappings)
@@ -351,8 +352,6 @@
      */
     public void setConstraintMappings(List<ConstraintMapping> constraintMappings, Set<String> roles)
     {
-        if (isStarted())
-            throw new IllegalStateException("Started");
         _constraintMappings.clear();
         _constraintMappings.addAll(constraintMappings);
 
@@ -371,6 +370,14 @@
             }
         }
         setRoles(roles);
+        
+        if (isStarted())
+        {
+            for (ConstraintMapping mapping : _constraintMappings)
+            {
+                processConstraintMapping(mapping);
+            }
+        }
     }
 
     /* ------------------------------------------------------------ */
@@ -383,9 +390,6 @@
      */
     public void setRoles(Set<String> roles)
     {
-        if (isStarted())
-            throw new IllegalStateException("Started");
-
         _roles.clear();
         _roles.addAll(roles);
     }
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
index 1c71363..055060a 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
@@ -48,6 +48,7 @@
 import javax.servlet.descriptor.TaglibDescriptor;
 
 import org.eclipse.jetty.security.ConstraintAware;
+import org.eclipse.jetty.security.ConstraintMapping;
 import org.eclipse.jetty.security.ConstraintSecurityHandler;
 import org.eclipse.jetty.security.SecurityHandler;
 import org.eclipse.jetty.server.Dispatcher;
@@ -59,6 +60,7 @@
 import org.eclipse.jetty.server.handler.HandlerWrapper;
 import org.eclipse.jetty.server.session.SessionHandler;
 import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.security.Constraint;
 
 
 /* ------------------------------------------------------------ */
@@ -377,10 +379,21 @@
      * @param registration ServletRegistration.Dynamic instance that setServletSecurity was called on
      * @param servletSecurityElement new security info
      * @return the set of exact URL mappings currently associated with the registration that are also present in the web.xml
-     * security constratins and thus will be unaffected by this call.
+     * security constraints and thus will be unaffected by this call.
      */
     public Set<String> setServletSecurity(ServletRegistration.Dynamic registration, ServletSecurityElement servletSecurityElement)
     {
+        //Default implementation is to just accept them all. If using a webapp, then this behaviour is overridden in WebAppContext.setServletSecurity       
+        Collection<String> pathSpecs = registration.getMappings();
+        if (pathSpecs != null)
+        {
+            for (String pathSpec:pathSpecs)
+            {
+                List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
+                for (ConstraintMapping m:mappings)
+                    ((ConstraintAware)getSecurityHandler()).addConstraintMapping(m);
+            }
+        }
         return Collections.emptySet();
     }
 
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java
index 284a1cd..cdd84b7 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java
@@ -538,6 +538,15 @@
         OriginInfo x = new OriginInfo (name, Origin.Annotation);
         _origins.put(name, x);
     }
+    
+    public void setOrigin(String name, Origin origin)
+    {
+        if (name == null)
+            return;
+       
+        OriginInfo x = new OriginInfo (name, origin);
+        _origins.put(name, x);
+    }
 
     public boolean isMetaDataComplete()
     {
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Origin.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Origin.java
index 1481b38..f2f941c 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Origin.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Origin.java
@@ -18,4 +18,4 @@
 
 package org.eclipse.jetty.webapp;
 
-public enum Origin {NotSet, WebXml, WebDefaults, WebOverride, WebFragment, Annotation}
\ No newline at end of file
+public enum Origin {NotSet, WebXml, WebDefaults, WebOverride, WebFragment, Annotation, API}
\ No newline at end of file
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
index ed0d8f2..45ffa3a 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
@@ -25,18 +25,29 @@
 import java.security.PermissionCollection;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.EventListener;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
+import javax.servlet.HttpMethodConstraintElement;
 import javax.servlet.ServletContext;
+import javax.servlet.ServletRegistration.Dynamic;
+import javax.servlet.ServletSecurityElement;
+import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
+import javax.servlet.annotation.ServletSecurity.TransportGuarantee;
 import javax.servlet.http.HttpSessionActivationListener;
 import javax.servlet.http.HttpSessionAttributeListener;
 import javax.servlet.http.HttpSessionBindingListener;
 import javax.servlet.http.HttpSessionListener;
 
+import org.eclipse.jetty.security.ConstraintAware;
+import org.eclipse.jetty.security.ConstraintMapping;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
 import org.eclipse.jetty.security.SecurityHandler;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.HandlerContainer;
@@ -56,6 +67,7 @@
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.resource.Resource;
 import org.eclipse.jetty.util.resource.ResourceCollection;
+import org.eclipse.jetty.util.security.Constraint;
 
 /* ------------------------------------------------------------ */
 /** Web Application Context Handler.
@@ -1237,6 +1249,79 @@
 
         super.startContext();
     }
+       
+    /* ------------------------------------------------------------ */    
+    @Override
+    public Set<String> setServletSecurity(Dynamic registration, ServletSecurityElement servletSecurityElement)
+    {
+     
+        Set<String> unchangedURLMappings = new HashSet<String>();
+        //From javadoc for ServletSecurityElement:
+        /*
+        If a URL pattern of this ServletRegistration is an exact target of a security-constraint that 
+        was established via the portable deployment descriptor, then this method does not change the 
+        security-constraint for that pattern, and the pattern will be included in the return value.
+
+        If a URL pattern of this ServletRegistration is an exact target of a security constraint 
+        that was established via the ServletSecurity annotation or a previous call to this method, 
+        then this method replaces the security constraint for that pattern.
+
+        If a URL pattern of this ServletRegistration is neither the exact target of a security constraint 
+        that was established via the ServletSecurity annotation or a previous call to this method, 
+        nor the exact target of a security-constraint in the portable deployment descriptor, then 
+        this method establishes the security constraint for that pattern from the argument ServletSecurityElement. 
+         */
+
+        Collection<String> pathMappings = registration.getMappings();
+        if (pathMappings != null)
+        {
+            Constraint constraint = ConstraintSecurityHandler.createConstraint(registration.getName(), servletSecurityElement);
+
+            for (String pathSpec:pathMappings)
+            {
+                Origin origin = getMetaData().getOrigin("constraint.url."+pathSpec);
+               
+                switch (origin)
+                {
+                    case NotSet:
+                    {
+                        //No mapping for this url already established
+                        List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
+                        for (ConstraintMapping m:mappings)
+                            ((ConstraintAware)getSecurityHandler()).addConstraintMapping(m);
+                        getMetaData().setOrigin("constraint.url."+pathSpec, Origin.API);
+                        break;
+                    }
+                    case WebXml:
+                    case WebDefaults:
+                    case WebOverride:
+                    case WebFragment:
+                    {
+                        //a mapping for this url was created in a descriptor, which overrides everything
+                        unchangedURLMappings.add(pathSpec);
+                        break;
+                    }
+                    case Annotation:
+                    case API:
+                    {
+                        //mapping established via an annotation or by previous call to this method,
+                        //replace the security constraint for this pattern
+                        List<ConstraintMapping> constraintMappings = ConstraintSecurityHandler.removeConstraintMappingsForPath(pathSpec, ((ConstraintAware)getSecurityHandler()).getConstraintMappings());
+                       
+                        List<ConstraintMapping> freshMappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
+                        constraintMappings.addAll(freshMappings);
+                           
+                        ((ConstraintSecurityHandler)getSecurityHandler()).setConstraintMappings(constraintMappings);
+                        break;
+                    }
+                }
+            }
+        }
+        
+        return unchangedURLMappings;
+    }
+
+
 
     /* ------------------------------------------------------------ */
     public class Context extends ServletContextHandler.Context
@@ -1287,6 +1372,8 @@
             }
         }
 
+        
+        
     }
 
     /* ------------------------------------------------------------ */
diff --git a/test-jetty-webapp/src/main/config/contexts/test.d/override-web.xml b/test-jetty-webapp/src/main/config/contexts/test.d/override-web.xml
index 9e42d6d..7b7f9e4 100644
--- a/test-jetty-webapp/src/main/config/contexts/test.d/override-web.xml
+++ b/test-jetty-webapp/src/main/config/contexts/test.d/override-web.xml
@@ -15,15 +15,6 @@
     <param-value>a context value</param-value>
   </context-param>
 
-  <!-- Add or override filter init parameter -->
-  <filter>
-    <filter-name>TestFilter</filter-name>
-    <filter-class>com.acme.TestFilter</filter-class>
-    <init-param>
-      <param-name>remote</param-name>
-      <param-value>false</param-value>
-    </init-param>
-  </filter>
 
   <!-- Add or override servlet init parameter -->
   <servlet>
diff --git a/test-jetty-webapp/src/main/java/com/acme/RegTest.java b/test-jetty-webapp/src/main/java/com/acme/RegTest.java
new file mode 100644
index 0000000..8d99cd1
--- /dev/null
+++ b/test-jetty-webapp/src/main/java/com/acme/RegTest.java
@@ -0,0 +1,194 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package com.acme;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.util.StringUtil;
+
+
+
+
+/* ------------------------------------------------------------ */
+/** Rego Servlet - tests being accessed from servlet 3.0 programmatic 
+ * configuration.
+ * 
+ */
+public class RegTest extends HttpServlet
+{
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void init(ServletConfig config) throws ServletException
+    {
+    	super.init(config);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        doGet(request, response);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
+    { 
+        request.setCharacterEncoding("UTF-8");        
+        PrintWriter pout=null;
+        
+        try
+        {
+            pout =response.getWriter();
+        }
+        catch(IllegalStateException e)
+        {
+            pout=new PrintWriter(new OutputStreamWriter(response.getOutputStream(),"UTF-8"));
+        }
+   
+        try
+        {
+            pout.write("<html>\n<body>\n");
+            pout.write("<h1>Rego Servlet</h1>\n");
+            pout.write("<table width=\"95%\">");
+            pout.write("<tr>\n");
+            pout.write("<th align=\"right\">getMethod:&nbsp;</th>");
+            pout.write("<td>" + notag(request.getMethod())+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getContentLength:&nbsp;</th>");
+            pout.write("<td>"+Integer.toString(request.getContentLength())+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getContentType:&nbsp;</th>");
+            pout.write("<td>"+notag(request.getContentType())+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getRequestURI:&nbsp;</th>");
+            pout.write("<td>"+notag(request.getRequestURI())+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getRequestURL:&nbsp;</th>");
+            pout.write("<td>"+notag(request.getRequestURL().toString())+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getContextPath:&nbsp;</th>");
+            pout.write("<td>"+request.getContextPath()+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getServletPath:&nbsp;</th>");
+            pout.write("<td>"+notag(request.getServletPath())+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getPathInfo:&nbsp;</th>");
+            pout.write("<td>"+notag(request.getPathInfo())+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getPathTranslated:&nbsp;</th>");
+            pout.write("<td>"+notag(request.getPathTranslated())+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getQueryString:&nbsp;</th>");
+            pout.write("<td>"+notag(request.getQueryString())+"</td>");
+            pout.write("</tr><tr>\n");
+            
+            pout.write("<th align=\"right\">getProtocol:&nbsp;</th>");
+            pout.write("<td>"+request.getProtocol()+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getScheme:&nbsp;</th>");
+            pout.write("<td>"+request.getScheme()+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getServerName:&nbsp;</th>");
+            pout.write("<td>"+notag(request.getServerName())+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getServerPort:&nbsp;</th>");
+            pout.write("<td>"+Integer.toString(request.getServerPort())+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getLocalName:&nbsp;</th>");
+            pout.write("<td>"+request.getLocalName()+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getLocalAddr:&nbsp;</th>");
+            pout.write("<td>"+request.getLocalAddr()+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getLocalPort:&nbsp;</th>");
+            pout.write("<td>"+Integer.toString(request.getLocalPort())+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getRemoteUser:&nbsp;</th>");
+            pout.write("<td>"+request.getRemoteUser()+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getUserPrincipal:&nbsp;</th>");
+            pout.write("<td>"+request.getUserPrincipal()+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getRemoteAddr:&nbsp;</th>");
+            pout.write("<td>"+request.getRemoteAddr()+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getRemoteHost:&nbsp;</th>");
+            pout.write("<td>"+request.getRemoteHost()+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getRemotePort:&nbsp;</th>");
+            pout.write("<td>"+request.getRemotePort()+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">getRequestedSessionId:&nbsp;</th>");
+            pout.write("<td>"+request.getRequestedSessionId()+"</td>");
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">isSecure():&nbsp;</th>");
+            pout.write("<td>"+request.isSecure()+"</td>");
+
+            pout.write("</tr><tr>\n");
+            pout.write("<th align=\"right\">isUserInRole(admin):&nbsp;</th>");
+            pout.write("<td>"+request.isUserInRole("admin")+"</td>");
+
+            pout.write("</tr></table>");
+   
+        }
+        catch (Exception e)
+        {
+            getServletContext().log("dump "+e);
+        }
+        
+        
+        pout.write("</body>\n</html>\n");
+        
+        pout.close();
+    }
+
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String getServletInfo()
+    {
+        return "Rego Servlet";
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public synchronized void destroy()
+    {
+    }
+
+    
+    private String notag(String s)
+    {
+        if (s==null)
+            return "null";
+        s=StringUtil.replace(s,"&","&amp;");
+        s=StringUtil.replace(s,"<","&lt;");
+        s=StringUtil.replace(s,">","&gt;");
+        return s;
+    }
+}
diff --git a/test-jetty-webapp/src/main/java/com/acme/TestListener.java b/test-jetty-webapp/src/main/java/com/acme/TestListener.java
index faaa760..cf950ed 100644
--- a/test-jetty-webapp/src/main/java/com/acme/TestListener.java
+++ b/test-jetty-webapp/src/main/java/com/acme/TestListener.java
@@ -18,6 +18,7 @@
 
 package com.acme;
 
+import javax.servlet.DispatcherType;
 import javax.servlet.ServletContextAttributeEvent;
 import javax.servlet.ServletContextAttributeListener;
 import javax.servlet.ServletContextEvent;
@@ -26,6 +27,12 @@
 import javax.servlet.ServletRequestAttributeListener;
 import javax.servlet.ServletRequestEvent;
 import javax.servlet.ServletRequestListener;
+import javax.servlet.ServletRegistration;
+import javax.servlet.FilterRegistration;
+import javax.servlet.ServletSecurityElement;
+import javax.servlet.HttpConstraintElement;
+import javax.servlet.HttpMethodConstraintElement;
+import javax.servlet.annotation.ServletSecurity;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSessionActivationListener;
 import javax.servlet.http.HttpSessionAttributeListener;
@@ -33,6 +40,9 @@
 import javax.servlet.http.HttpSessionEvent;
 import javax.servlet.http.HttpSessionListener;
 
+import java.util.EnumSet;
+import java.util.Set;
+
 public class TestListener implements HttpSessionListener,  HttpSessionAttributeListener, HttpSessionActivationListener, ServletContextListener, ServletContextAttributeListener, ServletRequestListener, ServletRequestAttributeListener
 {
     public void attributeAdded(HttpSessionBindingEvent se)
@@ -62,16 +72,30 @@
 
     public void contextInitialized(ServletContextEvent sce)
     {	
-    	/* TODO  for servlet 3.0
-    	 * FilterRegistration registration=context.addFilter("TestFilter",TestFilter.class.getName());
-    	
-    	
+        //configure programmatic security
+        ServletRegistration.Dynamic rego = sce.getServletContext().addServlet("RegoTest", RegTest.class.getName());
+        rego.addMapping("/rego/*");
+        HttpConstraintElement constraintElement = new HttpConstraintElement(ServletSecurity.EmptyRoleSemantic.PERMIT, 
+                                                                            ServletSecurity.TransportGuarantee.NONE, new String[]{"admin"});
+        ServletSecurityElement securityElement = new ServletSecurityElement(constraintElement, null);
+        Set<String> unchanged = rego.setServletSecurity(securityElement);
+        System.err.println("Security constraints registered: "+unchanged.isEmpty());
+
+        //Test that a security constraint from web.xml can't be overridden programmatically
+        ServletRegistration.Dynamic rego2 = sce.getServletContext().addServlet("RegoTest2", RegTest.class.getName());
+        rego2.addMapping("/rego2/*");
+        securityElement = new ServletSecurityElement(constraintElement, null);
+        unchanged = rego2.setServletSecurity(securityElement);
+        System.err.println("Overridding web.xml constraints not possible:" +!unchanged.isEmpty());
+
+    	/* For servlet 3.0 */
+    	FilterRegistration.Dynamic registration = sce.getServletContext().addFilter("TestFilter",TestFilter.class.getName());
+    	registration.setInitParameter("remote", "false");
     	registration.setAsyncSupported(true);
     	registration.addMappingForUrlPatterns(
     	        EnumSet.of(DispatcherType.ERROR,DispatcherType.ASYNC,DispatcherType.FORWARD,DispatcherType.INCLUDE,DispatcherType.REQUEST),
     	        true, 
-    	        new String[]{"/dump/*","/dispatch/*","*.dump"});
-    	        */
+    	        new String[]{"/*"});
     }
 
     public void contextDestroyed(ServletContextEvent sce)
diff --git a/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml b/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml
index a0c2bb3..9fc7003 100644
--- a/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml
+++ b/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml
@@ -18,20 +18,6 @@
     <listener-class>com.acme.TestListener</listener-class>
   </listener>
 
-  <filter>
-    <filter-name>TestFilter</filter-name>
-    <filter-class>com.acme.TestFilter</filter-class>
-    <async-support>true</async-support>
-    <init-param>
-      <param-name>remote</param-name>
-      <param-value>false</param-value>
-    </init-param>
-  </filter>
-  <filter-mapping>
-    <filter-name>TestFilter</filter-name>
-    <url-pattern>/*</url-pattern>
-  </filter-mapping>
-
 
   <filter>
     <filter-name>QoSFilter</filter-name>
@@ -120,7 +106,6 @@
   </filter-mapping>
   -->
 
-
   <servlet>
     <servlet-name>Hello</servlet-name>
     <servlet-class>com.acme.HelloWorld</servlet-class>
@@ -275,6 +260,18 @@
     <location>/error404.html</location>
   </error-page>
 
+
+
+  <security-constraint>
+    <web-resource-collection>
+      <web-resource-name>Rego2</web-resource-name>
+      <url-pattern>/rego2/*</url-pattern>
+    </web-resource-collection>
+    <auth-constraint>
+      <role-name>server-administrator</role-name>
+    </auth-constraint>
+  </security-constraint>
+
   <security-constraint>
     <web-resource-collection>
       <web-resource-name>Auth2</web-resource-name>
diff --git a/test-jetty-webapp/src/main/webapp/auth.html b/test-jetty-webapp/src/main/webapp/auth.html
index 1b1de11..0bce2d5 100644
--- a/test-jetty-webapp/src/main/webapp/auth.html
+++ b/test-jetty-webapp/src/main/webapp/auth.html
@@ -18,6 +18,8 @@
 <li><a href="dump/auth/info">dump/auth/*</a> - Authenticated any user</li>
 <li><a href="dump/auth/admin/info">dump/auth/admin/*</a> - Authenticated admin role (<a href="session/?Action=Invalidate">click</a> to invalidate session)</li>
 <li><a href="dump/auth/ssl/info">dump/auth/ssl/*</a> - Confidential</li>
+<li><a href="rego/info">rego/info/*</a> - Authenticated admin role from programmatic security (<a href="session/?Action=Invalidate">click</a> to invalidate session)</li>
+<li><a href="rego2/info">rego2/info/*</a> - Authenticated servlet-administrator role from programmatic security (login as admin/admin, <a href="session/?Action=Invalidate">click</a> to invalidate session)</li>
 </ul>
 <p/>
 <p>