diff --git a/pom.xml b/pom.xml
index d1093990f..3c86a084d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -72,13 +72,6 @@
-
-
- org.springframework.data
- spring-data-jpa
- ${springdata.jpa}
- test
-
org.hibernate.javax.persistence
@@ -94,6 +87,13 @@
test
+
+ org.springframework.data
+ spring-data-jpa
+ ${springdata.jpa}
+ test
+
+
org.springframework.data
spring-data-mongodb
diff --git a/spring-data-rest-core/pom.xml b/spring-data-rest-core/pom.xml
index a316b3519..e5ba45b4b 100644
--- a/spring-data-rest-core/pom.xml
+++ b/spring-data-rest-core/pom.xml
@@ -28,17 +28,17 @@
spring-tx
-
- org.springframework.data
- spring-data-commons
- ${springdata.commons}
-
-
org.springframework.hateoas
spring-hateoas
${spring.hateoas}
+
+
+ org.springframework.data
+ spring-data-commons
+ ${springdata.commons}
+
org.springframework.plugin
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/invoke/RepositoryMethod.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/invoke/RepositoryMethod.java
deleted file mode 100644
index 006634d47..000000000
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/invoke/RepositoryMethod.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package org.springframework.data.rest.core.invoke;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.springframework.core.MethodParameter;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
-import org.springframework.data.repository.query.Param;
-import org.springframework.data.rest.core.support.Methods;
-
-/**
- * An abstraction to encapsulate metadata about a repository method.
- *
- * @author Jon Brisbin
- */
-public class RepositoryMethod {
-
- private Method method;
- private List methodParameters = new ArrayList();
- private List paramNames = new ArrayList();
- private boolean pageable = false;
- private boolean sortable = false;
-
- public RepositoryMethod(Method method) {
- this.method = method;
-
- Class>[] paramTypes = method.getParameterTypes();
- String[] paramNames = Methods.NAME_DISCOVERER.getParameterNames(method);
- if (null == paramNames) {
- paramNames = new String[paramTypes.length];
- }
-
- Annotation[][] paramAnnos = method.getParameterAnnotations();
- for (int i = 0; i < paramAnnos.length; i++) {
- if (paramAnnos[i].length > 0) {
- for (Annotation anno : paramAnnos[i]) {
- if (Param.class.isAssignableFrom(anno.getClass())) {
- Param p = (Param) anno;
- paramNames[i] = p.value();
- break;
- }
- }
- }
- if (null == paramNames[i]) {
- paramNames[i] = "arg" + i;
- }
- }
-
- int idx = 0;
- for (Class> type : paramTypes) {
- if (Pageable.class.isAssignableFrom(type)) {
- pageable = true;
- }
- if (Sort.class.isAssignableFrom(type)) {
- sortable = true;
- }
- methodParameters.add(new MethodParameter(method, idx));
- idx++;
- }
-
- Collections.addAll(this.paramNames, paramNames);
- }
-
- /**
- * Get the method parameter types.
- *
- * @return Array of parameter types.
- */
- public List getParameters() {
- return methodParameters;
- }
-
- /**
- * Get the method parameter names.
- *
- * @return Array of parameter names.
- */
- public List getParameterNames() {
- return paramNames;
- }
-
- /**
- * Get the reflected {@link Method} to invoke.
- *
- * @return The {@link Method} to invoke.
- */
- public Method getMethod() {
- return method;
- }
-
- /**
- * Flag denoting whether this repository method returns a {@link org.springframework.data.domain.Page} result or not.
- *
- * @return {@literal true} if this method returns a {@link org.springframework.data.domain.Page}, {@literal false}
- * otherwise.
- */
- public boolean isPageable() {
- return pageable;
- }
-
- /**
- * Flag denoting whether this repository method accepts sorting information.
- *
- * @return {@literal true} if this method accepts a {@link Sort}, {@literal false} otherwise.
- */
- public boolean isSortable() {
- return sortable;
- }
-
-}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/invoke/RepositoryQueryMethod.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/invoke/RepositoryQueryMethod.java
deleted file mode 100644
index 67c4655d5..000000000
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/invoke/RepositoryQueryMethod.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package org.springframework.data.rest.core.invoke;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
-import org.springframework.data.repository.query.Param;
-import org.springframework.util.Assert;
-
-/**
- * Represents a query method on a repository interface.
- *
- * @author Jon Brisbin
- */
-public class RepositoryQueryMethod {
-
- private Method method;
- private Class>[] paramTypes;
- private String[] paramNames;
-
- public RepositoryQueryMethod(Method method) {
- this.method = method;
- paramTypes = method.getParameterTypes();
- paramNames = new String[paramTypes.length];
- if (null == paramNames) {
- paramNames = new String[paramTypes.length];
- }
- Annotation[][] paramAnnos = method.getParameterAnnotations();
- for (int i = 0; i < paramAnnos.length; i++) {
- if (paramAnnos[i].length == 0) {
- continue;
- }
-
- for (Annotation anno : paramAnnos[i]) {
- if (Param.class.isAssignableFrom(anno.getClass())) {
- Param p = (Param) anno;
- paramNames[i] = p.value();
- break;
- }
- }
-
- if (Pageable.class.isAssignableFrom(paramTypes[i]) || Sort.class.isAssignableFrom(paramTypes[i])) {
- continue;
- }
-
- Assert.notNull(paramNames[i], "No @Param('name') was provided for parameter " + (i + 1) + " of type "
- + paramTypes[i] + " on " + (method.getDeclaringClass().getName() + "." + method.getName()));
- }
- }
-
- /**
- * The method's parameter types.
- *
- * @return
- */
- public Class>[] paramTypes() {
- return paramTypes;
- }
-
- /**
- * The parameter names as pulled from the {@link Param} annotations.
- *
- * @return
- */
- public String[] paramNames() {
- return paramNames;
- }
-
- /**
- * The {@link Method} to invoke.
- *
- * @return
- */
- public Method method() {
- return method;
- }
-
-}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryAwareResourceInformation.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryAwareResourceInformation.java
index 8572fdf06..9f167982c 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryAwareResourceInformation.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryAwareResourceInformation.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 the original author or authors.
+ * Copyright 2013-2014 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.
@@ -18,12 +18,14 @@ package org.springframework.data.rest.core.mapping;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.mapping.PersistentProperty;
-import org.springframework.data.repository.core.RepositoryInformation;
+import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.core.Path;
import org.springframework.util.Assert;
/**
+ * {@link ResourceMetadata} for a single repository.
+ *
* @author Oliver Gierke
*/
class RepositoryAwareResourceInformation implements ResourceMetadata {
@@ -31,26 +33,37 @@ class RepositoryAwareResourceInformation implements ResourceMetadata {
private final Repositories repositories;
private final CollectionResourceMapping mapping;
private final ResourceMappings provider;
- private final RepositoryInformation repositoryInterface;
+ private final RepositoryMetadata repositoryInterface;
/**
+ * Creates a new {@link RepositoryAwareResourceInformation} for the given {@link Repositories},
+ * {@link CollectionResourceMapping}, {@link ResourceMappings} and {@link RepositoryMetadata}.
+ *
* @param repositories must not be {@literal null}.
* @param mapping must not be {@literal null}.
* @param provider must not be {@literal null}.
+ * @param repositoryMetadata must not be {@literal null}.
*/
public RepositoryAwareResourceInformation(Repositories repositories, CollectionResourceMapping mapping,
- ResourceMappings provider, RepositoryInformation repositoryInterface) {
+ ResourceMappings provider, RepositoryMetadata repositoryMetadata) {
Assert.notNull(repositories, "Repositories must not be null!");
- Assert.notNull(mapping, "ResourceMapping must not be null!");
+ Assert.notNull(mapping, "CollectionResourceMapping must not be null!");
Assert.notNull(provider, "ResourceMetadataProvider must not be null!");
+ Assert.notNull(repositoryMetadata, "RepositoryMetadata must not be null!");
this.repositories = repositories;
this.mapping = mapping;
this.provider = provider;
- this.repositoryInterface = repositoryInterface;
+ this.repositoryInterface = repositoryMetadata;
}
+ /**
+ * Returns whether the current {@link ResourceMetadata} instance for the repository is the primary one to be used.
+ * Reflects to the primary state of the bean definition.
+ *
+ * @return
+ */
public boolean isPrimary() {
return AnnotationUtils.findAnnotation(repositoryInterface.getRepositoryInterface(), Primary.class) != null;
}
@@ -129,6 +142,15 @@ class RepositoryAwareResourceInformation implements ResourceMetadata {
return mapping.getPath();
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.mapping.CollectionResourceMapping#isPagingResource()
+ */
+ @Override
+ public boolean isPagingResource() {
+ return mapping.isPagingResource();
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.rest.core.mapping.ResourceMetadata#getSearchResourceMappings()
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryCollectionResourceMapping.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryCollectionResourceMapping.java
index a03a019e3..54298b07e 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryCollectionResourceMapping.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryCollectionResourceMapping.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 the original author or authors.
+ * Copyright 2013-2014 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.
@@ -18,6 +18,7 @@ package org.springframework.data.rest.core.mapping;
import java.lang.reflect.Modifier;
import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.rest.core.Path;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.data.rest.core.support.RepositoriesUtils;
@@ -41,9 +42,10 @@ class RepositoryCollectionResourceMapping implements CollectionResourceMapping {
private final RestResource annotation;
private final CollectionResourceMapping domainTypeMapping;
private final boolean repositoryIsExportCandidate;
+ private final RepositoryMetadata metadata;
- public RepositoryCollectionResourceMapping(Class> repositoryType) {
- this(repositoryType, new EvoInflectorRelProvider());
+ public RepositoryCollectionResourceMapping(RepositoryMetadata metadata) {
+ this(metadata, new EvoInflectorRelProvider());
}
/**
@@ -53,7 +55,12 @@ class RepositoryCollectionResourceMapping implements CollectionResourceMapping {
* @param repositoryType must not be {@literal null}.
* @param relProvider must not be {@literal null}.
*/
- public RepositoryCollectionResourceMapping(Class> repositoryType, RelProvider relProvider) {
+ public RepositoryCollectionResourceMapping(RepositoryMetadata metadata, RelProvider relProvider) {
+
+ Assert.notNull(metadata, "Repository metadata must not be null!");
+ this.metadata = metadata;
+
+ Class> repositoryType = metadata.getRepositoryInterface();
Assert.isTrue(RepositoriesUtils.isRepositoryInterface(repositoryType), "Given type is not a repository!");
Assert.notNull(relProvider, "RelProvider must not be null!");
@@ -103,4 +110,13 @@ class RepositoryCollectionResourceMapping implements CollectionResourceMapping {
public Boolean isExported() {
return annotation == null ? repositoryIsExportCandidate && domainTypeMapping.isExported() : annotation.exported();
}
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.mapping.CollectionResourceMapping#isPagingResource()
+ */
+ @Override
+ public boolean isPagingResource() {
+ return metadata.isPagingRepository();
+ }
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryMethodResourceMapping.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryMethodResourceMapping.java
index 16ec0c08a..b9dcc3fb0 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryMethodResourceMapping.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/RepositoryMethodResourceMapping.java
@@ -17,11 +17,13 @@ package org.springframework.data.rest.core.mapping;
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.Path;
import org.springframework.data.rest.core.annotation.RestResource;
@@ -43,6 +45,7 @@ class RepositoryMethodResourceMapping implements MethodResourceMapping {
private final String rel;
private final Path path;
private final Method method;
+ private final boolean paging;
private final List parameterNames;
@@ -65,6 +68,7 @@ class RepositoryMethodResourceMapping implements MethodResourceMapping {
annotation.path());
this.method = method;
this.parameterNames = discoverParameterNames(method);
+ this.paging = Arrays.asList(method.getParameterTypes()).contains(Pageable.class);
}
private static final List discoverParameterNames(Method method) {
@@ -125,4 +129,13 @@ class RepositoryMethodResourceMapping implements MethodResourceMapping {
public List getParameterNames() {
return parameterNames;
}
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.mapping.ResourceMapping#isPagingResource()
+ */
+ @Override
+ public boolean isPagingResource() {
+ return paging;
+ }
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMapping.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMapping.java
index 3d8aed519..32132332b 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMapping.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMapping.java
@@ -44,4 +44,11 @@ public interface ResourceMapping {
* @return will never be {@literal null}.
*/
Path getPath();
+
+ /**
+ * Returns whether the resource is paging one.
+ *
+ * @return
+ */
+ boolean isPagingResource();
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java
index 36dcc8648..8ba7dcee2 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 the original author or authors.
+ * Copyright 2013-2014 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.
@@ -98,7 +98,7 @@ public class ResourceMappings implements Iterable {
RepositoryInformation repositoryInformation = repositories.getRepositoryInformationFor(type);
Class> repositoryInterface = repositoryInformation.getRepositoryInterface();
- CollectionResourceMapping mapping = new RepositoryCollectionResourceMapping(repositoryInterface, relProvider);
+ CollectionResourceMapping mapping = new RepositoryCollectionResourceMapping(repositoryInformation, relProvider);
RepositoryAwareResourceInformation information = new RepositoryAwareResourceInformation(repositories, mapping,
this, repositoryInformation);
@@ -137,8 +137,8 @@ public class ResourceMappings implements Iterable {
if (resourceMapping.isExported()) {
for (Method queryMethod : repositoryInformation.getQueryMethods()) {
- RepositoryMethodResourceMapping methodMapping = new RepositoryMethodResourceMapping(
- queryMethod, resourceMapping);
+ RepositoryMethodResourceMapping methodMapping = new RepositoryMethodResourceMapping(queryMethod,
+ resourceMapping);
if (methodMapping.isExported()) {
mappings.add(methodMapping);
}
@@ -305,5 +305,14 @@ public class ResourceMappings implements Iterable {
return !typeMapping.isExported() ? false : annotation == null ? true : annotation.exported();
}
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.mapping.ResourceMapping#isPagingResource()
+ */
+ @Override
+ public boolean isPagingResource() {
+ return false;
+ }
}
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/SearchResourceMappings.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/SearchResourceMappings.java
index 246f927ac..f859ed8d8 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/SearchResourceMappings.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/SearchResourceMappings.java
@@ -104,6 +104,15 @@ public class SearchResourceMappings implements Iterable,
return !mappings.isEmpty();
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.mapping.ResourceMapping#isPagingResource()
+ */
+ @Override
+ public boolean isPagingResource() {
+ return false;
+ }
+
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/TypeBasedCollectionResourceMapping.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/TypeBasedCollectionResourceMapping.java
index 42b2a3047..d9ef57e14 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/TypeBasedCollectionResourceMapping.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/TypeBasedCollectionResourceMapping.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 the original author or authors.
+ * Copyright 2013-2014 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.
@@ -106,6 +106,15 @@ class TypeBasedCollectionResourceMapping implements CollectionResourceMapping {
return relProvider.getSingleResourceRelFor(type);
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.mapping.CollectionResourceMapping#isPagingResource()
+ */
+ @Override
+ public boolean isPagingResource() {
+ return false;
+ }
+
/**
* Returns the default path to be used if the path is not configured manually.
*
diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/invoke/RepositoryMethodUnitTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/invoke/RepositoryMethodUnitTests.java
deleted file mode 100644
index cccda4954..000000000
--- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/invoke/RepositoryMethodUnitTests.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package org.springframework.data.rest.core.invoke;
-
-import static org.hamcrest.MatcherAssert.*;
-import static org.hamcrest.Matchers.*;
-import static org.springframework.util.ReflectionUtils.*;
-
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.rest.core.domain.jpa.PersonRepository;
-import org.springframework.data.rest.core.invoke.RepositoryMethod;
-import org.springframework.data.rest.core.support.Methods;
-import org.springframework.util.ReflectionUtils;
-
-/**
- * Tests to verify the integrity of the {@link RepositoryMethod} abstraction.
- *
- * @author Jon Brisbin
- */
-public class RepositoryMethodUnitTests {
-
- Map methods = new HashMap();
- RepositoryMethod method;
-
- @Before
- public void setup() {
- doWithMethods(PersonRepository.class, new ReflectionUtils.MethodCallback() {
- @Override
- public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
- String name = method.getName();
- RepositoryMethod repoMethod = new RepositoryMethod(method);
- methods.put(name, repoMethod);
- }
- }, Methods.USER_METHODS);
- method = methods.get("findByFirstName");
- }
-
- @Test
- public void shouldFindSimpleQueryMethods() throws Exception {
- assertThat(method, notNullValue());
- }
-
- @Test
- public void shouldFindPageableInformationOnMethod() throws Exception {
- assertThat(method, notNullValue());
- assertThat(method.isPageable(), is(true));
- }
-
- @Test
- public void shouldNotFindSortInformationOnMethod() throws Exception {
- assertThat(method, notNullValue());
- assertThat(method.isSortable(), is(false));
- }
-
- @Test
- public void shouldProvideParameterClassTypes() throws Exception {
- assertThat(method, notNullValue());
- assertThat(method.getParameters().get(0).getParameterType(), is(typeCompatibleWith(String.class)));
- assertThat(method.getParameters().get(1).getParameterType(), is(typeCompatibleWith(Pageable.class)));
- }
-
- @Test
- public void shouldProvideParameterNames() throws Exception {
- assertThat(method, notNullValue());
- assertThat(method.getParameterNames(), contains("firstName", "arg1"));
- }
-
-}
diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryCollectionResourceMappingUnitTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryCollectionResourceMappingUnitTests.java
index 12fc7d545..df7973096 100644
--- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryCollectionResourceMappingUnitTests.java
+++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryCollectionResourceMappingUnitTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 the original author or authors.
+ * Copyright 2013-2014 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.
@@ -19,12 +19,13 @@ import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.Repository;
+import org.springframework.data.repository.core.RepositoryMetadata;
+import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.springframework.data.rest.core.Path;
import org.springframework.data.rest.core.annotation.RestResource;
-import org.springframework.data.rest.core.mapping.CollectionResourceMapping;
-import org.springframework.data.rest.core.mapping.RepositoryCollectionResourceMapping;
-import org.springframework.data.rest.core.mapping.ResourceMapping;
/**
* Unit tests for {@link RepositoryCollectionResourceMapping}.
@@ -36,7 +37,7 @@ public class RepositoryCollectionResourceMappingUnitTests {
@Test
public void buildsDefaultMappingForRepository() {
- CollectionResourceMapping mapping = new RepositoryCollectionResourceMapping(PersonRepository.class);
+ CollectionResourceMapping mapping = getResourceMappingFor(PersonRepository.class);
assertThat(mapping.getPath(), is(new Path("persons")));
assertThat(mapping.getRel(), is("persons"));
@@ -47,7 +48,7 @@ public class RepositoryCollectionResourceMappingUnitTests {
@Test
public void honorsAnnotatedsMapping() {
- CollectionResourceMapping mapping = new RepositoryCollectionResourceMapping(AnnotatedPersonRepository.class);
+ CollectionResourceMapping mapping = getResourceMappingFor(AnnotatedPersonRepository.class);
assertThat(mapping.getPath(), is(new Path("bar")));
assertThat(mapping.getRel(), is("foo"));
@@ -58,8 +59,7 @@ public class RepositoryCollectionResourceMappingUnitTests {
@Test
public void repositoryAnnotationTrumpsDomainTypeMapping() {
- CollectionResourceMapping mapping = new RepositoryCollectionResourceMapping(
- AnnotatedAnnotatedPersonRepository.class);
+ CollectionResourceMapping mapping = getResourceMappingFor(AnnotatedAnnotatedPersonRepository.class);
assertThat(mapping.getPath(), is(new Path("/trumpsAll")));
assertThat(mapping.getRel(), is("foo"));
@@ -70,16 +70,33 @@ public class RepositoryCollectionResourceMappingUnitTests {
@Test
public void doesNotExposeRepositoryForPublicDomainTypeIfRepoIsPackageProtected() {
- ResourceMapping mapping = new RepositoryCollectionResourceMapping(PackageProtectedRepository.class);
+ ResourceMapping mapping = getResourceMappingFor(PackageProtectedRepository.class);
assertThat(mapping.isExported(), is(false));
}
+ /**
+ * @see DATAREST-229
+ */
+ @Test
+ public void detectsPagingRepository() {
+ assertThat(getResourceMappingFor(PersonRepository.class).isPagingResource(), is(true));
+ }
+
+ private static CollectionResourceMapping getResourceMappingFor(Class> repositoryInterface) {
+
+ RepositoryMetadata metadata = new DefaultRepositoryMetadata(repositoryInterface);
+ return new RepositoryCollectionResourceMapping(metadata);
+ }
+
public static class Person {}
@RestResource(path = "bar", rel = "foo", exported = false)
static class AnnotatedPerson {}
- public interface PersonRepository extends Repository {}
+ public interface PersonRepository extends Repository {
+
+ Page findAll(Pageable pageable);
+ }
interface AnnotatedPersonRepository extends Repository {}
@@ -88,5 +105,5 @@ public class RepositoryCollectionResourceMappingUnitTests {
public static class PublicClass {}
- static interface PackageProtectedRepository extends Repository {}
+ interface PackageProtectedRepository extends Repository {}
}
diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryMethodResourceMappingUnitTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryMethodResourceMappingUnitTests.java
index a8baf895c..3593cc67d 100644
--- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryMethodResourceMappingUnitTests.java
+++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryMethodResourceMappingUnitTests.java
@@ -21,7 +21,11 @@ import static org.junit.Assert.*;
import java.lang.reflect.Method;
import org.junit.Test;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.Repository;
+import org.springframework.data.repository.core.RepositoryMetadata;
+import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.Path;
import org.springframework.data.rest.core.annotation.RestResource;
@@ -33,7 +37,8 @@ import org.springframework.data.rest.core.annotation.RestResource;
*/
public class RepositoryMethodResourceMappingUnitTests {
- RepositoryCollectionResourceMapping resourceMapping = new RepositoryCollectionResourceMapping(PersonRepository.class);
+ RepositoryMetadata metadata = new DefaultRepositoryMetadata(PersonRepository.class);
+ RepositoryCollectionResourceMapping resourceMapping = new RepositoryCollectionResourceMapping(metadata);
@Test
public void defaultsMappingToMethodName() throws Exception {
@@ -78,6 +83,18 @@ public class RepositoryMethodResourceMappingUnitTests {
assertThat(mapping.getParameterNames(), hasItem("firstname"));
}
+ /**
+ * @see DATAREST-229
+ */
+ @Test
+ public void considersPagingFinderAPagingResource() throws Exception {
+
+ Method method = PersonRepository.class.getMethod("findByEmailAddress", String.class, Pageable.class);
+ MethodResourceMapping mapping = new RepositoryMethodResourceMapping(method, resourceMapping);
+
+ assertThat(mapping.isPagingResource(), is(true));
+ }
+
static class Person {}
interface PersonRepository extends Repository {
@@ -89,5 +106,8 @@ public class RepositoryMethodResourceMappingUnitTests {
@RestResource(path = "foo")
Iterable findByEmailAddress(String email);
+
+ @RestResource(path = "fooPaged")
+ Page findByEmailAddress(String email, Pageable pageable);
}
}
diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java
index a151d2f7a..5092d57be 100644
--- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java
+++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java
@@ -244,7 +244,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
*/
@Bean
public EntityLinks entityLinks() {
- return new RepositoryEntityLinks(repositories(), resourceMappings(), config());
+ return new RepositoryEntityLinks(repositories(), resourceMappings(), config(), pageableResolver());
}
/**
diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryEntityLinks.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryEntityLinks.java
index f1829cb31..01f677b5f 100644
--- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryEntityLinks.java
+++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryEntityLinks.java
@@ -5,12 +5,21 @@ import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.core.mapping.ResourceMappings;
import org.springframework.data.rest.core.mapping.ResourceMetadata;
+import org.springframework.data.web.HateoasPageableHandlerMethodArgumentResolver;
+import org.springframework.hateoas.EntityLinks;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.LinkBuilder;
+import org.springframework.hateoas.TemplateVariables;
+import org.springframework.hateoas.UriTemplate;
import org.springframework.hateoas.core.AbstractEntityLinks;
import org.springframework.util.Assert;
+import org.springframework.web.util.UriComponents;
+import org.springframework.web.util.UriComponentsBuilder;
/**
+ * {@link EntityLinks} implementation that is able to create {@link Link} for domain classes managed by Spring Data
+ * REST.
+ *
* @author Jon Brisbin
* @author Oliver Gierke
*/
@@ -19,15 +28,29 @@ public class RepositoryEntityLinks extends AbstractEntityLinks {
private final Repositories repositories;
private final ResourceMappings mappings;
private final RepositoryRestConfiguration config;
+ private final HateoasPageableHandlerMethodArgumentResolver resolver;
+ /**
+ * Creates a new {@link RepositoryEntityLinks}.
+ *
+ * @param repositories must not be {@literal null}.
+ * @param mappings must not be {@literal null}.
+ * @param config must not be {@literal null}.
+ * @param resolver must not be {@literal null}.
+ */
@Autowired
- public RepositoryEntityLinks(Repositories repositories, ResourceMappings mappings, RepositoryRestConfiguration config) {
+ public RepositoryEntityLinks(Repositories repositories, ResourceMappings mappings,
+ RepositoryRestConfiguration config, HateoasPageableHandlerMethodArgumentResolver resolver) {
Assert.notNull(repositories, "Repositories must not be null!");
+ Assert.notNull(mappings, "ResourceMappings must not be null!");
+ Assert.notNull(config, "RepositoryRestConfiguration must not be null!");
+ Assert.notNull(resolver, "HateoasPageableHandlerMethodArgumentResolver must not be null!");
this.repositories = repositories;
this.mappings = mappings;
this.config = config;
+ this.resolver = resolver;
}
/*
@@ -67,6 +90,17 @@ public class RepositoryEntityLinks extends AbstractEntityLinks {
public Link linkToCollectionResource(Class> type) {
ResourceMetadata metadata = mappings.getMappingFor(type);
+
+ if (metadata.isPagingResource()) {
+
+ Link link = linkFor(type).withSelfRel();
+ String href = link.getHref();
+ UriComponents components = UriComponentsBuilder.fromUriString(href).build();
+ TemplateVariables variables = resolver.getPaginationTemplateVariables(null, components);
+
+ return new Link(new UriTemplate(href, variables), metadata.getRel());
+ }
+
return linkFor(type).withRel(metadata.getRel());
}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractControllerIntegrationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractControllerIntegrationTests.java
index da93e4b9e..b1059dff3 100644
--- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractControllerIntegrationTests.java
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractControllerIntegrationTests.java
@@ -15,6 +15,7 @@
*/
package org.springframework.data.rest.webmvc;
+import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mapping.PersistentEntity;
@@ -47,6 +48,11 @@ public abstract class AbstractControllerIntegrationTests {
@Autowired RepositoryInvokerFactory invokerFactory;
@Autowired ResourceMappings mappings;
+ @Before
+ public void initWebInfrastructure() {
+ WebTestUtils.initWebTest();
+ }
+
/**
* Returns a {@link RepositoryRestRequest} for the given domain type.
*
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractWebIntegrationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractWebIntegrationTests.java
index 2c8443097..f340d83a2 100644
--- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractWebIntegrationTests.java
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractWebIntegrationTests.java
@@ -87,7 +87,7 @@ public abstract class AbstractWebIntegrationTests {
}
protected MockHttpServletResponse request(Link link) throws Exception {
- return request(link.getHref());
+ return request(link.expand().getHref());
}
protected MockHttpServletResponse request(String href) throws Exception {
@@ -95,7 +95,7 @@ public abstract class AbstractWebIntegrationTests {
}
protected ResultActions follow(Link link) throws Exception {
- return follow(link.getHref());
+ return follow(link.expand().getHref());
}
protected ResultActions follow(String href) throws Exception {
@@ -264,7 +264,7 @@ public abstract class AbstractWebIntegrationTests {
request(link);
// Schema - TODO:Improve by using hypermedia
- mvc.perform(get(link.getHref() + "/schema").//
+ mvc.perform(get(link.expand().getHref() + "/schema").//
accept(MediaType.parseMediaType("application/schema+json"))).//
andExpect(status().isOk());
}
@@ -313,7 +313,7 @@ public abstract class AbstractWebIntegrationTests {
}
@Test
- public void postsPayloadToResource() throws Exception {
+ public void nic() throws Exception {
Map payloads = getPayloadToPost();
assumeFalse(payloads.isEmpty());
@@ -325,9 +325,11 @@ public abstract class AbstractWebIntegrationTests {
String payload = payloads.get(rel);
if (payload != null) {
- Link link = assertHasLinkWithRel(rel, response);
- MockHttpServletRequestBuilder request = post(link.getHref()).//
+ Link link = assertHasLinkWithRel(rel, response);
+ String target = link.expand().getHref();
+
+ MockHttpServletRequestBuilder request = post(target).//
content(payload).//
contentType(MediaType.APPLICATION_JSON);
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/WebTestUtils.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/WebTestUtils.java
new file mode 100644
index 000000000..c56670102
--- /dev/null
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/WebTestUtils.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2013 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.rest.webmvc;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+/**
+ * Helper methods for web integration testing.
+ *
+ * @author Oliver Gierke
+ */
+public class WebTestUtils {
+
+ /**
+ * Initializes web tests. Will register a {@link MockHttpServletRequest} for the current thread.
+ */
+ public static void initWebTest() {
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
+ RequestContextHolder.setRequestAttributes(requestAttributes);
+ }
+}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaRepositoryConfig.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaRepositoryConfig.java
index f16eaf2b0..d5cf50ccd 100644
--- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaRepositoryConfig.java
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaRepositoryConfig.java
@@ -48,6 +48,7 @@ public class JpaRepositoryConfig {
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
+
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.HSQL);
vendorAdapter.setGenerateDdl(true);
@@ -55,6 +56,7 @@ public class JpaRepositoryConfig {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan(getClass().getPackage().getName());
+ factory.setPersistenceUnitName("spring-data-rest-webmvc");
factory.setDataSource(dataSource());
factory.afterPropertiesSet();
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/RepositoryEntityLinksIntegrationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/RepositoryEntityLinksIntegrationTests.java
new file mode 100644
index 000000000..7e92eb01f
--- /dev/null
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/RepositoryEntityLinksIntegrationTests.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 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.rest.webmvc.support;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.rest.webmvc.AbstractControllerIntegrationTests;
+import org.springframework.data.rest.webmvc.jpa.JpaRepositoryConfig;
+import org.springframework.data.rest.webmvc.jpa.Person;
+import org.springframework.hateoas.Link;
+import org.springframework.test.context.ContextConfiguration;
+
+/**
+ * Integration tests for {@link RepositoryEntityLinks}.
+ *
+ * @author Oliver Gierke
+ */
+@ContextConfiguration(classes = JpaRepositoryConfig.class)
+public class RepositoryEntityLinksIntegrationTests extends AbstractControllerIntegrationTests {
+
+ @Autowired RepositoryEntityLinks entityLinks;
+
+ @Test
+ public void returnsLinkToSingleResource() {
+
+ Link link = entityLinks.linkToSingleResource(Person.class, 1);
+
+ assertThat(link.getHref(), endsWith("/people/1"));
+ assertThat(link.getRel(), is("person"));
+ }
+
+ @Test
+ public void returnsTemplatedLinkForPagingResource() {
+
+ Link link = entityLinks.linkToCollectionResource(Person.class);
+
+ assertThat(link.isTemplated(), is(true));
+ assertThat(link.getVariableNames(), hasItems("page", "size", "sort"));
+ assertThat(link.getRel(), is("people"));
+ }
+}