You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by tm...@apache.org on 2006/11/05 08:47:20 UTC

svn commit: r471375 - in /struts/struts2/trunk/core/src: main/java/org/apache/struts2/dispatcher/ test/java/org/apache/struts2/dispatcher/

Author: tmjee
Date: Sat Nov  4 23:47:19 2006
New Revision: 471375

URL: http://svn.apache.org/viewvc?view=rev&rev=471375
Log:
WW-1489
 - Refactor ActionContextCleanUp and DispatcherFilter to have common logics in an abstract super class


Added:
    struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/AbstractFilter.java
    struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/ActionContextCleanUpTest.java
    struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/FilterTest.java
Modified:
    struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ActionContextCleanUp.java
    struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/FilterDispatcher.java
    struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/FilterDispatcherTest.java

Added: struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/AbstractFilter.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/AbstractFilter.java?view=auto&rev=471375
==============================================================================
--- struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/AbstractFilter.java (added)
+++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/AbstractFilter.java Sat Nov  4 23:47:19 2006
@@ -0,0 +1,191 @@
+/*
+ * $Id: ActionContextCleanUp.java 454720 2006-10-10 12:31:52Z tmjee $
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.dispatcher;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * An abstract for superclass for Struts2 filter, encapsulating common logics and 
+ * helper methods usefull to subclass, to avoid duplication. 
+ * 
+ * Common logics encapsulated:-
+ * <ul>
+ * 	<li>
+ * 		Dispatcher instance creation through <code>createDispatcher</code> method that acts
+ * 		as a hook subclass could override. By default it creates an instance of Dispatcher.
+ *  </li>
+ *  <li>
+ *  	<code>postInit(FilterConfig)</code> is a hook subclass may use to add post initialization
+ *      logics in <code>{@link javax.servlet.Filter#init(FilterConfig)}</code>. It is called before
+ *      {@link javax.servlet.Filter#init(FilterConfig)} method ends.
+ *  </li>
+ *  <li>
+ *  	A default <code>{@link javax.servlet.Filter#destroy()}</code> that clean up Dispatcher by 
+ *      calling <code>dispatcher.cleanup()</code>
+ *  </li>
+ *  <li>
+ *  	<code>prepareDispatcherAndWrapRequest(HttpServletRequest, HttpServletResponse)</code> helper method
+ *      that basically called <code>dispatcher.prepare()</code>, wrap the HttpServletRequest and return the 
+ *      wrapped version.
+ *  </li>
+ *  <li>
+ *  	Various other common helper methods like
+ *      <ul>
+ *      	<li>getFilterConfig</li>
+ *      	<li>getServletContext</li>
+ *      </ul>
+ *  </li>
+ * </ul>
+ * 
+ * 
+ * @see Dispatcher
+ * @see FilterDispatcher
+ * @see ActionContextCleanUp
+ * 
+ * @version $Date$ $Id$
+ */
+public abstract class AbstractFilter implements Filter {
+
+	private static final Log LOG = LogFactory.getLog(AbstractFilter.class);
+	
+	/** Internal copy of dispatcher, created when Filter instance gets initialized. */
+	private Dispatcher _dispatcher; 
+	
+	protected FilterConfig filterConfig;
+	
+	/** Dispatcher instance to be used by subclass. */
+	protected Dispatcher dispatcher;
+	
+	
+	/**
+     * Initializes the filter
+     * 
+     * @param filterConfig The filter configuration
+     */
+    public void init(FilterConfig filterConfig) throws ServletException {
+        this.filterConfig = filterConfig;
+        _dispatcher = createDispatcher();
+        postInit(filterConfig);
+    }
+    
+    /**
+     * Cleans up the dispatcher
+     * 
+     * @see javax.servlet.Filter#destroy()
+     */
+    public void destroy() {
+        if (_dispatcher == null) {
+        	LOG.warn("something is seriously wrong, Dispatcher is not initialized (null) ");
+        } else {
+        	_dispatcher.cleanup();
+        }
+    }
+    
+    /**
+     * Hook for subclass todo custom initialization, called after 
+     * <code>javax.servlet.Filter.init(FilterConfig)</code>.
+     * 
+     * @param filterConfig
+     * @throws ServletException
+     */
+    protected abstract void postInit(FilterConfig filterConfig) throws ServletException;
+	
+    /**
+     * Create a {@link Dispatcher}, this serves as a hook for subclass to overried
+     * such that a custom {@link Dispatcher} could be created. 
+     * 
+     * @return Dispatcher
+     */
+    protected Dispatcher createDispatcher() {
+    	return new Dispatcher(filterConfig.getServletContext());
+    }
+    
+    /**
+     * Servlet 2.3 specifies that the servlet context can be retrieved from the session. Unfortunately, some versions of
+     * WebLogic can only retrieve the servlet context from the filter config. Hence, this method enables subclasses to
+     * retrieve the servlet context from other sources.
+     *
+     * @param session the HTTP session where, in Servlet 2.3, the servlet context can be retrieved
+     * @return the servlet context.
+     */
+    protected ServletContext getServletContext() {
+        return filterConfig.getServletContext();
+    }
+    
+    /** 
+     * Gets this filter's configuration
+     * 
+     * @return The filter config
+     */
+    protected FilterConfig getFilterConfig() {
+        return filterConfig;
+    }
+    
+    /**
+     * Helper method that prepare <code>Dispatcher</code> 
+     * (by calling <code>Dispatcher.prepare(HttpServletRequest, HttpServletResponse)</code>)
+     * following by wrapping and returning  the wrapping <code>HttpServletRequest</code> [ through 
+     * <code>dispatcher.wrapRequest(HttpServletRequest, ServletContext)</code> ]
+     * 
+     * @param request
+     * @param response
+     * @return HttpServletRequest
+     * @throws ServletException
+     */
+    protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException {
+    	
+    	Dispatcher du = Dispatcher.getInstance();
+        
+    	// Prepare and wrap the request if the cleanup filter hasn't already, cleanup filter should be
+    	// configured first before struts2 dispatcher filter, hence when its cleanup filter's turn, 
+    	// static instance of Dispatcher should be null.
+    	if (du == null) {
+    		dispatcher = _dispatcher;
+    		
+    		Dispatcher.setInstance(dispatcher);
+    		
+    		// prepare the request no matter what - this ensures that the proper character encoding
+    		// is used before invoking the mapper (see WW-9127)
+    		dispatcher.prepare(request, response);
+
+    		try {
+    			// Wrap request first, just in case it is multipart/form-data 
+    			// parameters might not be accessible through before encoding (ww-1278)
+    			request = dispatcher.wrapRequest(request, getServletContext());
+    		} catch (IOException e) {
+    			String message = "Could not wrap servlet request with MultipartRequestWrapper!";
+    			LOG.error(message, e);
+    			throw new ServletException(message, e);
+    		}
+    	}
+    	else {
+    		dispatcher = du;
+    	}
+		return request;
+    }
+}

Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ActionContextCleanUp.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ActionContextCleanUp.java?view=diff&rev=471375&r1=471374&r2=471375
==============================================================================
--- struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ActionContextCleanUp.java (original)
+++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ActionContextCleanUp.java Sat Nov  4 23:47:19 2006
@@ -19,10 +19,8 @@
 
 import java.io.IOException;
 
-import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
-import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
@@ -56,29 +54,28 @@
  * </ul>
  * <!-- SNIPPET END: description -->
  *
- * @version $Date$ $Id$
  *
  * @see FilterDispatcher
+ * @see AbstractFilter
+ * @see Dispatcher
+ * 
+ * @version $Date$ $Id$
  */
-public class ActionContextCleanUp implements Filter {
+public class ActionContextCleanUp extends AbstractFilter {
 
     private static final Log LOG = LogFactory.getLog(ActionContextCleanUp.class);
 
     private static final String COUNTER = "__cleanup_recursion_counter";
 
     protected FilterConfig filterConfig;
-    protected Dispatcher dispatcher;
+
 
     /**
-     * Initializes the filter
-     * 
-     * @param filterConfig The filter configuration
+     * Empty implementation.
      */
-    public void init(FilterConfig filterConfig) throws ServletException {
-        this.filterConfig = filterConfig;
-        dispatcher = new Dispatcher(filterConfig.getServletContext());
+    protected void postInit(FilterConfig filterConfig) throws ServletException {
+    	// does nothing.
     }
-
     
     /**
      * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
@@ -92,19 +89,7 @@
         try {
         	UtilTimerStack.push(timerKey);
         	
-        	// prepare the request no matter what - this ensures that the proper character encoding
-        	// is used before invoking the mapper (see WW-9127)
-        	Dispatcher.setInstance(dispatcher);
-        	dispatcher.prepare(request, response);
-
-        	ServletContext servletContext = filterConfig.getServletContext();
-        	try {
-        		request = dispatcher.wrapRequest(request, servletContext);
-        	} catch (IOException e) {
-        		String message = "Could not wrap servlet request with MultipartRequestWrapper!";
-        		LOG.error(message, e);
-        		throw new ServletException(message, e);
-        	}
+        	request = prepareDispatcherAndWrapRequest(request, response);
 
         	try {
         		Integer count = (Integer)request.getAttribute(COUNTER);
@@ -115,6 +100,11 @@
         			count = new Integer(count.intValue()+1);
         		}
         		request.setAttribute(COUNTER, count);
+        		
+        		if (LOG.isDebugEnabled()) {
+            		LOG.debug("filtering counter="+count);
+            	}
+        		
         		chain.doFilter(request, response);
         	} finally {
         		int counterVal = ((Integer)request.getAttribute(COUNTER)).intValue();
@@ -135,21 +125,20 @@
      */
     protected static void cleanUp(ServletRequest req) {
         // should we clean up yet?
-        if (req.getAttribute(COUNTER) != null &&
-                 ((Integer)req.getAttribute(COUNTER)).intValue() > 0 ) {
-             return;
-         }
+    	Integer count = (Integer) req.getAttribute(COUNTER);
+        if (count != null && count > 0 ) {
+        	if (LOG.isDebugEnabled()) {
+        		LOG.debug("skipping cleanup counter="+count);
+        	}
+            return;
+        }
 
         // always dontClean up the thread request, even if an action hasn't been executed
         ActionContext.setContext(null);
-        
         Dispatcher.setInstance(null);
-    }
-
-    
-    /* (non-Javadoc)
-     * @see javax.servlet.Filter#destroy()
-     */
-    public void destroy() {
+        
+        if (LOG.isDebugEnabled()) {
+    		LOG.debug("clean up ");
+    	}
     }
 }

Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/FilterDispatcher.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/FilterDispatcher.java?view=diff&rev=471375&r1=471374&r2=471375
==============================================================================
--- struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/FilterDispatcher.java (original)
+++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/FilterDispatcher.java Sat Nov  4 23:47:19 2006
@@ -28,7 +28,6 @@
 import java.util.StringTokenizer;
 import java.util.TimeZone;
 
-import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
 import javax.servlet.ServletContext;
@@ -37,7 +36,6 @@
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -118,49 +116,30 @@
  * 
  * @version $Date$ $Id$
  */
-public class FilterDispatcher implements Filter, StrutsStatics {
+public class FilterDispatcher extends AbstractFilter implements StrutsStatics {
+	
     private static final Log LOG = LogFactory.getLog(FilterDispatcher.class);
 
-    private FilterConfig filterConfig;
     private String[] pathPrefixes;
-    private Dispatcher dispatcher;
 
     private SimpleDateFormat df = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss");
     private final Calendar lastModifiedCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
     private final String lastModified = df.format(lastModifiedCal.getTime());
 
-    /** 
-     * Gets this filter's configuration
-     * 
-     * @return The filter config
-     */
-    protected FilterConfig getFilterConfig() {
-        return filterConfig;
-    }
-
-    /**
-     * Cleans up the dispatcher
-     */
-    public void destroy() {
-        	if (dispatcher == null) {
-        		LOG.warn("something is seriously wrong, DispatcherUtil is not initialized (null) ");
-        	} else {
-        	    dispatcher.cleanup();
-        }
-    }
-
+    
     /**
-     * Initializes the dispatcher and filter
+     * Look for "packages" defined through filter-config's parameters.
+     * 
+     * @param FilterConfig
+     * @throws ServletException
      */
-    public void init(FilterConfig filterConfig) throws ServletException {
-        this.filterConfig = filterConfig;
-        String param = filterConfig.getInitParameter("packages");
+    protected void postInit(FilterConfig filterConfig) throws ServletException {
+    	String param = filterConfig.getInitParameter("packages");
         String packages = "org.apache.struts2.static template org.apache.struts2.interceptor.debugging";
         if (param != null) {
             packages = param + " " + packages;
         }
         this.pathPrefixes = parse(packages);
-        dispatcher = createDispatcher();
     }
     
     /**
@@ -188,45 +167,29 @@
     }
 
 
-    /* (non-Javadoc)
+    /**
      * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
      */
     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
         HttpServletRequest request = (HttpServletRequest) req;
         HttpServletResponse response = (HttpServletResponse) res;
-        ServletContext servletContext = filterConfig.getServletContext();
+        ServletContext servletContext = getServletContext();
 
         String timerKey = "FilterDispatcher_doFilter: ";
         try {
         	UtilTimerStack.push(timerKey);
-        	Dispatcher du = Dispatcher.getInstance();
         
-        	// Prepare and wrap the request if the cleanup filter hasn't already
-        	if (du == null) {
-        		du = dispatcher;
-        		// prepare the request no matter what - this ensures that the proper character encoding
-        		// is used before invoking the mapper (see WW-9127)
-        		du.prepare(request, response);
-
-        		try {
-        			// Wrap request first, just in case it is multipart/form-data 
-        			// parameters might not be accessible through before encoding (ww-1278)
-        			request = du.wrapRequest(request, servletContext);
-        		} catch (IOException e) {
-        			String message = "Could not wrap servlet request with MultipartRequestWrapper!";
-        			LOG.error(message, e);
-        			throw new ServletException(message, e);
-        		}
-        		Dispatcher.setInstance(du);
-        	}
+			request = prepareDispatcherAndWrapRequest(request, response);
+
 
         	ActionMapper mapper = null;
         	ActionMapping mapping = null;
         	try {
         		mapper = ActionMapperFactory.getMapper();
-        		mapping = mapper.getMapping(request, du.getConfigurationManager());
+        		mapping = mapper.getMapping(request, dispatcher.getConfigurationManager());
         	} catch (Exception ex) {
-        		du.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
+        		LOG.error("error getting ActionMapping", ex);
+        		dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
         		ActionContextCleanUp.cleanUp(req);
         		return;
         	}
@@ -264,18 +227,6 @@
     }
 
     /**
-     * Servlet 2.3 specifies that the servlet context can be retrieved from the session. Unfortunately, some versions of
-     * WebLogic can only retrieve the servlet context from the filter config. Hence, this method enables subclasses to
-     * retrieve the servlet context from other sources.
-     *
-     * @param session the HTTP session where, in Servlet 2.3, the servlet context can be retrieved
-     * @return the servlet context.
-     */
-    protected ServletContext getServletContext(HttpSession session) {
-        return filterConfig.getServletContext();
-    }
-
-    /**
      * Finds a static resource
      * 
      * @param name The resource name
@@ -385,15 +336,5 @@
         resourcePath = URLDecoder.decode(resourcePath, enc);
 
         return ClassLoaderUtil.getResourceAsStream(resourcePath, getClass());
-    }
-    
-    /**
-     * Create a {@link Dispatcher}, this serves as a hook for subclass to overried
-     * such that a custom {@link Dispatcher} could be created. 
-     * 
-     * @return Dispatcher
-     */
-    protected Dispatcher createDispatcher() {
-    	return new Dispatcher(filterConfig.getServletContext());
     }
 }

Added: struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/ActionContextCleanUpTest.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/ActionContextCleanUpTest.java?view=auto&rev=471375
==============================================================================
--- struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/ActionContextCleanUpTest.java (added)
+++ struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/ActionContextCleanUpTest.java Sat Nov  4 23:47:19 2006
@@ -0,0 +1,205 @@
+/*
+ * $Id: FilterDispatcherTest.java 449367 2006-09-24 06:49:04Z mrdon $
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.dispatcher;
+
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.dispatcher.mapper.ActionMapping;
+import org.springframework.mock.web.MockFilterConfig;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.mock.web.MockServletContext;
+
+import com.mockobjects.servlet.MockFilterChain;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Date$ $Id$
+ */
+public class ActionContextCleanUpTest extends TestCase {
+	
+	
+	protected MockFilterConfig filterConfig;
+	protected MockHttpServletRequest request;
+	protected MockHttpServletResponse response;
+	protected MockFilterChain filterChain;
+	protected MockFilterChain filterChain2;
+	protected MockServletContext servletContext;
+	
+	protected Counter counter;
+	protected Map<String, Integer> _tmpStore;
+	protected InnerDispatcher _dispatcher;
+	protected InnerDispatcher _dispatcher2;
+	protected ActionContextCleanUp cleanUp;
+	protected ActionContextCleanUp cleanUp2;
+	
+	
+	@Override
+	protected void tearDown() throws Exception {
+		filterConfig = null;
+		request = null;
+		response = null;
+		filterChain = null;
+		filterChain2 = null;
+		servletContext = null;
+		counter = null;
+		_tmpStore = null;
+		_dispatcher = null;
+		_dispatcher2 = null;
+		cleanUp = null;
+		cleanUp2 = null;
+	}
+	
+	@Override
+	protected void setUp() throws Exception {
+		Dispatcher.setInstance(null);
+		
+		counter = new Counter();
+		_tmpStore = new LinkedHashMap<String, Integer>();
+		
+		filterConfig = new MockFilterConfig();
+		request = new MockHttpServletRequest();
+		response = new MockHttpServletResponse();
+		servletContext = new MockServletContext();
+		_dispatcher = new InnerDispatcher(servletContext) {
+			@Override
+			public String toString() {
+				return "dispatcher";
+			}
+		};
+		_dispatcher2 = new InnerDispatcher(servletContext){
+			@Override
+			public String toString() {
+				return "dispatcher2";
+			}
+		};
+		
+		
+		filterChain = new MockFilterChain() {
+			@Override
+			public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
+				_tmpStore.put("counter"+(counter.count++), (Integer) request.getAttribute("__cleanup_recursion_counter"));
+			}
+		};
+		
+		cleanUp = new ActionContextCleanUp() {
+			@Override
+			protected Dispatcher createDispatcher() {
+				return _dispatcher;
+			}
+		};
+		
+		cleanUp2 = new ActionContextCleanUp() {
+			@Override
+			protected Dispatcher createDispatcher() {
+				return _dispatcher2;
+			}
+		};
+		
+		filterChain2 = new MockFilterChain() {
+			@Override
+			public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
+				_tmpStore.put("counter"+(counter.count++), (Integer) request.getAttribute("__cleanup_recursion_counter"));
+				cleanUp2.doFilter(request, response, filterChain);
+			}
+		};
+	}
+	
+	
+	public void testSingle() throws Exception {
+		assertFalse(_dispatcher.prepare);
+		assertFalse(_dispatcher.wrapRequest);
+		assertNull(request.getAttribute("__cleanup_recursion_counter"));
+		
+		cleanUp.init(filterConfig);
+		cleanUp.doFilter(request, response, filterChain);
+		cleanUp.destroy();
+		
+		assertEquals(_tmpStore.size(), 1);
+		assertEquals(_tmpStore.get("counter0"), new Integer(1));
+		
+		assertTrue(_dispatcher.prepare);
+		assertTrue(_dispatcher.wrapRequest);
+		assertEquals(request.getAttribute("__cleanup_recursion_counter"), new Integer("0"));
+	}
+	
+	public void testMultiple() throws Exception {
+		assertFalse(_dispatcher.prepare);
+		assertFalse(_dispatcher.wrapRequest);
+		assertFalse(_dispatcher2.prepare);
+		assertFalse(_dispatcher2.wrapRequest);
+		assertNull(request.getAttribute("__cleanup_recursion_counter"));
+		
+		cleanUp.init(filterConfig);
+		cleanUp2.init(filterConfig);
+		cleanUp.doFilter(request, response, filterChain2);
+		cleanUp2.destroy();
+		cleanUp.destroy();
+		
+		assertEquals(_tmpStore.size(), 2);
+		assertEquals(_tmpStore.get("counter0"), new Integer(1));
+		assertEquals(_tmpStore.get("counter1"), new Integer(2));
+		
+		assertFalse(_dispatcher2.prepare);
+		assertFalse(_dispatcher2.wrapRequest);
+		assertTrue(_dispatcher.prepare);
+		assertTrue(_dispatcher.wrapRequest);
+		assertEquals(request.getAttribute("__cleanup_recursion_counter"), new Integer("0"));
+	}
+	
+	
+	class InnerDispatcher extends Dispatcher {
+		public boolean prepare = false;
+		public boolean wrapRequest = false;
+		public boolean service = false;
+		
+		public InnerDispatcher(ServletContext servletContext) {
+			super(servletContext);
+		}
+		
+		@Override
+		public void prepare(HttpServletRequest request, HttpServletResponse response) {
+			prepare = true;
+		}
+		
+		@Override
+		public HttpServletRequest wrapRequest(HttpServletRequest request, ServletContext servletContext) throws IOException {
+			wrapRequest = true;
+			return request;
+		}
+		
+		@Override
+		public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context, ActionMapping mapping) throws ServletException {
+			service = true;
+		}
+	}
+	
+	class Counter {
+		public int count=0;
+	}
+}

Modified: struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/FilterDispatcherTest.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/FilterDispatcherTest.java?view=diff&rev=471375&r1=471374&r2=471375
==============================================================================
--- struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/FilterDispatcherTest.java (original)
+++ struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/FilterDispatcherTest.java Sat Nov  4 23:47:19 2006
@@ -47,11 +47,13 @@
 /**
  * FilterDispatcher TestCase.
  *
+ * @version $Date$ $Id$
  */
 public class FilterDispatcherTest extends StrutsTestCase {
 
 
     public void testParsePackages() throws Exception {
+    	
         FilterDispatcher filterDispatcher = new FilterDispatcher();
         String[] result1 = filterDispatcher.parse("foo.bar.package1 foo.bar.package2 foo.bar.package3");
         String[] result2 = filterDispatcher.parse("foo.bar.package1\tfoo.bar.package2\tfoo.bar.package3");
@@ -136,12 +138,12 @@
     		MockHttpServletRequest req = new MockHttpServletRequest(servletContext);
     		MockHttpServletResponse res = new MockHttpServletResponse();
     		MockFilterChain chain = new MockFilterChain();
-    		final NoOpDispatcher dispatcher = new NoOpDispatcher(servletContext);
+    		final NoOpDispatcher _dispatcher = new NoOpDispatcher(servletContext);
     		Dispatcher.setInstance(null);
 
     		ConfigurationManager confManager = new ConfigurationManager();
     		confManager.setConfiguration(new DefaultConfiguration());
-    		dispatcher.setConfigurationManager(confManager);
+    		_dispatcher.setConfigurationManager(confManager);
     		
     		
     		ObjectFactory.setObjectFactory(new InnerObjectFactory());
@@ -152,13 +154,13 @@
     	
     		FilterDispatcher filter = new FilterDispatcher() {
     			protected Dispatcher createDispatcher() {
-    				return dispatcher;
+    				return _dispatcher;
     			}
     		};
     		filter.init(filterConfig);
     		filter.doFilter(req, res, chain);
     	
-    		assertFalse(dispatcher.serviceRequest);
+    		assertFalse(_dispatcher.serviceRequest);
     	}
     	finally {
     		Settings.reset();
@@ -172,12 +174,12 @@
     		MockHttpServletRequest req = new MockHttpServletRequest(servletContext);
     		MockHttpServletResponse res = new MockHttpServletResponse();
     		MockFilterChain chain = new MockFilterChain();
-    		final InnerDispatcher dispatcher = new InnerDispatcher(servletContext);
+    		final InnerDispatcher _dispatcher = new InnerDispatcher(servletContext);
     		Dispatcher.setInstance(null);
 
     		ConfigurationManager confManager = new ConfigurationManager();
     		confManager.setConfiguration(new DefaultConfiguration());
-    		dispatcher.setConfigurationManager(confManager);
+    		_dispatcher.setConfigurationManager(confManager);
     		
     		
     		ObjectFactory.setObjectFactory(new InnerObjectFactory());
@@ -189,14 +191,14 @@
     	
     		FilterDispatcher filter = new FilterDispatcher() {
     			protected Dispatcher createDispatcher() {
-    				return dispatcher;
+    				return _dispatcher;
     			}
     		};
     		filter.init(filterConfig);
     		filter.doFilter(req, res, chain);
     	
-    		assertTrue(dispatcher.wrappedRequest);
-    		assertTrue(dispatcher.serviceRequest);
+    		assertTrue(_dispatcher.wrappedRequest);
+    		assertTrue(_dispatcher.serviceRequest);
     	}
     	finally {
     		Settings.reset();

Added: struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/FilterTest.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/FilterTest.java?view=auto&rev=471375
==============================================================================
--- struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/FilterTest.java (added)
+++ struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/FilterTest.java Sat Nov  4 23:47:19 2006
@@ -0,0 +1,344 @@
+/*
+ * $Id: FilterDispatcherTest.java 449367 2006-09-24 06:49:04Z mrdon $
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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.apache.struts2.dispatcher;
+
+import java.io.IOException;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestCase;
+
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.config.Settings;
+import org.apache.struts2.dispatcher.mapper.ActionMapper;
+import org.apache.struts2.dispatcher.mapper.ActionMapping;
+import org.springframework.mock.web.MockFilterConfig;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.mock.web.MockServletContext;
+
+import com.mockobjects.servlet.MockFilterChain;
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.config.ConfigurationManager;
+
+
+/**
+ * 
+ * @version $Date$ $Id$
+ */
+public class FilterTest extends TestCase { 
+	
+	protected MockFilterConfig filterConfig;
+	protected MockHttpServletRequest request;
+	protected MockHttpServletResponse response;
+	protected MockFilterChain filterChain;
+	protected MockFilterChain filterChain2;
+	protected MockServletContext servletContext;
+	
+	protected InnerDispatcher _dispatcher1;
+	protected InnerDispatcher _dispatcher2;
+	protected ActionContextCleanUp cleanUp;
+	protected FilterDispatcher filterDispatcher;
+	
+	protected int cleanUpFilterCreateDispatcherCount = 0; // number of times clean up filter create a dispatcher
+	protected int filterDispatcherCreateDispatcherCount = 0; // number of times FilterDispatcher create a dispatcher
+	
+	
+	@Override
+	protected void tearDown() throws Exception {
+		filterConfig = null;
+		request = null;
+		response = null;
+		filterChain = null;
+		filterChain2 = null;
+		servletContext = null;
+		_dispatcher1 = null;
+		_dispatcher2 = null;
+		cleanUp = null;
+		filterDispatcher = null;
+	}
+	
+	@Override
+	protected void setUp() throws Exception {
+		Dispatcher.setInstance(null);
+		
+		filterConfig = new MockFilterConfig();
+		request = new MockHttpServletRequest();
+		response = new MockHttpServletResponse();
+		servletContext = new MockServletContext();
+		
+		_dispatcher1 = new InnerDispatcher(servletContext){
+			@Override
+			public String toString() {
+				return "dispatcher1";
+			}
+		};
+		_dispatcher2 = new InnerDispatcher(servletContext){
+			@Override
+			public String toString() {
+				return "dispatcher2";
+			}
+		};
+		filterChain = new MockFilterChain() {
+			@Override
+			public void doFilter(ServletRequest req, ServletResponse res) throws IOException, ServletException {
+				filterDispatcher.doFilter(req, res, filterChain2);
+			}
+		};
+		filterChain2 = new MockFilterChain() {
+			@Override
+			public void doFilter(ServletRequest req, ServletResponse res) throws IOException, ServletException {
+			}
+		};
+		
+		
+		cleanUp = new ActionContextCleanUp() {
+			@Override
+			protected Dispatcher createDispatcher() {
+				cleanUpFilterCreateDispatcherCount++;
+				return _dispatcher1;
+			}
+			
+			@Override
+			public String toString() {
+				return "cleanUp";
+			}
+		};
+		
+		filterDispatcher = new FilterDispatcher() {
+			@Override
+			protected Dispatcher createDispatcher() {
+				filterDispatcherCreateDispatcherCount++;
+				return _dispatcher2;
+			}
+			
+			@Override
+			public String toString() {
+				return "filterDispatcher";
+			}
+		};
+	}
+	
+	
+	public void testUsingFilterDispatcherOnly() throws Exception {
+		ObjectFactory oldObjecFactory = ObjectFactory.getObjectFactory();
+		try {
+			ObjectFactory.setObjectFactory(new InnerObjectFactory());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_CLASS, "org.apache.struts2.dispatcher.FilterTest$InnerMapper");
+		
+			assertEquals(cleanUpFilterCreateDispatcherCount, 0);
+			assertEquals(filterDispatcherCreateDispatcherCount, 0);
+			assertFalse(_dispatcher1.prepare);
+			assertFalse(_dispatcher1.wrapRequest);
+			assertFalse(_dispatcher1.service);
+			assertFalse(_dispatcher2.prepare);
+			assertFalse(_dispatcher2.wrapRequest);
+			assertFalse(_dispatcher2.service);
+		
+			filterDispatcher.init(filterConfig);
+			filterDispatcher.doFilter(request, response, filterChain2);
+			filterDispatcher.destroy();
+		
+			// we are using FilterDispatcher only, so cleanUp filter's Dispatcher should not be created.
+			assertEquals(cleanUpFilterCreateDispatcherCount, 0);
+			assertEquals(filterDispatcherCreateDispatcherCount, 1);
+			assertFalse(_dispatcher1.prepare);
+			assertFalse(_dispatcher1.wrapRequest);
+			assertFalse(_dispatcher1.service);
+			assertTrue(_dispatcher2.prepare);
+			assertTrue(_dispatcher2.wrapRequest);
+			assertTrue(_dispatcher2.service);
+			assertTrue(Dispatcher.getInstance() == null);
+		}
+		finally {
+			ObjectFactory.setObjectFactory(oldObjecFactory);
+		}
+	}
+	
+	public void testUsingFilterDispatcherOnly_Multiple() throws Exception {
+		ObjectFactory oldObjecFactory = ObjectFactory.getObjectFactory();
+		try {
+			ObjectFactory.setObjectFactory(new InnerObjectFactory());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_CLASS, "org.apache.struts2.dispatcher.FilterTest$InnerMapper");
+		
+			assertEquals(cleanUpFilterCreateDispatcherCount, 0);
+			assertEquals(filterDispatcherCreateDispatcherCount, 0);
+			assertFalse(_dispatcher1.prepare);
+			assertFalse(_dispatcher1.wrapRequest);
+			assertFalse(_dispatcher1.service);
+			assertFalse(_dispatcher2.prepare);
+			assertFalse(_dispatcher2.wrapRequest);
+			assertFalse(_dispatcher2.service);
+		
+			filterDispatcher.init(filterConfig);
+			filterDispatcher.doFilter(request, response, filterChain2);
+			filterDispatcher.doFilter(request, response, filterChain2);
+			filterDispatcher.destroy();
+		
+			assertEquals(cleanUpFilterCreateDispatcherCount, 0);
+			// We should create dispatcher once, although filter.doFilter(...) is called  many times.
+			assertEquals(filterDispatcherCreateDispatcherCount, 1);
+			assertFalse(_dispatcher1.prepare);
+			assertFalse(_dispatcher1.wrapRequest);
+			assertFalse(_dispatcher1.service);
+			assertTrue(_dispatcher2.prepare);
+			assertTrue(_dispatcher2.wrapRequest);
+			assertTrue(_dispatcher2.service);
+			assertTrue(Dispatcher.getInstance() == null);
+		}
+		finally {
+			ObjectFactory.setObjectFactory(oldObjecFactory);
+		}
+	}
+	
+	public void testUsingCleanUpAndFilterDispatcher() throws Exception {
+		ObjectFactory oldObjecFactory = ObjectFactory.getObjectFactory();
+		try {
+			ObjectFactory.setObjectFactory(new InnerObjectFactory());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_CLASS, "org.apache.struts2.dispatcher.FilterTest$InnerMapper");
+		
+			assertEquals(cleanUpFilterCreateDispatcherCount, 0);
+			assertEquals(filterDispatcherCreateDispatcherCount, 0);
+			assertFalse(_dispatcher1.prepare);
+			assertFalse(_dispatcher1.wrapRequest);
+			assertFalse(_dispatcher1.service);
+			assertFalse(_dispatcher2.prepare);
+			assertFalse(_dispatcher2.wrapRequest);
+			assertFalse(_dispatcher2.service);
+		
+			cleanUp.init(filterConfig);
+			filterDispatcher.init(filterConfig);
+			cleanUp.doFilter(request, response, filterChain);
+			filterDispatcher.destroy();
+			cleanUp.destroy();
+		
+			assertEquals(cleanUpFilterCreateDispatcherCount, 1);
+			assertEquals(filterDispatcherCreateDispatcherCount, 1);
+			assertTrue(_dispatcher1.prepare);
+			assertTrue(_dispatcher1.wrapRequest);
+			assertTrue(_dispatcher1.service);
+			assertFalse(_dispatcher2.prepare);
+			assertFalse(_dispatcher2.wrapRequest);
+			assertFalse(_dispatcher2.service);
+			assertTrue(Dispatcher.getInstance() == null);
+		}
+		finally {
+			ObjectFactory.setObjectFactory(oldObjecFactory);
+		}
+	}
+	
+	
+	public void testUsingCleanUpAndFilterDispatcher_Multiple() throws Exception {
+		ObjectFactory oldObjecFactory = ObjectFactory.getObjectFactory();
+		try {
+			ObjectFactory.setObjectFactory(new InnerObjectFactory());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_CLASS, "org.apache.struts2.dispatcher.FilterTest$InnerMapper");
+		
+			assertEquals(cleanUpFilterCreateDispatcherCount, 0);
+			assertEquals(filterDispatcherCreateDispatcherCount, 0);
+			assertFalse(_dispatcher1.prepare);
+			assertFalse(_dispatcher1.wrapRequest);
+			assertFalse(_dispatcher1.service);
+			assertFalse(_dispatcher2.prepare);
+			assertFalse(_dispatcher2.wrapRequest);
+			assertFalse(_dispatcher2.service);
+		
+			cleanUp.init(filterConfig);
+			filterDispatcher.init(filterConfig);
+			cleanUp.doFilter(request, response, filterChain);
+			cleanUp.doFilter(request, response, filterChain);
+			filterDispatcher.destroy();
+			cleanUp.destroy();
+		
+			assertEquals(cleanUpFilterCreateDispatcherCount, 1);
+			assertEquals(filterDispatcherCreateDispatcherCount, 1);
+			assertTrue(_dispatcher1.prepare);
+			assertTrue(_dispatcher1.wrapRequest);
+			assertTrue(_dispatcher1.service);
+			assertFalse(_dispatcher2.prepare);
+			assertFalse(_dispatcher2.wrapRequest);
+			assertFalse(_dispatcher2.service);
+			assertTrue(Dispatcher.getInstance() == null);
+		}
+		finally {
+			ObjectFactory.setObjectFactory(oldObjecFactory);
+		}
+	}
+	
+	
+	class InnerDispatcher extends Dispatcher {
+		public boolean prepare = false;
+		public boolean wrapRequest = false;
+		public boolean service = false;
+		
+		public InnerDispatcher(ServletContext servletContext) {
+			super(servletContext);
+		}
+		
+		@Override
+		public void prepare(HttpServletRequest request, HttpServletResponse response) {
+			prepare = true;
+		}
+		
+		@Override
+		public HttpServletRequest wrapRequest(HttpServletRequest request, ServletContext servletContext) throws IOException {
+			wrapRequest = true;
+			return request;
+		}
+		
+		@Override
+		public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context, ActionMapping mapping) throws ServletException {
+			service = true;
+		}
+	}
+	
+	class NullInnerMapper implements ActionMapper {
+		public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) {
+			return null;
+		}
+
+		public String getUriFromActionMapping(ActionMapping mapping) {
+			return null;
+		}
+	}
+	
+	public static class InnerMapper implements ActionMapper {
+		
+		public InnerMapper() {}
+		
+		public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) {
+			return new ActionMapping();
+		}
+
+		public String getUriFromActionMapping(ActionMapping mapping) {
+			return "";
+		}
+	}
+	
+	class InnerObjectFactory extends ObjectFactory {
+		public InnerObjectFactory() {
+			super();
+		}
+	}
+}
+