View Javadoc

1   /*
2    * Copyright 2004-2010 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  
17  package org.seasar.cubby.filter;
18  
19  import static org.seasar.cubby.CubbyConstants.ATTR_ACTION;
20  import static org.seasar.cubby.CubbyConstants.ATTR_CONTEXT_PATH;
21  import static org.seasar.cubby.CubbyConstants.ATTR_FORM_WRAPPER_FACTORY;
22  import static org.seasar.cubby.CubbyConstants.ATTR_MESSAGES;
23  import static org.seasar.cubby.CubbyConstants.ATTR_MESSAGES_RESOURCE_BUNDLE;
24  
25  import java.util.ArrayList;
26  import java.util.Enumeration;
27  import java.util.HashMap;
28  import java.util.HashSet;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.ResourceBundle;
32  import java.util.Set;
33  import java.util.Map.Entry;
34  
35  import javax.servlet.ServletRequest;
36  import javax.servlet.http.HttpServletRequest;
37  import javax.servlet.http.HttpServletRequestWrapper;
38  
39  import org.seasar.cubby.CubbyConstants;
40  import org.seasar.cubby.controller.FormWrapperFactory;
41  import org.seasar.cubby.controller.MessagesBehaviour;
42  import org.seasar.cubby.internal.controller.impl.FormWrapperFactoryImpl;
43  import org.seasar.cubby.internal.util.IteratorEnumeration;
44  import org.seasar.cubby.spi.beans.Attribute;
45  import org.seasar.cubby.spi.beans.BeanDesc;
46  import org.seasar.cubby.spi.beans.BeanDescFactory;
47  
48  /**
49   * 特別な属性を取得するためにサーブレットへの要求をラップします。
50   * <p>
51   * <ul>
52   * <li>{@link #getAttribute(String)}</li>
53   * <li>{@link #getAttributeNames()}</li>
54   * </ul>
55   * 上記メソッドでは、ラップされた要求の属性に加えて以下のような属性を使用することができます。
56   * <table>
57   * <thead>
58   * <tr>
59   * <th>属性名</th>
60   * <th>値</th>
61   * <th>型</th>
62   * </tr>
63   * </thead><tbody>
64   * <tr>
65   * <td>{@link CubbyConstants#ATTR_CONTEXT_PATH}</td>
66   * <td>コンテキストパス</td>
67   * <td>{@link String}</td>
68   * </tr>
69   * <tr>
70   * <td>{@link CubbyConstants#ATTR_ACTION}</td>
71   * <td>アクション</td>
72   * <td>{@link org.seasar.cubby.action.Action}</td>
73   * </tr>
74   * <tr>
75   * <td>{@link CubbyConstants#ATTR_MESSAGES}</td>
76   * <td>メッセージリソースの <code>Map</code></td>
77   * <td>{@link java.util.Map}</td>
78   * </tr>
79   * <tr>
80   * <td>{@link CubbyConstants#ATTR_MESSAGES_RESOURCE_BUNDLE}</td>
81   * <td>メッセージリソースの <code>ResourceBundle</code></td>
82   * <td>{@link java.util.ResourceBundle}</td>
83   * </tr>
84   * <tr>
85   * <td>{@link CubbyConstants#ATTR_FORM_WRAPPER_FACTORY}</td>
86   * <td>フォームオブジェクトのラッパーファクトリ</td>
87   * <td>{@link FormWrapperFactory}</td>
88   * </tr>
89   * <tr>
90   * <td>アクションのプロパティ名</td>
91   * <td>アクションのプロパティ値</td>
92   * <td>任意</td>
93   * </tr>
94   * </table>
95   * これらの属性は通常の属性よりも優先されるのでご注意ください。
96   * </p>
97   * <p>
98   * また、以下の要求パラメータに関するメソッドは、通常の要求パラメータに加え、URI パラメータも対象として処理します。
99   * <ul>
100  * <li>{@link #getParameter(String)}</li>
101  * <li>{@link #getParameterMap()}</li>
102  * <li>{@link #getParameterNames()}</li>
103  * <li>{@link #getParameterValues(String)}</li>
104  * </ul>
105  * </p>
106  * 
107  * @author baba
108  */
109 class CubbyHttpServletRequestWrapper extends HttpServletRequestWrapper {
110 
111 	/** Cubby フィルタです。 */
112 	private final CubbyFilter cubbyFilter;
113 
114 	/** URI パラメータの {@link Map} です。 */
115 	private final Map<String, String[]> uriParameters;
116 
117 	/** フォームオブジェクトのラッパーファクトリです。 */
118 	private FormWrapperFactory formWrapperFactory;
119 
120 	/** メッセージ表示用リソースバンドルの振る舞い */
121 	private MessagesBehaviour messagesBehaviour;
122 
123 	/**
124 	 * 指定された要求をラップした要求オブジェクトを構築します。
125 	 * 
126 	 * @param cubbyFilter
127 	 *            Cubby フィルタ
128 	 * @param request
129 	 *            要求
130 	 * @param uriParameters
131 	 *            URI パラメータの {@link Map}
132 	 */
133 	CubbyHttpServletRequestWrapper(final CubbyFilter cubbyFilter,
134 			final HttpServletRequest request,
135 			final Map<String, String[]> uriParameters) {
136 		super(request);
137 		this.cubbyFilter = cubbyFilter;
138 		this.uriParameters = uriParameters;
139 	}
140 
141 	/**
142 	 * 指定された属性の値を <code>Object</code> として返します。指定された名前の属性が存在しない場合は、
143 	 * <code>null</code> を返します。
144 	 * 
145 	 * @param name
146 	 *            属性の名前を指定する <code>String</code>
147 	 * @return 属性の値を含む <code>Object</code>。属性が存在しない場合は <code>null</code>
148 	 */
149 	@Override
150 	public Object getAttribute(final String name) {
151 		final Object value;
152 		if (ATTR_CONTEXT_PATH.equals(name)) {
153 			value = this.getContextPath();
154 		} else if (ATTR_MESSAGES.equals(name)) {
155 			value = getMessagesAsMap(this.getRequest(), this
156 					.getMessagesBehaviour());
157 		} else if (ATTR_MESSAGES_RESOURCE_BUNDLE.equals(name)) {
158 			value = getMessagesAsResourceBundle(this.getRequest(), this
159 					.getMessagesBehaviour());
160 		} else if (ATTR_FORM_WRAPPER_FACTORY.equals(name)) {
161 			if (this.formWrapperFactory == null) {
162 				this.formWrapperFactory = new FormWrapperFactoryImpl();
163 			}
164 			value = this.formWrapperFactory;
165 		} else {
166 			final Object action = super.getAttribute(ATTR_ACTION);
167 			if (action != null) {
168 				final BeanDesc beanDesc = BeanDescFactory.getBeanDesc(action
169 						.getClass());
170 				if (beanDesc.hasPropertyAttribute(name)) {
171 					final Attribute attribute = beanDesc
172 							.getPropertyAttribute(name);
173 					if (attribute.isReadable()) {
174 						value = attribute.getValue(action);
175 					} else {
176 						value = super.getAttribute(name);
177 					}
178 				} else {
179 					value = super.getAttribute(name);
180 				}
181 			} else {
182 				value = super.getAttribute(name);
183 			}
184 		}
185 		return value;
186 	}
187 
188 	/**
189 	 * この要求で利用できる属性の名前が格納された <code>Enumeration</code> を返します。利用できる属性が要求にない場合は、空の
190 	 * <code>Enumeration</code> を返します。
191 	 * 
192 	 * @return 要求に付随する属性の名前が格納された文字列の <code>Enumeration</code>
193 	 */
194 	@SuppressWarnings("unchecked")
195 	@Override
196 	public Enumeration getAttributeNames() {
197 		final Set attributeNames = new HashSet();
198 
199 		attributeNames.add(ATTR_CONTEXT_PATH);
200 		attributeNames.add(ATTR_ACTION);
201 		attributeNames.add(ATTR_MESSAGES);
202 		attributeNames.add(ATTR_MESSAGES_RESOURCE_BUNDLE);
203 		attributeNames.add(ATTR_FORM_WRAPPER_FACTORY);
204 
205 		final Object action = super.getAttribute(ATTR_ACTION);
206 		if (action != null) {
207 			final BeanDesc beanDesc = BeanDescFactory.getBeanDesc(action
208 					.getClass());
209 			for (final Attribute attribute : beanDesc.findtPropertyAttributes()) {
210 				if (attribute.isReadable()) {
211 					attributeNames.add(attribute.getName());
212 				}
213 			}
214 		}
215 
216 		final Enumeration defaultAttributeNames = super.getAttributeNames();
217 		while (defaultAttributeNames.hasMoreElements()) {
218 			attributeNames.add(defaultAttributeNames.nextElement());
219 		}
220 		return new IteratorEnumeration(attributeNames.iterator());
221 	}
222 
223 	/**
224 	 * 要求パラメータの値を <code>String</code> として返します。
225 	 * <p>
226 	 * パラメータが存在しない場合は、<code>null</code> を返します。
227 	 * </p>
228 	 * 
229 	 * @param name
230 	 *            パラメータの名前を指定する <code>String</code>
231 	 * @return パラメータの単一の値を表す <code>String</code>
232 	 */
233 	@Override
234 	public String getParameter(final String name) {
235 		final String[] parameters = this.getParameterValues(name);
236 		if (parameters == null) {
237 			return null;
238 		} else {
239 			return parameters[0];
240 		}
241 	}
242 
243 	/**
244 	 * この要求に含まれるパラメータの名前を格納した、<code>String</code> オブジェクトの
245 	 * <code>Enumeration</code> を返します。
246 	 * <p>
247 	 * パラメータが要求にない場合、このメソッドは空の <code>Enumeration</code> を返します。
248 	 * </p>
249 	 */
250 	@SuppressWarnings("unchecked")
251 	@Override
252 	public Enumeration getParameterNames() {
253 		return new IteratorEnumeration(this.getParameterMap().keySet()
254 				.iterator());
255 	}
256 
257 	/**
258 	 * 指定された要求パラメータのすべての値が格納された <code>String</code> オブジェクトの配列を返します。
259 	 * <p>
260 	 * パラメータが存在しない場合は、<code>null</code> を返します。
261 	 * </p>
262 	 * 
263 	 * @param name
264 	 *            取得したいパラメータの名前を表す <code>String</code>
265 	 * @return パラメータの値が格納された <code>String</code> オブジェクトの配列
266 	 */
267 	@SuppressWarnings("unchecked")
268 	@Override
269 	public String[] getParameterValues(final String name) {
270 		final Map<String, String[]> parameterMap = this.getParameterMap();
271 		return parameterMap.get(name);
272 	}
273 
274 	/**
275 	 * この要求から取得できるパラメータを <code>java.util.Map</code> で返します。
276 	 * 
277 	 * @return キーとしてパラメータ名、マップ値としてパラメータ値が格納された不変の <code>java.util.Map</code>。
278 	 *         <p>
279 	 *         パラメータマップ内のキーは <code>String</code> 型。パラメータマップ内の値は
280 	 *         <code>String</code> の配列型
281 	 *         </p>
282 	 */
283 	@SuppressWarnings("unchecked")
284 	@Override
285 	public Map getParameterMap() {
286 		final Map<String, String[]> parameterMap = buildParameterMap(
287 				(HttpServletRequest) getRequest(), uriParameters);
288 		return parameterMap;
289 	}
290 
291 	/**
292 	 * 要求パラメータを構築します。
293 	 * 
294 	 * @param request
295 	 *            要求
296 	 * @param uriParameters
297 	 *            URI パラメータの {@link Map}
298 	 * @return URI パラメータを含む要求パラメータの {@link Map}
299 	 */
300 	private Map<String, String[]> buildParameterMap(
301 			final HttpServletRequest request,
302 			final Map<String, String[]> uriParameters) {
303 		final Map<String, List<String>> extendedParameterMap = new HashMap<String, List<String>>();
304 
305 		final Map<?, ?> originalParameterMap = request.getParameterMap();
306 		for (final Entry<?, ?> entry : originalParameterMap.entrySet()) {
307 			final String name = (String) entry.getKey();
308 			final List<String> values = new ArrayList<String>();
309 			for (final String value : (String[]) entry.getValue()) {
310 				values.add(value);
311 			}
312 			extendedParameterMap.put(name, values);
313 		}
314 		for (final Entry<String, String[]> entry : uriParameters.entrySet()) {
315 			final String name = entry.getKey();
316 			if (extendedParameterMap.containsKey(name)) {
317 				final List<String> values = extendedParameterMap.get(name);
318 				for (final String value : entry.getValue()) {
319 					values.add(value);
320 				}
321 			} else {
322 				final List<String> values = new ArrayList<String>();
323 				for (final String value : entry.getValue()) {
324 					values.add(value);
325 				}
326 				extendedParameterMap.put(name, values);
327 			}
328 		}
329 
330 		final Map<String, String[]> parameterMap = new HashMap<String, String[]>();
331 		for (final Entry<String, List<String>> entry : extendedParameterMap
332 				.entrySet()) {
333 			parameterMap.put(entry.getKey(), entry.getValue().toArray(
334 					new String[0]));
335 		}
336 		return parameterMap;
337 	}
338 
339 	/**
340 	 * 現在の実行スレッドに関連付けられた要求に対応するメッセージ用の {@link ResourceBundle} を取得します。
341 	 * 
342 	 * @return リソースバンドル
343 	 */
344 	private static ResourceBundle getMessagesAsResourceBundle(
345 			final ServletRequest request,
346 			final MessagesBehaviour messagesBehaviour) {
347 		final ResourceBundle bundle = (ResourceBundle) request
348 				.getAttribute(ATTR_MESSAGES_RESOURCE_BUNDLE);
349 		if (bundle != null) {
350 			return bundle;
351 		}
352 
353 		final ResourceBundle newBundle = messagesBehaviour.getBundle(request
354 				.getLocale());
355 		request.setAttribute(ATTR_MESSAGES_RESOURCE_BUNDLE, newBundle);
356 		return newBundle;
357 	}
358 
359 	/**
360 	 * {@link #getMessagesResourceBundle()} で取得できる {@link ResourceBundle} を変換した
361 	 * {@link Map} を取得します。
362 	 * 
363 	 * @return メッセージの {@link Map}
364 	 */
365 	private static Map<String, Object> getMessagesAsMap(
366 			final ServletRequest request,
367 			final MessagesBehaviour messagesBehaviour) {
368 		@SuppressWarnings("unchecked")
369 		final Map<String, Object> messages = (Map<String, Object>) request
370 				.getAttribute(ATTR_MESSAGES);
371 		if (messages != null) {
372 			return messages;
373 		}
374 
375 		final ResourceBundle bundle = getMessagesAsResourceBundle(request,
376 				messagesBehaviour);
377 		final Map<String, Object> newMessages = messagesBehaviour.toMap(bundle);
378 		request.setAttribute(ATTR_MESSAGES, newMessages);
379 		return newMessages;
380 	}
381 
382 	/**
383 	 * メッセージ表示用リソースバンドルの振る舞いを取得します。
384 	 * 
385 	 * @param context
386 	 *            実行スレッドのコンテキスト情報
387 	 * @return メッセージ表示用リソースバンドルの振る舞い
388 	 */
389 	private MessagesBehaviour getMessagesBehaviour() {
390 		if (this.messagesBehaviour == null) {
391 			this.messagesBehaviour = cubbyFilter.createMessagesBehaviour();
392 		}
393 		return this.messagesBehaviour;
394 	}
395 
396 }