DATAJPA-19 - Added Hades extensions module.
Added DomainClassPropertyEditor and DomainClassConverter to automatically bind domain classes to Spring MVC controller methods. Same applies to PageableArgumentResolver. Polished up some generics. Introduced RepositoryFactoryInformation interface being implemented by RepositoryFactorySupport that allows the extension components to get access to the raw factories, access EntityInformation of it and then find out about the repository interface to lookup the actual repository instance.
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright 2008-2011 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.data.extensions.beans;
|
||||
|
||||
import java.beans.PropertyEditor;
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.beans.PropertyEditorRegistry;
|
||||
import org.springframework.beans.SimpleTypeConverter;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.support.EntityInformation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Generic {@link PropertyEditor} to map entities handled by a
|
||||
* {@link Repository} to their id's and vice versa.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class DomainClassPropertyEditor<T, ID extends Serializable> extends
|
||||
PropertyEditorSupport {
|
||||
|
||||
private final Repository<T, ID> repository;
|
||||
private final EntityInformation<T, ID> information;
|
||||
private final PropertyEditorRegistry registry;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link DomainClassPropertyEditor} for the given
|
||||
* {@link Repository}.
|
||||
*
|
||||
* @param repository
|
||||
* @param registry
|
||||
*/
|
||||
public DomainClassPropertyEditor(Repository<T, ID> repository,
|
||||
EntityInformation<T, ID> information,
|
||||
PropertyEditorRegistry registry) {
|
||||
|
||||
Assert.notNull(repository);
|
||||
Assert.notNull(registry);
|
||||
|
||||
this.repository = repository;
|
||||
this.information = information;
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setAsText(String idAsString) throws IllegalArgumentException {
|
||||
|
||||
if (!StringUtils.hasText(idAsString)) {
|
||||
setValue(null);
|
||||
return;
|
||||
}
|
||||
|
||||
setValue(repository.findById(getId(idAsString)));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.beans.PropertyEditorSupport#getAsText()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public String getAsText() {
|
||||
|
||||
T entity = (T) getValue();
|
||||
|
||||
if (null == entity) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object id = getId(entity);
|
||||
return id == null ? null : id.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Looks up the id of the given entity using one of the
|
||||
* {@link org.synyx.hades.dao.orm.GenericDaoSupport.IdAware} implementations
|
||||
* of Hades.
|
||||
*
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
private ID getId(T entity) {
|
||||
|
||||
return information.getId(entity);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the actual typed id. Looks up an available customly registered
|
||||
* {@link PropertyEditor} from the {@link PropertyEditorRegistry} before
|
||||
* falling back on a {@link SimpleTypeConverter} to translate the
|
||||
* {@link String} id into the type one.
|
||||
*
|
||||
* @param idAsString
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private ID getId(String idAsString) {
|
||||
|
||||
Class<ID> idClass = information.getIdType();
|
||||
|
||||
PropertyEditor idEditor = registry.findCustomEditor(idClass, null);
|
||||
|
||||
if (idEditor != null) {
|
||||
idEditor.setAsText(idAsString);
|
||||
return (ID) idEditor.getValue();
|
||||
}
|
||||
|
||||
return new SimpleTypeConverter()
|
||||
.convertIfNecessary(idAsString, idClass);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj == null || this.getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DomainClassPropertyEditor<?, ?> that =
|
||||
(DomainClassPropertyEditor<?, ?>) obj;
|
||||
|
||||
return this.repository.equals(that.repository)
|
||||
&& this.registry.equals(that.registry)
|
||||
&& this.information.equals(that.information);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int hashCode = 17;
|
||||
hashCode += repository.hashCode() * 32;
|
||||
hashCode += information.hashCode() * 32;
|
||||
hashCode += registry.hashCode() * 32;
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2008-2011 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.data.extensions.beans;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.beans.PropertyEditorRegistrar;
|
||||
import org.springframework.beans.PropertyEditorRegistry;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.support.EntityInformation;
|
||||
import org.springframework.data.repository.support.RepositoryFactoryInformation;
|
||||
|
||||
|
||||
/**
|
||||
* Simple helper class to use Hades DAOs to provide
|
||||
* {@link java.beans.PropertyEditor}s for domain classes. To get this working
|
||||
* configure a
|
||||
* {@link org.springframework.web.bind.support.ConfigurableWebBindingInitializer}
|
||||
* for your
|
||||
* {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter}
|
||||
* and register the {@link DomainClassPropertyEditorRegistrar} there: <code>
|
||||
* <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
|
||||
* <property name="webBindingInitializer">
|
||||
* <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
|
||||
* <property name="propertyEditorRegistrars">
|
||||
* <bean class="org.springframework.data.extensions.beans.DomainClassPropertyEditorRegistrar" />
|
||||
* </property>
|
||||
* </bean>
|
||||
* </property>
|
||||
* </bean>
|
||||
* </code> Make sure this bean declaration is in the {@link ApplicationContext}
|
||||
* created by the {@link DispatcherServlet} whereas the repositories need to be
|
||||
* declared in the root
|
||||
* {@link org.springframework.web.context.WebApplicationContext}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class DomainClassPropertyEditorRegistrar implements
|
||||
PropertyEditorRegistrar, ApplicationContextAware {
|
||||
|
||||
private final Map<EntityInformation<Object, Serializable>, Repository<Object, Serializable>> repositories =
|
||||
new HashMap<EntityInformation<Object, Serializable>, Repository<Object, Serializable>>();
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.PropertyEditorRegistrar#registerCustomEditors
|
||||
* (org.springframework.beans.PropertyEditorRegistry)
|
||||
*/
|
||||
public void registerCustomEditors(PropertyEditorRegistry registry) {
|
||||
|
||||
for (Entry<EntityInformation<Object, Serializable>, Repository<Object, Serializable>> entry : repositories
|
||||
.entrySet()) {
|
||||
|
||||
EntityInformation<Object, Serializable> metadata = entry.getKey();
|
||||
Repository<Object, Serializable> repository = entry.getValue();
|
||||
|
||||
DomainClassPropertyEditor<Object, Serializable> editor =
|
||||
new DomainClassPropertyEditor<Object, Serializable>(
|
||||
repository, metadata, registry);
|
||||
|
||||
registry.registerCustomEditor(metadata.getJavaType(), editor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.context.ApplicationContextAware#setApplicationContext
|
||||
* (org.springframework.context.ApplicationContext)
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void setApplicationContext(ApplicationContext context) {
|
||||
|
||||
Collection<RepositoryFactoryInformation> providers =
|
||||
BeanFactoryUtils.beansOfTypeIncludingAncestors(context,
|
||||
RepositoryFactoryInformation.class).values();
|
||||
|
||||
for (RepositoryFactoryInformation information : providers) {
|
||||
|
||||
EntityInformation<Object, Serializable> metadata =
|
||||
information.getEntityInformation();
|
||||
Class<Repository<Object, Serializable>> objectType =
|
||||
information.getRepositoryInterface();
|
||||
Repository<Object, Serializable> repository =
|
||||
BeanFactoryUtils.beanOfType(context, objectType);
|
||||
|
||||
this.repositories.put(metadata, repository);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2008-2011 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.data.extensions.converter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.ConditionalGenericConverter;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.support.EntityInformation;
|
||||
import org.springframework.data.repository.support.RepositoryFactoryInformation;
|
||||
|
||||
|
||||
/**
|
||||
* {@link org.springframework.core.convert.converter.Converter} to convert
|
||||
* arbitrary input into domain classes managed by Spring Data {@link Repository}
|
||||
* s. The implementation uses a {@link ConversionService} in turn to convert the
|
||||
* source type into the domain class' id type which is then converted into a
|
||||
* domain class object by using a {@link Repository}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class DomainClassConverter implements ConditionalGenericConverter,
|
||||
ApplicationContextAware {
|
||||
|
||||
private final Map<EntityInformation<?, Serializable>, Repository<?, Serializable>> repositories =
|
||||
new HashMap<EntityInformation<?, Serializable>, Repository<?, Serializable>>();
|
||||
private final ConversionService service;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link DomainClassConverter}.
|
||||
*
|
||||
* @param service
|
||||
*/
|
||||
public DomainClassConverter(ConversionService service) {
|
||||
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.core.convert.converter.GenericConverter#
|
||||
* getConvertibleTypes()
|
||||
*/
|
||||
public Set<ConvertiblePair> getConvertibleTypes() {
|
||||
|
||||
return Collections.singleton(new ConvertiblePair(Object.class,
|
||||
Object.class));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.core.convert.converter.GenericConverter#convert(java
|
||||
* .lang.Object, org.springframework.core.convert.TypeDescriptor,
|
||||
* org.springframework.core.convert.TypeDescriptor)
|
||||
*/
|
||||
public Object convert(Object source, TypeDescriptor sourceType,
|
||||
TypeDescriptor targetType) {
|
||||
|
||||
EntityInformation<?, Serializable> info =
|
||||
getRepositoryForDomainType(targetType.getType());
|
||||
|
||||
Repository<?, Serializable> repository = repositories.get(info);
|
||||
Serializable id = service.convert(source, info.getIdType());
|
||||
return repository.findById(id);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.core.convert.converter.ConditionalGenericConverter
|
||||
* #matches(org.springframework.core.convert.TypeDescriptor,
|
||||
* org.springframework.core.convert.TypeDescriptor)
|
||||
*/
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
|
||||
EntityInformation<?, ?> info =
|
||||
getRepositoryForDomainType(targetType.getType());
|
||||
|
||||
if (info == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return service.canConvert(sourceType.getType(), info.getIdType());
|
||||
}
|
||||
|
||||
|
||||
private EntityInformation<?, Serializable> getRepositoryForDomainType(
|
||||
Class<?> domainType) {
|
||||
|
||||
for (EntityInformation<?, Serializable> information : repositories
|
||||
.keySet()) {
|
||||
|
||||
if (domainType.equals(information.getJavaType())) {
|
||||
return information;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.context.ApplicationContextAware#setApplicationContext
|
||||
* (org.springframework.context.ApplicationContext)
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void setApplicationContext(ApplicationContext context) {
|
||||
|
||||
Collection<RepositoryFactoryInformation> providers =
|
||||
BeanFactoryUtils.beansOfTypeIncludingAncestors(context,
|
||||
RepositoryFactoryInformation.class).values();
|
||||
|
||||
for (RepositoryFactoryInformation entry : providers) {
|
||||
|
||||
EntityInformation<Object, Serializable> metadata =
|
||||
entry.getEntityInformation();
|
||||
Class<Repository<Object, Serializable>> objectType =
|
||||
entry.getRepositoryInterface();
|
||||
Repository<Object, Serializable> repository =
|
||||
BeanFactoryUtils.beanOfType(context, objectType);
|
||||
|
||||
this.repositories.put(metadata, repository);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
* Copyright 2008-2011 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.data.extensions.web;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.validation.DataBinder;
|
||||
import org.springframework.web.bind.ServletRequestDataBinder;
|
||||
import org.springframework.web.bind.ServletRequestParameterPropertyValues;
|
||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
|
||||
/**
|
||||
* Extracts paging information from web requests and thus allows injecting
|
||||
* {@link Pageable} instances into controller methods. Request properties to be
|
||||
* parsed can be configured. Default configuration uses request properties
|
||||
* beginning with {@link #DEFAULT_PREFIX}{@link #DEFAULT_SEPARATOR}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class PageableArgumentResolver implements WebArgumentResolver {
|
||||
|
||||
private static final Pageable DEFAULT_PAGE_REQUEST = new PageRequest(0, 10);
|
||||
private static final String DEFAULT_PREFIX = "page";
|
||||
private static final String DEFAULT_SEPARATOR = ".";
|
||||
|
||||
private Pageable fallbackPagable = DEFAULT_PAGE_REQUEST;
|
||||
private String prefix = DEFAULT_PREFIX;
|
||||
private String separator = DEFAULT_SEPARATOR;
|
||||
|
||||
|
||||
/**
|
||||
* Setter to configure a fallback instance of {@link Pageable} that is being
|
||||
* used to back missing parameters. Defaults to
|
||||
* {@value #DEFAULT_PAGE_REQUEST}.
|
||||
*
|
||||
* @param fallbackPagable the fallbackPagable to set
|
||||
*/
|
||||
public void setFallbackPagable(Pageable fallbackPagable) {
|
||||
|
||||
this.fallbackPagable =
|
||||
null == fallbackPagable ? DEFAULT_PAGE_REQUEST
|
||||
: fallbackPagable;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Setter to configure the prefix of request parameters to be used to
|
||||
* retrieve paging information. Defaults to {@link #DEFAULT_PREFIX}.
|
||||
*
|
||||
* @param prefix the prefix to set
|
||||
*/
|
||||
public void setPrefix(String prefix) {
|
||||
|
||||
this.prefix = null == prefix ? DEFAULT_PREFIX : prefix;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Setter to configure the separator between prefix and actual property
|
||||
* value. Defaults to {@link #DEFAULT_SEPARATOR}.
|
||||
*
|
||||
* @param separator the separator to set
|
||||
*/
|
||||
public void setSeparator(String separator) {
|
||||
|
||||
this.separator = null == separator ? DEFAULT_SEPARATOR : separator;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.web.bind.support.WebArgumentResolver#resolveArgument
|
||||
* (org.springframework.core.MethodParameter,
|
||||
* org.springframework.web.context.request.NativeWebRequest)
|
||||
*/
|
||||
public Object resolveArgument(MethodParameter methodParameter,
|
||||
NativeWebRequest webRequest) {
|
||||
|
||||
if (methodParameter.getParameterType().equals(Pageable.class)) {
|
||||
|
||||
assertPageableUniqueness(methodParameter);
|
||||
|
||||
Pageable request =
|
||||
getDefaultFromAnnotationOrFallback(methodParameter);
|
||||
|
||||
ServletRequest servletRequest =
|
||||
(ServletRequest) webRequest.getNativeRequest();
|
||||
|
||||
PropertyValues propertyValues =
|
||||
new ServletRequestParameterPropertyValues(servletRequest,
|
||||
getPrefix(methodParameter), separator);
|
||||
|
||||
DataBinder binder = new ServletRequestDataBinder(request);
|
||||
|
||||
binder.initDirectFieldAccess();
|
||||
binder.registerCustomEditor(Sort.class, new SortPropertyEditor(
|
||||
"sort.dir", propertyValues));
|
||||
binder.bind(propertyValues);
|
||||
|
||||
if (request.getPageNumber() > 0) {
|
||||
|
||||
request =
|
||||
new PageRequest(request.getPageNumber() - 1,
|
||||
request.getPageSize(), request.getSort());
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
return UNRESOLVED;
|
||||
}
|
||||
|
||||
|
||||
private Pageable getDefaultFromAnnotationOrFallback(
|
||||
MethodParameter methodParameter) {
|
||||
|
||||
// search for PageableDefaults annotation
|
||||
for (Annotation annotation : methodParameter.getParameterAnnotations()) {
|
||||
if (annotation instanceof PageableDefaults) {
|
||||
PageableDefaults defaults = (PageableDefaults) annotation;
|
||||
// +1 is because we substract 1 later
|
||||
return new PageRequest(defaults.pageNumber() + 1,
|
||||
defaults.value());
|
||||
}
|
||||
}
|
||||
|
||||
// Construct request with fallback request to ensure sensible
|
||||
// default values. Create fresh copy as Spring will manipulate the
|
||||
// instance under the covers
|
||||
return new PageRequest(fallbackPagable.getPageNumber(),
|
||||
fallbackPagable.getPageSize(), fallbackPagable.getSort());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolves the prefix to use to bind properties from. Will prepend a
|
||||
* possible {@link Qualifier} if available or return the configured prefix
|
||||
* otherwise.
|
||||
*
|
||||
* @param parameter
|
||||
* @return
|
||||
*/
|
||||
private String getPrefix(MethodParameter parameter) {
|
||||
|
||||
for (Annotation annotation : parameter.getParameterAnnotations()) {
|
||||
if (annotation instanceof Qualifier) {
|
||||
return new StringBuilder(((Qualifier) annotation).value())
|
||||
.append("_").append(prefix).toString();
|
||||
}
|
||||
}
|
||||
|
||||
return prefix;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Asserts uniqueness of all {@link Pageable} parameters of the method of
|
||||
* the given {@link MethodParameter}.
|
||||
*
|
||||
* @param parameter
|
||||
*/
|
||||
private void assertPageableUniqueness(MethodParameter parameter) {
|
||||
|
||||
Method method = parameter.getMethod();
|
||||
|
||||
if (containsMoreThanOnePageableParameter(method)) {
|
||||
Annotation[][] annotations = method.getParameterAnnotations();
|
||||
assertQualifiersFor(method.getParameterTypes(), annotations);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link Method} has more than one
|
||||
* {@link Pageable} parameter.
|
||||
*
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
private boolean containsMoreThanOnePageableParameter(Method method) {
|
||||
|
||||
boolean pageableFound = false;
|
||||
|
||||
for (Class<?> type : method.getParameterTypes()) {
|
||||
|
||||
if (pageableFound && type.equals(Pageable.class)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type.equals(Pageable.class)) {
|
||||
pageableFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that every {@link Pageable} parameter of the given parameters
|
||||
* carries an {@link Qualifier} annotation to distinguish them from each
|
||||
* other.
|
||||
*
|
||||
* @param parameterTypes
|
||||
* @param annotations
|
||||
*/
|
||||
private void assertQualifiersFor(Class<?>[] parameterTypes,
|
||||
Annotation[][] annotations) {
|
||||
|
||||
Set<String> values = new HashSet<String>();
|
||||
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
|
||||
if (Pageable.class.equals(parameterTypes[i])) {
|
||||
|
||||
Qualifier qualifier = findAnnotation(annotations[i]);
|
||||
|
||||
if (null == qualifier) {
|
||||
throw new IllegalStateException(
|
||||
"Ambiguous Pageable arguments in handler method. If you use multiple parameters of type Pageable you need to qualify them with @Qualifier");
|
||||
}
|
||||
|
||||
if (values.contains(qualifier.value())) {
|
||||
throw new IllegalStateException(
|
||||
"Values of the user Qualifiers must be unique!");
|
||||
}
|
||||
|
||||
values.add(qualifier.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a {@link Qualifier} annotation from the given array of
|
||||
* {@link Annotation}s. Returns {@literal null} if the array does not
|
||||
* contain a {@link Qualifier} annotation.
|
||||
*
|
||||
* @param annotations
|
||||
* @return
|
||||
*/
|
||||
private Qualifier findAnnotation(Annotation[] annotations) {
|
||||
|
||||
for (Annotation annotation : annotations) {
|
||||
if (annotation instanceof Qualifier) {
|
||||
return (Qualifier) annotation;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link java.beans.PropertyEditor} to create {@link Sort} instances from
|
||||
* textual representations. The implementation interprets the string as a
|
||||
* comma separated list where the first entry is the sort direction (
|
||||
* {@code asc}, {@code desc}) followed by the properties to sort by.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class SortPropertyEditor extends PropertyEditorSupport {
|
||||
|
||||
private final String orderProperty;
|
||||
private final PropertyValues values;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link SortPropertyEditor}.
|
||||
*
|
||||
* @param orderProperty
|
||||
* @param values
|
||||
*/
|
||||
public SortPropertyEditor(String orderProperty, PropertyValues values) {
|
||||
|
||||
this.orderProperty = orderProperty;
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setAsText(String text) throws IllegalArgumentException {
|
||||
|
||||
PropertyValue rawOrder = values.getPropertyValue(orderProperty);
|
||||
Direction order =
|
||||
null == rawOrder ? Direction.ASC : Direction
|
||||
.fromString(rawOrder.getValue().toString());
|
||||
|
||||
setValue(new Sort(order, text));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2008-2011 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.data.extensions.web;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
|
||||
/**
|
||||
* Annotation to set defaults when injecting a {@link Pageable} into a
|
||||
* controller method.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface PageableDefaults {
|
||||
|
||||
/**
|
||||
* The default-size the injected
|
||||
* {@link org.springframework.data.domain.Pageable} should get if no
|
||||
* corresponding parameter defined in request (default is 10).
|
||||
*/
|
||||
int value() default 10;
|
||||
|
||||
|
||||
/**
|
||||
* The default-pagenumber the injected
|
||||
* {@link org.synyx.hades.domain.Pageable} should get if no corresponding
|
||||
* parameter defined in request (default is 0).
|
||||
*/
|
||||
int pageNumber() default 0;
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright 2008-2011 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.data.extensions.beans;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.beans.PropertyEditorRegistry;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.support.EntityInformation;
|
||||
import org.springframework.data.repository.support.RepositoryFactoryInformation;
|
||||
|
||||
|
||||
/**
|
||||
* Unit test for {@link DomainClassPropertyEditorRegistrar}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DomainClassPropertyEditorRegistrarUnitTests {
|
||||
|
||||
DomainClassPropertyEditorRegistrar registrar =
|
||||
new DomainClassPropertyEditorRegistrar();
|
||||
@Mock
|
||||
ApplicationContext context;
|
||||
@Mock
|
||||
PropertyEditorRegistry registry;
|
||||
@Mock
|
||||
EntityRepository repository;
|
||||
@Mock
|
||||
EntityInformation<Entity, Long> information;
|
||||
@Mock
|
||||
RepositoryFactoryInformation<Entity, Long> provider;
|
||||
|
||||
DomainClassPropertyEditor<Entity, Long> reference;
|
||||
|
||||
|
||||
@Before
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void setup() {
|
||||
|
||||
when(information.getJavaType()).thenReturn(Entity.class);
|
||||
when(provider.getEntityInformation()).thenReturn(information);
|
||||
when(provider.getRepositoryInterface()).thenReturn(
|
||||
(Class) EntityRepository.class);
|
||||
Map<String, EntityRepository> map = getBeanAsMap(repository);
|
||||
when(context.getBeansOfType(EntityRepository.class)).thenReturn(map);
|
||||
|
||||
reference =
|
||||
new DomainClassPropertyEditor<Entity, Long>(repository,
|
||||
information, registry);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void addsRepositoryForEntityIfAvailableInAppContext()
|
||||
throws Exception {
|
||||
|
||||
letContextContain(provider);
|
||||
registrar.setApplicationContext(context);
|
||||
registrar.registerCustomEditors(registry);
|
||||
|
||||
verify(registry).registerCustomEditor(eq(Entity.class), eq(reference));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void doesNotAddDaoAtAllIfNoDaosFound() throws Exception {
|
||||
|
||||
letContextContain(provider);
|
||||
registrar.registerCustomEditors(registry);
|
||||
|
||||
verify(registry, never()).registerCustomEditor(eq(Entity.class),
|
||||
eq(reference));
|
||||
}
|
||||
|
||||
|
||||
private void letContextContain(Object bean) {
|
||||
|
||||
Map<String, Object> beanMap = getBeanAsMap(bean);
|
||||
|
||||
when(context.getBeansOfType(argThat(is(subtypeOf(bean.getClass())))))
|
||||
.thenReturn(beanMap);
|
||||
}
|
||||
|
||||
|
||||
private <T> Map<String, T> getBeanAsMap(T bean) {
|
||||
|
||||
Map<String, T> beanMap = new HashMap<String, T>();
|
||||
beanMap.put(bean.getClass().getName(), bean);
|
||||
return beanMap;
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class Entity implements Serializable {
|
||||
|
||||
}
|
||||
|
||||
private static interface EntityRepository extends Repository<Entity, Long> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static <T> TypeSafeMatcher<Class<T>> subtypeOf(
|
||||
final Class<? extends T> type) {
|
||||
|
||||
return new TypeSafeMatcher<Class<T>>() {
|
||||
|
||||
public void describeTo(Description arg0) {
|
||||
|
||||
arg0.appendText("not a subtype of");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean matchesSafely(Class<T> arg0) {
|
||||
|
||||
return arg0.isAssignableFrom(type);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright 2008-2011 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.data.extensions.beans;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.beans.PropertyEditor;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.beans.PropertyEditorRegistry;
|
||||
import org.springframework.data.domain.Persistable;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.support.EntityInformation;
|
||||
|
||||
|
||||
/**
|
||||
* Unit test for {@link DomainClassPropertyEditor}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DomainClassPropertyEditorUnitTests {
|
||||
|
||||
DomainClassPropertyEditor<User, Integer> editor;
|
||||
|
||||
@Mock
|
||||
PropertyEditorRegistry registry;
|
||||
@Mock
|
||||
UserRepository userRepository;
|
||||
@Mock
|
||||
EntityInformation<User, Integer> information;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
when(information.getIdType()).thenReturn(Integer.class);
|
||||
editor =
|
||||
new DomainClassPropertyEditor<User, Integer>(userRepository,
|
||||
information, registry);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void convertsPlainIdTypeCorrectly() throws Exception {
|
||||
|
||||
User user = new User(1);
|
||||
when(information.getId(user)).thenReturn(user.getId());
|
||||
when(userRepository.findById(1)).thenReturn(user);
|
||||
|
||||
editor.setAsText("1");
|
||||
|
||||
verify(userRepository, times(1)).findById(1);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void convertsEntityToIdCorrectly() throws Exception {
|
||||
|
||||
User user = new User(1);
|
||||
editor.setValue(user);
|
||||
when(information.getId(user)).thenReturn(user.getId());
|
||||
assertThat(editor.getAsText(), is("1"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void usesCustomEditorIfConfigured() throws Exception {
|
||||
|
||||
PropertyEditor customEditor = mock(PropertyEditor.class);
|
||||
when(customEditor.getValue()).thenReturn(1);
|
||||
|
||||
when(registry.findCustomEditor(Integer.class, null)).thenReturn(
|
||||
customEditor);
|
||||
|
||||
convertsPlainIdTypeCorrectly();
|
||||
|
||||
verify(customEditor, times(1)).setAsText("1");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void returnsNullIdIfNoEntitySet() throws Exception {
|
||||
|
||||
editor.setValue(null);
|
||||
assertThat(editor.getAsText(), is(nullValue()));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void resetsValueToNullAfterEmptyStringConversion() throws Exception {
|
||||
|
||||
assertValueResetToNullAfterConverting("");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void resetsValueToNullAfterNullStringConversion() throws Exception {
|
||||
|
||||
assertValueResetToNullAfterConverting(null);
|
||||
}
|
||||
|
||||
|
||||
private void assertValueResetToNullAfterConverting(String source)
|
||||
throws Exception {
|
||||
|
||||
convertsPlainIdTypeCorrectly();
|
||||
assertThat(editor.getValue(), is(notNullValue()));
|
||||
|
||||
editor.setAsText(source);
|
||||
assertThat(editor.getValue(), is(nullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample entity.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static class User implements Persistable<Integer> {
|
||||
|
||||
private Integer id;
|
||||
|
||||
|
||||
public User(Integer id) {
|
||||
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Persistable#getId()
|
||||
*/
|
||||
@Override
|
||||
public Integer getId() {
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Persistable#isNew()
|
||||
*/
|
||||
@Override
|
||||
public boolean isNew() {
|
||||
|
||||
return getId() != null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample generic DAO interface.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static interface UserRepository extends Repository<User, Integer> {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright 2008-2011 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.data.extensions.conversion;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.data.extensions.converter.DomainClassConverter;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.support.EntityInformation;
|
||||
import org.springframework.data.repository.support.RepositoryFactoryInformation;
|
||||
|
||||
|
||||
/**
|
||||
* Unit test for {@link DomainClassConverter}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DomainClassConverterUnitTests {
|
||||
|
||||
static final User USER = new User();
|
||||
|
||||
DomainClassConverter converter;
|
||||
|
||||
TypeDescriptor sourceDescriptor;
|
||||
TypeDescriptor targetDescriptor;
|
||||
|
||||
Map<String, RepositoryFactoryInformation> providers;
|
||||
|
||||
@Mock
|
||||
ApplicationContext context;
|
||||
@Mock
|
||||
UserRepository repository;
|
||||
@Mock
|
||||
ConversionService service;
|
||||
@Mock
|
||||
EntityInformation<User, Long> information;
|
||||
@Mock
|
||||
RepositoryFactoryInformation<User, Long> provider;
|
||||
|
||||
|
||||
@Before
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void setUp() {
|
||||
|
||||
converter = new DomainClassConverter(service);
|
||||
providers = new HashMap<String, RepositoryFactoryInformation>();
|
||||
|
||||
sourceDescriptor = TypeDescriptor.valueOf(String.class);
|
||||
targetDescriptor = TypeDescriptor.valueOf(User.class);
|
||||
|
||||
Map<String, UserRepository> map = getBeanAsMap(repository);
|
||||
when(context.getBeansOfType(UserRepository.class)).thenReturn(map);
|
||||
when(context.getBeansOfType(RepositoryFactoryInformation.class))
|
||||
.thenReturn(providers);
|
||||
when(provider.getEntityInformation()).thenReturn(information);
|
||||
when(provider.getRepositoryInterface()).thenReturn(
|
||||
(Class) UserRepository.class);
|
||||
when(information.getJavaType()).thenReturn(User.class);
|
||||
when(information.getIdType()).thenReturn(Long.class);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void matchFailsIfNoDaoAvailable() throws Exception {
|
||||
|
||||
converter.setApplicationContext(context);
|
||||
assertMatches(false);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void matchesIfConversionInBetweenIsPossible() throws Exception {
|
||||
|
||||
letContextContain(provider);
|
||||
converter.setApplicationContext(context);
|
||||
|
||||
when(service.canConvert(String.class, Long.class)).thenReturn(true);
|
||||
|
||||
assertMatches(true);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void matchFailsIfNoIntermediateConversionIsPossible()
|
||||
throws Exception {
|
||||
|
||||
letContextContain(provider);
|
||||
converter.setApplicationContext(context);
|
||||
|
||||
when(service.canConvert(String.class, Long.class)).thenReturn(false);
|
||||
|
||||
assertMatches(false);
|
||||
}
|
||||
|
||||
|
||||
private void assertMatches(boolean matchExpected) {
|
||||
|
||||
assertThat(converter.matches(sourceDescriptor, targetDescriptor),
|
||||
is(matchExpected));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void convertsStringToUserCorrectly() throws Exception {
|
||||
|
||||
letContextContain(provider);
|
||||
converter.setApplicationContext(context);
|
||||
|
||||
when(service.canConvert(String.class, Long.class)).thenReturn(true);
|
||||
when(service.convert(anyString(), eq(Long.class))).thenReturn(1L);
|
||||
when(repository.findById(1L)).thenReturn(USER);
|
||||
|
||||
Object user =
|
||||
converter.convert("1", sourceDescriptor, targetDescriptor);
|
||||
assertThat(user, is(instanceOf(User.class)));
|
||||
assertThat(user, is((Object) USER));
|
||||
}
|
||||
|
||||
|
||||
private void letContextContain(Object bean) {
|
||||
|
||||
Map<String, Object> beanMap = getBeanAsMap(bean);
|
||||
when(context.getBeansOfType(argThat(is(subtypeOf(bean.getClass())))))
|
||||
.thenReturn(beanMap);
|
||||
}
|
||||
|
||||
|
||||
private <T> Map<String, T> getBeanAsMap(T bean) {
|
||||
|
||||
Map<String, T> beanMap = new HashMap<String, T>();
|
||||
beanMap.put(bean.getClass().getName(), bean);
|
||||
return beanMap;
|
||||
}
|
||||
|
||||
|
||||
private static <T> TypeSafeMatcher<Class<T>> subtypeOf(
|
||||
final Class<? extends T> type) {
|
||||
|
||||
return new TypeSafeMatcher<Class<T>>() {
|
||||
|
||||
public void describeTo(Description arg0) {
|
||||
|
||||
arg0.appendText("not a subtype of");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean matchesSafely(Class<T> arg0) {
|
||||
|
||||
return arg0.isAssignableFrom(type);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class User {
|
||||
|
||||
}
|
||||
|
||||
private static interface UserRepository extends Repository<User, Long> {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright 2008-2011 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.data.extensions.web;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
|
||||
/**
|
||||
* Unit test for {@link PageableArgumentResolver}.
|
||||
*
|
||||
* @author Oliver Gierke - gierke@synyx.de
|
||||
*/
|
||||
public class PageableArgumentResolverUnitTests {
|
||||
|
||||
Method correctMethod;
|
||||
Method failedMethod;
|
||||
Method invalidQualifiers;
|
||||
Method defaultsMethod;
|
||||
|
||||
MockHttpServletRequest request;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws SecurityException, NoSuchMethodException {
|
||||
|
||||
correctMethod =
|
||||
SampleController.class.getMethod("correctMethod",
|
||||
Pageable.class, Pageable.class);
|
||||
failedMethod =
|
||||
SampleController.class.getMethod("failedMethod",
|
||||
Pageable.class, Pageable.class);
|
||||
invalidQualifiers =
|
||||
SampleController.class.getMethod("invalidQualifiers",
|
||||
Pageable.class, Pageable.class);
|
||||
|
||||
defaultsMethod =
|
||||
SampleController.class.getMethod("defaultsMethod",
|
||||
Pageable.class);
|
||||
|
||||
request = new MockHttpServletRequest();
|
||||
|
||||
// Add pagination info for foo table
|
||||
request.addParameter("foo_page.size", "50");
|
||||
request.addParameter("foo_page.sort", "foo");
|
||||
request.addParameter("foo_page.sort.dir", "asc");
|
||||
|
||||
// Add pagination info for bar table
|
||||
request.addParameter("bar_page.size", "60");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testname() throws Exception {
|
||||
|
||||
assertSizeForPrefix(50, new Sort(Direction.ASC, "foo"), 0);
|
||||
assertSizeForPrefix(60, null, 1);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void rejectsInvalidlyMappedPageables() throws Exception {
|
||||
|
||||
MethodParameter parameter = new MethodParameter(failedMethod, 0);
|
||||
NativeWebRequest webRequest = new ServletWebRequest(request);
|
||||
|
||||
new PageableArgumentResolver().resolveArgument(parameter, webRequest);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void rejectsInvalidQualifiers() throws Exception {
|
||||
|
||||
MethodParameter parameter = new MethodParameter(invalidQualifiers, 0);
|
||||
NativeWebRequest webRequest = new ServletWebRequest(request);
|
||||
|
||||
new PageableArgumentResolver().resolveArgument(parameter, webRequest);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void assertDefaults() throws Exception {
|
||||
|
||||
MethodParameter parameter = new MethodParameter(defaultsMethod, 0);
|
||||
NativeWebRequest webRequest =
|
||||
new ServletWebRequest(new MockHttpServletRequest());
|
||||
PageableArgumentResolver resolver = new PageableArgumentResolver();
|
||||
Object argument = resolver.resolveArgument(parameter, webRequest);
|
||||
|
||||
assertTrue(argument instanceof Pageable);
|
||||
|
||||
Pageable pageable = (Pageable) argument;
|
||||
assertEquals(SampleController.DEFAULT_PAGESIZE, pageable.getPageSize());
|
||||
assertEquals(SampleController.DEFAULT_PAGENUMBER,
|
||||
pageable.getPageNumber());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void assertOverridesDefaults() throws Exception {
|
||||
|
||||
Integer sizeParam = 5;
|
||||
|
||||
MethodParameter parameter = new MethodParameter(defaultsMethod, 0);
|
||||
MockHttpServletRequest mockRequest = new MockHttpServletRequest();
|
||||
|
||||
mockRequest.addParameter("page.page", sizeParam.toString());
|
||||
NativeWebRequest webRequest = new ServletWebRequest(mockRequest);
|
||||
PageableArgumentResolver resolver = new PageableArgumentResolver();
|
||||
Object argument = resolver.resolveArgument(parameter, webRequest);
|
||||
|
||||
assertTrue(argument instanceof Pageable);
|
||||
|
||||
Pageable pageable = (Pageable) argument;
|
||||
assertEquals(SampleController.DEFAULT_PAGESIZE, pageable.getPageSize());
|
||||
assertEquals(sizeParam - 1, pageable.getPageNumber());
|
||||
}
|
||||
|
||||
|
||||
private void assertSizeForPrefix(int size, Sort sort, int index)
|
||||
throws Exception {
|
||||
|
||||
MethodParameter parameter = new MethodParameter(correctMethod, index);
|
||||
NativeWebRequest webRequest = new ServletWebRequest(request);
|
||||
|
||||
PageableArgumentResolver resolver = new PageableArgumentResolver();
|
||||
|
||||
Object argument = resolver.resolveArgument(parameter, webRequest);
|
||||
assertTrue(argument instanceof Pageable);
|
||||
|
||||
Pageable pageable = (Pageable) argument;
|
||||
assertEquals(size, pageable.getPageSize());
|
||||
|
||||
if (null != sort) {
|
||||
assertEquals(sort, pageable.getSort());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private class SampleController {
|
||||
|
||||
static final int DEFAULT_PAGESIZE = 198;
|
||||
static final int DEFAULT_PAGENUMBER = 42;
|
||||
|
||||
|
||||
public void defaultsMethod(
|
||||
@PageableDefaults(value = DEFAULT_PAGESIZE, pageNumber = DEFAULT_PAGENUMBER) Pageable pageable) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void correctMethod(@Qualifier("foo") Pageable first,
|
||||
@Qualifier("bar") Pageable second) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void failedMethod(Pageable first, Pageable second) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void invalidQualifiers(@Qualifier("foo") Pageable first,
|
||||
@Qualifier("foo") Pageable second) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user