diff --git a/org.springframework.web.portlet/.classpath b/org.springframework.web.portlet/.classpath index 6ad70b93a7c1a47090bcf2e4e9edae459e22e3e9..f272084009af4d92b70d982a8c81b6377e2c2459 100644 --- a/org.springframework.web.portlet/.classpath +++ b/org.springframework.web.portlet/.classpath @@ -10,7 +10,7 @@ - + diff --git a/org.springframework.web.portlet/ivy.xml b/org.springframework.web.portlet/ivy.xml index 08cc3ab4186d4a2f1ff25518bb1b33a982972184..6bd790f1297570dcdce8cd2ea2acfd323608ed92 100644 --- a/org.springframework.web.portlet/ivy.xml +++ b/org.springframework.web.portlet/ivy.xml @@ -21,7 +21,7 @@ - + diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/DispatcherPortlet.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/DispatcherPortlet.java index 144fdfef6aadd23a7c43942699960628d7340e81..70b586a9c60adaed84b3482fae163a7830f28549 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/DispatcherPortlet.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/DispatcherPortlet.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. @@ -26,12 +26,17 @@ import java.util.Map; import java.util.Properties; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; +import javax.portlet.EventRequest; +import javax.portlet.EventResponse; +import javax.portlet.MimeResponse; import javax.portlet.PortletException; import javax.portlet.PortletRequest; import javax.portlet.PortletResponse; import javax.portlet.PortletSession; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; +import javax.portlet.ResourceRequest; +import javax.portlet.ResourceResponse; import javax.portlet.UnavailableException; import org.apache.commons.logging.Log; @@ -42,18 +47,12 @@ import org.springframework.beans.factory.BeanFactoryUtils; 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.context.i18n.SimpleLocaleContext; import org.springframework.core.OrderComparator; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; -import org.springframework.web.context.request.RequestAttributes; -import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.multipart.MultipartException; -import org.springframework.web.portlet.context.PortletRequestAttributes; import org.springframework.web.portlet.multipart.MultipartActionRequest; import org.springframework.web.portlet.multipart.PortletMultipartResolver; import org.springframework.web.servlet.View; @@ -74,17 +73,15 @@ import org.springframework.web.servlet.ViewResolver; * *
  • It can use any {@link HandlerMapping} implementation - pre-built or provided * as part of an application - to control the routing of requests to handler objects. - * Default is a {@link org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping} - * on Java 5+; there is no default on Java 1.4. HandlerMapping objects can be defined as - * beans in the portlet's application context, implementing the HandlerMapping interface, - * overriding the default HandlerMapping if present. HandlerMappings can be given any - * bean name (they are tested by type). + * Default is a {@link org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping}. + * HandlerMapping objects can be defined as beans in the portlet's application context, + * implementing the HandlerMapping interface, overriding the default HandlerMapping if present. + * HandlerMappings can be given any bean name (they are tested by type). * *
  • It can use any {@link HandlerAdapter}; this allows for using any handler interface. * The default adapter is {@link org.springframework.web.portlet.mvc.SimpleControllerHandlerAdapter} * for Spring's {@link org.springframework.web.portlet.mvc.Controller} interface. - * When running in a Java 5+ environment, a default - * {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter} + * A default {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter} * will be registered as well. HandlerAdapter objects can be added as beans in the * application context, overriding the default HandlerAdapter. Like HandlerMappings, * HandlerAdapters can be given any bean name (they are tested by type). @@ -253,9 +250,6 @@ public class DispatcherPortlet extends FrameworkPortlet { /** URL that points to the ViewRendererServlet */ private String viewRendererUrl = DEFAULT_VIEW_RENDERER_URL; - /** Expose LocaleContext and RequestAttributes as inheritable for child threads? */ - private boolean threadContextInheritable = false; - /** MultipartResolver used by this portlet */ private PortletMultipartResolver multipartResolver; @@ -325,22 +319,6 @@ public class DispatcherPortlet extends FrameworkPortlet { this.viewRendererUrl = viewRendererUrl; } - /** - * 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}. @@ -638,19 +616,6 @@ public class DispatcherPortlet extends FrameworkPortlet { logger.debug("DispatcherPortlet with name '" + getPortletName() + "' received action request"); } - // 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(); - PortletRequestAttributes requestAttributes = new PortletRequestAttributes(request); - RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); - - if (logger.isDebugEnabled()) { - logger.debug("Bound action request context to thread: " + request); - } - ActionRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; int interceptorIndex = -1; @@ -713,16 +678,6 @@ public class DispatcherPortlet extends FrameworkPortlet { if (processedRequest instanceof MultipartActionRequest && processedRequest != request) { this.multipartResolver.cleanupMultipart((MultipartActionRequest) processedRequest); } - - // Reset thread-bound context. - RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable); - LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable); - - // Clear request attributes. - requestAttributes.requestCompleted(); - if (logger.isDebugEnabled()) { - logger.debug("Cleared thread-bound action request context: " + request); - } } } @@ -741,19 +696,6 @@ public class DispatcherPortlet extends FrameworkPortlet { logger.debug("DispatcherPortlet with name '" + getPortletName() + "' received render request"); } - // 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(); - PortletRequestAttributes requestAttributes = new PortletRequestAttributes(request); - RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); - - if (logger.isDebugEnabled()) { - logger.debug("Bound render request context to thread: " + request); - } - HandlerExecutionChain mappedHandler = null; int interceptorIndex = -1; @@ -843,31 +785,185 @@ public class DispatcherPortlet extends FrameworkPortlet { triggerAfterRenderCompletion(mappedHandler, interceptorIndex, request, response, ex); throw ex; } + } - finally { - // Reset thread-bound context. - RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable); - LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable); + /** + * Processes the actual dispatching to the handler for resource requests. + *

    The handler will be obtained by applying the portlet's HandlerMappings in order. + * The HandlerAdapter will be obtained by querying the portlet's installed + * HandlerAdapters to find the first that supports the handler class. + * @param request current portlet render request + * @param response current portlet render response + * @throws Exception in case of any kind of processing failure + */ + @Override + protected void doResourceService(ResourceRequest request, ResourceResponse response) throws Exception { + if (logger.isDebugEnabled()) { + logger.debug("DispatcherPortlet with name '" + getPortletName() + "' received resource request"); + } - // Clear request attributes. - requestAttributes.requestCompleted(); - if (logger.isDebugEnabled()) { - logger.debug("Cleared thread-bound render request context: " + request); + HandlerExecutionChain mappedHandler = null; + int interceptorIndex = -1; + + try { + ModelAndView mv = null; + try { + // Check for forwarded exception from the action phase + PortletSession session = request.getPortletSession(false); + if (session != null) { + if (request.getParameter(ACTION_EXCEPTION_RENDER_PARAMETER) != null) { + Exception ex = (Exception) session.getAttribute(ACTION_EXCEPTION_SESSION_ATTRIBUTE); + if (ex != null) { + logger.debug("Render phase found exception caught during action phase - rethrowing it"); + throw ex; + } + } + else { + session.removeAttribute(ACTION_EXCEPTION_SESSION_ATTRIBUTE); + } + } + + // Determine handler for the current request. + mappedHandler = getHandler(request, false); + if (mappedHandler == null || mappedHandler.getHandler() == null) { + noHandlerFound(request, response); + return; + } + + // Apply preHandle methods of registered interceptors. + HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); + if (interceptors != null) { + for (int i = 0; i < interceptors.length; i++) { + HandlerInterceptor interceptor = interceptors[i]; + if (!interceptor.preHandleResource(request, response, mappedHandler.getHandler())) { + triggerAfterResourceCompletion(mappedHandler, interceptorIndex, request, response, null); + return; + } + interceptorIndex = i; + } + } + + // Actually invoke the handler. + HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); + mv = ha.handleResource(request, response, mappedHandler.getHandler()); + + // Apply postHandle methods of registered interceptors. + if (interceptors != null) { + for (int i = interceptors.length - 1; i >= 0; i--) { + HandlerInterceptor interceptor = interceptors[i]; + interceptor.postHandleResource(request, response, mappedHandler.getHandler(), mv); + } + } + } + catch (ModelAndViewDefiningException ex) { + logger.debug("ModelAndViewDefiningException encountered", ex); + mv = ex.getModelAndView(); + } + catch (Exception ex) { + Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); + mv = processHandlerException(request, response, handler, ex); + } + + // Did the handler return a view to render? + if (mv != null && !mv.isEmpty()) { + render(mv, request, response); } + else { + if (logger.isDebugEnabled()) { + logger.debug("Null ModelAndView returned to DispatcherPortlet with name '" + + getPortletName() + "': assuming HandlerAdapter completed request handling"); + } + } + + // Trigger after-completion for successful outcome. + triggerAfterResourceCompletion(mappedHandler, interceptorIndex, request, response, null); } - } + catch (Exception ex) { + // Trigger after-completion for thrown exception. + triggerAfterResourceCompletion(mappedHandler, interceptorIndex, request, response, ex); + throw ex; + } + catch (Error err) { + PortletException ex = + new PortletException("Error occured during request processing: " + err.getMessage(), err); + // Trigger after-completion for thrown exception. + triggerAfterResourceCompletion(mappedHandler, interceptorIndex, request, response, ex); + throw ex; + } + } /** - * 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 + * Processes the actual dispatching to the handler for event requests. + *

    The handler will be obtained by applying the portlet's HandlerMappings in order. + * The HandlerAdapter will be obtained by querying the portlet's installed + * HandlerAdapters to find the first that supports the handler class. + * @param request current portlet action request + * @param response current portlet Action response + * @throws Exception in case of any kind of processing failure */ - protected LocaleContext buildLocaleContext(PortletRequest request) { - return new SimpleLocaleContext(request.getLocale()); + @Override + protected void doEventService(EventRequest request, EventResponse response) throws Exception { + if (logger.isDebugEnabled()) { + logger.debug("DispatcherPortlet with name '" + getPortletName() + "' received action request"); + } + + HandlerExecutionChain mappedHandler = null; + int interceptorIndex = -1; + + try { + // Determine handler for the current request. + mappedHandler = getHandler(request, false); + if (mappedHandler == null || mappedHandler.getHandler() == null) { + noHandlerFound(request, response); + return; + } + + // Apply preHandle methods of registered interceptors. + HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); + if (interceptors != null) { + for (int i = 0; i < interceptors.length; i++) { + HandlerInterceptor interceptor = interceptors[i]; + if (!interceptor.preHandleEvent(request, response, mappedHandler.getHandler())) { + triggerAfterEventCompletion(mappedHandler, interceptorIndex, request, response, null); + return; + } + interceptorIndex = i; + } + } + + // Actually invoke the handler. + HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); + ha.handleEvent(request, response, mappedHandler.getHandler()); + + // Trigger after-completion for successful outcome. + triggerAfterEventCompletion(mappedHandler, interceptorIndex, request, response, null); + } + + catch (Exception ex) { + // Trigger after-completion for thrown exception. + triggerAfterEventCompletion(mappedHandler, interceptorIndex, request, response, ex); + // Forward the exception to the render phase to be displayed. + try { + response.setRenderParameter(ACTION_EXCEPTION_RENDER_PARAMETER, ex.toString()); + request.getPortletSession().setAttribute(ACTION_EXCEPTION_SESSION_ATTRIBUTE, ex); + logger.debug("Caught exception during action phase - forwarding to render phase", ex); + } + catch (IllegalStateException ex2) { + // Probably sendRedirect called... need to rethrow exception immediately. + throw ex; + } + } + catch (Error err) { + PortletException ex = + new PortletException("Error occured during request processing: " + err.getMessage(), err); + // Trigger after-completion for thrown exception. + triggerAfterEventCompletion(mappedHandler, interceptorIndex, request, response, ex); + throw ex; + } } + /** * Convert the request into a multipart request, and make multipart resolver available. * If no multipart resolver is set, simply use the existing request. @@ -929,11 +1025,11 @@ public class DispatcherPortlet extends FrameworkPortlet { protected void noHandlerFound(PortletRequest request, PortletResponse response) throws Exception { if (pageNotFoundLogger.isWarnEnabled()) { pageNotFoundLogger.warn("No mapping found for current request " + - "in DispatcherPortlet with name '" + getPortletName() + "'" + - ", mode '" + request.getPortletMode() + "'" + - ", type '" + (response instanceof ActionResponse ? "action" : "render") + "'" + - ", session '" + request.getRequestedSessionId() + "'" + - ", user '" + getUsernameForRequest(request) + "'"); + "in DispatcherPortlet with name '" + getPortletName() + + "', mode '" + request.getPortletMode() + + "', phase '" + request.getAttribute(PortletRequest.LIFECYCLE_PHASE) + + "', session '" + request.getRequestedSessionId() + + "', user '" + getUsernameForRequest(request) + "'"); } throw new UnavailableException("No handler found for request"); } @@ -957,67 +1053,6 @@ public class DispatcherPortlet extends FrameworkPortlet { "]: Does your handler implement a supported interface like Controller?"); } - /** - * Determine an error ModelAndView via the registered HandlerExceptionResolvers. - * @param request current portlet request - * @param response current portlet response - * @param handler the executed handler, or null if none chosen at the time of - * the exception (for example, if multipart resolution failed) - * @param ex the exception that got thrown during handler execution - * @return a corresponding ModelAndView to forward to - * @throws Exception if no error ModelAndView found - */ - protected ModelAndView processHandlerException( - RenderRequest request, RenderResponse response, Object handler, Exception ex) - throws Exception { - - ModelAndView exMv = null; - for (Iterator it = this.handlerExceptionResolvers.iterator(); exMv == null && it.hasNext();) { - HandlerExceptionResolver resolver = it.next(); - exMv = resolver.resolveException(request, response, handler, ex); - } - if (exMv != null) { - if (logger.isDebugEnabled()) { - logger.debug("HandlerExceptionResolver returned ModelAndView [" + exMv + "] for exception"); - } - logger.warn("Handler execution resulted in exception - forwarding to resolved error view", ex); - return exMv; - } - else { - throw ex; - } - } - - /** - * Trigger afterCompletion callbacks on the mapped HandlerInterceptors. - * Will just invoke afterCompletion for all interceptors whose preHandle - * invocation has successfully completed and returned true. - * @param mappedHandler the mapped HandlerExecutionChain - * @param interceptorIndex index of last interceptor that successfully completed - * @param ex Exception thrown on handler execution, or null if none - * @see HandlerInterceptor#afterRenderCompletion - */ - private void triggerAfterActionCompletion(HandlerExecutionChain mappedHandler, int interceptorIndex, - ActionRequest request, ActionResponse response, Exception ex) - throws Exception { - - // Apply afterCompletion methods of registered interceptors. - if (mappedHandler != null) { - HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); - if (interceptors != null) { - for (int i = interceptorIndex; i >= 0; i--) { - HandlerInterceptor interceptor = interceptors[i]; - try { - interceptor.afterActionCompletion(request, response, mappedHandler.getHandler(), ex); - } - catch (Throwable ex2) { - logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); - } - } - } - } - } - /** * Render the given ModelAndView. This is the last stage in handling a request. @@ -1027,7 +1062,7 @@ public class DispatcherPortlet extends FrameworkPortlet { * @param response current portlet render response * @throws Exception if there's a problem rendering the view */ - protected void render(ModelAndView mv, RenderRequest request, RenderResponse response) throws Exception { + protected void render(ModelAndView mv, PortletRequest request, MimeResponse response) throws Exception { View view = null; if (mv.isReference()) { // We need to resolve the view name. @@ -1087,7 +1122,7 @@ public class DispatcherPortlet extends FrameworkPortlet { * (typically in case of problems creating an actual View object) * @see ViewResolver#resolveViewName */ - protected View resolveViewName(String viewName, Map model, RenderRequest request) throws Exception { + protected View resolveViewName(String viewName, Map model, PortletRequest request) throws Exception { for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, request.getLocale()); if (view != null) { @@ -1107,7 +1142,7 @@ public class DispatcherPortlet extends FrameworkPortlet { * @param response current portlet render response * @throws Exception if there's a problem rendering the view */ - protected void doRender(View view, Map model, RenderRequest request, RenderResponse response) throws Exception { + protected void doRender(View view, Map model, PortletRequest request, MimeResponse response) throws Exception { // Expose Portlet ApplicationContext to view objects. request.setAttribute(ViewRendererServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE, getPortletApplicationContext()); @@ -1119,6 +1154,99 @@ public class DispatcherPortlet extends FrameworkPortlet { getPortletContext().getRequestDispatcher(this.viewRendererUrl).include(request, response); } + + /** + * Determine an error ModelAndView via the registered HandlerExceptionResolvers. + * @param request current portlet request + * @param response current portlet response + * @param handler the executed handler, or null if none chosen at the time of + * the exception (for example, if multipart resolution failed) + * @param ex the exception that got thrown during handler execution + * @return a corresponding ModelAndView to forward to + * @throws Exception if no error ModelAndView found + */ + protected ModelAndView processHandlerException( + RenderRequest request, RenderResponse response, Object handler, Exception ex) + throws Exception { + + ModelAndView exMv = null; + for (Iterator it = this.handlerExceptionResolvers.iterator(); exMv == null && it.hasNext();) { + HandlerExceptionResolver resolver = it.next(); + exMv = resolver.resolveException(request, response, handler, ex); + } + if (exMv != null) { + if (logger.isDebugEnabled()) { + logger.debug("HandlerExceptionResolver returned ModelAndView [" + exMv + "] for exception"); + } + logger.warn("Handler execution resulted in exception - forwarding to resolved error view", ex); + return exMv; + } + else { + throw ex; + } + } + + /** + * Determine an error ModelAndView via the registered HandlerExceptionResolvers. + * @param request current portlet request + * @param response current portlet response + * @param handler the executed handler, or null if none chosen at the time of + * the exception (for example, if multipart resolution failed) + * @param ex the exception that got thrown during handler execution + * @return a corresponding ModelAndView to forward to + * @throws Exception if no error ModelAndView found + */ + protected ModelAndView processHandlerException( + ResourceRequest request, ResourceResponse response, Object handler, Exception ex) + throws Exception { + + ModelAndView exMv = null; + for (Iterator it = this.handlerExceptionResolvers.iterator(); exMv == null && it.hasNext();) { + HandlerExceptionResolver resolver = it.next(); + exMv = resolver.resolveException(request, response, handler, ex); + } + if (exMv != null) { + if (logger.isDebugEnabled()) { + logger.debug("HandlerExceptionResolver returned ModelAndView [" + exMv + "] for exception"); + } + logger.warn("Handler execution resulted in exception - forwarding to resolved error view", ex); + return exMv; + } + else { + throw ex; + } + } + + /** + * Trigger afterCompletion callbacks on the mapped HandlerInterceptors. + * Will just invoke afterCompletion for all interceptors whose preHandle + * invocation has successfully completed and returned true. + * @param mappedHandler the mapped HandlerExecutionChain + * @param interceptorIndex index of last interceptor that successfully completed + * @param ex Exception thrown on handler execution, or null if none + * @see HandlerInterceptor#afterRenderCompletion + */ + private void triggerAfterActionCompletion(HandlerExecutionChain mappedHandler, int interceptorIndex, + ActionRequest request, ActionResponse response, Exception ex) + throws Exception { + + // Apply afterCompletion methods of registered interceptors. + if (mappedHandler != null) { + HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); + if (interceptors != null) { + for (int i = interceptorIndex; i >= 0; i--) { + HandlerInterceptor interceptor = interceptors[i]; + try { + interceptor.afterActionCompletion(request, response, mappedHandler.getHandler(), ex); + } + catch (Throwable ex2) { + logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); + } + } + } + } + } + /** * Trigger afterCompletion callbacks on the mapped HandlerInterceptors. * Will just invoke afterCompletion for all interceptors whose preHandle @@ -1149,4 +1277,64 @@ public class DispatcherPortlet extends FrameworkPortlet { } } + /** + * Trigger afterCompletion callbacks on the mapped HandlerInterceptors. + * Will just invoke afterCompletion for all interceptors whose preHandle + * invocation has successfully completed and returned true. + * @param mappedHandler the mapped HandlerExecutionChain + * @param interceptorIndex index of last interceptor that successfully completed + * @param ex Exception thrown on handler execution, or null if none + * @see HandlerInterceptor#afterRenderCompletion + */ + private void triggerAfterResourceCompletion(HandlerExecutionChain mappedHandler, int interceptorIndex, + ResourceRequest request, ResourceResponse response, Exception ex) + throws Exception { + + // Apply afterCompletion methods of registered interceptors. + if (mappedHandler != null) { + HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); + if (interceptors != null) { + for (int i = interceptorIndex; i >= 0; i--) { + HandlerInterceptor interceptor = interceptors[i]; + try { + interceptor.afterResourceCompletion(request, response, mappedHandler.getHandler(), ex); + } + catch (Throwable ex2) { + logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); + } + } + } + } + } + + /** + * Trigger afterCompletion callbacks on the mapped HandlerInterceptors. + * Will just invoke afterCompletion for all interceptors whose preHandle + * invocation has successfully completed and returned true. + * @param mappedHandler the mapped HandlerExecutionChain + * @param interceptorIndex index of last interceptor that successfully completed + * @param ex Exception thrown on handler execution, or null if none + * @see HandlerInterceptor#afterRenderCompletion + */ + private void triggerAfterEventCompletion(HandlerExecutionChain mappedHandler, int interceptorIndex, + EventRequest request, EventResponse response, Exception ex) + throws Exception { + + // Apply afterCompletion methods of registered interceptors. + if (mappedHandler != null) { + HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); + if (interceptors != null) { + for (int i = interceptorIndex; i >= 0; i--) { + HandlerInterceptor interceptor = interceptors[i]; + try { + interceptor.afterEventCompletion(request, response, mappedHandler.getHandler(), ex); + } + catch (Throwable ex2) { + logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); + } + } + } + } + } + } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/FrameworkPortlet.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/FrameworkPortlet.java index 170a150ee7c3bb1e9713e62ca3a1354ae57e6114..e1822197791042ac2434b830b47f4fdd14e950eb 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/FrameworkPortlet.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/FrameworkPortlet.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. @@ -19,14 +19,17 @@ package org.springframework.web.portlet; import java.io.IOException; import java.security.Principal; import java.util.Map; - import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; +import javax.portlet.EventRequest; +import javax.portlet.EventResponse; import javax.portlet.PortletException; import javax.portlet.PortletRequest; import javax.portlet.PortletResponse; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; +import javax.portlet.ResourceRequest; +import javax.portlet.ResourceResponse; import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; @@ -37,8 +40,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.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.portlet.context.ConfigurablePortletApplicationContext; import org.springframework.web.portlet.context.PortletApplicationContextUtils; +import org.springframework.web.portlet.context.PortletRequestAttributes; import org.springframework.web.portlet.context.PortletRequestHandledEvent; import org.springframework.web.portlet.context.XmlPortletApplicationContext; @@ -136,6 +145,9 @@ public abstract class FrameworkPortlet extends GenericPortletBean implements App /** Should we publish a PortletRequestHandledEvent at the end of each request? */ private boolean publishEvents = true; + /** Expose LocaleContext and RequestAttributes as inheritable for child threads? */ + private boolean threadContextInheritable = false; + /** USER_INFO attributes that may contain the username of the current user */ private String[] userinfoUsernameAttributes = DEFAULT_USERINFO_ATTRIBUTE_NAMES; @@ -205,13 +217,6 @@ public abstract class FrameworkPortlet extends GenericPortletBean implements App this.publishContext = publishContext; } - /** - * Return whether to publish this portlet's context as a PortletContext attribute. - */ - public boolean isPublishContext() { - return this.publishContext; - } - /** * Set whether this portlet should publish a PortletRequestHandledEvent at the end * of each request. Default is true; can be turned off for a slight performance @@ -223,11 +228,19 @@ public abstract class FrameworkPortlet extends GenericPortletBean implements App } /** - * Return whether this portlet should publish a PortletRequestHandledEvent at the end - * of each request. + * 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 boolean isPublishEvents() { - return this.publishEvents; + public void setThreadContextInheritable(boolean threadContextInheritable) { + this.threadContextInheritable = threadContextInheritable; } /** @@ -239,15 +252,6 @@ public abstract class FrameworkPortlet extends GenericPortletBean implements App this.userinfoUsernameAttributes = userinfoUsernameAttributes; } - /** - * Returns the list of attributes that will be searched in the USER_INFO map - * when trying to find the username of the current user - * @see #getUsernameForRequest - */ - public String[] getUserinfoUsernameAttributes() { - return this.userinfoUsernameAttributes; - } - /** * Overridden method of GenericPortletBean, invoked after any bean properties @@ -297,7 +301,7 @@ public abstract class FrameworkPortlet extends GenericPortletBean implements App onRefresh(pac); } - if (isPublishContext()) { + if (this.publishContext) { // publish the context as a portlet context attribute String attName = getPortletContextAttributeName(); getPortletContext().setAttribute(attName, pac); @@ -447,25 +451,39 @@ public abstract class FrameworkPortlet extends GenericPortletBean implements App } /** - * Delegate render requests to processRequest/doRenderService. + * Delegate action requests to processRequest/doActionService. */ @Override - protected final void doDispatch(RenderRequest request, RenderResponse response) + public final void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException { processRequest(request, response); } /** - * Delegate action requests to processRequest/doActionService. + * Delegate render requests to processRequest/doRenderService. */ @Override - public final void processAction(ActionRequest request, ActionResponse response) + protected final void doDispatch(RenderRequest request, RenderResponse response) throws PortletException, IOException { processRequest(request, response); } - + + @Override + public void serveResource(ResourceRequest request, ResourceResponse response) + throws PortletException, IOException { + + processRequest(request, response); + } + + @Override + public void processEvent(EventRequest request, EventResponse response) + throws PortletException, IOException { + + processRequest(request, response); + } + /** * Process this request, publishing an event regardless of the outcome. * The actual event handling is performed by the abstract @@ -479,13 +497,36 @@ public abstract class FrameworkPortlet extends GenericPortletBean implements App 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(); + PortletRequestAttributes requestAttributes = new PortletRequestAttributes(request); + RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); + + if (logger.isTraceEnabled()) { + logger.trace("Bound request context to thread: " + request); + } + try { - if (request instanceof ActionRequest) { + String phase = (String) request.getAttribute(PortletRequest.LIFECYCLE_PHASE); + if (PortletRequest.ACTION_PHASE.equals(phase)) { doActionService((ActionRequest) request, (ActionResponse) response); } - else { + else if (PortletRequest.RENDER_PHASE.equals(phase)) { doRenderService((RenderRequest) request, (RenderResponse) response); } + else if (PortletRequest.RESOURCE_PHASE.equals(phase)) { + doResourceService((ResourceRequest) request, (ResourceResponse) response); + } + else if (PortletRequest.EVENT_PHASE.equals(phase)) { + doEventService((EventRequest) request, (EventResponse) response); + } + else { + throw new IllegalStateException("Invalid portlet request phase: " + phase); + } } catch (PortletException ex) { failureCause = ex; @@ -501,13 +542,23 @@ public abstract class FrameworkPortlet extends GenericPortletBean implements App } 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 resource request context: " + request); + } + if (failureCause != null) { logger.error("Could not complete request", failureCause); } else { logger.debug("Successfully completed request"); } - if (isPublishEvents()) { + if (this.publishEvents) { // Whether or not we succeeded, publish an event. long processingTime = System.currentTimeMillis() - startTime; this.portletApplicationContext.publishEvent( @@ -520,6 +571,16 @@ public abstract class FrameworkPortlet extends GenericPortletBean implements App } } + /** + * 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(PortletRequest request) { + return new SimpleLocaleContext(request.getLocale()); + } + /** * Determine the username for the given request. *

    The default implementation first tries the UserPrincipal. @@ -560,6 +621,21 @@ public abstract class FrameworkPortlet extends GenericPortletBean implements App return null; } + + /** + * Subclasses must implement this method to do the work of action request handling. + *

    The contract is essentially the same as that for the processAction + * method of GenericPortlet. + *

    This class intercepts calls to ensure that exception handling and + * event publication takes place. + * @param request current action request + * @param response current action response + * @throws Exception in case of any kind of processing failure + * @see javax.portlet.GenericPortlet#processAction + */ + protected abstract void doActionService(ActionRequest request, ActionResponse response) + throws Exception; + /** * Subclasses must implement this method to do the work of render request handling. *

    The contract is essentially the same as that for the doDispatch @@ -575,17 +651,31 @@ public abstract class FrameworkPortlet extends GenericPortletBean implements App throws Exception; /** - * Subclasses must implement this method to do the work of action request handling. - *

    The contract is essentially the same as that for the processAction + * Subclasses must implement this method to do the work of resource request handling. + *

    The contract is essentially the same as that for the serveResource * method of GenericPortlet. *

    This class intercepts calls to ensure that exception handling and * event publication takes place. - * @param request current action request - * @param response current action response + * @param request current resource request + * @param response current resource response * @throws Exception in case of any kind of processing failure - * @see javax.portlet.GenericPortlet#processAction + * @see javax.portlet.GenericPortlet#serveResource */ - protected abstract void doActionService(ActionRequest request, ActionResponse response) + protected abstract void doResourceService(ResourceRequest request, ResourceResponse response) + throws Exception; + + /** + * Subclasses must implement this method to do the work of event request handling. + *

    The contract is essentially the same as that for the processEvent + * method of GenericPortlet. + *

    This class intercepts calls to ensure that exception handling and + * event publication takes place. + * @param request current event request + * @param response current event response + * @throws Exception in case of any kind of processing failure + * @see javax.portlet.GenericPortlet#processEvent + */ + protected abstract void doEventService(EventRequest request, EventResponse response) throws Exception; diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/HandlerAdapter.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/HandlerAdapter.java index ef771e02c372bc15a000c7db200135a862662559..c1e618fa6d4fcfcdbc67d5c9eae89baf0e0505d8 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/HandlerAdapter.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/HandlerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 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,8 +18,12 @@ package org.springframework.web.portlet; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; +import javax.portlet.EventRequest; +import javax.portlet.EventResponse; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; +import javax.portlet.ResourceRequest; +import javax.portlet.ResourceResponse; /** * Portlet MVC framework SPI interface, allowing parameterization of core MVC workflow. @@ -67,6 +71,7 @@ public interface HandlerAdapter { * to the supports method of this interface, which must have * returned true. * @throws Exception in case of errors + * @see javax.portlet.Portlet#processAction */ void handleAction(ActionRequest request, ActionResponse response, Object handler) throws Exception; @@ -81,7 +86,36 @@ public interface HandlerAdapter { * @throws Exception in case of errors * @return ModelAndView object with the name of the view and the required * model data, or null if the request has been handled directly + * @see javax.portlet.Portlet#render */ ModelAndView handleRender(RenderRequest request, RenderResponse response, Object handler) throws Exception; + /** + * Use the given handler to handle this resource request. + * The workflow that is required may vary widely. + * @param request current render request + * @param response current render response + * @param handler handler to use. This object must have previously been passed + * to the supports method of this interface, which must have + * returned true. + * @throws Exception in case of errors + * @return ModelAndView object with the name of the view and the required + * model data, or null if the request has been handled directly + * @see javax.portlet.ResourceServingPortlet#serveResource + */ + ModelAndView handleResource(ResourceRequest request, ResourceResponse response, Object handler) throws Exception; + + /** + * Use the given handler to handle this event request. + * The workflow that is required may vary widely. + * @param request current action request + * @param response current action response + * @param handler handler to use. This object must have previously been passed + * to the supports method of this interface, which must have + * returned true. + * @throws Exception in case of errors + * @see javax.portlet.EventPortlet#processEvent + */ + void handleEvent(EventRequest request, EventResponse response, Object handler) throws Exception; + } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/HandlerExceptionResolver.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/HandlerExceptionResolver.java index 42695edd47489e919428d58d8e635d2e8bd22b7c..d2efc4484dd28c0f3a7ba389d7aac7ca3f11db31 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/HandlerExceptionResolver.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/HandlerExceptionResolver.java @@ -1,12 +1,12 @@ /* - * Copyright 2002-2005 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. * 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. @@ -18,6 +18,8 @@ package org.springframework.web.portlet; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; +import javax.portlet.ResourceRequest; +import javax.portlet.ResourceResponse; /** * Interface to be implemented by objects than can resolve exceptions thrown @@ -47,4 +49,17 @@ public interface HandlerExceptionResolver { ModelAndView resolveException( RenderRequest request, RenderResponse response, Object handler, Exception ex); + /** + * Try to resolve the given exception that got thrown during on handler execution, + * returning a ModelAndView that represents a specific error page if appropriate. + * @param request current portlet request + * @param response current portlet response + * @param handler the executed handler, or null if none chosen at the time of + * the exception (for example, if multipart resolution failed) + * @param ex the exception that got thrown during handler execution + * @return a corresponding ModelAndView to forward to, or null for default processing + */ + ModelAndView resolveException( + ResourceRequest request, ResourceResponse response, Object handler, Exception ex); + } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/HandlerInterceptor.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/HandlerInterceptor.java index 9ab888cd4c3b18e0261395ebd6e3b5a85768434c..a09d13d5b707f06a91b7044e61032742c185862f 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/HandlerInterceptor.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/HandlerInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 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. @@ -20,6 +20,10 @@ import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; +import javax.portlet.EventResponse; +import javax.portlet.EventRequest; +import javax.portlet.ResourceResponse; +import javax.portlet.ResourceRequest; /** * Workflow interface that allows for customized handler execution chains. @@ -130,7 +134,7 @@ public interface HandlerInterceptor { * @throws Exception in case of errors */ boolean preHandleAction(ActionRequest request, ActionResponse response, Object handler) - throws Exception; + throws Exception; /** * Callback after completion of request processing in the action phase, that is, @@ -149,7 +153,7 @@ public interface HandlerInterceptor { */ void afterActionCompletion( ActionRequest request, ActionResponse response, Object handler, Exception ex) - throws Exception; + throws Exception; /** * Intercept the execution of a handler in the render phase. @@ -169,7 +173,7 @@ public interface HandlerInterceptor { * @throws Exception in case of errors */ boolean preHandleRender(RenderRequest request, RenderResponse response, Object handler) - throws Exception; + throws Exception; /** * Intercept the execution of a handler in the render phase. @@ -208,4 +212,101 @@ public interface HandlerInterceptor { RenderRequest request, RenderResponse response, Object handler, Exception ex) throws Exception; + /** + * Intercept the execution of a handler in the render phase. + *

    Called after a HandlerMapping determines an appropriate handler object + * to handle a {@link RenderRequest}, but before said HandlerAdapter actually + * invokes the handler. + *

    {@link DispatcherPortlet} processes a handler in an execution chain, + * consisting of any number of interceptors, with the handler itself at the end. + * With this method, each interceptor can decide to abort the execution chain, + * typically throwing an exception or writing a custom response. + * @param request current portlet render request + * @param response current portlet render response + * @param handler chosen handler to execute, for type and/or instance evaluation + * @return true if the execution chain should proceed with the + * next interceptor or the handler itself. Else, DispatcherPortlet + * assumes that this interceptor has already dealt with the response itself + * @throws Exception in case of errors + */ + boolean preHandleResource(ResourceRequest request, ResourceResponse response, Object handler) + throws Exception; + + /** + * Intercept the execution of a handler in the render phase. + *

    Called after a {@link HandlerAdapter} actually invoked the handler, but + * before the DispatcherPortlet renders the view. Can thus expose + * additional model objects to the view via the given {@link ModelAndView}. + *

    DispatcherPortlet processes a handler in an execution chain, + * consisting of any number of interceptors, with the handler itself at the end. + * With this method, each interceptor can post-process an execution, getting + * applied in inverse order of the execution chain. + * @param request current portlet render request + * @param response current portlet render response + * @param handler chosen handler to execute, for type and/or instance examination + * @param modelAndView the ModelAndView that the handler returned + * (can also be null) + * @throws Exception in case of errors + */ + void postHandleResource( + ResourceRequest request, ResourceResponse response, Object handler, ModelAndView modelAndView) + throws Exception; + + /** + * Callback after completion of request processing, that is, after rendering + * the view. Will be called on any outcome of handler execution, thus allowing + * for proper resource cleanup. + *

    Note: Will only be called if this interceptor's + * {@link #preHandleRender(javax.portlet.RenderRequest, javax.portlet.RenderResponse, Object)} + * method has successfully completed and returned true! + * @param request current portlet render request + * @param response current portlet render response + * @param handler chosen handler to execute, for type and/or instance examination + * @param ex exception thrown on handler execution, if any + * @throws Exception in case of errors + */ + void afterResourceCompletion( + ResourceRequest request, ResourceResponse response, Object handler, Exception ex) + throws Exception; + + + /** + * Intercept the execution of a handler in the action phase. + *

    Called after a HandlerMapping determines an appropriate handler object + * to handle an {@link ActionRequest}, but before said HandlerAdapter actually + * invokes the handler. + *

    {@link DispatcherPortlet} processes a handler in an execution chain, + * consisting of any number of interceptors, with the handler itself at the end. + * With this method, each interceptor can decide to abort the execution chain, + * typically throwing an exception or writing a custom response. + * @param request current portlet action request + * @param response current portlet action response + * @param handler chosen handler to execute, for type and/or instance evaluation + * @return true if the execution chain should proceed with the + * next interceptor or the handler itself. Else, DispatcherPortlet + * assumes that this interceptor has already dealt with the response itself + * @throws Exception in case of errors + */ + boolean preHandleEvent(EventRequest request, EventResponse response, Object handler) + throws Exception; + + /** + * Callback after completion of request processing in the action phase, that is, + * after rendering the view. Will be called on any outcome of handler execution, + * thus allowing for proper resource cleanup. + *

    Note: Will only be called if this interceptor's + * {@link #preHandleAction(javax.portlet.ActionRequest, javax.portlet.ActionResponse, Object)} + * method has successfully completed and returned true! + * @param request current portlet action request + * @param response current portlet action response + * @param handler chosen handler to execute, for type and/or instance examination + * @param ex exception thrown on handler execution, if any (only included as + * additional context information for the case where a handler threw an exception; + * request execution may have failed even when this argument is null) + * @throws Exception in case of errors + */ + void afterEventCompletion( + EventRequest request, EventResponse response, Object handler, Exception ex) + throws Exception; + } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/annotation/ActionMapping.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/annotation/ActionMapping.java new file mode 100644 index 0000000000000000000000000000000000000000..0adb5ded1c673708bed723b861987874fe9272c1 --- /dev/null +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/annotation/ActionMapping.java @@ -0,0 +1,63 @@ +/* + * 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.portlet.bind.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.web.bind.annotation.Mapping; + +/** + * Annotation for mapping Portlet action requests onto handler methods. + * + * @author Juergen Hoeller + * @since 3.0 + * @see org.springframework.web.bind.annotation.RequestMapping + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Mapping +public @interface ActionMapping { + + /** + * The name of the action, according to the Portlet 2.0 + * "javax.portlet.action" parameter. + *

    If not specified, the method will be used as default handler: + * i.e. for action requests where no specific action mapping was found. + *

    Note that all such annotated action methods only apply within the + * @RequestMapping constraints of the containing handler class. + * @see javax.portlet.ActionRequest#ACTION_NAME + */ + String value() default ""; + + /** + * The parameters of the mapped request, narrowing the primary mapping. + *

    Same format for any environment: a sequence of "myParam=myValue" style + * expressions, with a request only mapped if each such parameter is found + * to have the given value. "myParam" style expressions are also supported, + * with such parameters having to be present in the request (allowed to have + * any value). Finally, "!myParam" style expressions indicate that the + * specified parameter is not supposed to be present in the request. + * @see org.springframework.web.bind.annotation.RequestMapping#params() + */ + String[] params() default {}; + +} diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/annotation/EventMapping.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/annotation/EventMapping.java new file mode 100644 index 0000000000000000000000000000000000000000..cbd3086d66227db74d8ba42adc7f981e97da85cf --- /dev/null +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/annotation/EventMapping.java @@ -0,0 +1,63 @@ +/* + * 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.portlet.bind.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.web.bind.annotation.Mapping; + +/** + * Annotation for mapping Portlet event requests onto handler methods. + * + * @author Juergen Hoeller + * @since 3.0 + * @see org.springframework.web.bind.annotation.RequestMapping + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Mapping +public @interface EventMapping { + + /** + * The name of the event to be handled. + *

    Typically the local name of the event, but fully qualified names + * with a "{...}" namespace part will be mapped correctly as well. + *

    If not specified, the render method will be invoked for any + * event request within its general mapping. + * @see javax.portlet.EventRequest#getEvent() + * @see javax.portlet.Event#getName() + */ + String value(); + + /** + * The parameters of the mapped request, narrowing the primary mapping. + *

    Same format for any environment: a sequence of "myParam=myValue" style + * expressions, with a request only mapped if each such parameter is found + * to have the given value. "myParam" style expressions are also supported, + * with such parameters having to be present in the request (allowed to have + * any value). Finally, "!myParam" style expressions indicate that the + * specified parameter is not supposed to be present in the request. + * @see org.springframework.web.bind.annotation.RequestMapping#params() + */ + String[] params() default {}; + +} diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/annotation/RenderMapping.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/annotation/RenderMapping.java new file mode 100644 index 0000000000000000000000000000000000000000..7eb8b1dcf9d547e5491e42162f9b0eb4de5b85f2 --- /dev/null +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/annotation/RenderMapping.java @@ -0,0 +1,62 @@ +/* + * 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.portlet.bind.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.web.bind.annotation.Mapping; + +/** + * Annotation for mapping Portlet render requests onto handler methods. + * + * @author Juergen Hoeller + * @since 3.0 + * @see org.springframework.web.bind.annotation.RequestMapping + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Mapping +public @interface RenderMapping { + + /** + * The window state that the annotated render method applies for. + *

    If not specified, the render method will be invoked for any + * window state within its general mapping. + *

    Standard Portlet spec values: "NORMAL", "MAXIMIZED", "MINIMIZED". + * Custom window states can be used as well, as supported by the portal. + * @see javax.portlet.PortletRequest#getWindowState() + */ + String value() default ""; + + /** + * The parameters of the mapped request, narrowing the primary mapping. + *

    Same format for any environment: a sequence of "myParam=myValue" style + * expressions, with a request only mapped if each such parameter is found + * to have the given value. "myParam" style expressions are also supported, + * with such parameters having to be present in the request (allowed to have + * any value). Finally, "!myParam" style expressions indicate that the + * specified parameter is not supposed to be present in the request. + * @see org.springframework.web.bind.annotation.RequestMapping#params() + */ + String[] params() default {}; + +} diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/annotation/ResourceMapping.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/annotation/ResourceMapping.java new file mode 100644 index 0000000000000000000000000000000000000000..de9ac2836fc8ac7ac9929279c852bc1d113fc109 --- /dev/null +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/annotation/ResourceMapping.java @@ -0,0 +1,60 @@ +/* + * 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.portlet.bind.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.web.bind.annotation.Mapping; + +/** + * Annotation for mapping Portlet resource requests onto handler methods. + * + * @author Juergen Hoeller + * @since 3.0 + * @see org.springframework.web.bind.annotation.RequestMapping + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Mapping +public @interface ResourceMapping { + + /** + * The id of the resource to be handled. + *

    If not specified, the render method will be invoked for any + * resource request within its general mapping. + * @see javax.portlet.ResourceRequest#getResourceID() + */ + String value() default ""; + + /** + * The parameters of the mapped request, narrowing the primary mapping. + *

    Same format for any environment: a sequence of "myParam=myValue" style + * expressions, with a request only mapped if each such parameter is found + * to have the given value. "myParam" style expressions are also supported, + * with such parameters having to be present in the request (allowed to have + * any value). Finally, "!myParam" style expressions indicate that the + * specified parameter is not supposed to be present in the request. + * @see org.springframework.web.bind.annotation.RequestMapping#params() + */ + String[] params() default {}; + +} diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/annotation/package.html b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/annotation/package.html new file mode 100644 index 0000000000000000000000000000000000000000..943baab59fc420090563413d06112381a57f9758 --- /dev/null +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/annotation/package.html @@ -0,0 +1,7 @@ + + + +Annotations for binding portlet requests to handler methods. + + + diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/AbstractMapBasedHandlerMapping.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/AbstractMapBasedHandlerMapping.java index e9479b5ecc9743ca92364ab6c984420afb1b68dd..14174097f9db68285f55858d91022d504557ce06 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/AbstractMapBasedHandlerMapping.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/AbstractMapBasedHandlerMapping.java @@ -22,6 +22,7 @@ import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import javax.portlet.PortletException; import javax.portlet.PortletRequest; import org.springframework.beans.BeansException; @@ -80,6 +81,7 @@ public abstract class AbstractMapBasedHandlerMapping extends AbstractHandlerM Collections.sort(predicates); for (PortletRequestMappingPredicate predicate : predicates) { if (predicate.match(request)) { + predicate.validate(request); return predicateMap.get(predicate); } } @@ -187,6 +189,13 @@ public abstract class AbstractMapBasedHandlerMapping extends AbstractHandlerM * @param request current portlet request */ boolean match(PortletRequest request); + + /** + * Validate this predicate's mapping against the current request. + * @param request current portlet request + * @throws PortletException if validation failed + */ + void validate(PortletRequest request) throws PortletException; } } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/HandlerInterceptorAdapter.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/HandlerInterceptorAdapter.java index 5a3557be7436b785df4bcbf81512dcda3b88ae9e..4920da4f68cce5149fe37595e32127732e0e1d11 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/HandlerInterceptorAdapter.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/HandlerInterceptorAdapter.java @@ -1,12 +1,12 @@ /* - * Copyright 2002-2005 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. * 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. @@ -16,21 +16,22 @@ package org.springframework.web.portlet.handler; -import java.io.IOException; - import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; -import javax.portlet.RenderRequest; -import javax.portlet.RenderResponse; +import javax.portlet.EventRequest; +import javax.portlet.EventResponse; import javax.portlet.PortletRequest; import javax.portlet.PortletResponse; -import javax.portlet.PortletException; +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; +import javax.portlet.ResourceRequest; +import javax.portlet.ResourceResponse; import org.springframework.web.portlet.HandlerInterceptor; import org.springframework.web.portlet.ModelAndView; /** - * Abstract adapter class for the HandlerInterceptor interface, + * Abstract adapter class for the {@link HandlerInterceptor} interface, * for simplified implementation of pre-only/post-only interceptors. * * @author Juergen Hoeller @@ -40,29 +41,31 @@ import org.springframework.web.portlet.ModelAndView; public abstract class HandlerInterceptorAdapter implements HandlerInterceptor { /** - * This implementation delegates to preHandle. - * @see #preHandle + * This implementation delegates to {@link #preHandle}. */ - public boolean preHandleAction(ActionRequest request, ActionResponse response, Object handler) throws Exception { + public boolean preHandleAction(ActionRequest request, ActionResponse response, Object handler) + throws Exception { + return preHandle(request, response, handler); } /** - * This implementation delegates to afterCompletion. - * @see #afterCompletion + * This implementation delegates to {@link #afterCompletion}. */ public void afterActionCompletion( - ActionRequest request, ActionResponse response, Object handler, Exception ex) throws Exception { + ActionRequest request, ActionResponse response, Object handler, Exception ex) + throws Exception { afterCompletion(request, response, handler, ex); } /** - * This implementation delegates to preHandle. - * @see #preHandle + * This implementation delegates to {@link #preHandle}. */ - public boolean preHandleRender(RenderRequest request, RenderResponse response, Object handler) throws Exception { + public boolean preHandleRender(RenderRequest request, RenderResponse response, Object handler) + throws Exception { + return preHandle(request, response, handler); } @@ -70,26 +73,72 @@ public abstract class HandlerInterceptorAdapter implements HandlerInterceptor { * This implementation is empty. */ public void postHandleRender( - RenderRequest request, RenderResponse response, Object handler, ModelAndView modelAndView) throws Exception { + RenderRequest request, RenderResponse response, Object handler, ModelAndView modelAndView) + throws Exception { } /** - * This implementation delegates to afterCompletion. - * @see #afterCompletion + * This implementation delegates to {@link #afterCompletion}. */ public void afterRenderCompletion( - RenderRequest request, RenderResponse response, Object handler, Exception ex) throws Exception { + RenderRequest request, RenderResponse response, Object handler, Exception ex) + throws Exception { + + afterCompletion(request, response, handler, ex); + } + + + /** + * This implementation delegates to {@link #preHandle}. + */ + public boolean preHandleResource(ResourceRequest request, ResourceResponse response, Object handler) + throws Exception { + + return preHandle(request, response, handler); + } + + /** + * This implementation is empty. + */ + public void postHandleResource( + ResourceRequest request, ResourceResponse response, Object handler, ModelAndView modelAndView) + throws Exception { + } + + /** + * This implementation delegates to {@link #afterCompletion}. + */ + public void afterResourceCompletion( + ResourceRequest request, ResourceResponse response, Object handler, Exception ex) + throws Exception { + + afterCompletion(request, response, handler, ex); + } + + + /** + * This implementation delegates to {@link #preHandle}. + */ + public boolean preHandleEvent(EventRequest request, EventResponse response, Object handler) + throws Exception { + + return preHandle(request, response, handler); + } + + /** + * This implementation delegates to {@link #afterCompletion}. + */ + public void afterEventCompletion( + EventRequest request, EventResponse response, Object handler, Exception ex) + throws Exception { afterCompletion(request, response, handler, ex); } /** - * Default callback that both preHandleRender - * and preHandleAction delegate to. + * Default callback that all "pre*" methods delegate to. *

    This implementation always returns true. - * @see #preHandleRender - * @see #preHandleAction */ protected boolean preHandle(PortletRequest request, PortletResponse response, Object handler) throws Exception { @@ -98,14 +147,12 @@ public abstract class HandlerInterceptorAdapter implements HandlerInterceptor { } /** - * Default callback that both preHandleRender - * and preHandleAction delegate to. + * Default callback that all "after*" methods delegate to. *

    This implementation is empty. - * @see #afterRenderCompletion - * @see #afterActionCompletion */ protected void afterCompletion( - PortletRequest request, PortletResponse response, Object handler, Exception ex) throws Exception { + PortletRequest request, PortletResponse response, Object handler, Exception ex) + throws Exception { } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/PortletContentGenerator.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/PortletContentGenerator.java index 470e0e2aa093c7677c0a956bee1e8e028191fe65..318c5d1ceb65deecbfbfc810ed6b911748177ea5 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/PortletContentGenerator.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/PortletContentGenerator.java @@ -16,11 +16,10 @@ package org.springframework.web.portlet.handler; +import javax.portlet.MimeResponse; import javax.portlet.PortletException; import javax.portlet.PortletRequest; import javax.portlet.PortletResponse; -import javax.portlet.RenderRequest; -import javax.portlet.RenderResponse; import org.springframework.web.portlet.context.PortletApplicationObjectSupport; @@ -102,7 +101,7 @@ public abstract class PortletContentGenerator extends PortletApplicationObjectSu * @param response current portlet response * @throws PortletException if the request cannot be handled because a check failed */ - protected final void checkAndPrepare(RenderRequest request, RenderResponse response) + protected final void checkAndPrepare(PortletRequest request, MimeResponse response) throws PortletException { checkAndPrepare(request, response, this.cacheSeconds); @@ -118,8 +117,7 @@ public abstract class PortletContentGenerator extends PortletApplicationObjectSu * response should be cacheable for, 0 to prevent caching * @throws PortletException if the request cannot be handled because a check failed */ - protected final void checkAndPrepare( - RenderRequest request, RenderResponse response, int cacheSeconds) + protected final void checkAndPrepare(PortletRequest request, MimeResponse response, int cacheSeconds) throws PortletException { check(request, response); @@ -129,7 +127,7 @@ public abstract class PortletContentGenerator extends PortletApplicationObjectSu /** * Prevent the render response from being cached. */ - protected final void preventCaching(RenderResponse response) { + protected final void preventCaching(MimeResponse response) { cacheForSeconds(response, 0); } @@ -139,8 +137,8 @@ public abstract class PortletContentGenerator extends PortletApplicationObjectSu * @param seconds number of seconds into the future that the response * should be cacheable for */ - protected final void cacheForSeconds(RenderResponse response, int seconds) { - response.setProperty(RenderResponse.EXPIRATION_CACHE, Integer.toString(seconds)); + protected final void cacheForSeconds(MimeResponse response, int seconds) { + response.setProperty(MimeResponse.EXPIRATION_CACHE, Integer.toString(seconds)); } /** @@ -149,7 +147,7 @@ public abstract class PortletContentGenerator extends PortletApplicationObjectSu * @param seconds positive number of seconds into the future that the * response should be cacheable for, 0 to prevent caching */ - protected final void applyCacheSeconds(RenderResponse response, int seconds) { + protected final void applyCacheSeconds(MimeResponse response, int seconds) { if (seconds > 0) { cacheForSeconds(response, seconds); } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/PortletRequestMethodNotSupportedException.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/PortletRequestMethodNotSupportedException.java new file mode 100644 index 0000000000000000000000000000000000000000..f347a027fba4e32d5d53579210eeccca32ec3d3d --- /dev/null +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/PortletRequestMethodNotSupportedException.java @@ -0,0 +1,82 @@ +/* + * 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.portlet.handler; + +import javax.portlet.PortletException; + +import org.springframework.util.StringUtils; + +/** + * Exception thrown when a request handler does not support a + * specific request method. + * + * @author Juergen Hoeller + * @since 3.0 + */ +public class PortletRequestMethodNotSupportedException extends PortletException { + + private String method; + + private String[] supportedMethods; + + + /** + * Create a new PortletRequestMethodNotSupportedException. + * @param method the unsupported HTTP request method + */ + public PortletRequestMethodNotSupportedException(String method) { + this(method, null); + } + + /** + * Create a new PortletRequestMethodNotSupportedException. + * @param method the unsupported HTTP request method + * @param supportedMethods the actually supported HTTP methods + */ + public PortletRequestMethodNotSupportedException(String method, String[] supportedMethods) { + super("Request method '" + method + "' not supported by mapped handler"); + this.method = method; + this.supportedMethods = supportedMethods; + } + + /** + * Create a new PortletRequestMethodNotSupportedException. + * @param method the unsupported HTTP request method + * @param supportedMethods the actually supported HTTP methods + */ + public PortletRequestMethodNotSupportedException(String[] supportedMethods) { + super("Mapped handler only supports client data requests with methods " + + StringUtils.arrayToCommaDelimitedString(supportedMethods)); + this.supportedMethods = supportedMethods; + } + + + /** + * Return the HTTP request method that caused the failure. + */ + public String getMethod() { + return this.method; + } + + /** + * Return the actually supported HTTP methods, if known. + */ + public String[] getSupportedMethods() { + return this.supportedMethods; + } + +} \ No newline at end of file diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimpleMappingExceptionResolver.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimpleMappingExceptionResolver.java index 03071d1419becc13e075a2534f779da8f661f735..9bb2f51b6787df8f8494d6d1f9fcc4b5174279a3 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimpleMappingExceptionResolver.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimpleMappingExceptionResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2008 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. @@ -19,9 +19,12 @@ package org.springframework.web.portlet.handler; import java.util.Enumeration; import java.util.Properties; import java.util.Set; - +import javax.portlet.MimeResponse; +import javax.portlet.PortletRequest; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; +import javax.portlet.ResourceRequest; +import javax.portlet.ResourceResponse; import javax.portlet.WindowState; import org.apache.commons.logging.Log; @@ -190,6 +193,18 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, } } + public ModelAndView resolveException( + ResourceRequest request, ResourceResponse response, Object handler, Exception ex) { + + if (shouldApplyTo(request, handler)) { + return doResolveException(request, response, handler, ex); + } + else { + return null; + } + } + + /** * Check whether this resolver is supposed to apply to the given handler. *

    The default implementation checks against the specified mapped handlers @@ -203,19 +218,19 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, * @see #setMappedHandlers * @see #setMappedHandlerClasses */ - protected boolean shouldApplyTo(RenderRequest request, Object handler) { + protected boolean shouldApplyTo(PortletRequest request, Object handler) { // If the portlet is minimized and we don't want to render then return null. if (WindowState.MINIMIZED.equals(request.getWindowState()) && !this.renderWhenMinimized) { return false; } - + // Check mapped handlers... if (handler != null) { if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) { return true; } if (this.mappedHandlerClasses != null) { - for (int i = 0; i < this.mappedHandlerClasses.length; i++) { - if (this.mappedHandlerClasses[i].isInstance(handler)) { + for (Class mappedClass : this.mappedHandlerClasses) { + if (mappedClass.isInstance(handler)) { return true; } } @@ -236,7 +251,7 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, * @return a corresponding ModelAndView to forward to, or null for default processing */ protected ModelAndView doResolveException( - RenderRequest request, RenderResponse response, Object handler, Exception ex) { + PortletRequest request, MimeResponse response, Object handler, Exception ex) { // Log exception, both at debug log level and at warn level, if desired. if (logger.isDebugEnabled()) { @@ -266,7 +281,7 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, * @see #buildLogMessage * @see org.apache.commons.logging.Log#warn(Object, Throwable) */ - protected void logException(Exception ex, RenderRequest request) { + protected void logException(Exception ex, PortletRequest request) { if (this.warnLogger != null && this.warnLogger.isWarnEnabled()) { this.warnLogger.warn(buildLogMessage(ex, request), ex); } @@ -279,7 +294,7 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, * @param request current portlet request (useful for obtaining metadata) * @return the log message to use */ - protected String buildLogMessage(Exception ex, RenderRequest request) { + protected String buildLogMessage(Exception ex, PortletRequest request) { return "Handler execution resulted in exception"; } @@ -292,7 +307,7 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, * @param request current portlet request (useful for obtaining metadata) * @return the resolved view name, or null if none found */ - protected String determineViewName(Exception ex, RenderRequest request) { + protected String determineViewName(Exception ex, PortletRequest request) { String viewName = null; // Check for specific exception mappings. if (this.exceptionMappings != null) { @@ -348,7 +363,7 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, } private int getDepth(String exceptionMapping, Class exceptionClass, int depth) { - if (exceptionClass.getName().indexOf(exceptionMapping) != -1) { + if (exceptionClass.getName().contains(exceptionMapping)) { // Found it! return depth; } @@ -369,7 +384,7 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, * @return the ModelAndView instance * @see #getModelAndView(String, Exception) */ - protected ModelAndView getModelAndView(String viewName, Exception ex, RenderRequest request) { + protected ModelAndView getModelAndView(String viewName, Exception ex, PortletRequest request) { return getModelAndView(viewName, ex); } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimplePortletHandlerAdapter.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimplePortletHandlerAdapter.java index f2f2ad3142b4a60cd6c9970896fc6e274f3b1a95..a4ea17611da45238ff0c07b438da226883f9edcb 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimplePortletHandlerAdapter.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimplePortletHandlerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 the original author or authors. + * Copyright 2002-2008 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,12 +18,21 @@ package org.springframework.web.portlet.handler; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; +import javax.portlet.EventPortlet; +import javax.portlet.EventRequest; +import javax.portlet.EventResponse; import javax.portlet.Portlet; +import javax.portlet.PortletContext; +import javax.portlet.PortletRequestDispatcher; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; +import javax.portlet.ResourceRequest; +import javax.portlet.ResourceResponse; +import javax.portlet.ResourceServingPortlet; import org.springframework.web.portlet.HandlerAdapter; import org.springframework.web.portlet.ModelAndView; +import org.springframework.web.portlet.context.PortletContextAware; /** * Adapter to use the Portlet interface with the generic DispatcherPortlet. @@ -49,8 +58,16 @@ import org.springframework.web.portlet.ModelAndView; * @see SimplePortletPostProcessor * @see org.springframework.web.portlet.mvc.PortletWrappingController */ -public class SimplePortletHandlerAdapter implements HandlerAdapter { +public class SimplePortletHandlerAdapter implements HandlerAdapter, PortletContextAware { + private PortletContext portletContext; + + + public void setPortletContext(PortletContext portletContext) { + this.portletContext = portletContext; + } + + public boolean supports(Object handler) { return (handler instanceof Portlet); } @@ -68,4 +85,32 @@ public class SimplePortletHandlerAdapter implements HandlerAdapter { return null; } + public ModelAndView handleResource(ResourceRequest request, ResourceResponse response, Object handler) + throws Exception { + + if (handler instanceof ResourceServingPortlet) { + ((ResourceServingPortlet) handler).serveResource(request, response); + } + else { + // equivalent to Portlet 2.0 GenericPortlet + if (request.getResourceID() != null) { + PortletRequestDispatcher rd = this.portletContext.getRequestDispatcher(request.getResourceID()); + if (rd != null) { + rd.forward(request, response); + } + } + } + return null; + } + + public void handleEvent(EventRequest request, EventResponse response, Object handler) throws Exception { + if (handler instanceof EventPortlet) { + ((EventPortlet) handler).processEvent(request, response); + } + else { + // if no event processing method was found just keep render params + response.setRenderParameters(request); + } + } + } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimplePortletPostProcessor.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimplePortletPostProcessor.java index 6525db44aee7b395fd5a8c40ab4c83a2954734c0..b5687fd6e7657d45dda1f7d278efc23fec7c22b4 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimplePortletPostProcessor.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimplePortletPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 the original author or authors. + * Copyright 2002-2008 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,13 +18,16 @@ package org.springframework.web.portlet.handler; import java.util.Collections; import java.util.Enumeration; +import java.util.HashSet; import java.util.Locale; +import java.util.Map; import java.util.ResourceBundle; - import javax.portlet.Portlet; import javax.portlet.PortletConfig; import javax.portlet.PortletContext; import javax.portlet.PortletException; +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanInitializationException; @@ -144,23 +147,47 @@ public class SimplePortletPostProcessor } public String getPortletName() { - return portletName; + return this.portletName; } public PortletContext getPortletContext() { - return portletContext; + return this.portletContext; } public String getInitParameter(String paramName) { return null; } - public Enumeration getInitParameterNames() { - return Collections.enumeration(Collections.EMPTY_SET); + public Enumeration getInitParameterNames() { + return Collections.enumeration(new HashSet()); } public ResourceBundle getResourceBundle(Locale locale) { - return portletConfig == null ? null : portletConfig.getResourceBundle(locale); + return (this.portletConfig != null ? this.portletConfig.getResourceBundle(locale) : null); + } + + public Enumeration getPublicRenderParameterNames() { + return Collections.enumeration(new HashSet()); + } + + public String getDefaultNamespace() { + return XMLConstants.NULL_NS_URI; + } + + public Enumeration getPublishingEventQNames() { + return Collections.enumeration(new HashSet()); + } + + public Enumeration getProcessingEventQNames() { + return Collections.enumeration(new HashSet()); + } + + public Enumeration getSupportedLocales() { + return Collections.enumeration(new HashSet()); + } + + public Map getContainerRuntimeOptions() { + return (this.portletConfig != null ? this.portletConfig.getContainerRuntimeOptions() : null); } } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/UserRoleAuthorizationInterceptor.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/UserRoleAuthorizationInterceptor.java index 14c9f7a63f63d18182c45c6f11f929bd7a46747f..57f4119b6423925156339f415a6bf22dbeb7f20c 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/UserRoleAuthorizationInterceptor.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/UserRoleAuthorizationInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 the original author or authors. + * Copyright 2002-2008 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. @@ -51,8 +51,8 @@ public class UserRoleAuthorizationInterceptor extends HandlerInterceptorAdapter throws PortletException, IOException { if (this.authorizedRoles != null) { - for (int i = 0; i < this.authorizedRoles.length; i++) { - if (request.isUserInRole(this.authorizedRoles[i])) { + for (String role : this.authorizedRoles) { + if (request.isUserInRole(role)) { return true; } } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/WebRequestHandlerInterceptorAdapter.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/WebRequestHandlerInterceptorAdapter.java index 9389feabe44d25f2f8fcf53ae2122d76d2f19f1a..a29cd0578ec2ebf3f94921a601b6b608177b0b38 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/WebRequestHandlerInterceptorAdapter.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/WebRequestHandlerInterceptorAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 the original author or authors. + * Copyright 2002-2008 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,8 +18,12 @@ package org.springframework.web.portlet.handler; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; +import javax.portlet.EventRequest; +import javax.portlet.EventResponse; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; +import javax.portlet.ResourceRequest; +import javax.portlet.ResourceResponse; import org.springframework.util.Assert; import org.springframework.web.context.request.WebRequestInterceptor; @@ -105,4 +109,35 @@ public class WebRequestHandlerInterceptorAdapter implements HandlerInterceptor { this.requestInterceptor.afterCompletion(new PortletWebRequest(request), ex); } + public boolean preHandleResource(ResourceRequest request, ResourceResponse response, Object handler) + throws Exception { + + this.requestInterceptor.preHandle(new PortletWebRequest(request)); + return true; + } + + public void postHandleResource(ResourceRequest request, ResourceResponse response, Object handler, ModelAndView modelAndView) + throws Exception { + + this.requestInterceptor.postHandle(new PortletWebRequest(request), + (modelAndView != null ? modelAndView.getModelMap() : null)); + } + + public void afterResourceCompletion(ResourceRequest request, ResourceResponse response, Object handler, + Exception ex) throws Exception { + + this.requestInterceptor.afterCompletion(new PortletWebRequest(request), ex); + } + + public boolean preHandleEvent(EventRequest request, EventResponse response, Object handler) throws Exception { + this.requestInterceptor.preHandle(new PortletWebRequest(request)); + return true; + } + + public void afterEventCompletion(EventRequest request, EventResponse response, Object handler, Exception ex) + throws Exception { + + this.requestInterceptor.afterCompletion(new PortletWebRequest(request), ex); + } + } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/multipart/DefaultMultipartActionRequest.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/multipart/DefaultMultipartActionRequest.java index 01b46e47f2f574fb0414d15a21e74333466831ff..a1de7c049b3154f27146b1a361ee95846edb51bf 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/multipart/DefaultMultipartActionRequest.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/multipart/DefaultMultipartActionRequest.java @@ -24,9 +24,9 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; import javax.portlet.ActionRequest; +import javax.portlet.filter.ActionRequestWrapper; import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.portlet.util.ActionRequestWrapper; /** * Default implementation of the {@link MultipartActionRequest} interface. diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/AbstractController.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/AbstractController.java index fe7a6eea0810475c5bf187faadf3496479335771..6e824099ef9e5d5149e8ead48b758dd7cdef2792 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/AbstractController.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/AbstractController.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. @@ -122,6 +122,8 @@ import org.springframework.web.portlet.util.PortletUtils; * @author John A. Lewis * @author Juergen Hoeller * @since 2.0 + * @see ResourceAwareController + * @see EventAwareController */ public abstract class AbstractController extends PortletContentGenerator implements Controller { diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/Controller.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/Controller.java index bfb49294be0f03a537526f8b694b8ba3ad7fe0c7..356898f97ba2ebedd7481d389fa7a9c16a9aa590 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/Controller.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/Controller.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 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. @@ -45,15 +45,7 @@ import org.springframework.web.portlet.ModelAndView; * request and - if applicable - returning an appropriate ModelAndView. * So actually, these method are the main entrypoint for the * {@link org.springframework.web.portlet.DispatcherPortlet DispatcherPortlet} - * which delegates requests to controllers. These method - and also this interface - - * should preferrably not be implemented by custom controllers directly, since - * abstract controllers also provided by this package already provide a lot of - * functionality for typical use cases in portlet applications. A few examples of - * those controllers: - * {@link AbstractController AbstractController}, - * {@link AbstractCommandController AbstractCommandController}, - * {@link AbstractFormController AbstractFormController}, - * {@link SimpleFormController SimpleFormController}.

    + * which delegates requests to controllers.

    * *

    So basically any direct implementation of the Controller interface * just handles RenderRequests/ActionRequests and should return a ModelAndView, to be @@ -64,19 +56,16 @@ import org.springframework.web.portlet.ModelAndView; * @author William G. Thompson, Jr. * @author John A. Lewis * @since 2.0 + * @see ResourceAwareController + * @see EventAwareController * @see SimpleControllerHandlerAdapter * @see AbstractController - * @see AbstractCommandController - * @see AbstractFormController - * @see SimpleFormController - * @see org.springframework.context.ApplicationContextAware - * @see org.springframework.context.ResourceLoaderAware * @see org.springframework.web.portlet.context.PortletContextAware */ public interface Controller { /** - * Process the action request. There is nothing to return. + * Process the action request. There is nothing to return. * @param request current portlet action request * @param response current portlet action response * @throws Exception in case of errors diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/EventAwareController.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/EventAwareController.java new file mode 100644 index 0000000000000000000000000000000000000000..fd692733e2bfa6a663b962745b9326db8bc05d2c --- /dev/null +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/EventAwareController.java @@ -0,0 +1,43 @@ +/* + * 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.portlet.mvc; + +import javax.portlet.EventRequest; +import javax.portlet.EventResponse; + +/** + * Extension of the Portlet {@link Controller} interface that allows + * for handling Portlet 2.0 event requests as well. Can also be + * implemented by {@link AbstractController} subclasses. + * + * @author Juergen Hoeller + * @since 3.0 + * @see javax.portlet.EventPortlet + * @see Controller + * @see ResourceAwareController + */ +public interface EventAwareController { + + /** + * Process the event request. There is nothing to return. + * @param request current portlet event request + * @param response current portlet event response + * @throws Exception in case of errors + */ + void handleEventRequest(EventRequest request, EventResponse response) throws Exception; + +} diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/PortletWrappingController.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/PortletWrappingController.java index c8dcb3784a29b07cbd20007ce702b6f089ef2b07..9ff142f5009d037b8a8e9cdbd93b5afb1510e447 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/PortletWrappingController.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/PortletWrappingController.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 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. @@ -16,11 +16,13 @@ package org.springframework.web.portlet.mvc; +import java.util.Collections; import java.util.Enumeration; +import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.Locale; -import java.util.Properties; +import java.util.Map; import java.util.ResourceBundle; - import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.Portlet; @@ -28,6 +30,8 @@ import javax.portlet.PortletConfig; import javax.portlet.PortletContext; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.DisposableBean; @@ -79,7 +83,7 @@ public class PortletWrappingController extends AbstractController private String portletName; - private Properties initParameters = new Properties(); + private Map initParameters = new LinkedHashMap(); private String beanName; @@ -128,7 +132,7 @@ public class PortletWrappingController extends AbstractController * Specify init parameters for the portlet to wrap, * as name-value pairs. */ - public void setInitParameters(Properties initParameters) { + public void setInitParameters(Map initParameters) { this.initParameters = initParameters; } @@ -195,17 +199,40 @@ public class PortletWrappingController extends AbstractController } public String getInitParameter(String paramName) { - return initParameters.getProperty(paramName); + return initParameters.get(paramName); } - public Enumeration getInitParameterNames() { - return initParameters.keys(); + public Enumeration getInitParameterNames() { + return Collections.enumeration(initParameters.keySet()); } public ResourceBundle getResourceBundle(Locale locale) { return (portletConfig != null ? portletConfig.getResourceBundle(locale) : null); } + public Enumeration getPublicRenderParameterNames() { + return Collections.enumeration(new HashSet()); + } + + public String getDefaultNamespace() { + return XMLConstants.NULL_NS_URI; + } + + public Enumeration getPublishingEventQNames() { + return Collections.enumeration(new HashSet()); + } + + public Enumeration getProcessingEventQNames() { + return Collections.enumeration(new HashSet()); + } + + public Enumeration getSupportedLocales() { + return Collections.enumeration(new HashSet()); + } + + public Map getContainerRuntimeOptions() { + return (portletConfig != null ? portletConfig.getContainerRuntimeOptions() : null); + } } } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/ResourceAwareController.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/ResourceAwareController.java new file mode 100644 index 0000000000000000000000000000000000000000..24ad45d76b97f52bf40e2915fa57a32132693ee4 --- /dev/null +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/ResourceAwareController.java @@ -0,0 +1,48 @@ +/* + * 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.portlet.mvc; + +import javax.portlet.ResourceRequest; +import javax.portlet.ResourceResponse; + +import org.springframework.web.portlet.ModelAndView; + +/** + * Extension of the Portlet {@link Controller} interface that allows + * for handling Portlet 2.0 resource requests as well. Can also be + * implemented by {@link AbstractController} subclasses. + * + * @author Juergen Hoeller + * @since 3.0 + * @see javax.portlet.ResourceServingPortlet + * @see Controller + * @see EventAwareController + */ +public interface ResourceAwareController { + + /** + * Process the resource request and return a ModelAndView object which the DispatcherPortlet + * will render. A null return value is not an error: It indicates that this + * object completed request processing itself, thus there is no ModelAndView to render. + * @param request current portlet resource request + * @param response current portlet resource response + * @return a ModelAndView to render, or null if handled directly + * @throws Exception in case of errors + */ + ModelAndView handleResourceRequest(ResourceRequest request, ResourceResponse response) throws Exception; + +} diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/SimpleControllerHandlerAdapter.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/SimpleControllerHandlerAdapter.java index 5eac5c4ae180df346b6e8c17ab5400b5c9722e37..d0a3ba7edbfdfa78efb26c0c5176afec2a758c4d 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/SimpleControllerHandlerAdapter.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/SimpleControllerHandlerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 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,24 +18,42 @@ package org.springframework.web.portlet.mvc; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; +import javax.portlet.EventRequest; +import javax.portlet.EventResponse; +import javax.portlet.PortletContext; +import javax.portlet.PortletRequestDispatcher; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; +import javax.portlet.ResourceRequest; +import javax.portlet.ResourceResponse; import org.springframework.web.portlet.HandlerAdapter; import org.springframework.web.portlet.ModelAndView; +import org.springframework.web.portlet.context.PortletContextAware; /** * Adapter to use the Controller workflow interface with the generic DispatcherPortlet. * *

    This is an SPI class, not used directly by application code. * + * @author Juergen Hoeller * @author John A. Lewis * @since 2.0 * @see org.springframework.web.portlet.DispatcherPortlet * @see Controller - */ -public class SimpleControllerHandlerAdapter implements HandlerAdapter { - + * @see ResourceAwareController + * @see EventAwareController + */ +public class SimpleControllerHandlerAdapter implements HandlerAdapter, PortletContextAware { + + private PortletContext portletContext; + + + public void setPortletContext(PortletContext portletContext) { + this.portletContext = portletContext; + } + + public boolean supports(Object handler) { return (handler instanceof Controller); } @@ -52,4 +70,32 @@ public class SimpleControllerHandlerAdapter implements HandlerAdapter { return ((Controller) handler).handleRenderRequest(request, response); } + public ModelAndView handleResource(ResourceRequest request, ResourceResponse response, Object handler) + throws Exception { + + if (handler instanceof ResourceAwareController) { + return ((ResourceAwareController) handler).handleResourceRequest(request, response); + } + else { + // equivalent to Portlet 2.0 GenericPortlet + if (request.getResourceID() != null) { + PortletRequestDispatcher rd = this.portletContext.getRequestDispatcher(request.getResourceID()); + if (rd != null) { + rd.forward(request, response); + } + } + return null; + } + } + + public void handleEvent(EventRequest request, EventResponse response, Object handler) throws Exception { + if (handler instanceof EventAwareController) { + ((EventAwareController) handler).handleEventRequest(request, response); + } + else { + // if no event processing method was found just keep render params + response.setRenderParameters(request); + } + } + } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java index c8e20c22f52f6aced0cee23bd4ae010560a5876f..94ce35562008479503fcb00e3c54e3f7d7f58062 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/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. @@ -23,13 +23,20 @@ import java.io.Writer; import java.lang.reflect.Method; import java.security.Principal; import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; - import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; +import javax.portlet.ClientDataRequest; +import javax.portlet.Event; +import javax.portlet.EventRequest; +import javax.portlet.EventResponse; +import javax.portlet.MimeResponse; import javax.portlet.PortalContext; import javax.portlet.PortletException; import javax.portlet.PortletMode; @@ -39,6 +46,8 @@ import javax.portlet.PortletResponse; import javax.portlet.PortletSession; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; +import javax.portlet.ResourceRequest; +import javax.portlet.ResourceResponse; import javax.portlet.UnavailableException; import javax.portlet.WindowState; @@ -54,11 +63,14 @@ import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; import org.springframework.validation.support.BindingAwareModelMap; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.SessionAttributes; import org.springframework.web.bind.annotation.support.HandlerMethodInvoker; @@ -72,6 +84,10 @@ import org.springframework.web.portlet.HandlerAdapter; import org.springframework.web.portlet.ModelAndView; import org.springframework.web.portlet.bind.MissingPortletRequestParameterException; import org.springframework.web.portlet.bind.PortletRequestDataBinder; +import org.springframework.web.portlet.bind.annotation.ActionMapping; +import org.springframework.web.portlet.bind.annotation.EventMapping; +import org.springframework.web.portlet.bind.annotation.RenderMapping; +import org.springframework.web.portlet.bind.annotation.ResourceMapping; import org.springframework.web.portlet.context.PortletWebRequest; import org.springframework.web.portlet.handler.PortletContentGenerator; import org.springframework.web.portlet.handler.PortletSessionRequiredException; @@ -218,14 +234,25 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl return doHandle(request, response, handler); } + public ModelAndView handleResource(ResourceRequest request, ResourceResponse response, Object handler) throws Exception { + checkAndPrepare(request, response); + return doHandle(request, response, handler); + } + + public void handleEvent(EventRequest request, EventResponse response, Object handler) throws Exception { + Object returnValue = doHandle(request, response, handler); + if (returnValue != null) { + throw new IllegalStateException("Invalid action method return value: " + returnValue); + } + } + protected ModelAndView doHandle(PortletRequest request, PortletResponse response, Object handler) throws Exception { ExtendedModelMap implicitModel = null; - if (request instanceof RenderRequest && response instanceof RenderResponse) { - RenderRequest renderRequest = (RenderRequest) request; - RenderResponse renderResponse = (RenderResponse) response; + if (response instanceof MimeResponse) { + MimeResponse mimeResponse = (MimeResponse) response; // Detect implicit model from associated action phase. - if (renderRequest.getParameter(IMPLICIT_MODEL_ATTRIBUTE) != null) { + if (request.getParameter(IMPLICIT_MODEL_ATTRIBUTE) != null) { PortletSession session = request.getPortletSession(false); if (session != null) { implicitModel = (ExtendedModelMap) session.getAttribute(IMPLICIT_MODEL_ATTRIBUTE); @@ -233,11 +260,11 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl } if (handler.getClass().getAnnotation(SessionAttributes.class) != null) { // Always prevent caching in case of session attribute management. - checkAndPrepare(renderRequest, renderResponse, this.cacheSecondsForSessionAttributeHandlers); + checkAndPrepare(request, mimeResponse, this.cacheSecondsForSessionAttributeHandlers); } else { // Uses configured default cacheSeconds setting. - checkAndPrepare(renderRequest, renderResponse); + checkAndPrepare(request, mimeResponse); } } @@ -265,7 +292,7 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl PortletWebRequest webRequest = new PortletWebRequest(request, response); PortletHandlerMethodResolver methodResolver = getMethodResolver(handler); - Method handlerMethod = methodResolver.resolveHandlerMethod(request, response); + Method handlerMethod = methodResolver.resolveHandlerMethod(request); PortletHandlerMethodInvoker methodInvoker = new PortletHandlerMethodInvoker(methodResolver); Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel); @@ -324,42 +351,51 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl private static class PortletHandlerMethodResolver extends HandlerMethodResolver { + private final Map mappings = new HashMap(); + public PortletHandlerMethodResolver(Class handlerType) { - super(handlerType); + init(handlerType); } - public Method resolveHandlerMethod(PortletRequest request, PortletResponse response) throws PortletException { - String lookupMode = request.getPortletMode().toString(); + @Override + protected boolean isHandlerMethod(Method method) { + RequestMappingInfo mappingInfo = new RequestMappingInfo(); + RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class); + ActionMapping actionMapping = AnnotationUtils.findAnnotation(method, ActionMapping.class); + RenderMapping renderMapping = AnnotationUtils.findAnnotation(method, RenderMapping.class); + ResourceMapping resourceMapping = AnnotationUtils.findAnnotation(method, ResourceMapping.class); + EventMapping eventMapping = AnnotationUtils.findAnnotation(method, EventMapping.class); + if (actionMapping != null) { + mappingInfo.initPhaseMapping(PortletRequest.ACTION_PHASE, actionMapping.value(), actionMapping.params()); + } + if (renderMapping != null) { + mappingInfo.initPhaseMapping(PortletRequest.RENDER_PHASE, renderMapping.value(), renderMapping.params()); + } + if (resourceMapping != null) { + mappingInfo.initPhaseMapping(PortletRequest.RESOURCE_PHASE, resourceMapping.value(), resourceMapping.params()); + } + if (eventMapping != null) { + mappingInfo.initPhaseMapping(PortletRequest.EVENT_PHASE, eventMapping.value(), eventMapping.params()); + } + if (requestMapping != null) { + mappingInfo.initStandardMapping(requestMapping.value(), requestMapping.method(), requestMapping.params()); + if (mappingInfo.phase == null) { + mappingInfo.phase = determineDefaultPhase(method); + } + } + this.mappings.put(method, mappingInfo); + return (mappingInfo.phase != null); + } + + public Method resolveHandlerMethod(PortletRequest request) throws PortletException { Map targetHandlerMethods = new LinkedHashMap(); for (Method handlerMethod : getHandlerMethods()) { - RequestMapping mapping = AnnotationUtils.findAnnotation(handlerMethod, RequestMapping.class); - RequestMappingInfo mappingInfo = new RequestMappingInfo(); - mappingInfo.modes = mapping.value(); - mappingInfo.params = mapping.params(); - mappingInfo.action = isActionMethod(handlerMethod); - mappingInfo.render = isRenderMethod(handlerMethod); - boolean match = false; - if (mappingInfo.modes.length > 0) { - for (String mappedMode : mappingInfo.modes) { - if (mappedMode.equalsIgnoreCase(lookupMode)) { - if (checkParameters(request, response, mappingInfo)) { - match = true; - } - else { - break; - } - } - } - } - else { - // No modes specified: parameter match sufficient. - match = checkParameters(request, response, mappingInfo); - } - if (match) { + RequestMappingInfo mappingInfo = this.mappings.get(handlerMethod); + if (mappingInfo.match(request)) { Method oldMappedMethod = targetHandlerMethods.put(mappingInfo, handlerMethod); if (oldMappedMethod != null && oldMappedMethod != handlerMethod) { throw new IllegalStateException("Ambiguous handler methods mapped for portlet mode '" + - lookupMode + "': {" + oldMappedMethod + ", " + handlerMethod + + request.getPortletMode() + "': {" + oldMappedMethod + ", " + handlerMethod + "}. If you intend to handle the same mode in multiple methods, then factor " + "them out into a dedicated handler class with that mode mapped at the type level!"); } @@ -376,8 +412,7 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl bestMappingMatch = mapping; } else { - if ((bestMappingMatch.modes.length == 0 && mapping.modes.length > 0) || - bestMappingMatch.params.length < mapping.params.length) { + if (mapping.isBetterMatchThan(bestMappingMatch)) { bestMappingMatch = mapping; } } @@ -386,50 +421,123 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl } } else { - throw new UnavailableException("No matching handler method found for portlet request: mode '" + - request.getPortletMode() + "', type '" + (response instanceof ActionResponse ? "action" : "render") + + throw new UnavailableException( + "No matching handler method found for portlet request: mode '" + request.getPortletMode() + + "', phase '" + request.getAttribute(PortletRequest.LIFECYCLE_PHASE) + "', parameters " + StylerUtils.style(request.getParameterMap())); } } - private boolean checkParameters(PortletRequest request, PortletResponse response, RequestMappingInfo mapping) { - if (response instanceof RenderResponse) { - if (mapping.action) { - return false; - } - } - else if (response instanceof ActionResponse) { - if (mapping.render) { - return false; - } - } - return PortletAnnotationMappingUtils.checkParameters(mapping.params, request); - } - - private boolean isActionMethod(Method handlerMethod) { + private String determineDefaultPhase(Method handlerMethod) { if (!void.class.equals(handlerMethod.getReturnType())) { - return false; + return PortletRequest.RENDER_PHASE; } for (Class argType : handlerMethod.getParameterTypes()) { if (ActionRequest.class.isAssignableFrom(argType) || ActionResponse.class.isAssignableFrom(argType) || InputStream.class.isAssignableFrom(argType) || Reader.class.isAssignableFrom(argType)) { - return true; + return PortletRequest.ACTION_PHASE; + } + else if (RenderRequest.class.isAssignableFrom(argType) || RenderResponse.class.isAssignableFrom(argType) || + OutputStream.class.isAssignableFrom(argType) || Writer.class.isAssignableFrom(argType)) { + return PortletRequest.RENDER_PHASE; + } + else if (ResourceRequest.class.isAssignableFrom(argType) || ResourceResponse.class.isAssignableFrom(argType)) { + return PortletRequest.RESOURCE_PHASE; + } + else if (EventRequest.class.isAssignableFrom(argType) || EventResponse.class.isAssignableFrom(argType)) { + return PortletRequest.EVENT_PHASE; } } - return false; + return ""; } + } - private boolean isRenderMethod(Method handlerMethod) { - if (!void.class.equals(handlerMethod.getReturnType())) { - return true; + + private static class RequestMappingInfo { + + public Set modes = new HashSet(); + + public String phase; + + public String value; + + public Set methods = new HashSet(); + + public String[] params = new String[0]; + + public void initStandardMapping(String[] modes, RequestMethod[] methods, String[] params) { + for (String mode : modes) { + this.modes.add(new PortletMode(mode)); } - for (Class argType : handlerMethod.getParameterTypes()) { - if (RenderRequest.class.isAssignableFrom(argType) || RenderResponse.class.isAssignableFrom(argType) || - OutputStream.class.isAssignableFrom(argType) || Writer.class.isAssignableFrom(argType)) { - return true; + for (RequestMethod method : methods) { + this.methods.add(method.name()); + } + this.params = StringUtils.mergeStringArrays(this.params, params); + } + + public void initPhaseMapping(String phase, String value, String[] params) { + if (this.phase != null) { + throw new IllegalStateException( + "Invalid mapping - more than one phase specified: '" + this.phase + "', '" + phase + "'"); + } + this.phase = phase; + this.value = value; + this.params = StringUtils.mergeStringArrays(this.params, params); + } + + public boolean match(PortletRequest request) { + if (!this.modes.isEmpty() && !this.modes.contains(request.getPortletMode())) { + return false; + } + if (StringUtils.hasLength(this.phase) && + !this.phase.equals(request.getAttribute(PortletRequest.LIFECYCLE_PHASE))) { + return false; + } + if (StringUtils.hasLength(this.value)) { + if (this.phase.equals(PortletRequest.ACTION_PHASE) && + !this.value.equals(request.getParameter(ActionRequest.ACTION_NAME))) { + return false; + } + else if (this.phase.equals(PortletRequest.RENDER_PHASE) && + !(new WindowState(this.value)).equals(request.getWindowState())) { + return false; + } + else if (this.phase.equals(PortletRequest.RESOURCE_PHASE) && + !this.value.equals(((ResourceRequest) request).getResourceID())) { + return false; + } + else if (this.phase.equals(PortletRequest.EVENT_PHASE)) { + Event event = ((EventRequest) request).getEvent(); + if (!this.value.equals(event.getName()) && !this.value.equals(event.getQName().toString())) { + return false; + } } } - return false; + return PortletAnnotationMappingUtils.checkRequestMethod(this.methods, request) && + PortletAnnotationMappingUtils.checkParameters(this.params, request); + } + + public boolean isBetterMatchThan(RequestMappingInfo other) { + return ((!this.modes.isEmpty() && other.modes.isEmpty()) || + (StringUtils.hasLength(this.phase) && !StringUtils.hasLength(other.phase)) || + (StringUtils.hasLength(this.value) && !StringUtils.hasLength(other.value)) || + (!this.methods.isEmpty() && other.methods.isEmpty()) || + this.params.length > other.params.length); + } + + @Override + public boolean equals(Object obj) { + RequestMappingInfo other = (RequestMappingInfo) obj; + return (this.modes.equals(other.modes) && + ObjectUtils.nullSafeEquals(this.phase, other.phase) && + ObjectUtils.nullSafeEquals(this.value, other.value) && + this.methods.equals(other.methods) && + Arrays.equals(this.params, other.params)); + } + + @Override + public int hashCode() { + return (ObjectUtils.nullSafeHashCode(this.modes) * 29 + this.phase.hashCode()); } } @@ -505,28 +613,34 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl return request.getLocale(); } else if (InputStream.class.isAssignableFrom(parameterType)) { - if (!(request instanceof ActionRequest)) { - throw new IllegalStateException("InputStream can only get obtained for ActionRequest"); + if (!(request instanceof ClientDataRequest)) { + throw new IllegalStateException("InputStream can only get obtained for Action/ResourceRequest"); } - return ((ActionRequest) request).getPortletInputStream(); + return ((ClientDataRequest) request).getPortletInputStream(); } else if (Reader.class.isAssignableFrom(parameterType)) { - if (!(request instanceof ActionRequest)) { - throw new IllegalStateException("Reader can only get obtained for ActionRequest"); + if (!(request instanceof ClientDataRequest)) { + throw new IllegalStateException("Reader can only get obtained for Action/ResourceRequest"); } - return ((ActionRequest) request).getReader(); + return ((ClientDataRequest) request).getReader(); } else if (OutputStream.class.isAssignableFrom(parameterType)) { - if (!(response instanceof RenderResponse)) { - throw new IllegalStateException("OutputStream can only get obtained for RenderResponse"); + if (!(response instanceof MimeResponse)) { + throw new IllegalStateException("OutputStream can only get obtained for Render/ResourceResponse"); } - return ((RenderResponse) response).getPortletOutputStream(); + return ((MimeResponse) response).getPortletOutputStream(); } else if (Writer.class.isAssignableFrom(parameterType)) { - if (!(response instanceof RenderResponse)) { - throw new IllegalStateException("Writer can only get obtained for RenderResponse"); + if (!(response instanceof MimeResponse)) { + throw new IllegalStateException("Writer can only get obtained for Render/ResourceResponse"); + } + return ((MimeResponse) response).getWriter(); + } + else if (Event.class.equals(parameterType)) { + if (!(request instanceof EventRequest)) { + throw new IllegalStateException("Event can only get obtained from EventRequest"); } - return ((RenderResponse) response).getWriter(); + return ((EventRequest) request).getEvent(); } return super.resolveStandardArgument(parameterType, webRequest); } @@ -581,28 +695,4 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl } } - - private static class RequestMappingInfo { - - public String[] modes = new String[0]; - - public String[] params = new String[0]; - - private boolean action = false; - - private boolean render = false; - - @Override - public boolean equals(Object obj) { - RequestMappingInfo other = (RequestMappingInfo) obj; - return (this.action == other.action && this.render == other.render && - Arrays.equals(this.modes, other.modes) && Arrays.equals(this.params, other.params)); - } - - @Override - public int hashCode() { - return (Arrays.hashCode(this.modes) * 29 + Arrays.hashCode(this.params)); - } - } - } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/DefaultAnnotationHandlerMapping.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/DefaultAnnotationHandlerMapping.java index 2ae1422bd98fef135e3eb4ff84bcc710275d4ec6..473eb5052640ae482fa8182a2f0efed19fe14b73 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/DefaultAnnotationHandlerMapping.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/DefaultAnnotationHandlerMapping.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. @@ -16,9 +16,12 @@ package org.springframework.web.portlet.mvc.annotation; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set; +import javax.portlet.ClientDataRequest; +import javax.portlet.PortletException; import javax.portlet.PortletMode; import javax.portlet.PortletRequest; @@ -28,8 +31,11 @@ import org.springframework.core.annotation.AnnotationUtils; import org.springframework.stereotype.Controller; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.Mapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.portlet.handler.AbstractMapBasedHandlerMapping; +import org.springframework.web.portlet.handler.PortletRequestMethodNotSupportedException; /** * Implementation of the {@link org.springframework.web.portlet.HandlerMapping} @@ -99,12 +105,13 @@ public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapp if (mapping != null) { String[] modeKeys = mapping.value(); String[] params = mapping.params(); + RequestMethod[] methods = mapping.method(); boolean registerHandlerType = true; if (modeKeys.length == 0 || params.length == 0) { registerHandlerType = !detectHandlerMethods(handlerType, beanName, mapping); } if (registerHandlerType) { - ParameterMappingPredicate predicate = new ParameterMappingPredicate(params); + ParameterMappingPredicate predicate = new ParameterMappingPredicate(params, methods); for (String modeKey : modeKeys) { registerHandler(new PortletMode(modeKey), beanName, predicate); } @@ -128,9 +135,20 @@ public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapp final Set handlersRegistered = new HashSet(1); ReflectionUtils.doWithMethods(handlerType, new ReflectionUtils.MethodCallback() { public void doWith(Method method) { - RequestMapping mapping = method.getAnnotation(RequestMapping.class); - if (mapping != null) { - String[] modeKeys = mapping.value(); + boolean mappingFound = false; + String[] modeKeys = new String[0]; + String[] params = new String[0]; + for (Annotation ann : method.getAnnotations()) { + if (AnnotationUtils.findAnnotation(ann.getClass(), Mapping.class) != null) { + mappingFound = true; + if (ann instanceof RequestMapping) { + modeKeys = (String[]) AnnotationUtils.getValue(ann); + } + String[] specificParams = (String[]) AnnotationUtils.getValue(ann, "params"); + params = StringUtils.mergeStringArrays(params, specificParams); + } + } + if (mappingFound) { if (modeKeys.length == 0) { if (typeMapping != null) { modeKeys = typeMapping.value(); @@ -140,7 +158,6 @@ public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapp "No portlet mode mappings specified - neither at type nor method level"); } } - String[] params = mapping.params(); if (typeMapping != null) { PortletAnnotationMappingUtils.validateModeMapping(modeKeys, typeMapping.value()); params = StringUtils.mergeStringArrays(typeMapping.params(), params); @@ -172,14 +189,35 @@ public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapp private final String[] params; - private ParameterMappingPredicate(String[] params) { + private final Set methods = new HashSet(); + + public ParameterMappingPredicate(String[] params) { + this.params = params; + } + + public ParameterMappingPredicate(String[] params, RequestMethod[] methods) { this.params = params; + for (RequestMethod method : methods) { + this.methods.add(method.name()); + } } public boolean match(PortletRequest request) { return PortletAnnotationMappingUtils.checkParameters(this.params, request); } + public void validate(PortletRequest request) throws PortletException { + if (!this.methods.isEmpty()) { + if (!(request instanceof ClientDataRequest)) { + throw new PortletRequestMethodNotSupportedException(StringUtils.toStringArray(this.methods)); + } + String method = ((ClientDataRequest) request).getMethod(); + if (!this.methods.contains(method)) { + throw new PortletRequestMethodNotSupportedException(method, StringUtils.toStringArray(this.methods)); + } + } + } + public int compareTo(Object other) { if (other instanceof PortletRequestMappingPredicate) { return new Integer(((ParameterMappingPredicate) other).params.length).compareTo(this.params.length); diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/PortletAnnotationMappingUtils.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/PortletAnnotationMappingUtils.java index cb8edf7062aef850bd1d065b8b5364ceff042ce3..71c1db87d1adb6c083d9338cebd382fb1e530a45 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/PortletAnnotationMappingUtils.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/PortletAnnotationMappingUtils.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. @@ -16,6 +16,8 @@ package org.springframework.web.portlet.mvc.annotation; +import java.util.Set; +import javax.portlet.ClientDataRequest; import javax.portlet.PortletRequest; import org.springframework.util.ObjectUtils; @@ -30,7 +32,7 @@ import org.springframework.web.portlet.util.PortletUtils; abstract class PortletAnnotationMappingUtils { /** - * Check whether the given request matches the specified request methods. + * Check whether the given portlet modes matches the specified type-level modes. * @param modes the mapped portlet modes to check * @param typeLevelModes the type-level mode mappings to check against */ @@ -51,11 +53,29 @@ abstract class PortletAnnotationMappingUtils { return true; } + /** + * Check whether the given request matches the specified request methods. + * @param methods the request methods to check against + * @param request the current request to check + */ + public static boolean checkRequestMethod(Set methods, PortletRequest request) { + if (!methods.isEmpty()) { + if (!(request instanceof ClientDataRequest)) { + return false; + } + String method = ((ClientDataRequest) request).getMethod(); + if (!methods.contains(method)) { + return false; + } + } + return true; + } + /** * Check whether the given request matches the specified parameter conditions. * @param params the parameter conditions, following * {@link org.springframework.web.bind.annotation.RequestMapping#params()} - * @param request the current HTTP request to check + * @param request the current request to check */ public static boolean checkParameters(String[] params, PortletRequest request) { if (!ObjectUtils.isEmpty(params)) { diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/util/ActionRequestWrapper.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/util/ActionRequestWrapper.java deleted file mode 100644 index 3993af1f9c0176948bb1fb99bf6e7398e0d73938..0000000000000000000000000000000000000000 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/util/ActionRequestWrapper.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2002-2006 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.portlet.util; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; - -import javax.portlet.ActionRequest; - -/** - * Simple wrapper for a Portlet {@link javax.portlet.ActionRequest}, - * delegating all calls to the underlying request. - * - *

    (In the style of the Servlet API's {@link javax.servlet.http.HttpServletRequestWrapper}.) - * - * @author Juergen Hoeller - * @since 2.0 - * @see ActionRequestWrapper - * @see javax.servlet.http.HttpServletRequestWrapper - */ -public class ActionRequestWrapper extends PortletRequestWrapper implements ActionRequest { - - /** Original request that we're delegating to */ - private final ActionRequest actionRequest; - - - /** - * Create a ActionRequestWrapper for the given request. - * @param request the original request to wrap - * @throws IllegalArgumentException if the supplied request is null - */ - public ActionRequestWrapper(ActionRequest request) { - super(request); - this.actionRequest = request; - } - - - public InputStream getPortletInputStream() throws IOException { - return this.actionRequest.getPortletInputStream(); - } - - public void setCharacterEncoding(String enc) throws UnsupportedEncodingException { - this.actionRequest.setCharacterEncoding(enc); - } - - public BufferedReader getReader() throws IOException { - return this.actionRequest.getReader(); - } - - public String getCharacterEncoding() { - return this.actionRequest.getCharacterEncoding(); - } - - public String getContentType() { - return this.actionRequest.getContentType(); - } - - public int getContentLength() { - return this.actionRequest.getContentLength(); - } - -} diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/util/PortletRequestWrapper.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/util/PortletRequestWrapper.java deleted file mode 100644 index 9c65318e1320b8f50dcb57d1ae7c4773b476284d..0000000000000000000000000000000000000000 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/util/PortletRequestWrapper.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2002-2006 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.portlet.util; - -import java.util.Enumeration; -import java.util.Locale; -import java.util.Map; -import java.security.Principal; - -import javax.portlet.PortalContext; -import javax.portlet.PortletMode; -import javax.portlet.PortletPreferences; -import javax.portlet.PortletRequest; -import javax.portlet.PortletSession; -import javax.portlet.WindowState; - -import org.springframework.util.Assert; - -/** - * Simple wrapper for a {@link javax.portlet.PortletRequest}, delegating all - * calls to the underlying request. - * - *

    (In the style of the Servlet API's {@link javax.servlet.ServletRequestWrapper}.) - * - * @author Juergen Hoeller - * @since 2.0 - * @see ActionRequestWrapper - * @see javax.servlet.ServletRequestWrapper - */ -public class PortletRequestWrapper implements PortletRequest { - - /** Original request that we're delegating to */ - private final PortletRequest portletRequest; - - - /** - * Create a PortletRequestWrapper for the given {@link javax.portlet.PortletRequest}. - * @param request the original {@link javax.portlet.PortletRequest} to wrap - * @throws IllegalArgumentException if the supplied request is null - */ - public PortletRequestWrapper(PortletRequest request) { - Assert.notNull(request, "Request is required"); - this.portletRequest = request; - } - - - public boolean isWindowStateAllowed(WindowState state) { - return this.portletRequest.isWindowStateAllowed(state); - } - - public boolean isPortletModeAllowed(PortletMode mode) { - return this.portletRequest.isPortletModeAllowed(mode); - } - - public PortletMode getPortletMode() { - return this.portletRequest.getPortletMode(); - } - - public WindowState getWindowState() { - return this.portletRequest.getWindowState(); - } - - public PortletPreferences getPreferences() { - return this.portletRequest.getPreferences(); - } - - public PortletSession getPortletSession() { - return this.portletRequest.getPortletSession(); - } - - public PortletSession getPortletSession(boolean create) { - return this.portletRequest.getPortletSession(create); - } - - public String getProperty(String name) { - return this.portletRequest.getProperty(name); - } - - public Enumeration getProperties(String name) { - return this.portletRequest.getProperties(name); - } - - public Enumeration getPropertyNames() { - return this.portletRequest.getPropertyNames(); - } - - public PortalContext getPortalContext() { - return this.portletRequest.getPortalContext(); - } - - public String getAuthType() { - return this.portletRequest.getAuthType(); - } - - public String getContextPath() { - return this.portletRequest.getContextPath(); - } - - public String getRemoteUser() { - return this.portletRequest.getRemoteUser(); - } - - public Principal getUserPrincipal() { - return this.portletRequest.getUserPrincipal(); - } - - public boolean isUserInRole(String role) { - return this.portletRequest.isUserInRole(role); - } - - public Object getAttribute(String name) { - return this.portletRequest.getAttribute(name); - } - - public Enumeration getAttributeNames() { - return this.portletRequest.getAttributeNames(); - } - - public String getParameter(String name) { - return this.portletRequest.getParameter(name); - } - - public Enumeration getParameterNames() { - return this.portletRequest.getParameterNames(); - } - - public String[] getParameterValues(String name) { - return this.portletRequest.getParameterValues(name); - } - - public Map getParameterMap() { - return this.portletRequest.getParameterMap(); - } - - public boolean isSecure() { - return this.portletRequest.isSecure(); - } - - public void setAttribute(String name, Object value) { - this.portletRequest.setAttribute(name, value); - } - - public void removeAttribute(String name) { - this.portletRequest.removeAttribute(name); - } - - public String getRequestedSessionId() { - return this.portletRequest.getRequestedSessionId(); - } - - public boolean isRequestedSessionIdValid() { - return this.portletRequest.isRequestedSessionIdValid(); - } - - public String getResponseContentType() { - return this.portletRequest.getResponseContentType(); - } - - public Enumeration getResponseContentTypes() { - return this.portletRequest.getResponseContentTypes(); - } - - public Locale getLocale() { - return this.portletRequest.getLocale(); - } - - public Enumeration getLocales() { - return this.portletRequest.getLocales(); - } - - public String getScheme() { - return this.portletRequest.getScheme(); - } - - public String getServerName() { - return this.portletRequest.getServerName(); - } - - public int getServerPort() { - return this.portletRequest.getServerPort(); - } - -} diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockActionRequest.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockActionRequest.java index e85da537d15127fea1953f90c73f01987489ea23..a687ccb6715d31b75adb9d76a58fa9cbec118b4a 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockActionRequest.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockActionRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 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. @@ -16,14 +16,6 @@ package org.springframework.mock.web.portlet; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.UnsupportedEncodingException; - import javax.portlet.ActionRequest; import javax.portlet.PortalContext; import javax.portlet.PortletContext; @@ -36,25 +28,28 @@ import javax.portlet.PortletMode; * @author Juergen Hoeller * @since 2.0 */ -public class MockActionRequest extends MockPortletRequest implements ActionRequest { - - private String characterEncoding; - - private byte[] content; - - private String contentType; - +public class MockActionRequest extends MockClientDataRequest implements ActionRequest { /** * Create a new MockActionRequest with a default {@link MockPortalContext} * and a default {@link MockPortletContext}. - * @see MockPortalContext - * @see MockPortletContext + * @see org.springframework.mock.web.portlet.MockPortalContext + * @see org.springframework.mock.web.portlet.MockPortletContext */ public MockActionRequest() { super(); } + /** + * Create a new MockActionRequest with a default {@link MockPortalContext} + * and a default {@link MockPortletContext}. + * @param actionName the name of the action to trigger + */ + public MockActionRequest(String actionName) { + super(); + setParameter(ActionRequest.ACTION_NAME, actionName); + } + /** * Create a new MockActionRequest with a default {@link MockPortalContext} * and a default {@link MockPortletContext}. @@ -83,49 +78,9 @@ public class MockActionRequest extends MockPortletRequest implements ActionReque } - public void setContent(byte[] content) { - this.content = content; - } - - public InputStream getPortletInputStream() throws IOException { - if (this.content != null) { - return new ByteArrayInputStream(this.content); - } - else { - return null; - } - } - - public void setCharacterEncoding(String characterEncoding) { - this.characterEncoding = characterEncoding; - } - - public BufferedReader getReader() throws UnsupportedEncodingException { - if (this.content != null) { - InputStream sourceStream = new ByteArrayInputStream(this.content); - Reader sourceReader = (this.characterEncoding != null) ? - new InputStreamReader(sourceStream, this.characterEncoding) : new InputStreamReader(sourceStream); - return new BufferedReader(sourceReader); - } - else { - return null; - } - } - - public String getCharacterEncoding() { - return characterEncoding; - } - - public void setContentType(String contentType) { - this.contentType = contentType; - } - - public String getContentType() { - return contentType; - } - - public int getContentLength() { - return (this.content != null ? content.length : -1); + @Override + protected String getLifecyclePhase() { + return ACTION_PHASE; } } diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockActionResponse.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockActionResponse.java index e608e81c49f53e3dfb457fbf1c2ffd8935cfdc77..e9c8587745b3ebfe219ffb95ad51df80a305bf5a 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockActionResponse.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockActionResponse.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. @@ -17,7 +17,9 @@ package org.springframework.mock.web.portlet; import java.io.IOException; +import java.io.Serializable; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; @@ -27,6 +29,7 @@ import javax.portlet.PortletMode; import javax.portlet.PortletModeException; import javax.portlet.WindowState; import javax.portlet.WindowStateException; +import javax.xml.namespace.QName; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -38,16 +41,12 @@ import org.springframework.util.CollectionUtils; * @author Juergen Hoeller * @since 2.0 */ -public class MockActionResponse extends MockPortletResponse implements ActionResponse { +public class MockActionResponse extends MockStateAwareResponse implements ActionResponse { - private WindowState windowState; - - private PortletMode portletMode; + private boolean redirectAllowed = true; private String redirectedUrl; - private final Map renderParameters = new LinkedHashMap(); - /** * Create a new MockActionResponse with a default {@link MockPortalContext}. @@ -71,93 +70,60 @@ public class MockActionResponse extends MockPortletResponse implements ActionRes if (this.redirectedUrl != null) { throw new IllegalStateException("Cannot set WindowState after sendRedirect has been called"); } - if (!CollectionUtils.contains(getPortalContext().getSupportedWindowStates(), windowState)) { - throw new WindowStateException("WindowState not supported", windowState); - } - this.windowState = windowState; - } - - public WindowState getWindowState() { - return windowState; + super.setWindowState(windowState); + this.redirectAllowed = false; } public void setPortletMode(PortletMode portletMode) throws PortletModeException { if (this.redirectedUrl != null) { throw new IllegalStateException("Cannot set PortletMode after sendRedirect has been called"); } - if (!CollectionUtils.contains(getPortalContext().getSupportedPortletModes(), portletMode)) { - throw new PortletModeException("PortletMode not supported", portletMode); - } - this.portletMode = portletMode; - } - - public PortletMode getPortletMode() { - return portletMode; - } - - public void sendRedirect(String url) throws IOException { - if (this.windowState != null || this.portletMode != null || !this.renderParameters.isEmpty()) { - throw new IllegalStateException( - "Cannot call sendRedirect after windowState, portletMode, or renderParameters have been set"); - } - Assert.notNull(url, "Redirect URL must not be null"); - this.redirectedUrl = url; + super.setPortletMode(portletMode); + this.redirectAllowed = false; } - public String getRedirectedUrl() { - return redirectedUrl; - } - - public void setRenderParameters(Map parameters) { + public void setRenderParameters(Map parameters) { if (this.redirectedUrl != null) { throw new IllegalStateException("Cannot set render parameters after sendRedirect has been called"); } - Assert.notNull(parameters, "Parameters Map must not be null"); - this.renderParameters.clear(); - for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) { - Map.Entry entry = (Map.Entry) it.next(); - Assert.isTrue(entry.getKey() instanceof String, "Key must be of type String"); - Assert.isTrue(entry.getValue() instanceof String[], "Value must be of type String[]"); - this.renderParameters.put((String) entry.getKey(), (String[]) entry.getValue()); - } + super.setRenderParameters(parameters); + this.redirectAllowed = false; } public void setRenderParameter(String key, String value) { if (this.redirectedUrl != null) { throw new IllegalStateException("Cannot set render parameters after sendRedirect has been called"); } - Assert.notNull(key, "Parameter key must not be null"); - Assert.notNull(value, "Parameter value must not be null"); - this.renderParameters.put(key, new String[] {value}); - } - - public String getRenderParameter(String key) { - Assert.notNull(key, "Parameter key must not be null"); - String[] arr = this.renderParameters.get(key); - return (arr != null && arr.length > 0 ? arr[0] : null); + super.setRenderParameter(key, value); + this.redirectAllowed = false; } public void setRenderParameter(String key, String[] values) { if (this.redirectedUrl != null) { throw new IllegalStateException("Cannot set render parameters after sendRedirect has been called"); } - Assert.notNull(key, "Parameter key must not be null"); - Assert.notNull(values, "Parameter values must not be null"); - this.renderParameters.put(key, values); + super.setRenderParameter(key, values); + this.redirectAllowed = false; } - public String[] getRenderParameterValues(String key) { - Assert.notNull(key, "Parameter key must not be null"); - return this.renderParameters.get(key); + public void sendRedirect(String location) throws IOException { + if (!this.redirectAllowed) { + throw new IllegalStateException( + "Cannot call sendRedirect after windowState, portletMode, or renderParameters have been set"); + } + Assert.notNull(location, "Redirect URL must not be null"); + this.redirectedUrl = location; } - public Iterator getRenderParameterNames() { - return this.renderParameters.keySet().iterator(); + public void sendRedirect(String location, String renderUrlParamName) throws IOException { + sendRedirect(location); + if (renderUrlParamName != null) { + setRenderParameter(renderUrlParamName, location); + } } - public Map getRenderParameterMap() { - return Collections.unmodifiableMap(this.renderParameters); + public String getRedirectedUrl() { + return this.redirectedUrl; } - } diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockBaseURL.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockBaseURL.java new file mode 100644 index 0000000000000000000000000000000000000000..aad992058b9e697f232ab7c8bb6166fcc0bdbc1a --- /dev/null +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockBaseURL.java @@ -0,0 +1,152 @@ +/* + * 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.mock.web.portlet; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.URLEncoder; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import javax.portlet.BaseURL; +import javax.portlet.PortletSecurityException; + +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Mock implementation of the {@link javax.portlet.BaseURL} interface. + * + * @author Juergen Hoeller + * @since 3.0 + */ +public abstract class MockBaseURL implements BaseURL { + + public static final String URL_TYPE_RENDER = "render"; + + public static final String URL_TYPE_ACTION = "action"; + + private static final String ENCODING = "UTF-8"; + + + protected final Map parameters = new LinkedHashMap(); + + private boolean secure = false; + + private final Map properties = new LinkedHashMap(); + + + //--------------------------------------------------------------------- + // BaseURL methods + //--------------------------------------------------------------------- + + public void setParameter(String key, String value) { + Assert.notNull(key, "Parameter key must be null"); + Assert.notNull(value, "Parameter value must not be null"); + this.parameters.put(key, new String[] {value}); + } + + public void setParameter(String key, String[] values) { + Assert.notNull(key, "Parameter key must be null"); + Assert.notNull(values, "Parameter values must not be null"); + this.parameters.put(key, values); + } + + public void setParameters(Map parameters) { + Assert.notNull(parameters, "Parameters Map must not be null"); + this.parameters.clear(); + this.parameters.putAll(parameters); + } + + public Set getParameterNames() { + return this.parameters.keySet(); + } + + public String getParameter(String name) { + String[] arr = this.parameters.get(name); + return (arr != null && arr.length > 0 ? arr[0] : null); + } + + public String[] getParameterValues(String name) { + return this.parameters.get(name); + } + + public Map getParameterMap() { + return Collections.unmodifiableMap(this.parameters); + } + + public void setSecure(boolean secure) throws PortletSecurityException { + this.secure = secure; + } + + public boolean isSecure() { + return this.secure; + } + + public void write(Writer out) throws IOException { + out.write(toString()); + } + + public void write(Writer out, boolean escapeXML) throws IOException { + out.write(toString()); + } + + public void addProperty(String key, String value) { + String[] values = this.properties.get(key); + if (values != null) { + this.properties.put(key, StringUtils.addStringToArray(values, value)); + } + else { + this.properties.put(key, new String[] {value}); + } + } + + public void setProperty(String key, String value) { + this.properties.put(key, new String[] {value}); + } + + public Map getProperties() { + return Collections.unmodifiableMap(this.properties); + } + + + protected String encodeParameter(String name, String value) { + try { + return URLEncoder.encode(name, ENCODING) + "=" + URLEncoder.encode(value, ENCODING); + } + catch (UnsupportedEncodingException ex) { + return null; + } + } + + protected String encodeParameter(String name, String[] values) { + try { + StringBuilder sb = new StringBuilder(); + for (int i = 0, n = values.length; i < n; i++) { + sb.append(i > 0 ? ";" : "").append(URLEncoder.encode(name, ENCODING)).append("=") + .append(URLEncoder.encode(values[i], ENCODING)); + } + return sb.toString(); + } + catch (UnsupportedEncodingException ex) { + return null; + } + } + +} diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockCacheControl.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockCacheControl.java new file mode 100644 index 0000000000000000000000000000000000000000..45dcae7d5eee62afbc65579e9def6a27e611e13d --- /dev/null +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockCacheControl.java @@ -0,0 +1,70 @@ +/* + * 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.mock.web.portlet; + +import javax.portlet.CacheControl; + +/** + * Mock implementation of the {@link javax.portlet.CacheControl} interface. + * + * @author Juergen Hoeller + * @since 3.0 + */ +public class MockCacheControl implements CacheControl { + + private int expirationTime = 0; + + private boolean publicScope = false; + + private String etag; + + private boolean useCachedContent = false; + + + public int getExpirationTime() { + return this.expirationTime; + } + + public void setExpirationTime(int time) { + this.expirationTime = time; + } + + public boolean isPublicScope() { + return this.publicScope; + } + + public void setPublicScope(boolean publicScope) { + this.publicScope = publicScope; + } + + public String getETag() { + return this.etag; + } + + public void setETag(String token) { + this.etag = token; + } + + public boolean useCachedContent() { + return this.useCachedContent; + } + + public void setUseCachedContent(boolean useCachedContent) { + this.useCachedContent = useCachedContent; + } + +} diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockClientDataRequest.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockClientDataRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..4a71c7d7a832bfc9c86d92326d861ff64f4d1b0c --- /dev/null +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockClientDataRequest.java @@ -0,0 +1,129 @@ +/* + * 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.mock.web.portlet; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import javax.portlet.ClientDataRequest; +import javax.portlet.PortalContext; +import javax.portlet.PortletContext; +import javax.portlet.PortletMode; + +/** + * Mock implementation of the {@link javax.portlet.ClientDataRequest} interface. + * + * @author Juergen Hoeller + * @since 3.0 + */ +public class MockClientDataRequest extends MockPortletRequest implements ClientDataRequest { + + private String characterEncoding; + + private byte[] content; + + private String contentType; + + private String method; + + + /** + * Create a new MockClientDataRequest with a default {@link MockPortalContext} + * and a default {@link MockPortletContext}. + * @see org.springframework.mock.web.portlet.MockPortalContext + * @see org.springframework.mock.web.portlet.MockPortletContext + */ + public MockClientDataRequest() { + super(); + } + + /** + * Create a new MockClientDataRequest with a default {@link MockPortalContext}. + * @param portletContext the PortletContext that the request runs in + */ + public MockClientDataRequest(PortletContext portletContext) { + super(portletContext); + } + + /** + * Create a new MockClientDataRequest. + * @param portalContext the PortalContext that the request runs in + * @param portletContext the PortletContext that the request runs in + */ + public MockClientDataRequest(PortalContext portalContext, PortletContext portletContext) { + super(portalContext, portletContext); + } + + + public void setContent(byte[] content) { + this.content = content; + } + + public InputStream getPortletInputStream() throws IOException { + if (this.content != null) { + return new ByteArrayInputStream(this.content); + } + else { + return null; + } + } + + public void setCharacterEncoding(String characterEncoding) { + this.characterEncoding = characterEncoding; + } + + public BufferedReader getReader() throws UnsupportedEncodingException { + if (this.content != null) { + InputStream sourceStream = new ByteArrayInputStream(this.content); + Reader sourceReader = (this.characterEncoding != null) ? + new InputStreamReader(sourceStream, this.characterEncoding) : new InputStreamReader(sourceStream); + return new BufferedReader(sourceReader); + } + else { + return null; + } + } + + public String getCharacterEncoding() { + return this.characterEncoding; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + + public String getContentType() { + return this.contentType; + } + + public int getContentLength() { + return (this.content != null ? content.length : -1); + } + + public void setMethod(String method) { + this.method = method; + } + + public String getMethod() { + return this.method; + } + +} \ No newline at end of file diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockEvent.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..b58aced0ba5227c469f51e34530bd00b00290af8 --- /dev/null +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockEvent.java @@ -0,0 +1,88 @@ +/* + * 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.mock.web.portlet; + +import java.io.Serializable; +import javax.portlet.Event; +import javax.xml.namespace.QName; + +/** + * Mock implementation of the {@link javax.portlet.Event} interface. + * + * @author Juergen Hoeller + * @since 3.0 + * @see MockEventRequest + */ +public class MockEvent implements Event { + + private final QName name; + + private final Serializable value; + + + /** + * Create a new MockEvent with the given name. + * @param name the name of the event + */ + public MockEvent(QName name) { + this.name = name; + this.value = null; + } + + /** + * Create a new MockEvent with the given name and value. + * @param name the name of the event + * @param value the associated payload of the event + */ + public MockEvent(QName name, Serializable value) { + this.name = name; + this.value = value; + } + + /** + * Create a new MockEvent with the given name. + * @param name the name of the event + */ + public MockEvent(String name) { + this.name = new QName(name); + this.value = null; + } + + /** + * Create a new MockEvent with the given name and value. + * @param name the name of the event + * @param value the associated payload of the event + */ + public MockEvent(String name, Serializable value) { + this.name = new QName(name); + this.value = value; + } + + + public QName getQName() { + return this.name; + } + + public String getName() { + return this.name.getLocalPart(); + } + + public Serializable getValue() { + return this.value; + } + +} \ No newline at end of file diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockEventRequest.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockEventRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..2406f18d01ca83a574ab393635a4b4f11b264cc7 --- /dev/null +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockEventRequest.java @@ -0,0 +1,88 @@ +/* + * 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.mock.web.portlet; + +import javax.portlet.Event; +import javax.portlet.EventRequest; +import javax.portlet.PortalContext; +import javax.portlet.PortletContext; + +/** + * Mock implementation of the {@link javax.portlet.EventRequest} interface. + * + * @author Juergen Hoeller + * @since 3.0 + */ +public class MockEventRequest extends MockPortletRequest implements EventRequest { + + private final Event event; + + private String method; + + + /** + * Create a new MockEventRequest with a default {@link MockPortalContext} + * and a default {@link MockPortletContext}. + * @param event the event that this request wraps + * @see MockEvent + */ + public MockEventRequest(Event event) { + super(); + this.event = event; + } + + /** + * Create a new MockEventRequest with a default {@link MockPortalContext}. + * @param event the event that this request wraps + * @param portletContext the PortletContext that the request runs in + * @see MockEvent + */ + public MockEventRequest(Event event, PortletContext portletContext) { + super(portletContext); + this.event = event; + } + + /** + * Create a new MockEventRequest. + * @param event the event that this request wraps + * @param portalContext the PortletContext that the request runs in + * @param portletContext the PortletContext that the request runs in + */ + public MockEventRequest(Event event, PortalContext portalContext, PortletContext portletContext) { + super(portalContext, portletContext); + this.event = event; + } + + + @Override + protected String getLifecyclePhase() { + return EVENT_PHASE; + } + + public Event getEvent() { + return this.event; + } + + public void setMethod(String method) { + this.method = method; + } + + public String getMethod() { + return this.method; + } + +} \ No newline at end of file diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockEventResponse.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockEventResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..aa8dac4ce1e13fa50fe7b24d934ea1d353aab25f --- /dev/null +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockEventResponse.java @@ -0,0 +1,34 @@ +/* + * 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.mock.web.portlet; + +import javax.portlet.EventResponse; +import javax.portlet.EventRequest; + +/** + * Mock implementation of the {@link javax.portlet.EventResponse} interface. + * + * @author Juergen Hoeller + * @since 3.0 + */ +public class MockEventResponse extends MockStateAwareResponse implements EventResponse { + + public void setRenderParameters(EventRequest request) { + setRenderParameters(request.getParameterMap()); + } + +} diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockMimeResponse.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockMimeResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..e606fc808d290fbb7320f93ca8b5870c200fc9c9 --- /dev/null +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockMimeResponse.java @@ -0,0 +1,243 @@ +/* + * 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.mock.web.portlet; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Locale; +import javax.portlet.CacheControl; +import javax.portlet.MimeResponse; +import javax.portlet.PortalContext; +import javax.portlet.PortletRequest; +import javax.portlet.PortletURL; +import javax.portlet.ResourceURL; + +import org.springframework.util.CollectionUtils; +import org.springframework.web.util.WebUtils; + +/** + * Mock implementation of the {@link javax.portlet.MimeResponse} interface. + * + * @author Juergen Hoeller + * @since 3.0 + */ +public class MockMimeResponse extends MockPortletResponse implements MimeResponse { + + private PortletRequest request; + + private String contentType; + + private String characterEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING; + + private PrintWriter writer; + + private Locale locale = Locale.getDefault(); + + private int bufferSize = 4096; + + private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + private final CacheControl cacheControl = new MockCacheControl(); + + private boolean committed; + + private String includedUrl; + + private String forwardedUrl; + + + /** + * Create a new MockMimeResponse with a default {@link MockPortalContext}. + * @see org.springframework.mock.web.portlet.MockPortalContext + */ + public MockMimeResponse() { + super(); + } + + /** + * Create a new MockMimeResponse. + * @param portalContext the PortalContext defining the supported + * PortletModes and WindowStates + */ + public MockMimeResponse(PortalContext portalContext) { + super(portalContext); + } + + /** + * Create a new MockMimeResponse. + * @param portalContext the PortalContext defining the supported + * PortletModes and WindowStates + * @param request the corresponding render/resource request that this response + * is being generated for + */ + public MockMimeResponse(PortalContext portalContext, PortletRequest request) { + super(portalContext); + this.request = request; + } + + + //--------------------------------------------------------------------- + // RenderResponse methods + //--------------------------------------------------------------------- + + public void setContentType(String contentType) { + if (this.request != null) { + Enumeration supportedTypes = this.request.getResponseContentTypes(); + if (!CollectionUtils.contains(supportedTypes, contentType)) { + throw new IllegalArgumentException("Content type [" + contentType + "] not in supported list: " + + Collections.list(supportedTypes)); + } + } + this.contentType = contentType; + } + + public String getContentType() { + return this.contentType; + } + + public void setCharacterEncoding(String characterEncoding) { + this.characterEncoding = characterEncoding; + } + + public String getCharacterEncoding() { + return this.characterEncoding; + } + + public PrintWriter getWriter() throws UnsupportedEncodingException { + if (this.writer == null) { + Writer targetWriter = (this.characterEncoding != null + ? new OutputStreamWriter(this.outputStream, this.characterEncoding) + : new OutputStreamWriter(this.outputStream)); + this.writer = new PrintWriter(targetWriter); + } + return this.writer; + } + + public byte[] getContentAsByteArray() { + flushBuffer(); + return this.outputStream.toByteArray(); + } + + public String getContentAsString() throws UnsupportedEncodingException { + flushBuffer(); + return (this.characterEncoding != null) + ? this.outputStream.toString(this.characterEncoding) + : this.outputStream.toString(); + } + + public void setLocale(Locale locale) { + this.locale = locale; + } + + public Locale getLocale() { + return this.locale; + } + + public void setBufferSize(int bufferSize) { + this.bufferSize = bufferSize; + } + + public int getBufferSize() { + return this.bufferSize; + } + + public void flushBuffer() { + if (this.writer != null) { + this.writer.flush(); + } + if (this.outputStream != null) { + try { + this.outputStream.flush(); + } + catch (IOException ex) { + throw new IllegalStateException("Could not flush OutputStream: " + ex.getMessage()); + } + } + this.committed = true; + } + + public void resetBuffer() { + if (this.committed) { + throw new IllegalStateException("Cannot reset buffer - response is already committed"); + } + this.outputStream.reset(); + } + + public void setCommitted(boolean committed) { + this.committed = committed; + } + + public boolean isCommitted() { + return this.committed; + } + + public void reset() { + resetBuffer(); + this.characterEncoding = null; + this.contentType = null; + this.locale = null; + } + + public OutputStream getPortletOutputStream() throws IOException { + return this.outputStream; + } + + public PortletURL createRenderURL() { + return new MockPortletURL(getPortalContext(), MockPortletURL.URL_TYPE_RENDER); + } + + public PortletURL createActionURL() { + return new MockPortletURL(getPortalContext(), MockPortletURL.URL_TYPE_ACTION); + } + + public ResourceURL createResourceURL() { + return new MockResourceURL(); + } + + public CacheControl getCacheControl() { + return this.cacheControl; + } + + + //--------------------------------------------------------------------- + // Methods for MockPortletRequestDispatcher + //--------------------------------------------------------------------- + + public void setIncludedUrl(String includedUrl) { + this.includedUrl = includedUrl; + } + + public String getIncludedUrl() { + return this.includedUrl; + } + + public void setForwardedUrl(String forwardedUrl) { + this.forwardedUrl = forwardedUrl; + } + + public String getForwardedUrl() { + return this.forwardedUrl; + } + +} diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockMultipartActionRequest.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockMultipartActionRequest.java index cc2b2277cffbb8c3b5ad0ef1020c47c409a114d4..50d095d3e146a762f9ad3e6b58b012cb2db9fccc 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockMultipartActionRequest.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockMultipartActionRequest.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. diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortalContext.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortalContext.java index 1a81847f978932b6f898fd55e98dc36f22346ee1..949d077b9a26fccaf50d0c35aed54f7e552d6bf2 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortalContext.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortalContext.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. diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletConfig.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletConfig.java index 249429e1a1307e76ff727d88cb65367e597c911e..d69e75d0c111de79bcee85f2ddd88c178728eed1 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletConfig.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletConfig.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. @@ -16,16 +16,19 @@ package org.springframework.mock.web.portlet; +import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.Locale; import java.util.Map; -import java.util.Properties; import java.util.ResourceBundle; -import java.util.LinkedHashMap; -import java.util.Collections; +import java.util.Set; import javax.portlet.PortletConfig; import javax.portlet.PortletContext; +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; import org.springframework.util.Assert; @@ -46,6 +49,18 @@ public class MockPortletConfig implements PortletConfig { private final Map initParameters = new LinkedHashMap(); + private final Set publicRenderParameterNames = new LinkedHashSet(); + + private String defaultNamespace = XMLConstants.NULL_NS_URI; + + private final Set publishingEventQNames = new LinkedHashSet(); + + private final Set processingEventQNames = new LinkedHashSet(); + + private final Set supportedLocales = new LinkedHashSet(); + + private final Map containerRuntimeOptions = new LinkedHashMap(); + /** * Create a new MockPortletConfig with a default {@link MockPortletContext}. @@ -113,4 +128,56 @@ public class MockPortletConfig implements PortletConfig { return Collections.enumeration(this.initParameters.keySet()); } + public void addPublicRenderParameterName(String name) { + this.publicRenderParameterNames.add(name); + } + + public Enumeration getPublicRenderParameterNames() { + return Collections.enumeration(this.publicRenderParameterNames); + } + + public void setDefaultNamespace(String defaultNamespace) { + this.defaultNamespace = defaultNamespace; + } + + public String getDefaultNamespace() { + return this.defaultNamespace; + } + + public void addPublishingEventQName(QName name) { + this.publishingEventQNames.add(name); + } + + public Enumeration getPublishingEventQNames() { + return Collections.enumeration(this.publishingEventQNames); + } + + public void addProcessingEventQName(QName name) { + this.processingEventQNames.add(name); + } + + public Enumeration getProcessingEventQNames() { + return Collections.enumeration(this.processingEventQNames); + } + + public void addSupportedLocale(Locale locale) { + this.supportedLocales.add(locale); + } + + public Enumeration getSupportedLocales() { + return Collections.enumeration(this.supportedLocales); + } + + public void addContainerRuntimeOption(String key, String value) { + this.containerRuntimeOptions.put(key, new String[] {value}); + } + + public void addContainerRuntimeOption(String key, String[] values) { + this.containerRuntimeOptions.put(key, values); + } + + public Map getContainerRuntimeOptions() { + return Collections.unmodifiableMap(this.containerRuntimeOptions); + } + } diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletContext.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletContext.java index 336b21e95b4e30d6ae291a929974bd2777f7dc6b..f297a637de87ada1b7110cf427ba7ddfe1468659 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletContext.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletContext.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. @@ -25,6 +25,7 @@ import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import javax.portlet.PortletContext; @@ -63,6 +64,8 @@ public class MockPortletContext implements PortletContext { private String portletContextName = "MockPortletContext"; + private Set containerRuntimeOptions = new LinkedHashSet(); + /** * Create a new MockPortletContext with no base path and a @@ -248,7 +251,15 @@ public class MockPortletContext implements PortletContext { } public String getPortletContextName() { - return portletContextName; + return this.portletContextName; + } + + public void addContainerRuntimeOption(String key) { + this.containerRuntimeOptions.add(key); + } + + public Enumeration getContainerRuntimeOptions() { + return Collections.enumeration(this.containerRuntimeOptions); } } diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletPreferences.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletPreferences.java index 122ead007e61d710884403375e066df3474bf5be..ec9574491cf76c4836c1980930b419ebee5a262c 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletPreferences.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletPreferences.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. diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletRequest.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletRequest.java index 84c8af733f330eeb1a92aa80e71f945dc189597e..92973e5f1afd4c514552571f395baeaaa709a7f3 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletRequest.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletRequest.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. @@ -33,6 +33,7 @@ import javax.portlet.PortletPreferences; import javax.portlet.PortletRequest; import javax.portlet.PortletSession; import javax.portlet.WindowState; +import javax.servlet.http.Cookie; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -90,6 +91,12 @@ public class MockPortletRequest implements PortletRequest { private int serverPort = 80; + private String windowID; + + private Cookie[] cookies; + + private final Set publicParameterNames = new HashSet(); + /** * Create a new MockPortletRequest with a default {@link MockPortalContext} @@ -120,6 +127,7 @@ public class MockPortletRequest implements PortletRequest { this.portletContext = (portletContext != null ? portletContext : new MockPortletContext()); this.responseContentTypes.add("text/html"); this.locales.add(Locale.ENGLISH); + this.attributes.put(LIFECYCLE_PHASE, getLifecyclePhase()); } @@ -127,6 +135,13 @@ public class MockPortletRequest implements PortletRequest { // Lifecycle methods //--------------------------------------------------------------------- + /** + * Return the Portlet 2.0 lifecycle id for the current phase. + */ + protected String getLifecyclePhase() { + return null; + } + /** * Return whether this request is still active (that is, not completed yet). */ @@ -363,7 +378,7 @@ public class MockPortletRequest implements PortletRequest { return this.parameters.get(name); } - public Map getParameterMap() { + public Map getParameterMap() { return Collections.unmodifiableMap(this.parameters); } @@ -459,4 +474,54 @@ public class MockPortletRequest implements PortletRequest { return this.serverPort; } + public void setWindowID(String windowID) { + this.windowID = windowID; + } + + public String getWindowID() { + return this.windowID; + } + + public void setCookies(Cookie[] cookies) { + this.cookies = cookies; + } + + public Cookie[] getCookies() { + return this.cookies; + } + + public Map getPrivateParameterMap() { + if (!this.publicParameterNames.isEmpty()) { + Map filtered = new LinkedHashMap(); + for (String key : this.parameters.keySet()) { + if (!this.publicParameterNames.contains(key)) { + filtered.put(key, this.parameters.get(key)); + } + } + return filtered; + } + else { + return Collections.unmodifiableMap(this.parameters); + } + } + + public Map getPublicParameterMap() { + if (!this.publicParameterNames.isEmpty()) { + Map filtered = new LinkedHashMap(); + for (String key : this.parameters.keySet()) { + if (this.publicParameterNames.contains(key)) { + filtered.put(key, this.parameters.get(key)); + } + } + return filtered; + } + else { + return Collections.emptyMap(); + } + } + + public void registerPublicParameter(String name) { + this.publicParameterNames.add(name); + } + } diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletRequestDispatcher.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletRequestDispatcher.java index 2703c1920c2e8c85fc64fb0300296e2a4e973145..ca8695f90e266f7e3d29d12406b7117658150682 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletRequestDispatcher.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletRequestDispatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 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. @@ -17,9 +17,10 @@ package org.springframework.mock.web.portlet; import java.io.IOException; - import javax.portlet.PortletException; +import javax.portlet.PortletRequest; import javax.portlet.PortletRequestDispatcher; +import javax.portlet.PortletResponse; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; @@ -53,15 +54,31 @@ public class MockPortletRequestDispatcher implements PortletRequestDispatcher { public void include(RenderRequest request, RenderResponse response) throws PortletException, IOException { + include((PortletRequest) request, (PortletResponse) response); + } + + public void include(PortletRequest request, PortletResponse response) throws PortletException, IOException { Assert.notNull(request, "Request must not be null"); Assert.notNull(response, "Response must not be null"); - if (!(response instanceof MockRenderResponse)) { - throw new IllegalArgumentException("MockPortletRequestDispatcher requires MockRenderResponse"); + if (!(response instanceof MockMimeResponse)) { + throw new IllegalArgumentException("MockPortletRequestDispatcher requires MockMimeResponse"); } - ((MockRenderResponse) response).setIncludedUrl(this.url); + ((MockMimeResponse) response).setIncludedUrl(this.url); if (logger.isDebugEnabled()) { logger.debug("MockPortletRequestDispatcher: including URL [" + this.url + "]"); + } } + + public void forward(PortletRequest request, PortletResponse response) throws PortletException, IOException { + Assert.notNull(request, "Request must not be null"); + Assert.notNull(response, "Response must not be null"); + if (!(response instanceof MockMimeResponse)) { + throw new IllegalArgumentException("MockPortletRequestDispatcher requires MockMimeResponse"); + } + ((MockMimeResponse) response).setForwardedUrl(this.url); + if (logger.isDebugEnabled()) { + logger.debug("MockPortletRequestDispatcher: forwarding to URL [" + this.url + "]"); + } } } diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletResponse.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletResponse.java index 2c94ddda4a5c281925c66e473bc1ba503e150ac6..b556209fcd37ba280acb190da10c260c28405145 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletResponse.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletResponse.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. @@ -16,12 +16,20 @@ package org.springframework.mock.web.portlet; +import java.util.Collections; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; - import javax.portlet.PortalContext; import javax.portlet.PortletResponse; +import javax.servlet.http.Cookie; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; import org.springframework.util.Assert; @@ -38,6 +46,14 @@ public class MockPortletResponse implements PortletResponse { private final Map properties = new LinkedHashMap(); + private String namespace = ""; + + private final Set cookies = new LinkedHashSet(); + + private final Map xmlProperties = new LinkedHashMap(); + + private Document xmlDocument; + /** * Create a new MockPortletResponse with a default {@link MockPortalContext}. @@ -88,8 +104,8 @@ public class MockPortletResponse implements PortletResponse { this.properties.put(key, new String[] {value}); } - public Set getPropertyNames() { - return this.properties.keySet(); + public Set getPropertyNames() { + return Collections.unmodifiableSet(this.properties.keySet()); } public String getProperty(String key) { @@ -107,4 +123,73 @@ public class MockPortletResponse implements PortletResponse { return path; } + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public String getNamespace() { + return this.namespace; + } + + public void addProperty(Cookie cookie) { + Assert.notNull(cookie, "Cookie must not be null"); + this.cookies.add(cookie); + } + + public Cookie[] getCookies() { + return this.cookies.toArray(new Cookie[this.cookies.size()]); + } + + public Cookie getCookie(String name) { + Assert.notNull(name, "Cookie name must not be null"); + for (Cookie cookie : this.cookies) { + if (name.equals(cookie.getName())) { + return cookie; + } + } + return null; + } + + public void addProperty(String key, Element value) { + Assert.notNull(key, "Property key must not be null"); + Element[] oldArr = this.xmlProperties.get(key); + if (oldArr != null) { + Element[] newArr = new Element[oldArr.length + 1]; + System.arraycopy(oldArr, 0, newArr, 0, oldArr.length); + newArr[oldArr.length] = value; + this.xmlProperties.put(key, newArr); + } + else { + this.xmlProperties.put(key, new Element[] {value}); + } + } + + + public Set getXmlPropertyNames() { + return Collections.unmodifiableSet(this.xmlProperties.keySet()); + } + + public Element getXmlProperty(String key) { + Assert.notNull(key, "Property key must not be null"); + Element[] arr = this.xmlProperties.get(key); + return (arr != null && arr.length > 0 ? arr[0] : null); + } + + public Element[] getXmlProperties(String key) { + Assert.notNull(key, "Property key must not be null"); + return this.xmlProperties.get(key); + } + + public Element createElement(String tagName) throws DOMException { + if (this.xmlDocument == null) { + try { + this.xmlDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + } + catch (ParserConfigurationException ex) { + throw new DOMException(DOMException.INVALID_STATE_ERR, ex.toString()); + } + } + return this.xmlDocument.createElement(tagName); + } + } diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletSession.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletSession.java index ab27defe000aa8a20572706b2ac06c49cf73f856..558961121d1877f80e2ece9eba06e14f4221a0b5 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletSession.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletSession.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. @@ -184,7 +184,23 @@ public class MockPortletSession implements PortletSession { } public PortletContext getPortletContext() { - return portletContext; + return this.portletContext; + } + + public Map getAttributeMap() { + return Collections.unmodifiableMap(this.portletAttributes); + } + + public Map getAttributeMap(int scope) { + if (scope == PortletSession.PORTLET_SCOPE) { + return Collections.unmodifiableMap(this.portletAttributes); + } + else if (scope == PortletSession.APPLICATION_SCOPE) { + return Collections.unmodifiableMap(this.applicationAttributes); + } + else { + return Collections.emptyMap(); + } } } diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletURL.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletURL.java index edf889cf93e083eed466ba9f982588660f32a68a..12abdf57c93165717064d85bdbeebb0b1625610b 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletURL.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockPortletURL.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. @@ -16,18 +16,10 @@ package org.springframework.mock.web.portlet; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.Map; -import java.util.Set; - import javax.portlet.PortalContext; import javax.portlet.PortletMode; import javax.portlet.PortletModeException; -import javax.portlet.PortletSecurityException; import javax.portlet.PortletURL; import javax.portlet.WindowState; import javax.portlet.WindowStateException; @@ -42,14 +34,12 @@ import org.springframework.util.CollectionUtils; * @author Juergen Hoeller * @since 2.0 */ -public class MockPortletURL implements PortletURL { +public class MockPortletURL extends MockBaseURL implements PortletURL { public static final String URL_TYPE_RENDER = "render"; public static final String URL_TYPE_ACTION = "action"; - private static final String ENCODING = "UTF-8"; - private final PortalContext portalContext; @@ -59,10 +49,6 @@ public class MockPortletURL implements PortletURL { private PortletMode portletMode; - private final Map parameters = new LinkedHashMap(); - - private boolean secure = false; - /** * Create a new MockPortletURL for the given URL type. @@ -90,6 +76,10 @@ public class MockPortletURL implements PortletURL { this.windowState = windowState; } + public WindowState getWindowState() { + return this.windowState; + } + public void setPortletMode(PortletMode portletMode) throws PortletModeException { if (!CollectionUtils.contains(this.portalContext.getSupportedPortletModes(), portletMode)) { throw new PortletModeException("PortletMode not supported", portletMode); @@ -97,77 +87,12 @@ public class MockPortletURL implements PortletURL { this.portletMode = portletMode; } - public void setParameter(String key, String value) { - Assert.notNull(key, "Parameter key must be null"); - Assert.notNull(value, "Parameter value must not be null"); - this.parameters.put(key, new String[] {value}); - } - - public void setParameter(String key, String[] values) { - Assert.notNull(key, "Parameter key must be null"); - Assert.notNull(values, "Parameter values must not be null"); - this.parameters.put(key, values); - } - - public void setParameters(Map parameters) { - Assert.notNull(parameters, "Parameters Map must not be null"); - this.parameters.clear(); - for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) { - Map.Entry entry = (Map.Entry) it.next(); - Assert.isTrue(entry.getKey() instanceof String, "Key must be of type String"); - Assert.isTrue(entry.getValue() instanceof String[], "Value must be of type String[]"); - this.parameters.put((String) entry.getKey(), (String[]) entry.getValue()); - } - } - - public Set getParameterNames() { - return this.parameters.keySet(); - } - - public String getParameter(String name) { - String[] arr = this.parameters.get(name); - return (arr != null && arr.length > 0 ? arr[0] : null); + public PortletMode getPortletMode() { + return this.portletMode; } - public String[] getParameterValues(String name) { - return this.parameters.get(name); - } - - public Map getParameterMap() { - return Collections.unmodifiableMap(this.parameters); - } - - public void setSecure(boolean secure) throws PortletSecurityException { - this.secure = secure; - } - - public boolean isSecure() { - return this.secure; - } - - - private String encodeParameter(String name, String value) { - try { - return URLEncoder.encode(name, ENCODING) + "=" + URLEncoder.encode(value, ENCODING); - } - catch (UnsupportedEncodingException ex) { - return null; - } - } - - private String encodeParameter(String name, String[] values) { - try { - StringBuilder sb = new StringBuilder(); - for (int i = 0, n = values.length; i < n; i++) { - sb.append((i > 0 ? ";" : "") + - URLEncoder.encode(name, ENCODING) + "=" + - URLEncoder.encode(values[i], ENCODING)); - } - return sb.toString(); - } - catch (UnsupportedEncodingException ex) { - return null; - } + public void removePublicRenderParameter(String name) { + this.parameters.remove(name); } @@ -183,7 +108,7 @@ public class MockPortletURL implements PortletURL { for (Map.Entry entry : this.parameters.entrySet()) { sb.append(";").append(encodeParameter("param_" + entry.getKey(), entry.getValue())); } - return (this.secure ? "https:" : "http:") + + return (isSecure() ? "https:" : "http:") + "//localhost/mockportlet?" + sb.toString(); } diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockRenderRequest.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockRenderRequest.java index 5bb8c54e42d7966c0457c5a2fb4cf4c1f2f29d6c..d86ba7f2ad4443e538656b22d54c801b1fb0ed28 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockRenderRequest.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockRenderRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 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. @@ -20,6 +20,7 @@ import javax.portlet.PortalContext; import javax.portlet.PortletContext; import javax.portlet.PortletMode; import javax.portlet.RenderRequest; +import javax.portlet.WindowState; /** * Mock implementation of the {@link javax.portlet.RenderRequest} interface. @@ -50,6 +51,18 @@ public class MockRenderRequest extends MockPortletRequest implements RenderReque setPortletMode(portletMode); } + /** + * Create a new MockRenderRequest with a default {@link MockPortalContext} + * and a default {@link MockPortletContext}. + * @param portletMode the mode that the portlet runs in + * @param windowState the window state to run the portlet in + */ + public MockRenderRequest(PortletMode portletMode, WindowState windowState) { + super(); + setPortletMode(portletMode); + setWindowState(windowState); + } + /** * Create a new MockRenderRequest with a default {@link MockPortalContext}. * @param portletContext the PortletContext that the request runs in @@ -67,4 +80,14 @@ public class MockRenderRequest extends MockPortletRequest implements RenderReque super(portalContext, portletContext); } + + @Override + protected String getLifecyclePhase() { + return RENDER_PHASE; + } + + public String getETag() { + return getProperty(RenderRequest.ETAG); + } + } diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockRenderResponse.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockRenderResponse.java index c63e9a4459996cb32bb96bb051e307410bde3279..a2feeb3726df68b589e951cc7d09818fabf4cd4f 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockRenderResponse.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockRenderResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 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. @@ -16,21 +16,12 @@ package org.springframework.mock.web.portlet; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; -import java.io.Writer; -import java.util.Locale; - +import java.util.Collection; import javax.portlet.PortalContext; -import javax.portlet.PortletURL; +import javax.portlet.PortletMode; +import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; -import org.springframework.web.util.WebUtils; - /** * Mock implementation of the {@link javax.portlet.RenderResponse} interface. * @@ -38,27 +29,11 @@ import org.springframework.web.util.WebUtils; * @author Juergen Hoeller * @since 2.0 */ -public class MockRenderResponse extends MockPortletResponse implements RenderResponse { - - private String contentType; - - private String namespace = "MockPortlet"; +public class MockRenderResponse extends MockMimeResponse implements RenderResponse { private String title; - private String characterEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING; - - private PrintWriter writer; - - private Locale locale = Locale.getDefault(); - - private int bufferSize = 4096; - - private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - - private boolean committed; - - private String includedUrl; + private Collection nextPossiblePortletModes; /** @@ -78,139 +53,36 @@ public class MockRenderResponse extends MockPortletResponse implements RenderRes super(portalContext); } + /** + * Create a new MockRenderResponse. + * @param portalContext the PortalContext defining the supported + * PortletModes and WindowStates + * @param request the corresponding render request that this response + * is generated for + */ + public MockRenderResponse(PortalContext portalContext, RenderRequest request) { + super(portalContext, request); + } + //--------------------------------------------------------------------- // RenderResponse methods //--------------------------------------------------------------------- - public String getContentType() { - return this.contentType; - } - - public PortletURL createRenderURL() { - PortletURL url = new MockPortletURL(getPortalContext(), MockPortletURL.URL_TYPE_RENDER); - return url; - } - - public PortletURL createActionURL() { - PortletURL url = new MockPortletURL(getPortalContext(), MockPortletURL.URL_TYPE_ACTION); - return url; - } - - public String getNamespace() { - return this.namespace; - } - public void setTitle(String title) { this.title = title; } public String getTitle() { - return title; - } - - public void setContentType(String contentType) { - this.contentType = contentType; - } - - public void setCharacterEncoding(String characterEncoding) { - this.characterEncoding = characterEncoding; - } - - public String getCharacterEncoding() { - return this.characterEncoding; + return this.title; } - public PrintWriter getWriter() throws UnsupportedEncodingException { - if (this.writer == null) { - Writer targetWriter = (this.characterEncoding != null - ? new OutputStreamWriter(this.outputStream, this.characterEncoding) - : new OutputStreamWriter(this.outputStream)); - this.writer = new PrintWriter(targetWriter); - } - return this.writer; - } - - public byte[] getContentAsByteArray() { - flushBuffer(); - return this.outputStream.toByteArray(); - } - - public String getContentAsString() throws UnsupportedEncodingException { - flushBuffer(); - return (this.characterEncoding != null) - ? this.outputStream.toString(this.characterEncoding) - : this.outputStream.toString(); - } - - public void setLocale(Locale locale) { - this.locale = locale; - } - - public Locale getLocale() { - return this.locale; - } - - public void setBufferSize(int bufferSize) { - this.bufferSize = bufferSize; - } - - public int getBufferSize() { - return this.bufferSize; - } - - public void flushBuffer() { - if (this.writer != null) { - this.writer.flush(); - } - if (this.outputStream != null) { - try { - this.outputStream.flush(); - } - catch (IOException ex) { - throw new IllegalStateException("Could not flush OutputStream: " + ex.getMessage()); - } - } - this.committed = true; - } - - public void resetBuffer() { - if (this.committed) { - throw new IllegalStateException("Cannot reset buffer - response is already committed"); - } - this.outputStream.reset(); - } - - public void setCommitted(boolean committed) { - this.committed = committed; - } - - public boolean isCommitted() { - return this.committed; - } - - public void reset() { - resetBuffer(); - this.characterEncoding = null; - this.contentType = null; - this.locale = null; - } - - public OutputStream getPortletOutputStream() throws IOException { - return this.outputStream; - } - - - //--------------------------------------------------------------------- - // Methods for MockPortletRequestDispatcher - //--------------------------------------------------------------------- - - public void setIncludedUrl(String includedUrl) { - this.includedUrl = includedUrl; + public void setNextPossiblePortletModes(Collection portletModes) { + this.nextPossiblePortletModes = portletModes; } - public String getIncludedUrl() { - return includedUrl; + public Collection getNextPossiblePortletModes() { + return this.nextPossiblePortletModes; } } diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockResourceRequest.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockResourceRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..7cce4ba3c6d09dceb6a8f98b1b968c56c9aab15c --- /dev/null +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockResourceRequest.java @@ -0,0 +1,128 @@ +/* + * 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.mock.web.portlet; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import javax.portlet.PortalContext; +import javax.portlet.PortletContext; +import javax.portlet.RenderRequest; +import javax.portlet.ResourceRequest; + +/** + * Mock implementation of the {@link javax.portlet.ResourceRequest} interface. + * + * @author Juergen Hoeller + * @since 3.0 + */ +public class MockResourceRequest extends MockClientDataRequest implements ResourceRequest { + + private String resourceID; + + private String cacheability; + + private final Map privateRenderParameterMap = new LinkedHashMap(); + + + /** + * Create a new MockResourceRequest with a default {@link MockPortalContext} + * and a default {@link MockPortletContext}. + * @see org.springframework.mock.web.portlet.MockPortalContext + * @see org.springframework.mock.web.portlet.MockPortletContext + */ + public MockResourceRequest() { + super(); + } + + /** + * Create a new MockResourceRequest with a default {@link MockPortalContext} + * and a default {@link MockPortletContext}. + * @param resourceID the resource id for this request + */ + public MockResourceRequest(String resourceID) { + super(); + this.resourceID = resourceID; + } + + /** + * Create a new MockResourceRequest with a default {@link MockPortalContext} + * and a default {@link MockPortletContext}. + * @param url the resource URL for this request + */ + public MockResourceRequest(MockResourceURL url) { + super(); + this.resourceID = url.getResourceID(); + this.cacheability = url.getCacheability(); + } + + /** + * Create a new MockResourceRequest with a default {@link MockPortalContext}. + * @param portletContext the PortletContext that the request runs in + */ + public MockResourceRequest(PortletContext portletContext) { + super(portletContext); + } + + /** + * Create a new MockResourceRequest. + * @param portalContext the PortalContext that the request runs in + * @param portletContext the PortletContext that the request runs in + */ + public MockResourceRequest(PortalContext portalContext, PortletContext portletContext) { + super(portalContext, portletContext); + } + + + @Override + protected String getLifecyclePhase() { + return RESOURCE_PHASE; + } + + public void setResourceID(String resourceID) { + this.resourceID = resourceID; + } + + public String getResourceID() { + return this.resourceID; + } + + public void setCacheability(String cacheLevel) { + this.cacheability = cacheLevel; + } + + public String getCacheability() { + return this.cacheability; + } + + public String getETag() { + return getProperty(RenderRequest.ETAG); + } + + public void addPrivateRenderParameter(String key, String value) { + this.privateRenderParameterMap.put(key, new String[] {value}); + } + + public void addPrivateRenderParameter(String key, String[] values) { + this.privateRenderParameterMap.put(key, values); + } + + public Map getPrivateRenderParameterMap() { + return Collections.unmodifiableMap(this.privateRenderParameterMap); + } + +} diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockResourceResponse.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockResourceResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..297a19682eac9ee5341cfd5936e19ea48cd1d1ec --- /dev/null +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockResourceResponse.java @@ -0,0 +1,40 @@ +/* + * 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.mock.web.portlet; + +import javax.portlet.ResourceResponse; + +/** + * Mock implementation of the {@link javax.portlet.ResourceResponse} interface. + * + * @author Juergen Hoeller + * @since 3.0 + */ +public class MockResourceResponse extends MockMimeResponse implements ResourceResponse { + + private int contentLength = 0; + + + public void setContentLength(int len) { + this.contentLength = len; + } + + public int getContentLength() { + return this.contentLength; + } + +} diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockResourceURL.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockResourceURL.java new file mode 100644 index 0000000000000000000000000000000000000000..cce36b18d55265b385eed162ce5207e2022250c0 --- /dev/null +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockResourceURL.java @@ -0,0 +1,69 @@ +/* + * 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.mock.web.portlet; + +import java.util.Map; +import javax.portlet.ResourceURL; + +/** + * Mock implementation of the {@link javax.portlet.ResourceURL} interface. + * + * @author Juergen Hoeller + * @since 3.0 + */ +public class MockResourceURL extends MockBaseURL implements ResourceURL { + + private String resourceID; + + private String cacheability; + + + //--------------------------------------------------------------------- + // ResourceURL methods + //--------------------------------------------------------------------- + + public void setResourceID(String resourceID) { + this.resourceID = resourceID; + } + + public String getResourceID() { + return this.resourceID; + } + + public void setCacheability(String cacheLevel) { + this.cacheability = cacheLevel; + } + + public String getCacheability() { + return this.cacheability; + } + + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(encodeParameter("resourceID", this.resourceID)); + if (this.cacheability != null) { + sb.append(";").append(encodeParameter("cacheability", this.cacheability)); + } + for (Map.Entry entry : this.parameters.entrySet()) { + sb.append(";").append(encodeParameter("param_" + entry.getKey(), entry.getValue())); + } + return (isSecure() ? "https:" : "http:") + + "//localhost/mockportlet?" + sb.toString(); + } + +} diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockStateAwareResponse.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockStateAwareResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..131110971d4aca76997c04993d56a552a46f0fc2 --- /dev/null +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockStateAwareResponse.java @@ -0,0 +1,156 @@ +/* + * 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.mock.web.portlet; + +import java.io.IOException; +import java.io.Serializable; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import javax.portlet.ActionResponse; +import javax.portlet.PortalContext; +import javax.portlet.PortletMode; +import javax.portlet.PortletModeException; +import javax.portlet.WindowState; +import javax.portlet.WindowStateException; +import javax.portlet.StateAwareResponse; +import javax.xml.namespace.QName; + +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; + +/** + * Mock implementation of the {@link javax.portlet.StateAwareResponse} interface. + * + * @author Juergen Hoeller + * @since 3.0 + */ +public class MockStateAwareResponse extends MockPortletResponse implements StateAwareResponse { + + private WindowState windowState; + + private PortletMode portletMode; + + private final Map renderParameters = new LinkedHashMap(); + + private final Map events = new HashMap(); + + + /** + * Create a new MockActionResponse with a default {@link MockPortalContext}. + * @see org.springframework.mock.web.portlet.MockPortalContext + */ + public MockStateAwareResponse() { + super(); + } + + /** + * Create a new MockActionResponse. + * @param portalContext the PortalContext defining the supported + * PortletModes and WindowStates + */ + public MockStateAwareResponse(PortalContext portalContext) { + super(portalContext); + } + + + public void setWindowState(WindowState windowState) throws WindowStateException { + if (!CollectionUtils.contains(getPortalContext().getSupportedWindowStates(), windowState)) { + throw new WindowStateException("WindowState not supported", windowState); + } + this.windowState = windowState; + } + + public WindowState getWindowState() { + return this.windowState; + } + + public void setPortletMode(PortletMode portletMode) throws PortletModeException { + if (!CollectionUtils.contains(getPortalContext().getSupportedPortletModes(), portletMode)) { + throw new PortletModeException("PortletMode not supported", portletMode); + } + this.portletMode = portletMode; + } + + public PortletMode getPortletMode() { + return this.portletMode; + } + + public void setRenderParameters(Map parameters) { + Assert.notNull(parameters, "Parameters Map must not be null"); + this.renderParameters.clear(); + this.renderParameters.putAll(parameters); + } + + public void setRenderParameter(String key, String value) { + Assert.notNull(key, "Parameter key must not be null"); + Assert.notNull(value, "Parameter value must not be null"); + this.renderParameters.put(key, new String[] {value}); + } + + public void setRenderParameter(String key, String[] values) { + Assert.notNull(key, "Parameter key must not be null"); + Assert.notNull(values, "Parameter values must not be null"); + this.renderParameters.put(key, values); + } + + public String getRenderParameter(String key) { + Assert.notNull(key, "Parameter key must not be null"); + String[] arr = this.renderParameters.get(key); + return (arr != null && arr.length > 0 ? arr[0] : null); + } + + public String[] getRenderParameterValues(String key) { + Assert.notNull(key, "Parameter key must not be null"); + return this.renderParameters.get(key); + } + + public Iterator getRenderParameterNames() { + return this.renderParameters.keySet().iterator(); + } + + public Map getRenderParameterMap() { + return Collections.unmodifiableMap(this.renderParameters); + } + + public void removePublicRenderParameter(String name) { + this.renderParameters.remove(name); + } + + public void setEvent(QName name, Serializable value) { + this.events.put(name, value); + } + + public void setEvent(String name, Serializable value) { + this.events.put(new QName(name), value); + } + + public Iterator getEventNames() { + return this.events.keySet().iterator(); + } + + public Serializable getEvent(QName name) { + return this.events.get(name); + } + + public Serializable getEvent(String name) { + return this.events.get(new QName(name)); + } + +} \ No newline at end of file diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/ComplexPortletApplicationContext.java b/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/ComplexPortletApplicationContext.java index f7aa8c1aad8d5a5da9bdb4aa14abdebfeb80b9f9..a7299bee7ef1981bc93266dcdf774679b1df8397 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/ComplexPortletApplicationContext.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/ComplexPortletApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 the original author or authors. + * Copyright 2002-2008 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. @@ -22,15 +22,18 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; - import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; +import javax.portlet.EventRequest; +import javax.portlet.EventResponse; import javax.portlet.Portlet; import javax.portlet.PortletConfig; import javax.portlet.PortletException; import javax.portlet.PortletRequest; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; +import javax.portlet.ResourceRequest; +import javax.portlet.ResourceResponse; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; @@ -50,6 +53,7 @@ import org.springframework.web.multipart.MultipartException; import org.springframework.web.portlet.bind.PortletRequestBindingException; import org.springframework.web.portlet.context.PortletRequestHandledEvent; import org.springframework.web.portlet.context.StaticPortletApplicationContext; +import org.springframework.web.portlet.handler.HandlerInterceptorAdapter; import org.springframework.web.portlet.handler.ParameterHandlerMapping; import org.springframework.web.portlet.handler.ParameterMappingInterceptor; import org.springframework.web.portlet.handler.PortletModeHandlerMapping; @@ -352,17 +356,18 @@ public class ComplexPortletApplicationContext extends StaticPortletApplicationCo ((MyHandler) delegate).doSomething(request); return null; } - } - - - public static class MyHandlerInterceptor1 implements HandlerInterceptor { - public boolean preHandleAction(ActionRequest request, ActionResponse response, Object handler) { - return true; + public ModelAndView handleResource(ResourceRequest request, ResourceResponse response, Object handler) + throws Exception { + return null; } - public void afterActionCompletion(ActionRequest request, ActionResponse response, Object handler, Exception ex) { + public void handleEvent(EventRequest request, EventResponse response, Object handler) throws Exception { } + } + + + public static class MyHandlerInterceptor1 extends HandlerInterceptorAdapter { public boolean preHandleRender(RenderRequest request, RenderResponse response, Object handler) throws PortletException { @@ -398,14 +403,7 @@ public class ComplexPortletApplicationContext extends StaticPortletApplicationCo } - public static class MyHandlerInterceptor2 implements HandlerInterceptor { - - public boolean preHandleAction(ActionRequest request, ActionResponse response, Object handler) { - return true; - } - - public void afterActionCompletion(ActionRequest request, ActionResponse response, Object handler, Exception ex) { - } + public static class MyHandlerInterceptor2 extends HandlerInterceptorAdapter { public boolean preHandleRender(RenderRequest request, RenderResponse response, Object handler) throws PortletException { diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/mvc/annotation/Portlet20AnnotationControllerTests.java b/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/mvc/annotation/Portlet20AnnotationControllerTests.java new file mode 100644 index 0000000000000000000000000000000000000000..fae16d091b6638f31ac275ff69683b220acedead --- /dev/null +++ b/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/mvc/annotation/Portlet20AnnotationControllerTests.java @@ -0,0 +1,923 @@ +/* + * 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.portlet.mvc.annotation; + +import java.io.IOException; +import java.io.Writer; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import javax.portlet.ActionRequest; +import javax.portlet.ActionResponse; +import javax.portlet.EventResponse; +import javax.portlet.MimeResponse; +import javax.portlet.PortletContext; +import javax.portlet.PortletMode; +import javax.portlet.PortletRequest; +import javax.portlet.PortletSession; +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; +import javax.portlet.StateAwareResponse; +import javax.portlet.UnavailableException; +import javax.portlet.WindowState; + +import junit.framework.TestCase; + +import org.springframework.beans.BeansException; +import org.springframework.beans.DerivedTestBean; +import org.springframework.beans.ITestBean; +import org.springframework.beans.TestBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.propertyeditors.CustomDateEditor; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigUtils; +import org.springframework.core.MethodParameter; +import org.springframework.mock.web.portlet.MockActionRequest; +import org.springframework.mock.web.portlet.MockActionResponse; +import org.springframework.mock.web.portlet.MockEvent; +import org.springframework.mock.web.portlet.MockEventRequest; +import org.springframework.mock.web.portlet.MockEventResponse; +import org.springframework.mock.web.portlet.MockPortletConfig; +import org.springframework.mock.web.portlet.MockPortletContext; +import org.springframework.mock.web.portlet.MockRenderRequest; +import org.springframework.mock.web.portlet.MockRenderResponse; +import org.springframework.mock.web.portlet.MockResourceRequest; +import org.springframework.mock.web.portlet.MockResourceResponse; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ExtendedModelMap; +import org.springframework.ui.Model; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.validation.Errors; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.support.WebArgumentResolver; +import org.springframework.web.bind.support.WebBindingInitializer; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.context.support.GenericWebApplicationContext; +import org.springframework.web.portlet.DispatcherPortlet; +import org.springframework.web.portlet.ModelAndView; +import org.springframework.web.portlet.bind.annotation.ActionMapping; +import org.springframework.web.portlet.bind.annotation.EventMapping; +import org.springframework.web.portlet.bind.annotation.RenderMapping; +import org.springframework.web.portlet.bind.annotation.ResourceMapping; +import org.springframework.web.portlet.context.StaticPortletApplicationContext; +import org.springframework.web.portlet.mvc.AbstractController; + +/** + * @author Juergen Hoeller + * @since 3.0 + */ +public class Portlet20AnnotationControllerTests extends TestCase { + + public void testStandardHandleMethod() throws Exception { + DispatcherPortlet portlet = new DispatcherPortlet() { + protected ApplicationContext createPortletApplicationContext(ApplicationContext parent) throws BeansException { + GenericWebApplicationContext wac = new GenericWebApplicationContext(); + wac.registerBeanDefinition("controller", new RootBeanDefinition(MyController.class)); + wac.refresh(); + return wac; + } + }; + portlet.init(new MockPortletConfig()); + + MockRenderRequest request = new MockRenderRequest(PortletMode.VIEW); + MockRenderResponse response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("test", response.getContentAsString()); + } + + public void testAdaptedHandleMethods() throws Exception { + doTestAdaptedHandleMethods(MyAdaptedController.class); + } + + public void testAdaptedHandleMethods2() throws Exception { + doTestAdaptedHandleMethods(MyAdaptedController2.class); + } + + public void testAdaptedHandleMethods3() throws Exception { + doTestAdaptedHandleMethods(MyAdaptedController3.class); + } + + public void doTestAdaptedHandleMethods(final Class controllerClass) throws Exception { + DispatcherPortlet portlet = new DispatcherPortlet() { + protected ApplicationContext createPortletApplicationContext(ApplicationContext parent) throws BeansException { + GenericWebApplicationContext wac = new GenericWebApplicationContext(); + wac.registerBeanDefinition("controller", new RootBeanDefinition(controllerClass)); + wac.refresh(); + return wac; + } + }; + portlet.init(new MockPortletConfig()); + + MockActionRequest actionRequest = new MockActionRequest(PortletMode.VIEW); + MockActionResponse actionResponse = new MockActionResponse(); + portlet.processAction(actionRequest, actionResponse); + assertEquals("value", actionResponse.getRenderParameter("test")); + + MockRenderRequest request = new MockRenderRequest(PortletMode.EDIT); + request.addParameter("param1", "value1"); + request.addParameter("param2", "2"); + MockRenderResponse response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("test-value1-2", response.getContentAsString()); + + request = new MockRenderRequest(PortletMode.HELP); + request.addParameter("name", "name1"); + request.addParameter("age", "2"); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("test-name1-2", response.getContentAsString()); + + request = new MockRenderRequest(PortletMode.VIEW); + request.addParameter("name", "name1"); + request.addParameter("age", "value2"); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("test-name1-typeMismatch", response.getContentAsString()); + } + + public void testFormController() throws Exception { + DispatcherPortlet portlet = new DispatcherPortlet() { + protected ApplicationContext createPortletApplicationContext(ApplicationContext parent) throws BeansException { + GenericWebApplicationContext wac = new GenericWebApplicationContext(); + wac.registerBeanDefinition("controller", new RootBeanDefinition(MyFormController.class)); + wac.refresh(); + return wac; + } + protected void render(ModelAndView mv, PortletRequest request, MimeResponse response) throws Exception { + new TestView().render(mv.getViewName(), mv.getModel(), request, response); + } + }; + portlet.init(new MockPortletConfig()); + + MockRenderRequest request = new MockRenderRequest(PortletMode.VIEW); + request.addParameter("name", "name1"); + request.addParameter("age", "value2"); + MockRenderResponse response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myView-name1-typeMismatch-tb1-myValue", response.getContentAsString()); + } + + public void testModelFormController() throws Exception { + DispatcherPortlet portlet = new DispatcherPortlet() { + protected ApplicationContext createPortletApplicationContext(ApplicationContext parent) throws BeansException { + GenericWebApplicationContext wac = new GenericWebApplicationContext(); + wac.registerBeanDefinition("controller", new RootBeanDefinition(MyModelFormController.class)); + wac.refresh(); + return wac; + } + protected void render(ModelAndView mv, PortletRequest request, MimeResponse response) throws Exception { + new TestView().render(mv.getViewName(), mv.getModel(), request, response); + } + }; + portlet.init(new MockPortletConfig()); + + MockRenderRequest request = new MockRenderRequest(PortletMode.VIEW); + request.addParameter("name", "name1"); + request.addParameter("age", "value2"); + MockRenderResponse response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myView-name1-typeMismatch-tb1-myValue", response.getContentAsString()); + } + + public void testCommandProvidingFormController() throws Exception { + DispatcherPortlet portlet = new DispatcherPortlet() { + protected ApplicationContext createPortletApplicationContext(ApplicationContext parent) throws BeansException { + GenericWebApplicationContext wac = new GenericWebApplicationContext(); + wac.registerBeanDefinition("controller", new RootBeanDefinition(MyCommandProvidingFormController.class)); + RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class); + adapterDef.getPropertyValues().addPropertyValue("webBindingInitializer", new MyWebBindingInitializer()); + wac.registerBeanDefinition("handlerAdapter", adapterDef); + wac.refresh(); + return wac; + } + protected void render(ModelAndView mv, PortletRequest request, MimeResponse response) throws Exception { + new TestView().render(mv.getViewName(), mv.getModel(), request, response); + } + }; + portlet.init(new MockPortletConfig()); + + MockRenderRequest request = new MockRenderRequest(PortletMode.VIEW); + request.addParameter("defaultName", "myDefaultName"); + request.addParameter("age", "value2"); + request.addParameter("date", "2007-10-02"); + MockRenderResponse response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myView-String:myDefaultName-typeMismatch-tb1-myOriginalValue", response.getContentAsString()); + } + + public void testTypedCommandProvidingFormController() throws Exception { + DispatcherPortlet portlet = new DispatcherPortlet() { + protected ApplicationContext createPortletApplicationContext(ApplicationContext parent) throws BeansException { + GenericWebApplicationContext wac = new GenericWebApplicationContext(); + wac.registerBeanDefinition("controller", new RootBeanDefinition(MyTypedCommandProvidingFormController.class)); + wac.registerBeanDefinition("controller2", new RootBeanDefinition(MyOtherTypedCommandProvidingFormController.class)); + RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class); + adapterDef.getPropertyValues().addPropertyValue("webBindingInitializer", new MyWebBindingInitializer()); + adapterDef.getPropertyValues().addPropertyValue("customArgumentResolver", new MySpecialArgumentResolver()); + wac.registerBeanDefinition("handlerAdapter", adapterDef); + wac.refresh(); + return wac; + } + protected void render(ModelAndView mv, PortletRequest request, MimeResponse response) throws Exception { + new TestView().render(mv.getViewName(), mv.getModel(), request, response); + } + }; + portlet.init(new MockPortletConfig()); + + MockRenderRequest request = new MockRenderRequest(PortletMode.VIEW); + request.addParameter("myParam", "myValue"); + request.addParameter("defaultName", "10"); + request.addParameter("age", "value2"); + request.addParameter("date", "2007-10-02"); + MockRenderResponse response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myView-Integer:10-typeMismatch-tb1-myOriginalValue", response.getContentAsString()); + + request = new MockRenderRequest(PortletMode.VIEW); + request.addParameter("myParam", "myOtherValue"); + request.addParameter("defaultName", "10"); + request.addParameter("age", "value2"); + request.addParameter("date", "2007-10-02"); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myOtherView-Integer:10-typeMismatch-tb1-myOriginalValue", response.getContentAsString()); + + request = new MockRenderRequest(PortletMode.EDIT); + request.addParameter("myParam", "myValue"); + request.addParameter("defaultName", "10"); + request.addParameter("age", "value2"); + request.addParameter("date", "2007-10-02"); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myView-myName-typeMismatch-tb1-myOriginalValue", response.getContentAsString()); + } + + public void testBinderInitializingCommandProvidingFormController() throws Exception { + DispatcherPortlet portlet = new DispatcherPortlet() { + protected ApplicationContext createPortletApplicationContext(ApplicationContext parent) throws BeansException { + GenericWebApplicationContext wac = new GenericWebApplicationContext(); + wac.registerBeanDefinition("controller", new RootBeanDefinition(MyBinderInitializingCommandProvidingFormController.class)); + wac.refresh(); + return wac; + } + protected void render(ModelAndView mv, PortletRequest request, MimeResponse response) throws Exception { + new TestView().render(mv.getViewName(), mv.getModel(), request, response); + } + }; + portlet.init(new MockPortletConfig()); + + MockRenderRequest request = new MockRenderRequest(PortletMode.VIEW); + request.addParameter("defaultName", "myDefaultName"); + request.addParameter("age", "value2"); + request.addParameter("date", "2007-10-02"); + MockRenderResponse response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myView-String:myDefaultName-typeMismatch-tb1-myOriginalValue", response.getContentAsString()); + } + + public void testSpecificBinderInitializingCommandProvidingFormController() throws Exception { + DispatcherPortlet portlet = new DispatcherPortlet() { + protected ApplicationContext createPortletApplicationContext(ApplicationContext parent) throws BeansException { + StaticPortletApplicationContext wac = new StaticPortletApplicationContext(); + wac.registerBeanDefinition("controller", new RootBeanDefinition(MySpecificBinderInitializingCommandProvidingFormController.class)); + wac.refresh(); + return wac; + } + protected void render(ModelAndView mv, PortletRequest request, MimeResponse response) throws Exception { + new TestView().render(mv.getViewName(), mv.getModel(), request, response); + } + }; + portlet.init(new MockPortletConfig()); + + MockRenderRequest request = new MockRenderRequest(PortletMode.VIEW); + request.addParameter("defaultName", "myDefaultName"); + request.addParameter("age", "value2"); + request.addParameter("date", "2007-10-02"); + MockRenderResponse response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myView-String:myDefaultName-typeMismatch-tb1-myOriginalValue", response.getContentAsString()); + } + + public void testParameterDispatchingController() throws Exception { + DispatcherPortlet portlet = new DispatcherPortlet() { + protected ApplicationContext createPortletApplicationContext(ApplicationContext parent) throws BeansException { + StaticPortletApplicationContext wac = new StaticPortletApplicationContext(); + wac.setPortletContext(new MockPortletContext()); + RootBeanDefinition bd = new RootBeanDefinition(MyParameterDispatchingController.class); + bd.setScope(WebApplicationContext.SCOPE_REQUEST); + wac.registerBeanDefinition("controller", bd); + AnnotationConfigUtils.registerAnnotationConfigProcessors(wac); + wac.refresh(); + return wac; + } + }; + portlet.init(new MockPortletConfig()); + + MockRenderRequest request = new MockRenderRequest(PortletMode.VIEW); + MockRenderResponse response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myView", response.getContentAsString()); + + request = new MockRenderRequest(PortletMode.VIEW); + request.addParameter("view", "other"); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myOtherView", response.getContentAsString()); + + request = new MockRenderRequest(PortletMode.VIEW); + request.addParameter("view", "my"); + request.addParameter("lang", "de"); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myLangView", response.getContentAsString()); + + request = new MockRenderRequest(PortletMode.VIEW); + request.addParameter("surprise", "!"); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("mySurpriseView", response.getContentAsString()); + } + + public void testTypeLevelParameterDispatchingController() throws Exception { + DispatcherPortlet portlet = new DispatcherPortlet() { + protected ApplicationContext createPortletApplicationContext(ApplicationContext parent) throws BeansException { + StaticPortletApplicationContext wac = new StaticPortletApplicationContext(); + wac.setPortletContext(new MockPortletContext()); + RootBeanDefinition bd = new RootBeanDefinition(MyTypeLevelParameterDispatchingController.class); + bd.setScope(WebApplicationContext.SCOPE_REQUEST); + wac.registerBeanDefinition("controller", bd); + RootBeanDefinition bd2 = new RootBeanDefinition(MySpecialParameterDispatchingController.class); + bd2.setScope(WebApplicationContext.SCOPE_REQUEST); + wac.registerBeanDefinition("controller2", bd2); + RootBeanDefinition bd3 = new RootBeanDefinition(MyOtherSpecialParameterDispatchingController.class); + bd3.setScope(WebApplicationContext.SCOPE_REQUEST); + wac.registerBeanDefinition("controller3", bd3); + RootBeanDefinition bd4 = new RootBeanDefinition(MyParameterDispatchingController.class); + bd4.setScope(WebApplicationContext.SCOPE_REQUEST); + wac.registerBeanDefinition("controller4", bd4); + AnnotationConfigUtils.registerAnnotationConfigProcessors(wac); + wac.refresh(); + return wac; + } + }; + portlet.init(new MockPortletConfig()); + + MockRenderRequest request = new MockRenderRequest(PortletMode.HELP); + MockRenderResponse response = new MockRenderResponse(); + try { + portlet.render(request, response); + fail("Should have thrown UnavailableException"); + } + catch (UnavailableException ex) { + // expected + } + + request = new MockRenderRequest(PortletMode.EDIT); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myDefaultView", response.getContentAsString()); + + request = new MockRenderRequest(PortletMode.EDIT); + request.addParameter("myParam", "myValue"); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myView", response.getContentAsString()); + + request = new MockRenderRequest(PortletMode.EDIT); + request.addParameter("myParam", "mySpecialValue"); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("mySpecialView", response.getContentAsString()); + + request = new MockRenderRequest(PortletMode.EDIT); + request.addParameter("myParam", "myOtherSpecialValue"); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myOtherSpecialView", response.getContentAsString()); + + request = new MockRenderRequest(PortletMode.VIEW); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myView", response.getContentAsString()); + + request = new MockRenderRequest(PortletMode.EDIT); + request.addParameter("myParam", "myValue"); + request.addParameter("view", "other"); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myOtherView", response.getContentAsString()); + + request = new MockRenderRequest(PortletMode.EDIT); + request.addParameter("myParam", "myValue"); + request.addParameter("view", "my"); + request.addParameter("lang", "de"); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myLangView", response.getContentAsString()); + + request = new MockRenderRequest(PortletMode.EDIT); + request.addParameter("myParam", "myValue"); + request.addParameter("surprise", "!"); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("mySurpriseView", response.getContentAsString()); + } + + public void testPortlet20DispatchingController() throws Exception { + DispatcherPortlet portlet = new DispatcherPortlet() { + protected ApplicationContext createPortletApplicationContext(ApplicationContext parent) throws BeansException { + StaticPortletApplicationContext wac = new StaticPortletApplicationContext(); + wac.setPortletContext(new MockPortletContext()); + RootBeanDefinition bd = new RootBeanDefinition(MyPortlet20DispatchingController.class); + bd.setScope(WebApplicationContext.SCOPE_REQUEST); + wac.registerBeanDefinition("controller", bd); + AnnotationConfigUtils.registerAnnotationConfigProcessors(wac); + wac.refresh(); + return wac; + } + }; + portlet.init(new MockPortletConfig()); + + MockRenderRequest request = new MockRenderRequest(PortletMode.VIEW); + MockRenderResponse response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myView", response.getContentAsString()); + + MockActionRequest actionRequest = new MockActionRequest("this"); + MockActionResponse actionResponse = new MockActionResponse(); + portlet.processAction(actionRequest, actionResponse); + + request = new MockRenderRequest(PortletMode.VIEW, WindowState.MAXIMIZED); + request.setParameters(actionResponse.getRenderParameterMap()); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myLargeView-value", response.getContentAsString()); + + actionRequest = new MockActionRequest("that"); + actionResponse = new MockActionResponse(); + portlet.processAction(actionRequest, actionResponse); + + request = new MockRenderRequest(PortletMode.VIEW, WindowState.MAXIMIZED); + request.setParameters(actionResponse.getRenderParameterMap()); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myLargeView-value2", response.getContentAsString()); + + MockEventRequest eventRequest = new MockEventRequest(new MockEvent("event1")); + MockEventResponse eventResponse = new MockEventResponse(); + portlet.processEvent(eventRequest, eventResponse); + + request = new MockRenderRequest(PortletMode.VIEW, WindowState.MAXIMIZED); + request.setParameters(eventResponse.getRenderParameterMap()); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myLargeView-value3", response.getContentAsString()); + + eventRequest = new MockEventRequest(new MockEvent("event2")); + eventResponse = new MockEventResponse(); + portlet.processEvent(eventRequest, eventResponse); + + request = new MockRenderRequest(PortletMode.VIEW, WindowState.MAXIMIZED); + request.setParameters(eventResponse.getRenderParameterMap()); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myLargeView-value4", response.getContentAsString()); + + request = new MockRenderRequest(PortletMode.VIEW, WindowState.NORMAL); + request.setParameters(actionResponse.getRenderParameterMap()); + response = new MockRenderResponse(); + portlet.render(request, response); + assertEquals("myView", response.getContentAsString()); + + MockResourceRequest resourceRequest = new MockResourceRequest("resource1"); + MockResourceResponse resourceResponse = new MockResourceResponse(); + portlet.serveResource(resourceRequest, resourceResponse); + assertEquals("myResource", resourceResponse.getContentAsString()); + + resourceRequest = new MockResourceRequest("resource2"); + resourceResponse = new MockResourceResponse(); + portlet.serveResource(resourceRequest, resourceResponse); + assertEquals("myDefaultResource", resourceResponse.getContentAsString()); + } + + + @RequestMapping("VIEW") + private static class MyController extends AbstractController { + + protected ModelAndView handleRenderRequestInternal(RenderRequest request, RenderResponse response) throws Exception { + response.getWriter().write("test"); + return null; + } + } + + + @Controller + private static class MyAdaptedController { + + @RequestMapping("VIEW") + @ActionMapping + public void myHandle(ActionRequest request, ActionResponse response) throws IOException { + response.setRenderParameter("test", "value"); + } + + @RequestMapping("EDIT") + @RenderMapping + public void myHandle(@RequestParam("param1")String p1, @RequestParam("param2")int p2, RenderResponse response) throws IOException { + response.getWriter().write("test-" + p1 + "-" + p2); + } + + @RequestMapping("HELP") + @RenderMapping + public void myHandle(TestBean tb, RenderResponse response) throws IOException { + response.getWriter().write("test-" + tb.getName() + "-" + tb.getAge()); + } + + @RequestMapping("VIEW") + @RenderMapping + public void myHandle(TestBean tb, Errors errors, RenderResponse response) throws IOException { + response.getWriter().write("test-" + tb.getName() + "-" + errors.getFieldError("age").getCode()); + } + } + + + @Controller + private static class MyAdaptedController2 { + + @RequestMapping("VIEW") + @ActionMapping + public void myHandle(ActionRequest request, ActionResponse response) throws IOException { + response.setRenderParameter("test", "value"); + } + + @RequestMapping("EDIT") + @RenderMapping + public void myHandle(@RequestParam("param1")String p1, int param2, RenderResponse response) throws IOException { + response.getWriter().write("test-" + p1 + "-" + param2); + } + + @RequestMapping("HELP") + @RenderMapping + public void myHandle(TestBean tb, RenderResponse response) throws IOException { + response.getWriter().write("test-" + tb.getName() + "-" + tb.getAge()); + } + + @RequestMapping("VIEW") + @RenderMapping + public void myHandle(TestBean tb, Errors errors, RenderResponse response) throws IOException { + response.getWriter().write("test-" + tb.getName() + "-" + errors.getFieldError("age").getCode()); + } + } + + + @Controller + @RequestMapping({"VIEW", "EDIT", "HELP"}) + private static class MyAdaptedController3 { + + @ActionMapping + public void myHandle(ActionRequest request, ActionResponse response) { + response.setRenderParameter("test", "value"); + } + + @RequestMapping("EDIT") + @RenderMapping + public void myHandle(@RequestParam("param1")String p1, @RequestParam("param2")int p2, RenderResponse response) throws IOException { + response.getWriter().write("test-" + p1 + "-" + p2); + } + + @RequestMapping("HELP") + @RenderMapping + public void myHandle(TestBean tb, RenderResponse response) throws IOException { + response.getWriter().write("test-" + tb.getName() + "-" + tb.getAge()); + } + + @RenderMapping + public void myHandle(TestBean tb, Errors errors, RenderResponse response) throws IOException { + response.getWriter().write("test-" + tb.getName() + "-" + errors.getFieldError("age").getCode()); + } + } + + + @Controller + private static class MyFormController { + + @ModelAttribute("testBeanList") + public List getTestBeans() { + List list = new LinkedList(); + list.add(new TestBean("tb1")); + list.add(new TestBean("tb2")); + return list; + } + + @RequestMapping("VIEW") + @RenderMapping + public String myHandle(@ModelAttribute("myCommand")TestBean tb, BindingResult errors, ModelMap model) { + if (!model.containsKey("myKey")) { + model.addAttribute("myKey", "myValue"); + } + return "myView"; + } + } + + + @Controller + private static class MyModelFormController { + + @ModelAttribute + public List getTestBeans() { + List list = new LinkedList(); + list.add(new TestBean("tb1")); + list.add(new TestBean("tb2")); + return list; + } + + @RequestMapping("VIEW") + @RenderMapping + public String myHandle(@ModelAttribute("myCommand")TestBean tb, BindingResult errors, Model model) { + if (!model.containsAttribute("myKey")) { + model.addAttribute("myKey", "myValue"); + } + return "myView"; + } + } + + + @Controller + private static class MyCommandProvidingFormController extends MyFormController { + + @ModelAttribute("myCommand") + private TestBean createTestBean( + @RequestParam T defaultName, Map model, @RequestParam Date date) { + model.put("myKey", "myOriginalValue"); + return new TestBean(defaultName.getClass().getSimpleName() + ":" + defaultName.toString()); + } + + @RequestMapping("VIEW") + @RenderMapping + public String myHandle(@ModelAttribute("myCommand") TestBean tb, BindingResult errors, ModelMap model) { + if (!model.containsKey("myKey")) { + model.addAttribute("myKey", "myValue"); + } + return "myView"; + } + + @RequestMapping("EDIT") + @RenderMapping + public String myOtherHandle(TB tb, BindingResult errors, ExtendedModelMap model, MySpecialArg arg) { + TestBean tbReal = (TestBean) tb; + tbReal.setName("myName"); + assertTrue(model.get("ITestBean") instanceof DerivedTestBean); + assertNotNull(arg); + return super.myHandle(tbReal, errors, model); + } + + @ModelAttribute + protected TB2 getModelAttr() { + return (TB2) new DerivedTestBean(); + } + } + + + private static class MySpecialArg { + + public MySpecialArg(String value) { + } + } + + + @Controller + @RequestMapping(params = "myParam=myValue") + private static class MyTypedCommandProvidingFormController + extends MyCommandProvidingFormController { + + } + + + @Controller + @RequestMapping(params = "myParam=myOtherValue") + private static class MyOtherTypedCommandProvidingFormController + extends MyCommandProvidingFormController { + + @RequestMapping("VIEW") + @RenderMapping + public String myHandle(@ModelAttribute("myCommand") TestBean tb, BindingResult errors, ModelMap model) { + if (!model.containsKey("myKey")) { + model.addAttribute("myKey", "myValue"); + } + return "myOtherView"; + } + } + + + @Controller + private static class MyBinderInitializingCommandProvidingFormController extends MyCommandProvidingFormController { + + @InitBinder + private void initBinder(WebDataBinder binder) { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + dateFormat.setLenient(false); + binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); + } + } + + + @Controller + private static class MySpecificBinderInitializingCommandProvidingFormController extends MyCommandProvidingFormController { + + @SuppressWarnings("unused") + @InitBinder({"myCommand", "date"}) + private void initBinder(WebDataBinder binder) { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + dateFormat.setLenient(false); + binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); + } + } + + + private static class MyWebBindingInitializer implements WebBindingInitializer { + + public void initBinder(WebDataBinder binder, WebRequest request) { + assertNotNull(request.getLocale()); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + dateFormat.setLenient(false); + binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); + } + } + + + private static class MySpecialArgumentResolver implements WebArgumentResolver { + + public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) { + if (methodParameter.getParameterType().equals(MySpecialArg.class)) { + return new MySpecialArg("myValue"); + } + return UNRESOLVED; + } + } + + + @Controller + @RequestMapping("VIEW") + private static class MyParameterDispatchingController { + + @Autowired + private PortletContext portletContext; + + @Autowired + private PortletSession session; + + @Autowired + private PortletRequest request; + + @RenderMapping + public void myHandle(RenderResponse response) throws IOException { + if (this.portletContext == null || this.session == null || this.request == null) { + throw new IllegalStateException(); + } + response.getWriter().write("myView"); + } + + @RenderMapping(params = {"view", "!lang"}) + public void myOtherHandle(RenderResponse response) throws IOException { + response.getWriter().write("myOtherView"); + } + + @RenderMapping(params = {"view=my", "lang=de"}) + public void myLangHandle(RenderResponse response) throws IOException { + response.getWriter().write("myLangView"); + } + + @RenderMapping(params = "surprise") + public void mySurpriseHandle(RenderResponse response) throws IOException { + response.getWriter().write("mySurpriseView"); + } + } + + + @Controller + @RequestMapping(value = "EDIT", params = "myParam=myValue") + private static class MyTypeLevelParameterDispatchingController extends MyParameterDispatchingController { + + } + + + @Controller + @RequestMapping("EDIT") + private static class MySpecialParameterDispatchingController { + + @RenderMapping(params = "myParam=mySpecialValue") + public void myHandle(RenderResponse response) throws IOException { + response.getWriter().write("mySpecialView"); + } + + @RenderMapping + public void myDefaultHandle(RenderResponse response) throws IOException { + response.getWriter().write("myDefaultView"); + } + } + + + @Controller + @RequestMapping("EDIT") + private static class MyOtherSpecialParameterDispatchingController { + + @RenderMapping(params = "myParam=myOtherSpecialValue") + public void myHandle(RenderResponse response) throws IOException { + response.getWriter().write("myOtherSpecialView"); + } + } + + + @Controller + @RequestMapping("VIEW") + private static class MyPortlet20DispatchingController { + + @ActionMapping("this") + public void myHandle(StateAwareResponse response) { + response.setRenderParameter("test", "value"); + } + + @ActionMapping("that") + public void myHandle2(StateAwareResponse response) { + response.setRenderParameter("test", "value2"); + } + + @EventMapping("event1") + public void myHandle(EventResponse response) throws IOException { + response.setRenderParameter("test", "value3"); + } + + @EventMapping("event2") + public void myHandle2(EventResponse response) throws IOException { + response.setRenderParameter("test", "value4"); + } + + @RenderMapping("MAXIMIZED") + public void myHandle(Writer writer, @RequestParam("test") String renderParam) throws IOException { + writer.write("myLargeView-" + renderParam); + } + + @RenderMapping + public void myDefaultHandle(Writer writer) throws IOException { + writer.write("myView"); + } + + @ResourceMapping("resource1") + public void myResource(Writer writer) throws IOException { + writer.write("myResource"); + } + + @ResourceMapping + public void myDefaultResource(Writer writer) throws IOException { + writer.write("myDefaultResource"); + } + } + + + private static class TestView { + + public void render(String viewName, Map model, PortletRequest request, MimeResponse response) throws Exception { + TestBean tb = (TestBean) model.get("testBean"); + if (tb == null) { + tb = (TestBean) model.get("myCommand"); + } + if (tb.getName().endsWith("myDefaultName")) { + assertTrue(tb.getDate().getYear() == 107); + } + Errors errors = (Errors) model.get(BindingResult.MODEL_KEY_PREFIX + "testBean"); + if (errors == null) { + errors = (Errors) model.get(BindingResult.MODEL_KEY_PREFIX + "myCommand"); + } + if (errors.hasFieldErrors("date")) { + throw new IllegalStateException(); + } + List testBeans = (List) model.get("testBeanList"); + response.getWriter().write(viewName + "-" + tb.getName() + "-" + errors.getFieldError("age").getCode() + + "-" + testBeans.get(0).getName() + "-" + model.get("myKey")); + } + } + +} diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/mvc/annotation/PortletAnnotationControllerTests.java b/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/mvc/annotation/PortletAnnotationControllerTests.java index 714f250dcf958f4674ee5ac7b2311f9566f02fd2..fe2ab448c53d08e2a976507042320d9883d2435f 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/mvc/annotation/PortletAnnotationControllerTests.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/mvc/annotation/PortletAnnotationControllerTests.java @@ -22,9 +22,9 @@ import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Map; - import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; +import javax.portlet.MimeResponse; import javax.portlet.PortletContext; import javax.portlet.PortletMode; import javax.portlet.PortletRequest; @@ -154,8 +154,7 @@ public class PortletAnnotationControllerTests extends TestCase { wac.refresh(); return wac; } - - protected void render(ModelAndView mv, RenderRequest request, RenderResponse response) throws Exception { + protected void render(ModelAndView mv, PortletRequest request, MimeResponse response) throws Exception { new TestView().render(mv.getViewName(), mv.getModel(), request, response); } }; @@ -177,8 +176,7 @@ public class PortletAnnotationControllerTests extends TestCase { wac.refresh(); return wac; } - - protected void render(ModelAndView mv, RenderRequest request, RenderResponse response) throws Exception { + protected void render(ModelAndView mv, PortletRequest request, MimeResponse response) throws Exception { new TestView().render(mv.getViewName(), mv.getModel(), request, response); } }; @@ -203,8 +201,7 @@ public class PortletAnnotationControllerTests extends TestCase { wac.refresh(); return wac; } - - protected void render(ModelAndView mv, RenderRequest request, RenderResponse response) throws Exception { + protected void render(ModelAndView mv, PortletRequest request, MimeResponse response) throws Exception { new TestView().render(mv.getViewName(), mv.getModel(), request, response); } }; @@ -232,7 +229,7 @@ public class PortletAnnotationControllerTests extends TestCase { wac.refresh(); return wac; } - protected void render(ModelAndView mv, RenderRequest request, RenderResponse response) throws Exception { + protected void render(ModelAndView mv, PortletRequest request, MimeResponse response) throws Exception { new TestView().render(mv.getViewName(), mv.getModel(), request, response); } }; @@ -274,8 +271,7 @@ public class PortletAnnotationControllerTests extends TestCase { wac.refresh(); return wac; } - - protected void render(ModelAndView mv, RenderRequest request, RenderResponse response) throws Exception { + protected void render(ModelAndView mv, PortletRequest request, MimeResponse response) throws Exception { new TestView().render(mv.getViewName(), mv.getModel(), request, response); } }; @@ -298,8 +294,7 @@ public class PortletAnnotationControllerTests extends TestCase { wac.refresh(); return wac; } - - protected void render(ModelAndView mv, RenderRequest request, RenderResponse response) throws Exception { + protected void render(ModelAndView mv, PortletRequest request, MimeResponse response) throws Exception { new TestView().render(mv.getViewName(), mv.getModel(), request, response); } }; @@ -753,7 +748,7 @@ public class PortletAnnotationControllerTests extends TestCase { private static class TestView { - public void render(String viewName, Map model, RenderRequest request, RenderResponse response) throws Exception { + public void render(String viewName, Map model, PortletRequest request, MimeResponse response) throws Exception { TestBean tb = (TestBean) model.get("testBean"); if (tb == null) { tb = (TestBean) model.get("myCommand"); diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/util/PortletUtilsTests.java b/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/util/PortletUtilsTests.java index cb2089844f12127aa300d928b4892ae580c5b68c..d37bb93da3a5f4050371ad8707342facfa05caa2 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/util/PortletUtilsTests.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/util/PortletUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 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. @@ -16,21 +16,19 @@ package org.springframework.web.portlet.util; -import static org.easymock.EasyMock.*; -import static org.junit.Assert.*; - import java.io.File; import java.io.FileNotFoundException; import java.util.Collections; -import java.util.Enumeration; import java.util.HashMap; import java.util.Map; - import javax.portlet.PortletContext; import javax.portlet.PortletRequest; import javax.portlet.PortletSession; +import static org.easymock.EasyMock.*; +import static org.junit.Assert.*; import org.junit.Test; + import org.springframework.beans.ITestBean; import org.springframework.beans.TestBean; import org.springframework.mock.web.portlet.MockActionRequest; @@ -41,8 +39,6 @@ import org.springframework.mock.web.portlet.MockPortletSession; import org.springframework.web.util.WebUtils; /** - * Unit tests for {@link PortletUtils}. - * * @author Rick Evans * @author Chris Beams */ @@ -303,27 +299,14 @@ public final class PortletUtilsTests { PortletUtils.exposeRequestAttributes(new MockPortletRequest(), null); } - - @SuppressWarnings("unchecked") - @Test(expected=ClassCastException.class) - public void testExposeRequestAttributesWithAttributesMapContainingBadKeyType() throws Exception { - MockPortletRequest request = new MockPortletRequest(); - Map attributes = new HashMap(); - attributes.put(new Object(), "bad key type"); - PortletUtils.exposeRequestAttributes(request, attributes); - } - - @SuppressWarnings("unchecked") @Test public void testExposeRequestAttributesSunnyDay() throws Exception { MockPortletRequest request = new MockPortletRequest(); - Map attributes = new HashMap(); + Map attributes = new HashMap(); attributes.put("ace", "Rick Hunter"); attributes.put("mentor", "Roy Fokker"); PortletUtils.exposeRequestAttributes(request, attributes); - assertEquals("Obviously all of the entries in the supplied attributes Map are not being copied over (exposed)", - attributes.size(), countElementsIn(request.getAttributeNames())); assertEquals("Rick Hunter", request.getAttribute("ace")); assertEquals("Roy Fokker", request.getAttribute("mentor")); } @@ -332,10 +315,8 @@ public final class PortletUtilsTests { @Test public void testExposeRequestAttributesWithEmptyAttributesMapIsAnIdempotentOperation() throws Exception { MockPortletRequest request = new MockPortletRequest(); - Map attributes = new HashMap(); + Map attributes = new HashMap(); PortletUtils.exposeRequestAttributes(request, attributes); - assertEquals("Obviously all of the entries in the supplied attributes Map are not being copied over (exposed)", - attributes.size(), countElementsIn(request.getAttributeNames())); } @Test(expected=IllegalArgumentException.class) @@ -546,16 +527,6 @@ public final class PortletUtilsTests { } - private static int countElementsIn(Enumeration enumeration) { - int count = 0; - while (enumeration.hasMoreElements()) { - enumeration.nextElement(); - ++count; - } - return count; - } - - private static final class NoPublicCtor { private NoPublicCtor() { diff --git a/org.springframework.web.portlet/template.mf b/org.springframework.web.portlet/template.mf index 9814039a48d4bb412cf44e053c1400f411c32626..3bca8f234032e72bbb3aa80f0dbc09a65dfa1899 100644 --- a/org.springframework.web.portlet/template.mf +++ b/org.springframework.web.portlet/template.mf @@ -3,7 +3,7 @@ Bundle-Name: Spring Web Portlet Bundle-Vendor: SpringSource Bundle-ManifestVersion: 2 Import-Template: - javax.portlet.*;version="[1.0.0, 2.0.0)", + javax.portlet.*;version="[1.0.0, 3.0.0)", javax.servlet.*;version="[2.4.0, 3.0.0)", org.apache.commons.fileupload.*;version="[1.2.0, 2.0.0)";resolution:=optional, org.apache.commons.logging.*;version="[1.1.1, 2.0.0)", diff --git a/org.springframework.web.portlet/web-portlet.iml b/org.springframework.web.portlet/web-portlet.iml index 4ca907ac3da2995e957fdc0f7f607bfaadd4c441..fc04443b6232bd85add32c592ba846ab24489df3 100644 --- a/org.springframework.web.portlet/web-portlet.iml +++ b/org.springframework.web.portlet/web-portlet.iml @@ -30,10 +30,12 @@ - + - + + +