Spring Framework
  1. Spring Framework
  2. SPR-7344

Spring Portlet MVC - Unable to return JSON data from @ResourceMapping

    Details

    • Type: Improvement Improvement
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: 3.0.3
    • Fix Version/s: 4.x Backlog
    • Component/s: Web
    • Labels:
      None
    • Last commented by a User:
      true

      Description

      In many scenarios it is required to support returning JSON data from the @ResourceMapping annotated method of controller. The @ResponseBody and ContentNegotiatingViewResolver currently work only in the servlet environment.

        Issue Links

          Activity

          Hide
          Wendy Cameron added a comment -

          I also notice in order to support @ResponseStatus the SimpleMappingExceptionResolver will need to have the properties and code relating to

          private Integer defaultStatusCode;
          
          private Map<String, Integer> statusCodes = new HashMap<String, Integer>();
          

          Added and possibly need to create a portlet version of ResponseStatusExceptionResolver.

          Show
          Wendy Cameron added a comment - I also notice in order to support @ResponseStatus the SimpleMappingExceptionResolver will need to have the properties and code relating to private Integer defaultStatusCode; private Map< String , Integer > statusCodes = new HashMap< String , Integer >(); Added and possibly need to create a portlet version of ResponseStatusExceptionResolver.
          Hide
          Wendy Cameron added a comment -

          Tested out the following code:

          package au.com.optus.oneportal.handler;
          
          /*
           * 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.
           * 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.
           */
          
          import org.springframework.web.portlet.ModelAndView;
          import org.springframework.web.portlet.handler.AbstractHandlerExceptionResolver;
          import org.springframework.web.util.WebUtils;
          
          import javax.portlet.MimeResponse;
          import javax.portlet.PortletRequest;
          import javax.portlet.PortletResponse;
          import java.util.Enumeration;
          import java.util.HashMap;
          import java.util.Map;
          import java.util.Properties;
          
          import static javax.portlet.ResourceResponse.HTTP_STATUS_CODE;
          
          /**
           * {@link org.springframework.web.portlet.HandlerExceptionResolver} implementation
           * that allows for mapping exception class names to view names, either for a
           * set of given handlers or for all handlers in the DispatcherPortlet.
           *
           * <p>Error views are analogous to error page JSPs, but can be used with any
           * kind of exception including any checked one, with fine-granular mappings for
           * specific handlers.
           *
           * @author Juergen Hoeller
           * @author John A. Lewis
           * @author Arjen Poutsma
           * @since 2.0
           */
          public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionResolver {
          
          	/**
          	 * The default name of the exception attribute: "exception".
          	 */
          	public static final String DEFAULT_EXCEPTION_ATTRIBUTE = "exception";
          
          	private Properties exceptionMappings;
          
          	private String defaultErrorView;
          
              private Integer defaultStatusCode;
          
          	private Map<String, Integer> statusCodes = new HashMap<String, Integer>();
          
          	private String exceptionAttribute = DEFAULT_EXCEPTION_ATTRIBUTE;
          
          	/**
          	 * Set the mappings between exception class names and error view names.
          	 * The exception class name can be a substring, with no wildcard support
          	 * at present. A value of "PortletException" would match
          	 * <code>javax.portet.PortletException</code> and subclasses, for example.
          	 * <p><b>NB:</b> Consider carefully how specific the pattern is, and whether
          	 * to include package information (which isn't mandatory). For example,
          	 * "Exception" will match nearly anything, and will probably hide other rules.
          	 * "java.lang.Exception" would be correct if "Exception" was meant to define
          	 * a rule for all checked exceptions. With more unusual exception names such
          	 * as "BaseBusinessException" there's no need to use a FQN.
          	 * <p>Follows the same matching algorithm as RuleBasedTransactionAttribute
          	 * and RollbackRuleAttribute.
          	 * @param mappings exception patterns (can also be fully qualified class names)
          	 * as keys, and error view names as values
          	 * @see org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
          	 * @see org.springframework.transaction.interceptor.RollbackRuleAttribute
          	 */
          	public void setExceptionMappings(Properties mappings) {
          		this.exceptionMappings = mappings;
          	}
          
          	/**
          	 * Set the name of the default error view.
          	 * This view will be returned if no specific mapping was found.
          	 * <p>Default is none.
          	 */
          	public void setDefaultErrorView(String defaultErrorView) {
          		this.defaultErrorView = defaultErrorView;
          	}
          
              /**
          	 * Set the HTTP status code that this exception resolver will apply for a given resolved error view. Keys are
          	 * view names; values are status codes.
          	 * <p>Note that this error code will only get applied in case of a top-level request. It will not be set for an include
          	 * request, since the HTTP status cannot be modified from within an include.
          	 * <p>If not specified, the default status code will be applied.
          	 * @see #setDefaultStatusCode(int)
          	 */
          	public void setStatusCodes(Properties statusCodes) {
          		for (Enumeration enumeration = statusCodes.propertyNames(); enumeration.hasMoreElements();) {
          			String viewName = (String) enumeration.nextElement();
          			Integer statusCode = new Integer(statusCodes.getProperty(viewName));
          			this.statusCodes.put(viewName, statusCode);
          		}
          	}
          
          	/**
          	 * Set the default HTTP status code that this exception resolver will apply if it resolves an error view and if there
          	 * is no status code mapping defined.
          	 * <p>Note that this error code will only get applied in case of a top-level request. It will not be set for an
          	 * include request, since the HTTP status cannot be modified from within an include.
          	 * <p>If not specified, no status code will be applied, either leaving this to the controller or view, or keeping
          	 * the servlet engine's default of 200 (OK).
          	 * @param defaultStatusCode HTTP status code value, for example 500
          	 * ({@link javax.servlet.http.HttpServletResponse#SC_INTERNAL_SERVER_ERROR}) or 404 ({@link javax.servlet.http.HttpServletResponse#SC_NOT_FOUND})
          	 * @see #setStatusCodes(Properties)
          	 */
          	public void setDefaultStatusCode(int defaultStatusCode) {
          		this.defaultStatusCode = defaultStatusCode;
          	}
          
          	/**
          	 * Set the name of the model attribute as which the exception should
          	 * be exposed. Default is "exception".
          	 * @see #DEFAULT_EXCEPTION_ATTRIBUTE
          	 */
          	public void setExceptionAttribute(String exceptionAttribute) {
          		this.exceptionAttribute = exceptionAttribute;
          	}
          
          	/**
          	 * Actually 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
          	 */
          	@Override
          	protected ModelAndView doResolveException(
          			PortletRequest request, MimeResponse response, Object handler, Exception ex) {
          
          		// Log exception, both at debug log level and at warn level, if desired.
          		if (logger.isDebugEnabled()) {
          			logger.debug("Resolving exception from handler [" + handler + "]: " + ex);
          		}
          		logException(ex, request);
          
          		// Expose ModelAndView for chosen error view.
          		String viewName = determineViewName(ex, request);
          		if (viewName != null) {
                      Integer statusCode = determineStatusCode(request, viewName);
          			if (statusCode != null) {
          				applyStatusCodeIfPossible(request, response, statusCode);
          			}
          			return getModelAndView(viewName, ex, request);
          		}
          		else {
          			return null;
          		}
          	}
          
          	/**
          	 * Determine the view name for the given exception, searching the
          	 * {@link #setExceptionMappings "exceptionMappings"}, using the
          	 * {@link #setDefaultErrorView "defaultErrorView"} as fallback.
          	 * @param ex the exception that got thrown during handler execution
          	 * @param request current portlet request (useful for obtaining metadata)
          	 * @return the resolved view name, or <code>null</code> if none found
          	 */
          	protected String determineViewName(Exception ex, PortletRequest request) {
          		String viewName = null;
          		// Check for specific exception mappings.
          		if (this.exceptionMappings != null) {
          			viewName = findMatchingViewName(this.exceptionMappings, ex);
          		}
          		// Return default error view else, if defined.
          		if (viewName == null && this.defaultErrorView != null) {
          			if (logger.isDebugEnabled()) {
          				logger.debug("Resolving to default view '" + this.defaultErrorView +
          						"' for exception of type [" + ex.getClass().getName() + "]");
          			}
          			viewName = this.defaultErrorView;
          		}
          		return viewName;
          	}
          
          	/**
          	 * Find a matching view name in the given exception mappings
          	 * @param exceptionMappings mappings between exception class names and error view names
          	 * @param ex the exception that got thrown during handler execution
          	 * @return the view name, or <code>null</code> if none found
          	 * @see #setExceptionMappings
          	 */
          	protected String findMatchingViewName(Properties exceptionMappings, Exception ex) {
          		String viewName = null;
          		String dominantMapping = null;
          		int deepest = Integer.MAX_VALUE;
          		for (Enumeration names = exceptionMappings.propertyNames(); names.hasMoreElements();) {
          			String exceptionMapping = (String) names.nextElement();
          			int depth = getDepth(exceptionMapping, ex);
          			if (depth >= 0 && depth < deepest) {
          				deepest = depth;
          				dominantMapping = exceptionMapping;
          				viewName = exceptionMappings.getProperty(exceptionMapping);
          			}
          		}
          		if (viewName != null && logger.isDebugEnabled()) {
          			logger.debug("Resolving to view '" + viewName + "' for exception of type [" + ex.getClass().getName() +
          					"], based on exception mapping [" + dominantMapping + "]");
          		}
          		return viewName;
          	}
          
          	/**
          	 * Return the depth to the superclass matching.
          	 * <p>0 means ex matches exactly. Returns -1 if there's no match.
          	 * Otherwise, returns depth. Lowest depth wins.
          	 * <p>Follows the same algorithm as
          	 * {@link org.springframework.transaction.interceptor.RollbackRuleAttribute}.
          	 */
          	protected int getDepth(String exceptionMapping, Exception ex) {
          		return getDepth(exceptionMapping, ex.getClass(), 0);
          	}
          
          	private int getDepth(String exceptionMapping, Class exceptionClass, int depth) {
          		if (exceptionClass.getName().contains(exceptionMapping)) {
          			// Found it!
          			return depth;
          		}
          		// If we've gone as far as we can go and haven't found it...
          		if (exceptionClass.equals(Throwable.class)) {
          			return -1;
          		}
          		return getDepth(exceptionMapping, exceptionClass.getSuperclass(), depth + 1);
          	}
          
              /**
          	 * Determine the HTTP status code to apply for the given error view.
          	 * <p>The default implementation returns the status code for the given view name (specified through the
          	 * {@link #setStatusCodes(Properties) statusCodes} property), or falls back to the
          	 * {@link #setDefaultStatusCode defaultStatusCode} if there is no match.
          	 * <p>Override this in a custom subclass to customize this behavior.
          	 *
               * @param request current HTTP request
               * @param viewName the name of the error view
               * @return the HTTP status code to use, or <code>null</code> for the servlet container's default
          	 * (200 in case of a standard error view)
          	 * @see #setDefaultStatusCode
          	 * @see #applyStatusCodeIfPossible
          	 */
          	protected Integer determineStatusCode(PortletRequest request, String viewName) {
          		if (this.statusCodes.containsKey(viewName)) {
          			return this.statusCodes.get(viewName);
          		}
          		return this.defaultStatusCode;
          	}
          
          	/**
          	 * Apply the specified HTTP status code to the given response, if possible (that is,
          	 * if not executing within an include request).
          	 * @param request current HTTP request
          	 * @param response current HTTP response
          	 * @param statusCode the status code to apply
          	 * @see #determineStatusCode
          	 * @see #setDefaultStatusCode
          	 * @see javax.servlet.http.HttpServletResponse#setStatus
          	 */
          	protected void applyStatusCodeIfPossible(PortletRequest request, PortletResponse response, int statusCode) {
                  if (logger.isDebugEnabled()) {
                      logger.debug("Applying HTTP status code " + statusCode);
                  }
                  response.setProperty(HTTP_STATUS_CODE, String.valueOf(statusCode));
                  request.setAttribute(WebUtils.ERROR_STATUS_CODE_ATTRIBUTE, statusCode);
          	}
          
          
          	/**
          	 * Return a ModelAndView for the given request, view name and exception.
          	 * Default implementation delegates to <code>getModelAndView(viewName, ex)</code>.
          	 * @param viewName the name of the error view
          	 * @param ex the exception that got thrown during handler execution
          	 * @param request current portlet request (useful for obtaining metadata)
          	 * @return the ModelAndView instance
          	 * @see #getModelAndView(String, Exception)
          	 */
          	protected ModelAndView getModelAndView(String viewName, Exception ex, PortletRequest request) {
          		return getModelAndView(viewName, ex);
          	}
          
          	/**
          	 * Return a ModelAndView for the given view name and exception.
          	 * Default implementation adds the specified exception attribute.
          	 * Can be overridden in subclasses.
          	 * @param viewName the name of the error view
          	 * @param ex the exception that got thrown during handler execution
          	 * @return the ModelAndView instance
          	 * @see #setExceptionAttribute
          	 */
          	protected ModelAndView getModelAndView(String viewName, Exception ex) {
          		ModelAndView mv = new ModelAndView(viewName);
          		if (this.exceptionAttribute != null) {
          			if (logger.isDebugEnabled()) {
          				logger.debug("Exposing Exception as model attribute '" + this.exceptionAttribute + "'");
          			}
          			mv.addObject(this.exceptionAttribute, ex);
          		}
          		return mv;
          	}
          
          }
          

          This appears to work adding a header
          portlet.http-status-code:500

          I wonder if the solution for replacing sendError(statusCode, reason) is to create a header
          portlet.http-status-code-reason: (Place Reason Here)

          And set that instead.

          Show
          Wendy Cameron added a comment - Tested out the following code: package au.com.optus.oneportal.handler; /* * 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. * 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. */ import org.springframework.web.portlet.ModelAndView; import org.springframework.web.portlet.handler.AbstractHandlerExceptionResolver; import org.springframework.web.util.WebUtils; import javax.portlet.MimeResponse; import javax.portlet.PortletRequest; import javax.portlet.PortletResponse; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Properties; import static javax.portlet.ResourceResponse.HTTP_STATUS_CODE; /** * {@link org.springframework.web.portlet.HandlerExceptionResolver} implementation * that allows for mapping exception class names to view names, either for a * set of given handlers or for all handlers in the DispatcherPortlet. * * <p>Error views are analogous to error page JSPs, but can be used with any * kind of exception including any checked one, with fine-granular mappings for * specific handlers. * * @author Juergen Hoeller * @author John A. Lewis * @author Arjen Poutsma * @since 2.0 */ public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionResolver { /** * The default name of the exception attribute: "exception" . */ public static final String DEFAULT_EXCEPTION_ATTRIBUTE = "exception" ; private Properties exceptionMappings; private String defaultErrorView; private Integer defaultStatusCode; private Map< String , Integer > statusCodes = new HashMap< String , Integer >(); private String exceptionAttribute = DEFAULT_EXCEPTION_ATTRIBUTE; /** * Set the mappings between exception class names and error view names. * The exception class name can be a substring, with no wildcard support * at present. A value of "PortletException" would match * <code>javax.portet.PortletException</code> and subclasses, for example. * <p><b>NB:</b> Consider carefully how specific the pattern is, and whether * to include package information (which isn't mandatory). For example, * "Exception" will match nearly anything, and will probably hide other rules. * "java.lang.Exception" would be correct if "Exception" was meant to define * a rule for all checked exceptions. With more unusual exception names such * as "BaseBusinessException" there's no need to use a FQN. * <p>Follows the same matching algorithm as RuleBasedTransactionAttribute * and RollbackRuleAttribute. * @param mappings exception patterns (can also be fully qualified class names) * as keys, and error view names as values * @see org.springframework.transaction.interceptor.RuleBasedTransactionAttribute * @see org.springframework.transaction.interceptor.RollbackRuleAttribute */ public void setExceptionMappings(Properties mappings) { this .exceptionMappings = mappings; } /** * Set the name of the default error view. * This view will be returned if no specific mapping was found. * <p>Default is none. */ public void setDefaultErrorView( String defaultErrorView) { this .defaultErrorView = defaultErrorView; } /** * Set the HTTP status code that this exception resolver will apply for a given resolved error view. Keys are * view names; values are status codes. * <p>Note that this error code will only get applied in case of a top-level request. It will not be set for an include * request, since the HTTP status cannot be modified from within an include. * <p>If not specified, the default status code will be applied. * @see #setDefaultStatusCode( int ) */ public void setStatusCodes(Properties statusCodes) { for (Enumeration enumeration = statusCodes.propertyNames(); enumeration.hasMoreElements();) { String viewName = ( String ) enumeration.nextElement(); Integer statusCode = new Integer (statusCodes.getProperty(viewName)); this .statusCodes.put(viewName, statusCode); } } /** * Set the default HTTP status code that this exception resolver will apply if it resolves an error view and if there * is no status code mapping defined. * <p>Note that this error code will only get applied in case of a top-level request. It will not be set for an * include request, since the HTTP status cannot be modified from within an include. * <p>If not specified, no status code will be applied, either leaving this to the controller or view, or keeping * the servlet engine's default of 200 (OK). * @param defaultStatusCode HTTP status code value, for example 500 * ({@link javax.servlet.http.HttpServletResponse#SC_INTERNAL_SERVER_ERROR}) or 404 ({@link javax.servlet.http.HttpServletResponse#SC_NOT_FOUND}) * @see #setStatusCodes(Properties) */ public void setDefaultStatusCode( int defaultStatusCode) { this .defaultStatusCode = defaultStatusCode; } /** * Set the name of the model attribute as which the exception should * be exposed. Default is "exception" . * @see #DEFAULT_EXCEPTION_ATTRIBUTE */ public void setExceptionAttribute( String exceptionAttribute) { this .exceptionAttribute = exceptionAttribute; } /** * Actually 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 */ @Override protected ModelAndView doResolveException( PortletRequest request, MimeResponse response, Object handler, Exception ex) { // Log exception, both at debug log level and at warn level, if desired. if (logger.isDebugEnabled()) { logger.debug( "Resolving exception from handler [" + handler + "]: " + ex); } logException(ex, request); // Expose ModelAndView for chosen error view. String viewName = determineViewName(ex, request); if (viewName != null ) { Integer statusCode = determineStatusCode(request, viewName); if (statusCode != null ) { applyStatusCodeIfPossible(request, response, statusCode); } return getModelAndView(viewName, ex, request); } else { return null ; } } /** * Determine the view name for the given exception, searching the * {@link #setExceptionMappings "exceptionMappings" }, using the * {@link #setDefaultErrorView "defaultErrorView" } as fallback. * @param ex the exception that got thrown during handler execution * @param request current portlet request (useful for obtaining metadata) * @ return the resolved view name, or <code> null </code> if none found */ protected String determineViewName(Exception ex, PortletRequest request) { String viewName = null ; // Check for specific exception mappings. if ( this .exceptionMappings != null ) { viewName = findMatchingViewName( this .exceptionMappings, ex); } // Return default error view else , if defined. if (viewName == null && this .defaultErrorView != null ) { if (logger.isDebugEnabled()) { logger.debug( "Resolving to default view '" + this .defaultErrorView + "' for exception of type [" + ex.getClass().getName() + "]" ); } viewName = this .defaultErrorView; } return viewName; } /** * Find a matching view name in the given exception mappings * @param exceptionMappings mappings between exception class names and error view names * @param ex the exception that got thrown during handler execution * @ return the view name, or <code> null </code> if none found * @see #setExceptionMappings */ protected String findMatchingViewName(Properties exceptionMappings, Exception ex) { String viewName = null ; String dominantMapping = null ; int deepest = Integer .MAX_VALUE; for (Enumeration names = exceptionMappings.propertyNames(); names.hasMoreElements();) { String exceptionMapping = ( String ) names.nextElement(); int depth = getDepth(exceptionMapping, ex); if (depth >= 0 && depth < deepest) { deepest = depth; dominantMapping = exceptionMapping; viewName = exceptionMappings.getProperty(exceptionMapping); } } if (viewName != null && logger.isDebugEnabled()) { logger.debug( "Resolving to view '" + viewName + "' for exception of type [" + ex.getClass().getName() + "], based on exception mapping [" + dominantMapping + "]" ); } return viewName; } /** * Return the depth to the superclass matching. * <p>0 means ex matches exactly. Returns -1 if there's no match. * Otherwise, returns depth. Lowest depth wins. * <p>Follows the same algorithm as * {@link org.springframework.transaction.interceptor.RollbackRuleAttribute}. */ protected int getDepth( String exceptionMapping, Exception ex) { return getDepth(exceptionMapping, ex.getClass(), 0); } private int getDepth( String exceptionMapping, Class exceptionClass, int depth) { if (exceptionClass.getName().contains(exceptionMapping)) { // Found it! return depth; } // If we've gone as far as we can go and haven't found it... if (exceptionClass.equals(Throwable.class)) { return -1; } return getDepth(exceptionMapping, exceptionClass.getSuperclass(), depth + 1); } /** * Determine the HTTP status code to apply for the given error view. * <p>The default implementation returns the status code for the given view name (specified through the * {@link #setStatusCodes(Properties) statusCodes} property), or falls back to the * {@link #setDefaultStatusCode defaultStatusCode} if there is no match. * <p>Override this in a custom subclass to customize this behavior. * * @param request current HTTP request * @param viewName the name of the error view * @ return the HTTP status code to use, or <code> null </code> for the servlet container's default * (200 in case of a standard error view) * @see #setDefaultStatusCode * @see #applyStatusCodeIfPossible */ protected Integer determineStatusCode(PortletRequest request, String viewName) { if ( this .statusCodes.containsKey(viewName)) { return this .statusCodes.get(viewName); } return this .defaultStatusCode; } /** * Apply the specified HTTP status code to the given response, if possible (that is, * if not executing within an include request). * @param request current HTTP request * @param response current HTTP response * @param statusCode the status code to apply * @see #determineStatusCode * @see #setDefaultStatusCode * @see javax.servlet.http.HttpServletResponse#setStatus */ protected void applyStatusCodeIfPossible(PortletRequest request, PortletResponse response, int statusCode) { if (logger.isDebugEnabled()) { logger.debug( "Applying HTTP status code " + statusCode); } response.setProperty(HTTP_STATUS_CODE, String .valueOf(statusCode)); request.setAttribute(WebUtils.ERROR_STATUS_CODE_ATTRIBUTE, statusCode); } /** * Return a ModelAndView for the given request, view name and exception. * Default implementation delegates to <code>getModelAndView(viewName, ex)</code>. * @param viewName the name of the error view * @param ex the exception that got thrown during handler execution * @param request current portlet request (useful for obtaining metadata) * @ return the ModelAndView instance * @see #getModelAndView( String , Exception) */ protected ModelAndView getModelAndView( String viewName, Exception ex, PortletRequest request) { return getModelAndView(viewName, ex); } /** * Return a ModelAndView for the given view name and exception. * Default implementation adds the specified exception attribute. * Can be overridden in subclasses. * @param viewName the name of the error view * @param ex the exception that got thrown during handler execution * @ return the ModelAndView instance * @see #setExceptionAttribute */ protected ModelAndView getModelAndView( String viewName, Exception ex) { ModelAndView mv = new ModelAndView(viewName); if ( this .exceptionAttribute != null ) { if (logger.isDebugEnabled()) { logger.debug( "Exposing Exception as model attribute '" + this .exceptionAttribute + "'" ); } mv.addObject( this .exceptionAttribute, ex); } return mv; } } This appears to work adding a header portlet.http-status-code:500 I wonder if the solution for replacing sendError(statusCode, reason) is to create a header portlet.http-status-code-reason: (Place Reason Here) And set that instead.
          Hide
          Michał Mela added a comment -

          Was there any progress on the subject? I am especially interested in any development in the areas of

          • @RequestBody handler method's annotation Wendy mentioned
          • MessageConverters property in portlet's version of AnnotationMethodHandlerAdapter - it would be nice to at least know why is this absent, because I couldn't find anything on that particular matter in documentation
          Show
          Michał Mela added a comment - Was there any progress on the subject? I am especially interested in any development in the areas of @RequestBody handler method's annotation Wendy mentioned MessageConverters property in portlet's version of AnnotationMethodHandlerAdapter - it would be nice to at least know why is this absent, because I couldn't find anything on that particular matter in documentation
          Hide
          Eugene Petruhin added a comment -

          I can't believe it's been open for 2.5 years and still no implementation is available.
          Spring's version is now 3.2 and servlets enjoy the benefits of @RequestBody and @Valid being used together but portlets can't even do @RequestBody for resource requests.

          Please consider adding this functionality to the next release.

          Show
          Eugene Petruhin added a comment - I can't believe it's been open for 2.5 years and still no implementation is available. Spring's version is now 3.2 and servlets enjoy the benefits of @RequestBody and @Valid being used together but portlets can't even do @RequestBody for resource requests. Please consider adding this functionality to the next release.
          Hide
          Abdulrhman Nabih ALKoptan added a comment -

          Any Update ?

          Show
          Abdulrhman Nabih ALKoptan added a comment - Any Update ?

            People

            • Assignee:
              Juergen Hoeller
              Reporter:
              Ashish Sarin
              Last updater:
              Abdulrhman Nabih ALKoptan
            • Votes:
              55 Vote for this issue
              Watchers:
              37 Start watching this issue

              Dates

              • Created:
                Updated:
                Days since last comment:
                9 hours, 6 minutes ago