1 /*
2 * Copyright 2002-2008 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.springframework.web.filter;
18
19 import java.io.IOException;
20
21 import javax.servlet.FilterChain;
22 import javax.servlet.ServletException;
23 import javax.servlet.ServletRequest;
24 import javax.servlet.ServletResponse;
25 import javax.servlet.http.HttpServletRequest;
26 import javax.servlet.http.HttpServletResponse;
27
28 /**
29 * Filter base class that guarantees to be just executed once per request,
30 * on any servlet container. It provides a {@link #doFilterInternal}
31 * method with HttpServletRequest and HttpServletResponse arguments.
32 *
33 * <p>The {@link #getAlreadyFilteredAttributeName} method determines how
34 * to identify that a request is already filtered. The default implementation
35 * is based on the configured name of the concrete filter instance.
36 *
37 * @author Juergen Hoeller
38 * @since 06.12.2003
39 */
40 public abstract class OncePerRequestFilter extends GenericFilterBean {
41
42 /**
43 * Suffix that gets appended to the filter name for the
44 * "already filtered" request attribute.
45 * @see #getAlreadyFilteredAttributeName
46 */
47 public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";
48
49
50 /**
51 * This <code>doFilter</code> implementation stores a request attribute for
52 * "already filtered", proceeding without filtering again if the
53 * attribute is already there.
54 * @see #getAlreadyFilteredAttributeName
55 * @see #shouldNotFilter
56 * @see #doFilterInternal
57 */
58 public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
59 throws ServletException, IOException {
60
61 if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
62 throw new ServletException("OncePerRequestFilter just supports HTTP requests");
63 }
64 HttpServletRequest httpRequest = (HttpServletRequest) request;
65 HttpServletResponse httpResponse = (HttpServletResponse) response;
66
67 String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
68 if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(httpRequest)) {
69 // Proceed without invoking this filter...
70 filterChain.doFilter(request, response);
71 }
72 else {
73 // Do invoke this filter...
74 request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
75 try {
76 doFilterInternal(httpRequest, httpResponse, filterChain);
77 }
78 finally {
79 // Remove the "already filtered" request attribute for this request.
80 request.removeAttribute(alreadyFilteredAttributeName);
81 }
82 }
83 }
84
85 /**
86 * Return the name of the request attribute that identifies that a request
87 * is already filtered.
88 * <p>Default implementation takes the configured name of the concrete filter
89 * instance and appends ".FILTERED". If the filter is not fully initialized,
90 * it falls back to its class name.
91 * @see #getFilterName
92 * @see #ALREADY_FILTERED_SUFFIX
93 */
94 protected String getAlreadyFilteredAttributeName() {
95 String name = getFilterName();
96 if (name == null) {
97 name = getClass().getName();
98 }
99 return name + ALREADY_FILTERED_SUFFIX;
100 }
101
102 /**
103 * Can be overridden in subclasses for custom filtering control,
104 * returning <code>true</code> to avoid filtering of the given request.
105 * <p>The default implementation always returns <code>false</code>.
106 * @param request current HTTP request
107 * @return whether the given request should <i>not</i> be filtered
108 * @throws ServletException in case of errors
109 */
110 protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
111 return false;
112 }
113
114
115 /**
116 * Same contract as for <code>doFilter</code>, but guaranteed to be
117 * just invoked once per request. Provides HttpServletRequest and
118 * HttpServletResponse arguments instead of the default ServletRequest
119 * and ServletResponse ones.
120 */
121 protected abstract void doFilterInternal(
122 HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
123 throws ServletException, IOException;
124
125 }