From d950b56999fced11d13aa0c638db156a6274e605 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 19 Jan 2009 23:34:41 +0000 Subject: [PATCH] revised common handler method processing for Portlet 2.0 update --- .../web/bind/annotation/Mapping.java | 35 +++++++++++ .../web/bind/annotation/RequestMapping.java | 6 +- .../support/HandlerMethodResolver.java | 16 +++-- .../web/servlet/DispatcherServlet.java | 49 +-------------- .../web/servlet/FrameworkServlet.java | 61 ++++++++++++++++++- .../handler/AbstractUrlHandlerMapping.java | 8 +-- .../AnnotationMethodHandlerAdapter.java | 4 +- .../ServletAnnotationMappingUtils.java | 19 +++--- 8 files changed, 123 insertions(+), 75 deletions(-) create mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/Mapping.java diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/Mapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/Mapping.java new file mode 100644 index 00000000000..afbd1f2e723 --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/Mapping.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.bind.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Meta annotation that indicates a web mapping annotation. + * + * @author Juergen Hoeller + * @since 3.0 + * @see RequestMapping + */ +@Target({ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Mapping { + +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java index cd80754cf62..6adc9606d41 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -165,6 +165,7 @@ import java.lang.annotation.Target; @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented +@Mapping public @interface RequestMapping { /** @@ -202,8 +203,7 @@ public @interface RequestMapping { * When used at the type level, all method-level mappings inherit * this HTTP method restriction (i.e. the type-level restriction * gets checked before the handler method is even resolved). - *

Currently only supported in Servlet environments! - * To be supported for Portlet 2.0 resource requests in Spring 3.0 as well. + *

Supported for Servlet environments as well as Portlet 2.0 environments. */ RequestMethod[] method() default {}; diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodResolver.java index 468e2f39fb7..ee350dfa27d 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodResolver.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,9 +53,9 @@ public class HandlerMethodResolver { private final Set modelAttributeMethods = new LinkedHashSet(); - private final RequestMapping typeLevelMapping; + private RequestMapping typeLevelMapping; - private final boolean sessionAttributesFound; + private boolean sessionAttributesFound; private final Set sessionAttributeNames = new HashSet(); @@ -65,13 +65,13 @@ public class HandlerMethodResolver { /** - * Create a new HandlerMethodResolver for the specified handler type. + * Initialize a new HandlerMethodResolver for the specified handler type. * @param handlerType the handler class to introspect */ - public HandlerMethodResolver(final Class handlerType) { + public void init(final Class handlerType) { ReflectionUtils.doWithMethods(handlerType, new ReflectionUtils.MethodCallback() { public void doWith(Method method) { - if (method.isAnnotationPresent(RequestMapping.class)) { + if (isHandlerMethod(method)) { handlerMethods.add(ClassUtils.getMostSpecificMethod(method, handlerType)); } else if (method.isAnnotationPresent(InitBinder.class)) { @@ -91,6 +91,10 @@ public class HandlerMethodResolver { } } + protected boolean isHandlerMethod(Method method) { + return method.isAnnotationPresent(RequestMapping.class); + } + public final boolean hasHandlerMethods() { return !this.handlerMethods.isEmpty(); diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java index 39978e8800b..b0c3b0715cf 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,7 +42,6 @@ import org.springframework.beans.factory.BeanInitializationException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.context.i18n.LocaleContext; -import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.core.OrderComparator; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.support.PropertiesLoaderUtils; @@ -50,9 +49,6 @@ import org.springframework.ui.context.ThemeSource; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import org.springframework.web.HttpRequestMethodNotSupportedException; -import org.springframework.web.context.request.RequestAttributes; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.multipart.MultipartException; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.MultipartResolver; @@ -282,9 +278,6 @@ public class DispatcherServlet extends FrameworkServlet { /** Perform cleanup of request attributes after include request? */ private boolean cleanupAfterInclude = true; - /** Expose LocaleContext and RequestAttributes as inheritable for child threads? */ - private boolean threadContextInheritable = false; - /** MultipartResolver used by this servlet */ private MultipartResolver multipartResolver; @@ -372,22 +365,6 @@ public class DispatcherServlet extends FrameworkServlet { this.cleanupAfterInclude = cleanupAfterInclude; } - /** - * Set whether to expose the LocaleContext and RequestAttributes as inheritable - * for child threads (using an {@link java.lang.InheritableThreadLocal}). - *

Default is "false", to avoid side effects on spawned background threads. - * Switch this to "true" to enable inheritance for custom child threads which - * are spawned during request processing and only used for this request - * (that is, ending after their initial task, without reuse of the thread). - *

WARNING: Do not use inheritance for child threads if you are - * accessing a thread pool which is configured to potentially add new threads - * on demand (e.g. a JDK {@link java.util.concurrent.ThreadPoolExecutor}), - * since this will expose the inherited context to such a pooled thread. - */ - public void setThreadContextInheritable(boolean threadContextInheritable) { - this.threadContextInheritable = threadContextInheritable; - } - /** * This implementation calls {@link #initStrategies}. @@ -815,19 +792,6 @@ public class DispatcherServlet extends FrameworkServlet { HandlerExecutionChain mappedHandler = null; int interceptorIndex = -1; - // Expose current LocaleResolver and request as LocaleContext. - LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); - LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable); - - // Expose current RequestAttributes to current thread. - RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes(); - ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request); - RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); - - if (logger.isTraceEnabled()) { - logger.trace("Bound request context to thread: " + request); - } - try { ModelAndView mv = null; boolean errorView = false; @@ -917,16 +881,6 @@ public class DispatcherServlet extends FrameworkServlet { if (processedRequest != request) { cleanupMultipart(processedRequest); } - - // Reset thread-bound context. - RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable); - LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable); - - // Clear request attributes. - requestAttributes.requestCompleted(); - if (logger.isTraceEnabled()) { - logger.trace("Cleared thread-bound request context: " + request); - } } } @@ -973,6 +927,7 @@ public class DispatcherServlet extends FrameworkServlet { * @param request current HTTP request * @return the corresponding LocaleContext */ + @Override protected LocaleContext buildLocaleContext(final HttpServletRequest request) { return new LocaleContext() { public Locale getLocale() { diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java index 7e9f716f3a4..c71b5dcbef6 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ package org.springframework.web.servlet; import java.io.IOException; import java.security.Principal; - import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -32,8 +31,14 @@ import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.SourceFilteringListener; +import org.springframework.context.i18n.LocaleContext; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.context.i18n.SimpleLocaleContext; import org.springframework.web.context.ConfigurableWebApplicationContext; import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.support.ServletRequestHandledEvent; import org.springframework.web.context.support.WebApplicationContextUtils; import org.springframework.web.context.support.XmlWebApplicationContext; @@ -129,6 +134,9 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic /** Should we publish a ServletRequestHandledEvent at the end of each request? */ private boolean publishEvents = true; + /** Expose LocaleContext and RequestAttributes as inheritable for child threads? */ + private boolean threadContextInheritable = false; + /** Should we dispatch an HTTP OPTIONS request to {@link #doService}? */ private boolean dispatchOptionsRequest = false; @@ -230,6 +238,22 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic this.publishEvents = publishEvents; } + /** + * Set whether to expose the LocaleContext and RequestAttributes as inheritable + * for child threads (using an {@link java.lang.InheritableThreadLocal}). + *

Default is "false", to avoid side effects on spawned background threads. + * Switch this to "true" to enable inheritance for custom child threads which + * are spawned during request processing and only used for this request + * (that is, ending after their initial task, without reuse of the thread). + *

WARNING: Do not use inheritance for child threads if you are + * accessing a thread pool which is configured to potentially add new threads + * on demand (e.g. a JDK {@link java.util.concurrent.ThreadPoolExecutor}), + * since this will expose the inherited context to such a pooled thread. + */ + public void setThreadContextInheritable(boolean threadContextInheritable) { + this.threadContextInheritable = threadContextInheritable; + } + /** * Set whether this servlet should dispatch an HTTP OPTIONS request to * the {@link #doService} method. @@ -574,6 +598,19 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic long startTime = System.currentTimeMillis(); Throwable failureCause = null; + // Expose current LocaleResolver and request as LocaleContext. + LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); + LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable); + + // Expose current RequestAttributes to current thread. + RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes(); + ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request); + RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); + + if (logger.isTraceEnabled()) { + logger.trace("Bound request context to thread: " + request); + } + try { doService(request, response); } @@ -591,6 +628,16 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic } finally { + // Reset thread-bound context. + RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable); + LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable); + + // Clear request attributes. + requestAttributes.requestCompleted(); + if (logger.isTraceEnabled()) { + logger.trace("Cleared thread-bound request context: " + request); + } + if (failureCause != null) { this.logger.debug("Could not complete request", failureCause); } @@ -610,6 +657,16 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic } } + /** + * Build a LocaleContext for the given request, exposing the request's + * primary locale as current locale. + * @param request current HTTP request + * @return the corresponding LocaleContext + */ + protected LocaleContext buildLocaleContext(HttpServletRequest request) { + return new SimpleLocaleContext(request.getLocale()); + } + /** * Determine the username for the given request. *

The default implementation takes the name of the UserPrincipal, if any. diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java index d3e32e27d45..7e2a68499b8 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -245,9 +245,9 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping { * @param uriTemplateVariables the URI template variables, can be null if no variables found * @return the final handler object */ - protected Object buildPathExposingHandler(Object rawHandler, - String pathWithinMapping, - Map uriTemplateVariables) { + protected Object buildPathExposingHandler( + Object rawHandler, String pathWithinMapping, Map uriTemplateVariables) { + // Bean name or resolved handler? if (rawHandler instanceof String) { String handlerName = (String) rawHandler; diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java index 466e67b0e85..fb39deef9d9 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -428,7 +428,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen private class ServletHandlerMethodResolver extends HandlerMethodResolver { private ServletHandlerMethodResolver(Class handlerType) { - super(handlerType); + init(handlerType); } public Method resolveHandlerMethod(HttpServletRequest request) throws ServletException { diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationMappingUtils.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationMappingUtils.java index 3b757036453..70cf9526a68 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationMappingUtils.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationMappingUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,18 +36,15 @@ abstract class ServletAnnotationMappingUtils { * @param request the current HTTP request to check */ public static boolean checkRequestMethod(RequestMethod[] methods, HttpServletRequest request) { - if (!ObjectUtils.isEmpty(methods)) { - boolean match = false; - for (RequestMethod method : methods) { - if (method.name().equals(request.getMethod())) { - match = true; - } - } - if (!match) { - return false; + if (ObjectUtils.isEmpty(methods)) { + return true; + } + for (RequestMethod method : methods) { + if (method.name().equals(request.getMethod())) { + return true; } } - return true; + return false; } /**