Package com.crygier.spring.util.web

Source Code of com.crygier.spring.util.web.MimeTypeViewResolver

/**
* Copyright 2012 John Crygier
*
* 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 com.crygier.spring.util.web;

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 java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.http.MediaType;
import org.springframework.oxm.xstream.XStreamMarshaller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.Mapping;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.view.AbstractCachingViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.json.MappingJacksonJsonView;
import org.springframework.web.servlet.view.xml.MarshallingView;

/**
* Utilizes the MIME Type from the Accept Header of the request to choose the proper view resolver.  It will
* allow you to choose your own mappings by setting the 'mimeTypeToViewResolver' property.  The intent here is
* to allow for extremely simple REST-like interfaces.  A sample Spring Setup would be as follows:
*
* <pre>
* {@code
<bean id="tilesViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver"
*        p:viewClass="org.springframework.web.servlet.view.tiles2.TilesView" />
<bean class="com.crygier.spring.util.web.MimeTypeViewResolver">
*    <property name="mimeTypeToViewResolver">
*      <map>
*        <entry key="text/html" value-ref="tilesViewResolver"/>
*      </map>
*    </property>
</bean>
*   }
* </pre>
*
* If you don't set any mappings, there are several defaults:
* <pre>
* text/html - org.springframework.web.servlet.view.InternalResourceViewResolver
*           - Default Prefix is /.  Override by setting 'defaultInternalResourcePrefix'
*           - Default Suffix is .jsp.  Override by setting 'defaultInternalResourceSuffix'
* application/json - org.springframework.web.servlet.view.json.MappingJacksonJsonView
* text/xml - org.springframework.oxm.xstream.XStreamMarshaller
* </pre>
*
* This means that Controllers should know nothing about Views, and should use the @RequestMapping annotation in this
* class to tell it how to resolve views that need a name, just as the JSP view resolver.  So, to map to a JSP view with
* the default text/html view handler, setting the annotation "@ResponseMapping("welcome")" on a method will resolve to
* "/welcome.jsp".
*
* @author John Crygier
*
*/
public class MimeTypeViewResolver extends AbstractCachingViewResolver implements Ordered, InitializingBean
{
  public static final Log logger = LogFactory.getLog(MimeTypeViewResolver.class);
 
  private List<HandlerMapping> handlerMappings;
 
  private Map<String, ViewResolver> mimeTypeToViewResolver = new HashMap<String, ViewResolver>();
  private ViewResolver defaultResolver;
  private String defaultInternalResourcePrefix = "/";
  private String defaultInternalResourceSuffix = ".jsp";
 
  private int order = HIGHEST_PRECEDENCE;

  @Override
  protected View loadView(String viewName, Locale locale) throws Exception {
    String name = getViewNameFromRequest();

    String acceptHeader = getCurrentRequestAttributes().getRequest().getHeader("Accept");
   
    List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
    if (StringUtils.hasText(acceptHeader)) {
      try {
        acceptableMediaTypes = MediaType.parseMediaTypes(acceptHeader);
      } catch (Exception e) {
        logger.warn("Unparsable Accept header, defaulting to text/html");
        acceptableMediaTypes = new ArrayList<MediaType>();
        acceptableMediaTypes.add(MediaType.TEXT_HTML);
      }
    }
   
    for (MediaType requestedMediaType : acceptableMediaTypes) {
      ViewResolver viewResolver = findViewResolverForMediaType(requestedMediaType);
     
      if (viewResolver != null)
        return viewResolver.resolveViewName(name, locale);
    }
   
    logger.warn("No matching view resolvers found, backing onto Default resolver: " + defaultResolver);
    return defaultResolver.resolveViewName(name, locale);
  }
 
  protected ViewResolver findViewResolverForMediaType(MediaType requestedMediaType) {
    if (requestedMediaType.equals(MediaType.ALL))
      return defaultResolver;
   
    for (String mimeType : mimeTypeToViewResolver.keySet()) {
      MediaType searchingType = MediaType.parseMediaType(mimeType);
     
      if (requestedMediaType.isCompatibleWith(searchingType))
        return mimeTypeToViewResolver.get(mimeType);
    }
   
    return defaultResolver;
  }
 
  /**
   * Gets the name of the view to resolve from the request.  First looks to see if there is a ResponseMapping
   * annotation on the method that is called, and uses the value attribute of that.  If that is not found,
   * it will use the name of the method that is called.
   *
   * @return
   * @throws Exception
   */
  protected String getViewNameFromRequest() throws Exception {
    HandlerExecutionChain execution = getHandler(getCurrentRequestAttributes().getRequest());
    HandlerMethod hm = (HandlerMethod) execution.getHandler();
    String name = hm.getMethod().getName();
   
    if (hm.getMethod().isAnnotationPresent(ResponseMapping.class)) {
      ResponseMapping responseMapping = hm.getMethod().getAnnotation(ResponseMapping.class);
      name = responseMapping.value()[0];
    }
   
    return name;
  }
 
  protected List<HandlerMapping> getHandlerMappings() {
    if (this.handlerMappings == null) {
      Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(getApplicationContext(), HandlerMapping.class, true, false);
      if (!matchingBeans.isEmpty()) {
        this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
        // We keep HandlerMappings in sorted order.
        OrderComparator.sort(this.handlerMappings);
      }
    }
   
    return this.handlerMappings;
  }
 
  protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    for (HandlerMapping hm : getHandlerMappings()) {
      HandlerExecutionChain handler = hm.getHandler(request);
      if (handler != null) {
        return handler;
      }
    }
    return null;
  }
 
  @Override
  protected Object getCacheKey(String viewName, Locale locale) {
    String name = null;
   
    try {
      name = getViewNameFromRequest();
    } catch (Exception e) {}
   
    String acceptHeader = getCurrentRequestAttributes().getRequest().getHeader("Accept");
   
    return name + "_" + locale + "_" + acceptHeader;
  }
 
  @Override
  public void afterPropertiesSet() throws Exception {
    // After the properties are set, throw the defaults into the mimeTypeMappings
   
    if (mimeTypeToViewResolver.containsKey(MediaType.TEXT_HTML_VALUE) == false) {
      InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
      viewResolver.setPrefix(defaultInternalResourcePrefix);
      viewResolver.setSuffix(defaultInternalResourceSuffix);
      viewResolver.setApplicationContext(getApplicationContext());
     
      mimeTypeToViewResolver.put(MediaType.TEXT_HTML_VALUE, viewResolver);
      if (defaultResolver == null)
        defaultResolver = viewResolver;
    }
   
    if (mimeTypeToViewResolver.containsKey(MediaType.APPLICATION_JSON_VALUE) == false) {
      ViewResolver viewResolver = new ViewResolver() {
        @Override
        public View resolveViewName(String viewName, Locale locale) throws Exception {
          MappingJacksonJsonView jsonView = new MappingJacksonJsonView();
          jsonView.setApplicationContext(getApplicationContext());
          return jsonView;
        }
      };
     
      mimeTypeToViewResolver.put(MediaType.APPLICATION_JSON_VALUE, viewResolver);
    }
   
    if (mimeTypeToViewResolver.containsKey(MediaType.TEXT_XML_VALUE) == false) {
      ViewResolver viewResolver = new ViewResolver() {
        @Override
        public View resolveViewName(String viewName, Locale locale) throws Exception {
          XStreamMarshaller marshaller = new XStreamMarshaller();
          marshaller.setAutodetectAnnotations(true);
          MarshallingView view = new MarshallingView(marshaller);
         
          return view;
        }
      };
     
      mimeTypeToViewResolver.put(MediaType.TEXT_XML_VALUE, viewResolver);
    }
   
  }
 
  protected ServletRequestAttributes getCurrentRequestAttributes() {
    return (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
  }
 
  public void setMimeTypeToViewResolver(Map<String, ViewResolver> mimeTypeToViewResolver) {
    this.mimeTypeToViewResolver = mimeTypeToViewResolver;
  }
 
  /**
   * Sets the default InternalResourceViewResolver Prefix for mapping with the default handler
   * for text/html.
   *
   * @param defaultInternalResourcePrefix
   */
  public void setDefaultInternalResourcePrefix(String defaultInternalResourcePrefix) {
    this.defaultInternalResourcePrefix = defaultInternalResourcePrefix;
  }
 
  /**
   * Sets the default InternalResourceViewResolver Suffix for mapping with the default handler
   * for text/html.
   *
   * @param defaultInternalResourceSuffix
   */
  public void setDefaultInternalResourceSuffix(String defaultInternalResourceSuffix) {
    this.defaultInternalResourceSuffix = defaultInternalResourceSuffix;
  }
 
  /**
   * Sets the default view resolver.  If a header comes in that doesn't match anything (or everything)
   * this is the view resolver that will be used.
   *
   * @param defaultResolver
   */
  public void setDefaultResolver(ViewResolver defaultResolver) {
    this.defaultResolver = defaultResolver;
  }
 
  @Override
  public int getOrder() {
    return order;
  }
 
  public void setOrder(int order) {
    this.order = order;
  }
 
  @Target({ElementType.METHOD, ElementType.TYPE})
  @Retention(RetentionPolicy.RUNTIME)
  @Documented
  @Mapping
  public @interface ResponseMapping {
    String[] value() default {};
  }
}
TOP

Related Classes of com.crygier.spring.util.web.MimeTypeViewResolver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.