View Javadoc

1   /*
2    * $Id: ActionContextCleanUp.java 454720 2006-10-10 12:31:52Z tmjee $
3    *
4    * Copyright 2006 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.struts2.dispatcher;
19  
20  import java.io.IOException;
21  
22  import javax.servlet.Filter;
23  import javax.servlet.FilterChain;
24  import javax.servlet.FilterConfig;
25  import javax.servlet.ServletContext;
26  import javax.servlet.ServletException;
27  import javax.servlet.ServletRequest;
28  import javax.servlet.ServletResponse;
29  import javax.servlet.http.HttpServletRequest;
30  import javax.servlet.http.HttpServletResponse;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  
35  import com.opensymphony.xwork2.ActionContext;
36  import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
37  
38  /***
39   * <!-- SNIPPET START: description -->
40   * Special filter designed to work with the {@link FilterDispatcher} and allow
41   * for easier integration with SiteMesh. Normally, ordering your filters to have
42   * SiteMesh go first, and then {@link FilterDispatcher} go second is perfectly fine.
43   * However, sometimes you may wish to access Struts features, including the
44   * value stack, from within your SiteMesh decorators. Because {@link FilterDispatcher}
45   * cleans up the {@link ActionContext}, your decorator won't have access to the
46   * date you want.
47   * <p/>
48   * <p/>
49   * By adding this filter, the {@link FilterDispatcher} will know to not clean up and
50   * instead defer cleanup to this filter. The ordering of the filters should then be:
51   * <p/>
52   * <ul>
53   * <li>this filter</li>
54   * <li>SiteMesh filter</li>
55   * <li>{@link FilterDispatcher}</li>
56   * </ul>
57   * <!-- SNIPPET END: description -->
58   *
59   * @version $Date: 2006-10-10 07:31:52 -0500 (Tue, 10 Oct 2006) $ $Id: ActionContextCleanUp.java 454720 2006-10-10 12:31:52Z tmjee $
60   *
61   * @see FilterDispatcher
62   */
63  public class ActionContextCleanUp implements Filter {
64  
65      private static final Log LOG = LogFactory.getLog(ActionContextCleanUp.class);
66  
67      private static final String COUNTER = "__cleanup_recursion_counter";
68  
69      protected FilterConfig filterConfig;
70      protected Dispatcher dispatcher;
71  
72      /***
73       * Initializes the filter
74       * 
75       * @param filterConfig The filter configuration
76       */
77      public void init(FilterConfig filterConfig) throws ServletException {
78          this.filterConfig = filterConfig;
79          dispatcher = new Dispatcher(filterConfig.getServletContext());
80      }
81  
82      
83      /***
84       * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
85       */
86      public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
87  
88          HttpServletRequest request = (HttpServletRequest) req;
89          HttpServletResponse response = (HttpServletResponse) res;
90  
91          String timerKey = "ActionContextCleanUp_doFilter: ";
92          try {
93          	UtilTimerStack.push(timerKey);
94          	
95          	// prepare the request no matter what - this ensures that the proper character encoding
96          	// is used before invoking the mapper (see WW-9127)
97          	Dispatcher.setInstance(dispatcher);
98          	dispatcher.prepare(request, response);
99  
100         	ServletContext servletContext = filterConfig.getServletContext();
101         	try {
102         		request = dispatcher.wrapRequest(request, servletContext);
103         	} catch (IOException e) {
104         		String message = "Could not wrap servlet request with MultipartRequestWrapper!";
105         		LOG.error(message, e);
106         		throw new ServletException(message, e);
107         	}
108 
109         	try {
110         		Integer count = (Integer)request.getAttribute(COUNTER);
111         		if (count == null) {
112         			count = new Integer(1);
113         		}
114         		else {
115         			count = new Integer(count.intValue()+1);
116         		}
117         		request.setAttribute(COUNTER, count);
118         		chain.doFilter(request, response);
119         	} finally {
120         		int counterVal = ((Integer)request.getAttribute(COUNTER)).intValue();
121         		counterVal -= 1;
122         		request.setAttribute(COUNTER, new Integer(counterVal));
123         		cleanUp(request);
124         	}
125         }
126         finally {
127         	UtilTimerStack.pop(timerKey);
128         }
129     }
130 
131     /***
132      * Clean up the request of threadlocals if this is the last execution
133      * 
134      * @param req The servlet request
135      */
136     protected static void cleanUp(ServletRequest req) {
137         // should we clean up yet?
138         if (req.getAttribute(COUNTER) != null &&
139                  ((Integer)req.getAttribute(COUNTER)).intValue() > 0 ) {
140              return;
141          }
142 
143         // always dontClean up the thread request, even if an action hasn't been executed
144         ActionContext.setContext(null);
145         
146         Dispatcher.setInstance(null);
147     }
148 
149     
150     /* (non-Javadoc)
151      * @see javax.servlet.Filter#destroy()
152      */
153     public void destroy() {
154     }
155 }