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:
1
pom.xml
1
pom.xml
@@ -12,6 +12,7 @@
|
||||
<module>spring-data-commons-parent</module>
|
||||
<module>spring-data-commons-core</module>
|
||||
<module>spring-data-commons-aspects</module>
|
||||
<module>spring-data-commons-extensions</module>
|
||||
</modules>
|
||||
|
||||
<developers>
|
||||
|
||||
@@ -89,7 +89,7 @@ public class QueryMethod {
|
||||
}
|
||||
|
||||
|
||||
public EntityMetadata<?> getEntityMetadata() {
|
||||
public EntityMetadata<?> getEntityInformation() {
|
||||
|
||||
return new EntityMetadata() {
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
@@ -24,8 +26,8 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class AbstractEntityInformation<T> implements
|
||||
EntityInformation<T> {
|
||||
public abstract class AbstractEntityInformation<T, ID extends Serializable> implements
|
||||
EntityInformation<T, ID> {
|
||||
|
||||
private final Class<T> domainClass;
|
||||
|
||||
|
||||
@@ -15,13 +15,15 @@
|
||||
*/
|
||||
package org.springframework.data.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Extension of {@link EntityMetadata} to add functionality to query information
|
||||
* of entity instances.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface EntityInformation<T> extends EntityMetadata<T> {
|
||||
public interface EntityInformation<T, ID extends Serializable> extends EntityMetadata<T> {
|
||||
|
||||
/**
|
||||
* Returns whether the given entity is considered to be new.
|
||||
@@ -38,5 +40,12 @@ public interface EntityInformation<T> extends EntityMetadata<T> {
|
||||
* @param entity must never be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
Object getId(T entity);
|
||||
ID getId(T entity);
|
||||
|
||||
/**
|
||||
* Returns the type of the id of the entity.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<ID> getIdType();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.data.domain.Persistable;
|
||||
|
||||
|
||||
@@ -25,18 +28,21 @@ import org.springframework.data.domain.Persistable;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class PersistableEntityInformation<T extends Persistable> extends
|
||||
AbstractEntityInformation<T> {
|
||||
public class PersistableEntityInformation<T extends Persistable<ID>, ID extends Serializable> extends
|
||||
AbstractEntityInformation<T, ID> {
|
||||
|
||||
private Class<ID> idClass;
|
||||
|
||||
/**
|
||||
* Creates a new {@link PersistableEntityInformation}.
|
||||
*
|
||||
* @param domainClass
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public PersistableEntityInformation(Class<T> domainClass) {
|
||||
|
||||
super(domainClass);
|
||||
this.idClass = (Class<ID>) GenericTypeResolver.resolveTypeArgument(domainClass, Persistable.class);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,8 +67,15 @@ public class PersistableEntityInformation<T extends Persistable> extends
|
||||
* org.springframework.data.repository.support.IdAware#getId(java.lang.Object
|
||||
* )
|
||||
*/
|
||||
public Object getId(T entity) {
|
||||
public ID getId(T entity) {
|
||||
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.EntityInformation#getIdType()
|
||||
*/
|
||||
public Class<ID> getIdType() {
|
||||
return this.idClass;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
@@ -31,8 +33,8 @@ import org.springframework.util.Assert;
|
||||
* @author Oliver Gierke
|
||||
* @param <T> the type of the repository
|
||||
*/
|
||||
public abstract class RepositoryFactoryBeanSupport<T extends Repository<?, ?>>
|
||||
implements FactoryBean<T>, InitializingBean {
|
||||
public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>, S, ID extends Serializable>
|
||||
implements InitializingBean, RepositoryFactoryInformation<S, ID>, FactoryBean<T> {
|
||||
|
||||
private RepositoryFactorySupport factory;
|
||||
|
||||
@@ -75,6 +77,25 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<?, ?>>
|
||||
this.customImplementation = customImplementation;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.EntityMetadataProvider#getEntityMetadata()
|
||||
*/
|
||||
public EntityInformation<S, ID> getEntityInformation() {
|
||||
|
||||
RepositoryMetadata repositoryMetadata = factory.getRepositoryMetadata(repositoryInterface);
|
||||
return (EntityInformation<S, ID>) factory.getEntityInformation(repositoryMetadata.getDomainClass());
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryFactoryInformation#getRepositoryInterface()
|
||||
*/
|
||||
@Override
|
||||
public Class<? extends T> getRepositoryInterface() {
|
||||
|
||||
return repositoryInterface;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 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.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.data.repository.Repository;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for components that can provide {@link EntityInformation} this
|
||||
* interface
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface RepositoryFactoryInformation<T, ID extends Serializable> {
|
||||
|
||||
/**
|
||||
* Returns {@link EntityInformation} the repository factory is using.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
EntityInformation<T, ID> getEntityInformation();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the interface of the {@link Repository} the factory will create.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<? extends Repository<T, ID>> getRepositoryInterface();
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.repository.support;
|
||||
|
||||
import static org.springframework.util.ReflectionUtils.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -124,8 +125,7 @@ public abstract class RepositoryFactorySupport {
|
||||
Object customImplementation) {
|
||||
|
||||
RepositoryMetadata metadata =
|
||||
new DefaultRepositoryMetadata(repositoryInterface,
|
||||
getRepositoryBaseClass(repositoryInterface));
|
||||
getRepositoryMetadata(repositoryInterface);
|
||||
|
||||
validate(metadata, customImplementation);
|
||||
|
||||
@@ -145,6 +145,29 @@ public abstract class RepositoryFactorySupport {
|
||||
|
||||
return (T) result.getProxy();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link RepositoryMetadata} for the given repository interface.
|
||||
*
|
||||
* @param repositoryInterface
|
||||
* @return
|
||||
*/
|
||||
protected RepositoryMetadata getRepositoryMetadata(Class<?> repositoryInterface) {
|
||||
|
||||
return new DefaultRepositoryMetadata(repositoryInterface, getRepositoryBaseClass(repositoryInterface));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link EntityInformation} for the given domain class.
|
||||
*
|
||||
* @param <T> the entity type
|
||||
* @param <ID> the id type
|
||||
* @param domainClass
|
||||
* @return
|
||||
*/
|
||||
public abstract <T, ID extends Serializable> EntityInformation<T, ID> getEntityInformation(Class<T> domainClass);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
@@ -32,8 +34,8 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class TransactionalRepositoryFactoryBeanSupport<T extends Repository<?, ?>>
|
||||
extends RepositoryFactoryBeanSupport<T> implements BeanFactoryAware {
|
||||
public abstract class TransactionalRepositoryFactoryBeanSupport<T extends Repository<S, ID>, S, ID extends Serializable>
|
||||
extends RepositoryFactoryBeanSupport<T, S, ID> implements BeanFactoryAware {
|
||||
|
||||
private String transactionManagerName = TxUtils.DEFAULT_TRANSACTION_MANAGER;
|
||||
private RepositoryProxyPostProcessor txPostProcessor;
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.data.repository.support;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
@@ -38,14 +40,14 @@ public class AbstractEntityInformationUnitTests {
|
||||
@Test
|
||||
public void considersEntityNewIfGetIdReturnsNull() throws Exception {
|
||||
|
||||
EntityInformation<Object> metadata =
|
||||
EntityInformation<Object, Serializable> metadata =
|
||||
new DummyAbstractEntityInformation(Object.class);
|
||||
assertThat(metadata.isNew(null), is(true));
|
||||
assertThat(metadata.isNew(new Object()), is(false));
|
||||
}
|
||||
|
||||
private static class DummyAbstractEntityInformation extends
|
||||
AbstractEntityInformation<Object> {
|
||||
AbstractEntityInformation<Object, Serializable> {
|
||||
|
||||
public DummyAbstractEntityInformation(Class<Object> domainClass) {
|
||||
|
||||
@@ -60,9 +62,18 @@ public class AbstractEntityInformationUnitTests {
|
||||
* org.springframework.data.repository.support.EntityMetadata#getId(
|
||||
* java.lang.Object)
|
||||
*/
|
||||
public Object getId(Object entity) {
|
||||
public Serializable getId(Object entity) {
|
||||
|
||||
return entity == null ? null : entity.toString();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.EntityInformation#getIdType()
|
||||
*/
|
||||
@Override
|
||||
public Class<Serializable> getIdType() {
|
||||
|
||||
return Serializable.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@ import org.springframework.data.domain.Persistable;
|
||||
public class PersistableEntityInformationUnitTests {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
static final PersistableEntityInformation<Persistable> metadata =
|
||||
new PersistableEntityInformation<Persistable>(Persistable.class);
|
||||
static final PersistableEntityInformation metadata =
|
||||
new PersistableEntityInformation(Persistable.class);
|
||||
|
||||
@Mock
|
||||
Persistable<Long> persistable;
|
||||
@@ -64,8 +64,8 @@ public class PersistableEntityInformationUnitTests {
|
||||
@Test
|
||||
public void returnsGivenClassAsEntityType() throws Exception {
|
||||
|
||||
PersistableEntityInformation<PersistableEntity> info =
|
||||
new PersistableEntityInformation<PersistableEntity>(
|
||||
PersistableEntityInformation<PersistableEntity, Long> info =
|
||||
new PersistableEntityInformation<PersistableEntity, Long>(
|
||||
PersistableEntity.class);
|
||||
|
||||
assertEquals(PersistableEntity.class, info.getJavaType());
|
||||
|
||||
@@ -62,6 +62,16 @@ public class RepositoryFactorySupportUnitTests {
|
||||
}
|
||||
|
||||
class DummyRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T, ID extends Serializable> EntityInformation<T, ID> getEntityInformation(
|
||||
Class<T> domainClass) {
|
||||
|
||||
return mock(EntityInformation.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getTargetRepository(RepositoryMetadata metadata) {
|
||||
|
||||
98
spring-data-commons-extensions/pom.xml
Normal file
98
spring-data-commons-extensions/pom.xml
Normal file
@@ -0,0 +1,98 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-commons-parent</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../spring-data-commons-parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-data-commons-extensions</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Data Commons Extensions</name>
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-data-commons-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<version>2.5</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Logging -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>jsr250-api</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
<version>1.6</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.springsource.bundlor</groupId>
|
||||
<artifactId>com.springsource.bundlor.maven</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
16
spring-data-commons-extensions/template.mf
Normal file
16
spring-data-commons-extensions/template.mf
Normal file
@@ -0,0 +1,16 @@
|
||||
Bundle-SymbolicName: org.springframework.data.extensions
|
||||
Bundle-Name: Spring Data Commons Extensions
|
||||
Bundle-Vendor: SpringSource
|
||||
Bundle-ManifestVersion: 2
|
||||
Export-Template:
|
||||
org.springframework.data.extensions.*;version="${project.version:[=.=.=,+1.0.0)}"
|
||||
Import-Template:
|
||||
org.springframework.data.*;version="${project.version:[=.=.=,+1.0.0)}",
|
||||
org.springframework.core.*;version="${org.springframework.version:[=.=.=,+1.0.0)}",
|
||||
org.springframework.beans.*;version="${org.springframework.version:[=.=.=,+1.0.0)}",
|
||||
org.springframework.context.*;version="${org.springframework.version:[=.=.=,+1.0.0)}",
|
||||
org.springframework.util.*;version="${org.springframework.version:[=.=.=,+1.0.0)}",
|
||||
org.springframework.validation.*;version="${org.springframework.version:[=.=.=,+1.0.0)}",
|
||||
org.springframework.web.*;version="${org.springframework.version:[=.=.=,+1.0.0)}",
|
||||
javax.servlet.*;version="[2.5.0, 3.0.0)"
|
||||
|
||||
@@ -111,6 +111,16 @@
|
||||
<artifactId>spring-tx</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>${org.springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user