From 7f8abc5aa835d45713a1124a842eeb985d9c12c1 Mon Sep 17 00:00:00 2001
From: Jon Brisbin
Date: Thu, 26 Jul 2012 13:31:33 -0500
Subject: [PATCH] Big update. Bug fixes, code re-formatting, changing tests.
---
build.gradle | 4 +-
gradle.properties | 2 +-
.../data/rest/core/Handler.java | 4 +-
.../springframework/data/rest/core/Links.java | 25 +
.../data/rest/core/SimpleLink.java | 5 +-
.../convert/DelegatingConversionService.java | 45 +-
.../core/convert/StringToUUIDConverter.java | 3 +-
.../core/convert/UUIDToStringConverter.java | 3 +-
.../data/rest/core/util/BeanUtils.java | 96 +-
.../core/util/FluentBeanDeserializer.java | 31 +-
.../rest/core/util/FluentBeanSerializer.java | 54 +-
.../data/rest/core/util/FluentBeanUtils.java | 70 +-
.../data/rest/core/util/UriUtils.java | 76 +-
spring-data-rest-repository/build.gradle | 7 +-
.../rest/repository/AttributeMetadata.java | 23 +-
.../data/rest/repository/EntityMetadata.java | 4 +-
...epositoryConstraintViolationException.java | 3 +-
.../rest/repository/RepositoryExporter.java | 78 +-
.../repository/RepositoryExporterSupport.java | 34 +-
.../rest/repository/RepositoryMetadata.java | 10 +-
.../RepositoryNotFoundException.java | 3 +-
.../repository/RepositoryQueryMethod.java | 56 -
.../rest/repository/ValidationErrors.java | 9 +-
.../AbstractRepositoryEventListener.java | 37 +-
.../repository/context/AfterDeleteEvent.java | 3 +-
.../context/AfterLinkSaveEvent.java | 3 +-
.../repository/context/AfterSaveEvent.java | 3 +-
...notatedHandlerRepositoryEventListener.java | 61 +-
.../repository/context/BeforeDeleteEvent.java | 3 +-
.../context/BeforeLinkSaveEvent.java | 3 +-
.../repository/context/BeforeSaveEvent.java | 3 +-
.../repository/context/LinkSaveEvent.java | 3 +-
.../repository/context/RepositoryEvent.java | 3 +-
.../ValidatingRepositoryEventListener.java | 40 +-
.../{ => invoke}/RepositoryMethod.java | 51 +-
.../invoke/RepositoryMethodResponse.java | 103 +
.../invoke/RepositoryQueryMethod.java | 64 +
.../repository/jpa/JpaAttributeMetadata.java | 57 +-
.../repository/jpa/JpaEntityMetadata.java | 35 +-
.../repository/jpa/JpaRepositoryExporter.java | 3 +-
.../repository/jpa/JpaRepositoryMetadata.java | 23 +-
.../repository/spec/JpaMetadataSpec.groovy | 9 +-
.../repository/test/ApplicationConfig.java | 66 +
spring-data-rest-webmvc/build.gradle | 10 +-
.../data/rest/webmvc/JacksonUtil.java | 44 +-
.../data/rest/webmvc/Links.java | 28 -
.../data/rest/webmvc/MediaTypes.java | 33 +
.../data/rest/webmvc/PagingAndSorting.java | 20 +-
...agingAndSortingMethodArgumentResolver.java | 35 +-
.../webmvc/RepositoryRestConfiguration.java | 39 +-
.../rest/webmvc/RepositoryRestController.java | 1964 ++++++++++-------
.../webmvc/RepositoryRestHandlerAdapter.java | 18 +-
.../webmvc/RepositoryRestHandlerMapping.java | 15 +-
.../RepositoryRestMvcConfiguration.java | 9 +-
...rverHttpRequestMethodArgumentResolver.java | 15 +-
.../webmvc/UriListHttpMessageConverter.java | 93 +-
.../src/main/webapp/WEB-INF/web.xml | 18 -
.../data/rest/webmvc/spec/BaseSpec.groovy | 106 +
.../data/rest/webmvc/spec/EventsSpec.groovy | 47 +
.../rest/webmvc/spec/RelationshipsSpec.groovy | 40 +
.../spec/RepositoryRestControllerSpec.groovy | 2 +
.../webmvc/spec/TopLevelEntitySpec.groovy | 39 +
.../data/rest/test/RestBuilder.java | 40 +-
.../data/rest/test/webmvc/Address.java | 21 +-
.../rest/test/webmvc/AddressRepository.java | 4 +
.../rest/test/webmvc/ApplicationConfig.java | 64 +
.../data/rest/test/webmvc/Family.java | 6 +-
.../rest/test/webmvc/FamilyRepository.java | 3 +-
.../data/rest/test/webmvc/Person.java | 14 +-
.../data/rest/test/webmvc/PersonLoader.java | 8 +-
.../rest/test/webmvc/PersonRepository.java | 1 -
.../rest/test/webmvc/PersonValidator.java | 5 +-
.../data/rest/test/webmvc/Profile.java | 27 +-
.../rest/test/webmvc/ProfileRepository.java | 4 +
.../webmvc/RestExporterWebInitializer.java | 33 +
.../webmvc/TestRepositoryEventListener.java | 32 +
.../rest/test/webmvc/UuidTestRepository.java | 3 +-
.../spring-data-rest/repositories-export.xml | 8 +-
.../META-INF/spring-data-rest/shared.xml | 25 -
.../src/test/resources/load_data.sh | 1 +
80 files changed, 2551 insertions(+), 1541 deletions(-)
create mode 100644 spring-data-rest-core/src/main/java/org/springframework/data/rest/core/Links.java
delete mode 100644 spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryQueryMethod.java
rename spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/{ => invoke}/RepositoryMethod.java (63%)
create mode 100644 spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryMethodResponse.java
create mode 100644 spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryQueryMethod.java
create mode 100644 spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/test/ApplicationConfig.java
delete mode 100644 spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/Links.java
create mode 100644 spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/MediaTypes.java
delete mode 100644 spring-data-rest-webmvc/src/main/webapp/WEB-INF/web.xml
create mode 100644 spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/BaseSpec.groovy
create mode 100644 spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/EventsSpec.groovy
create mode 100644 spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/RelationshipsSpec.groovy
create mode 100644 spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/TopLevelEntitySpec.groovy
create mode 100644 spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/ApplicationConfig.java
create mode 100644 spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/RestExporterWebInitializer.java
create mode 100644 spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/TestRepositoryEventListener.java
delete mode 100644 spring-data-rest-webmvc/src/test/resources/META-INF/spring-data-rest/shared.xml
diff --git a/build.gradle b/build.gradle
index daa266efd..b65166963 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@ allprojects {
repositories {
//maven { url "http://repo.springsource.org/libs-snapshot" }
- //maven { url "http://repo.springsource.org/libs-milestone" }
+ maven { url "http://repo.springsource.org/libs-milestone" }
maven { url "http://repo.springsource.org/libs-release" }
}
@@ -70,7 +70,9 @@ configure(subprojects) { subproject ->
compile("org.springframework:spring-context:$springVersion") { force = true }
compile("org.springframework:spring-core:$springVersion") { force = true }
compile("org.springframework:spring-orm:$springVersion") { force = true }
+ compile("org.springframework:spring-tx:$springVersion") { force = true }
compile("org.springframework:spring-web:$springVersion") { force = true }
+ runtime "cglib:cglib-nodep:2.2.2"
// Testing
testCompile "org.spockframework:spock-core:$spockVersion"
diff --git a/gradle.properties b/gradle.properties
index fc09ec164..16a40318d 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -11,7 +11,7 @@ groovyVersion = 1.8.6
# Supporting libraries
sdCommonsVersion = 1.3.2.RELEASE
-sdJpaVersion = 1.1.0.RELEASE
+sdJpaVersion = 1.2.0.M1
jacksonVersion = 1.9.7
hibernateVersion = 4.1.4.Final
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/Handler.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/Handler.java
index 22c5e1eb3..2d4e0566d 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/Handler.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/Handler.java
@@ -10,7 +10,9 @@ public interface Handler {
/**
* Accept an argument and possibly produce a result.
*
- * @param t arg
+ * @param t
+ * arg
+ *
* @return Some object or {@literal null} if no result.
*/
V handle(T t);
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/Links.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/Links.java
new file mode 100644
index 000000000..980995cab
--- /dev/null
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/Links.java
@@ -0,0 +1,25 @@
+package org.springframework.data.rest.core;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * @author Jon Brisbin
+ */
+public class Links {
+
+ private List links = new ArrayList ();
+
+ public Links add(Link link) {
+ links.add(link);
+ return this;
+ }
+
+ @JsonProperty("_links")
+ public List getLinks() {
+ return this.links;
+ }
+
+}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/SimpleLink.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/SimpleLink.java
index 558594f3e..2eac65899 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/SimpleLink.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/SimpleLink.java
@@ -7,10 +7,11 @@ import java.net.URI;
*
* @author Jon Brisbin
*/
-public class SimpleLink implements Link {
+public class SimpleLink
+ implements Link {
private String rel;
- private URI href;
+ private URI href;
public SimpleLink() {
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/convert/DelegatingConversionService.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/convert/DelegatingConversionService.java
index fc93c7f62..83c2c20bc 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/convert/DelegatingConversionService.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/convert/DelegatingConversionService.java
@@ -7,9 +7,14 @@ import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.TypeDescriptor;
/**
- * @author Jon Brisbin
+ * This {@link ConversionService} implementation delegates the actual conversion the ConversionService if finds in its
+ * internal List that claims to be able to convert a given class. It will roll through the internal Stack
+ * of ConversionServices until it finds one that can convert the given type.
+ *
+ * @author Jon Brisbin
*/
-public class DelegatingConversionService implements ConversionService {
+public class DelegatingConversionService
+ implements ConversionService {
private Stack conversionServices = new Stack();
@@ -20,21 +25,39 @@ public class DelegatingConversionService implements ConversionService {
addConversionServices(svcs);
}
+ /**
+ * Add {@link ConversionService}s to the internal list of those to delegate to.
+ *
+ * @param svcs
+ * The ConversionServices to delegate to (in order).
+ *
+ * @return @this
+ */
public DelegatingConversionService addConversionServices(ConversionService... svcs) {
- for (ConversionService svc : svcs) {
+ for(ConversionService svc : svcs) {
conversionServices.add(svc);
}
return this;
}
+ /**
+ * Add a {@link ConversionService} to the internal list at a specific index for controlling the priority.
+ *
+ * @param atIndex
+ * Where in the stack to add this ConversionService.
+ * @param svc
+ * The ConversionService to add.
+ *
+ * @return
+ */
public DelegatingConversionService addConversionService(int atIndex, ConversionService svc) {
conversionServices.add(atIndex, svc);
return this;
}
@Override public boolean canConvert(Class> from, Class> to) {
- for (ConversionService svc : conversionServices) {
- if (svc.canConvert(from, to)) {
+ for(ConversionService svc : conversionServices) {
+ if(svc.canConvert(from, to)) {
return true;
}
}
@@ -42,8 +65,8 @@ public class DelegatingConversionService implements ConversionService {
}
@Override public boolean canConvert(TypeDescriptor from, TypeDescriptor to) {
- for (ConversionService svc : conversionServices) {
- if (svc.canConvert(from, to)) {
+ for(ConversionService svc : conversionServices) {
+ if(svc.canConvert(from, to)) {
return true;
}
}
@@ -51,8 +74,8 @@ public class DelegatingConversionService implements ConversionService {
}
@Override public T convert(Object o, Class type) {
- for (ConversionService svc : conversionServices) {
- if (svc.canConvert(o.getClass(), type)) {
+ for(ConversionService svc : conversionServices) {
+ if(svc.canConvert(o.getClass(), type)) {
return svc.convert(o, type);
}
}
@@ -60,8 +83,8 @@ public class DelegatingConversionService implements ConversionService {
}
@Override public Object convert(Object o, TypeDescriptor from, TypeDescriptor to) {
- for (ConversionService svc : conversionServices) {
- if (svc.canConvert(from, to)) {
+ for(ConversionService svc : conversionServices) {
+ if(svc.canConvert(from, to)) {
return svc.convert(o, from, to);
}
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/convert/StringToUUIDConverter.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/convert/StringToUUIDConverter.java
index 77bfb752c..d90c91d5f 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/convert/StringToUUIDConverter.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/convert/StringToUUIDConverter.java
@@ -7,7 +7,8 @@ import org.springframework.core.convert.converter.Converter;
/**
* @author Jon Brisbin
*/
-public class StringToUUIDConverter implements Converter {
+public class StringToUUIDConverter
+ implements Converter {
@Override public UUID convert(String s) {
return (null != s ? UUID.fromString(s) : null);
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/convert/UUIDToStringConverter.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/convert/UUIDToStringConverter.java
index 84933a6e3..51df846ab 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/convert/UUIDToStringConverter.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/convert/UUIDToStringConverter.java
@@ -7,7 +7,8 @@ import org.springframework.core.convert.converter.Converter;
/**
* @author Jon Brisbin
*/
-public class UUIDToStringConverter implements Converter {
+public class UUIDToStringConverter
+ implements Converter {
@Override public String convert(UUID uuid) {
return (null != uuid ? uuid.toString() : null);
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/BeanUtils.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/BeanUtils.java
index b219536ad..d3d1149a4 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/BeanUtils.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/BeanUtils.java
@@ -30,13 +30,14 @@ public abstract class BeanUtils {
public static ConfigurableConversionService CONVERSION_SERVICE = new DefaultConversionService();
- private static final LoadingCache fields = CacheBuilder.newBuilder().build(
+ private static final LoadingCache fields = CacheBuilder.newBuilder().build(
new CacheLoader() {
- @Override public Field load(Object[] key) throws Exception {
- Class> clazz = (Class>) key[0];
- String name = (String) key[1];
+ @Override public Field load(Object[] key)
+ throws Exception {
+ Class> clazz = (Class>)key[0];
+ String name = (String)key[1];
Field f = ReflectionUtils.findField(clazz, name);
- if (null != f) {
+ if(null != f) {
ReflectionUtils.makeAccessible(f);
return f;
} else {
@@ -47,14 +48,15 @@ public abstract class BeanUtils {
);
private static final LoadingCache methods = CacheBuilder.newBuilder().build(
new CacheLoader() {
- @Override public Method load(Object[] key) throws Exception {
- Class> clazz = (Class>) key[0];
- String name = (String) key[1];
- Integer paramCnt = key.length == 3 ? (Integer) key[2] : 0;
+ @Override public Method load(Object[] key)
+ throws Exception {
+ Class> clazz = (Class>)key[0];
+ String name = (String)key[1];
+ Integer paramCnt = key.length == 3 ? (Integer)key[2] : 0;
- for (Method m : clazz.getDeclaredMethods()) {
- if (m.getName().equals(name)) {
- if (m.getParameterTypes().length == paramCnt) {
+ for(Method m : clazz.getDeclaredMethods()) {
+ if(m.getName().equals(name)) {
+ if(m.getParameterTypes().length == paramCnt) {
ReflectionUtils.makeAccessible(m);
return m;
}
@@ -67,28 +69,28 @@ public abstract class BeanUtils {
);
public static boolean hasProperty(String property, Object... objs) {
- for (Object obj : objs) {
- if (obj instanceof Map) {
- return ((Map) obj).containsKey(property);
+ for(Object obj : objs) {
+ if(obj instanceof Map) {
+ return ((Map)obj).containsKey(property);
}
Class> type = obj.getClass();
try {
- if (FluentBeanUtils.isFluentBean(type)) {
+ if(FluentBeanUtils.isFluentBean(type)) {
return null != methods.get(new Object[]{type, property});
} else {
- if (null == methods.get(new Object[]{type, "get" + StringUtils.capitalize(property)})) {
+ if(null == methods.get(new Object[]{type, "get" + StringUtils.capitalize(property)})) {
return null != fields.get(new Object[]{type, property});
} else {
return true;
}
}
- } catch (UncheckedExecutionException e) {
- if (e.getCause().getClass() == IllegalArgumentException.class) {
+ } catch(UncheckedExecutionException e) {
+ if(e.getCause().getClass() == IllegalArgumentException.class) {
return false;
} else {
throw new IllegalStateException(e);
}
- } catch (ExecutionException e) {
+ } catch(ExecutionException e) {
throw new IllegalStateException(e);
}
}
@@ -97,9 +99,9 @@ public abstract class BeanUtils {
@SuppressWarnings({"unchecked"})
public static T findFirst(Class clazz, List> stack) {
- for (Object o : stack) {
- if (ClassUtils.isAssignable(clazz, o.getClass())) {
- return (T) o;
+ for(Object o : stack) {
+ if(ClassUtils.isAssignable(clazz, o.getClass())) {
+ return (T)o;
}
}
return null;
@@ -107,44 +109,44 @@ public abstract class BeanUtils {
@SuppressWarnings({"unchecked"})
public static Object findFirst(Object o, Object... objs) {
- for (Object obj : objs) {
- if (o == obj || null != o && o.equals(obj)) {
+ for(Object obj : objs) {
+ if(o == obj || null != o && o.equals(obj)) {
return obj;
- } else if (obj instanceof List) {
- return Collections.binarySearch((List) obj, o);
- } else if (obj instanceof Object[]) {
- return Arrays.binarySearch((Object[]) obj, o);
+ } else if(obj instanceof List) {
+ return Collections.binarySearch((List)obj, o);
+ } else if(obj instanceof Object[]) {
+ return Arrays.binarySearch((Object[])obj, o);
}
}
return null;
}
public static Object findFirst(String property, Object... objs) {
- for (Object obj : objs) {
- if (obj instanceof Map) {
- return ((Map) obj).get(property);
+ for(Object obj : objs) {
+ if(obj instanceof Map) {
+ return ((Map)obj).get(property);
}
Class> type = obj.getClass();
try {
Field f = fields.get(new Object[]{type, property});
- if (FluentBeanUtils.isFluentBean(type)) {
+ if(FluentBeanUtils.isFluentBean(type)) {
return FluentBeanUtils.get(property, obj);
} else {
Method getter = methods.get(new Object[]{type, "get" + StringUtils.capitalize(property)});
try {
- if (null != getter) {
+ if(null != getter) {
return getter.invoke(obj);
} else {
return f.get(obj);
}
- } catch (IllegalAccessException e) {
+ } catch(IllegalAccessException e) {
throw new IllegalStateException(e);
- } catch (InvocationTargetException e) {
+ } catch(InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
- } catch (IllegalArgumentException e) {
- } catch (ExecutionException e) {
+ } catch(IllegalArgumentException e) {
+ } catch(ExecutionException e) {
throw new IllegalArgumentException(e);
}
}
@@ -157,8 +159,8 @@ public abstract class BeanUtils {
}
public static boolean containsType(Class> type, Object[] objs) {
- for (Object obj : objs) {
- if (null != obj && ClassUtils.isAssignable(obj.getClass(), type)) {
+ for(Object obj : objs) {
+ if(null != obj && ClassUtils.isAssignable(obj.getClass(), type)) {
return true;
}
}
@@ -172,7 +174,7 @@ public abstract class BeanUtils {
@SuppressWarnings({"unchecked"})
public static T invoke(String methodName, Object target, Class returnType, Object... args) {
- if (null == target) {
+ if(null == target) {
return null;
}
@@ -181,11 +183,11 @@ public abstract class BeanUtils {
Method m = methods.get(new Object[]{type, methodName, args.length});
List newArgs = new ArrayList(args.length);
Class>[] paramTypes = m.getParameterTypes();
- for (int i = 0; i < args.length; i++) {
+ for(int i = 0; i < args.length; i++) {
Object o = args[i];
Class> oType = o.getClass();
Class> pType = paramTypes[i];
- if (!ClassUtils.isAssignable(oType, pType)) {
+ if(!ClassUtils.isAssignable(oType, pType)) {
newArgs.add(CONVERSION_SERVICE.convert(o, pType));
} else {
newArgs.add(o);
@@ -193,15 +195,15 @@ public abstract class BeanUtils {
}
Object rtnVal = m.invoke(target, newArgs.toArray());
- if ((returnType != Void.TYPE || returnType != Object.class)
+ if((returnType != Void.TYPE || returnType != Object.class)
&& null != rtnVal
&& !ClassUtils.isAssignable(returnType, rtnVal.getClass())) {
return CONVERSION_SERVICE.convert(rtnVal, returnType);
} else {
- return (T) rtnVal;
+ return (T)rtnVal;
}
- } catch (IllegalArgumentException e) {
- } catch (Exception e) {
+ } catch(IllegalArgumentException e) {
+ } catch(Exception e) {
throw new IllegalStateException(e);
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/FluentBeanDeserializer.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/FluentBeanDeserializer.java
index 2b2e7b4f4..c0a342e48 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/FluentBeanDeserializer.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/FluentBeanDeserializer.java
@@ -18,9 +18,10 @@ import org.springframework.util.ClassUtils;
*
* @author Jon Brisbin
*/
-public class FluentBeanDeserializer extends StdDeserializer {
+public class FluentBeanDeserializer
+ extends StdDeserializer {
- private ConversionService conversionService;
+ private ConversionService conversionService;
private FluentBeanUtils.Metadata beanMeta;
@SuppressWarnings({"unchecked"})
@@ -29,7 +30,7 @@ public class FluentBeanDeserializer extends StdDeserializer {
this.conversionService = conversionService;
this.beanMeta = FluentBeanUtils.metadata(valueClass);
- if (!FluentBeanUtils.isFluentBean(valueClass)) {
+ if(!FluentBeanUtils.isFluentBean(valueClass)) {
throw new IllegalArgumentException("Class of type " + valueClass + " is not a FluentBean");
}
}
@@ -39,46 +40,46 @@ public class FluentBeanDeserializer extends StdDeserializer {
DeserializationContext ctxt)
throws IOException,
JsonProcessingException {
- if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
+ if(jp.getCurrentToken() != JsonToken.START_OBJECT) {
throw ctxt.mappingException(_valueClass);
}
Object bean;
try {
bean = _valueClass.newInstance();
- } catch (InstantiationException e) {
+ } catch(InstantiationException e) {
throw new IllegalStateException(e);
- } catch (IllegalAccessException e) {
+ } catch(IllegalAccessException e) {
throw new IllegalStateException(e);
}
- while (jp.nextToken() != JsonToken.END_OBJECT) {
+ while(jp.nextToken() != JsonToken.END_OBJECT) {
String name = jp.getCurrentName();
Method setter = beanMeta.setters().get(name);
Object obj;
- if (null != setter) {
+ if(null != setter) {
Class> targetType = setter.getParameterTypes()[0];
- if (ClassUtils.isAssignable(targetType, Long.class)) {
+ if(ClassUtils.isAssignable(targetType, Long.class)) {
obj = jp.nextLongValue(-1);
- } else if (ClassUtils.isAssignable(targetType, Integer.class)) {
+ } else if(ClassUtils.isAssignable(targetType, Integer.class)) {
obj = jp.nextIntValue(-1);
- } else if (ClassUtils.isAssignable(targetType, Boolean.class)) {
+ } else if(ClassUtils.isAssignable(targetType, Boolean.class)) {
obj = jp.nextBooleanValue();
} else {
obj = jp.nextTextValue();
}
- if (null != obj) {
- if (!ClassUtils.isAssignable(obj.getClass(), targetType)) {
+ if(null != obj) {
+ if(!ClassUtils.isAssignable(obj.getClass(), targetType)) {
obj = conversionService.convert(obj, targetType);
}
try {
setter.invoke(bean, obj);
- } catch (IllegalAccessException e) {
+ } catch(IllegalAccessException e) {
throw new IllegalStateException(e);
- } catch (InvocationTargetException e) {
+ } catch(InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/FluentBeanSerializer.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/FluentBeanSerializer.java
index 7f1bfec34..463aba1dd 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/FluentBeanSerializer.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/FluentBeanSerializer.java
@@ -16,60 +16,62 @@ import org.springframework.util.ClassUtils;
*
* @author Jon Brisbin
*/
-public class FluentBeanSerializer extends SerializerBase {
+public class FluentBeanSerializer
+ extends SerializerBase {
@SuppressWarnings({"unchecked", "rawtypes"})
- public FluentBeanSerializer( final Class t ) {
- super( t );
+ public FluentBeanSerializer(final Class t) {
+ super(t);
- if ( !FluentBeanUtils.isFluentBean( t ) ) {
- throw new IllegalArgumentException( "Class of type " + t + " is not a FluentBean" );
+ if(!FluentBeanUtils.isFluentBean(t)) {
+ throw new IllegalArgumentException("Class of type " + t + " is not a FluentBean");
}
}
@SuppressWarnings({"unchecked"})
@Override
- public void serialize( final Object value,
- final JsonGenerator jgen,
- final SerializerProvider provider )
+ public void serialize(final Object value,
+ final JsonGenerator jgen,
+ final SerializerProvider provider)
throws IOException,
JsonGenerationException {
- if ( null == value ) {
- provider.defaultSerializeNull( jgen );
+ if(null == value) {
+ provider.defaultSerializeNull(jgen);
} else {
Class> type = value.getClass();
- if ( ClassUtils.isAssignable( type, Collection.class ) ) {
+ if(ClassUtils.isAssignable(type, Collection.class)) {
jgen.writeStartArray();
- for ( Object o : (Collection) value ) {
- write( o, jgen, provider );
+ for(Object o : (Collection)value) {
+ write(o, jgen, provider);
}
jgen.writeEndArray();
- } else if ( ClassUtils.isAssignable( type, Map.class ) ) {
+ } else if(ClassUtils.isAssignable(type, Map.class)) {
jgen.writeStartObject();
- for ( Map.Entry entry : ((Map) value).entrySet() ) {
- jgen.writeFieldName( entry.getKey() );
- write( entry.getValue(), jgen, provider );
+ for(Map.Entry entry : ((Map)value).entrySet()) {
+ jgen.writeFieldName(entry.getKey());
+ write(entry.getValue(), jgen, provider);
}
jgen.writeEndObject();
} else {
- write( value, jgen, provider );
+ write(value, jgen, provider);
}
}
}
- private void write( final Object value,
- final JsonGenerator jgen,
- final SerializerProvider provider ) throws IOException {
+ private void write(final Object value,
+ final JsonGenerator jgen,
+ final SerializerProvider provider)
+ throws IOException {
Class> type = value.getClass();
- if ( ClassUtils.isAssignable( type, _handledType ) ) {
+ if(ClassUtils.isAssignable(type, _handledType)) {
jgen.writeStartObject();
- for ( String fname : FluentBeanUtils.metadata( type ).fieldNames() ) {
- jgen.writeFieldName( fname );
- write( FluentBeanUtils.get( fname, value ), jgen, provider );
+ for(String fname : FluentBeanUtils.metadata(type).fieldNames()) {
+ jgen.writeFieldName(fname);
+ write(FluentBeanUtils.get(fname, value), jgen, provider);
}
jgen.writeEndObject();
} else {
- jgen.writeObject( value );
+ jgen.writeObject(value);
}
}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/FluentBeanUtils.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/FluentBeanUtils.java
index 791c414f1..e820d45c9 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/FluentBeanUtils.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/FluentBeanUtils.java
@@ -22,24 +22,27 @@ import org.springframework.util.ReflectionUtils;
*/
public abstract class FluentBeanUtils {
- private static final Logger log = LoggerFactory.getLogger(FluentBeanUtils.class);
+ private static final Logger log = LoggerFactory.getLogger(FluentBeanUtils.class);
private static final LoadingCache, Metadata> metadata = CacheBuilder.newBuilder().build(
new CacheLoader, Metadata>() {
- @Override public Metadata load(Class> type) throws Exception {
+ @Override public Metadata load(Class> type)
+ throws Exception {
final Metadata meta = new Metadata();
ReflectionUtils.doWithFields(
type,
new ReflectionUtils.FieldCallback() {
- @Override public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
+ @Override public void doWith(Field field)
+ throws IllegalArgumentException, IllegalAccessException {
final String fname = field.getName();
- if (!fname.startsWith("_")) {
+ if(!fname.startsWith("_")) {
ReflectionUtils.doWithMethods(field.getDeclaringClass(), new ReflectionUtils.MethodCallback() {
@Override
- public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
- if (method.getName().equals(fname)) {
- if (method.getParameterTypes().length == 0) {
+ public void doWith(Method method)
+ throws IllegalArgumentException, IllegalAccessException {
+ if(method.getName().equals(fname)) {
+ if(method.getParameterTypes().length == 0) {
meta.getters.put(fname, method);
- } else if (method.getParameterTypes().length == 1) {
+ } else if(method.getParameterTypes().length == 1) {
meta.setters.put(fname, method);
}
meta.fieldNames.add(fname);
@@ -58,13 +61,15 @@ public abstract class FluentBeanUtils {
/**
* Interrogate a bean and collect {@link Metadata} on it.
*
- * @param targetType The type to interrogate.
+ * @param targetType
+ * The type to interrogate.
+ *
* @return {@link Metadata} for the fluent bean.
*/
public static Metadata metadata(Class> targetType) {
try {
return metadata.get(targetType);
- } catch (ExecutionException e) {
+ } catch(ExecutionException e) {
throw new IllegalStateException(e);
}
}
@@ -72,27 +77,31 @@ public abstract class FluentBeanUtils {
/**
* Set the property of a fluent bean.
*
- * @param property Name of the property to set.
- * @param value Value of the property.
- * @param bean Bean on which to set this property.
+ * @param property
+ * Name of the property to set.
+ * @param value
+ * Value of the property.
+ * @param bean
+ * Bean on which to set this property.
+ *
* @return Usually {@literal null} but will return whatever the "setter" returns, which could be {@this} or something
* else.
*/
public static Object set(String property, Object value, Object bean) {
- if (null == bean) {
+ if(null == bean) {
return null;
}
Class> type = bean.getClass();
try {
Method setter = metadata.get(type).setters.get(property);
- if (null != setter) {
+ if(null != setter) {
return setter.invoke(bean, value);
} else {
return null;
}
- } catch (Throwable t) {
- if (log.isDebugEnabled()) {
+ } catch(Throwable t) {
+ if(log.isDebugEnabled()) {
log.debug(t.getMessage(), t);
}
return null;
@@ -102,25 +111,28 @@ public abstract class FluentBeanUtils {
/**
* Get the value of a property.
*
- * @param property Name of the property.
- * @param bean Bean of which to get the property.
+ * @param property
+ * Name of the property.
+ * @param bean
+ * Bean of which to get the property.
+ *
* @return Value of the property. Could be {@literal null}
*/
public static Object get(String property, Object bean) {
- if (null == bean) {
+ if(null == bean) {
return null;
}
Class> type = bean.getClass();
try {
Method getter = metadata.get(type).getters.get(property);
- if (null != getter) {
+ if(null != getter) {
return getter.invoke(bean);
} else {
return null;
}
- } catch (Throwable t) {
- if (log.isDebugEnabled()) {
+ } catch(Throwable t) {
+ if(log.isDebugEnabled()) {
log.debug(t.getMessage(), t);
}
return null;
@@ -132,21 +144,23 @@ public abstract class FluentBeanUtils {
* to a field of the same name. A "getter" is that method which is named the same as the field and has 0 parameters.
* The "setter" is that method which is named the same as the field and has a single argument.
*
- * @param type The class to inspect.
+ * @param type
+ * The class to inspect.
+ *
* @return {@literal true} if this looks like a fluent bean, {@literal false} otherwise.
*/
public static boolean isFluentBean(Class> type) {
try {
return metadata.get(type).getters.size() > 0;
- } catch (ExecutionException e) {
+ } catch(ExecutionException e) {
throw new IllegalStateException(e);
}
}
public static class Metadata {
- List fieldNames = new ArrayList();
- Map getters = new HashMap();
- Map setters = new HashMap();
+ List fieldNames = new ArrayList();
+ Map getters = new HashMap();
+ Map setters = new HashMap();
public List fieldNames() {
return fieldNames;
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/UriUtils.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/UriUtils.java
index b4503f9eb..d7ec4d2ad 100644
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/UriUtils.java
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/UriUtils.java
@@ -24,8 +24,11 @@ public abstract class UriUtils {
* http://localhost:8080/data/person}, this method would report the baseUri being a valid base of the given URI.
*
*
- * @param baseUri {@link URI} to check.
- * @param uri {@link URI} against which to compare the base.
+ * @param baseUri
+ * {@link URI} to check.
+ * @param uri
+ * {@link URI} against which to compare the base.
+ *
* @return {@literal true} if the baseUri is valid against the given {@link URI}, {@literal false} otherwise.
*/
public static boolean validBaseUri(URI baseUri, URI uri) {
@@ -41,16 +44,21 @@ public abstract class UriUtils {
* second time passing a relative {@link URI} of "1".
*
*
- * @param baseUri base {@link URI}
- * @param uri {@link URI} to explode and iteratre over.
- * @param handler {@link Handler} to call for each segment of the URI's path.
- * @param Return type of the handler.
+ * @param baseUri
+ * base {@link URI}
+ * @param uri
+ * {@link URI} to explode and iteratre over.
+ * @param handler
+ * {@link Handler} to call for each segment of the URI's path.
+ * @param
+ * Return type of the handler.
+ *
* @return Handler return value, or possibly {@literal null}.
*/
public static V foreach(URI baseUri, URI uri, Handler handler) {
List uris = explode(baseUri, uri);
V v = null;
- for (URI u : uris) {
+ for(URI u : uris) {
v = handler.handle(u);
}
return v;
@@ -63,16 +71,19 @@ public abstract class UriUtils {
* in
* a {@link Stack} of relative {@link URI}s of size 2--one for "person" and one for "1".
*
- * @param baseUri base {@link URI}
- * @param uri {@link URI} to explode
+ * @param baseUri
+ * base {@link URI}
+ * @param uri
+ * {@link URI} to explode
+ *
* @return {@link Stack} of relative {@link URI}s.
*/
public static Stack explode(URI baseUri, URI uri) {
Stack uris = new Stack();
- if (StringUtils.hasText(uri.getPath())) {
+ if(StringUtils.hasText(uri.getPath())) {
URI relativeUri = baseUri.relativize(uri);
- if (StringUtils.hasText(relativeUri.getPath())) {
- for (String part : relativeUri.getPath().split("/")) {
+ if(StringUtils.hasText(relativeUri.getPath())) {
+ for(String part : relativeUri.getPath().split("/")) {
uris.add(URI.create(part + (StringUtils.hasText(uri.getQuery()) ? "?" + uri.getQuery() : "")));
}
}
@@ -86,38 +97,41 @@ public abstract class UriUtils {
* e.g. merging base URI {@literal http://localhost:8080/data} and relative uri {@literal person/1?name=John+Doe}
* would result in an absolute URI of {@literal http://localhost:8080/data/person/1?name=John+Doe}
*
- * @param baseUri base {@link URI}
- * @param uris {@link URI}s to merge
+ * @param baseUri
+ * base {@link URI}
+ * @param uris
+ * {@link URI}s to merge
+ *
* @return {@link URI} that is the combination of all the given (possibly relative, possibly absolute) URIs.
*/
public static URI merge(URI baseUri, URI... uris) {
StringBuilder query = new StringBuilder();
UriComponentsBuilder ub = UriComponentsBuilder.fromUri(baseUri);
- for (URI uri : uris) {
+ for(URI uri : uris) {
String s = uri.getScheme();
- if (null != s) {
+ if(null != s) {
ub.scheme(s);
}
s = uri.getUserInfo();
- if (null != s) {
+ if(null != s) {
ub.userInfo(s);
}
s = uri.getHost();
- if (null != s) {
+ if(null != s) {
ub.host(s);
}
int i = uri.getPort();
- if (i > 0) {
+ if(i > 0) {
ub.port(i);
}
s = uri.getPath();
- if (null != s) {
- if (!uri.isAbsolute() && StringUtils.hasText(s)) {
+ if(null != s) {
+ if(!uri.isAbsolute() && StringUtils.hasText(s)) {
ub.pathSegment(s);
} else {
ub.path(s);
@@ -125,20 +139,20 @@ public abstract class UriUtils {
}
s = uri.getQuery();
- if (null != s) {
- if (query.length() > 0) {
+ if(null != s) {
+ if(query.length() > 0) {
query.append("&");
}
query.append(s);
}
s = uri.getFragment();
- if (null != s) {
+ if(null != s) {
ub.fragment(s);
}
}
- if (query.length() > 0) {
+ if(query.length() > 0) {
ub.query(query.toString());
}
@@ -149,14 +163,15 @@ public abstract class UriUtils {
* Just the path portion of the {@link URI}, but with any trailing slash "/" removed.
*
* @param uri
+ *
* @return
*/
public static String path(URI uri) {
- if (null == uri) {
+ if(null == uri) {
return null;
}
String s = uri.getPath();
- if (s.endsWith("/")) {
+ if(s.endsWith("/")) {
return s.substring(0, s.length() - 1);
} else {
return s;
@@ -166,8 +181,11 @@ public abstract class UriUtils {
/**
* The very last segment of the {@link URI}.
*
- * @param baseUri base {@link URI}
- * @param uri {@link URI} to explode
+ * @param baseUri
+ * base {@link URI}
+ * @param uri
+ * {@link URI} to explode
+ *
* @return Relative {@link URI} that is the last segment of the path for the given URI.
*/
public static URI tail(URI baseUri, URI uri) {
diff --git a/spring-data-rest-repository/build.gradle b/spring-data-rest-repository/build.gradle
index 482ac1852..40eec5a77 100644
--- a/spring-data-rest-repository/build.gradle
+++ b/spring-data-rest-repository/build.gradle
@@ -1,15 +1,14 @@
dependencies {
// Spring
- compile("org.springframework:spring-orm:$springVersion") { force = true }
- compile("org.springframework:spring-oxm:$springVersion") { force = true }
- compile("org.springframework:spring-tx:$springVersion") { force = true }
+ //compile("org.springframework:spring-orm:$springVersion") { force = true }
+ //compile("org.springframework:spring-oxm:$springVersion") { force = true }
// JPA
compile "org.hibernate.javax.persistence:hibernate-jpa-2.0-api:1.0.1.Final"
// Spring Data
- compile "org.springframework.data:spring-data-commons-core:$sdCommonsVersion"
+ //compile "org.springframework.data:spring-data-commons-core:$sdCommonsVersion"
compile "org.springframework.data:spring-data-jpa:$sdJpaVersion"
// Exporter core
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/AttributeMetadata.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/AttributeMetadata.java
index 904e870c7..584fdb03f 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/AttributeMetadata.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/AttributeMetadata.java
@@ -42,7 +42,9 @@ public interface AttributeMetadata {
/**
* Get the path of this attribute as a {@link Collection}.
*
- * @param target The entity to inspect for this attribute.
+ * @param target
+ * The entity to inspect for this attribute.
+ *
* @return attribute value as a {@link Collection}
*/
Collection> asCollection(Object target);
@@ -57,7 +59,9 @@ public interface AttributeMetadata {
/**
* Get the path of this attribute as a {@link Set}.
*
- * @param target The entity to inspect for this attribute.
+ * @param target
+ * The entity to inspect for this attribute.
+ *
* @return attribute value as a {@link Set}
*/
Set> asSet(Object target);
@@ -72,7 +76,9 @@ public interface AttributeMetadata {
/**
* Get the path of this attribute as a {@link Map}.
*
- * @param target The entity to inspect for this attribute.
+ * @param target
+ * The entity to inspect for this attribute.
+ *
* @return attribute value as a {@link Map}
*/
Map asMap(Object target);
@@ -80,7 +86,9 @@ public interface AttributeMetadata {
/**
* Get the path of this attribute.
*
- * @param target The entity to inspect for this attribute.
+ * @param target
+ * The entity to inspect for this attribute.
+ *
* @return attribute value
*/
Object get(Object target);
@@ -88,8 +96,11 @@ public interface AttributeMetadata {
/**
* Set the path of this attribute.
*
- * @param value Value to set on this attribute.
- * @param target The entity to set this attribute's value on.
+ * @param value
+ * Value to set on this attribute.
+ * @param target
+ * The entity to set this attribute's value on.
+ *
* @return @this
*/
AttributeMetadata set(Object value, Object target);
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/EntityMetadata.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/EntityMetadata.java
index bbf9660bf..9bd40d63b 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/EntityMetadata.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/EntityMetadata.java
@@ -47,7 +47,9 @@ public interface EntityMetadata {
/**
* Get {@link AttributeMetadata} by name.
*
- * @param name The name of the attribute.
+ * @param name
+ * The name of the attribute.
+ *
* @return {@link AttributeMetadata} or {@literal null} if that attribute doesn't exist.
*/
A attribute(String name);
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryConstraintViolationException.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryConstraintViolationException.java
index 44752c4ab..bcdcf811a 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryConstraintViolationException.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryConstraintViolationException.java
@@ -6,7 +6,8 @@ import org.springframework.validation.Errors;
/**
* @author Jon Brisbin
*/
-public class RepositoryConstraintViolationException extends DataIntegrityViolationException {
+public class RepositoryConstraintViolationException
+ extends DataIntegrityViolationException {
private Errors errors;
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryExporter.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryExporter.java
index 90266b13f..92ae7d6c5 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryExporter.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryExporter.java
@@ -25,7 +25,7 @@ public abstract class RepositoryExporter, E exte
InitializingBean {
protected ApplicationContext applicationContext;
- protected Repositories repositories;
+ protected Repositories repositories;
protected List exportOnlyTheseClasses = Collections.emptyList();
protected Map repositoryMetadata;
@@ -42,22 +42,24 @@ public abstract class RepositoryExporter, E exte
* Set the class names of only those Repositories you want exported.
* Default is to export all found Repositories.
*
- * @param exportOnlyTheseClasses {@link List} of class names to export.
+ * @param exportOnlyTheseClasses
+ * {@link List} of class names to export.
+ *
* @return @this
*/
@SuppressWarnings({"unchecked"})
public M setExportOnlyTheseClasses(List exportOnlyTheseClasses) {
this.exportOnlyTheseClasses = exportOnlyTheseClasses;
- return (M) this;
+ return (M)this;
}
- @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ @Override public void setApplicationContext(ApplicationContext applicationContext)
+ throws BeansException {
this.applicationContext = applicationContext;
}
@SuppressWarnings({"unchecked"})
@Override public void afterPropertiesSet() throws Exception {
-
}
/**
@@ -66,20 +68,22 @@ public abstract class RepositoryExporter, E exte
* @return {@link List} of class names to export.
*/
public Set repositoryNames() {
- findRepositories();
+ refresh();
return repositoryMetadata.keySet();
}
/**
* Is a Repository being exporter that supports this domain type?
*
- * @param domainType Type of the domain class.
+ * @param domainType
+ * Type of the domain class.
+ *
* @return {@literal true} if a Repository is being exported, {@literal false} otherwise.
*/
public boolean hasRepositoryFor(Class> domainType) {
- findRepositories();
- for (M repoMeta : repositoryMetadata.values()) {
- if (repoMeta.domainType().isAssignableFrom(domainType)) {
+ refresh();
+ for(M repoMeta : repositoryMetadata.values()) {
+ if(repoMeta.domainType().isAssignableFrom(domainType)) {
return true;
}
}
@@ -89,13 +93,15 @@ public abstract class RepositoryExporter, E exte
/**
* Get the RepositoryMetadata for the Repository responsible for this domain type.
*
- * @param domainType Type of the domain class.
+ * @param domainType
+ * Type of the domain class.
+ *
* @return {@link RepositoryMetadata} instance
*/
public M repositoryMetadataFor(Class> domainType) {
- findRepositories();
- for (M repoMeta : repositoryMetadata.values()) {
- if (repoMeta.domainType().isAssignableFrom(domainType)) {
+ refresh();
+ for(M repoMeta : repositoryMetadata.values()) {
+ if(repoMeta.domainType().isAssignableFrom(domainType)) {
return repoMeta;
}
}
@@ -105,11 +111,13 @@ public abstract class RepositoryExporter, E exte
/**
* Get the {@link RepositoryMetadata} for the Repository exported under the given name.
*
- * @param name Name a Repository would be exported under.
+ * @param name
+ * Name a Repository would be exported under.
+ *
* @return {@link RepositoryMetadata} instance
*/
public M repositoryMetadataFor(String name) {
- findRepositories();
+ refresh();
return repositoryMetadata.get(name);
}
@@ -118,25 +126,27 @@ public abstract class RepositoryExporter, E exte
Class> repoClass,
Repositories repositories);
- private void findRepositories() {
- if (null == repositories) {
- repositories = new Repositories(applicationContext);
- repositoryMetadata = new HashMap();
- for (Class> domainType : repositories) {
- if (exportOnlyTheseClasses.isEmpty() || exportOnlyTheseClasses.contains(domainType.getName())) {
- Class> repoClass = repositories.getRepositoryInformationFor(domainType).getRepositoryInterface();
- String name = StringUtils.uncapitalize(repoClass.getSimpleName().replaceAll("Repository", ""));
- RestResource resourceAnno = repoClass.getAnnotation(RestResource.class);
- boolean exported = true;
- if (null != resourceAnno) {
- if (StringUtils.hasText(resourceAnno.path())) {
- name = resourceAnno.path();
- }
- exported = resourceAnno.exported();
- }
- if (exported) {
- repositoryMetadata.put(name, createRepositoryMetadata(name, domainType, repoClass, repositories));
+ @SuppressWarnings({"unchecked"})
+ public void refresh() {
+ if(null != repositories) {
+ return;
+ }
+ repositories = new Repositories(applicationContext);
+ repositoryMetadata = new HashMap();
+ for(Class> domainType : repositories) {
+ if(exportOnlyTheseClasses.isEmpty() || exportOnlyTheseClasses.contains(domainType.getName())) {
+ Class> repoClass = repositories.getRepositoryInformationFor(domainType).getRepositoryInterface();
+ String name = StringUtils.uncapitalize(repoClass.getSimpleName().replaceAll("Repository", ""));
+ RestResource resourceAnno = repoClass.getAnnotation(RestResource.class);
+ boolean exported = true;
+ if(null != resourceAnno) {
+ if(StringUtils.hasText(resourceAnno.path())) {
+ name = resourceAnno.path();
}
+ exported = resourceAnno.exported();
+ }
+ if(exported) {
+ repositoryMetadata.put(name, createRepositoryMetadata(name, domainType, repoClass, repositories));
}
}
}
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryExporterSupport.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryExporterSupport.java
index e1710362d..f5e8c8b0f 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryExporterSupport.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryExporterSupport.java
@@ -28,7 +28,8 @@ public abstract class RepositoryExporterSupport repositoryExporters) {
this.repositoryExporters = repositoryExporters;
@@ -46,39 +47,44 @@ public abstract class RepositoryExporterSupport repositoryExporters) {
setRepositoryExporters(repositoryExporters);
- return (S) this;
+ return (S)this;
}
/**
* Set the {@link RepositoryExporter}s to use.
*
* @param repositoryExporter
+ *
* @return
*/
@SuppressWarnings({"unchecked"})
public S repositoryExporters(RepositoryExporter... repositoryExporter) {
setRepositoryExporters(Arrays.asList(repositoryExporter));
- return (S) this;
+ return (S)this;
}
/**
* Find {@link RepositoryMetadata} for the {@link org.springframework.data.repository.Repository} exported under this
* name.
*
- * @param name URL segment name.
+ * @param name
+ * URL segment name.
+ *
* @return {@link RepositoryMetadata} or {@literal null} if none found.
*/
@SuppressWarnings({"unchecked"})
protected RepositoryMetadata repositoryMetadataFor(String name) {
- for (RepositoryExporter exporter : repositoryExporters) {
+ for(RepositoryExporter exporter : repositoryExporters) {
RepositoryMetadata repoMeta = exporter.repositoryMetadataFor(name);
- if (null != repoMeta) {
+ if(null != repoMeta) {
return repoMeta;
}
}
@@ -89,14 +95,16 @@ public abstract class RepositoryExporterSupport domainType) {
- for (RepositoryExporter exporter : repositoryExporters) {
+ for(RepositoryExporter exporter : repositoryExporters) {
RepositoryMetadata repoMeta = exporter.repositoryMetadataFor(domainType);
- if (null != repoMeta) {
+ if(null != repoMeta) {
return repoMeta;
}
}
@@ -107,12 +115,14 @@ public abstract class RepositoryExporterSupport
*/
-public class RepositoryNotFoundException extends DataAccessResourceFailureException {
+public class RepositoryNotFoundException
+ extends DataAccessResourceFailureException {
public RepositoryNotFoundException(String msg) {
super(msg);
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryQueryMethod.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryQueryMethod.java
deleted file mode 100644
index ec0f2343a..000000000
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryQueryMethod.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package org.springframework.data.rest.repository;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-
-import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
-import org.springframework.data.repository.query.Param;
-
-/**
- * @author Jon Brisbin
- */
-public class RepositoryQueryMethod {
-
- private static final LocalVariableTableParameterNameDiscoverer nameLookup = new LocalVariableTableParameterNameDiscoverer();
-
- private Method method;
- private Class>[] paramTypes;
- private String[] paramNames;
-
- public RepositoryQueryMethod(Method method) {
- this.method = method;
- paramTypes = method.getParameterTypes();
- paramNames = nameLookup.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;
- }
- }
- }
-
- public Class>[] paramTypes() {
- return paramTypes;
- }
-
- public String[] paramNames() {
- return paramNames;
- }
-
- public Method method() {
- return method;
- }
-
-}
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/ValidationErrors.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/ValidationErrors.java
index ed6982344..eb646c150 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/ValidationErrors.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/ValidationErrors.java
@@ -11,13 +11,14 @@ import org.springframework.validation.ObjectError;
/**
* @author Jon Brisbin
*/
-public class ValidationErrors extends AbstractErrors {
+public class ValidationErrors
+ extends AbstractErrors {
- private String name;
- private Object entity;
+ private String name;
+ private Object entity;
private EntityMetadata entityMetadata;
private List globalErrors = new ArrayList();
- private List fieldErrors = new ArrayList();
+ private List fieldErrors = new ArrayList();
public ValidationErrors(String name, Object entity, EntityMetadata entityMetadata) {
this.name = name;
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/AbstractRepositoryEventListener.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/AbstractRepositoryEventListener.java
index e0b7f78ab..b56980aac 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/AbstractRepositoryEventListener.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/AbstractRepositoryEventListener.java
@@ -23,7 +23,8 @@ public abstract class AbstractRepositoryEventListener
*/
-public class AfterDeleteEvent extends RepositoryEvent {
+public class AfterDeleteEvent
+ extends RepositoryEvent {
public AfterDeleteEvent(Object source) {
super(source);
}
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/AfterLinkSaveEvent.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/AfterLinkSaveEvent.java
index b316f9841..825317042 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/AfterLinkSaveEvent.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/AfterLinkSaveEvent.java
@@ -5,7 +5,8 @@ package org.springframework.data.rest.repository.context;
*
* @author Jon Brisbin
*/
-public class AfterLinkSaveEvent extends LinkSaveEvent {
+public class AfterLinkSaveEvent
+ extends LinkSaveEvent {
public AfterLinkSaveEvent(Object source, Object child) {
super(source, child);
}
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/AfterSaveEvent.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/AfterSaveEvent.java
index 3df0d9eca..895f7405e 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/AfterSaveEvent.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/AfterSaveEvent.java
@@ -5,7 +5,8 @@ package org.springframework.data.rest.repository.context;
*
* @author Jon Brisbin
*/
-public class AfterSaveEvent extends RepositoryEvent {
+public class AfterSaveEvent
+ extends RepositoryEvent {
public AfterSaveEvent(Object source) {
super(source);
}
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/AnnotatedHandlerRepositoryEventListener.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/AnnotatedHandlerRepositoryEventListener.java
index 3200dda6c..2ba6524a9 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/AnnotatedHandlerRepositoryEventListener.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/AnnotatedHandlerRepositoryEventListener.java
@@ -37,7 +37,7 @@ public class AnnotatedHandlerRepositoryEventListener
ApplicationContextAware,
InitializingBean {
- private String basePackage;
+ private String basePackage;
private ApplicationContext applicationContext;
private Multimap, EventHandlerMethod> handlerMethods = ArrayListMultimap.create();
@@ -48,7 +48,8 @@ public class AnnotatedHandlerRepositoryEventListener
this.basePackage = basePackage;
}
- @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ @Override public void setApplicationContext(ApplicationContext applicationContext)
+ throws BeansException {
this.applicationContext = applicationContext;
}
@@ -64,7 +65,9 @@ public class AnnotatedHandlerRepositoryEventListener
/**
* Set the base package in which to search for event handlers.
*
- * @param basePackage Base package to search for handlers.
+ * @param basePackage
+ * Base package to search for handlers.
+ *
* @return @this
*/
public AnnotatedHandlerRepositoryEventListener setBasePackage(String basePackage) {
@@ -84,7 +87,9 @@ public class AnnotatedHandlerRepositoryEventListener
/**
* Set the base package in which to search for event handlers.
*
- * @param basePackage Base package to search for handlers.
+ * @param basePackage
+ * Base package to search for handlers.
+ *
* @return @this
*/
public AnnotatedHandlerRepositoryEventListener basePackage(String basePackage) {
@@ -92,23 +97,25 @@ public class AnnotatedHandlerRepositoryEventListener
return this;
}
- @Override public void afterPropertiesSet() throws Exception {
+ @Override public void afterPropertiesSet()
+ throws Exception {
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(RepositoryEventHandler.class, true, true));
- for (BeanDefinition beanDef : scanner.findCandidateComponents(basePackage)) {
+ for(BeanDefinition beanDef : scanner.findCandidateComponents(basePackage)) {
String typeName = beanDef.getBeanClassName();
Class> handlerType = ClassUtils.forName(typeName, ClassUtils.getDefaultClassLoader());
RepositoryEventHandler typeAnno = handlerType.getAnnotation(RepositoryEventHandler.class);
Class>[] targetTypes = typeAnno.value();
- if (targetTypes.length == 0) {
+ if(targetTypes.length == 0) {
targetTypes = new Class>[]{null};
}
- for (final Class> targetType : targetTypes) {
- for (final Object handler : applicationContext.getBeansOfType(handlerType).values()) {
+ for(final Class> targetType : targetTypes) {
+ for(final Object handler : applicationContext.getBeansOfType(handlerType).values()) {
ReflectionUtils.doWithMethods(
handler.getClass(),
new ReflectionUtils.MethodCallback() {
- @Override public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
+ @Override public void doWith(Method method)
+ throws IllegalArgumentException, IllegalAccessException {
inspect(targetType, handler, method, HandleBeforeSave.class, BeforeSaveEvent.class);
inspect(targetType, handler, method, HandleAfterSave.class, AfterSaveEvent.class);
inspect(targetType, handler, method, HandleBeforeLinkSave.class, BeforeLinkSaveEvent.class);
@@ -133,21 +140,21 @@ public class AnnotatedHandlerRepositoryEventListener
@Override public void onApplicationEvent(RepositoryEvent event) {
Class extends RepositoryEvent> eventType = event.getClass();
- if (handlerMethods.containsKey(eventType)) {
- for (EventHandlerMethod handlerMethod : handlerMethods.get(eventType)) {
+ if(handlerMethods.containsKey(eventType)) {
+ for(EventHandlerMethod handlerMethod : handlerMethods.get(eventType)) {
try {
Object src = event.getSource();
- if (ClassUtils.isAssignable(handlerMethod.targetType, src.getClass())) {
+ if(ClassUtils.isAssignable(handlerMethod.targetType, src.getClass())) {
List params = new ArrayList();
params.add(src);
- if (event instanceof BeforeLinkSaveEvent) {
- params.add(((BeforeLinkSaveEvent) event).getLinked());
- } else if (event instanceof AfterLinkSaveEvent) {
- params.add(((AfterLinkSaveEvent) event).getLinked());
+ if(event instanceof BeforeLinkSaveEvent) {
+ params.add(((BeforeLinkSaveEvent)event).getLinked());
+ } else if(event instanceof AfterLinkSaveEvent) {
+ params.add(((AfterLinkSaveEvent)event).getLinked());
}
handlerMethod.method.invoke(handlerMethod.handler, params.toArray());
}
- } catch (Exception e) {
+ } catch(Exception e) {
throw new IllegalStateException(e);
}
}
@@ -160,31 +167,31 @@ public class AnnotatedHandlerRepositoryEventListener
Class annoType,
Class extends RepositoryEvent> eventType) {
T anno = method.getAnnotation(annoType);
- if (null != anno) {
+ if(null != anno) {
try {
Class>[] targetTypes;
- if (null == targetType) {
- targetTypes = (Class>[]) anno.getClass().getMethod("value", new Class[0]).invoke(anno);
+ if(null == targetType) {
+ targetTypes = (Class>[])anno.getClass().getMethod("value", new Class[0]).invoke(anno);
} else {
targetTypes = new Class>[]{targetType};
}
- for (Class> type : targetTypes) {
+ for(Class> type : targetTypes) {
handlerMethods.put(eventType,
new EventHandlerMethod(type,
handler,
method));
}
- } catch (NoSuchMethodException ignored) {
- } catch (InvocationTargetException ignored) {
- } catch (IllegalAccessException ignored) {
+ } catch(NoSuchMethodException ignored) {
+ } catch(InvocationTargetException ignored) {
+ } catch(IllegalAccessException ignored) {
}
}
}
private class EventHandlerMethod {
final Class> targetType;
- final Method method;
- final Object handler;
+ final Method method;
+ final Object handler;
private EventHandlerMethod(Class> targetType,
Object handler,
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/BeforeDeleteEvent.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/BeforeDeleteEvent.java
index ee61acedb..5f5c53bed 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/BeforeDeleteEvent.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/BeforeDeleteEvent.java
@@ -5,7 +5,8 @@ package org.springframework.data.rest.repository.context;
*
* @author Jon Brisbin
*/
-public class BeforeDeleteEvent extends RepositoryEvent {
+public class BeforeDeleteEvent
+ extends RepositoryEvent {
public BeforeDeleteEvent(Object source) {
super(source);
}
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/BeforeLinkSaveEvent.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/BeforeLinkSaveEvent.java
index 34991125b..d7c5a45d4 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/BeforeLinkSaveEvent.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/BeforeLinkSaveEvent.java
@@ -5,7 +5,8 @@ package org.springframework.data.rest.repository.context;
*
* @author Jon Brisbin
*/
-public class BeforeLinkSaveEvent extends LinkSaveEvent {
+public class BeforeLinkSaveEvent
+ extends LinkSaveEvent {
public BeforeLinkSaveEvent(Object source, Object linked) {
super(source, linked);
}
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/BeforeSaveEvent.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/BeforeSaveEvent.java
index ddcfa2408..a5ff5be32 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/BeforeSaveEvent.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/BeforeSaveEvent.java
@@ -5,7 +5,8 @@ package org.springframework.data.rest.repository.context;
*
* @author Jon Brisbin
*/
-public class BeforeSaveEvent extends RepositoryEvent {
+public class BeforeSaveEvent
+ extends RepositoryEvent {
public BeforeSaveEvent(Object source) {
super(source);
}
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/LinkSaveEvent.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/LinkSaveEvent.java
index ea71c7e4a..694a845e0 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/LinkSaveEvent.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/LinkSaveEvent.java
@@ -5,7 +5,8 @@ package org.springframework.data.rest.repository.context;
*
* @author Jon Brisbin
*/
-public abstract class LinkSaveEvent extends RepositoryEvent {
+public abstract class LinkSaveEvent
+ extends RepositoryEvent {
private final Object linked;
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/RepositoryEvent.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/RepositoryEvent.java
index f74b15e15..95a1fa101 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/RepositoryEvent.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/RepositoryEvent.java
@@ -5,7 +5,8 @@ import org.springframework.context.ApplicationEvent;
/**
* @author Jon Brisbin
*/
-public abstract class RepositoryEvent extends ApplicationEvent {
+public abstract class RepositoryEvent
+ extends ApplicationEvent {
protected RepositoryEvent(Object source) {
super(source);
}
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/ValidatingRepositoryEventListener.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/ValidatingRepositoryEventListener.java
index ba0f33ff9..067dd8a27 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/ValidatingRepositoryEventListener.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/ValidatingRepositoryEventListener.java
@@ -29,20 +29,21 @@ public class ValidatingRepositoryEventListener
private Multimap validators = ArrayListMultimap.create();
- @Override public void afterPropertiesSet() throws Exception {
- if (validators.size() == 0) {
- for (Map.Entry entry : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext,
- Validator.class)
- .entrySet()) {
+ @Override public void afterPropertiesSet()
+ throws Exception {
+ if(validators.size() == 0) {
+ for(Map.Entry entry : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext,
+ Validator.class)
+ .entrySet()) {
String name = null;
Validator v = entry.getValue();
- if (entry.getKey().contains("Save")) {
+ if(entry.getKey().contains("Save")) {
name = entry.getKey().substring(0, entry.getKey().indexOf("Save") + 4);
- } else if (entry.getKey().contains("Delete")) {
+ } else if(entry.getKey().contains("Delete")) {
name = entry.getKey().substring(0, entry.getKey().indexOf("Delete") + 6);
}
- if (null != name) {
+ if(null != name) {
this.validators.put(name, v);
}
}
@@ -61,11 +62,13 @@ public class ValidatingRepositoryEventListener
/**
* Assign a Map of {@link Validator}s that are assigned to the various {@link RepositoryEvent}s.
*
- * @param validators A Map of Validators to wire.
+ * @param validators
+ * A Map of Validators to wire.
+ *
* @return @this
*/
public ValidatingRepositoryEventListener setValidators(Map> validators) {
- for (Map.Entry> entry : validators.entrySet()) {
+ for(Map.Entry> entry : validators.entrySet()) {
this.validators.replaceValues(entry.getKey(), entry.getValue());
}
return this;
@@ -74,8 +77,11 @@ public class ValidatingRepositoryEventListener
/**
* Add a {@link Validator} that will be triggered on the given event.
*
- * @param event The event to listen for.
- * @param validator The Validator to execute when that event fires.
+ * @param event
+ * The event to listen for.
+ * @param validator
+ * The Validator to execute when that event fires.
+ *
* @return @this
*/
public ValidatingRepositoryEventListener addValidator(String event, Validator validator) {
@@ -109,21 +115,21 @@ public class ValidatingRepositoryEventListener
private Errors validate(String event, Object o) {
Errors errors = null;
- if (null != o) {
+ if(null != o) {
Class> domainType = o.getClass();
errors = new ValidationErrors(domainType.getSimpleName(),
o,
repositoryMetadataFor(domainType).entityMetadata());
Collection validators = this.validators.get(event);
- if (null != validators) {
- for (Validator v : validators) {
- if (v.supports(o.getClass())) {
+ if(null != validators) {
+ for(Validator v : validators) {
+ if(v.supports(o.getClass())) {
LOG.debug(event + ": " + o + " with " + v);
ValidationUtils.invokeValidator(v, o, errors);
}
}
}
- if (errors.getErrorCount() > 0) {
+ if(errors.getErrorCount() > 0) {
throw new RepositoryConstraintViolationException(errors);
}
}
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryMethod.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryMethod.java
similarity index 63%
rename from spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryMethod.java
rename to spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryMethod.java
index c91d0c94a..03e134200 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryMethod.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryMethod.java
@@ -1,8 +1,7 @@
-package org.springframework.data.rest.repository;
+package org.springframework.data.rest.repository.invoke;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
-import java.util.Arrays;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.data.domain.Pageable;
@@ -23,16 +22,16 @@ public class RepositoryMethod {
FIND_ONE,
SAVE;
- public static Type fromMethodName( String s ) {
- if ( "count".equals( s ) ) {
+ public static Type fromMethodName(String s) {
+ if("count".equals(s)) {
return COUNT;
- } else if ( "delete".equals( s ) ) {
+ } else if("delete".equals(s)) {
return DELETE;
- } else if ( "findAll".equals( s ) ) {
+ } else if("findAll".equals(s)) {
return FIND_ALL;
- } else if ( "findOne".equals( s ) ) {
+ } else if("findOne".equals(s)) {
return FIND_ONE;
- } else if ( "save".equals( s ) ) {
+ } else if("save".equals(s)) {
return SAVE;
} else {
return CUSTOM;
@@ -40,7 +39,7 @@ public class RepositoryMethod {
}
public String toMethodName() {
- switch (this) {
+ switch(this) {
case COUNT:
return "count";
case DELETE:
@@ -58,49 +57,49 @@ public class RepositoryMethod {
}
- public static final ReflectionUtils.MethodFilter USER_METHODS = new ReflectionUtils.MethodFilter() {
- @Override public boolean matches( Method method ) {
+ public static final ReflectionUtils.MethodFilter USER_METHODS = new ReflectionUtils.MethodFilter() {
+ @Override public boolean matches(Method method) {
return (!method.isSynthetic()
&& !method.isBridge()
&& method.getDeclaringClass() != Object.class
- && !method.getName().contains( "$" ));
+ && !method.getName().contains("$"));
}
};
public static final LocalVariableTableParameterNameDiscoverer NAME_DISCOVERER = new LocalVariableTableParameterNameDiscoverer();
- private Method method;
+ private Method method;
private Class>[] paramTypes;
- private String[] paramNames;
+ private String[] paramNames;
private boolean pageable = false;
private boolean sortable = false;
- public RepositoryMethod( Method method ) {
+ public RepositoryMethod(Method method) {
this.method = method;
paramTypes = method.getParameterTypes();
- for ( Class> type : paramTypes ) {
- if ( Pageable.class.isAssignableFrom( type ) ) {
+ for(Class> type : paramTypes) {
+ if(Pageable.class.isAssignableFrom(type)) {
pageable = true;
}
- if ( Sort.class.isAssignableFrom( type ) ) {
+ if(Sort.class.isAssignableFrom(type)) {
sortable = true;
}
}
- paramNames = NAME_DISCOVERER.getParameterNames( method );
- if ( null == paramNames ) {
+ paramNames = 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;
+ 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] ) {
+ if(null == paramNames[i]) {
paramNames[i] = "arg" + i;
}
}
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryMethodResponse.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryMethodResponse.java
new file mode 100644
index 000000000..681060054
--- /dev/null
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryMethodResponse.java
@@ -0,0 +1,103 @@
+package org.springframework.data.rest.repository.invoke;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.springframework.data.rest.core.Link;
+
+/**
+ * @author Jon Brisbin
+ */
+public class RepositoryMethodResponse {
+
+ @JsonProperty("results")
+ private List results = new ArrayList();
+ @JsonProperty("_links")
+ private List links = new ArrayList ();
+ private long totalCount = 0;
+ private int totalPages = 1;
+ private int currentPage = 1;
+
+ public RepositoryMethodResponse addLink(Link l) {
+ links.add(l);
+ return this;
+ }
+
+ public RepositoryMethodResponse addResult(Object obj) {
+ results.add(obj);
+ return this;
+ }
+
+ public RepositoryMethodResponse addAllResults(Iterator results) {
+ if(null == results) {
+ return this;
+ }
+
+ while(results.hasNext()) {
+ addResult(results.next());
+ }
+
+ return this;
+ }
+
+ public List getResults() {
+ return results;
+ }
+
+ public boolean hasResults() {
+ return (results.size() > 0);
+ }
+
+ public RepositoryMethodResponse setResults(List results) {
+ if(null == results) {
+ this.results = Collections.emptyList();
+ } else {
+ this.results = results;
+ }
+ return this;
+ }
+
+ public List getLinks() {
+ return links;
+ }
+
+ public RepositoryMethodResponse setLinks(List links) {
+ if(null == links) {
+ this.links = Collections.emptyList();
+ } else {
+ this.links = links;
+ }
+ return this;
+ }
+
+ public long getTotalCount() {
+ return totalCount;
+ }
+
+ public RepositoryMethodResponse setTotalCount(long totalCount) {
+ this.totalCount = totalCount;
+ return this;
+ }
+
+ public int getTotalPages() {
+ return totalPages;
+ }
+
+ public RepositoryMethodResponse setTotalPages(int totalPages) {
+ this.totalPages = totalPages;
+ return this;
+ }
+
+ public int getCurrentPage() {
+ return currentPage;
+ }
+
+ public RepositoryMethodResponse setCurrentPage(int currentPage) {
+ this.currentPage = currentPage;
+ return this;
+ }
+
+}
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryQueryMethod.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryQueryMethod.java
new file mode 100644
index 000000000..b8cedb95e
--- /dev/null
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/invoke/RepositoryQueryMethod.java
@@ -0,0 +1,64 @@
+package org.springframework.data.rest.repository.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;
+
+/**
+ * @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()));
+ }
+ }
+
+ public Class>[] paramTypes() {
+ return paramTypes;
+ }
+
+ public String[] paramNames() {
+ return paramNames;
+ }
+
+ public Method method() {
+ return method;
+ }
+
+}
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/jpa/JpaAttributeMetadata.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/jpa/JpaAttributeMetadata.java
index 8e3f0bd8d..06362adaa 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/jpa/JpaAttributeMetadata.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/jpa/JpaAttributeMetadata.java
@@ -17,14 +17,15 @@ import org.springframework.util.ReflectionUtils;
/**
* @author Jon Brisbin
*/
-public class JpaAttributeMetadata implements AttributeMetadata {
+public class JpaAttributeMetadata
+ implements AttributeMetadata {
- private String name;
+ private String name;
private Attribute attribute;
- private Class> type;
- private Field field;
- private Method getter;
- private Method setter;
+ private Class> type;
+ private Field field;
+ private Method getter;
+ private Method setter;
public JpaAttributeMetadata(EntityType> entityType, Attribute attribute) {
this.attribute = attribute;
@@ -35,14 +36,16 @@ public class JpaAttributeMetadata implements AttributeMetadata {
ReflectionUtils.makeAccessible(field);
PropertyDescriptor property = BeanUtils.getPropertyDescriptor(entityType.getJavaType(), name);
- if (null != property) {
+ if(null != property) {
getter = property.getReadMethod();
- if (null != getter)
+ if(null != getter) {
ReflectionUtils.makeAccessible(getter);
+ }
setter = property.getWriteMethod();
- if (null != setter)
+ if(null != setter) {
ReflectionUtils.makeAccessible(setter);
+ }
}
}
@@ -56,14 +59,14 @@ public class JpaAttributeMetadata implements AttributeMetadata {
@Override public Class> elementType() {
return (attribute instanceof PluralAttribute
- ? ((PluralAttribute) attribute).getElementType().getJavaType()
- : null);
+ ? ((PluralAttribute)attribute).getElementType().getJavaType()
+ : null);
}
@Override public boolean isCollectionLike() {
- if (attribute instanceof PluralAttribute) {
- PluralAttribute plattr = (PluralAttribute) attribute;
- switch (plattr.getCollectionType()) {
+ if(attribute instanceof PluralAttribute) {
+ PluralAttribute plattr = (PluralAttribute)attribute;
+ switch(plattr.getCollectionType()) {
case COLLECTION:
case LIST:
return true;
@@ -76,13 +79,13 @@ public class JpaAttributeMetadata implements AttributeMetadata {
}
@Override public Collection> asCollection(Object target) {
- return (Collection>) get(target);
+ return (Collection>)get(target);
}
@Override public boolean isSetLike() {
- if (attribute instanceof PluralAttribute) {
- PluralAttribute plattr = (PluralAttribute) attribute;
- switch (plattr.getCollectionType()) {
+ if(attribute instanceof PluralAttribute) {
+ PluralAttribute plattr = (PluralAttribute)attribute;
+ switch(plattr.getCollectionType()) {
case SET:
return true;
default:
@@ -94,13 +97,13 @@ public class JpaAttributeMetadata implements AttributeMetadata {
}
@Override public Set> asSet(Object target) {
- return (Set>) get(target);
+ return (Set>)get(target);
}
@Override public boolean isMapLike() {
- if (attribute instanceof PluralAttribute) {
- PluralAttribute plattr = (PluralAttribute) attribute;
- switch (plattr.getCollectionType()) {
+ if(attribute instanceof PluralAttribute) {
+ PluralAttribute plattr = (PluralAttribute)attribute;
+ switch(plattr.getCollectionType()) {
case MAP:
return true;
default:
@@ -112,29 +115,29 @@ public class JpaAttributeMetadata implements AttributeMetadata {
}
@Override public Map asMap(Object target) {
- return (Map) get(target);
+ return (Map)get(target);
}
@Override public Object get(Object target) {
try {
- if (null != getter) {
+ if(null != getter) {
return getter.invoke(target);
} else {
return field.get(target);
}
- } catch (Exception e) {
+ } catch(Exception e) {
return null;
}
}
@Override public AttributeMetadata set(Object value, Object target) {
try {
- if (null != setter) {
+ if(null != setter) {
setter.invoke(target, value);
} else {
field.set(target, value);
}
- } catch (Exception e) {
+ } catch(Exception e) {
}
return this;
}
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/jpa/JpaEntityMetadata.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/jpa/JpaEntityMetadata.java
index 90ff4e5dc..fdf234a0d 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/jpa/JpaEntityMetadata.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/jpa/JpaEntityMetadata.java
@@ -16,40 +16,41 @@ import org.springframework.util.ReflectionUtils;
/**
* @author Jon Brisbin
*/
-public class JpaEntityMetadata implements EntityMetadata {
+public class JpaEntityMetadata
+ implements EntityMetadata {
- private Class> type;
+ private Class> type;
private JpaAttributeMetadata idAttribute;
private JpaAttributeMetadata versionAttribute;
private Map embeddedAttributes = new HashMap();
- private Map linkedAttributes = new HashMap();
+ private Map linkedAttributes = new HashMap();
@SuppressWarnings({"unchecked"})
public JpaEntityMetadata(Repositories repositories, EntityType> entityType) {
type = entityType.getJavaType();
idAttribute = new JpaAttributeMetadata(entityType, entityType.getId(entityType.getIdType().getJavaType()));
- if (null != entityType.getVersion(Long.class)) {
+ if(null != entityType.getVersion(Long.class)) {
versionAttribute = new JpaAttributeMetadata(entityType, entityType.getVersion(Long.class));
}
- for (Attribute attr : entityType.getAttributes()) {
+ for(Attribute attr : entityType.getAttributes()) {
boolean exported = true;
Field field = ReflectionUtils.findField(type, attr.getJavaMember().getName());
- if (null != field) {
+ if(null != field) {
RestResource fieldResourceAnno = field.getAnnotation(RestResource.class);
- if (null != fieldResourceAnno) {
+ if(null != fieldResourceAnno) {
exported = fieldResourceAnno.exported();
}
}
- if (exported) {
+ if(exported) {
Class> attrType = (attr instanceof PluralAttribute
- ? ((PluralAttribute) attr).getElementType().getJavaType()
- : attr.getJavaType());
- if (repositories.hasRepositoryFor(attrType)) {
+ ? ((PluralAttribute)attr).getElementType().getJavaType()
+ : attr.getJavaType());
+ if(repositories.hasRepositoryFor(attrType)) {
linkedAttributes.put(attr.getName(), new JpaAttributeMetadata(entityType, attr));
} else {
- if (!(attr instanceof SingularAttribute && ((SingularAttribute) attr).isId())
- && !(attr instanceof SingularAttribute && ((SingularAttribute) attr).isVersion())) {
+ if(!(attr instanceof SingularAttribute && ((SingularAttribute)attr).isId())
+ && !(attr instanceof SingularAttribute && ((SingularAttribute)attr).isVersion())) {
embeddedAttributes.put(attr.getName(), new JpaAttributeMetadata(entityType, attr));
}
}
@@ -78,13 +79,13 @@ public class JpaEntityMetadata implements EntityMetadata {
}
@Override public JpaAttributeMetadata attribute(String name) {
- if (idAttribute.name().equals(name)) {
+ if(idAttribute.name().equals(name)) {
return idAttribute;
- } else if (null != versionAttribute && versionAttribute.name().equals(name)) {
+ } else if(null != versionAttribute && versionAttribute.name().equals(name)) {
return versionAttribute;
- } else if (embeddedAttributes.containsKey(name)) {
+ } else if(embeddedAttributes.containsKey(name)) {
return embeddedAttributes.get(name);
- } else if (linkedAttributes.containsKey(name)) {
+ } else if(linkedAttributes.containsKey(name)) {
return linkedAttributes.get(name);
}
return null;
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/jpa/JpaRepositoryExporter.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/jpa/JpaRepositoryExporter.java
index b43ff5e2f..9e523ca45 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/jpa/JpaRepositoryExporter.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/jpa/JpaRepositoryExporter.java
@@ -12,7 +12,8 @@ import org.springframework.data.rest.repository.RepositoryExporter;
*
* @author Jon Brisbin
*/
-public class JpaRepositoryExporter extends RepositoryExporter {
+public class JpaRepositoryExporter
+ extends RepositoryExporter {
protected EntityManager entityManager;
diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/jpa/JpaRepositoryMetadata.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/jpa/JpaRepositoryMetadata.java
index 52fab5ad2..1dfe91846 100644
--- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/jpa/JpaRepositoryMetadata.java
+++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/jpa/JpaRepositoryMetadata.java
@@ -12,23 +12,24 @@ import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.repository.RepositoryMetadata;
-import org.springframework.data.rest.repository.RepositoryQueryMethod;
import org.springframework.data.rest.repository.annotation.RestResource;
+import org.springframework.data.rest.repository.invoke.RepositoryQueryMethod;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* @author Jon Brisbin
*/
-public class JpaRepositoryMetadata implements RepositoryMetadata {
+public class JpaRepositoryMetadata
+ implements RepositoryMetadata {
- private final String name;
- private final Class> repoClass;
+ private final String name;
+ private final Class> repoClass;
private final CrudRepository repository;
- private final EntityInformation entityInfo;
+ private final EntityInformation entityInfo;
private final Map queryMethods = new HashMap();
- private String rel;
+ private String rel;
private JpaEntityMetadata entityMetadata;
@SuppressWarnings({"unchecked"})
@@ -43,23 +44,23 @@ public class JpaRepositoryMetadata implements RepositoryMetadata
*/
-@ContextConfiguration(locations = ["/JpaMetadataSpec-test.xml"])
+@ContextConfiguration(classes = [ApplicationConfig])
class JpaMetadataSpec extends Specification {
@Autowired
diff --git a/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/test/ApplicationConfig.java b/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/test/ApplicationConfig.java
new file mode 100644
index 000000000..379192579
--- /dev/null
+++ b/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/test/ApplicationConfig.java
@@ -0,0 +1,66 @@
+package org.springframework.data.rest.repository.test;
+
+import javax.persistence.EntityManagerFactory;
+import javax.sql.DataSource;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.data.rest.repository.jpa.JpaRepositoryExporter;
+import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
+import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
+import org.springframework.orm.jpa.JpaDialect;
+import org.springframework.orm.jpa.JpaTransactionManager;
+import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
+import org.springframework.orm.jpa.vendor.Database;
+import org.springframework.orm.jpa.vendor.HibernateJpaDialect;
+import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+/**
+ * @author Jon Brisbin
+ */
+@Configuration
+@ComponentScan(basePackageClasses = ApplicationConfig.class)
+@EnableJpaRepositories
+@EnableTransactionManagement
+public class ApplicationConfig {
+
+ @Bean public DataSource dataSource() {
+ EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
+ return builder.setType(EmbeddedDatabaseType.HSQL).build();
+ }
+
+ @Bean public EntityManagerFactory entityManagerFactory() {
+ HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
+ vendorAdapter.setDatabase(Database.HSQL);
+ vendorAdapter.setGenerateDdl(true);
+
+ LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
+ factory.setJpaVendorAdapter(vendorAdapter);
+ factory.setPackagesToScan(getClass().getPackage().getName());
+ factory.setDataSource(dataSource());
+ factory.setPersistenceXmlLocation("/JpaMetadataSpec-persistence.xml");
+
+ factory.afterPropertiesSet();
+
+ return factory.getObject();
+ }
+
+ @Bean public JpaDialect jpaDialect() {
+ return new HibernateJpaDialect();
+ }
+
+ @Bean public PlatformTransactionManager transactionManager() {
+ JpaTransactionManager txManager = new JpaTransactionManager();
+ txManager.setEntityManagerFactory(entityManagerFactory());
+ return txManager;
+ }
+
+ @Bean public JpaRepositoryExporter jpaRepositoryExporter() {
+ return new JpaRepositoryExporter();
+ }
+
+}
diff --git a/spring-data-rest-webmvc/build.gradle b/spring-data-rest-webmvc/build.gradle
index 390f5af77..330203430 100644
--- a/spring-data-rest-webmvc/build.gradle
+++ b/spring-data-rest-webmvc/build.gradle
@@ -1,7 +1,14 @@
+apply plugin: "war"
+apply plugin: "jetty"
+
+jettyRun {
+ contextPath = ""
+}
+
dependencies {
// APIS
- compile "javax.servlet:servlet-api:2.5"
+ compile "javax.servlet:javax.servlet-api:3.0.1"
// JPA
compile "org.hibernate.javax.persistence:hibernate-jpa-2.0-api:1.0.1.Final"
@@ -12,7 +19,6 @@ dependencies {
// Spring
compile "org.springframework:spring-webmvc:$springVersion"
- runtime "cglib:cglib-nodep:2.2.2"
// Repository Exporter support
compile project(":spring-data-rest-repository")
diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/JacksonUtil.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/JacksonUtil.java
index b2077cfae..bbca8d00c 100644
--- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/JacksonUtil.java
+++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/JacksonUtil.java
@@ -1,7 +1,6 @@
package org.springframework.data.rest.webmvc;
import java.io.IOException;
-import java.nio.charset.Charset;
import java.util.Arrays;
import org.codehaus.jackson.JsonEncoding;
@@ -16,51 +15,50 @@ import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
/**
+ * Utility class for creating a custom-configured {@see MappingJacksonHttpMessageConverter} that has our own
+ * serializers and {@see MediaType} mappings on it.
+ *
* @author Jon Brisbin
*/
public abstract class JacksonUtil {
- public static final Charset DEFAULT_CHARSET = Charset.forName( "UTF-8" );
- public static final MediaType COMPACT_JSON = new MediaType( "application",
- "x-spring-data-compact+json",
- DEFAULT_CHARSET );
- public static final MediaType VERBOSE_JSON = new MediaType( "application",
- "x-spring-data-verbose+json",
- DEFAULT_CHARSET );
- public static final MediaType APPLICATION_JAVASCRIPT = new MediaType( "application",
- "javascript",
- DEFAULT_CHARSET );
-
private JacksonUtil() {
}
- public static MappingJacksonHttpMessageConverter createJacksonHttpMessageConverter( final ObjectMapper objectMapper ) {
+ public static MappingJacksonHttpMessageConverter createJacksonHttpMessageConverter(final ObjectMapper objectMapper) {
+ // We need a custom serializer for handling beans that don't conform to the JavaBeans standard 'get' and 'set'
CustomSerializerFactory customSerializerFactory = new CustomSerializerFactory();
- customSerializerFactory.addSpecificMapping( SimpleLink.class, new FluentBeanSerializer( SimpleLink.class ) );
- objectMapper.setSerializerFactory( customSerializerFactory );
+ customSerializerFactory.addSpecificMapping(SimpleLink.class, new FluentBeanSerializer(SimpleLink.class));
+ objectMapper.setSerializerFactory(customSerializerFactory);
+ // We want to support all our custom types of JSON and also the catch-all
MappingJacksonHttpMessageConverter jsonConverter = new MappingJacksonHttpMessageConverter() {
{
- setSupportedMediaTypes( Arrays.asList( MediaType.APPLICATION_JSON, COMPACT_JSON, VERBOSE_JSON ) );
+ setSupportedMediaTypes(Arrays.asList(
+ MediaType.APPLICATION_JSON,
+ MediaTypes.COMPACT_JSON,
+ MediaTypes.VERBOSE_JSON,
+ MediaType.ALL
+ ));
}
@Override
- protected void writeInternal( Object object, HttpOutputMessage outputMessage )
+ protected void writeInternal(Object object, HttpOutputMessage outputMessage)
throws IOException,
HttpMessageNotWritableException {
- JsonEncoding encoding = getJsonEncoding( outputMessage.getHeaders().getContentType() );
+ JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
// Believe it or not, this is the only way to get pretty-printing from Jackson in this configuration
JsonGenerator jsonGenerator = objectMapper
.getJsonFactory()
- .createJsonGenerator( outputMessage.getBody(), encoding )
+ .createJsonGenerator(outputMessage.getBody(), encoding)
.useDefaultPrettyPrinter();
try {
- objectMapper.writeValue( jsonGenerator, object );
- } catch ( IOException ex ) {
- throw new HttpMessageNotWritableException( "Could not write JSON: " + ex.getMessage(), ex );
+ objectMapper.writeValue(jsonGenerator, object);
+ } catch(IOException ex) {
+ throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
}
};
- jsonConverter.setObjectMapper( objectMapper );
+ jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/Links.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/Links.java
deleted file mode 100644
index 5b02e155d..000000000
--- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/Links.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.springframework.data.rest.webmvc;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.codehaus.jackson.annotate.JsonProperty;
-import org.codehaus.jackson.map.annotate.JsonDeserialize;
-import org.springframework.data.rest.core.Link;
-import org.springframework.data.rest.core.SimpleLink;
-
-/**
- * @author Jon Brisbin
- */
-public class Links {
-
- private List links = new ArrayList();
-
- public Links add(SimpleLink link) {
- links.add(link);
- return this;
- }
-
- @JsonProperty("_links")
- public List getLinks() {
- return this.links;
- }
-
-}
diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/MediaTypes.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/MediaTypes.java
new file mode 100644
index 000000000..f7bf284d7
--- /dev/null
+++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/MediaTypes.java
@@ -0,0 +1,33 @@
+package org.springframework.data.rest.webmvc;
+
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.List;
+
+import org.springframework.http.MediaType;
+
+/**
+ * @author Jon Brisbin
+ */
+public abstract class MediaTypes {
+
+ private MediaTypes() {
+ }
+
+ public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
+
+ public static final List ACCEPT_ALL_TYPES = Collections.singletonList(MediaType.ALL);
+ public static final MediaType COMPACT_JSON = new MediaType("application",
+ "x-spring-data-compact+json",
+ ISO_8859_1);
+ public static final MediaType VERBOSE_JSON = new MediaType("application",
+ "x-spring-data-verbose+json",
+ ISO_8859_1);
+ public static final MediaType APPLICATION_JAVASCRIPT = new MediaType("application",
+ "javascript",
+ ISO_8859_1);
+ public static final MediaType URI_LIST = new MediaType("text",
+ "uri-list",
+ ISO_8859_1);
+
+}
diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PagingAndSorting.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PagingAndSorting.java
index b69cc4117..224d66653 100644
--- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PagingAndSorting.java
+++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PagingAndSorting.java
@@ -10,12 +10,15 @@ import org.springframework.data.domain.Sort;
import org.springframework.web.util.UriComponentsBuilder;
/**
+ * Implementation of {@link Pageable} that is URL-aware.
+ *
* @author Jon Brisbin
*/
-public class PagingAndSorting implements Pageable {
+public class PagingAndSorting
+ implements Pageable {
private final RepositoryRestConfiguration config;
- private final PageRequest pageRequest;
+ private final PageRequest pageRequest;
public PagingAndSorting(RepositoryRestConfiguration config,
PageRequest pageRequest) {
@@ -23,17 +26,24 @@ public class PagingAndSorting implements Pageable {
this.pageRequest = pageRequest;
}
+ /**
+ * Add the current sort parameters to the URI.
+ *
+ * @param urib
+ *
+ * @return
+ */
public PagingAndSorting addSortParameters(UriComponentsBuilder urib) {
Sort sort = pageRequest.getSort();
- if (null != sort) {
+ if(null != sort) {
Iterator iter = sort.iterator();
- while (iter.hasNext()) {
+ while(iter.hasNext()) {
Sort.Order order = iter.next();
urib.queryParam(config.getSortParamName(), order.getProperty());
try {
urib.queryParam(URLEncoder.encode(order.getProperty() + ".dir", "ISO-8859-1"),
order.getDirection().toString().toLowerCase());
- } catch (UnsupportedEncodingException ignored) {
+ } catch(UnsupportedEncodingException ignored) {
// this should never happen
}
}
diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PagingAndSortingMethodArgumentResolver.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PagingAndSortingMethodArgumentResolver.java
index e1163cd8c..10a086a09 100644
--- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PagingAndSortingMethodArgumentResolver.java
+++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PagingAndSortingMethodArgumentResolver.java
@@ -5,11 +5,11 @@ import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
-import org.apache.commons.lang.ClassUtils;
import org.springframework.core.MethodParameter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefaults;
+import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
@@ -19,14 +19,15 @@ import org.springframework.web.method.support.ModelAndViewContainer;
/**
* @author Jon Brisbin
*/
-public class PagingAndSortingMethodArgumentResolver implements HandlerMethodArgumentResolver {
+public class PagingAndSortingMethodArgumentResolver
+ implements HandlerMethodArgumentResolver {
private static final int DEFAULT_PAGE = 1; // We're 1-based, not 0-based
private RepositoryRestConfiguration config = RepositoryRestConfiguration.DEFAULT;
public PagingAndSortingMethodArgumentResolver(RepositoryRestConfiguration config) {
- if (null != config) {
+ if(null != config) {
this.config = config;
}
}
@@ -40,47 +41,49 @@ public class PagingAndSortingMethodArgumentResolver implements HandlerMethodArgu
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
- HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
+ HttpServletRequest request = (HttpServletRequest)webRequest.getNativeRequest();
PageRequest pr = null;
- for (Annotation annotation : parameter.getParameterAnnotations()) {
- if (annotation instanceof PageableDefaults) {
- PageableDefaults defaults = (PageableDefaults) annotation;
+ for(Annotation annotation : parameter.getParameterAnnotations()) {
+ if(annotation instanceof PageableDefaults) {
+ PageableDefaults defaults = (PageableDefaults)annotation;
pr = new PageRequest(defaults.pageNumber(), defaults.value());
break;
}
}
- if (null == pr) {
+ if(null == pr) {
int page = DEFAULT_PAGE;
String sPage = request.getParameter(config.getPageParamName());
- if (StringUtils.hasText(sPage)) {
+ if(StringUtils.hasText(sPage)) {
try {
page = Integer.parseInt(sPage);
- } catch (NumberFormatException ignored) {}
+ } catch(NumberFormatException ignored) {
+ }
}
int limit = config.getDefaultPageSize();
String sLimit = request.getParameter(config.getLimitParamName());
- if (StringUtils.hasText(sLimit)) {
+ if(StringUtils.hasText(sLimit)) {
try {
limit = Integer.parseInt(sLimit);
- } catch (NumberFormatException ignored) {}
+ } catch(NumberFormatException ignored) {
+ }
}
Sort sort = null;
List orders = new ArrayList();
String[] orderValues = request.getParameterValues(config.getSortParamName());
- if (null != orderValues) {
- for (String orderParam : orderValues) {
+ if(null != orderValues) {
+ for(String orderParam : orderValues) {
String sortDir = request.getParameter(orderParam + ".dir");
Sort.Direction dir = (null != sortDir ? Sort.Direction.valueOf(sortDir.toUpperCase()) : Sort.Direction.ASC);
orders.add(new Sort.Order(dir, orderParam));
}
- if (!orders.isEmpty()) {
+ if(!orders.isEmpty()) {
sort = new Sort(orders);
}
}
- if (null != sort) {
+ if(null != sort) {
pr = new PageRequest(page - 1, limit, sort);
} else {
pr = new PageRequest(page - 1, limit);
diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestConfiguration.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestConfiguration.java
index b708fe713..ea7948ddc 100644
--- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestConfiguration.java
+++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestConfiguration.java
@@ -3,6 +3,7 @@ package org.springframework.data.rest.webmvc;
import java.util.Collections;
import java.util.List;
+import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
/**
@@ -12,19 +13,20 @@ public class RepositoryRestConfiguration {
public static final RepositoryRestConfiguration DEFAULT = new RepositoryRestConfiguration();
- private int defaultPageSize = 20;
- private String pageParamName = "page";
- private String limitParamName = "limit";
- private String sortParamName = "sort";
- private String jsonpParamName = "callback";
- private String jsonpOnErrParamName = null;
- private List> customConverters = Collections.emptyList();
+ private int defaultPageSize = 20;
+ private String pageParamName = "page";
+ private String limitParamName = "limit";
+ private String sortParamName = "sort";
+ private String jsonpParamName = "callback";
+ private String jsonpOnErrParamName = null;
+ private List> customConverters = Collections.emptyList();
+ private MediaType defaultMediaType = MediaType.APPLICATION_JSON;
public int getDefaultPageSize() {
return defaultPageSize;
}
- public RepositoryRestConfiguration setDefaultPageSize( int defaultPageSize ) {
+ public RepositoryRestConfiguration setDefaultPageSize(int defaultPageSize) {
this.defaultPageSize = defaultPageSize;
return this;
}
@@ -33,7 +35,7 @@ public class RepositoryRestConfiguration {
return pageParamName;
}
- public RepositoryRestConfiguration setPageParamName( String pageParamName ) {
+ public RepositoryRestConfiguration setPageParamName(String pageParamName) {
this.pageParamName = pageParamName;
return this;
}
@@ -42,7 +44,7 @@ public class RepositoryRestConfiguration {
return limitParamName;
}
- public RepositoryRestConfiguration setLimitParamName( String limitParamName ) {
+ public RepositoryRestConfiguration setLimitParamName(String limitParamName) {
this.limitParamName = limitParamName;
return this;
}
@@ -51,7 +53,7 @@ public class RepositoryRestConfiguration {
return sortParamName;
}
- public RepositoryRestConfiguration setSortParamName( String sortParamName ) {
+ public RepositoryRestConfiguration setSortParamName(String sortParamName) {
this.sortParamName = sortParamName;
return this;
}
@@ -60,7 +62,7 @@ public class RepositoryRestConfiguration {
return customConverters;
}
- public RepositoryRestConfiguration setCustomConverters( List> customConverters ) {
+ public RepositoryRestConfiguration setCustomConverters(List> customConverters) {
this.customConverters = customConverters;
return this;
}
@@ -69,7 +71,7 @@ public class RepositoryRestConfiguration {
return jsonpParamName;
}
- public RepositoryRestConfiguration setJsonpParamName( String jsonpParamName ) {
+ public RepositoryRestConfiguration setJsonpParamName(String jsonpParamName) {
this.jsonpParamName = jsonpParamName;
return this;
}
@@ -78,9 +80,18 @@ public class RepositoryRestConfiguration {
return jsonpOnErrParamName;
}
- public RepositoryRestConfiguration setJsonpOnErrParamName( String jsonpOnErrParamName ) {
+ public RepositoryRestConfiguration setJsonpOnErrParamName(String jsonpOnErrParamName) {
this.jsonpOnErrParamName = jsonpOnErrParamName;
return this;
}
+ public MediaType getDefaultMediaType() {
+ return defaultMediaType;
+ }
+
+ public RepositoryRestConfiguration setDefaultMediaType(MediaType defaultMediaType) {
+ this.defaultMediaType = defaultMediaType;
+ return this;
+ }
+
}
diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestController.java
index 11412f7e1..09b7ed8f7 100644
--- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestController.java
+++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestController.java
@@ -1,16 +1,13 @@
package org.springframework.data.rest.webmvc;
-import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.net.URI;
-import java.nio.charset.Charset;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -23,7 +20,6 @@ import java.util.SortedSet;
import java.util.Stack;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicReference;
-import javax.servlet.http.HttpServletRequest;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
@@ -44,6 +40,7 @@ import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.Repository;
import org.springframework.data.rest.core.Handler;
import org.springframework.data.rest.core.Link;
+import org.springframework.data.rest.core.Links;
import org.springframework.data.rest.core.SimpleLink;
import org.springframework.data.rest.core.convert.DelegatingConversionService;
import org.springframework.data.rest.core.util.UriUtils;
@@ -54,7 +51,6 @@ import org.springframework.data.rest.repository.RepositoryExporter;
import org.springframework.data.rest.repository.RepositoryExporterSupport;
import org.springframework.data.rest.repository.RepositoryMetadata;
import org.springframework.data.rest.repository.RepositoryNotFoundException;
-import org.springframework.data.rest.repository.RepositoryQueryMethod;
import org.springframework.data.rest.repository.annotation.RestResource;
import org.springframework.data.rest.repository.context.AfterDeleteEvent;
import org.springframework.data.rest.repository.context.AfterLinkSaveEvent;
@@ -62,6 +58,9 @@ import org.springframework.data.rest.repository.context.AfterSaveEvent;
import org.springframework.data.rest.repository.context.BeforeDeleteEvent;
import org.springframework.data.rest.repository.context.BeforeLinkSaveEvent;
import org.springframework.data.rest.repository.context.BeforeSaveEvent;
+import org.springframework.data.rest.repository.context.RepositoryEvent;
+import org.springframework.data.rest.repository.invoke.RepositoryMethodResponse;
+import org.springframework.data.rest.repository.invoke.RepositoryQueryMethod;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
@@ -74,6 +73,7 @@ import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.util.Assert;
@@ -88,7 +88,34 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.util.UriComponentsBuilder;
/**
- * @author Jon Brisbin
+ * Exports Spring Data Repositories over the web in a RESTful
+ * manner that is HATEOAS friendly.
+ *
+ * This controller can be deployed in it's own DispatcherServlet. In that case, use the {@link
+ * RepositoryRestExporterServlet} in your web.xml. For example, to send all requests through the REST exporter, add the
+ * following to your web.xml:
+ *
+ * <servlet>
+ * <servlet-name>exporter</servlet-name>
+ * <servlet-class>org.springframework.data.rest.webmvc.RepositoryRestExporterServlet</servlet-class>
+ * <load-on-startup>1</load-on-startup>
+ * </servlet>
+ *
+ * <servlet-mapping>
+ * <servlet-name>exporter</servlet-name>
+ * <url-pattern>/*</url-pattern>
+ * </servlet-mapping>
+ *
+ *
+ * One can also deploy this controller into an existing Spring MVC application. In general, one should be able to
+ * simply create an instance of the {@link RepositoryRestMvcConfiguration} bean in your ApplicationContext
+ * or in JavaConfig.
+ *
+ * If you wish to alter the way the REST exporter functions, you don't configure the controller directly. Instead there
+ * is a {@link RepositoryRestConfiguration} helper class that you create in your ApplicationContext. If a feature is
+ * configurable in Spring Data REST, there is a property on this helper to configure it.
+ *
+ * @author Jon Brisbin
*/
public class RepositoryRestController
extends RepositoryExporterSupport
@@ -96,217 +123,255 @@ public class RepositoryRestController
InitializingBean {
public static final String LOCATION = "Location";
- public static final String SELF = "self";
- public static final String LINKS = "_links";
- public static final Charset DEFAULT_CHARSET = Charset.forName( "UTF-8" );
+ public static final String SELF = "self";
+ public static final String LINKS = "_links";
- private static final Logger LOG = LoggerFactory.getLogger( RepositoryRestController.class );
- private static final HttpHeaders EMPTY_HEADERS = new HttpHeaders();
- private static final List ALL_TYPES = Arrays.asList( MediaType.ALL );
- private static final MediaType URI_LIST = new MediaType( "text",
- "uri-list",
- UriListHttpMessageConverter.DEFAULT_CHARSET );
+ private static final Logger LOG = LoggerFactory.getLogger(RepositoryRestController.class);
private ApplicationContext applicationContext;
+ /**
+ * We manage a list of possible {@link ConversionService}s to handle converting objects in the controller. This list
+ * is prioritized as well, so one can add a ConversionService at index 0 to make sure that ConversionService takes
+ * priority whenever an object of the type it can convert is needing conversion.
+ */
@Autowired(required = false)
- private DelegatingConversionService conversionService = new DelegatingConversionService(
+ private DelegatingConversionService conversionService = new DelegatingConversionService(
new DefaultFormattingConversionService()
);
+ /**
+ * Converters for reading and writing representations of objects.
+ */
@Autowired(required = false)
- private List httpMessageConverters = new ArrayList();
- private SortedSet availableMediaTypes = new TreeSet();
+ private List httpMessageConverters = new ArrayList();
+ /**
+ * List of {@link MediaType}s we can support, given the list of {@link HttpMessageConverter}s currently configured.
+ */
+ private SortedSet availableMediaTypes = new TreeSet();
@Autowired(required = false)
- private RepositoryRestConfiguration config = RepositoryRestConfiguration.DEFAULT;
- private Map> resourceHandlers = Collections.emptyMap();
- private ObjectMapper objectMapper = new ObjectMapper();
+ private RepositoryRestConfiguration config = RepositoryRestConfiguration.DEFAULT;
+ private ObjectMapper objectMapper = new ObjectMapper();
{
List httpMessageConverters = new ArrayList();
- httpMessageConverters.add( 0, new StringHttpMessageConverter() );
- httpMessageConverters.add( 0, new ByteArrayHttpMessageConverter() );
- httpMessageConverters.add( 0, new FormHttpMessageConverter() );
- httpMessageConverters.add( 0, new UriListHttpMessageConverter() );
- httpMessageConverters.add( 0, JacksonUtil.createJacksonHttpMessageConverter( objectMapper ) );
+ httpMessageConverters.add(0, new StringHttpMessageConverter());
+ httpMessageConverters.add(0, new ByteArrayHttpMessageConverter());
+ httpMessageConverters.add(0, new FormHttpMessageConverter());
+ httpMessageConverters.add(0, JacksonUtil.createJacksonHttpMessageConverter(objectMapper));
+ httpMessageConverters.add(0, new UriListHttpMessageConverter());
- setHttpMessageConverters( httpMessageConverters );
+ setHttpMessageConverters(httpMessageConverters);
}
- @Override public void setApplicationContext( ApplicationContext applicationContext ) throws BeansException {
+ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
+ /**
+ * Get the {@link ConversionService} in use by the controller.
+ *
+ * @return The internal {@link ConversionService}.
+ */
public ConversionService getConversionService() {
return conversionService;
}
- public void setConversionService( ConversionService conversionService ) {
- if ( null != conversionService ) {
- this.conversionService.addConversionServices( conversionService );
+ /**
+ * Add this {@link ConversionService} to the list of those being delegated to by the internal {@link
+ * DelegatingConversionService}. Although this method does an 'add', it is called 'set' to make it JavaBean-friendly.
+ *
+ * @param conversionService
+ */
+ public void setConversionService(ConversionService conversionService) {
+ if(null != conversionService) {
+ this.conversionService.addConversionServices(conversionService);
}
}
+ /**
+ * @return The internal {@link ConversionService}.
+ *
+ * @see org.springframework.data.rest.webmvc.RepositoryRestController#getConversionService()
+ */
public ConversionService conversionService() {
return conversionService;
}
- public RepositoryRestController conversionService( ConversionService conversionService ) {
- setConversionService( conversionService );
+ /**
+ * @param conversionService
+ *
+ * @return @this
+ *
+ * @see RepositoryRestController#setConversionService(org.springframework.core.convert.ConversionService)
+ */
+ public RepositoryRestController conversionService(ConversionService conversionService) {
+ setConversionService(conversionService);
return this;
}
+ /**
+ * Get the list of default {@link HttpMessageConverter}s.
+ *
+ * @return Default converters.
+ */
public List getHttpMessageConverters() {
return httpMessageConverters;
}
+ /**
+ * Set the list of available {@link HttpMessageConverter}s, clobbering the defaults. This does not, however, affect
+ * those user-defined converters that come from the {@link RepositoryRestConfiguration}.
+ *
+ * @param httpMessageConverters
+ */
@SuppressWarnings({"unchecked"})
- public void setHttpMessageConverters( List httpMessageConverters ) {
- Assert.notNull( httpMessageConverters );
+ public void setHttpMessageConverters(List httpMessageConverters) {
+ Assert.notNull(httpMessageConverters);
this.httpMessageConverters = httpMessageConverters;
this.availableMediaTypes.clear();
- for ( HttpMessageConverter conv : httpMessageConverters ) {
- availableMediaTypes.addAll( conv.getSupportedMediaTypes() );
+ for(HttpMessageConverter conv : httpMessageConverters) {
+ availableMediaTypes.addAll(conv.getSupportedMediaTypes());
}
- for ( HttpMessageConverter conv : config.getCustomConverters() ) {
- availableMediaTypes.addAll( conv.getSupportedMediaTypes() );
+ for(HttpMessageConverter conv : config.getCustomConverters()) {
+ availableMediaTypes.addAll(conv.getSupportedMediaTypes());
}
}
+ /**
+ * @return @this
+ *
+ * @see org.springframework.data.rest.webmvc.RepositoryRestController#getHttpMessageConverters()
+ */
public List httpMessageConverters() {
return httpMessageConverters;
}
- public RepositoryRestController httpMessageConverters( List httpMessageConverters ) {
- setHttpMessageConverters( httpMessageConverters );
+ /**
+ * @param httpMessageConverters
+ *
+ * @return @this
+ *
+ * @see RepositoryRestController#setHttpMessageConverters(java.util.List)
+ */
+ public RepositoryRestController httpMessageConverters(List httpMessageConverters) {
+ setHttpMessageConverters(httpMessageConverters);
return this;
}
+ /**
+ * Get the configuration currently in use.
+ *
+ * @return Either the user-defined configuration or a default.
+ */
public RepositoryRestConfiguration getRepositoryRestConfig() {
return config;
}
- public RepositoryRestController setRepositoryRestConfig( RepositoryRestConfiguration config ) {
+ /**
+ * Set the configuration this controller will use to inflence its behavior.
+ *
+ * @param config
+ *
+ * @return @this
+ */
+ public RepositoryRestController setRepositoryRestConfig(RepositoryRestConfiguration config) {
this.config = config;
return this;
}
- public Map> getResourceHandlers() {
- return resourceHandlers;
- }
-
- public RepositoryRestController setResourceHandlers( Map> resourceHandlers ) {
- this.resourceHandlers = resourceHandlers;
- return this;
- }
-
- public Map> resourceHandlers() {
- return resourceHandlers;
- }
-
- public RepositoryRestController resourceHandlers( Map> resourceHandlers ) {
- setResourceHandlers( resourceHandlers );
- return this;
- }
-
@SuppressWarnings({"unchecked"})
@Override public void afterPropertiesSet() throws Exception {
- for ( ConversionService convsvc : BeanFactoryUtils.beansOfTypeIncludingAncestors( applicationContext,
- ConversionService.class )
- .values() ) {
- conversionService.addConversionServices( convsvc );
+ for(ConversionService convsvc : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext,
+ ConversionService.class)
+ .values()) {
+ conversionService.addConversionServices(convsvc);
}
}
+ /**
+ * List available {@link CrudRepository}s that are being exported.
+ *
+ * @param request
+ * @param uriBuilder
+ *
+ * @return
+ *
+ * @throws IOException
+ */
@SuppressWarnings({"unchecked"})
@RequestMapping(
value = "/",
method = RequestMethod.GET
)
@ResponseBody
- public ResponseEntity> listRepositories( ServletServerHttpRequest request,
- UriComponentsBuilder uriBuilder )
- throws IOException {
+ public ResponseEntity> listRepositories(ServletServerHttpRequest request,
+ UriComponentsBuilder uriBuilder) throws IOException {
URI baseUri = uriBuilder.build().toUri();
Links links = new Links();
- for ( RepositoryExporter repoExporter : repositoryExporters ) {
- for ( String name : (Set) repoExporter.repositoryNames() ) {
- RepositoryMetadata repoMeta = repoExporter.repositoryMetadataFor( name );
+ for(RepositoryExporter repoExporter : repositoryExporters) {
+ for(String name : (Set)repoExporter.repositoryNames()) {
+ RepositoryMetadata repoMeta = repoExporter.repositoryMetadataFor(name);
String rel = repoMeta.rel();
- URI path = buildUri( baseUri, name );
- links.add( new SimpleLink( rel, path ) );
+ URI path = buildUri(baseUri, name);
+ links.add(new SimpleLink(rel, path));
}
}
- return negotiateResponse( request, HttpStatus.OK, EMPTY_HEADERS, links );
+ return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), links);
}
+ /**
+ * List entities of a {@link CrudRepository} by invoking
+ * {@link org.springframework.data.repository.CrudRepository#findAll()}
+ * and applying any available paging parameters.
+ *
+ * @param request
+ * @param pageSort
+ * @param uriBuilder
+ * @param repository
+ *
+ * @return
+ *
+ * @throws IOException
+ */
@SuppressWarnings({"unchecked"})
@RequestMapping(
value = "/{repository}",
method = RequestMethod.GET
)
@ResponseBody
- public ResponseEntity> listEntities( ServletServerHttpRequest request,
- PagingAndSorting pageSort,
- UriComponentsBuilder uriBuilder,
- @PathVariable String repository )
- throws IOException {
+ public ResponseEntity> listEntities(ServletServerHttpRequest request,
+ PagingAndSorting pageSort,
+ UriComponentsBuilder uriBuilder,
+ @PathVariable String repository) throws IOException {
URI baseUri = uriBuilder.build().toUri();
- RepositoryMetadata repoMeta = repositoryMetadataFor( repository );
+ RepositoryMetadata repoMeta = repositoryMetadataFor(repository);
+ RepositoryMethodResponse response = new RepositoryMethodResponse();
- Page page = null;
- Iterator iter;
- if ( repoMeta.repository() instanceof PagingAndSortingRepository ) {
- page = ((PagingAndSortingRepository) repoMeta.repository()).findAll( pageSort );
- iter = page.iterator();
- } else {
- iter = repoMeta.repository().findAll().iterator();
- }
-
- Map resultMap = new HashMap();
- Links links = new Links();
- resultMap.put( LINKS, links.getLinks() );
- List resultList = new ArrayList();
- resultMap.put( "results", resultList );
-
- boolean returnLinks = shouldReturnLinks( request.getServletRequest().getHeader( "Accept" ) );
- if ( null != iter ) {
- while ( iter.hasNext() ) {
- Object o = iter.next();
- Serializable id = (Serializable) repoMeta.entityMetadata().idAttribute().get( o );
- if ( returnLinks ) {
- links.add( new SimpleLink( repoMeta.rel() + "." + o.getClass().getSimpleName(),
- buildUri( baseUri, repository, id.toString() ) ) );
- } else {
- Map entityDto = extractPropertiesLinkAware( repoMeta.rel(),
- o,
- repoMeta.entityMetadata(),
- buildUri( baseUri, repository, id.toString() ) );
- addSelfLink( baseUri, entityDto, repository, id.toString() );
- resultList.add( entityDto );
- }
+ Iterator allEntities = Collections.emptyList().iterator();
+ if(repoMeta.repository() instanceof PagingAndSortingRepository) {
+ Page page = ((PagingAndSortingRepository)repoMeta.repository()).findAll(pageSort);
+ if(page.hasContent()) {
+ allEntities = page.iterator();
}
- links.add( new SimpleLink( repoMeta.rel() + ".search",
- buildUri( baseUri, repository, "search" ) ) );
- }
- // Add paging links
- if ( null != page ) {
- resultMap.put( "totalCount", page.getTotalElements() );
- resultMap.put( "totalPages", page.getTotalPages() );
- resultMap.put( "currentPage", page.getNumber() + 1 );
+ // Set page counts in the response
+ response.setTotalCount(page.getTotalElements());
+ response.setTotalPages(page.getTotalPages());
+ response.setCurrentPage(page.getNumber() + 1);
+
// Copy over parameters
- UriComponentsBuilder urib = UriComponentsBuilder.fromUri( baseUri ).pathSegment( repository );
- for ( String name : ((Map) request.getServletRequest().getParameterMap()).keySet() ) {
- if ( !config.getPageParamName().equals( name ) && !config.getLimitParamName().equals( name )
- && !config.getSortParamName().equals( name ) ) {
- urib.queryParam( name, request.getServletRequest().getParameter( name ) );
+ UriComponentsBuilder selfUri = UriComponentsBuilder.fromUri(baseUri).pathSegment(repository);
+ for(String name : request.getServletRequest().getParameterMap().keySet()) {
+ if(notPagingParam(name)) {
+ selfUri.queryParam(name, request.getServletRequest().getParameter(name));
}
}
- URI nextPrevBase = urib.build().toUri();
+ // Add next/prev links as necessary
+ URI nextPrevBase = selfUri.build().toUri();
maybeAddPrevNextLink(
nextPrevBase,
repoMeta,
@@ -315,7 +380,7 @@ public class RepositoryRestController
!page.isFirstPage() && page.hasPreviousPage(),
page.getNumber(),
"prev",
- links
+ response.getLinks()
);
maybeAddPrevNextLink(
nextPrevBase,
@@ -325,157 +390,204 @@ public class RepositoryRestController
!page.isLastPage() && page.hasNextPage(),
page.getNumber() + 2,
"next",
- links
+ response.getLinks()
);
+
+ } else {
+ Iterable it = repoMeta.repository().findAll();
+ if(null != it) {
+ allEntities = it.iterator();
+ }
}
- return negotiateResponse( request, HttpStatus.OK, EMPTY_HEADERS, resultMap );
+ while(allEntities.hasNext()) {
+ Object o = allEntities.next();
+ Serializable id = (Serializable)repoMeta.entityMetadata().idAttribute().get(o);
+ if(shouldReturnLinks(request.getServletRequest().getHeader("Accept"))) {
+ response.addLink(new SimpleLink(repoMeta.rel() + "." + o.getClass().getSimpleName(),
+ buildUri(baseUri, repository, id.toString())));
+ } else {
+ Map entityDto = extractPropertiesLinkAware(repoMeta.rel(),
+ o,
+ repoMeta.entityMetadata(),
+ buildUri(baseUri, repository, id.toString()));
+ addSelfLink(baseUri, entityDto, repository, id.toString());
+ response.addResult(entityDto);
+ }
+ }
+ response.addLink(new SimpleLink(repoMeta.rel() + ".search",
+ buildUri(baseUri, repository, "search")));
+
+ return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), response);
}
+ /**
+ * List the URIs of query methods found on this repository interface.
+ *
+ * @param request
+ * @param uriBuilder
+ * @param repository
+ *
+ * @return
+ *
+ * @throws IOException
+ */
@SuppressWarnings({"unchecked"})
@RequestMapping(
value = "/{repository}/search",
method = RequestMethod.GET
)
@ResponseBody
- public ResponseEntity> listQueryMethods( ServletServerHttpRequest request,
- UriComponentsBuilder uriBuilder,
- @PathVariable String repository )
- throws IOException {
+ public ResponseEntity> listQueryMethods(ServletServerHttpRequest request,
+ UriComponentsBuilder uriBuilder,
+ @PathVariable String repository) throws IOException {
URI baseUri = uriBuilder.build().toUri();
- RepositoryMetadata repoMeta = repositoryMetadataFor( repository );
+ RepositoryMetadata repoMeta = repositoryMetadataFor(repository);
Links links = new Links();
- for ( Map.Entry entry : ((Map) repoMeta.queryMethods())
- .entrySet() ) {
- String rel = repoMeta.rel() + "." + entry.getKey();
- URI path = buildUri( baseUri, repository, "search", entry.getKey() );
- RestResource resourceAnno = entry.getValue().method().getAnnotation( RestResource.class );
- if ( null != resourceAnno ) {
- if ( StringUtils.hasText( resourceAnno.path() ) ) {
- path = buildUri( baseUri, repository, "search", resourceAnno.path() );
- }
- if ( StringUtils.hasText( resourceAnno.rel() ) ) {
- rel = repoMeta.rel() + "." + resourceAnno.rel();
- }
+ for(Map.Entry entry : ((Map)repoMeta.queryMethods())
+ .entrySet()) {
+ URI baseSearchUri = buildUri(baseUri, repository, "search");
+
+ // Check for customized rel and path
+ Method m = entry.getValue().method();
+ if(m.isAnnotationPresent(RestResource.class)) {
+ RestResource resourceAnno = m.getAnnotation(RestResource.class);
+ links.add(new SimpleLink(
+ (StringUtils.hasText(resourceAnno.rel())
+ ? repoMeta.rel() + "." + resourceAnno.rel()
+ : repoMeta.rel() + "." + entry.getKey()),
+ buildUri(baseSearchUri,
+ (StringUtils.hasText(resourceAnno.path())
+ ? resourceAnno.path()
+ : entry.getKey()))
+ ));
+ } else {
+ // No customizations, use the default
+ links.add(new SimpleLink(repoMeta.rel() + "." + entry.getKey(),
+ buildUri(baseSearchUri, entry.getKey())));
}
- links.add( new SimpleLink( rel, path ) );
}
- return negotiateResponse( request, HttpStatus.OK, EMPTY_HEADERS, links );
+ return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), links);
}
+ /**
+ * Invoke a custom query method on a repository and page the results based on URL parameters supplied by the user or
+ * the default page size.
+ *
+ * @param request
+ * @param pageSort
+ * @param uriBuilder
+ * @param repository
+ * @param query
+ *
+ * @return
+ *
+ * @throws InvocationTargetException
+ * @throws IllegalAccessException
+ * @throws IOException
+ */
@SuppressWarnings({"unchecked"})
@RequestMapping(
value = "/{repository}/search/{query}",
method = RequestMethod.GET
)
@ResponseBody
- public ResponseEntity> query( ServletServerHttpRequest request,
- PagingAndSorting pageSort,
- UriComponentsBuilder uriBuilder,
- @PathVariable String repository,
- @PathVariable String query )
- throws InvocationTargetException,
- IllegalAccessException,
- IOException {
+ public ResponseEntity> query(ServletServerHttpRequest request,
+ PagingAndSorting pageSort,
+ UriComponentsBuilder uriBuilder,
+ @PathVariable String repository,
+ @PathVariable String query) throws InvocationTargetException,
+ IllegalAccessException,
+ IOException {
URI baseUri = uriBuilder.build().toUri();
- Page page = null;
- RepositoryMetadata repoMeta = repositoryMetadataFor( repository );
+ RepositoryMetadata repoMeta = repositoryMetadataFor(repository);
Repository repo = repoMeta.repository();
- RepositoryQueryMethod queryMethod = repoMeta.queryMethod( query );
- if ( null == queryMethod ) {
- return negotiateResponse( request, HttpStatus.NOT_FOUND, EMPTY_HEADERS, null );
+ RepositoryQueryMethod queryMethod = repoMeta.queryMethod(query);
+ if(null == queryMethod) {
+ return notFoundResponse(request);
}
Class>[] paramTypes = queryMethod.paramTypes();
String[] paramNames = queryMethod.paramNames();
Object[] paramVals = new Object[paramTypes.length];
- for ( int i = 0; i < paramVals.length; i++ ) {
- String queryVal = request.getServletRequest().getParameter( paramNames[i] );
- if ( String.class.isAssignableFrom( paramTypes[i] ) ) {
+ for(int i = 0; i < paramVals.length; i++) {
+ String queryVal = request.getServletRequest().getParameter(paramNames[i]);
+ if(null == queryVal) {
+ continue;
+ }
+
+ RepositoryMetadata paramRepoMeta;
+ if(String.class.isAssignableFrom(paramTypes[i])) {
// Param type is a String
paramVals[i] = queryVal;
- } else if ( Pageable.class.isAssignableFrom( paramTypes[i] ) ) {
+ } else if(Pageable.class.isAssignableFrom(paramTypes[i])) {
// Handle paging
paramVals[i] = pageSort;
- } else if ( Sort.class.isAssignableFrom( paramTypes[i] ) ) {
+ } else if(Sort.class.isAssignableFrom(paramTypes[i])) {
// Handle sorting
paramVals[i] = (null != pageSort ? pageSort.getSort() : null);
- } else if ( conversionService.canConvert( String.class, paramTypes[i] ) ) {
+ } else if(null != (paramRepoMeta = repositoryMetadataFor(paramTypes[i]))) {
+ // Complex parameter is a managed type
+ Serializable id = stringToSerializable(queryVal,
+ (Class)paramRepoMeta.entityMetadata()
+ .idAttribute()
+ .type());
+ Object o = paramRepoMeta.repository().findOne(id);
+ if(null == o) {
+ return notFoundResponse(request);
+ }
+
+ paramVals[i] = o;
+ } else if(conversionService.canConvert(String.class, paramTypes[i])) {
// There's a converter from String -> param type
- paramVals[i] = conversionService.convert( queryVal, paramTypes[i] );
+ paramVals[i] = conversionService.convert(queryVal, paramTypes[i]);
} else {
// Param type isn't a "simple" type or no converter exists, try JSON
try {
- paramVals[i] = objectMapper.readValue( queryVal, paramTypes[i] );
- } catch ( IOException e ) {
- throw new IllegalArgumentException( e );
+ paramVals[i] = objectMapper.readValue(queryVal, paramTypes[i]);
+ } catch(IOException e) {
+ throw new IllegalArgumentException(e);
}
}
}
- Object result = queryMethod.method().invoke( repo, paramVals );
- Iterator iter;
- if ( null != result ) {
- if ( result instanceof Collection ) {
- iter = ((Collection) result).iterator();
- } else if ( result instanceof Page ) {
- page = (Page) result;
- iter = page.iterator();
- } else {
- List l = new ArrayList();
- l.add( result );
- iter = l.iterator();
- }
- } else {
- iter = Collections.emptyList().iterator();
+ RepositoryMethodResponse response = new RepositoryMethodResponse();
+
+ Object result;
+ if(null == (result = queryMethod.method().invoke(repo, paramVals))) {
+ return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), response);
}
- Map resultMap = new HashMap();
- Links links = new Links();
- resultMap.put( LINKS, links.getLinks() );
- List resultList = new ArrayList();
- resultMap.put( "results", resultList );
+ Iterator entities = Collections.emptyList().iterator();
+ if(result instanceof Collection) {
+ entities = ((Collection)result).iterator();
+ response.setTotalCount(((Collection)result).size());
+ } else if(result instanceof Page) {
+ Page page = (Page)result;
- boolean returnLinks = shouldReturnLinks( request.getServletRequest().getHeader( "Accept" ) );
- while ( iter.hasNext() ) {
- Object obj = iter.next();
- RepositoryMetadata elemRepoMeta = repositoryMetadataFor( obj.getClass() );
- if ( null != elemRepoMeta ) {
- String id = elemRepoMeta.entityMetadata().idAttribute().get( obj ).toString();
- if ( returnLinks ) {
- String rel = elemRepoMeta.rel() + "." + elemRepoMeta.entityMetadata().type().getSimpleName();
- URI path = buildUri( baseUri, repository, id );
- links.add( new SimpleLink( rel, path ) );
- } else {
- Map entityDto = extractPropertiesLinkAware( repoMeta.rel(),
- obj,
- repoMeta.entityMetadata(),
- buildUri( baseUri, repository, id ) );
- addSelfLink( baseUri, entityDto, repository, id );
- resultList.add( entityDto );
- }
+ if(page.hasContent()) {
+ entities = page.iterator();
}
- }
- // Add paging links
- if ( null != page ) {
- resultMap.put( "totalCount", page.getTotalElements() );
- resultMap.put( "totalPages", page.getTotalPages() );
- resultMap.put( "currentPage", page.getNumber() + 1 );
- // Copy over search parameters
- UriComponentsBuilder urib = UriComponentsBuilder.fromUri( baseUri ).pathSegment( repository, "search", query );
- for ( String name : ((Map) request.getServletRequest().getParameterMap()).keySet() ) {
- if ( !config.getPageParamName().equals( name ) && !config.getLimitParamName().equals( name )
- && !config.getSortParamName().equals( name ) ) {
- urib.queryParam( name, request.getServletRequest().getParameter( name ) );
+ // Set page counts in the response
+ response.setTotalCount(page.getTotalElements());
+ response.setTotalPages(page.getTotalPages());
+ response.setCurrentPage(page.getNumber() + 1);
+
+ // Copy over parameters
+ UriComponentsBuilder selfUri = UriComponentsBuilder.fromUri(baseUri).pathSegment(repository, "search", query);
+ for(String name : request.getServletRequest().getParameterMap().keySet()) {
+ if(notPagingParam(name)) {
+ selfUri.queryParam(name, request.getServletRequest().getParameter(name));
}
}
- URI nextPrevBase = urib.build().toUri();
+ // Add next/prev links as necessary
+ URI nextPrevBase = selfUri.build().toUri();
maybeAddPrevNextLink(
nextPrevBase,
repoMeta,
@@ -484,7 +596,7 @@ public class RepositoryRestController
!page.isFirstPage() && page.hasPreviousPage(),
page.getNumber(),
"prev",
- links
+ response.getLinks()
);
maybeAddPrevNextLink(
nextPrevBase,
@@ -494,110 +606,172 @@ public class RepositoryRestController
!page.isLastPage() && page.hasNextPage(),
page.getNumber() + 2,
"next",
- links
+ response.getLinks()
);
+
} else {
- resultMap.put( "totalCount", resultList.size() );
+ entities = Collections.singletonList(result).iterator();
}
- return negotiateResponse( request, HttpStatus.OK, EMPTY_HEADERS, resultMap );
+ while(entities.hasNext()) {
+ Object obj = entities.next();
+
+ RepositoryMetadata elemRepoMeta;
+ if(null == (elemRepoMeta = repositoryMetadataFor(obj.getClass()))) {
+ response.addResult(obj);
+ continue;
+ }
+
+ // This object is managed by a repository
+ String id = elemRepoMeta.entityMetadata().idAttribute().get(obj).toString();
+ if(shouldReturnLinks(request.getServletRequest().getHeader("Accept"))) {
+ String rel = elemRepoMeta.rel() + "." + elemRepoMeta.entityMetadata().type().getSimpleName();
+ URI path = buildUri(baseUri, repository, id);
+ response.addLink(new SimpleLink(rel, path));
+ } else {
+ Map entityDto = extractPropertiesLinkAware(repoMeta.rel(),
+ obj,
+ repoMeta.entityMetadata(),
+ buildUri(baseUri, repository, id));
+ addSelfLink(baseUri, entityDto, repository, id);
+ response.addResult(entityDto);
+ }
+ }
+
+ return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), response);
}
+ /**
+ * Create a new entity by reading the incoming data and calling {@link CrudRepository#save(Object)} and letting the
+ * ID be auto-generated.
+ *
+ * To get the entity back in the body of the response, simpy add the URL parameter returnBody=true .
+ *
+ * @param request
+ * @param uriBuilder
+ * @param repository
+ *
+ * @return
+ *
+ * @throws IOException
+ */
@SuppressWarnings({"unchecked"})
@RequestMapping(
value = "/{repository}",
method = RequestMethod.POST
)
@ResponseBody
- public ResponseEntity> create( ServletServerHttpRequest request,
- HttpServletRequest servletRequest,
- UriComponentsBuilder uriBuilder,
- @PathVariable String repository )
- throws IOException {
+ public ResponseEntity> create(ServletServerHttpRequest request,
+ UriComponentsBuilder uriBuilder,
+ @PathVariable String repository) throws IOException {
URI baseUri = uriBuilder.build().toUri();
- RepositoryMetadata repoMeta = repositoryMetadataFor( repository );
+ RepositoryMetadata repoMeta = repositoryMetadataFor(repository);
CrudRepository repo = repoMeta.repository();
+
MediaType incomingMediaType = request.getHeaders().getContentType();
- final Object incoming = readIncoming( request, incomingMediaType, repoMeta.entityMetadata().type() );
- if ( null == incoming ) {
- return negotiateResponse( request, HttpStatus.BAD_REQUEST, EMPTY_HEADERS, null );
- } else {
- if ( null != applicationContext ) {
- applicationContext.publishEvent( new BeforeSaveEvent( incoming ) );
- }
- Object savedEntity = repo.save( incoming );
- if ( null != applicationContext ) {
- applicationContext.publishEvent( new AfterSaveEvent( savedEntity ) );
- }
- String sId = repoMeta.entityMetadata().idAttribute().get( savedEntity ).toString();
-
- URI selfUri = buildUri( baseUri, repository, sId );
-
- HttpHeaders headers = new HttpHeaders();
- headers.set( LOCATION, selfUri.toString() );
-
- Object body = null;
- if ( null != servletRequest.getParameter( "returnBody" ) && "true".equals( servletRequest.getParameter(
- "returnBody" ) ) ) {
- Map entityDto = extractPropertiesLinkAware( repoMeta.rel(),
- savedEntity,
- repoMeta.entityMetadata(),
- buildUri( baseUri, repository, sId ) );
- addSelfLink( baseUri, entityDto, repository, sId );
- body = entityDto;
- }
- return negotiateResponse( request, HttpStatus.CREATED, headers, body );
+ Object incoming = readIncoming(request, incomingMediaType, repoMeta.entityMetadata().type());
+ if(null == incoming) {
+ throw new HttpMessageNotReadableException("Could not create an instance of " + repoMeta.entityMetadata()
+ .type()
+ .getSimpleName() + " from input.");
}
+
+ publishEvent(new BeforeSaveEvent(incoming));
+ Object savedEntity = repo.save(incoming);
+ publishEvent(new AfterSaveEvent(savedEntity));
+
+ String sId = repoMeta.entityMetadata().idAttribute().get(savedEntity).toString();
+ URI selfUri = buildUri(baseUri, repository, sId);
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.set(LOCATION, selfUri.toString());
+
+ Object body = null;
+ if(null != request.getServletRequest().getParameter("returnBody")
+ && "true".equals(request.getServletRequest().getParameter("returnBody"))) {
+ Map entityDto = extractPropertiesLinkAware(repoMeta.rel(),
+ savedEntity,
+ repoMeta.entityMetadata(),
+ buildUri(baseUri, repository, sId));
+ addSelfLink(baseUri, entityDto, repository, sId);
+ body = entityDto;
+ }
+
+ return negotiateResponse(request, HttpStatus.CREATED, headers, body);
}
+ /**
+ * Retrieve a specific entity.
+ *
+ * @param request
+ * @param uriBuilder
+ * @param repository
+ * @param id
+ *
+ * @return
+ *
+ * @throws IOException
+ */
@SuppressWarnings({"unchecked"})
@RequestMapping(
value = "/{repository}/{id}",
method = RequestMethod.GET
)
@ResponseBody
- public ResponseEntity> entity( ServletServerHttpRequest request,
- UriComponentsBuilder uriBuilder,
- @PathVariable String repository,
- @PathVariable String id )
- throws IOException {
+ public ResponseEntity> entity(ServletServerHttpRequest request,
+ UriComponentsBuilder uriBuilder,
+ @PathVariable String repository,
+ @PathVariable String id) throws IOException {
URI baseUri = uriBuilder.build().toUri();
- RepositoryMetadata repoMeta = repositoryMetadataFor( repository );
- Serializable serId = stringToSerializable( id,
- (Class extends Serializable>) repoMeta.entityMetadata()
- .idAttribute()
- .type() );
+ RepositoryMetadata repoMeta = repositoryMetadataFor(repository);
+ Serializable serId = stringToSerializable(id,
+ (Class extends Serializable>)repoMeta.entityMetadata()
+ .idAttribute()
+ .type());
CrudRepository repo = repoMeta.repository();
- Object entity = repo.findOne( serId );
- if ( null == entity ) {
- return negotiateResponse( request, HttpStatus.NOT_FOUND, EMPTY_HEADERS, null );
- } else {
- HttpHeaders headers = new HttpHeaders();
- if ( null != repoMeta.entityMetadata().versionAttribute() ) {
- Object version = repoMeta.entityMetadata().versionAttribute().get( entity );
- if ( null != version ) {
- List etags = request.getHeaders().getIfNoneMatch();
- for ( String etag : etags ) {
- if ( ("\"" + version.toString() + "\"").equals( etag ) ) {
- return negotiateResponse( request, HttpStatus.NOT_MODIFIED, EMPTY_HEADERS, null );
- }
- }
- headers.set( "ETag", "\"" + version.toString() + "\"" );
- }
- }
- Map entityDto = extractPropertiesLinkAware( repoMeta.rel(),
- entity,
- repoMeta.entityMetadata(),
- buildUri( baseUri, repository, id ) );
- addSelfLink( baseUri, entityDto, repository, id );
-
- return negotiateResponse( request, HttpStatus.OK, headers, entityDto );
+ Object entity = repo.findOne(serId);
+ if(null == entity) {
+ return notFoundResponse(request);
}
+ HttpHeaders headers = new HttpHeaders();
+ if(null != repoMeta.entityMetadata().versionAttribute()) {
+ Object version = repoMeta.entityMetadata().versionAttribute().get(entity);
+ if(null != version) {
+ List etags = request.getHeaders().getIfNoneMatch();
+ for(String etag : etags) {
+ if(("\"" + version.toString() + "\"").equals(etag)) {
+ return negotiateResponse(request, HttpStatus.NOT_MODIFIED, new HttpHeaders(), null);
+ }
+ }
+ headers.set("ETag", "\"" + version.toString() + "\"");
+ }
+ }
+ Map entityDto = extractPropertiesLinkAware(repoMeta.rel(),
+ entity,
+ repoMeta.entityMetadata(),
+ buildUri(baseUri, repository, id));
+ addSelfLink(baseUri, entityDto, repository, id);
+
+ return negotiateResponse(request, HttpStatus.OK, headers, entityDto);
}
+ /**
+ * Create an entity with a specific ID or update an existing entity.
+ *
+ * @param request
+ * @param uriBuilder
+ * @param repository
+ * @param id
+ *
+ * @return
+ *
+ * @throws IOException
+ * @throws IllegalAccessException
+ * @throws InstantiationException
+ */
@SuppressWarnings({"unchecked"})
@RequestMapping(
value = "/{repository}/{id}",
@@ -607,178 +781,209 @@ public class RepositoryRestController
}
)
@ResponseBody
- public ResponseEntity> createOrUpdate( ServletServerHttpRequest request,
- UriComponentsBuilder uriBuilder,
- @PathVariable String repository,
- @PathVariable String id )
- throws IOException,
- IllegalAccessException,
- InstantiationException {
+ public ResponseEntity> createOrUpdate(ServletServerHttpRequest request,
+ UriComponentsBuilder uriBuilder,
+ @PathVariable String repository,
+ @PathVariable String id) throws IOException,
+ IllegalAccessException,
+ InstantiationException {
URI baseUri = uriBuilder.build().toUri();
- RepositoryMetadata repoMeta = repositoryMetadataFor( repository );
- Serializable serId = stringToSerializable( id,
- (Class extends Serializable>) repoMeta.entityMetadata()
- .idAttribute()
- .type() );
+ RepositoryMetadata repoMeta = repositoryMetadataFor(repository);
+ Serializable serId = stringToSerializable(id,
+ (Class extends Serializable>)repoMeta.entityMetadata()
+ .idAttribute()
+ .type());
CrudRepository repo = repoMeta.repository();
Class> domainType = repoMeta.entityMetadata().type();
- final MediaType incomingMediaType = request.getHeaders().getContentType();
- final Object incoming = readIncoming( request, incomingMediaType, domainType );
- if ( null == incoming ) {
- throw new HttpMessageNotReadableException( "Could not create an instance of " + domainType.getSimpleName() + " from input." );
+ boolean returnBody = true;
+ if(null != request.getServletRequest().getParameter("returnBody")) {
+ returnBody = Boolean.parseBoolean(request.getServletRequest().getParameter("returnBody"));
+ }
+
+ MediaType incomingMediaType = request.getHeaders().getContentType();
+ Object incoming;
+ if(null == (incoming = readIncoming(request, incomingMediaType, domainType))) {
+ throw new HttpMessageNotReadableException("Could not create an instance of " + domainType.getSimpleName() + " from input.");
+ }
+
+ repoMeta.entityMetadata().idAttribute().set(serId, incoming);
+ if(request.getMethod() == HttpMethod.POST) {
+
+ publishEvent(new BeforeSaveEvent(incoming));
+ Object savedEntity = repo.save(incoming);
+ publishEvent(new AfterSaveEvent(savedEntity));
+
+ URI selfUri = buildUri(baseUri, repository, id);
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.set(LOCATION, selfUri.toString());
+
+ return negotiateResponse(request, HttpStatus.CREATED, headers, (returnBody ? savedEntity : null));
} else {
- repoMeta.entityMetadata().idAttribute().set( serId, incoming );
- if ( request.getMethod() == HttpMethod.POST ) {
- if ( null != applicationContext ) {
- applicationContext.publishEvent( new BeforeSaveEvent( incoming ) );
- }
- Object savedEntity = repo.save( incoming );
- if ( null != applicationContext ) {
- applicationContext.publishEvent( new AfterSaveEvent( savedEntity ) );
- }
- URI selfUri = buildUri( baseUri, repository, id );
- HttpHeaders headers = new HttpHeaders();
- headers.set( LOCATION, selfUri.toString() );
- boolean returnBody = true;
- if ( null != request.getServletRequest().getParameter( "returnBody" ) ) {
- returnBody = Boolean.parseBoolean( request.getServletRequest().getParameter( "returnBody" ) );
- }
- return negotiateResponse( request, HttpStatus.CREATED, headers, (returnBody ? savedEntity : null) );
- } else {
- Object entity = repo.findOne( serId );
- if ( null == entity ) {
- return negotiateResponse( request, HttpStatus.NOT_FOUND, EMPTY_HEADERS, null );
- } else {
- for ( AttributeMetadata attrMeta : (Collection) repoMeta.entityMetadata()
- .embeddedAttributes()
- .values() ) {
- Object incomingVal = attrMeta.get( incoming );
- if ( null != incomingVal ) {
- attrMeta.set( incomingVal, entity );
- }
- }
- if ( null != applicationContext ) {
- applicationContext.publishEvent( new BeforeSaveEvent( entity ) );
- }
- Object savedEntity = repo.save( entity );
- if ( null != applicationContext ) {
- applicationContext.publishEvent( new AfterSaveEvent( savedEntity ) );
- }
- return negotiateResponse( request, HttpStatus.NO_CONTENT, EMPTY_HEADERS, null );
+ Object entity;
+ if(null == (entity = repo.findOne(serId))) {
+ return notFoundResponse(request);
+ }
+
+ for(AttributeMetadata attrMeta : (Collection)repoMeta.entityMetadata()
+ .embeddedAttributes()
+ .values()) {
+ Object incomingVal = attrMeta.get(incoming);
+ if(null != incomingVal) {
+ attrMeta.set(incomingVal, entity);
}
}
+
+ publishEvent(new BeforeSaveEvent(entity));
+ Object savedEntity = repo.save(entity);
+ publishEvent(new AfterSaveEvent(savedEntity));
+
+ return negotiateResponse(request, HttpStatus.NO_CONTENT, new HttpHeaders(), (returnBody ? savedEntity : null));
}
}
+ /**
+ * Delete an entity.
+ *
+ * @param request
+ * @param repository
+ * @param id
+ *
+ * @return
+ *
+ * @throws IOException
+ */
@SuppressWarnings({"unchecked"})
@RequestMapping(
value = "/{repository}/{id}",
method = RequestMethod.DELETE
)
@ResponseBody
- public ResponseEntity> deleteEntity( ServletServerHttpRequest request,
- @PathVariable String repository,
- @PathVariable String id )
- throws IOException {
- RepositoryMetadata repoMeta = repositoryMetadataFor( repository );
- Serializable serId = stringToSerializable( id,
- (Class extends Serializable>) repoMeta.entityMetadata()
- .idAttribute()
- .type() );
+ public ResponseEntity> deleteEntity(ServletServerHttpRequest request,
+ @PathVariable String repository,
+ @PathVariable String id) throws IOException {
+ RepositoryMetadata repoMeta = repositoryMetadataFor(repository);
+ Serializable serId = stringToSerializable(id,
+ (Class extends Serializable>)repoMeta.entityMetadata()
+ .idAttribute()
+ .type());
CrudRepository repo = repoMeta.repository();
- Object entity = repo.findOne( serId );
- if ( null == entity ) {
- return negotiateResponse( request, HttpStatus.NOT_FOUND, EMPTY_HEADERS, null );
- } else {
- if ( null != applicationContext ) {
- applicationContext.publishEvent( new BeforeDeleteEvent( entity ) );
- }
- repo.delete( serId );
- if ( null != applicationContext ) {
- applicationContext.publishEvent( new AfterDeleteEvent( entity ) );
- }
- return negotiateResponse( request, HttpStatus.NO_CONTENT, EMPTY_HEADERS, null );
+ Object entity;
+ if(null == (entity = repo.findOne(serId))) {
+ return notFoundResponse(request);
}
+
+ publishEvent(new BeforeDeleteEvent(entity));
+ repo.delete(serId);
+ publishEvent(new AfterDeleteEvent(entity));
+
+ return negotiateResponse(request, HttpStatus.NO_CONTENT, new HttpHeaders(), null);
}
-
+ /**
+ * Retrieve the property of an entity.
+ *
+ * @param request
+ * @param uriBuilder
+ * @param repository
+ * @param id
+ * @param property
+ *
+ * @return
+ *
+ * @throws IOException
+ */
@SuppressWarnings({"unchecked"})
@RequestMapping(
value = "/{repository}/{id}/{property}",
method = RequestMethod.GET
)
@ResponseBody
- public ResponseEntity> propertyOfEntity( ServletServerHttpRequest request,
- UriComponentsBuilder uriBuilder,
- @PathVariable String repository,
- @PathVariable String id,
- @PathVariable String property )
- throws IOException {
+ public ResponseEntity> propertyOfEntity(ServletServerHttpRequest request,
+ UriComponentsBuilder uriBuilder,
+ @PathVariable String repository,
+ @PathVariable String id,
+ @PathVariable String property) throws IOException {
URI baseUri = uriBuilder.build().toUri();
- RepositoryMetadata repoMeta = repositoryMetadataFor( repository );
- Serializable serId = stringToSerializable( id,
- (Class extends Serializable>) repoMeta.entityMetadata()
- .idAttribute()
- .type() );
+ RepositoryMetadata repoMeta = repositoryMetadataFor(repository);
+ Serializable serId = stringToSerializable(id,
+ (Class extends Serializable>)repoMeta.entityMetadata()
+ .idAttribute()
+ .type());
CrudRepository repo = repoMeta.repository();
- Object entity = repo.findOne( serId );
- if ( null == entity ) {
- return negotiateResponse( request, HttpStatus.NOT_FOUND, EMPTY_HEADERS, null );
- } else {
- AttributeMetadata attrMeta = repoMeta.entityMetadata().attribute( property );
- if ( null == attrMeta ) {
- return negotiateResponse( request, HttpStatus.NOT_FOUND, EMPTY_HEADERS, null );
- } else {
- Class> attrType = attrMeta.elementType();
- if ( null == attrType ) {
- attrType = attrMeta.type();
- }
- RepositoryMetadata propRepoMeta = repositoryMetadataFor( attrType );
- Object propVal = attrMeta.get( entity );
- AttributeMetadata idAttr = propRepoMeta.entityMetadata().idAttribute();
- if ( null != propVal ) {
- Links links = new Links();
- if ( propVal instanceof Collection ) {
- for ( Object o : (Collection) propVal ) {
- String propValId = idAttr.get( o ).toString();
- String rel = repository + "."
- + entity.getClass().getSimpleName() + "."
- + attrType.getSimpleName();
- URI path = buildUri( baseUri, repository, id, property, propValId );
- links.add( new SimpleLink( rel, path ) );
- }
- } else if ( propVal instanceof Map ) {
- for ( Map.Entry entry : ((Map) propVal).entrySet() ) {
- String propValId = idAttr.get( entry.getValue() ).toString();
- URI path = buildUri( baseUri, repository, id, property, propValId );
- Object oKey = entry.getKey();
- String sKey;
- if ( ClassUtils.isAssignable( oKey.getClass(), String.class ) ) {
- sKey = (String) oKey;
- } else {
- sKey = conversionService.convert( oKey, String.class );
- }
- String rel = repository + "." + entity.getClass().getSimpleName() + "." + sKey;
- links.add( new SimpleLink( rel, path ) );
- }
- } else {
- String propValId = idAttr.get( propVal ).toString();
- String rel = repository + "." + entity.getClass().getSimpleName() + "." + property;
- URI path = buildUri( baseUri, repository, id, property, propValId );
- links.add( new SimpleLink( rel, path ) );
- }
- return negotiateResponse( request, HttpStatus.OK, EMPTY_HEADERS, links );
- } else {
- return negotiateResponse( request, HttpStatus.NOT_FOUND, EMPTY_HEADERS, null );
- }
- }
+ Object entity;
+ if(null == (entity = repo.findOne(serId))) {
+ return notFoundResponse(request);
}
+
+ AttributeMetadata attrMeta;
+ if(null == (attrMeta = repoMeta.entityMetadata().attribute(property))) {
+ return notFoundResponse(request);
+ }
+
+ Class> attrType;
+ if(null == (attrType = attrMeta.elementType())) {
+ attrType = attrMeta.type();
+ }
+
+ RepositoryMetadata propRepoMeta = repositoryMetadataFor(attrType);
+
+ Object propVal;
+ if(null == (propVal = attrMeta.get(entity))) {
+ return notFoundResponse(request);
+ }
+
+ AttributeMetadata idAttr = propRepoMeta.entityMetadata().idAttribute();
+ Links links = new Links();
+ if(propVal instanceof Collection) {
+ for(Object o : (Collection)propVal) {
+ String propValId = idAttr.get(o).toString();
+ String rel = repository + "."
+ + entity.getClass().getSimpleName() + "."
+ + attrType.getSimpleName();
+ URI path = buildUri(baseUri, repository, id, property, propValId);
+ links.add(new SimpleLink(rel, path));
+ }
+ } else if(propVal instanceof Map) {
+ for(Map.Entry entry : ((Map)propVal).entrySet()) {
+ String propValId = idAttr.get(entry.getValue()).toString();
+ URI path = buildUri(baseUri, repository, id, property, propValId);
+ Object oKey = entry.getKey();
+ String sKey;
+ if(ClassUtils.isAssignable(oKey.getClass(), String.class)) {
+ sKey = (String)oKey;
+ } else {
+ sKey = conversionService.convert(oKey, String.class);
+ }
+ links.add(new SimpleLink(sKey, path));
+ }
+ } else {
+ String propValId = idAttr.get(propVal).toString();
+ String rel = repository + "." + entity.getClass().getSimpleName() + "." + property;
+ URI path = buildUri(baseUri, repository, id, property, propValId);
+ links.add(new SimpleLink(rel, path));
+ }
+
+ return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), links);
}
+ /**
+ * Update the property of an entity if that property is also managed by a {@link CrudRepository}.
+ *
+ * @param request
+ * @param uriBuilder
+ * @param repository
+ * @param id
+ * @param property
+ *
+ * @return
+ *
+ * @throws IOException
+ */
@SuppressWarnings({"unchecked"})
@RequestMapping(
value = "/{repository}/{id}/{property}",
@@ -788,121 +993,109 @@ public class RepositoryRestController
}
)
@ResponseBody
- public ResponseEntity> updatePropertyOfEntity( final ServletServerHttpRequest request,
- UriComponentsBuilder uriBuilder,
- @PathVariable String repository,
- @PathVariable String id,
- final @PathVariable String property ) throws IOException {
+ public ResponseEntity> updatePropertyOfEntity(final ServletServerHttpRequest request,
+ UriComponentsBuilder uriBuilder,
+ @PathVariable String repository,
+ @PathVariable String id,
+ final @PathVariable String property) throws IOException {
URI baseUri = uriBuilder.build().toUri();
- final RepositoryMetadata repoMeta = repositoryMetadataFor( repository );
- Serializable serId = stringToSerializable( id,
- (Class extends Serializable>) repoMeta.entityMetadata()
- .idAttribute()
- .type() );
+ final RepositoryMetadata repoMeta = repositoryMetadataFor(repository);
+ Serializable serId = stringToSerializable(id,
+ (Class extends Serializable>)repoMeta.entityMetadata()
+ .idAttribute()
+ .type());
CrudRepository repo = repoMeta.repository();
- final Object entity = repo.findOne( serId );
- if ( null == entity ) {
- return negotiateResponse( request, HttpStatus.NOT_FOUND, EMPTY_HEADERS, null );
- } else {
- final AttributeMetadata attrMeta = repoMeta.entityMetadata().attribute( property );
- if ( null == attrMeta ) {
- return negotiateResponse( request, HttpStatus.NOT_FOUND, EMPTY_HEADERS, null );
- } else {
- Object linked = attrMeta.get( entity );
- final AtomicReference rel = new AtomicReference();
- Handler> entityHandler = new Handler>() {
- @Override public ResponseEntity> handle( Object linkedEntity ) {
- if ( attrMeta.isCollectionLike() ) {
- Collection c = new ArrayList();
- Collection current = attrMeta.asCollection( entity );
- if ( request.getMethod() == HttpMethod.POST && null != current ) {
- c.addAll( current );
- }
- c.add( linkedEntity );
- attrMeta.set( c, entity );
- } else if ( attrMeta.isSetLike() ) {
- Set s = new HashSet();
- Set current = attrMeta.asSet( entity );
- if ( request.getMethod() == HttpMethod.POST && null != current ) {
- s.addAll( current );
- }
- s.add( linkedEntity );
- attrMeta.set( s, entity );
- } else if ( attrMeta.isMapLike() ) {
- Map m = new HashMap();
- Map current = attrMeta.asMap( entity );
- if ( request.getMethod() == HttpMethod.POST && null != current ) {
- m.putAll( current );
- }
- String key = rel.get();
- if ( null == key ) {
- try {
- return negotiateResponse( request, HttpStatus.NOT_ACCEPTABLE, EMPTY_HEADERS, null );
- } catch ( IOException e ) {
- throw new RuntimeException( e );
- }
- } else {
- m.put( rel.get(), linkedEntity );
- attrMeta.set( m, entity );
- }
- } else {
- attrMeta.set( linkedEntity, entity );
- }
- return null;
+
+ final Object entity;
+ final AttributeMetadata attrMeta;
+ if(null == (entity = repo.findOne(serId)) || null == (attrMeta = repoMeta.entityMetadata().attribute(property))) {
+ return notFoundResponse(request);
+ }
+
+ Object linked = attrMeta.get(entity);
+ final AtomicReference rel = new AtomicReference();
+ Handler> entityHandler = new Handler>() {
+ @Override public ResponseEntity> handle(Object linkedEntity) {
+
+ if(attrMeta.isCollectionLike()) {
+ Collection c = new ArrayList();
+ Collection current = attrMeta.asCollection(entity);
+ if(request.getMethod() == HttpMethod.POST && null != current) {
+ c.addAll(current);
}
- };
- MediaType incomingMediaType = request.getHeaders().getContentType();
- if ( incomingMediaType.getSubtype().startsWith( "uri-list" ) ) {
- BufferedReader in = new BufferedReader( new InputStreamReader( request.getBody() ) );
- String line;
- while ( null != (line = in.readLine()) ) {
- String sLinkUri = line.trim();
- Object o = resolveTopLevelResource( baseUri, sLinkUri );
- if ( null != o ) {
- ResponseEntity> possibleResponse = entityHandler.handle( o );
- if ( null != possibleResponse ) {
- return possibleResponse;
- }
+ c.add(linkedEntity);
+ attrMeta.set(c, entity);
+ } else if(attrMeta.isSetLike()) {
+ Set s = new HashSet();
+ Set current = attrMeta.asSet(entity);
+ if(request.getMethod() == HttpMethod.POST && null != current) {
+ s.addAll(current);
+ }
+ s.add(linkedEntity);
+ attrMeta.set(s, entity);
+ } else if(attrMeta.isMapLike()) {
+ Map m = new HashMap();
+ Map current = attrMeta.asMap(entity);
+ if(request.getMethod() == HttpMethod.POST && null != current) {
+ m.putAll(current);
+ }
+ String key = rel.get();
+ if(null == key) {
+ try {
+ return negotiateResponse(request, HttpStatus.BAD_REQUEST, new HttpHeaders(), null);
+ } catch(IOException e) {
+ throw new RuntimeException(e);
}
}
+ m.put(rel.get(), linkedEntity);
+ attrMeta.set(m, entity);
} else {
- final Map>> incoming = readIncoming( request,
- incomingMediaType,
- Map.class );
- for ( Map link : incoming.get( LINKS ) ) {
- String sLinkUri = link.get( "href" );
- Object o = resolveTopLevelResource( baseUri, sLinkUri );
- rel.set( link.get( "rel" ) );
- if ( null != o ) {
- ResponseEntity> possibleResponse = entityHandler.handle( o );
- if ( null != possibleResponse ) {
- return possibleResponse;
- }
- }
- }
+ attrMeta.set(linkedEntity, entity);
}
- if ( null != applicationContext ) {
- applicationContext.publishEvent( new BeforeSaveEvent( entity ) );
- applicationContext.publishEvent( new BeforeLinkSaveEvent( entity, linked ) );
- }
- Object savedEntity = repo.save( entity );
- if ( null != applicationContext ) {
- linked = attrMeta.get( savedEntity );
- applicationContext.publishEvent( new AfterLinkSaveEvent( savedEntity, linked ) );
- applicationContext.publishEvent( new AfterSaveEvent( savedEntity ) );
- }
+ return null;
+ }
+ };
- if ( request.getMethod() == HttpMethod.PUT ) {
- return negotiateResponse( request, HttpStatus.NO_CONTENT, EMPTY_HEADERS, null );
- } else {
- return negotiateResponse( request, HttpStatus.CREATED, EMPTY_HEADERS, null );
+ MediaType incomingMediaType = request.getHeaders().getContentType();
+ Links incomingLinks = readIncoming(request, incomingMediaType, Links.class);
+ for(Link l : incomingLinks.getLinks()) {
+ Object o;
+ if(null != (o = resolveTopLevelResource(baseUri, l.href().toString()))) {
+ ResponseEntity> possibleResponse = entityHandler.handle(o);
+ if(null != possibleResponse) {
+ return possibleResponse;
}
}
+
+ publishEvent(new BeforeSaveEvent(entity));
+ publishEvent(new BeforeLinkSaveEvent(entity, linked));
+ Object savedEntity = repo.save(entity);
+ linked = attrMeta.get(savedEntity);
+ publishEvent(new AfterLinkSaveEvent(savedEntity, linked));
+ publishEvent(new AfterSaveEvent(savedEntity));
+ }
+
+ if(request.getMethod() == HttpMethod.PUT) {
+ return negotiateResponse(request, HttpStatus.NO_CONTENT, new HttpHeaders(), null);
+ } else {
+ return negotiateResponse(request, HttpStatus.CREATED, new HttpHeaders(), null);
}
}
+ /**
+ * Clear all linked entities of a specific property.
+ *
+ * @param request
+ * @param repository
+ * @param id
+ * @param property
+ *
+ * @return
+ *
+ * @throws IOException
+ */
@SuppressWarnings({"unchecked"})
@RequestMapping(
value = "/{repository}/{id}/{property}",
@@ -911,41 +1104,47 @@ public class RepositoryRestController
}
)
@ResponseBody
- public ResponseEntity> clearLinks( ServletServerHttpRequest request,
- @PathVariable String repository,
- @PathVariable String id,
- @PathVariable String property )
- throws IOException {
- RepositoryMetadata repoMeta = repositoryMetadataFor( repository );
- Serializable serId = stringToSerializable( id,
- (Class extends Serializable>) repoMeta.entityMetadata()
- .idAttribute()
- .type() );
+ public ResponseEntity> clearLinks(ServletServerHttpRequest request,
+ @PathVariable String repository,
+ @PathVariable String id,
+ @PathVariable String property) throws IOException {
+ RepositoryMetadata repoMeta = repositoryMetadataFor(repository);
CrudRepository repo = repoMeta.repository();
- final Object entity = repo.findOne( serId );
- if ( null == entity ) {
- return negotiateResponse( request, HttpStatus.NOT_FOUND, EMPTY_HEADERS, null );
- } else {
- AttributeMetadata attrMeta = repoMeta.entityMetadata().attribute( property );
- if ( null != attrMeta ) {
- Object linked = attrMeta.get( entity );
- attrMeta.set( null, entity );
+ Serializable serId = stringToSerializable(id,
+ (Class extends Serializable>)repoMeta.entityMetadata()
+ .idAttribute()
+ .type());
- if ( null != applicationContext ) {
- applicationContext.publishEvent( new BeforeLinkSaveEvent( entity, linked ) );
- }
- Object savedEntity = repo.save( entity );
- if ( null != applicationContext ) {
- applicationContext.publishEvent( new AfterLinkSaveEvent( savedEntity, null ) );
- }
-
- return negotiateResponse( request, HttpStatus.NO_CONTENT, EMPTY_HEADERS, null );
- } else {
- return negotiateResponse( request, HttpStatus.NOT_FOUND, EMPTY_HEADERS, null );
- }
+ Object entity;
+ AttributeMetadata attrMeta;
+ if(null == (entity = repo.findOne(serId)) || null == (attrMeta = repoMeta.entityMetadata().attribute(property))) {
+ return notFoundResponse(request);
}
+
+ Object linked = attrMeta.get(entity);
+ attrMeta.set(null, entity);
+
+ publishEvent(new BeforeLinkSaveEvent(entity, linked));
+ Object savedEntity = repo.save(entity);
+ publishEvent(new AfterLinkSaveEvent(savedEntity, null));
+
+ return negotiateResponse(request, HttpStatus.NO_CONTENT, new HttpHeaders(), null);
}
+ /**
+ * Retrieve a linked entity from a parent entity.
+ *
+ * @param request
+ * @param uriBuilder
+ * @param repository
+ * @param id
+ * @param property
+ * @param linkedId
+ *
+ * @return
+ *
+ * @throws IOException
+ */
@SuppressWarnings({"unchecked"})
@RequestMapping(
value = "/{repository}/{id}/{property}/{linkedId}",
@@ -954,54 +1153,64 @@ public class RepositoryRestController
}
)
@ResponseBody
- public ResponseEntity> linkedEntity( ServletServerHttpRequest request,
- UriComponentsBuilder uriBuilder,
- @PathVariable String repository,
- @PathVariable String id,
- @PathVariable String property,
- @PathVariable String linkedId )
- throws IOException {
+ public ResponseEntity> linkedEntity(ServletServerHttpRequest request,
+ UriComponentsBuilder uriBuilder,
+ @PathVariable String repository,
+ @PathVariable String id,
+ @PathVariable String property,
+ @PathVariable String linkedId) throws IOException {
URI baseUri = uriBuilder.build().toUri();
- RepositoryMetadata repoMeta = repositoryMetadataFor( repository );
- Serializable serId = stringToSerializable( id,
- (Class extends Serializable>) repoMeta.entityMetadata()
- .idAttribute()
- .type() );
- CrudRepository repo = repoMeta.repository();
- final Object entity = repo.findOne( serId );
- if ( null != entity ) {
- AttributeMetadata attrMeta = repoMeta.entityMetadata().attribute( property );
- if ( null != attrMeta ) {
- // Find linked entity
- RepositoryMetadata linkedRepoMeta = repositoryMetadataFor( attrMeta );
- if ( null != linkedRepoMeta ) {
- CrudRepository linkedRepo = linkedRepoMeta.repository();
- Serializable sChildId = stringToSerializable( linkedId,
- (Class extends Serializable>) linkedRepoMeta.entityMetadata()
- .idAttribute()
- .type() );
- Object linkedEntity = linkedRepo.findOne( sChildId );
- if ( null != linkedEntity ) {
- Map entityDto = extractPropertiesLinkAware( linkedRepoMeta.rel(),
- linkedEntity,
- linkedRepoMeta.entityMetadata(),
- buildUri( baseUri,
- linkedRepoMeta.name(),
- linkedId ) );
- URI selfUri = addSelfLink( baseUri, entityDto, linkedRepoMeta.name(), linkedId );
+ RepositoryMetadata repoMeta = repositoryMetadataFor(repository);
- HttpHeaders headers = new HttpHeaders();
- headers.add( "Content-Location", selfUri.toString() );
- return negotiateResponse( request, HttpStatus.OK, headers, entityDto );
- }
- }
- }
+ AttributeMetadata attrMeta;
+ if(null == (attrMeta = repoMeta.entityMetadata().attribute(property))) {
+ return notFoundResponse(request);
}
- return negotiateResponse( request, HttpStatus.NOT_FOUND, EMPTY_HEADERS, null );
+ // Find linked entity
+ RepositoryMetadata linkedRepoMeta;
+ if(null == (linkedRepoMeta = repositoryMetadataFor(attrMeta))) {
+ return notFoundResponse(request);
+ }
+
+ CrudRepository linkedRepo = linkedRepoMeta.repository();
+ Serializable sChildId = stringToSerializable(linkedId,
+ (Class extends Serializable>)linkedRepoMeta.entityMetadata()
+ .idAttribute()
+ .type());
+ Object linkedEntity;
+ if(null == (linkedEntity = linkedRepo.findOne(sChildId))) {
+ return notFoundResponse(request);
+ }
+
+ Map entityDto = extractPropertiesLinkAware(linkedRepoMeta.rel(),
+ linkedEntity,
+ linkedRepoMeta.entityMetadata(),
+ buildUri(baseUri,
+ linkedRepoMeta.name(),
+ linkedId));
+ URI selfUri = addSelfLink(baseUri, entityDto, linkedRepoMeta.name(), linkedId);
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("Content-Location", selfUri.toString());
+
+ return negotiateResponse(request, HttpStatus.OK, headers, entityDto);
}
+ /**
+ * Delete a specific relationship between a child entity and its parent.
+ *
+ * @param request
+ * @param repository
+ * @param id
+ * @param property
+ * @param linkedId
+ *
+ * @return
+ *
+ * @throws IOException
+ */
@SuppressWarnings({"unchecked"})
@RequestMapping(
value = "/{repository}/{id}/{property}/{linkedId}",
@@ -1010,127 +1219,184 @@ public class RepositoryRestController
}
)
@ResponseBody
- public ResponseEntity> deleteLink( ServletServerHttpRequest request,
- @PathVariable String repository,
- @PathVariable String id,
- @PathVariable String property,
- @PathVariable String linkedId ) throws IOException {
- RepositoryMetadata repoMeta = repositoryMetadataFor( repository );
- Serializable serId = stringToSerializable( id,
- (Class extends Serializable>) repoMeta.entityMetadata()
- .idAttribute()
- .type() );
+ public ResponseEntity> deleteLink(ServletServerHttpRequest request,
+ @PathVariable String repository,
+ @PathVariable String id,
+ @PathVariable String property,
+ @PathVariable String linkedId) throws IOException {
+ RepositoryMetadata repoMeta = repositoryMetadataFor(repository);
CrudRepository repo = repoMeta.repository();
- Object entity = repo.findOne( serId );
- if ( null == entity ) {
- return negotiateResponse( request, HttpStatus.NOT_FOUND, EMPTY_HEADERS, null );
- } else {
- AttributeMetadata attrMeta = repoMeta.entityMetadata().attribute( property );
- if ( null != attrMeta ) {
- // Find linked entity
- RepositoryMetadata linkedRepoMeta = repositoryMetadataFor( attrMeta );
- if ( null != linkedRepoMeta ) {
- CrudRepository linkedRepo = linkedRepoMeta.repository();
- Serializable sChildId = stringToSerializable( linkedId,
- (Class extends Serializable>) linkedRepoMeta.entityMetadata()
- .idAttribute()
- .type() );
- Object linkedEntity = linkedRepo.findOne( sChildId );
- if ( null != linkedEntity ) {
- // Remove linked entity from relationship based on property type
- if ( attrMeta.isCollectionLike() ) {
- Collection c = attrMeta.asCollection( entity );
- if ( null != c ) {
- c.remove( linkedEntity );
- }
- } else if ( attrMeta.isSetLike() ) {
- Set s = attrMeta.asSet( entity );
- if ( null != s ) {
- s.remove( linkedEntity );
- }
- } else if ( attrMeta.isMapLike() ) {
- Object keyToRemove = null;
- Map m = attrMeta.asMap( entity );
- if ( null != m ) {
- for ( Map.Entry entry : m.entrySet() ) {
- Object val = entry.getValue();
- if ( null != val && val.equals( linkedEntity ) ) {
- keyToRemove = entry.getKey();
- break;
- }
- }
- if ( null != keyToRemove ) {
- m.remove( keyToRemove );
- }
- }
- } else {
- attrMeta.set( linkedEntity, entity );
- }
+ Serializable serId = stringToSerializable(id,
+ (Class extends Serializable>)repoMeta.entityMetadata()
+ .idAttribute()
+ .type());
+ Object entity;
+ AttributeMetadata attrMeta;
+ if(null == (entity = repo.findOne(serId)) || null == (attrMeta = repoMeta.entityMetadata().attribute(property))) {
+ return notFoundResponse(request);
+ }
- return negotiateResponse( request, HttpStatus.NO_CONTENT, EMPTY_HEADERS, null );
+ // Find linked entity
+ RepositoryMetadata linkedRepoMeta;
+ if(null == (linkedRepoMeta = repositoryMetadataFor(attrMeta))) {
+ return notFoundResponse(request);
+ }
+
+ CrudRepository linkedRepo = linkedRepoMeta.repository();
+ Serializable sChildId = stringToSerializable(linkedId,
+ (Class extends Serializable>)linkedRepoMeta.entityMetadata()
+ .idAttribute()
+ .type());
+
+ Object linkedEntity;
+ if(null == (linkedEntity = linkedRepo.findOne(sChildId))) {
+ return notFoundResponse(request);
+ }
+
+ // Remove linked entity from relationship based on property type
+ if(attrMeta.isCollectionLike()) {
+ Collection c = attrMeta.asCollection(entity);
+ if(null != c) {
+ c.remove(linkedEntity);
+ }
+ } else if(attrMeta.isSetLike()) {
+ Set s = attrMeta.asSet(entity);
+ if(null != s) {
+ s.remove(linkedEntity);
+ }
+ } else if(attrMeta.isMapLike()) {
+ Object keyToRemove = null;
+ Map m = attrMeta.asMap(entity);
+ if(null != m) {
+ for(Map.Entry entry : m.entrySet()) {
+ Object val = entry.getValue();
+ if(null != val && val.equals(linkedEntity)) {
+ keyToRemove = entry.getKey();
+ break;
}
}
+ if(null != keyToRemove) {
+ m.remove(keyToRemove);
+ }
}
+ } else {
+ attrMeta.set(linkedEntity, entity);
}
- return negotiateResponse( request, HttpStatus.NOT_FOUND, EMPTY_HEADERS, null );
+ return negotiateResponse(request, HttpStatus.NO_CONTENT, new HttpHeaders(), null);
}
+ /**
+ * Send a 404 if no repository was found.
+ *
+ * @param e
+ * @param request
+ *
+ * @return
+ *
+ * @throws IOException
+ */
@ExceptionHandler(RepositoryNotFoundException.class)
@ResponseBody
- public ResponseEntity handleRepositoryNotFoundFailure( RepositoryNotFoundException e,
- ServletServerHttpRequest request )
- throws IOException {
- if ( LOG.isWarnEnabled() ) {
- LOG.warn( "RepositoryNotFoundException: " + e.getMessage() );
+ public ResponseEntity handleRepositoryNotFoundFailure(RepositoryNotFoundException e,
+ ServletServerHttpRequest request) throws IOException {
+ if(LOG.isWarnEnabled()) {
+ LOG.warn("RepositoryNotFoundException: " + e.getMessage());
}
- return negotiateResponse( request, HttpStatus.NOT_FOUND, EMPTY_HEADERS, null );
+ return notFoundResponse(request);
}
+ @ExceptionHandler(
+ {
+ NullPointerException.class,
+ IllegalArgumentException.class,
+ IllegalStateException.class,
+ ClassCastException.class
+ }
+ )
+ @ResponseBody
+ public ResponseEntity handleMiscFailures(Throwable t,
+ ServletServerHttpRequest request) throws IOException {
+ if(LOG.isErrorEnabled()) {
+ LOG.error(t.getMessage(), t);
+ }
+ return negotiateResponse(request, HttpStatus.BAD_REQUEST, new HttpHeaders(), null);
+ }
+
+ /**
+ * Send a 409 Conflict in case of concurrent modification.
+ *
+ * @param ex
+ * @param request
+ *
+ * @return
+ *
+ * @throws IOException
+ */
@SuppressWarnings({"unchecked"})
@ExceptionHandler(OptimisticLockingFailureException.class)
@ResponseBody
- public ResponseEntity handleLockingFailure( OptimisticLockingFailureException ex,
- ServletServerHttpRequest request )
- throws IOException {
- LOG.error( ex.getMessage(), ex );
+ public ResponseEntity handleLockingFailure(OptimisticLockingFailureException ex,
+ ServletServerHttpRequest request) throws IOException {
+ LOG.error(ex.getMessage(), ex);
HttpHeaders headers = new HttpHeaders();
- headers.setContentType( MediaType.APPLICATION_JSON );
+ headers.setContentType(MediaType.APPLICATION_JSON);
Map m = new HashMap();
- m.put( "message", ex.getMessage() );
- return negotiateResponse( request, HttpStatus.CONFLICT, headers, objectMapper.writeValueAsBytes( m ) );
+ m.put("message", ex.getMessage());
+ return negotiateResponse(request, HttpStatus.CONFLICT, headers, objectMapper.writeValueAsBytes(m));
}
+ /**
+ * Send a 400 Bad Request in case of a validation failure.
+ *
+ * @param ex
+ * @param request
+ *
+ * @return
+ *
+ * @throws IOException
+ */
@SuppressWarnings({"unchecked"})
@ExceptionHandler(RepositoryConstraintViolationException.class)
@ResponseBody
- public ResponseEntity handleValidationFailure( RepositoryConstraintViolationException ex,
- ServletServerHttpRequest request )
- throws IOException {
- LOG.error( ex.getMessage(), ex );
+ public ResponseEntity handleValidationFailure(RepositoryConstraintViolationException ex,
+ ServletServerHttpRequest request) throws IOException {
+ LOG.error(ex.getMessage(), ex);
Map m = new HashMap();
List errors = new ArrayList();
- for ( FieldError fe : ex.getErrors().getFieldErrors() ) {
- errors.add( fe.getDefaultMessage() );
+ for(FieldError fe : ex.getErrors().getFieldErrors()) {
+ errors.add(fe.getDefaultMessage());
}
- m.put( "errors", errors );
+ m.put("errors", errors);
- return negotiateResponse( request, HttpStatus.BAD_REQUEST, EMPTY_HEADERS, m );
+ return negotiateResponse(request, HttpStatus.BAD_REQUEST, new HttpHeaders(), m);
}
+ /**
+ * Send a 400 Bad Request in case no converter was found to process the input or output.
+ *
+ * @param ex
+ * @param request
+ *
+ * @return
+ *
+ * @throws IOException
+ */
@SuppressWarnings({"unchecked"})
- @ExceptionHandler(HttpMessageNotReadableException.class)
+ @ExceptionHandler({HttpMessageNotReadableException.class, HttpMessageNotWritableException.class})
@ResponseBody
- public ResponseEntity handleMessageConversionFailure( HttpMessageNotReadableException ex,
- ServletServerHttpRequest request )
- throws IOException {
- LOG.error( ex.getMessage(), ex );
+ public ResponseEntity handleMessageConversionFailure(HttpMessageNotReadableException ex,
+ ServletServerHttpRequest request) throws IOException {
+ LOG.error(ex.getMessage(), ex);
+
HttpHeaders headers = new HttpHeaders();
- headers.setContentType( MediaType.APPLICATION_JSON );
+ headers.setContentType(MediaType.APPLICATION_JSON);
Map m = new HashMap();
- m.put( "message", ex.getMessage() );
- return negotiateResponse( request, HttpStatus.BAD_REQUEST, headers, objectMapper.writeValueAsBytes( m ) );
+ m.put("message", ex.getMessage());
+ m.put("acceptableTypes", availableMediaTypes);
+
+ return negotiateResponse(request, HttpStatus.BAD_REQUEST, headers, objectMapper.writeValueAsBytes(m));
}
/*
@@ -1138,131 +1404,137 @@ public class RepositoryRestController
Internal helper methods
-----------------------------------
*/
- private static URI buildUri( URI baseUri, String... pathSegments ) {
- return UriComponentsBuilder.fromUri( baseUri ).pathSegment( pathSegments ).build().toUri();
+ private static URI buildUri(URI baseUri, String... pathSegments) {
+ return UriComponentsBuilder.fromUri(baseUri).pathSegment(pathSegments).build().toUri();
}
@SuppressWarnings({"unchecked"})
- private URI addSelfLink( URI baseUri, Map model, String... pathComponents ) {
- List links = (List ) model.get( LINKS );
- if ( null == links ) {
+ private URI addSelfLink(URI baseUri, Map model, String... pathComponents) {
+ List links = (List )model.get(LINKS);
+ if(null == links) {
links = new ArrayList ();
- model.put( LINKS, links );
+ model.put(LINKS, links);
}
- URI selfUri = buildUri( baseUri, pathComponents );
- links.add( new SimpleLink( SELF, selfUri ) );
+ URI selfUri = buildUri(baseUri, pathComponents);
+ links.add(new SimpleLink(SELF, selfUri));
return selfUri;
}
@SuppressWarnings({"unchecked"})
- private void maybeAddPrevNextLink( URI resourceUri,
- RepositoryMetadata repoMeta,
- PagingAndSorting pageSort,
- Page page,
- boolean addIf,
- int nextPage,
- String rel,
- Links links ) {
- if ( null != page && addIf ) {
- UriComponentsBuilder urib = UriComponentsBuilder.fromUri( resourceUri );
- urib.queryParam( config.getPageParamName(), nextPage ); // PageRequest is 0-based, so it's already (page - 1)
- urib.queryParam( config.getLimitParamName(), page.getSize() );
- pageSort.addSortParameters( urib );
- links.add( new SimpleLink( repoMeta.rel() + "." + rel, urib.build().toUri() ) );
+ private void maybeAddPrevNextLink(URI resourceUri,
+ RepositoryMetadata repoMeta,
+ PagingAndSorting pageSort,
+ Page page,
+ boolean addIf,
+ int nextPage,
+ String rel,
+ List links) {
+ if(null != page && addIf) {
+ UriComponentsBuilder urib = UriComponentsBuilder.fromUri(resourceUri);
+ urib.queryParam(config.getPageParamName(), nextPage); // PageRequest is 0-based, so it's already (page - 1)
+ urib.queryParam(config.getLimitParamName(), page.getSize());
+ pageSort.addSortParameters(urib);
+ links.add(new SimpleLink(repoMeta.rel() + "." + rel, urib.build().toUri()));
}
}
@SuppressWarnings({"unchecked"})
- private V stringToSerializable( String s, Class targetType ) {
- if ( ClassUtils.isAssignable( targetType, String.class ) ) {
- return (V) s;
+ private V stringToSerializable(String s, Class targetType) {
+ if(ClassUtils.isAssignable(targetType, String.class)) {
+ return (V)s;
} else {
- return conversionService.convert( s, targetType );
+ return conversionService.convert(s, targetType);
}
}
@SuppressWarnings({"unchecked"})
- private Object resolveTopLevelResource( URI baseUri, String uri ) {
- URI href = URI.create( uri );
+ private Object resolveTopLevelResource(URI baseUri, String uri) {
+ URI href = URI.create(uri);
- URI relativeUri = baseUri.relativize( href );
- Stack uris = UriUtils.explode( baseUri, relativeUri );
+ URI relativeUri = baseUri.relativize(href);
+ Stack uris = UriUtils.explode(baseUri, relativeUri);
- if ( uris.size() > 1 ) {
- String repoName = UriUtils.path( uris.get( 0 ) );
- String sId = UriUtils.path( uris.get( 1 ) );
+ if(uris.size() > 1) {
+ String repoName = UriUtils.path(uris.get(0));
+ String sId = UriUtils.path(uris.get(1));
- RepositoryMetadata repoMeta = repositoryMetadataFor( repoName );
- CrudRepository repo = repoMeta.repository();
- if ( null == repo ) {
+ RepositoryMetadata repoMeta = repositoryMetadataFor(repoName);
+
+ CrudRepository repo;
+ if(null == (repo = repoMeta.repository())) {
return null;
}
- EntityMetadata entityMeta = repoMeta.entityMetadata();
- if ( null == entityMeta ) {
+
+ EntityMetadata entityMeta;
+ if(null == (entityMeta = repoMeta.entityMetadata())) {
return null;
}
- Class extends Serializable> idType = (Class extends Serializable>) entityMeta.idAttribute().type();
- Serializable serId = stringToSerializable( sId, idType );
+ Class extends Serializable> idType = (Class extends Serializable>)entityMeta.idAttribute().type();
+ Serializable serId = stringToSerializable(sId, idType);
- return repo.findOne( serId );
+ return repo.findOne(serId);
}
return null;
}
@SuppressWarnings({"unchecked"})
- private V readIncoming( HttpInputMessage request, MediaType incomingMediaType, Class targetType ) throws IOException {
- for ( HttpMessageConverter converter : httpMessageConverters ) {
- if ( converter.canRead( targetType, incomingMediaType ) ) {
- return (V) converter.read( targetType, request );
+ private V readIncoming(HttpInputMessage request, MediaType incomingMediaType, Class targetType)
+ throws IOException {
+ // Check custom converters first
+ for(HttpMessageConverter conv : config.getCustomConverters()) {
+ if(conv.canRead(targetType, incomingMediaType)) {
+ return (V)conv.read(targetType, request);
+ }
+ }
+ // Use our always-available default list of converters
+ for(HttpMessageConverter conv : httpMessageConverters) {
+ if(conv.canRead(targetType, incomingMediaType)) {
+ return (V)conv.read(targetType, request);
}
}
return null;
}
@SuppressWarnings({"unchecked"})
- private Map extractPropertiesLinkAware( String repoRel,
- Object entity,
- EntityMetadata entityMetadata,
- URI baseUri ) {
+ private Map extractPropertiesLinkAware(String repoRel,
+ Object entity,
+ EntityMetadata entityMetadata,
+ URI baseUri) {
final Map entityDto = new HashMap();
- for ( Map.Entry attrMeta : entityMetadata.embeddedAttributes().entrySet() ) {
+ for(Map.Entry attrMeta : entityMetadata.embeddedAttributes().entrySet()) {
String name = attrMeta.getKey();
- Object val = attrMeta.getValue().get( entity );
- if ( null != val ) {
- entityDto.put( name, val );
+ Object val = attrMeta.getValue().get(entity);
+ if(null != val) {
+ entityDto.put(name, val);
}
}
- for ( String attrName : entityMetadata.linkedAttributes().keySet() ) {
- URI uri = buildUri( baseUri, attrName );
- Link l = new SimpleLink( repoRel + "." + entity.getClass().getSimpleName() + "." + attrName, uri );
- List links = (List ) entityDto.get( LINKS );
- if ( null == links ) {
+ for(String attrName : entityMetadata.linkedAttributes().keySet()) {
+ URI uri = buildUri(baseUri, attrName);
+ Link l = new SimpleLink(repoRel + "." + entity.getClass().getSimpleName() + "." + attrName, uri);
+ List links = (List )entityDto.get(LINKS);
+ if(null == links) {
links = new ArrayList ();
- entityDto.put( LINKS, links );
+ entityDto.put(LINKS, links);
}
- links.add( l );
+ links.add(l);
}
return entityDto;
}
- private String viewName( String name ) {
- return "org.springframework.data.rest." + name;
- }
-
- private boolean shouldReturnLinks( String acceptHeader ) {
- if ( null != acceptHeader ) {
- List accept = MediaType.parseMediaTypes( acceptHeader );
- for ( MediaType mt : accept ) {
- if ( mt.getSubtype().startsWith( "x-spring-data-verbose" ) ) {
+ private boolean shouldReturnLinks(String acceptHeader) {
+ if(null != acceptHeader) {
+ List accept = MediaType.parseMediaTypes(acceptHeader);
+ for(MediaType mt : accept) {
+ if(mt.getSubtype().startsWith("x-spring-data-verbose")) {
return false;
- } else if ( mt.getSubtype().startsWith( "x-spring-data-compact" ) ) {
+ } else if(mt.getSubtype().startsWith("x-spring-data-compact")) {
return true;
- } else if ( mt.getSubtype().equals( "uri-list" ) ) {
+ } else if(mt.getSubtype().equals("uri-list")) {
return true;
}
}
@@ -1270,109 +1542,111 @@ public class RepositoryRestController
return false;
}
- private ResponseEntity> noConverterFoundError( Class> fromResponseType ) {
- return new ResponseEntity(
- String.format( "{\"message\": \"No converter found for class <%s>\"}", fromResponseType ),
- HttpStatus.INTERNAL_SERVER_ERROR
- );
+ private void publishEvent(E event) {
+ if(null != applicationContext) {
+ applicationContext.publishEvent(event);
+ }
+ }
+
+ private boolean notPagingParam(String name) {
+ return (!config.getPageParamName().equals(name)
+ && !config.getLimitParamName().equals(name)
+ && !config.getSortParamName().equals(name));
+ }
+
+ private ResponseEntity notFoundResponse(ServletServerHttpRequest request) throws IOException {
+ return negotiateResponse(request, HttpStatus.NOT_FOUND, new HttpHeaders(), null);
}
@SuppressWarnings({"unchecked"})
- private ResponseEntity negotiateResponse( final ServletServerHttpRequest request,
- final HttpStatus status,
- final HttpHeaders headers,
- final Object resource ) throws IOException {
+ private ResponseEntity negotiateResponse(final ServletServerHttpRequest request,
+ final HttpStatus status,
+ final HttpHeaders headers,
+ final Object resource) throws IOException {
- String jsonpParam = request.getServletRequest().getParameter( config.getJsonpParamName() );
+ String jsonpParam = request.getServletRequest().getParameter(config.getJsonpParamName());
String jsonpOnErrParam = null;
- if ( null != config.getJsonpOnErrParamName() ) {
- jsonpOnErrParam = request.getServletRequest().getParameter( config.getJsonpOnErrParamName() );
+ if(null != config.getJsonpOnErrParamName()) {
+ jsonpOnErrParam = request.getServletRequest().getParameter(config.getJsonpOnErrParamName());
}
- HttpStatus responseStatus = status;
- byte[] responseBody = null;
- if ( null != resource ) {
- List acceptableTypes = new ArrayList();
+ if(null == resource) {
+ return maybeWrapJsonp(status, jsonpParam, jsonpOnErrParam, headers, null);
+ }
- if ( !request.getHeaders().getAccept().isEmpty() &&
- !Arrays.equals(
- request.getHeaders().getAccept().toArray(),
- ALL_TYPES.toArray()
- ) ) {
- acceptableTypes.addAll( request.getHeaders().getAccept() );
- } else {
- acceptableTypes.add( MediaType.APPLICATION_JSON );
- }
-
- for ( MediaType acceptType : acceptableTypes ) {
- HttpMessageConverter converterToUse = null;
- for ( HttpMessageConverter conv : config.getCustomConverters() ) {
- if ( conv.canWrite( resource.getClass(), acceptType ) ) {
- converterToUse = conv;
- break;
+ MediaType acceptType = config.getDefaultMediaType();
+ HttpMessageConverter converter = findWriteConverter(resource.getClass(), acceptType);
+ // If an Accept header is specified that isn't the catch-all, try and find a converter for it.
+ if(!MediaTypes.ACCEPT_ALL_TYPES.equals(request.getHeaders().getAccept())) {
+ for(MediaType mt : request.getHeaders().getAccept()) {
+ if(null != (converter = findWriteConverter(resource.getClass(), mt))) {
+ if(!"*".equals(mt.getSubtype())) {
+ acceptType = mt;
}
- }
- if ( null == converterToUse ) {
- for ( HttpMessageConverter conv : httpMessageConverters ) {
- if ( conv.canWrite( resource.getClass(), acceptType ) ) {
- converterToUse = conv;
- break;
- }
- }
- }
-
- if ( null != converterToUse ) {
- final ByteArrayOutputStream bout = new ByteArrayOutputStream();
- converterToUse.write( resource, acceptType, new HttpOutputMessage() {
- @Override public OutputStream getBody() throws IOException {
- return bout;
- }
-
- @Override public HttpHeaders getHeaders() {
- return headers;
- }
- } );
-
- if ( null != jsonpParam || null != jsonpOnErrParam ) {
- headers.setContentType( JacksonUtil.APPLICATION_JAVASCRIPT );
- }
- responseBody = bout.toByteArray();
- } else {
- responseStatus = HttpStatus.NOT_ACCEPTABLE;
- headers.setContentType( MediaType.TEXT_PLAIN );
- StringBuilder sb = new StringBuilder();
- if ( null != jsonpOnErrParam ) {
- sb.append( "\"" );
- }
- for ( MediaType mt : availableMediaTypes ) {
- sb.append( mt.toString() ).append( '\n' );
- }
- if ( null != jsonpOnErrParam ) {
- sb.append( "\"" );
- }
- responseBody = sb.toString().getBytes();
+ break;
}
}
}
+ headers.setContentType(acceptType);
- if ( responseStatus.value() > 400 && (null != jsonpOnErrParam) ) {
- String jsonp = jsonpOnErrParam + "(" + responseStatus.value() + "," + (null == responseBody ? "null" : new String(
- responseBody )) + ")";
- responseBody = jsonp.getBytes();
- responseStatus = HttpStatus.OK;
- } else if ( null != jsonpParam ) {
- String jsonp = jsonpParam + "(" + (null == responseBody ? "null" : new String( responseBody )) + ")";
- responseBody = jsonp.getBytes();
+ if(null == converter) {
+ throw new HttpMessageNotWritableException("No HttpMessageConverter found to handle " + resource.getClass());
}
- if ( null == responseBody ) {
- headers.setContentLength( 0 );
- } else {
- headers.setContentLength( responseBody.length );
+ final ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ converter.write(resource, headers.getContentType(), new HttpOutputMessage() {
+ @Override public OutputStream getBody() throws IOException {
+ return bout;
+ }
+
+ @Override public HttpHeaders getHeaders() {
+ return headers;
+ }
+ });
+
+ return maybeWrapJsonp(status, jsonpParam, jsonpOnErrParam, headers, bout.toByteArray());
+ }
+
+ @SuppressWarnings({"unchecked"})
+ private HttpMessageConverter findWriteConverter(Class> type, MediaType mediaType) {
+ for(HttpMessageConverter conv : config.getCustomConverters()) {
+ if(conv.canWrite(type, mediaType)) {
+ return conv;
+ }
}
+ for(HttpMessageConverter conv : httpMessageConverters) {
+ if(conv.canWrite(type, mediaType)) {
+ return conv;
+ }
+ }
+ return null;
+ }
+ private ResponseEntity maybeWrapJsonp(HttpStatus status,
+ String jsonpParam,
+ String jsonpOnErrParam,
+ HttpHeaders headers,
+ byte[] body) {
- return new ResponseEntity( responseBody, headers, responseStatus );
+ byte[] responseBody = (null == body ? new byte[0] : body);
+ if(status.value() >= 400 && null != jsonpOnErrParam) {
+ status = HttpStatus.OK;
+ responseBody = String.format("%s(%s, %s)",
+ jsonpOnErrParam,
+ status.value(),
+ (null != body ? new String(body) : null))
+ .getBytes();
+ headers.setContentType(MediaTypes.APPLICATION_JAVASCRIPT);
+ } else if(null != jsonpParam) {
+ responseBody = String.format("%s(%s)",
+ jsonpParam,
+ (null != body ? new String(body) : null))
+ .getBytes();
+ headers.setContentType(MediaTypes.APPLICATION_JAVASCRIPT);
+ }
+ headers.setContentLength(responseBody.length);
+
+ return new ResponseEntity(responseBody, headers, status);
}
}
diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerAdapter.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerAdapter.java
index cdf80268c..109a18cfa 100644
--- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerAdapter.java
+++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerAdapter.java
@@ -13,27 +13,27 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
*/
public class RepositoryRestHandlerAdapter extends RequestMappingHandlerAdapter {
- public RepositoryRestHandlerAdapter( RepositoryRestConfiguration config ) {
- setCustomArgumentResolvers( Arrays.asList(
+ public RepositoryRestHandlerAdapter(RepositoryRestConfiguration config) {
+ setCustomArgumentResolvers(Arrays.asList(
new ServerHttpRequestMethodArgumentResolver(),
- new PagingAndSortingMethodArgumentResolver( config )
- ) );
+ new PagingAndSortingMethodArgumentResolver(config)
+ ));
// Add JSON converter for special Spring Data media type
MappingJacksonHttpMessageConverter json = new MappingJacksonHttpMessageConverter();
json.setSupportedMediaTypes(
- Arrays.asList( MediaType.APPLICATION_JSON, MediaType.valueOf( "application/x-spring-data+json" ) )
+ Arrays.asList(MediaType.APPLICATION_JSON, MediaType.valueOf("application/x-spring-data+json"))
);
- getMessageConverters().add( json );
+ getMessageConverters().add(json);
}
@Override public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
- @Override protected boolean supportsInternal( HandlerMethod handlerMethod ) {
- return super.supportsInternal( handlerMethod )
- && RepositoryRestController.class.isAssignableFrom( handlerMethod.getBeanType() );
+ @Override protected boolean supportsInternal(HandlerMethod handlerMethod) {
+ return super.supportsInternal(handlerMethod)
+ && RepositoryRestController.class.isAssignableFrom(handlerMethod.getBeanType());
}
}
diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMapping.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMapping.java
index 003efd6d4..0288d8a57 100644
--- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMapping.java
+++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMapping.java
@@ -23,7 +23,7 @@ public class RepositoryRestHandlerMapping extends RequestMappingHandlerMapping {
private EntityManagerFactory entityManagerFactory;
@Autowired(required = false)
private List repositoryExporters = Collections.emptyList();
- private Set repositoryNames = new HashSet();
+ private Set repositoryNames = new HashSet();
public RepositoryRestHandlerMapping() {
setOrder(Ordered.HIGHEST_PRECEDENCE);
@@ -31,18 +31,19 @@ public class RepositoryRestHandlerMapping extends RequestMappingHandlerMapping {
@SuppressWarnings({"unchecked"})
@Override
- protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
- if (repositoryNames.isEmpty() && !repositoryExporters.isEmpty()) {
- for (RepositoryExporter re : repositoryExporters) {
+ protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request)
+ throws Exception {
+ if(repositoryNames.isEmpty() && !repositoryExporters.isEmpty()) {
+ for(RepositoryExporter re : repositoryExporters) {
repositoryNames.addAll(re.repositoryNames());
}
}
String[] parts = lookupPath.split("/");
- if (parts.length == 0) {
+ if(parts.length == 0) {
// Root request
return super.lookupHandlerMethod(lookupPath, request);
} else {
- if (repositoryNames.contains(parts[1])) {
+ if(repositoryNames.contains(parts[1])) {
return super.lookupHandlerMethod(lookupPath, request);
} else {
return null;
@@ -55,7 +56,7 @@ public class RepositoryRestHandlerMapping extends RequestMappingHandlerMapping {
}
@Override protected void extendInterceptors(List interceptors) {
- if (null != entityManagerFactory) {
+ if(null != entityManagerFactory) {
OpenEntityManagerInViewInterceptor omivi = new OpenEntityManagerInViewInterceptor();
omivi.setEntityManagerFactory(entityManagerFactory);
interceptors.add(omivi);
diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestMvcConfiguration.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestMvcConfiguration.java
index 820572d53..b2a00d938 100644
--- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestMvcConfiguration.java
+++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestMvcConfiguration.java
@@ -29,7 +29,7 @@ public class RepositoryRestMvcConfiguration {
}
@Bean public JpaRepositoryExporter jpaRepositoryExporter() {
- if ( null == customJpaRepositoryExporter ) {
+ if(null == customJpaRepositoryExporter) {
return new JpaRepositoryExporter();
} else {
return customJpaRepositoryExporter;
@@ -37,19 +37,20 @@ public class RepositoryRestMvcConfiguration {
}
@Bean public ValidatingRepositoryEventListener validatingRepositoryEventListener() {
- if ( null == validatingRepositoryEventListener ) {
+ if(null == validatingRepositoryEventListener) {
return new ValidatingRepositoryEventListener();
} else {
return validatingRepositoryEventListener;
}
}
- @Bean public RepositoryRestController repositoryRestController() throws Exception {
+ @Bean public RepositoryRestController repositoryRestController()
+ throws Exception {
return new RepositoryRestController();
}
@Bean public RepositoryRestHandlerAdapter repositoryExporterHandlerAdapter() {
- return new RepositoryRestHandlerAdapter( repositoryRestConfig );
+ return new RepositoryRestHandlerAdapter(repositoryRestConfig);
}
@Bean public RepositoryRestHandlerMapping repositoryExporterHandlerMapping() {
diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ServerHttpRequestMethodArgumentResolver.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ServerHttpRequestMethodArgumentResolver.java
index 172525f48..7413df49f 100644
--- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ServerHttpRequestMethodArgumentResolver.java
+++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ServerHttpRequestMethodArgumentResolver.java
@@ -15,16 +15,17 @@ import org.springframework.web.method.support.ModelAndViewContainer;
*/
public class ServerHttpRequestMethodArgumentResolver implements HandlerMethodArgumentResolver {
- @Override public boolean supportsParameter( MethodParameter parameter ) {
- return ClassUtils.isAssignable( parameter.getParameterType(), ServletServerHttpRequest.class );
+ @Override public boolean supportsParameter(MethodParameter parameter) {
+ return ClassUtils.isAssignable(parameter.getParameterType(), ServletServerHttpRequest.class);
}
@Override
- public Object resolveArgument( MethodParameter parameter,
- ModelAndViewContainer mavContainer,
- NativeWebRequest webRequest,
- WebDataBinderFactory binderFactory ) throws Exception {
- return new ServletServerHttpRequest( (HttpServletRequest) webRequest.getNativeRequest() );
+ public Object resolveArgument(MethodParameter parameter,
+ ModelAndViewContainer mavContainer,
+ NativeWebRequest webRequest,
+ WebDataBinderFactory binderFactory)
+ throws Exception {
+ return new ServletServerHttpRequest((HttpServletRequest)webRequest.getNativeRequest());
}
}
diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/UriListHttpMessageConverter.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/UriListHttpMessageConverter.java
index d041cb1f2..ca3d2426f 100644
--- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/UriListHttpMessageConverter.java
+++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/UriListHttpMessageConverter.java
@@ -5,96 +5,101 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URI;
-import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.data.rest.core.Link;
+import org.springframework.data.rest.core.Links;
import org.springframework.data.rest.core.SimpleLink;
+import org.springframework.data.rest.repository.invoke.RepositoryMethodResponse;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
-import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
+import org.springframework.http.server.ServletServerHttpRequest;
/**
+ * A special {@link org.springframework.http.converter.HttpMessageConverter} that can take various input formats and
+ * produce a plain-text list of URIs (or read the same).
+ *
* @author Jon Brisbin
*/
public class UriListHttpMessageConverter extends AbstractHttpMessageConverter {
- public static final Charset DEFAULT_CHARSET = Charset.forName( "ISO-8859-1" );
-
public UriListHttpMessageConverter() {
- super( new MediaType( "text", "uri-list", DEFAULT_CHARSET ) );
+ super(MediaTypes.URI_LIST);
}
- @Override protected boolean supports( Class> clazz ) {
- return (List.class.isAssignableFrom( clazz )
- || Map.class.isAssignableFrom( clazz )
- || Links.class.isAssignableFrom( clazz ));
+ @Override protected boolean supports(Class> clazz) {
+ return (RepositoryMethodResponse.class.isAssignableFrom(clazz)
+ || List.class.isAssignableFrom(clazz)
+ || Map.class.isAssignableFrom(clazz)
+ || Links.class.isAssignableFrom(clazz));
}
@SuppressWarnings({"unchecked"})
@Override
- protected Object readInternal( Class> clazz,
- HttpInputMessage inputMessage )
+ protected Object readInternal(Class> clazz,
+ HttpInputMessage inputMessage)
throws IOException,
HttpMessageNotReadableException {
- String rel = inputMessage.getHeaders().getFirst( "x-spring-data-urilist-rel" );
- if ( null == rel ) {
- rel = inputMessage.getHeaders().getLocation().getPath().substring( 1 ).replaceAll( "/", "." );
+ String rel = inputMessage.getHeaders().getFirst("x-spring-data-urilist-rel");
+ if(null == rel && inputMessage instanceof ServletServerHttpRequest) {
+ rel = ((ServletServerHttpRequest)inputMessage).getURI().getPath().substring(1).replaceAll("/", ".");
}
- BufferedReader reader = new BufferedReader( new InputStreamReader( inputMessage.getBody() ) );
- String line = null;
- Object links = null;
+ BufferedReader reader = new BufferedReader(new InputStreamReader(inputMessage.getBody()));
+ String line;
+ Object links;
try {
links = clazz.newInstance();
- } catch ( InstantiationException e ) {
- throw new HttpMessageNotReadableException( e.getMessage(), e );
- } catch ( IllegalAccessException e ) {
- throw new HttpMessageNotReadableException( e.getMessage(), e );
+ } catch(InstantiationException e) {
+ throw new HttpMessageNotReadableException(e.getMessage(), e);
+ } catch(IllegalAccessException e) {
+ throw new HttpMessageNotReadableException(e.getMessage(), e);
}
- while ( null != (line = reader.readLine()) ) {
- if ( links instanceof Links ) {
- ((Links) links).add( new SimpleLink( rel, URI.create( line.trim() ) ) );
- } else if ( links instanceof List ) {
- ((List) links).add( new SimpleLink( rel, URI.create( line.trim() ) ) );
- } else if ( links instanceof Map ) {
- List l = (List) ((Map) links).get( "_links" );
- if ( null == l ) {
+ while(null != (line = reader.readLine())) {
+ if(links instanceof Links) {
+ ((Links)links).add(new SimpleLink(rel, URI.create(line.trim())));
+ } else if(links instanceof List) {
+ ((List)links).add(new SimpleLink(rel, URI.create(line.trim())));
+ } else if(links instanceof Map) {
+ List l = (List)((Map)links).get("_links");
+ if(null == l) {
l = new ArrayList();
- ((Map) links).put( "_links", l );
+ ((Map)links).put("_links", l);
}
- l.add( new SimpleLink( rel, URI.create( line.trim() ) ) );
+ l.add(new SimpleLink(rel, URI.create(line.trim())));
}
}
return links;
}
@Override
- protected void writeInternal( Object links, HttpOutputMessage outputMessage )
+ protected void writeInternal(Object links, HttpOutputMessage outputMessage)
throws IOException,
HttpMessageNotWritableException {
OutputStream body = outputMessage.getBody();
- if ( links instanceof Links ) {
- for ( SimpleLink link : ((Links) links).getLinks() ) {
- body.write( link.href().toASCIIString().getBytes() );
- body.write( '\n' );
+ if(links instanceof Links) {
+ for(Link link : ((Links)links).getLinks()) {
+ body.write(link.href().toASCIIString().getBytes());
+ body.write('\n');
}
- } else if ( links instanceof List ) {
- for ( Object o : (List) links ) {
- if ( o instanceof Link ) {
- body.write( ((Link) o).href().toASCIIString().getBytes() );
+ } else if(links instanceof List) {
+ for(Object o : (List)links) {
+ if(o instanceof Link) {
+ body.write(((Link)o).href().toASCIIString().getBytes());
} else {
- body.write( o.toString().getBytes() );
+ body.write(o.toString().getBytes());
}
- body.write( '\n' );
+ body.write('\n');
}
- } else if ( links instanceof Map ) {
- writeInternal( ((Map) links).get( "_links" ), outputMessage );
+ } else if(links instanceof Map) {
+ writeInternal(((Map)links).get("_links"), outputMessage);
+ } else if(links instanceof RepositoryMethodResponse) {
+ writeInternal(((RepositoryMethodResponse)links).getLinks(), outputMessage);
}
}
diff --git a/spring-data-rest-webmvc/src/main/webapp/WEB-INF/web.xml b/spring-data-rest-webmvc/src/main/webapp/WEB-INF/web.xml
deleted file mode 100644
index 659c823d2..000000000
--- a/spring-data-rest-webmvc/src/main/webapp/WEB-INF/web.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
- exporter
- org.springframework.data.rest.webmvc.RepositoryRestExporterServlet
- 1
-
-
-
- exporter
- /*
-
-
-
diff --git a/spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/BaseSpec.groovy b/spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/BaseSpec.groovy
new file mode 100644
index 000000000..e5cd2bb74
--- /dev/null
+++ b/spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/BaseSpec.groovy
@@ -0,0 +1,106 @@
+package org.springframework.data.rest.webmvc.spec
+
+import org.codehaus.jackson.map.ObjectMapper
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.context.ApplicationContext
+import org.springframework.data.rest.test.webmvc.Address
+import org.springframework.data.rest.test.webmvc.AddressRepository
+import org.springframework.data.rest.test.webmvc.ApplicationConfig
+import org.springframework.data.rest.test.webmvc.Person
+import org.springframework.data.rest.test.webmvc.PersonRepository
+import org.springframework.data.rest.test.webmvc.TestRepositoryEventListener
+import org.springframework.data.rest.webmvc.RepositoryRestController
+import org.springframework.data.rest.webmvc.RepositoryRestMvcConfiguration
+import org.springframework.http.ResponseEntity
+import org.springframework.http.server.ServletServerHttpRequest
+import org.springframework.mock.web.MockHttpServletRequest
+import org.springframework.orm.jpa.EntityManagerHolder
+import org.springframework.test.context.ContextConfiguration
+import org.springframework.web.util.UriComponentsBuilder
+import spock.lang.Specification
+
+import javax.persistence.EntityManagerFactory
+
+import static org.springframework.transaction.support.TransactionSynchronizationManager.*
+
+/**
+ * @author Jon Brisbin
+ */
+@ContextConfiguration(classes = [ApplicationConfig, RepositoryRestMvcConfiguration])
+abstract class BaseSpec extends Specification {
+
+ @Autowired ApplicationContext appCtx
+ @Autowired TestRepositoryEventListener listener
+ @Autowired RepositoryRestController controller
+ @Autowired EntityManagerFactory emf
+ @Autowired PersonRepository people
+ @Autowired AddressRepository addresses
+ UriComponentsBuilder baseUri
+ ObjectMapper mapper = new ObjectMapper()
+
+ def setup() {
+ baseUri = UriComponentsBuilder.fromUriString("http://localhost:8080/data")
+
+ if (!hasResource(emf)) {
+ bindResource(emf, new EntityManagerHolder(emf.createEntityManager()))
+ }
+
+ for (Person p : people.findAll()) {
+ people.delete(p)
+ }
+ for (Address a : addresses.findAll()) {
+ addresses.delete(a)
+ }
+ }
+
+ def readJson(ResponseEntity entity) {
+ mapper.readValue((byte[]) entity.body, Map)
+ }
+
+ def createJsonRequest(method, path, query, obj) {
+ createRequest(method, path, null, "application/json", mapper.writeValueAsString(obj))
+ }
+
+ def createUriListRequest(method, path, query, obj) {
+ createRequest(method, path, null, "text/uri-list", obj.join("\n"))
+ }
+
+ def createRequest(method, path, query) {
+ createRequest(method, path, null, null, null)
+ }
+
+ def createRequest(method, path, query, contentType, content) {
+ def req = new MockHttpServletRequest(
+ serverPort: 8080,
+ requestURI: "/data/$path",
+ method: method
+ )
+ if (query) {
+ req.queryString = URLEncoder.encode(
+ query.collect {k, v -> "$k=$v"}.join("&")
+ )
+ }
+ if (contentType) {
+ req.contentType = contentType
+ }
+ if (content) {
+ req.content = content
+ }
+
+ new ServletServerHttpRequest(req)
+ }
+
+ def newPerson() {
+ people.save(new Person(name: "John Doe", addresses: [newAddress("Univille")]))
+ }
+
+ def newAddress(city) {
+ addresses.save(new Address(
+ ["1234 W. 1st St."] as String[],
+ city,
+ "ST",
+ "12345"
+ ))
+ }
+
+}
diff --git a/spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/EventsSpec.groovy b/spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/EventsSpec.groovy
new file mode 100644
index 000000000..c08ded450
--- /dev/null
+++ b/spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/EventsSpec.groovy
@@ -0,0 +1,47 @@
+package org.springframework.data.rest.webmvc.spec
+
+import org.springframework.data.rest.repository.RepositoryConstraintViolationException
+import org.springframework.data.rest.test.webmvc.Person
+import org.springframework.http.HttpStatus
+
+/**
+ * @author Jon Brisbin
+ */
+class EventsSpec extends BaseSpec {
+
+ def "cannot save invalid entity"() {
+
+ given:
+ def person = new Person()
+ def request = createJsonRequest("POST", "people", null, person)
+
+ when:
+ controller.create(request, baseUri, "people")
+
+ then:
+ thrown(RepositoryConstraintViolationException)
+
+ }
+
+ def "captures before and after events"() {
+
+ given:
+ def person = new Person(name: "John Doe")
+ def request = createJsonRequest("POST", "people", ["returnBody": "true"], person)
+ def persId
+ listener.handlers << { evt, p ->
+ if (evt == "afterSave")
+ persId = "${p.id}"
+ }
+
+ when:
+ def response = controller.create(request, baseUri, "people")
+ def returnedId = response.headers.getFirst('Location').tokenize("/").last()
+
+ then:
+ response.statusCode == HttpStatus.CREATED
+ persId == returnedId
+
+ }
+
+}
diff --git a/spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/RelationshipsSpec.groovy b/spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/RelationshipsSpec.groovy
new file mode 100644
index 000000000..4dcc898d2
--- /dev/null
+++ b/spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/RelationshipsSpec.groovy
@@ -0,0 +1,40 @@
+package org.springframework.data.rest.webmvc.spec
+
+import org.springframework.http.HttpStatus
+
+/**
+ * @author Jon Brisbin
+ */
+class RelationshipsSpec extends BaseSpec {
+
+ def "saves entity relationship"() {
+
+ given:
+ def person = newPerson()
+ def persId = person.id
+ def addr = newAddress("Smallville")
+ def addrId = addr.id
+ def request = createUriListRequest(
+ "POST",
+ "people/$persId/addresses",
+ null,
+ [baseUri.pathSegment("address", "$addrId").build().toUriString()]
+ )
+
+ when:
+ def response = controller.updatePropertyOfEntity(request, baseUri, "people", "$persId", "addresses")
+
+ then:
+ response.statusCode == HttpStatus.CREATED
+
+ when:
+ request = createRequest("GET", "people/$persId/addresses/$addrId", null)
+ response = controller.linkedEntity(request, baseUri, "people", "$persId", "addresses", "$addrId")
+
+ then:
+ response.statusCode == HttpStatus.OK
+ readJson(response).city == "Smallville"
+
+ }
+
+}
diff --git a/spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/RepositoryRestControllerSpec.groovy b/spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/RepositoryRestControllerSpec.groovy
index f4d976cda..7c9131042 100644
--- a/spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/RepositoryRestControllerSpec.groovy
+++ b/spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/RepositoryRestControllerSpec.groovy
@@ -21,6 +21,7 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
import org.springframework.ui.ExtendedModelMap
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext
import org.springframework.web.util.UriComponentsBuilder
+import spock.lang.Ignore
import spock.lang.Shared
import spock.lang.Specification
@@ -29,6 +30,7 @@ import javax.persistence.EntityManagerFactory
/**
* @author Jon Brisbin
*/
+@Ignore
class RepositoryRestControllerSpec extends Specification {
@Shared
diff --git a/spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/TopLevelEntitySpec.groovy b/spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/TopLevelEntitySpec.groovy
new file mode 100644
index 000000000..35088ac2d
--- /dev/null
+++ b/spring-data-rest-webmvc/src/test/groovy/org/springframework/data/rest/webmvc/spec/TopLevelEntitySpec.groovy
@@ -0,0 +1,39 @@
+package org.springframework.data.rest.webmvc.spec
+
+import org.springframework.data.rest.test.webmvc.Person
+import org.springframework.http.HttpStatus
+
+/**
+ * @author Jon Brisbin
+ */
+class TopLevelEntitySpec extends BaseSpec {
+
+ def "saves top-level entity"() {
+
+ given:
+ def person = new Person(name: "John Doe")
+ def request = createJsonRequest("POST", "people/1", null, person)
+
+ when:
+ def response = controller.createOrUpdate(request, baseUri, "people", "1")
+
+ then:
+ response.statusCode == HttpStatus.CREATED
+
+ }
+
+ def "retrieves top-level entity"() {
+
+ given:
+ def person = newPerson()
+ def request = createRequest("GET", "people/${person.id}", null)
+
+ when:
+ def response = controller.entity(request, baseUri, "people", "${person.id}")
+
+ then:
+ response.statusCode == HttpStatus.OK
+
+ }
+
+}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/RestBuilder.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/RestBuilder.java
index 83a1e9b85..61d55f31d 100644
--- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/RestBuilder.java
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/RestBuilder.java
@@ -38,12 +38,12 @@ public class RestBuilder {
private ConversionService conversionService = new DefaultConversionService();
private ClientHttpRequestFactory requestFactory;
- private RestTemplate restTemplate;
+ private RestTemplate restTemplate;
private HttpHeaders headers = new HttpHeaders();
private MediaType contentType;
private Class> responseType = byte[].class;
- private Map uriParams;
- private Object body;
+ private Map uriParams;
+ private Object body;
private Closure errorHandler;
public RestBuilder() {
@@ -57,7 +57,7 @@ public class RestBuilder {
public Object call(Closure cl) {
RestBuilder b = null != requestFactory ? new RestBuilder(requestFactory) : new RestBuilder();
- if (null != errorHandler) {
+ if(null != errorHandler) {
b.setErrorHandler(errorHandler);
}
b.conversionService = conversionService;
@@ -78,7 +78,7 @@ public class RestBuilder {
@SuppressWarnings({"unchecked"})
public Object post(String url) {
- if (responseType == URI.class) {
+ if(responseType == URI.class) {
return restTemplate.postForLocation(maybeAddParams(url), new HttpEntity(body, headers));
} else {
return restTemplate.postForEntity(maybeAddParams(url), new HttpEntity(body, headers), responseType);
@@ -87,7 +87,7 @@ public class RestBuilder {
@SuppressWarnings({"unchecked"})
public Object put(String url) {
- if (null != uriParams) {
+ if(null != uriParams) {
restTemplate.put(maybeAddParams(url), new HttpEntity(body, headers), uriParams);
} else {
restTemplate.put(maybeAddParams(url), new HttpEntity(body, headers));
@@ -118,23 +118,24 @@ public class RestBuilder {
@SuppressWarnings({"unchecked"})
public Object date(String date) {
- for (String fmt : DATE_FORMATS) {
+ for(String fmt : DATE_FORMATS) {
try {
Date dte = new SimpleDateFormat(fmt).parse(date);
headers.setDate(dte.getTime());
break;
- } catch (ParseException e) {}
+ } catch(ParseException e) {
+ }
}
return this;
}
@SuppressWarnings({"unchecked"})
public Object header(String key, Object val) {
- if (null != val) {
- if (val instanceof List) {
- headers.put(key, (List) val);
- } else if (ClassUtils.isAssignable(val.getClass(), String.class)) {
- headers.set(key, (String) val);
+ if(null != val) {
+ if(val instanceof List) {
+ headers.put(key, (List)val);
+ } else if(ClassUtils.isAssignable(val.getClass(), String.class)) {
+ headers.set(key, (String)val);
} else {
headers.set(key, conversionService.convert(val, String.class));
}
@@ -156,7 +157,7 @@ public class RestBuilder {
@SuppressWarnings({"unchecked"})
public Object param(String key, String value) {
- if (null == uriParams) {
+ if(null == uriParams) {
uriParams = new HashMap();
}
uriParams.put(key, value);
@@ -180,9 +181,10 @@ public class RestBuilder {
public Object setErrorHandler(Closure errorHandler) {
this.errorHandler = errorHandler;
- if (null != errorHandler) {
+ if(null != errorHandler) {
this.restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
- @Override public void handleError(ClientHttpResponse response) throws IOException {
+ @Override public void handleError(ClientHttpResponse response)
+ throws IOException {
RestBuilder.this.errorHandler.call(response);
}
});
@@ -198,12 +200,12 @@ public class RestBuilder {
@SuppressWarnings({"unchecked"})
private String maybeAddParams(String url) {
StringBuffer buff = new StringBuffer(url);
- if (null != uriParams) {
+ if(null != uriParams) {
buff.append("?");
- for (Map.Entry entry : ((Map) uriParams).entrySet()) {
+ for(Map.Entry entry : ((Map)uriParams).entrySet()) {
try {
buff.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue(), "UTF-8"));
- } catch (UnsupportedEncodingException e) {
+ } catch(UnsupportedEncodingException e) {
throw new IllegalStateException(e);
}
}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/Address.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/Address.java
index e271b1c95..ac7bfbbb2 100644
--- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/Address.java
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/Address.java
@@ -3,6 +3,7 @@ package org.springframework.data.rest.test.webmvc;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
+import javax.persistence.ManyToOne;
/**
* @author Jon Brisbin
@@ -10,11 +11,13 @@ import javax.persistence.Id;
@Entity
public class Address {
- @Id @GeneratedValue private Long id;
- private String[] lines;
- private String city;
- private String province;
- private String postalCode;
+ @Id @GeneratedValue private Long id;
+ private String[] lines;
+ private String city;
+ private String province;
+ private String postalCode;
+ @ManyToOne
+ private Person person;
public Address() {
}
@@ -62,4 +65,12 @@ public class Address {
this.postalCode = postalCode;
}
+ public Person getPerson() {
+ return person;
+ }
+
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+
}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/AddressRepository.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/AddressRepository.java
index 7884a924b..8ee8d3169 100644
--- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/AddressRepository.java
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/AddressRepository.java
@@ -1,9 +1,13 @@
package org.springframework.data.rest.test.webmvc;
import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.query.Param;
/**
* @author Jon Brisbin
*/
public interface AddressRepository extends CrudRepository {
+
+ public Address findByPerson(@Param("person") Person person);
+
}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/ApplicationConfig.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/ApplicationConfig.java
new file mode 100644
index 000000000..a2909fa3d
--- /dev/null
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/ApplicationConfig.java
@@ -0,0 +1,64 @@
+package org.springframework.data.rest.test.webmvc;
+
+import javax.persistence.EntityManagerFactory;
+import javax.sql.DataSource;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
+import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
+import org.springframework.orm.jpa.JpaDialect;
+import org.springframework.orm.jpa.JpaTransactionManager;
+import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
+import org.springframework.orm.jpa.vendor.Database;
+import org.springframework.orm.jpa.vendor.HibernateJpaDialect;
+import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+/**
+ * @author Jon Brisbin
+ */
+@Configuration
+@ComponentScan(basePackageClasses = ApplicationConfig.class)
+@EnableJpaRepositories
+@EnableTransactionManagement
+public class ApplicationConfig {
+
+ @Bean public DataSource dataSource() {
+ EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
+ return builder.setType(EmbeddedDatabaseType.HSQL).build();
+ }
+
+ @Bean public EntityManagerFactory entityManagerFactory() {
+ HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
+ vendorAdapter.setDatabase(Database.HSQL);
+ vendorAdapter.setGenerateDdl(true);
+
+ LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
+ factory.setJpaVendorAdapter(vendorAdapter);
+ factory.setPackagesToScan(getClass().getPackage().getName());
+ factory.setDataSource(dataSource());
+
+ factory.afterPropertiesSet();
+
+ return factory.getObject();
+ }
+
+ @Bean public JpaDialect jpaDialect() {
+ return new HibernateJpaDialect();
+ }
+
+ @Bean public PlatformTransactionManager transactionManager() {
+ JpaTransactionManager txManager = new JpaTransactionManager();
+ txManager.setEntityManagerFactory(entityManagerFactory());
+ return txManager;
+ }
+
+ @Bean public TestRepositoryEventListener testRepositoryEventListener() {
+ return new TestRepositoryEventListener();
+ }
+
+}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/Family.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/Family.java
index e5f3e7208..b1834137d 100644
--- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/Family.java
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/Family.java
@@ -12,10 +12,10 @@ import javax.persistence.OneToMany;
@Entity
public class Family {
- @Id @GeneratedValue private Long id;
- private String surname;
+ @Id @GeneratedValue private Long id;
+ private String surname;
@OneToMany
- private List members;
+ private List members;
public Long getId() {
return id;
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/FamilyRepository.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/FamilyRepository.java
index aa4a5a174..96ebc1492 100644
--- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/FamilyRepository.java
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/FamilyRepository.java
@@ -5,5 +5,6 @@ import org.springframework.data.repository.CrudRepository;
/**
* @author Jon Brisbin
*/
-public interface FamilyRepository extends CrudRepository {
+public interface FamilyRepository
+ extends CrudRepository {
}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/Person.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/Person.java
index f6e14890c..f072d3427 100644
--- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/Person.java
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/Person.java
@@ -5,25 +5,25 @@ import java.util.Map;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
+import javax.persistence.MapKey;
import javax.persistence.OneToMany;
import javax.persistence.Version;
-import org.springframework.data.rest.repository.annotation.RestResource;
-
/**
* @author Jon Brisbin
*/
@Entity
public class Person {
- @Id @GeneratedValue private Long id;
- private String name;
+ @Id @GeneratedValue private Long id;
+ private String name;
@Version
- private Long version;
+ private Long version;
@OneToMany
- private List addresses;
+ private List addresses;
@OneToMany
- private Map profiles;
+ @MapKey(name = "type")
+ private Map profiles;
public Person() {
}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/PersonLoader.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/PersonLoader.java
index e8c67010b..31e7afb52 100644
--- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/PersonLoader.java
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/PersonLoader.java
@@ -9,9 +9,10 @@ import org.springframework.beans.factory.InitializingBean;
/**
* @author Jon Brisbin
*/
-public class PersonLoader implements InitializingBean {
+public class PersonLoader
+ implements InitializingBean {
- private PersonRepository personRepository;
+ private PersonRepository personRepository;
private ProfileRepository profileRepository;
private AddressRepository addressRepository;
@@ -39,7 +40,8 @@ public class PersonLoader implements InitializingBean {
this.addressRepository = addressRepository;
}
- @Override public void afterPropertiesSet() throws Exception {
+ @Override public void afterPropertiesSet()
+ throws Exception {
Address pers1addr = addressRepository.save(new Address(new String[]{"1234 W. 1st St."}, "Univille", "ST", "12345"));
Map pers1profiles = new HashMap();
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/PersonRepository.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/PersonRepository.java
index 3c7561c5e..77d8fb5f9 100644
--- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/PersonRepository.java
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/PersonRepository.java
@@ -4,7 +4,6 @@ import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
-import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.repository.annotation.RestResource;
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/PersonValidator.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/PersonValidator.java
index 36dbc5ce8..c738f9872 100644
--- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/PersonValidator.java
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/PersonValidator.java
@@ -10,7 +10,8 @@ import org.springframework.validation.Validator;
/**
* @author Jon Brisbin
*/
-public class PersonValidator implements Validator {
+public class PersonValidator
+ implements Validator {
private static final Logger LOG = LoggerFactory.getLogger(PersonValidator.class);
@@ -19,7 +20,7 @@ public class PersonValidator implements Validator {
}
@Override public void validate(Object target, Errors errors) {
- Person p = (Person) target;
+ Person p = (Person)target;
LOG.debug("validating Person " + p);
ValidationUtils.rejectIfEmpty(errors, "name", "field.name.required", "Field 'name' cannot be blank.");
}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/Profile.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/Profile.java
index 1931603a9..a8e3957c7 100644
--- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/Profile.java
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/Profile.java
@@ -3,6 +3,7 @@ package org.springframework.data.rest.test.webmvc;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
+import javax.persistence.ManyToOne;
/**
* @author Jon Brisbin
@@ -10,9 +11,11 @@ import javax.persistence.Id;
@Entity
public class Profile {
- @Id @GeneratedValue private Long id;
- private String type;
- private String url;
+ @Id @GeneratedValue private Long id;
+ private String type;
+ private String url;
+ @ManyToOne
+ private Person person;
public Profile() {
}
@@ -38,29 +41,37 @@ public class Profile {
this.url = url;
}
+ public Person getPerson() {
+ return person;
+ }
+
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+
@Override public boolean equals(Object o) {
- if (!(o instanceof Profile)) {
+ if(!(o instanceof Profile)) {
return false;
}
- Profile p2 = (Profile) o;
+ Profile p2 = (Profile)o;
boolean idEq;
- if (null != id) {
+ if(null != id) {
idEq = id.equals(p2.id);
} else {
idEq = p2.id == null;
}
boolean typeEq;
- if (null != type) {
+ if(null != type) {
typeEq = type.equals(p2.type);
} else {
typeEq = p2.type == null;
}
boolean urlEq;
- if (null != url) {
+ if(null != url) {
urlEq = url.equals(p2.url);
} else {
urlEq = p2.url == null;
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/ProfileRepository.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/ProfileRepository.java
index fc4ba7b3d..9dc8ccd55 100644
--- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/ProfileRepository.java
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/ProfileRepository.java
@@ -1,9 +1,13 @@
package org.springframework.data.rest.test.webmvc;
import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.query.Param;
/**
* @author Jon Brisbin
*/
public interface ProfileRepository extends CrudRepository {
+
+ public Address findByPerson(@Param("person") Person person);
+
}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/RestExporterWebInitializer.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/RestExporterWebInitializer.java
new file mode 100644
index 000000000..9b3ee90c5
--- /dev/null
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/RestExporterWebInitializer.java
@@ -0,0 +1,33 @@
+package org.springframework.data.rest.test.webmvc;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+
+import org.springframework.data.rest.webmvc.RepositoryRestExporterServlet;
+import org.springframework.web.WebApplicationInitializer;
+import org.springframework.web.context.ContextLoaderListener;
+import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+import org.springframework.web.servlet.DispatcherServlet;
+
+/**
+ * @author Jon Brisbin
+ */
+public class RestExporterWebInitializer implements WebApplicationInitializer {
+
+ @Override public void onStartup(ServletContext servletContext) throws ServletException {
+ // Create the 'root' Spring application context
+ AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
+ rootContext.register(ApplicationConfig.class);
+
+ // Manage the lifecycle of the root application context
+ servletContext.addListener(new ContextLoaderListener(rootContext));
+
+ // Register and map the dispatcher servlet
+ DispatcherServlet servlet = new RepositoryRestExporterServlet();
+ ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", servlet);
+ dispatcher.setLoadOnStartup(1);
+ dispatcher.addMapping("/*");
+ }
+
+}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/TestRepositoryEventListener.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/TestRepositoryEventListener.java
new file mode 100644
index 000000000..8d00e4abe
--- /dev/null
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/TestRepositoryEventListener.java
@@ -0,0 +1,32 @@
+package org.springframework.data.rest.test.webmvc;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import groovy.lang.Closure;
+import org.springframework.data.rest.repository.context.AbstractRepositoryEventListener;
+
+/**
+ * @author Jon Brisbin
+ */
+public class TestRepositoryEventListener extends AbstractRepositoryEventListener {
+
+ private List handlers = new ArrayList();
+
+ public List getHandlers() {
+ return handlers;
+ }
+
+ @Override protected void onBeforeSave(Object entity) {
+ for(Closure cl : handlers) {
+ cl.call("beforeSave", entity);
+ }
+ }
+
+ @Override protected void onAfterSave(Object entity) {
+ for(Closure cl : handlers) {
+ cl.call("afterSave", entity);
+ }
+ }
+
+}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/UuidTestRepository.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/UuidTestRepository.java
index 205c6214a..e3fe4fe1f 100644
--- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/UuidTestRepository.java
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/test/webmvc/UuidTestRepository.java
@@ -9,5 +9,6 @@ import org.springframework.data.rest.repository.annotation.RestResource;
* @author Jon Brisbin
*/
@RestResource(exported = false)
-public interface UuidTestRepository extends CrudRepository {
+public interface UuidTestRepository
+ extends CrudRepository {
}
diff --git a/spring-data-rest-webmvc/src/test/resources/META-INF/spring-data-rest/repositories-export.xml b/spring-data-rest-webmvc/src/test/resources/META-INF/spring-data-rest/repositories-export.xml
index 4edd1a862..910313d5b 100644
--- a/spring-data-rest-webmvc/src/test/resources/META-INF/spring-data-rest/repositories-export.xml
+++ b/spring-data-rest-webmvc/src/test/resources/META-INF/spring-data-rest/repositories-export.xml
@@ -1,14 +1,10 @@
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/spring-data-rest-webmvc/src/test/resources/load_data.sh b/spring-data-rest-webmvc/src/test/resources/load_data.sh
index 250e9386f..0c78835d7 100755
--- a/spring-data-rest-webmvc/src/test/resources/load_data.sh
+++ b/spring-data-rest-webmvc/src/test/resources/load_data.sh
@@ -6,6 +6,7 @@ curl -d 'http://localhost:8080/people/1
http://localhost:8080/people/2' -H "Content-Type: text/uri-list" http://localhost:8080/family/1/members
curl -d '{"postalCode":"12345","province":"MO","lines":["1 W 1st St."],"city":"Univille"}' -H "Content-Type: application/json" http://localhost:8080/address
curl -d "http://localhost:8080/address/1" -H "Content-Type: text/uri-list" http://localhost:8080/people/1/addresses
+curl -d "http://localhost:8080/people/1" -X PUT -H "Content-Type: text/uri-list" http://localhost:8080/address/1/person
curl -d '{"postalCode":"54321","province":"MO","lines":["2 W 1st St."],"city":"Univille"}' -H "Content-Type: application/json" http://localhost:8080/address
curl -d "http://localhost:8080/address/2" -H "Content-Type: text/uri-list" http://localhost:8080/people/2/addresses
curl -d '{"type" : "twitter", "url": "#!/johndoe"}' -H "Content-Type: application/json" http://localhost:8080/profile