View Javadoc

1   /*
2    * Copyright 2004-2009 the Seasar Foundation and the Others.
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,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.seasar.cubby.filter;
17  
18  import java.io.IOException;
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.StringTokenizer;
22  import java.util.regex.Matcher;
23  import java.util.regex.Pattern;
24  
25  import javax.servlet.Filter;
26  import javax.servlet.FilterChain;
27  import javax.servlet.FilterConfig;
28  import javax.servlet.ServletException;
29  import javax.servlet.ServletRequest;
30  import javax.servlet.ServletResponse;
31  import javax.servlet.http.HttpServletRequest;
32  import javax.servlet.http.HttpServletResponse;
33  
34  import org.seasar.cubby.internal.util.StringUtils;
35  
36  /**
37   * 適用されたリクエストに対して、異常系の HTTP ステータスコードを返す {@link Filter} です。
38   * 
39   * @author baba
40   * @since 1.1.0
41   */
42  public class SendErrorFilter implements Filter {
43  
44  	/** レスポンスの HTTP ステータスコードの初期パラメータ名。 */
45  	public static final String STATUS_CODE = "statusCode";
46  
47  	/** 対象外とするパスの初期パラメータ名。 */
48  	public static final String IGNORE_PATH_PATTERN = "ignorePathPattern";
49  
50  	/** レスポンスの HTTP ステータスコード (デフォルトは 403 Forbidden)。 */
51  	private int statusCode = HttpServletResponse.SC_FORBIDDEN;
52  
53  	/** 対象外とするパスの正規表現パターンのリスト。 */
54  	private final List<Pattern> ignorePathPatterns = new ArrayList<Pattern>();
55  
56  	/**
57  	 * このフィルタを初期化します。
58  	 * <p>
59  	 * <table>
60  	 * <caption>使用可能な初期化パラメータ</caption>
61  	 * <thead>
62  	 * <th>初期化パラメータ名</th>
63  	 * <th>初期化パラメータの値</th>
64  	 * <th>例</th>
65  	 * </thead>
66  	 * <thead>
67  	 * <tr>
68  	 * <td>{@link #STATUS_CODE}</td>
69  	 * <td>レスポンスの HTTP ステータスコードを指定します。指定しなかった場合は
70  	 * {@link HttpServletResponse#SC_FORBIDDEN} を返します。</td>
71  	 * <td></td>
72  	 * <tr>
73  	 * <td>{@link #IGNORE_PATH_PATTERN}</td>
74  	 * <td>対象外とするパスの正規表現をカンマ区切りで指定します。 filter-mapping の url-pattern
75  	 * で指定する、このフィルタを適用する URL のうち、適用を除外したいパスを指定してください。</td>
76  	 * <td>
77  	 * 
78  	 * <pre>
79  	 * &lt;filter&gt;
80  	 *   &lt;filter-name&gt;sendErrorFilter&lt;/filter-name&gt;
81  	 *   &lt;filter-class&gt;org.seasar.cubby.filter.SendErrorFilter&lt;/filter-class&gt;
82  	 *   &lt;init-param&gt;
83  	 *     &lt;param-name&gt;statusCode;&lt;/param-name&gt;
84  	 *     &lt;param-value&gt;404&lt;param-name&gt;
85  	 *   &lt;/init-param&gt;
86  	 *   &lt;init-param&gt;
87  	 *     &lt;param-name&gt;ignorePathPattern&lt;/param-name&gt;
88  	 *     &lt;param-value&gt;/index.jsp&lt;param-name&gt;
89  	 *   &lt;/init-param&gt;
90  	 * &lt;/filter&gt;
91  	 * 
92  	 * &lt;filter-mapping&gt;
93  	 *   &lt;filter-name&gt;sendErrorFilter&lt;filter-name&gt;
94  	 *   &lt;url-pattern&gt;*.jsp&lt;url-pattern&gt;
95  	 *   &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;
96  	 * &lt;/filter-mapping&gt;
97  	 * </pre>
98  	 * 
99  	 * この例では、 /index.jsp を除く *.jsp にリクエストがあった場合に HTTP ステータスコード 404 (Not Found)
100 	 * を返します。</td>
101 	 * </tr>
102 	 * </thead>
103 	 * </p>
104 	 * 
105 	 * @param config
106 	 *            Filter 設定のためのオブジェクト
107 	 * @exception ServletException
108 	 *                初期化処理で例外が発生した場合
109 	 */
110 	public void init(final FilterConfig config) throws ServletException {
111 		final String statusCodeString = config.getInitParameter(STATUS_CODE);
112 		if (statusCodeString != null) {
113 			statusCode = Integer.parseInt(statusCodeString);
114 		}
115 		final String ignorePathPatternString = config
116 				.getInitParameter(IGNORE_PATH_PATTERN);
117 		if (!StringUtils.isEmpty(ignorePathPatternString)) {
118 
119 			for (final StringTokenizer tokenizer = new StringTokenizer(
120 					ignorePathPatternString, ","); tokenizer.hasMoreTokens();) {
121 				final String token = tokenizer.nextToken();
122 				final Pattern pattern = Pattern.compile(token);
123 				ignorePathPatterns.add(pattern);
124 			}
125 		}
126 	}
127 
128 	/**
129 	 * {@inheritDoc}
130 	 */
131 	public void destroy() {
132 	}
133 
134 	/**
135 	 * {@link HttpServletResponse#sendError(int)} によって、異常系の HTTP ステータスコードを返します。
136 	 * 
137 	 * @param req
138 	 *            要求
139 	 * @param res
140 	 *            応答
141 	 * @param chain
142 	 *            フィルターチェーン
143 	 * @throws IOException
144 	 *             要求の転送や要求のチェーンがこの例外をスローする場合
145 	 * @throws ServletException
146 	 *             要求の転送や要求のチェーンがこの例外をスローする場合
147 	 */
148 	public void doFilter(final ServletRequest req, final ServletResponse res,
149 			final FilterChain chain) throws IOException, ServletException {
150 
151 		final HttpServletRequest request = (HttpServletRequest) req;
152 		final HttpServletResponse response = (HttpServletResponse) res;
153 
154 		if (isIgnore(request)) {
155 			chain.doFilter(request, response);
156 		} else {
157 			response.sendError(statusCode);
158 		}
159 	}
160 
161 	/**
162 	 * 指定されたリクエストがこのフィルタの対象外であるかを示します。
163 	 * 
164 	 * @param request
165 	 *            リクエスト
166 	 * @return 指定されたリクエストがこのフィルタの対象外である場合は <code>true</code>、そうでない場合は
167 	 *         <code>false</code>
168 	 */
169 	private boolean isIgnore(final HttpServletRequest request) {
170 		final String servletPath = request.getServletPath();
171 		for (final Pattern ignorePattern : ignorePathPatterns) {
172 			final Matcher matcher = ignorePattern.matcher(servletPath);
173 			if (matcher.matches()) {
174 				return true;
175 			}
176 		}
177 		return false;
178 	}
179 
180 }