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.internal.controller.impl;
18  
19  import static org.seasar.cubby.CubbyConstants.ATTR_ACTION;
20  import static org.seasar.cubby.CubbyConstants.ATTR_ACTION_CONTEXT;
21  import static org.seasar.cubby.CubbyConstants.ATTR_CONVERSION_FAILURES;
22  import static org.seasar.cubby.CubbyConstants.ATTR_ERRORS;
23  import static org.seasar.cubby.CubbyConstants.ATTR_FLASH;
24  import static org.seasar.cubby.CubbyConstants.ATTR_PARAMS;
25  import static org.seasar.cubby.CubbyConstants.ATTR_VALIDATION_FAIL;
26  import static org.seasar.cubby.CubbyConstants.ATTR_WRAPEE_REQUEST;
27  import static org.seasar.cubby.internal.util.LogMessages.format;
28  import static org.seasar.cubby.validator.ValidationUtils.getValidation;
29  import static org.seasar.cubby.validator.ValidationUtils.getValidationRules;
30  
31  import java.lang.reflect.InvocationTargetException;
32  import java.lang.reflect.Method;
33  import java.util.Iterator;
34  import java.util.List;
35  import java.util.Map;
36  
37  import javax.servlet.ServletRequest;
38  import javax.servlet.http.HttpServletRequest;
39  import javax.servlet.http.HttpServletResponse;
40  
41  import org.seasar.cubby.action.ActionContext;
42  import org.seasar.cubby.action.ActionErrors;
43  import org.seasar.cubby.action.ActionException;
44  import org.seasar.cubby.action.ActionResult;
45  import org.seasar.cubby.action.Validation;
46  import org.seasar.cubby.internal.controller.ActionProcessor;
47  import org.seasar.cubby.internal.controller.ActionResultWrapper;
48  import org.seasar.cubby.internal.controller.ConversionFailure;
49  import org.seasar.cubby.internal.controller.RequestParameterBinder;
50  import org.seasar.cubby.internal.util.RequestUtils;
51  import org.seasar.cubby.plugin.ActionInvocation;
52  import org.seasar.cubby.plugin.Plugin;
53  import org.seasar.cubby.plugin.PluginRegistry;
54  import org.seasar.cubby.plugin.ValidationInvocation;
55  import org.seasar.cubby.routing.Routing;
56  import org.seasar.cubby.spi.ContainerProvider;
57  import org.seasar.cubby.spi.ProviderFactory;
58  import org.seasar.cubby.spi.container.Container;
59  import org.seasar.cubby.validator.ValidationException;
60  import org.seasar.cubby.validator.ValidationFailBehaviour;
61  import org.seasar.cubby.validator.ValidationRules;
62  import org.slf4j.Logger;
63  import org.slf4j.LoggerFactory;
64  
65  /**
66   * 要求のパスを元にアクションメソッドを決定して実行するクラスの実装です。
67   * 
68   * @author baba
69   */
70  public class ActionProcessorImpl implements ActionProcessor {
71  
72  	/** ロガー。 */
73  	private static final Logger logger = LoggerFactory
74  			.getLogger(ActionProcessorImpl.class);
75  
76  	/**
77  	 * {@inheritDoc}
78  	 */
79  	public ActionResultWrapper process(final HttpServletRequest request,
80  			final HttpServletResponse response, final Routing routing)
81  			throws Exception {
82  
83  		final Method actionMethod = routing.getActionMethod();
84  		if (logger.isDebugEnabled()) {
85  			logger.debug(format("DCUB0004", request.getRequestURI()));
86  			logger.debug(format("DCUB0005", actionMethod));
87  		}
88  
89  		final Class<?> actionClass = routing.getActionClass();
90  
91  		final Container container = ProviderFactory
92  				.get(ContainerProvider.class).getContainer();
93  
94  		final Object action = container.lookup(actionClass);
95  		request.setAttribute(ATTR_ACTION, action);
96  
97  		final HttpServletRequest wrapeeRequest = (HttpServletRequest) request
98  				.getAttribute(ATTR_WRAPEE_REQUEST);
99  		final ActionErrors actionErrors = setupActionErrors(wrapeeRequest);
100 		final Map<String, Object> flashMap = setupFlashMap(wrapeeRequest);
101 		final ActionContext actionContext = new ActionContextImpl(request,
102 				action, actionClass, actionMethod, actionErrors, flashMap);
103 		request.setAttribute(ATTR_ACTION_CONTEXT, actionContext);
104 
105 		actionContext.invokeInitializeMethod();
106 
107 		final ActionInvocation actionInvocation = new ActionInvocationImpl(
108 				request, response, actionContext);
109 		final ActionResult actionResult = actionInvocation.proceed();
110 		if (actionResult == null) {
111 			throw new ActionException(format("ECUB0101", actionMethod));
112 		}
113 
114 		final ActionResultWrapper actionResultWrapper = new ActionResultWrapperImpl(
115 				actionResult, actionContext);
116 		return actionResultWrapper;
117 	}
118 
119 	private ActionErrors setupActionErrors(final ServletRequest request) {
120 		final ActionErrors actionErrors = (ActionErrors) request
121 				.getAttribute(ATTR_ERRORS);
122 		if (actionErrors != null) {
123 			return actionErrors;
124 		}
125 
126 		final ActionErrors newActionErrors = new ActionErrorsImpl();
127 		request.setAttribute(ATTR_ERRORS, newActionErrors);
128 		return newActionErrors;
129 	}
130 
131 	private Map<String, Object> setupFlashMap(final ServletRequest request) {
132 		@SuppressWarnings("unchecked")
133 		final Map<String, Object> flashMap = (Map<String, Object>) request
134 				.getAttribute(ATTR_FLASH);
135 		if (flashMap != null) {
136 			return flashMap;
137 		}
138 
139 		final Map<String, Object> newFlashMap = new FlashMapImpl(
140 				(HttpServletRequest) request);
141 		request.setAttribute(ATTR_FLASH, newFlashMap);
142 		return newFlashMap;
143 	}
144 
145 	/**
146 	 * アクションの実行情報の実装です。
147 	 * 
148 	 * @author baba
149 	 */
150 	static class ActionInvocationImpl implements ActionInvocation {
151 
152 		/** 要求。 */
153 		private final HttpServletRequest request;
154 
155 		/** 応答。 */
156 		private final HttpServletResponse response;
157 
158 		/** アクションのコンテキスト。 */
159 		private final ActionContext actionContext;
160 
161 		/** プラグインのイテレータ。 */
162 		private final Iterator<Plugin> pluginsIterator;
163 
164 		/**
165 		 * インスタンス化します。
166 		 * 
167 		 * @param request
168 		 *            要求
169 		 * @param response
170 		 *            応答
171 		 * @param actionContext
172 		 *            アクションのコンテキスト
173 		 */
174 		public ActionInvocationImpl(final HttpServletRequest request,
175 				final HttpServletResponse response,
176 				final ActionContext actionContext) {
177 			this.request = request;
178 			this.response = response;
179 			this.actionContext = actionContext;
180 
181 			final PluginRegistry pluginRegistry = PluginRegistry.getInstance();
182 			this.pluginsIterator = pluginRegistry.getPlugins().iterator();
183 		}
184 
185 		/**
186 		 * {@inheritDoc}
187 		 */
188 		public ActionResult proceed() throws Exception {
189 			final ActionResult actionResult;
190 			if (pluginsIterator.hasNext()) {
191 				final Plugin plugin = pluginsIterator.next();
192 				actionResult = plugin.invokeAction(this);
193 			} else {
194 				final ValidationInvocation validationInvocation = new ValidationInvocationImpl(
195 						request, response, actionContext);
196 				return validationInvocation.proceed();
197 			}
198 			return actionResult;
199 		}
200 
201 		/**
202 		 * {@inheritDoc}
203 		 */
204 		public HttpServletRequest getRequest() {
205 			return request;
206 		}
207 
208 		/**
209 		 * {@inheritDoc}
210 		 */
211 		public HttpServletResponse getResponse() {
212 			return response;
213 		}
214 
215 		/**
216 		 * {@inheritDoc}
217 		 */
218 		public ActionContext getActionContext() {
219 			return actionContext;
220 		}
221 
222 	}
223 
224 	/**
225 	 * 入力検証の実行情報の実装です。
226 	 * 
227 	 * @author baba
228 	 */
229 	static class ValidationInvocationImpl implements ValidationInvocation {
230 
231 		/** 要求パラメータをオブジェクトへバインドするクラス。 */
232 		private final RequestParameterBinder requestParameterBinder = new RequestParameterBinderImpl();
233 
234 		/** 要求。 */
235 		private final HttpServletRequest request;
236 
237 		/** 応答。 */
238 		private final HttpServletResponse response;
239 
240 		/** アクションのコンテキスト。 */
241 		private final ActionContext actionContext;
242 
243 		/** プラグインのイテレータ。 */
244 		private final Iterator<Plugin> pluginsIterator;
245 
246 		/**
247 		 * インスタンス化します。
248 		 * 
249 		 * @param request
250 		 *            要求
251 		 * @param response
252 		 *            応答
253 		 * @param actionContext
254 		 *            アクションのコンテキスト
255 		 */
256 		public ValidationInvocationImpl(final HttpServletRequest request,
257 				final HttpServletResponse response,
258 				final ActionContext actionContext) {
259 			this.request = request;
260 			this.response = response;
261 			this.actionContext = actionContext;
262 
263 			final PluginRegistry pluginRegistry = PluginRegistry.getInstance();
264 			this.pluginsIterator = pluginRegistry.getPlugins().iterator();
265 		}
266 
267 		/**
268 		 * {@inheritDoc}
269 		 */
270 		public ActionResult proceed() throws Exception {
271 			final ActionResult actionResult;
272 			if (pluginsIterator.hasNext()) {
273 				final Plugin plugin = pluginsIterator.next();
274 				actionResult = plugin.invokeValidation(this);
275 			} else {
276 				final Map<String, Object[]> parameterMap = RequestUtils
277 						.getAttribute(request, ATTR_PARAMS);
278 				final Object formBean = actionContext.getFormBean();
279 
280 				if (formBean != null) {
281 					final List<ConversionFailure> conversionFailures = requestParameterBinder
282 							.bind(parameterMap, formBean, actionContext);
283 					request.setAttribute(ATTR_CONVERSION_FAILURES,
284 							conversionFailures);
285 				}
286 
287 				try {
288 					final Validation validation = getValidation(actionContext
289 							.getActionMethod());
290 					if (validation != null) {
291 						final ValidationRules validationRules = getValidationRules(
292 								actionContext.getAction(), validation.rules());
293 						validationRules.validate(parameterMap, formBean,
294 								actionContext.getActionErrors());
295 					}
296 					try {
297 						final Object action = actionContext.getAction();
298 						final Method actionMethod = actionContext
299 								.getActionMethod();
300 						actionResult = (ActionResult) actionMethod
301 								.invoke(action);
302 					} catch (final InvocationTargetException e) {
303 						final Throwable cause = e.getCause();
304 						if (cause instanceof ValidationException) {
305 							throw (ValidationException) cause;
306 						}
307 						throw e;
308 					}
309 				} catch (final ValidationException e) {
310 					request.setAttribute(ATTR_VALIDATION_FAIL, Boolean.TRUE);
311 					final ValidationFailBehaviour behaviour = e.getBehaviour();
312 					return behaviour
313 							.getValidationErrorActionResult(actionContext);
314 				}
315 			}
316 			return actionResult;
317 		}
318 
319 		/**
320 		 * {@inheritDoc}
321 		 */
322 		public HttpServletRequest getRequest() {
323 			return request;
324 		}
325 
326 		/**
327 		 * {@inheritDoc}
328 		 */
329 		public HttpServletResponse getResponse() {
330 			return response;
331 		}
332 
333 		/**
334 		 * {@inheritDoc}
335 		 */
336 		public ActionContext getActionContext() {
337 			return actionContext;
338 		}
339 
340 	}
341 
342 }