##### Coverage Report - org.seasar.cubby.action.Redirect

Classes in this File | Line Coverage | Branch Coverage | Complexity
Redirect |
72% | 72/100
61% | 21/34
| 2.0476190476190474;2.048

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.action;
17 |  |
18 |  |
     import static org.seasar.cubby.internal.util.LogMessages.format;
19 |  |
20 |  |
     import java.net.MalformedURLException;
21 |  |
     import java.net.URL;
22 |  |
     import java.util.Collections;
23 |  |
     import java.util.HashMap;
24 |  |
     import java.util.Map;
25 |  |
26 |  |
     import javax.servlet.http.HttpServletRequest;
27 |  |
     import javax.servlet.http.HttpServletResponse;
28 |  |
29 |  |
     import org.seasar.cubby.internal.util.MetaUtils;
30 |  |
     import org.seasar.cubby.internal.util.QueryStringBuilder;
31 |  |
     import org.seasar.cubby.internal.util.StringUtils;
32 |  |
     import org.seasar.cubby.routing.PathResolver;
33 |  |
     import org.seasar.cubby.spi.PathResolverProvider;
34 |  |
     import org.seasar.cubby.spi.ProviderFactory;
35 |  |
     import org.seasar.cubby.util.LinkBuilder;
36 |  |
     import org.slf4j.Logger;
37 |  |
     import org.slf4j.LoggerFactory;
38 |  |
39 |  |
     /**
40 |  |
      * 指定されたパスにリダイレクトする {@link ActionResult} です。
41 |  |
      * <p>
42 |  |
      * アクションメソッドの戻り値としてこのインスタンスを指定することで、指定されたパスにリダイレクトします。
43 |  |
      * </p>
44 |  |
      * <p>
45 |  |
      * 使用例1 : リダイレクト先を相対パスで指定
46 |  |
      *
47 |  |
      * <pre>
48 |  |
      * return new Redirect(&quot;list&quot;);
49 |  |
      * </pre>
50 |  |
      *
51 |  |
      * </p>
52 |  |
      * <p>
53 |  |
      * 使用例2 : リダイレクト先を絶対パスで指定
54 |  |
      *
55 |  |
      * <pre>
56 |  |
      * return new Redirect(&quot;/todo/list&quot;);
57 |  |
      * </pre>
58 |  |
      *
59 |  |
      * </p>
60 |  |
      * <p>
61 |  |
      * 使用例3 : リダイレクト先をクラスとメソッド名で指定
62 |  |
      *
63 |  |
      * <pre>
64 |  |
      * return new Redirect(TodoListAction.class, &quot;show&quot;);
65 |  |
      * </pre>
66 |  |
      *
67 |  |
      * </p>
68 |  |
      * <p>
69 |  |
      * 使用例4 : リダイレクト先をクラスとメソッド名で指定(paramメソッドによるパラメータつき)
70 |  |
      *
71 |  |
      * <pre>
72 |  |
      * return new Redirect(TodoListAction.class, &quot;show&quot;).param(&quot;value1&quot;, &quot;12345&quot;);
73 |  |
      * </pre>
74 |  |
      *
75 |  |
      * </p>
76 |  |
      * <p>
77 |  |
      * 使用例5 : リダイレクト先をクラスとメソッド名で指定(Mapによるパラメータつき)
78 |  |
      *
79 |  |
      * <pre>
80 |  |
      * Map&lt;String, String[]&gt; parameters = new HashMap();
81 |  |
      * parameters.put(&quot;value1&quot;, new String[] { &quot;12345&quot; });
82 |  |
      * return new Redirect(TodoListAction.class, &quot;show&quot;, parameters);
83 |  |
      * </pre>
84 |  |
      *
85 |  |
      * </p>
86 |  |
      * <p>
87 |  |
      * 通常は {@link HttpServletResponse#encodeRedirectURL(String)} によってエンコードされた URL
88 |  |
      * にリダイレクトするため、URL にセッション ID が埋め込まれます。 URL にセッション ID を埋め込みたくない場合は、noEncodeURL()
89 |  |
      * を使用してください。
90 |  |
      *
91 |  |
      * <pre>
92 |  |
      * return new Redirect(&quot;/todo/list&quot;).noEnocdeURL();
93 |  |
      * </pre>
94 |  |
      *
95 |  |
      * </p>
96 |  |
      *
97 |  |
      * @author baba
98 |  |
      */
99 |  |
     public class Redirect implements ActionResult {
100 |  |
101 |  |
             /** ロガー。 */
102 | 1 |
             private static final Logger logger = LoggerFactory
103 |  |
                             .getLogger(Redirect.class);
104 |  |
105 |  |
             /** 空のパラメータ。 */
106 | 1 |
             private static final Map<String, String[]> EMPTY_PARAMETERS = Collections
107 |  |
                             .emptyMap();
108 |  |
109 |  |
             /** {@link PathResolver} のプロバイダ。 */
110 |  |
             private final PathResolverProvider pathResolverProvider;
111 |  |
112 |  |
             /** リンクビルダ。 */
113 | 23 |
             private final LinkBuilder linkBuilder = new LinkBuilder();
114 |  |
115 |  |
             /** リダイレクト先のパス。 */
116 |  |
             private String path;
117 |  |
118 |  |
             /** リダイレクト先のアクションクラス。 */
119 |  |
             private Class<?> actionClass;
120 |  |
121 |  |
             /** リダイレクト先のアクションクラスのメソッド名。 */
122 |  |
             private String methodName;
123 |  |
124 |  |
             /** リダイレクト時のパラメータ。 */
125 |  |
             private Map<String, String[]> parameters;
126 |  |
127 |  |
             /** URI のエンコーディング。 */
128 |  |
             private String characterEncoding;
129 |  |
130 |  |
             /** URL をエンコードするか。 */
131 | 23 |
             private boolean encodeURL = true;
132 |  |
133 |  |
             /**
134 |  |
              * インスタンスを生成します。
135 |  |
              *
136 |  |
              * @param path
137 |  |
              *            リダイレクト先のパス
138 |  |
              */
139 | 12 |
             public Redirect(final String path) {
140 | 12 |
                     this.pathResolverProvider = null;
141 | 12 |
                     this.path = path;
142 | 12 |
             }
143 |  |
144 |  |
             /**
145 |  |
              * インスタンスを生成します。
146 |  |
              *
147 |  |
              * @param path
148 |  |
              *            リダイレクト先のパス
149 |  |
              * @param protocol
150 |  |
              *            リダイレクト先のプロトコル
151 |  |
              */
152 |  |
             public Redirect(final String path, final String protocol) {
153 | 3 |
                     this(path);
154 | 3 |
                     linkBuilder.setProtocol(protocol);
155 | 3 |
             }
156 |  |
157 |  |
             /**
158 |  |
              * インスタンスを生成します。
159 |  |
              *
160 |  |
              * @param path
161 |  |
              *            リダイレクト先のパス
162 |  |
              * @param protocol
163 |  |
              *            リダイレクト先のプロトコル
164 |  |
              * @param port
165 |  |
              *            リダイレクト先のポート
166 |  |
              */
167 |  |
             public Redirect(final String path, final String protocol, final int port) {
168 | 1 |
                     this(path);
169 | 1 |
                     linkBuilder.setProtocol(protocol);
170 | 1 |
                     linkBuilder.setPort(port);
171 | 1 |
             }
172 |  |
173 |  |
             /**
174 |  |
              * 指定されたアクションクラスのindexメソッドへリダイレクトするインスタンスを生成します。
175 |  |
              *
176 |  |
              * @param actionClass
177 |  |
              *            アクションクラス
178 |  |
              */
179 |  |
             public Redirect(final Class<?> actionClass) {
180 | 1 |
                     this(actionClass, "index");
181 | 1 |
             }
182 |  |
183 |  |
             /**
184 |  |
              * 指定されたアクションメソッドへリダイレクトするインスタンスを生成します。
185 |  |
              *
186 |  |
              * @param actionClass
187 |  |
              *            アクションクラス
188 |  |
              * @param methodName
189 |  |
              *            メソッド名
190 |  |
              */
191 |  |
             public Redirect(final Class<?> actionClass, final String methodName) {
192 | 6 |
                     this(actionClass, methodName, EMPTY_PARAMETERS);
193 | 6 |
             }
194 |  |
195 |  |
             /**
196 |  |
              * 指定されたアクションメソッドへリダイレクトするインスタンスを生成します。
197 |  |
              *
198 |  |
              * @param actionClass
199 |  |
              *            アクションクラス
200 |  |
              * @param methodName
201 |  |
              *            メソッド名
202 |  |
              * @param parameters
203 |  |
              *            パラメータ
204 |  |
              */
205 |  |
             public Redirect(final Class<?> actionClass, final String methodName,
206 | 11 |
                             final Map<String, String[]> parameters) {
207 | 11 |
                     this.pathResolverProvider = ProviderFactory
208 |  |
                                     .get(PathResolverProvider.class);
209 | 11 |
                     this.actionClass = actionClass;
210 | 11 |
                     this.methodName = methodName;
211 | 11 |
                     this.parameters = parameters;
212 | 11 |
             }
213 |  |
214 |  |
             /**
215 |  |
              * 指定されたアクションメソッドへリダイレクトするインスタンスを生成します。
216 |  |
              *
217 |  |
              * @param actionClass
218 |  |
              *            アクションクラス
219 |  |
              * @param methodName
220 |  |
              *            メソッド名
221 |  |
              * @param parameters
222 |  |
              *            パラメータ
223 |  |
              * @param protocol
224 |  |
              *            リダイレクト先のプロトコル
225 |  |
              */
226 |  |
             public Redirect(final Class<?> actionClass, final String methodName,
227 |  |
                             final Map<String, String[]> parameters, final String protocol) {
228 | 0 |
                     this(actionClass, methodName, parameters);
229 | 0 |
                     linkBuilder.setProtocol(protocol);
230 | 0 |
             }
231 |  |
232 |  |
             /**
233 |  |
              * 指定されたアクションメソッドへリダイレクトするインスタンスを生成します。
234 |  |
              *
235 |  |
              * @param actionClass
236 |  |
              *            アクションクラス
237 |  |
              * @param methodName
238 |  |
              *            メソッド名
239 |  |
              * @param parameters
240 |  |
              *            パラメータ
241 |  |
              * @param protocol
242 |  |
              *            リダイレクト先のプロトコル
243 |  |
              * @param port
244 |  |
              *            リダイレクト先のポート
245 |  |
              */
246 |  |
             public Redirect(final Class<?> actionClass, final String methodName,
247 |  |
                             final Map<String, String[]> parameters, final String protocol,
248 |  |
                             final int port) {
249 | 0 |
                     this(actionClass, methodName, parameters);
250 | 0 |
                     linkBuilder.setProtocol(protocol);
251 | 0 |
                     linkBuilder.setPort(port);
252 | 0 |
             }
253 |  |
254 |  |
             /**
255 |  |
              * パスを取得します。
256 |  |
              *
257 |  |
              * @param characterEncoding
258 |  |
              *            URI のエンコーディング
259 |  |
              * @return パス
260 |  |
              */
261 |  |
             public String getPath(final String characterEncoding) {
262 | 23 |
                     if (isReverseLookupRedirect()) {
263 | 11 |
                             final PathResolver pathResolver = this.pathResolverProvider
264 |  |
                                             .getPathResolver();
265 | 11 |
                             final String redirectPath = pathResolver.reverseLookup(actionClass,
266 |  |
                                             methodName, parameters, characterEncoding);
267 | 8 |
                             this.path = redirectPath;
268 |  |
                     }
269 | 20 |
                     return this.path;
270 |  |
             }
271 |  |
272 |  |
             /**
273 |  |
              * アクションクラスを指定したリダイレクトかどうかを判定します。
274 |  |
              *
275 |  |
              * @return アクションクラスを指定したリダイレクトならtrue
276 |  |
              */
277 |  |
             private boolean isReverseLookupRedirect() {
278 | 28 |
                     return this.actionClass != null && this.methodName != null
279 |  |
                                     && this.parameters != null;
280 |  |
             }
281 |  |
282 |  |
             /**
283 |  |
              * {@inheritDoc}
284 |  |
              */
285 |  |
             public void execute(final ActionContext actionContext,
286 |  |
                             final HttpServletRequest request, final HttpServletResponse response)
287 |  |
                             throws Exception {
288 |  |
                     final String characterEncoding;
289 | 9 |
                     if (this.characterEncoding == null) {
290 | 9 |
                             characterEncoding = request.getCharacterEncoding();
291 |  |
                     } else {
292 | 0 |
                             characterEncoding = this.characterEncoding;
293 |  |
                     }
294 | 9 |
                     final String redirectURL = calculateRedirectURL(
295 |  |
                                     getPath(characterEncoding), actionContext.getActionClass(),
296 |  |
                                     request);
297 | 9 |
                     final String encodedRedirectURL = encodeURL(redirectURL, response);
298 | 9 |
                     if (logger.isDebugEnabled()) {
299 | 9 |
                             logger.debug(format("DCUB0003", encodedRedirectURL));
300 |  |
                     }
301 | 9 |
                     response.sendRedirect(encodedRedirectURL);
302 | 9 |
             }
303 |  |
304 |  |
             /**
305 |  |
              * リダイレクトする URL を計算します。
306 |  |
              *
307 |  |
              * @param path
308 |  |
              *            パス
309 |  |
              * @param actionClass
310 |  |
              *            アクションクラス
311 |  |
              * @param request
312 |  |
              *            要求
313 |  |
              * @return URL
314 |  |
              */
315 |  |
             protected String calculateRedirectURL(final String path,
316 |  |
                             final Class<?> actionClass, final HttpServletRequest request) {
317 |  |
                     try {
318 | 9 |
                             final String redirectURL = new URL(path).toExternalForm();
319 | 1 |
                             return redirectURL;
320 | 8 |
                     } catch (final MalformedURLException e) {
321 | 8 |
                             final String redirectURL = calculateInternalRedirectURL(path,
322 |  |
                                             actionClass, request);
323 | 8 |
                             return redirectURL;
324 |  |
                     }
325 |  |
             }
326 |  |
327 |  |
             /**
328 |  |
              * リダイレクトする URL を計算します。
329 |  |
              *
330 |  |
              * @param path
331 |  |
              *            パス
332 |  |
              * @param actionClass
333 |  |
              *            アクションクラス
334 |  |
              * @param request
335 |  |
              *            要求
336 |  |
              * @return URL
337 |  |
              */
338 |  |
             private String calculateInternalRedirectURL(final String path,
339 |  |
                             final Class<?> actionClass, final HttpServletRequest request) {
340 |  |
                     final String redirectPath;
341 |  |
                     final String contextPath;
342 | 8 |
                     if ("/".equals(request.getContextPath())) {
343 | 1 |
                             contextPath = "";
344 |  |
                     } else {
345 | 7 |
                             contextPath = request.getContextPath();
346 |  |
                     }
347 | 8 |
                     if (path.startsWith("/")) {
348 | 2 |
                             redirectPath = contextPath + path;
349 |  |
                     } else {
350 | 6 |
                             final String actionDirectory = MetaUtils
351 |  |
                                             .getActionDirectory(actionClass);
352 | 6 |
                             if (StringUtils.isEmpty(actionDirectory)) {
353 | 0 |
                                     final StringBuilder builder = new StringBuilder();
354 | 0 |
                                     builder.append(contextPath);
355 | 0 |
                                     if (!contextPath.endsWith("/")) {
356 | 0 |
                                             builder.append("/");
357 |  |
                                     }
358 | 0 |
                                     builder.append(path);
359 | 0 |
                                     redirectPath = builder.toString();
360 | 0 |
                             } else {
361 | 6 |
                                     final StringBuilder builder = new StringBuilder();
362 | 6 |
                                     builder.append(contextPath);
363 | 6 |
                                     if (!contextPath.endsWith("/")
364 |  |
                                                     && !actionDirectory.startsWith("/")) {
365 | 6 |
                                             builder.append("/");
366 |  |
                                     }
367 | 6 |
                                     builder.append(actionDirectory);
368 | 6 |
                                     if (!actionDirectory.endsWith("/")) {
369 | 6 |
                                             builder.append("/");
370 |  |
                                     }
371 | 6 |
                                     builder.append(path);
372 | 6 |
                                     redirectPath = builder.toString();
373 |  |
                             }
374 |  |
                     }
375 |  |
376 |  |
                     try {
377 | 8 |
                             return linkBuilder.file(redirectPath).toLink(request);
378 | 0 |
                     } catch (final MalformedURLException e) {
379 | 0 |
                             throw new ActionException(e);
380 |  |
                     }
381 |  |
             }
382 |  |
383 |  |
             /**
384 |  |
              * URL をエンコードします。
385 |  |
              *
386 |  |
              * @param url
387 |  |
              *            URL
388 |  |
              * @param response
389 |  |
              *            応答
390 |  |
              * @return エンコードされた URL
391 |  |
              * @see HttpServletResponse#encodeRedirectURL(String)
392 |  |
              */
393 |  |
             protected String encodeURL(final String url,
394 |  |
                             final HttpServletResponse response) {
395 | 9 |
                     if (encodeURL) {
396 | 9 |
                             return response.encodeRedirectURL(url);
397 |  |
                     } else {
398 | 0 |
                             return url;
399 |  |
                     }
400 |  |
             }
401 |  |
402 |  |
             /**
403 |  |
              * {@link HttpServletResponse#encodeRedirectURL(String)}
404 |  |
              * によってエンコードせずにリダイレクトします。
405 |  |
              * <p>
406 |  |
              * URL 埋め込みのセッション ID を出力したくない場合に使用してください。
407 |  |
              * </p>
408 |  |
              *
409 |  |
              * @return このオブジェクト
410 |  |
              */
411 |  |
             public Redirect noEncodeURL() {
412 | 0 |
                     this.encodeURL = false;
413 | 0 |
                     return this;
414 |  |
             }
415 |  |
416 |  |
             /**
417 |  |
              * パラメータを追加します。
418 |  |
              *
419 |  |
              * @param paramName
420 |  |
              *            パラメータ名
421 |  |
              * @param paramValue
422 |  |
              *            パラメータの値。{@code Object#toString()}の結果が値として使用されます。
423 |  |
              * @return このオブジェクト
424 |  |
              */
425 |  |
             public Redirect param(final String paramName, final Object paramValue) {
426 | 5 |
                     return param(paramName, new String[] { paramValue.toString() });
427 |  |
             }
428 |  |
429 |  |
             /**
430 |  |
              * パラメータを追加します。
431 |  |
              *
432 |  |
              * @param paramName
433 |  |
              *            パラメータ名
434 |  |
              * @param paramValues
435 |  |
              *            パラメータの値の配列。配列の要素の{@code Object#toString()}の結果がそれぞれの値として使用されます。
436 |  |
              * @return このオブジェクト
437 |  |
              */
438 |  |
             public Redirect param(final String paramName, final Object[] paramValues) {
439 | 0 |
                     return param(paramName, toStringArray(paramValues));
440 |  |
             }
441 |  |
442 |  |
             /**
443 |  |
              * パラメータを追加します。
444 |  |
              *
445 |  |
              * @param paramName
446 |  |
              *            パラメータ名
447 |  |
              * @param paramValues
448 |  |
              *            パラメータの値
449 |  |
              * @return このオブジェクト
450 |  |
              */
451 |  |
             public Redirect param(final String paramName, final String[] paramValues) {
452 | 5 |
                     if (isReverseLookupRedirect()) {
453 | 3 |
                             if (this.parameters == EMPTY_PARAMETERS) {
454 | 1 |
                                     this.parameters = new HashMap<String, String[]>();
455 |  |
                             }
456 | 3 |
                             this.parameters.put(paramName, paramValues);
457 |  |
                     } else {
458 | 2 |
                             final QueryStringBuilder builder = new QueryStringBuilder(this.path);
459 | 2 |
                             builder.addParam(paramName, paramValues);
460 | 2 |
                             this.path = builder.toString();
461 |  |
                     }
462 | 5 |
                     return this;
463 |  |
             }
464 |  |
465 |  |
             /**
466 |  |
              * {@code Object#toString()}型の配列を{@code Object#toString()}型の配列に変換します。
467 |  |
              * <p>
468 |  |
              * 配列のそれぞれの要素に対して{@code Object#toString()}を使用して変換します。
469 |  |
              * </p>
470 |  |
              *
471 |  |
              * @param paramValues
472 |  |
              *            {@code Object#toString()}型の配列
473 |  |
              * @return {@code Object#toString()}型の配列。
474 |  |
              */
475 |  |
             private String[] toStringArray(final Object[] paramValues) {
476 | 0 |
                     final String[] values = new String[paramValues.length];
477 | 0 |
                     for (int i = 0; i < paramValues.length; i++) {
478 | 0 |
                             values[i] = paramValues[i].toString();
479 |  |
                     }
480 | 0 |
                     return values;
481 |  |
             }
482 |  |
483 |  |
             /**
484 |  |
              * URI のエンコーディングを指定します。
485 |  |
              *
486 |  |
              * @param characterEncoding
487 |  |
              *            URI のエンコーディング
488 |  |
              * @return このオブジェクト
489 |  |
              */
490 |  |
             public Redirect characterEncoding(final String characterEncoding) {
491 | 0 |
                     this.characterEncoding = characterEncoding;
492 | 0 |
                     return this;
493 |  |
             }
494 |  |
495 |  |
             /**
496 |  |
              * パスを取得します。
497 |  |
              *
498 |  |
              * @return パス
499 |  |
              * @deprecated use {@link #getPath(String)}
500 |  |
              */
501 |  |
             @Deprecated
502 |  |
             public String getPath() {
503 | 0 |
                     return getPath("UTF-8");
504 |  |
             }
505 |  |
506 |  |
     }