DATAREST-67 Can now using Spring Data REST without having to have any JPA dependencies in the classpath. The JPA-specific interceptor that is being used has been moved into a support class that is only created if JPA is in the classpath.

This commit is contained in:
Jon Brisbin
2013-03-06 14:06:44 -06:00
parent b83223d0e8
commit 93d3214115
3 changed files with 147 additions and 110 deletions

View File

@@ -5,9 +5,6 @@ import static org.springframework.util.StringUtils.*;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
@@ -17,6 +14,7 @@ import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.config.RepositoryRestConfiguration;
import org.springframework.data.rest.config.ResourceMapping;
import org.springframework.data.rest.webmvc.support.JpaHelper;
import org.springframework.http.MediaType;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor;
import org.springframework.web.method.HandlerMethod;
@@ -32,113 +30,108 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
*/
public class RepositoryRestHandlerMapping extends RequestMappingHandlerMapping {
@Autowired
private Repositories repositories;
@Autowired
private RepositoryRestConfiguration config;
private EntityManagerFactory entityManagerFactory;
@Autowired
private Repositories repositories;
@Autowired
private RepositoryRestConfiguration config;
@Autowired(required = false)
private JpaHelper jpaHelper;
public RepositoryRestHandlerMapping() {
setOrder(Ordered.LOWEST_PRECEDENCE);
}
public RepositoryRestHandlerMapping() {
setOrder(Ordered.LOWEST_PRECEDENCE);
}
@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this.entityManagerFactory = entityManager.getEntityManagerFactory();
}
@SuppressWarnings({"unchecked"})
@Override
protected HandlerMethod lookupHandlerMethod(String lookupPath,
HttpServletRequest origRequest) throws Exception {
String acceptType = origRequest.getHeader("Accept");
if(null == acceptType) {
acceptType = config.getDefaultMediaType().toString();
}
List<MediaType> acceptHeaderTypes = MediaType.parseMediaTypes(acceptType);
List<MediaType> acceptableTypes = new ArrayList<MediaType>();
for(MediaType mt : acceptHeaderTypes) {
if(("*".equals(mt.getType()) && ("*".equals(mt.getSubtype()))
|| ("application".equals(mt.getType()) && "*".equals(mt.getSubtype())))) {
mt = config.getDefaultMediaType();
}
if(!acceptableTypes.contains(mt)) {
acceptableTypes.add(mt);
}
}
if(acceptableTypes.size() > 1) {
acceptType = collectionToDelimitedString(acceptableTypes, ",");
} else if(acceptableTypes.size() == 1) {
acceptType = acceptableTypes.get(0).toString();
} else {
acceptType = config.getDefaultMediaType().toString();
}
@SuppressWarnings({"unchecked"})
@Override
protected HandlerMethod lookupHandlerMethod(String lookupPath,
HttpServletRequest origRequest) throws Exception {
String acceptType = origRequest.getHeader("Accept");
if(null == acceptType) {
acceptType = config.getDefaultMediaType().toString();
}
List<MediaType> acceptHeaderTypes = MediaType.parseMediaTypes(acceptType);
List<MediaType> acceptableTypes = new ArrayList<MediaType>();
for(MediaType mt : acceptHeaderTypes) {
if(("*".equals(mt.getType()) && ("*".equals(mt.getSubtype()))
|| ("application".equals(mt.getType()) && "*".equals(mt.getSubtype())))) {
mt = config.getDefaultMediaType();
}
if(!acceptableTypes.contains(mt)) {
acceptableTypes.add(mt);
}
}
if(acceptableTypes.size() > 1) {
acceptType = collectionToDelimitedString(acceptableTypes, ",");
} else if(acceptableTypes.size() == 1) {
acceptType = acceptableTypes.get(0).toString();
} else {
acceptType = config.getDefaultMediaType().toString();
}
HttpServletRequest request = new DefaultAcceptTypeHttpServletRequest(origRequest, acceptType);
HttpServletRequest request = new DefaultAcceptTypeHttpServletRequest(origRequest, acceptType);
if(acceptType.contains("javascript")) {
if(null != request.getParameter(config.getJsonpParamName())
|| null != request.getParameter(config.getJsonpOnErrParamName())) {
return super.lookupHandlerMethod(lookupPath, request);
} else {
return null;
}
}
String requestUri = lookupPath;
if(requestUri.startsWith("/")) {
requestUri = requestUri.substring(1);
}
if(!hasText(requestUri)) {
return super.lookupHandlerMethod(lookupPath, request);
}
String[] parts = requestUri.split("/");
if(parts.length == 0) {
// Root request
return super.lookupHandlerMethod(lookupPath, request);
}
if(acceptType.contains("javascript")) {
if(null != request.getParameter(config.getJsonpParamName())
|| null != request.getParameter(config.getJsonpOnErrParamName())) {
return super.lookupHandlerMethod(lookupPath, request);
} else {
return null;
}
}
String requestUri = lookupPath;
if(requestUri.startsWith("/")) {
requestUri = requestUri.substring(1);
}
if(!hasText(requestUri)) {
return super.lookupHandlerMethod(lookupPath, request);
}
String[] parts = requestUri.split("/");
if(parts.length == 0) {
// Root request
return super.lookupHandlerMethod(lookupPath, request);
}
for(Class<?> domainType : repositories) {
RepositoryInformation repoInfo = repositories.getRepositoryInformationFor(domainType);
ResourceMapping mapping = getResourceMapping(config, repoInfo);
if(mapping.getPath().equals(parts[0]) && mapping.isExported()) {
return super.lookupHandlerMethod(lookupPath, request);
}
}
for(Class<?> domainType : repositories) {
RepositoryInformation repoInfo = repositories.getRepositoryInformationFor(domainType);
ResourceMapping mapping = getResourceMapping(config, repoInfo);
if(mapping.getPath().equals(parts[0]) && mapping.isExported()) {
return super.lookupHandlerMethod(lookupPath, request);
}
}
return null;
}
return null;
}
@Override protected boolean isHandler(Class<?> beanType) {
return (RepositoryController.class.isAssignableFrom(beanType)
|| RepositoryEntityController.class.isAssignableFrom(beanType)
|| RepositoryPropertyReferenceController.class.isAssignableFrom(beanType)
|| RepositorySearchController.class.isAssignableFrom(beanType));
}
@Override protected boolean isHandler(Class<?> beanType) {
return (RepositoryController.class.isAssignableFrom(beanType)
|| RepositoryEntityController.class.isAssignableFrom(beanType)
|| RepositoryPropertyReferenceController.class.isAssignableFrom(beanType)
|| RepositorySearchController.class.isAssignableFrom(beanType));
}
@Override protected void extendInterceptors(List<Object> interceptors) {
if(null != jpaHelper) {
Object jpaInterceptor = jpaHelper.getInterceptor();
interceptors.add(jpaInterceptor);
}
}
@Override protected void extendInterceptors(List<Object> interceptors) {
if(null != entityManagerFactory) {
OpenEntityManagerInViewInterceptor omivi = new OpenEntityManagerInViewInterceptor();
omivi.setEntityManagerFactory(entityManagerFactory);
interceptors.add(omivi);
}
}
private static class DefaultAcceptTypeHttpServletRequest extends HttpServletRequestWrapper {
private final String defaultAcceptType;
private static class DefaultAcceptTypeHttpServletRequest extends HttpServletRequestWrapper {
private final String defaultAcceptType;
private DefaultAcceptTypeHttpServletRequest(HttpServletRequest request,
String defaultAcceptType) {
super(request);
this.defaultAcceptType = defaultAcceptType;
}
private DefaultAcceptTypeHttpServletRequest(HttpServletRequest request,
String defaultAcceptType) {
super(request);
this.defaultAcceptType = defaultAcceptType;
}
@Override public String getHeader(String name) {
if("accept".equals(name.toLowerCase())) {
return defaultAcceptType;
} else {
return super.getHeader(name);
}
}
}
@Override public String getHeader(String name) {
if("accept".equals(name.toLowerCase())) {
return defaultAcceptType;
} else {
return super.getHeader(name);
}
}
}
}

View File

@@ -37,6 +37,7 @@ import org.springframework.data.rest.webmvc.RepositoryRestRequestHandlerMethodAr
import org.springframework.data.rest.webmvc.RepositorySearchController;
import org.springframework.data.rest.webmvc.ServerHttpRequestMethodArgumentResolver;
import org.springframework.data.rest.webmvc.convert.UriListHttpMessageConverter;
import org.springframework.data.rest.webmvc.support.JpaHelper;
import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks;
import org.springframework.data.rest.webmvc.support.ValidationExceptionHandler;
import org.springframework.format.support.DefaultFormattingConversionService;
@@ -44,7 +45,6 @@ import org.springframework.hateoas.EntityLinks;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor;
import org.springframework.util.ClassUtils;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
@@ -68,6 +68,10 @@ public class RepositoryRestMvcConfiguration {
"javax.validation.ConstraintViolationException",
RepositoryRestMvcConfiguration.class.getClassLoader()
);
private static final boolean IS_JPA_AVAILABLE = ClassUtils.isPresent(
"javax.persistence.EntityManager",
RepositoryRestMvcConfiguration.class.getClassLoader()
);
@Bean public RepositoriesFactoryBean repositories() {
return new RepositoriesFactoryBean();
@@ -107,6 +111,14 @@ public class RepositoryRestMvcConfiguration {
}
}
@Bean @Lazy public JpaHelper jpaHelper() {
if(IS_JPA_AVAILABLE) {
return new JpaHelper();
} else {
return null;
}
}
/**
* Main configuration for the REST exporter.
*/
@@ -116,15 +128,6 @@ public class RepositoryRestMvcConfiguration {
return config;
}
/**
* For getting access to the {@link javax.persistence.EntityManagerFactory}.
*
* @return
*/
@Bean public PersistenceAnnotationBeanPostProcessor persistenceAnnotationBeanPostProcessor() {
return new PersistenceAnnotationBeanPostProcessor();
}
/**
* {@link org.springframework.beans.factory.config.BeanPostProcessor} to turn beans annotated as {@link
* org.springframework.data.rest.repository.annotation.RepositoryEventHandler}s.

View File

@@ -0,0 +1,41 @@
package org.springframework.data.rest.webmvc.support;
import javax.persistence.EntityManagerFactory;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor;
/**
* @author Jon Brisbin
*/
public class JpaHelper implements BeanFactoryAware {
private Object interceptor = null;
@Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
(ListableBeanFactory)beanFactory,
EntityManagerFactory.class
);
if(beanNames.length != 1) {
throw new BeanInstantiationException(JpaHelper.class,
String.format("Only one EntityManagerFactory expected but %s found",
beanNames.length));
}
String unitName = beanNames[0];
EntityManagerFactory emf = (EntityManagerFactory)beanFactory.getBean(unitName);
OpenEntityManagerInViewInterceptor omivi = new OpenEntityManagerInViewInterceptor();
omivi.setEntityManagerFactory(emf);
this.interceptor = omivi;
}
public Object getInterceptor() {
return interceptor;
}
}