[seam-commits] Seam SVN: r11279 - in branches/enterprise/JBPAPP_5_0: src/main/org/jboss/seam/mock and 1 other directory.

seam-commits at lists.jboss.org seam-commits at lists.jboss.org
Mon Jul 13 11:50:48 EDT 2009


Author: manaRH
Date: 2009-07-13 11:50:48 -0400 (Mon, 13 Jul 2009)
New Revision: 11279

Added:
   branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/DelegatingServletInputStream.java
   branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/DelegatingServletOutputStream.java
   branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/EnhancedMockHttpServletRequest.java
   branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/EnhancedMockHttpServletResponse.java
   branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/HeaderValueHolder.java
   branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/MockRequestDispatcher.java
   branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/ResourceRequestEnvironment.java
Removed:
   branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/AbstractDBUnitSeamTest.java
Modified:
   branches/enterprise/JBPAPP_5_0/doc/Seam_Reference_Guide/en-US/Webservices.xml
Log:
back ported JBSEAM-4195

Modified: branches/enterprise/JBPAPP_5_0/doc/Seam_Reference_Guide/en-US/Webservices.xml
===================================================================
--- branches/enterprise/JBPAPP_5_0/doc/Seam_Reference_Guide/en-US/Webservices.xml	2009-07-13 12:20:22 UTC (rev 11278)
+++ branches/enterprise/JBPAPP_5_0/doc/Seam_Reference_Guide/en-US/Webservices.xml	2009-07-13 15:50:48 UTC (rev 11279)
@@ -599,40 +599,49 @@
          <title>Testing resources and providers</title>
          
          <para>
-            Seam includes an extended unit testing superclass that helps you in creating unit tests for a RESTful
-            architecture. Extend the <literal>ResourceSeamTest</literal> class to emulate HTTP requests/response cycles:
+            Seam includes a unit testing utility class that helps you create unit tests for a RESTful
+            architecture. Extend the <literal>SeamTest</literal> class as usual and use the
+            <literal>ResourceRequestEnvironment.ResourceRequest</literal> to emulate HTTP requests/response cycles:
          </para>
       
-         <programlisting role="JAVA"><![CDATA[import org.jboss.seam.resteasy.testfwk.ResourceSeamTest;
-import org.jboss.seam.resteasy.testfwk.MockHttpServletResponse;
-import org.jboss.seam.resteasy.testfwk.MockHttpServletRequest;
+         <programlisting role="JAVA"><![CDATA[import org.jboss.seam.mock.ResourceRequestEnvironment;
+import org.jboss.seam.mock.EnhancedMockHttpServletRequest;
+import org.jboss.seam.mock.EnhancedMockHttpServletResponse;
+import static org.jboss.seam.mock.ResourceRequestEnvironment.ResourceRequest;
+import static org.jboss.seam.mock.ResourceRequestEnvironment.Method;
 
-public class MyTest extends ResourceSeamTest {
+public class MyTest extends SeamTest {
 
-   @Override
-   public Map<String, Object> getDefaultHeaders()
-   {
-      return new HashMap<String, Object>()
-      {{
-            put("Accept", "text/plain");
-      }};
+   ResourceRequestEnvironment sharedEnvironment;
+
+   @BeforeClass
+   public void prepareSharedEnvironment() throws Exception {
+       sharedEnvironment = new ResourceRequestEnvironment(this) {
+            @Override
+            public Map<String, Object> getDefaultHeaders() {
+               return new HashMap<String, Object>() {{
+                   put("Accept", "text/plain");
+               }};
+            }
+         };
    }
 
    @Test
    public void test() throws Exception
    {
-      new ResourceRequest(Method.GET, "/my/relative/uri)
+      //Not shared: new ResourceRequest(new ResourceRequestEnvironment(this), Method.GET, "/my/relative/uri)
+
+      new ResourceRequest(sharedEnvironment, Method.GET, "/my/relative/uri)
       {
-
          @Override
-         protected void prepareRequest(MockHttpServletRequest request)
+         protected void prepareRequest(EnhancedMockHttpServletRequest request)
          {
             request.addQueryParameter("foo", "123");
             request.addHeader("Accept-Language", "en_US, de");
          }
 
          @Override
-         protected void onResponse(MockHttpServletResponse response)
+         protected void onResponse(EnhancedMockHttpServletResponse response)
          {
             assert response.getStatus() == 200;
             assert response.getContentAsString().equals("foobar");
@@ -640,30 +649,22 @@
 
       }.run();
    }
-
 }]]></programlisting>
 
          <para>
             This test only executes local calls, it does not communicate with the <literal>SeamResourceServlet</literal>
             through TCP. The mock request is passed through the Seam servlet and filters and the response is then
-            available for test assertions. Overriding the <literal>getDefaultHeaders()</literal> method allows you
-            to set request headers for every test method in the test class.
+            available for test assertions. Overriding the <literal>getDefaultHeaders()</literal> method in a shared
+            instance of <literal>ResourceRequestEnvironment</literal> allows you to set request headers for every
+            test method in the test class.
          </para>
 
          <para>
             Note that a <literal>ResourceRequest</literal> has to be executed in a <literal>@Test</literal> method
-            or in a <literal>@BeforeMethod</literal> callback. You can and should not execute it in any other callback,
-            such as <literal>@BeforeClass</literal>. (This is an implementation limitation we will remove in a future
-            update.)
+            or in a <literal>@BeforeMethod</literal> callback. You can not execute it in any other callback,
+            such as <literal>@BeforeClass</literal>.
          </para>
 
-         <para>
-            Also note that the imported mock objects are not the same as the mock objects you use in other Seam unit
-            tests, which are in the package <literal>org.jboss.seam.mock</literal>. The
-            <literal>org.jboss.seam.resteasy.testfwk</literal> variations mimic real requests and responses much
-            more closely.
-         </para>
-
       </sect2>
 
    </sect1>

Deleted: branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/AbstractDBUnitSeamTest.java
===================================================================

Added: branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/DelegatingServletInputStream.java
===================================================================
--- branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/DelegatingServletInputStream.java	                        (rev 0)
+++ branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/DelegatingServletInputStream.java	2009-07-13 15:50:48 UTC (rev 11279)
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jboss.seam.mock;
+
+import javax.servlet.ServletInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Delegating implementation of {@link javax.servlet.ServletInputStream}.
+ * <p/>
+ * <p>Used by {@link MockHttpServletRequest}; typically not directly
+ * used for testing application controllers.
+ *
+ * @author Juergen Hoeller
+ * @see MockHttpServletRequest
+ * @since 1.0.2
+ */
+public class DelegatingServletInputStream extends ServletInputStream
+{
+
+   private final InputStream sourceStream;
+
+
+   /**
+    * Create a DelegatingServletInputStream for the given source stream.
+    *
+    * @param sourceStream the source stream (never <code>null</code>)
+    */
+   public DelegatingServletInputStream(InputStream sourceStream)
+   {
+      this.sourceStream = sourceStream;
+   }
+
+   /**
+    * Return the underlying source stream (never <code>null</code>).
+    */
+   public final InputStream getSourceStream()
+   {
+      return this.sourceStream;
+   }
+
+
+   public int read() throws IOException
+   {
+      return this.sourceStream.read();
+   }
+
+   public void close() throws IOException
+   {
+      super.close();
+      this.sourceStream.close();
+	}
+
+}
\ No newline at end of file

Added: branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/DelegatingServletOutputStream.java
===================================================================
--- branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/DelegatingServletOutputStream.java	                        (rev 0)
+++ branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/DelegatingServletOutputStream.java	2009-07-13 15:50:48 UTC (rev 11279)
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jboss.seam.mock;
+
+import javax.servlet.ServletOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+/**
+ * Delegating implementation of {@link javax.servlet.ServletOutputStream}.
+ * <p/>
+ * <p>Used by {@link MockHttpServletResponse}; typically not directly
+ * used for testing application controllers.
+ *
+ * @author Juergen Hoeller
+ * @see MockHttpServletResponse
+ * @since 1.0.2
+ */
+public class DelegatingServletOutputStream extends ServletOutputStream
+{
+
+   private final OutputStream targetStream;
+
+
+   /**
+    * Create a DelegatingServletOutputStream for the given target stream.
+    *
+    * @param targetStream the target stream (never <code>null</code>)
+    */
+   public DelegatingServletOutputStream(OutputStream targetStream)
+   {
+      this.targetStream = targetStream;
+   }
+
+   /**
+    * Return the underlying target stream (never <code>null</code>).
+    */
+   public final OutputStream getTargetStream()
+   {
+      return this.targetStream;
+   }
+
+
+   public void write(int b) throws IOException
+   {
+      this.targetStream.write(b);
+   }
+
+   public void flush() throws IOException
+   {
+      super.flush();
+      this.targetStream.flush();
+   }
+
+   public void close() throws IOException
+   {
+      super.close();
+      this.targetStream.close();
+   }
+
+}
\ No newline at end of file

Copied: branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/EnhancedMockHttpServletRequest.java (from rev 11257, branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/MockHttpServletRequest.java)
===================================================================
--- branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/EnhancedMockHttpServletRequest.java	                        (rev 0)
+++ branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/EnhancedMockHttpServletRequest.java	2009-07-13 15:50:48 UTC (rev 11279)
@@ -0,0 +1,1093 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jboss.seam.mock;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
+/**
+ * Mock implementation of the {@link javax.servlet.http.HttpServletRequest}
+ * interface. Supports the Servlet 2.4 API level.
+ * <p/>
+ * <p>Used for testing the web framework; also useful for testing
+ * application controllers.
+ *
+ * @author Juergen Hoeller
+ * @author Rod Johnson
+ * @author Rick Evans
+ * @author Mark Fisher
+ * @since 1.0.2
+ */
+public class EnhancedMockHttpServletRequest implements HttpServletRequest
+{
+
+   /**
+    * The default protocol: 'http'.
+    */
+   public static final String DEFAULT_PROTOCOL = "http";
+
+   /**
+    * The default server address: '127.0.0.1'.
+    */
+   public static final String DEFAULT_SERVER_ADDR = "127.0.0.1";
+
+   /**
+    * The default server name: 'localhost'.
+    */
+   public static final String DEFAULT_SERVER_NAME = "localhost";
+
+   /**
+    * The default server port: '80'.
+    */
+   public static final int DEFAULT_SERVER_PORT = 80;
+
+   /**
+    * The default remote address: '127.0.0.1'.
+    */
+   public static final String DEFAULT_REMOTE_ADDR = "127.0.0.1";
+
+   /**
+    * The default remote host: 'localhost'.
+    */
+   public static final String DEFAULT_REMOTE_HOST = "localhost";
+
+   private boolean active = true;
+
+
+   //---------------------------------------------------------------------
+   // ServletRequest properties
+   //---------------------------------------------------------------------
+
+   private final Hashtable attributes = new Hashtable();
+
+   private String characterEncoding;
+
+   private byte[] content;
+
+   private String contentType;
+
+   private final Map parameters = new LinkedHashMap(16);
+
+   private String protocol = DEFAULT_PROTOCOL;
+
+   private String scheme = DEFAULT_PROTOCOL;
+
+   private String serverName = DEFAULT_SERVER_NAME;
+
+   private int serverPort = DEFAULT_SERVER_PORT;
+
+   private String remoteAddr = DEFAULT_REMOTE_ADDR;
+
+   private String remoteHost = DEFAULT_REMOTE_HOST;
+
+   /**
+    * List of locales in descending order
+    */
+   private final Vector locales = new Vector();
+
+   private boolean secure = false;
+
+   private final ServletContext servletContext;
+
+   private int remotePort = DEFAULT_SERVER_PORT;
+
+   private String localName = DEFAULT_SERVER_NAME;
+
+   private String localAddr = DEFAULT_SERVER_ADDR;
+
+   private int localPort = DEFAULT_SERVER_PORT;
+
+
+   //---------------------------------------------------------------------
+   // HttpServletRequest properties
+   //---------------------------------------------------------------------
+
+   private String authType;
+
+   private Cookie[] cookies;
+
+   /**
+    * The key is the lowercase header name; the value is a {@link org.jboss.seam.mock.HeaderValueHolder} object.
+    */
+   private final Hashtable headers = new Hashtable();
+
+   private String method;
+
+   private String pathInfo;
+
+   private String contextPath = "";
+
+   private String queryString;
+
+   private Map<String, String> queryParameters = new HashMap();
+
+   private String remoteUser;
+
+   private Set<String> userRoles = new HashSet();
+
+   private Principal userPrincipal;
+
+   private String requestURI;
+
+   private String servletPath = "";
+
+   private HttpSession session;
+
+   private boolean requestedSessionIdValid = true;
+
+   private boolean requestedSessionIdFromCookie = true;
+
+   private boolean requestedSessionIdFromURL = false;
+
+
+   //---------------------------------------------------------------------
+   // Constructors
+   //---------------------------------------------------------------------
+
+   /**
+    * Create a new MockHttpServletRequest with a default
+    * {@link MockServletContext}.
+    *
+    * @see MockServletContext
+    */
+   public EnhancedMockHttpServletRequest()
+   {
+      this(null, "", "");
+   }
+
+   /**
+    * Create a new MockHttpServletRequest with a default
+    * {@link MockServletContext}.
+    *
+    * @param method     the request method (may be <code>null</code>)
+    * @param requestURI the request URI (may be <code>null</code>)
+    * @see #setMethod
+    * @see #setRequestURI
+    * @see MockServletContext
+    */
+   public EnhancedMockHttpServletRequest(String method, String requestURI)
+   {
+      this(null, method, requestURI);
+   }
+
+   /**
+    * Create a new MockHttpServletRequest.
+    *
+    * @param servletContext the ServletContext that the request runs in
+    *                       (may be <code>null</code> to use a default MockServletContext)
+    * @see MockServletContext
+    */
+   public EnhancedMockHttpServletRequest(ServletContext servletContext)
+   {
+      this(servletContext, "", "");
+   }
+
+   /**
+    * Create a new MockHttpServletRequest.
+    *
+    * @param servletContext the ServletContext that the request runs in
+    *                       (may be <code>null</code> to use a default MockServletContext)
+    * @param method         the request method (may be <code>null</code>)
+    * @param requestURI     the request URI (may be <code>null</code>)
+    * @see #setMethod
+    * @see #setRequestURI
+    * @see MockServletContext
+    */
+   public EnhancedMockHttpServletRequest(ServletContext servletContext, String method, String requestURI)
+   {
+      this.servletContext = (servletContext != null ? servletContext : new MockServletContext());
+      this.method = method;
+      this.requestURI = requestURI;
+      this.locales.add(Locale.ENGLISH);
+
+      // Old mock: The 1.2 RI NPEs if this header isn't present
+      addHeader("Accept", new String[0]);
+   }
+
+
+   // OLD CONSTRUCTORS FROM < SEAM 2.2 MOCKS!
+
+   public EnhancedMockHttpServletRequest(HttpSession session)
+   {
+      this(session, null, new HashSet<String>());
+   }
+
+   public EnhancedMockHttpServletRequest(HttpSession session, String principalName, Set<String> principalRoles)
+   {
+      this(session, principalName, principalRoles, new Cookie[]{}, null);
+   }
+
+   public EnhancedMockHttpServletRequest(HttpSession session, final String principalName, Set<String> principalRoles, Cookie[] cookies, String method)
+   {
+      this(null, method, "");
+      this.session = session;
+
+      this.userPrincipal = principalName == null
+            ? null
+            : new Principal()
+      {
+         public String getName()
+         {
+            return principalName;
+         }
+      };
+
+      this.userRoles = principalRoles;
+      this.cookies = cookies;
+
+      // Old mock: The 1.2 RI NPEs if this header isn't present
+      addHeader("Accept", new String[0]);
+   }
+
+
+   //---------------------------------------------------------------------
+   // Lifecycle methods
+   //---------------------------------------------------------------------
+
+   /**
+    * Return the ServletContext that this request is associated with.
+    * (Not available in the standard HttpServletRequest interface for some reason.)
+    */
+   public ServletContext getServletContext()
+   {
+      return this.servletContext;
+   }
+
+   /**
+    * Return whether this request is still active (that is, not completed yet).
+    */
+   public boolean isActive()
+   {
+      return this.active;
+   }
+
+   /**
+    * Mark this request as completed, keeping its state.
+    */
+   public void close()
+   {
+      this.active = false;
+   }
+
+   /**
+    * Invalidate this request, clearing its state.
+    */
+   public void invalidate()
+   {
+      close();
+      clearAttributes();
+   }
+
+   /**
+    * Check whether this request is still active (that is, not completed yet),
+    * throwing an IllegalStateException if not active anymore.
+    */
+   protected void checkActive() throws IllegalStateException
+   {
+      if (!this.active)
+      {
+         throw new IllegalStateException("Request is not active anymore");
+      }
+   }
+
+
+   //---------------------------------------------------------------------
+   // ServletRequest interface
+   //---------------------------------------------------------------------
+
+   public Object getAttribute(String name)
+   {
+      checkActive();
+      return this.attributes.get(name);
+   }
+
+   public Enumeration getAttributeNames()
+   {
+      checkActive();
+      return this.attributes.keys();
+   }
+
+   public String getCharacterEncoding()
+   {
+      return this.characterEncoding;
+   }
+
+   public void setCharacterEncoding(String characterEncoding)
+   {
+      this.characterEncoding = characterEncoding;
+   }
+
+   public void setContent(byte[] content)
+   {
+      this.content = content;
+   }
+
+   public int getContentLength()
+   {
+      return (this.content != null ? this.content.length : -1);
+   }
+
+   public void setContentType(String contentType)
+   {
+      this.contentType = contentType;
+   }
+
+   public String getContentType()
+   {
+      return this.contentType;
+   }
+
+   public ServletInputStream getInputStream() throws IOException
+   {
+      if (this.content != null)
+      {
+         return new DelegatingServletInputStream(new ByteArrayInputStream(this.content));
+      }
+      else
+      {
+         return null;
+      }
+   }
+
+   /**
+    * Set a single value for the specified HTTP parameter.
+    * <p>If there are already one or more values registered for the given
+    * parameter name, they will be replaced.
+    */
+   public void setParameter(String name, String value)
+   {
+      setParameter(name, new String[]{value});
+   }
+
+   /**
+    * Set an array of values for the specified HTTP parameter.
+    * <p>If there are already one or more values registered for the given
+    * parameter name, they will be replaced.
+    */
+   public void setParameter(String name, String[] values)
+   {
+      this.parameters.put(name, values);
+   }
+
+   /**
+    * Sets all provided parameters <emphasis>replacing</emphasis> any
+    * existing values for the provided parameter names. To add without
+    * replacing existing values, use {@link #addParameters(java.util.Map)}.
+    */
+   public void setParameters(Map params)
+   {
+      for (Iterator it = params.keySet().iterator(); it.hasNext();)
+      {
+         Object key = it.next();
+         Object value = params.get(key);
+         if (value instanceof String)
+         {
+            this.setParameter((String) key, (String) value);
+         }
+         else if (value instanceof String[])
+         {
+            this.setParameter((String) key, (String[]) value);
+         }
+         else
+         {
+            throw new IllegalArgumentException("Parameter map value must be single value " +
+                  " or array of type [" + String.class.getName() + "]");
+         }
+      }
+   }
+
+   /**
+    * Add a single value for the specified HTTP parameter.
+    * <p>If there are already one or more values registered for the given
+    * parameter name, the given value will be added to the end of the list.
+    */
+   public void addParameter(String name, String value)
+   {
+      addParameter(name, new String[]{value});
+   }
+
+   /**
+    * Add an array of values for the specified HTTP parameter.
+    * <p>If there are already one or more values registered for the given
+    * parameter name, the given values will be added to the end of the list.
+    */
+   public void addParameter(String name, String[] values)
+   {
+      String[] oldArr = (String[]) this.parameters.get(name);
+      if (oldArr != null)
+      {
+         String[] newArr = new String[oldArr.length + values.length];
+         System.arraycopy(oldArr, 0, newArr, 0, oldArr.length);
+         System.arraycopy(values, 0, newArr, oldArr.length, values.length);
+         this.parameters.put(name, newArr);
+      }
+      else
+      {
+         this.parameters.put(name, values);
+      }
+   }
+
+   /**
+    * Adds all provided parameters <emphasis>without</emphasis> replacing
+    * any existing values. To replace existing values, use
+    * {@link #setParameters(java.util.Map)}.
+    */
+   public void addParameters(Map params)
+   {
+      for (Iterator it = params.keySet().iterator(); it.hasNext();)
+      {
+         Object key = it.next();
+         Object value = params.get(key);
+         if (value instanceof String)
+         {
+            this.addParameter((String) key, (String) value);
+         }
+         else if (value instanceof String[])
+         {
+            this.addParameter((String) key, (String[]) value);
+         }
+         else
+         {
+            throw new IllegalArgumentException("Parameter map value must be single value " +
+                  " or array of type [" + String.class.getName() + "]");
+         }
+      }
+   }
+
+   /**
+    * Remove already registered values for the specified HTTP parameter, if any.
+    */
+   public void removeParameter(String name)
+   {
+      this.parameters.remove(name);
+   }
+
+   /**
+    * Removes all existing parameters.
+    */
+   public void removeAllParameters()
+   {
+      this.parameters.clear();
+   }
+
+   public String getParameter(String name)
+   {
+      String[] arr = (String[]) this.parameters.get(name);
+      return (arr != null && arr.length > 0 ? arr[0] : null);
+   }
+
+   public Enumeration getParameterNames()
+   {
+      return Collections.enumeration(this.parameters.keySet());
+   }
+
+   public String[] getParameterValues(String name)
+   {
+      return (String[]) this.parameters.get(name);
+   }
+
+   public Map getParameterMap()
+   {
+      return this.parameters;
+   }
+
+   // Old mock
+   public Map<String, String[]> getParameters()
+   {
+      return parameters;
+   }
+
+   /**
+    * Add a query parameter that will be appended to the URI query string.
+    */
+   public void addQueryParameter(String name, String value)
+   {
+      addParameter(name, value);
+      this.queryParameters.put(name, value);
+   }
+
+   public void removeQueryParameter(String name)
+   {
+      removeParameter(name);
+      this.queryParameters.remove(name);
+   }
+
+   public Map<String, String> getQueryParameters()
+   {
+      return queryParameters;
+   }
+
+   public void setProtocol(String protocol)
+   {
+      this.protocol = protocol;
+   }
+
+   public String getProtocol()
+   {
+      return this.protocol;
+   }
+
+   public void setScheme(String scheme)
+   {
+      this.scheme = scheme;
+   }
+
+   public String getScheme()
+   {
+      return this.scheme;
+   }
+
+   public void setServerName(String serverName)
+   {
+      this.serverName = serverName;
+   }
+
+   public String getServerName()
+   {
+      return this.serverName;
+   }
+
+   public void setServerPort(int serverPort)
+   {
+      this.serverPort = serverPort;
+   }
+
+   public int getServerPort()
+   {
+      return this.serverPort;
+   }
+
+   public BufferedReader getReader() throws UnsupportedEncodingException
+   {
+      if (this.content != null)
+      {
+         InputStream sourceStream = new ByteArrayInputStream(this.content);
+         Reader sourceReader = (this.characterEncoding != null) ?
+               new InputStreamReader(sourceStream, this.characterEncoding) : new InputStreamReader(sourceStream);
+         return new BufferedReader(sourceReader);
+      }
+      else
+      {
+         return null;
+      }
+   }
+
+   public void setRemoteAddr(String remoteAddr)
+   {
+      this.remoteAddr = remoteAddr;
+   }
+
+   public String getRemoteAddr()
+   {
+      return this.remoteAddr;
+   }
+
+   public void setRemoteHost(String remoteHost)
+   {
+      this.remoteHost = remoteHost;
+   }
+
+   public String getRemoteHost()
+   {
+      return this.remoteHost;
+   }
+
+   public void setAttribute(String name, Object value)
+   {
+      checkActive();
+      if (value != null)
+      {
+         this.attributes.put(name, value);
+      }
+      else
+      {
+         this.attributes.remove(name);
+      }
+   }
+
+   public void removeAttribute(String name)
+   {
+      checkActive();
+      this.attributes.remove(name);
+   }
+
+   /**
+    * Clear all of this request's attributes.
+    */
+   public void clearAttributes()
+   {
+      this.attributes.clear();
+   }
+
+   /**
+    * Add a new preferred locale, before any existing locales.
+    */
+   public void addPreferredLocale(Locale locale)
+   {
+      this.locales.add(0, locale);
+   }
+
+   public Locale getLocale()
+   {
+      return (Locale) this.locales.get(0);
+   }
+
+   public Enumeration getLocales()
+   {
+      return this.locales.elements();
+   }
+
+   public void setSecure(boolean secure)
+   {
+      this.secure = secure;
+   }
+
+   public boolean isSecure()
+   {
+      return this.secure;
+   }
+
+   public RequestDispatcher getRequestDispatcher(String path)
+   {
+      return new MockRequestDispatcher(path);
+   }
+
+   public String getRealPath(String path)
+   {
+      return this.servletContext.getRealPath(path);
+   }
+
+   public void setRemotePort(int remotePort)
+   {
+      this.remotePort = remotePort;
+   }
+
+   public int getRemotePort()
+   {
+      return this.remotePort;
+   }
+
+   public void setLocalName(String localName)
+   {
+      this.localName = localName;
+   }
+
+   public String getLocalName()
+   {
+      return this.localName;
+   }
+
+   public void setLocalAddr(String localAddr)
+   {
+      this.localAddr = localAddr;
+   }
+
+   public String getLocalAddr()
+   {
+      return this.localAddr;
+   }
+
+   public void setLocalPort(int localPort)
+   {
+      this.localPort = localPort;
+   }
+
+   public int getLocalPort()
+   {
+      return this.localPort;
+   }
+
+
+   //---------------------------------------------------------------------
+   // HttpServletRequest interface
+   //---------------------------------------------------------------------
+
+   public void setAuthType(String authType)
+   {
+      this.authType = authType;
+   }
+
+   public String getAuthType()
+   {
+      return this.authType;
+   }
+
+   public void setCookies(Cookie[] cookies)
+   {
+      this.cookies = cookies;
+   }
+
+   public Cookie[] getCookies()
+   {
+      return this.cookies;
+   }
+
+   public void addCookie(Cookie cookie)
+   {
+      this.cookies = new Cookie[this.cookies.length + 1];
+      this.cookies[this.cookies.length - 1] = cookie;
+   }
+
+   /**
+    * Add a header entry for the given name.
+    * <p>If there was no entry for that header name before,
+    * the value will be used as-is. In case of an existing entry,
+    * a String array will be created, adding the given value (more
+    * specifically, its toString representation) as further element.
+    * <p>Multiple values can only be stored as list of Strings,
+    * following the Servlet spec (see <code>getHeaders</code> accessor).
+    * As alternative to repeated <code>addHeader</code> calls for
+    * individual elements, you can use a single call with an entire
+    * array or Collection of values as parameter.
+    *
+    * @see #getHeaderNames
+    * @see #getHeader
+    * @see #getHeaders
+    * @see #getDateHeader
+    * @see #getIntHeader
+    */
+   public void addHeader(String name, Object value)
+   {
+      HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
+      if (header == null)
+      {
+         header = new HeaderValueHolder();
+         this.headers.put(name, header);
+      }
+      if (value instanceof Collection)
+      {
+         header.addValues((Collection) value);
+      }
+      else if (value.getClass().isArray())
+      {
+         header.addValueArray(value);
+      }
+      else
+      {
+         header.addValue(value);
+      }
+   }
+
+   public long getDateHeader(String name)
+   {
+      HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
+      Object value = (header != null ? header.getValue() : null);
+      if (value instanceof Date)
+      {
+         return ((Date) value).getTime();
+      }
+      else if (value instanceof Number)
+      {
+         return ((Number) value).longValue();
+      }
+      else if (value != null)
+      {
+         throw new IllegalArgumentException(
+               "Value for header '" + name + "' is neither a Date nor a Number: " + value);
+      }
+      else
+      {
+         return -1L;
+      }
+   }
+
+   public String getHeader(String name)
+   {
+      HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
+      return (header != null ? header.getValue().toString() : null);
+   }
+
+   public Map<String, String[]> getHeaders()
+   {
+      Map<String, String[]> headerValues = new HashMap();
+
+      for (Object o : this.headers.entrySet())
+      {
+         Map.Entry<String, HeaderValueHolder> entry = (Map.Entry<String, HeaderValueHolder>)o;
+         String[] values = (String[])entry.getValue().getValues().toArray(new String[entry.getValue().getValues().size()]);
+         headerValues.put(entry.getKey(), values);
+      }
+
+      return headerValues;
+   }
+
+   public Enumeration getHeaders(String name)
+   {
+      HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
+      return Collections.enumeration(header != null ? header.getValues() : Collections.EMPTY_LIST);
+   }
+
+   public Enumeration getHeaderNames()
+   {
+      return this.headers.keys();
+   }
+
+   public int getIntHeader(String name)
+   {
+      HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
+      Object value = (header != null ? header.getValue() : null);
+      if (value instanceof Number)
+      {
+         return ((Number) value).intValue();
+      }
+      else if (value instanceof String)
+      {
+         return Integer.parseInt((String) value);
+      }
+      else if (value != null)
+      {
+         throw new NumberFormatException("Value for header '" + name + "' is not a Number: " + value);
+      }
+      else
+      {
+         return -1;
+      }
+   }
+
+   public void setMethod(String method)
+   {
+      this.method = method;
+   }
+
+   public String getMethod()
+   {
+      return this.method;
+   }
+
+   public void setPathInfo(String pathInfo)
+   {
+      this.pathInfo = pathInfo;
+   }
+
+   public String getPathInfo()
+   {
+      return this.pathInfo;
+   }
+
+   public String getPathTranslated()
+   {
+      return (this.pathInfo != null ? getRealPath(this.pathInfo) : null);
+   }
+
+   public void setContextPath(String contextPath)
+   {
+      this.contextPath = contextPath;
+   }
+
+   public String getContextPath()
+   {
+      return this.contextPath != null ? this.contextPath : "/project"; // Old mock default
+   }
+
+   public void setQueryString(String queryString)
+   {
+      this.queryString = queryString;
+   }
+
+   public String getQueryString()
+   {
+      if (getQueryParameters().size() > 0)
+      {
+         StringBuilder q = new StringBuilder(queryString);
+         if (!queryString.endsWith("&")) q.append("&");
+         for (Map.Entry<String, String> entry : getQueryParameters().entrySet())
+         {
+            q.append(entry.getKey());
+            q.append("=");
+            q.append(entry.getValue());
+            q.append("&");
+         }
+         if (q.toString().endsWith("&"))
+         {
+            q.deleteCharAt(q.length() - 1);
+         }
+         return q.toString();
+      }
+      return this.queryString;
+   }
+
+   public void setRemoteUser(String remoteUser)
+   {
+      this.remoteUser = remoteUser;
+   }
+
+   public String getRemoteUser()
+   {
+      return this.remoteUser;
+   }
+
+   /**
+    * @see #addUserRole
+    * @deprecated in favor of addUserRole
+    */
+   public void addRole(String role)
+   {
+      addUserRole(role);
+   }
+
+   public void addUserRole(String role)
+   {
+      this.userRoles.add(role);
+   }
+
+   public boolean isUserInRole(String role)
+   {
+      return this.userRoles.contains(role);
+   }
+
+   public void setUserPrincipal(Principal userPrincipal)
+   {
+      this.userPrincipal = userPrincipal;
+   }
+
+   public Principal getUserPrincipal()
+   {
+      return this.userPrincipal;
+   }
+
+   public String getRequestedSessionId()
+   {
+      HttpSession session = getSession();
+      return (session != null ? session.getId() : null);
+   }
+
+   public void setRequestURI(String requestURI)
+   {
+      this.requestURI = requestURI;
+   }
+
+   public String getRequestURI()
+   {
+      return this.requestURI;
+   }
+
+   public StringBuffer getRequestURL()
+   {
+      StringBuffer url = new StringBuffer(this.scheme);
+      url.append("://").append(this.serverName).append(':').append(this.serverPort);
+      url.append(getRequestURI());
+      return url;
+   }
+
+   public void setServletPath(String servletPath)
+   {
+      this.servletPath = servletPath;
+   }
+
+   public String getServletPath()
+   {
+      return this.servletPath;
+   }
+
+   public void setSession(HttpSession session)
+   {
+      /*
+	   TODO: We don't track access times in mocks (yet) mockSession.access();
+		if (session instanceof MockHttpSession) {
+			MockHttpSession mockSession = ((MockHttpSession) session);
+			...
+		}
+		*/
+      this.session = session;
+   }
+
+   public HttpSession getSession(boolean create)
+   {
+      checkActive();
+      // Reset session if invalidated.
+      if (this.session instanceof MockHttpSession && ((MockHttpSession) this.session).isInvalid())
+      {
+         this.session = null;
+      }
+      // Create new session if necessary.
+      if (this.session == null && create)
+      {
+         this.session = new MockHttpSession(this.servletContext);
+      }
+      return this.session;
+   }
+
+   public HttpSession getSession()
+   {
+      return getSession(true);
+   }
+
+   public void setRequestedSessionIdValid(boolean requestedSessionIdValid)
+   {
+      this.requestedSessionIdValid = requestedSessionIdValid;
+   }
+
+   public boolean isRequestedSessionIdValid()
+   {
+      return this.requestedSessionIdValid;
+   }
+
+   public void setRequestedSessionIdFromCookie(boolean requestedSessionIdFromCookie)
+   {
+      this.requestedSessionIdFromCookie = requestedSessionIdFromCookie;
+   }
+
+   public boolean isRequestedSessionIdFromCookie()
+   {
+      return this.requestedSessionIdFromCookie;
+   }
+
+   public void setRequestedSessionIdFromURL(boolean requestedSessionIdFromURL)
+   {
+      this.requestedSessionIdFromURL = requestedSessionIdFromURL;
+   }
+
+   public boolean isRequestedSessionIdFromURL()
+   {
+      return this.requestedSessionIdFromURL;
+   }
+
+   public boolean isRequestedSessionIdFromUrl()
+   {
+      return isRequestedSessionIdFromURL();
+	}
+
+   public boolean isAllParametersInQueryString() {
+      return true;
+   }
+
+}
\ No newline at end of file

Copied: branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/EnhancedMockHttpServletResponse.java (from rev 11257, branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/MockHttpServletResponse.java)
===================================================================
--- branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/EnhancedMockHttpServletResponse.java	                        (rev 0)
+++ branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/EnhancedMockHttpServletResponse.java	2009-07-13 15:50:48 UTC (rev 11279)
@@ -0,0 +1,515 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jboss.seam.mock;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Mock implementation of the {@link javax.servlet.http.HttpServletResponse}
+ * interface. Supports the Servlet 2.4 API level.
+ *
+ * <p>Used for testing the web framework; also useful for testing
+ * application controllers.
+ *
+ * @author Juergen Hoeller
+ * @author Rod Johnson
+ * @since 1.0.2
+ */
+public class EnhancedMockHttpServletResponse implements HttpServletResponse {
+
+	public static final int DEFAULT_SERVER_PORT = 80;
+
+	private static final String CHARSET_PREFIX = "charset=";
+
+
+	//---------------------------------------------------------------------
+	// ServletResponse properties
+	//---------------------------------------------------------------------
+
+	private boolean outputStreamAccessAllowed = true;
+
+	private boolean writerAccessAllowed = true;
+
+	private String characterEncoding = "ISO-8859-1";
+
+	private final ByteArrayOutputStream content = new ByteArrayOutputStream();
+
+	private final ServletOutputStream outputStream = new ResponseServletOutputStream(this.content);
+
+	private PrintWriter writer;
+
+	private int contentLength = 0;
+
+	private String contentType;
+
+	private int bufferSize = 4096;
+
+	private boolean committed;
+
+	private Locale locale = Locale.getDefault();
+
+
+	//---------------------------------------------------------------------
+	// HttpServletResponse properties
+	//---------------------------------------------------------------------
+
+	private final List cookies = new ArrayList();
+
+	/**
+	 * The key is the lowercase header name; the value is a {@link org.jboss.seam.mock.HeaderValueHolder} object.
+	 */
+	private final Map headers = new HashMap();
+
+	private int status = HttpServletResponse.SC_OK;
+
+	private String statusMessage;
+
+	private String redirectedUrl;
+
+	private String forwardedUrl;
+
+	private String includedUrl;
+
+
+   //---------------------------------------------------------------------
+	// ServletResponse interface
+	//---------------------------------------------------------------------
+
+	/**
+	 * Set whether {@link #getOutputStream()} access is allowed.
+	 * <p>Default is <code>true</code>.
+	 */
+	public void setOutputStreamAccessAllowed(boolean outputStreamAccessAllowed) {
+		this.outputStreamAccessAllowed = outputStreamAccessAllowed;
+	}
+
+	/**
+	 * Return whether {@link #getOutputStream()} access is allowed.
+	 */
+	public boolean isOutputStreamAccessAllowed() {
+		return this.outputStreamAccessAllowed;
+	}
+
+	/**
+	 * Set whether {@link #getWriter()} access is allowed.
+	 * <p>Default is <code>true</code>.
+	 */
+	public void setWriterAccessAllowed(boolean writerAccessAllowed) {
+		this.writerAccessAllowed = writerAccessAllowed;
+	}
+
+	/**
+	 * Return whether {@link #getOutputStream()} access is allowed.
+	 */
+	public boolean isWriterAccessAllowed() {
+		return this.writerAccessAllowed;
+	}
+
+	public void setCharacterEncoding(String characterEncoding) {
+		this.characterEncoding = characterEncoding;
+	}
+
+	public String getCharacterEncoding() {
+		return this.characterEncoding;
+	}
+
+	public ServletOutputStream getOutputStream() {
+		if (!this.outputStreamAccessAllowed) {
+			throw new IllegalStateException("OutputStream access not allowed");
+		}
+		return this.outputStream;
+	}
+
+	public PrintWriter getWriter() throws UnsupportedEncodingException {
+		if (!this.writerAccessAllowed) {
+			throw new IllegalStateException("Writer access not allowed");
+		}
+		if (this.writer == null) {
+			Writer targetWriter = (this.characterEncoding != null ?
+					new OutputStreamWriter(this.content, this.characterEncoding) : new OutputStreamWriter(this.content));
+			this.writer = new ResponsePrintWriter(targetWriter);
+		}
+		return this.writer;
+	}
+
+	public byte[] getContentAsByteArray() {
+		flushBuffer();
+		return this.content.toByteArray();
+	}
+
+	public String getContentAsString() {
+		flushBuffer();
+      try {
+         return (this.characterEncoding != null) ?
+               this.content.toString(this.characterEncoding) : this.content.toString();
+      } catch (UnsupportedEncodingException ex) {
+         throw new RuntimeException(ex);
+      }
+	}
+
+	public void setContentLength(int contentLength) {
+		this.contentLength = contentLength;
+	}
+
+	public int getContentLength() {
+		return this.contentLength;
+	}
+
+	public void setContentType(String contentType) {
+		this.contentType = contentType;
+		if (contentType != null) {
+			int charsetIndex = contentType.toLowerCase().indexOf(CHARSET_PREFIX);
+			if (charsetIndex != -1) {
+				String encoding = contentType.substring(charsetIndex + CHARSET_PREFIX.length());
+				setCharacterEncoding(encoding);
+			}
+		}
+	}
+
+	public String getContentType() {
+		return this.contentType;
+	}
+
+	public void setBufferSize(int bufferSize) {
+		this.bufferSize = bufferSize;
+	}
+
+	public int getBufferSize() {
+		return this.bufferSize;
+	}
+
+	public void flushBuffer() {
+		setCommitted(true);
+	}
+
+	public void resetBuffer() {
+		if (isCommitted()) {
+			throw new IllegalStateException("Cannot reset buffer - response is already committed");
+		}
+		this.content.reset();
+	}
+
+	private void setCommittedIfBufferSizeExceeded() {
+		int bufSize = getBufferSize();
+		if (bufSize > 0 && this.content.size() > bufSize) {
+			setCommitted(true);
+		}
+	}
+
+	public void setCommitted(boolean committed) {
+		this.committed = committed;
+	}
+
+	public boolean isCommitted() {
+		return this.committed;
+	}
+
+	public void reset() {
+		resetBuffer();
+		this.characterEncoding = null;
+		this.contentLength = 0;
+		this.contentType = null;
+		this.locale = null;
+		this.cookies.clear();
+		this.headers.clear();
+		this.status = HttpServletResponse.SC_OK;
+		this.statusMessage = null;
+	}
+
+	public void setLocale(Locale locale) {
+		this.locale = locale;
+	}
+
+	public Locale getLocale() {
+		return this.locale;
+	}
+
+
+	//---------------------------------------------------------------------
+	// HttpServletResponse interface
+	//---------------------------------------------------------------------
+
+	public void addCookie(Cookie cookie) {
+		this.cookies.add(cookie);
+	}
+
+	public Cookie[] getCookies() {
+		return (Cookie[]) this.cookies.toArray(new Cookie[this.cookies.size()]);
+	}
+
+	public Cookie getCookie(String name) {
+		for (Iterator it = this.cookies.iterator(); it.hasNext();) {
+			Cookie cookie = (Cookie) it.next();
+			if (name.equals(cookie.getName())) {
+				return cookie;
+			}
+		}
+		return null;
+	}
+
+	public boolean containsHeader(String name) {
+		return (HeaderValueHolder.getByName(this.headers, name) != null);
+	}
+
+	/**
+	 * Return the names of all specified headers as a Set of Strings.
+	 * @return the <code>Set</code> of header name <code>Strings</code>, or an empty <code>Set</code> if none
+	 */
+	public Set getHeaderNames() {
+		return this.headers.keySet();
+	}
+
+	/**
+	 * Return the primary value for the given header, if any.
+	 * <p>Will return the first value in case of multiple values.
+	 * @param name the name of the header
+	 * @return the associated header value, or <code>null<code> if none
+	 */
+	public Object getHeader(String name) {
+		HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
+		return (header != null ? header.getValue() : null);
+	}
+
+	/**
+	 * Return all values for the given header as a List of value objects.
+	 * @param name the name of the header
+	 * @return the associated header values, or an empty List if none
+	 */
+	public List getHeaders(String name) {
+		HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
+		return (header != null ? header.getValues() : Collections.EMPTY_LIST);
+	}
+
+	/**
+	 * The default implementation returns the given URL String as-is.
+	 * <p>Can be overridden in subclasses, appending a session id or the like.
+	 */
+	public String encodeURL(String url) {
+		return url;
+	}
+
+	/**
+	 * The default implementation delegates to {@link #encodeURL},
+	 * returning the given URL String as-is.
+	 * <p>Can be overridden in subclasses, appending a session id or the like
+	 * in a redirect-specific fashion. For general URL encoding rules,
+	 * override the common {@link #encodeURL} method instead, appyling
+	 * to redirect URLs as well as to general URLs.
+	 */
+	public String encodeRedirectURL(String url) {
+		return encodeURL(url);
+	}
+
+	public String encodeUrl(String url) {
+		return encodeURL(url);
+	}
+
+	public String encodeRedirectUrl(String url) {
+		return encodeRedirectURL(url);
+	}
+
+	public void sendError(int status, String errorMessage) throws IOException {
+		if (isCommitted()) {
+			throw new IllegalStateException("Cannot set error status - response is already committed");
+		}
+		this.status = status;
+		this.statusMessage = errorMessage;
+		setCommitted(true);
+	}
+
+	public void sendError(int status) throws IOException {
+		if (isCommitted()) {
+			throw new IllegalStateException("Cannot set error status - response is already committed");
+		}
+		this.status = status;
+		setCommitted(true);
+	}
+
+	public void sendRedirect(String url) throws IOException {
+		if (isCommitted()) {
+			throw new IllegalStateException("Cannot send redirect - response is already committed");
+		}
+		this.redirectedUrl = url;
+		setCommitted(true);
+	}
+
+	public String getRedirectedUrl() {
+		return this.redirectedUrl;
+	}
+
+	public void setDateHeader(String name, long value) {
+		setHeaderValue(name, new Long(value));
+	}
+
+	public void addDateHeader(String name, long value) {
+		addHeaderValue(name, new Long(value));
+	}
+
+	public void setHeader(String name, String value) {
+		setHeaderValue(name, value);
+	}
+
+	public void addHeader(String name, String value) {
+		addHeaderValue(name, value);
+	}
+
+	public void setIntHeader(String name, int value) {
+		setHeaderValue(name, new Integer(value));
+	}
+
+	public void addIntHeader(String name, int value) {
+		addHeaderValue(name, new Integer(value));
+	}
+
+	private void setHeaderValue(String name, Object value) {
+		doAddHeaderValue(name, value, true);
+	}
+
+	private void addHeaderValue(String name, Object value) {
+		doAddHeaderValue(name, value, false);
+	}
+
+	private void doAddHeaderValue(String name, Object value, boolean replace) {
+		HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
+		if (header == null) {
+			header = new HeaderValueHolder();
+			this.headers.put(name, header);
+		}
+		if (replace) {
+			header.setValue(value);
+		}
+		else {
+			header.addValue(value);
+		}
+	}
+
+	public void setStatus(int status) {
+		this.status = status;
+	}
+
+	public void setStatus(int status, String statusMessage) {
+		this.status = status;
+		this.statusMessage = statusMessage;
+	}
+
+	public int getStatus() {
+		return this.status;
+	}
+
+	public String getStatusMessage() {
+		return this.statusMessage;
+	}
+
+
+	//---------------------------------------------------------------------
+	// Methods for MockRequestDispatcher
+	//---------------------------------------------------------------------
+
+	public void setForwardedUrl(String forwardedUrl) {
+		this.forwardedUrl = forwardedUrl;
+	}
+
+	public String getForwardedUrl() {
+		return this.forwardedUrl;
+	}
+
+	public void setIncludedUrl(String includedUrl) {
+		this.includedUrl = includedUrl;
+	}
+
+	public String getIncludedUrl() {
+		return this.includedUrl;
+	}
+
+
+   /**
+	 * Inner class that adapts the ServletOutputStream to mark the
+	 * response as committed once the buffer size is exceeded.
+	 */
+	private class ResponseServletOutputStream extends DelegatingServletOutputStream
+   {
+
+		public ResponseServletOutputStream(OutputStream out) {
+			super(out);
+		}
+
+		public void write(int b) throws IOException {
+			super.write(b);
+			super.flush();
+			setCommittedIfBufferSizeExceeded();
+		}
+
+		public void flush() throws IOException {
+			super.flush();
+			setCommitted(true);
+		}
+	}
+
+
+	/**
+	 * Inner class that adapts the PrintWriter to mark the
+	 * response as committed once the buffer size is exceeded.
+	 */
+	private class ResponsePrintWriter extends PrintWriter {
+
+		public ResponsePrintWriter(Writer out) {
+			super(out, true);
+		}
+
+		public void write(char buf[], int off, int len) {
+			super.write(buf, off, len);
+			super.flush();
+			setCommittedIfBufferSizeExceeded();
+		}
+
+		public void write(String s, int off, int len) {
+			super.write(s, off, len);
+			super.flush();
+			setCommittedIfBufferSizeExceeded();
+		}
+
+		public void write(int c) {
+			super.write(c);
+			super.flush();
+			setCommittedIfBufferSizeExceeded();
+		}
+
+		public void flush() {
+			super.flush();
+			setCommitted(true);
+		}
+	}
+
+}
\ No newline at end of file

Added: branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/HeaderValueHolder.java
===================================================================
--- branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/HeaderValueHolder.java	                        (rev 0)
+++ branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/HeaderValueHolder.java	2009-07-13 15:50:48 UTC (rev 11279)
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jboss.seam.mock;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Internal helper class that serves as value holder for request headers.
+ *
+ * @author Juergen Hoeller
+ * @author Rick Evans
+ * @since 2.0.1
+ */
+class HeaderValueHolder
+{
+
+   private final List values = new LinkedList();
+
+
+   public void setValue(Object value)
+   {
+      this.values.clear();
+      this.values.add(value);
+   }
+
+   public void addValue(Object value)
+   {
+      this.values.add(value);
+   }
+
+   public void addValues(Collection values)
+   {
+      this.values.addAll(values);
+   }
+
+   public void addValueArray(Object values)
+   {
+      Object[] arr = toObjectArray(values);
+      this.values.addAll(Arrays.asList(arr));
+   }
+
+   public List getValues()
+   {
+      return Collections.unmodifiableList(this.values);
+   }
+
+   public Object getValue()
+   {
+      return (!this.values.isEmpty() ? this.values.get(0) : null);
+   }
+
+
+   /**
+    * Find a HeaderValueHolder by name, ignoring casing.
+    *
+    * @param headers the Map of header names to HeaderValueHolders
+    * @param name    the name of the desired header
+    * @return the corresponding HeaderValueHolder,
+    *         or <code>null</code> if none found
+    */
+   public static HeaderValueHolder getByName(Map headers, String name)
+   {
+      for (Iterator it = headers.keySet().iterator(); it.hasNext();)
+      {
+         String headerName = (String) it.next();
+         if (headerName.equalsIgnoreCase(name))
+         {
+            return (HeaderValueHolder) headers.get(headerName);
+         }
+      }
+      return null;
+   }
+
+   public static Object[] toObjectArray(Object source)
+   {
+      if (source instanceof Object[])
+      {
+         return (Object[]) source;
+      }
+      if (source == null)
+      {
+         return new Object[0];
+      }
+      if (!source.getClass().isArray())
+      {
+         throw new IllegalArgumentException("Source is not an array: " + source);
+      }
+      int length = Array.getLength(source);
+      if (length == 0)
+      {
+         return new Object[0];
+      }
+      Class wrapperType = Array.get(source, 0).getClass();
+      Object[] newArray = (Object[]) Array.newInstance(wrapperType, length);
+      for (int i = 0; i < length; i++)
+      {
+         newArray[i] = Array.get(source, i);
+      }
+      return newArray;
+	}
+
+}
\ No newline at end of file

Added: branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/MockRequestDispatcher.java
===================================================================
--- branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/MockRequestDispatcher.java	                        (rev 0)
+++ branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/MockRequestDispatcher.java	2009-07-13 15:50:48 UTC (rev 11279)
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jboss.seam.mock;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+/**
+ * Mock implementation of the {@link javax.servlet.RequestDispatcher} interface.
+ * <p/>
+ * <p>Used for testing the web framework; typically not necessary for
+ * testing application controllers.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @since 1.0.2
+ */
+public class MockRequestDispatcher implements RequestDispatcher
+{
+
+   private final Log logger = LogFactory.getLog(getClass());
+
+   private final String url;
+
+
+   /**
+    * Create a new MockRequestDispatcher for the given URL.
+    *
+    * @param url the URL to dispatch to.
+    */
+   public MockRequestDispatcher(String url)
+   {
+      this.url = url;
+   }
+
+
+   public void forward(ServletRequest request, ServletResponse response)
+   {
+      if (response.isCommitted())
+      {
+         throw new IllegalStateException("Cannot perform forward - response is already committed");
+      }
+      getMockHttpServletResponse(response).setForwardedUrl(this.url);
+      if (logger.isDebugEnabled())
+      {
+         logger.debug("MockRequestDispatcher: forwarding to URL [" + this.url + "]");
+      }
+   }
+
+   public void include(ServletRequest request, ServletResponse response)
+   {
+      getMockHttpServletResponse(response).setIncludedUrl(this.url);
+      if (logger.isDebugEnabled())
+      {
+         logger.debug("MockRequestDispatcher: including URL [" + this.url + "]");
+      }
+   }
+
+   /**
+    * Obtain the underlying EnhancedMockHttpServletResponse,
+    * unwrapping {@link javax.servlet.http.HttpServletResponseWrapper} decorators if necessary.
+    */
+   protected EnhancedMockHttpServletResponse getMockHttpServletResponse(ServletResponse response)
+   {
+      if (response instanceof EnhancedMockHttpServletResponse)
+      {
+         return (EnhancedMockHttpServletResponse) response;
+      }
+      if (response instanceof HttpServletResponseWrapper)
+      {
+         return getMockHttpServletResponse(((HttpServletResponseWrapper) response).getResponse());
+      }
+      throw new IllegalArgumentException("MockRequestDispatcher requires MockHttpServletResponse");
+	}
+
+}
\ No newline at end of file

Added: branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/ResourceRequestEnvironment.java
===================================================================
--- branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/ResourceRequestEnvironment.java	                        (rev 0)
+++ branches/enterprise/JBPAPP_5_0/src/main/org/jboss/seam/mock/ResourceRequestEnvironment.java	2009-07-13 15:50:48 UTC (rev 11279)
@@ -0,0 +1,267 @@
+package org.jboss.seam.mock;
+
+import org.jboss.seam.servlet.SeamResourceServlet;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.Cookie;
+import javax.servlet.ServletException;
+import javax.servlet.ServletResponse;
+import javax.servlet.ServletRequest;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import java.util.Collections;
+import java.util.Set;
+import java.util.List;
+import java.util.Map;
+import java.util.Enumeration;
+import java.security.Principal;
+import java.io.IOException;
+
+/**
+ * Executes (through local calls, not TCP sockets) an HTTP request in a unit test, passing it through
+ * the Seam resource handlers and filters.
+ *
+ * <p>
+ * This class is supposed to be used <b>within</b> a <tt>SeamTest</tt>, in fact, you need
+ * to pass an instance of <tt>SeamTest</tt> into its constructor. This prepares the environment
+ * for the resource request processing. You can either share an instance of the environment between
+ * all your test methods (prepare it in <tt>&#064;BeforeClass</tt>) or you can create a new instance
+ * for each <tt>ResourceRequest</tt>:
+ * </p>
+ *
+ * <pre>
+ * import org.jboss.seam.mock.ResourceRequestEnvironment;
+ * import org.jboss.seam.mock.EnhancedMockHttpServletRequest;
+ * import org.jboss.seam.mock.EnhancedMockHttpServletResponse;
+ * import static org.jboss.seam.mock.ResourceRequestEnvironment.ResourceRequest;
+ * import static org.jboss.seam.mock.ResourceRequestEnvironment.Method;
+ *
+ * public class MyTest extends SeamTest {
+ *
+ *    ResourceRequestEnvironment sharedEnvironment;
+ *
+ *    &#064;BeforeClass
+ *    public void prepareSharedEnvironment() throws Exception {
+ *        sharedEnvironment = new ResourceRequestEnvironment(this) {
+ *             &#064;Override
+ *             public Map<String, Object> getDefaultHeaders() {
+ *                return new HashMap<String, Object>() {{
+ *                    put("Accept", "text/plain");
+ *                }};
+ *             }
+ *          };
+ *    }
+ *
+ *    &#064;Test
+ *    public void test() throws Exception
+ *    {
+ *       //Not shared: new ResourceRequest(new ResourceRequestEnvironment(this), Method.GET, "/my/relative/uri)
+ *
+ *       new ResourceRequest(sharedEnvironment, Method.GET, "/my/relative/uri)
+ *       {
+ *
+ *          &#064;Override
+ *          protected void prepareRequest(EnhancedMockHttpServletRequest request)
+ *          {
+ *             request.addQueryParameter("foo", "123");
+ *             request.addHeader("Accept-Language", "en_US, de");
+ *          }
+ *
+ *          &#064;Override
+ *          protected void onResponse(EnhancedMockHttpServletResponse response)
+ *          {
+ *             assert response.getStatus() == 200;
+ *             assert response.getContentAsString().equals("foobar");
+ *          }
+ *
+ *       }.run();
+ *    }
+ *
+ * }
+ * </pre>
+ * <p>
+ * Note that in a <tt>SeamTest</tt> the (mock) HTTP session is always shared between all requests in a particular test
+ * method. Each test method however executes with a new (mock) HTTP session. Design your tests accordingly, this is not
+ * configurable.
+ * </p>
+ * <p>
+ * <b>IMPORTANT: A <tt>ResourceRequest</tt> has to be executed in a <tt>@Test</tt> method or in a
+ * <tt>@BeforeMethod</tt> callback. You can not execute it in any other callback, such * as <tt>@BeforeClass</tt>.
+ * </p>
+ *
+ * @author Christian Bauer
+ */
+public class ResourceRequestEnvironment
+{
+
+   public enum Method
+   {
+      GET, PUT, POST, DELETE, HEAD, OPTIONS
+   }
+
+   final protected AbstractSeamTest seamTest;
+   final protected SeamResourceServlet resourceServlet;
+
+   public ResourceRequestEnvironment(AbstractSeamTest seamTest)
+   {
+      this.seamTest = seamTest;
+      resourceServlet = new SeamResourceServlet();
+      try {
+         resourceServlet.init(
+               new ServletConfig()
+               {
+                  public String getServletName()
+                  {
+                     return "Seam Resource Servlet";
+                  }
+
+                  public ServletContext getServletContext()
+                  {
+                     return ResourceRequestEnvironment.this.seamTest.servletContext;
+                  }
+
+                  public String getInitParameter(String s)
+                  {
+                     return null;
+                  }
+
+                  public Enumeration getInitParameterNames()
+                  {
+                     return null;
+                  }
+               }
+         );
+      } catch (Exception ex) {
+         throw new RuntimeException(ex);
+      }
+   }
+
+   public static class ResourceRequest
+   {
+
+      final private ResourceRequestEnvironment environment;
+      private Method httpMethod;
+      private String requestPath;
+      private EnhancedMockHttpServletRequest request;
+      private EnhancedMockHttpServletResponse response;
+
+      public ResourceRequest(ResourceRequestEnvironment environment, Method httpMethod, String requestPath)
+      {
+         this.environment = environment;
+         this.httpMethod = httpMethod;
+         this.requestPath = environment.getServletPath() + (requestPath.startsWith("/") ? requestPath : "/" + requestPath);
+      }
+
+      public void run() throws Exception
+      {
+         init();
+         prepareRequest(request);
+         environment.seamTest.seamFilter.doFilter(request, response, new FilterChain()
+         {
+            public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException
+            {
+               environment.resourceServlet.service(request, response);
+            }
+         });
+         environment.seamTest.seamFilter.destroy();
+         onResponse(getResponse());
+      }
+
+      protected void init()
+      {
+         request = createRequest();
+         response = createResponse();
+
+         request.setMethod(httpMethod.toString());
+         request.setRequestURI(requestPath);
+
+         request.setServletPath(environment.getServletPath());
+
+         request.setCookies(getCookies().toArray(new Cookie[getCookies().size()]));
+
+         for (Map.Entry<String, Object> entry : environment.getDefaultHeaders().entrySet())
+         {
+            request.addHeader(entry.getKey(), entry.getValue());
+         }
+
+         request.setUserPrincipal(
+               new Principal()
+               {
+                  public String getName()
+                  {
+                     return getPrincipalName();
+                  }
+               }
+         );
+         for (String role : getPrincipalRoles())
+         {
+            request.addUserRole(role);
+         }
+
+         // Use the (mock) HttpSession that Seam uses, see AbstractSeamTest
+         request.setSession(environment.seamTest.session);
+
+      }
+
+      protected EnhancedMockHttpServletRequest createRequest()
+      {
+         return new EnhancedMockHttpServletRequest();
+      }
+
+      protected EnhancedMockHttpServletResponse createResponse()
+      {
+         return new EnhancedMockHttpServletResponse();
+      }
+
+      protected Map<String, String> getRequestQueryParameters()
+      {
+         return Collections.EMPTY_MAP;
+      }
+
+      protected List<Cookie> getCookies()
+      {
+         return Collections.EMPTY_LIST;
+      }
+
+      protected String getPrincipalName()
+      {
+         return null;
+      }
+
+      protected Set<String> getPrincipalRoles()
+      {
+         return Collections.EMPTY_SET;
+      }
+
+      protected void prepareRequest(EnhancedMockHttpServletRequest request)
+      {
+      }
+
+      protected void onResponse(EnhancedMockHttpServletResponse response)
+      {
+      }
+
+      public HttpServletRequest getRequest()
+      {
+         return request;
+      }
+
+      public EnhancedMockHttpServletResponse getResponse()
+      {
+         return response;
+      }
+
+   }
+
+   public String getServletPath()
+   {
+      return "/seam/resource";
+   }
+
+   public Map<String, Object> getDefaultHeaders()
+   {
+      return Collections.EMPTY_MAP;
+   }
+
+}




More information about the seam-commits mailing list