GRAILS-1019: Allowing expressions to be used with the 'disabled' attribute for g...
[grails.git] / src / web / org / codehaus / groovy / grails / web / servlet / GrailsDispatcherServlet.java
blob14431f078272eb8ef6bb3e75d30af6cdce922c97
1 /*
2 * Copyright 2004-2005 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.
16 package org.codehaus.groovy.grails.web.servlet;
18 import grails.util.GrailsUtil;
19 import org.codehaus.groovy.grails.commons.*;
20 import org.codehaus.groovy.grails.commons.spring.GrailsApplicationContext;
21 import org.codehaus.groovy.grails.web.context.GrailsConfigUtils;
22 import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest;
23 import org.codehaus.groovy.grails.web.servlet.mvc.SimpleGrailsController;
24 import org.codehaus.groovy.grails.web.util.WebUtils;
25 import org.springframework.beans.BeansException;
26 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
27 import org.springframework.beans.factory.access.BootstrapException;
28 import org.springframework.context.i18n.LocaleContext;
29 import org.springframework.context.i18n.LocaleContextHolder;
30 import org.springframework.util.Assert;
31 import org.springframework.web.context.WebApplicationContext;
32 import org.springframework.web.context.request.RequestContextHolder;
33 import org.springframework.web.context.request.WebRequestInterceptor;
34 import org.springframework.web.context.support.WebApplicationContextUtils;
35 import org.springframework.web.multipart.MultipartException;
36 import org.springframework.web.multipart.MultipartHttpServletRequest;
37 import org.springframework.web.multipart.MultipartResolver;
38 import org.springframework.web.servlet.*;
39 import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter;
40 import org.springframework.web.util.NestedServletException;
41 import org.springframework.web.util.UrlPathHelper;
43 import javax.servlet.ServletException;
44 import javax.servlet.http.HttpServletRequest;
45 import javax.servlet.http.HttpServletResponse;
46 import java.util.Iterator;
47 import java.util.Locale;
48 import java.util.Map;
50 /**
51 * <p>Servlet that handles incoming requests for Grails.
52 * <p/>
53 * <p>This servlet loads the Spring configuration based on the Grails application
54 * in the parent application context.
56 * @author Steven Devijver
57 * @author Graeme Rocher
59 * @since Jul 2, 2005
61 public class GrailsDispatcherServlet extends DispatcherServlet {
62 private GrailsApplication application;
63 private UrlPathHelper urlHelper = new GrailsUrlPathHelper();
64 private SimpleGrailsController grailsController;
65 protected HandlerInterceptor[] interceptors;
66 protected MultipartResolver multipartResolver;
67 private static final String EXCEPTION_ATTRIBUTE = "exception";
69 public GrailsDispatcherServlet() {
70 super();
71 setDetectAllHandlerMappings(false);
75 protected void initFrameworkServlet() throws ServletException, BeansException {
76 super.initFrameworkServlet();
77 initMultipartResolver();
81 /**
82 * Initialize the MultipartResolver used by this class.
83 * If no bean is defined with the given name in the BeanFactory
84 * for this namespace, no multipart handling is provided.
86 private void initMultipartResolver() throws BeansException {
87 try {
88 this.multipartResolver = (MultipartResolver)
89 getWebApplicationContext().getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
90 if (logger.isInfoEnabled()) {
91 logger.info("Using MultipartResolver [" + this.multipartResolver + "]");
94 catch (NoSuchBeanDefinitionException ex) {
95 // Default is no multipart resolver.
96 this.multipartResolver = null;
97 if (logger.isInfoEnabled()) {
98 logger.info("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +
99 "': no multipart request handling provided");
104 protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) throws BeansException {
105 WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
106 WebApplicationContext webContext;
107 // construct the SpringConfig for the container managed application
108 Assert.notNull(parent, "Grails requires a parent ApplicationContext, is the /WEB-INF/applicationContext.xml file missing?");
109 this.application = (GrailsApplication) parent.getBean(GrailsApplication.APPLICATION_ID, GrailsApplication.class);
112 if(wac instanceof GrailsApplicationContext) {
113 webContext = wac;
115 else {
116 webContext = GrailsConfigUtils.configureWebApplicationContext(getServletContext(), parent);
117 try {
118 GrailsConfigUtils.executeGrailsBootstraps(application, webContext, getServletContext());
119 } catch (Exception e) {
120 GrailsUtil.deepSanitize(e);
121 if(e instanceof BeansException) throw (BeansException)e;
122 else {
123 throw new BootstrapException("Error executing bootstraps", e);
128 initGrailsController(webContext);
129 this.interceptors = establishInterceptors(webContext);
131 return webContext;
134 private void initGrailsController(WebApplicationContext webContext) {
135 if(webContext.containsBean(SimpleGrailsController.APPLICATION_CONTEXT_ID)) {
136 this.grailsController = (SimpleGrailsController)webContext.getBean(SimpleGrailsController.APPLICATION_CONTEXT_ID);
143 * Evalutes the given WebApplicationContext for all HandlerInterceptor and WebRequestInterceptor instances
145 * @param webContext The WebApplicationContext
146 * @return An array of HandlerInterceptor instances
148 protected HandlerInterceptor[] establishInterceptors(WebApplicationContext webContext) {
149 HandlerInterceptor[] interceptors;
150 String[] interceptorNames = webContext.getBeanNamesForType(HandlerInterceptor.class);
151 String[] webRequestInterceptors = webContext.getBeanNamesForType( WebRequestInterceptor.class);
152 interceptors = new HandlerInterceptor[interceptorNames.length+webRequestInterceptors.length];
154 // Merge the handler and web request interceptors into a single
155 // array. Note that we start with the web request interceptors
156 // to ensure that the OpenSessionInViewInterceptor (which is a
157 // web request interceptor) is invoked before the user-defined
158 // filters (which are attached to a handler interceptor). This
159 // should ensure that the Hibernate session is in the proper
160 // state if and when users access the database within their
161 // filters.
162 int j = 0;
163 for (int i = 0; i < webRequestInterceptors.length; i++) {
164 interceptors[j++] = new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor)webContext.getBean(webRequestInterceptors[i]));
166 for (int i = 0; i < interceptorNames.length; i++) {
167 interceptors[j++] = (HandlerInterceptor)webContext.getBean(interceptorNames[i]);
169 return interceptors;
172 public void destroy() {
173 WebApplicationContext webContext = getWebApplicationContext();
174 GrailsApplication application = (GrailsApplication) webContext.getBean(GrailsApplication.APPLICATION_ID, GrailsApplication.class);
176 GrailsClass[] bootstraps = application.getArtefacts(BootstrapArtefactHandler.TYPE);
177 for (int i = 0; i < bootstraps.length; i++) {
178 ((GrailsBootstrapClass)bootstraps[i]).callDestroy();
180 super.destroy();
184 public void setApplication(GrailsApplication application) {
185 this.application = application;
188 /* (non-Javadoc)
189 * @see org.springframework.web.servlet.DispatcherServlet#doDispatch(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
191 protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {
192 HttpServletRequest processedRequest = request;
193 HandlerExecutionChain mappedHandler = null;
194 int interceptorIndex = -1;
195 final LocaleResolver localeResolver = (LocaleResolver)request.getAttribute(LOCALE_RESOLVER_ATTRIBUTE);
198 // Expose current LocaleResolver and request as LocaleContext.
199 LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
200 LocaleContextHolder.setLocaleContext(new LocaleContext() {
201 public Locale getLocale() {
203 return localeResolver.resolveLocale(request);
208 // If the request is an include we need to try to use the original wrapped sitemesh
209 // response, otherwise layouts won't work properly
210 if(WebUtils.isIncludeRequest(request)) {
211 response = useWrappedOrOriginalResponse(response);
214 GrailsWebRequest requestAttributes = null;
215 GrailsWebRequest previousRequestAttributes = null;
216 try {
217 ModelAndView mv = null;
218 try {
219 Object exceptionAttribute = request.getAttribute(EXCEPTION_ATTRIBUTE);
220 // only process multipart requests if an exception hasn't occured
221 if(exceptionAttribute == null)
222 processedRequest = checkMultipart(request);
223 // Expose current RequestAttributes to current thread.
224 previousRequestAttributes = (GrailsWebRequest) RequestContextHolder.currentRequestAttributes();
225 requestAttributes = new GrailsWebRequest(processedRequest, response, getServletContext());
226 copyParamsFromPreviousRequest(previousRequestAttributes, requestAttributes);
228 // Update the current web request.
229 WebUtils.storeGrailsWebRequest(requestAttributes);
231 if (logger.isDebugEnabled()) {
232 logger.debug("Bound request context to thread: " + request);
233 logger.debug("Using response object: " + response.getClass());
239 // Determine handler for the current request.
240 mappedHandler = getHandler(processedRequest, false);
241 if (mappedHandler == null || mappedHandler.getHandler() == null) {
242 noHandlerFound(processedRequest, response);
243 return;
246 // Apply preHandle methods of registered interceptors.
247 if (mappedHandler.getInterceptors() != null) {
248 for (int i = 0; i < mappedHandler.getInterceptors().length; i++) {
249 HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
250 if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
251 triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
252 return;
254 interceptorIndex = i;
258 // Actually invoke the handler.
259 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
260 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
262 // Apply postHandle methods of registered interceptors.
263 if (mappedHandler.getInterceptors() != null) {
264 for (int i = mappedHandler.getInterceptors().length - 1; i >= 0; i--) {
265 HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
266 interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
270 catch (ModelAndViewDefiningException ex) {
271 GrailsUtil.deepSanitize(ex);
272 if (logger.isDebugEnabled())
273 logger.debug("ModelAndViewDefiningException encountered", ex);
274 mv = ex.getModelAndView();
276 catch (Exception ex) {
277 GrailsUtil.deepSanitize(ex);
278 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
279 mv = processHandlerException(request, response, handler, ex);
282 // Did the handler return a view to render?
283 if (mv != null && !mv.wasCleared()) {
284 // If an exception occurs in here, like a bad closing tag,
285 // we have nothing to render.
287 try {
288 render(mv, processedRequest, response);
289 } catch (Exception e) {
290 mv = super.processHandlerException(processedRequest, response, mappedHandler, e);
291 render(mv, processedRequest, response);
294 else {
295 if (logger.isDebugEnabled()) {
296 logger.debug("Null ModelAndView returned to DispatcherServlet with name '" +
297 getServletName() + "': assuming HandlerAdapter completed request handling");
301 // Trigger after-completion for successful outcome.
302 triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
305 catch (Exception ex) {
306 // Trigger after-completion for thrown exception.
307 triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
308 throw ex;
310 catch (Error err) {
311 ServletException ex = new NestedServletException("Handler processing failed", err);
312 // Trigger after-completion for thrown exception.
313 triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
314 throw ex;
317 finally {
318 // Clean up any resources used by a multipart request.
319 if (processedRequest instanceof MultipartHttpServletRequest && processedRequest != request) {
320 if(multipartResolver != null)
321 this.multipartResolver.cleanupMultipart((MultipartHttpServletRequest) processedRequest);
324 // Reset thread-bound RequestAttributes.
325 if(requestAttributes != null) {
327 requestAttributes.requestCompleted();
328 WebUtils.storeGrailsWebRequest(previousRequestAttributes);
330 // Reset thread-bound LocaleContext.
331 LocaleContextHolder.setLocaleContext(previousLocaleContext);
334 if (logger.isDebugEnabled()) {
335 logger.debug("Cleared thread-bound request context: " + request);
340 protected HttpServletResponse useWrappedOrOriginalResponse(HttpServletResponse response) {
341 HttpServletResponse r = WrappedResponseHolder.getWrappedResponse();
342 if(r != null) return r;
343 return response;
346 protected void copyParamsFromPreviousRequest(GrailsWebRequest previousRequestAttributes, GrailsWebRequest requestAttributes) {
347 Map previousParams = previousRequestAttributes.getParams();
348 Map params = requestAttributes.getParams();
349 for (Iterator i = previousParams.keySet().iterator(); i.hasNext();) {
350 String name = (String)i.next();
351 params.put(name, previousParams.get(name));
356 * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
357 * Will just invoke afterCompletion for all interceptors whose preHandle
358 * invocation has successfully completed and returned true.
359 * @param mappedHandler the mapped HandlerExecutionChain
360 * @param interceptorIndex index of last interceptor that successfully completed
361 * @param ex Exception thrown on handler execution, or <code>null</code> if none
362 * @see HandlerInterceptor#afterCompletion
364 protected void triggerAfterCompletion(
365 HandlerExecutionChain mappedHandler, int interceptorIndex,
366 HttpServletRequest request, HttpServletResponse response, Exception ex)
367 throws Exception {
369 // Apply afterCompletion methods of registered interceptors.
370 if (mappedHandler != null) {
371 if (mappedHandler.getInterceptors() != null) {
372 for (int i = interceptorIndex; i >= 0; i--) {
373 HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
374 try {
375 interceptor.afterCompletion(request, response, mappedHandler.getHandler(), ex);
377 catch (Throwable ex2) {
378 logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
386 * Convert the request into a multipart request, and make multipart resolver available.
387 * If no multipart resolver is set, simply use the existing request.
388 * @param request current HTTP request
389 * @return the processed request (multipart wrapper if necessary)
391 protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
392 if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
393 if (request instanceof MultipartHttpServletRequest) {
394 logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +
395 "this typically results from an additional MultipartFilter in web.xml");
397 else {
398 return this.multipartResolver.resolveMultipart(request);
401 // If not returned before: return original request.
402 return request;
405 * Overrides the default behaviour to establish the handler from the GrailsApplication instance
407 * @param request The request
408 * @param cache Whether to cache the Handler in the request
409 * @return The HandlerExecutionChain
411 * @throws Exception
413 protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
414 String uri = urlHelper.getPathWithinApplication(request);
415 if(logger.isDebugEnabled()) {
416 logger.debug("Looking up Grails controller for URI ["+uri+"]");
418 GrailsControllerClass controllerClass = (GrailsControllerClass) application.getArtefactForFeature(
419 ControllerArtefactHandler.TYPE, uri);
420 if(controllerClass!=null) {
421 HandlerInterceptor[] interceptors;
422 // if we're in a development environment we want to re-establish interceptors just in case they
423 // have changed at runtime
424 if(GrailsUtil.isDevelopmentEnv()) {
425 interceptors = establishInterceptors(getWebApplicationContext());
427 else {
428 interceptors = this.interceptors;
430 if(grailsController == null) {
431 initGrailsController(getWebApplicationContext());
433 return new HandlerExecutionChain(grailsController, interceptors);
435 return null;