diff --git a/.gitignore b/.gitignore
index 021421903..549b071d5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,4 @@ build/
.classpath
.project
.settings
-.log
+*.log
diff --git a/build.gradle b/build.gradle
index 3ea1000b5..4b45b49ae 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,52 +1,107 @@
+// spring-data-rest - Spring Data REST Exporter
apply plugin: "base"
-allprojects {
+ext {
+ gradleScriptDir = "${rootProject.projectDir}/gradle"
+
+ // Logging
+ slf4jVersion = "1.7.2"
+ logbackVersion = "1.0.7"
+
+ // Spring
+ springVersion = "3.1.3.RELEASE"
+ //springVersion = "3.2.0.RELEASE"
+ hateoasVersion = "0.4.0.BUILD-SNAPSHOT"
+ springPluginVersion = "0.8.0.BUILD-SNAPSHOT"
+ springSecurityVersion = "3.1.3.RELEASE"
+ sdCommonsVersion = "1.5.0.BUILD-SNAPSHOT"
+ sdJpaVersion = "1.3.0.BUILD-SNAPSHOT"
+ sdMongoVersion = "1.2.0.BUILD-SNAPSHOT"
+ sdGemfireVersion = "1.3.0.BUILD-SNAPSHOT"
+
+ // Libraries
+ guavaVersion = "13.0.1"
+ jacksonVersion = "2.1.2"
+ jodaVersion = "2.1"
+ hibernateVersion = "4.1.7.Final"
+ hibernateValidatorVersion = "4.3.0.Final"
+
+ // Supporting libraries
+ cglibVersion = "2.2.2"
+
+ // Testing
+ junitVersion = "4.11"
+ hamcrestVersion = "1.3"
+ jmockVersion = "2.6.0-RC2"
+ jettyVersion = "8.1.8.v20121106"
+}
+
+buildscript {
+ repositories {
+ maven { url "http://repo.springsource.org/plugins-release" }
+ }
+ dependencies {
+ classpath "org.springframework.build.gradle:docbook-reference-plugin:0.2.2"
+ }
+}
+
+configure(allprojects) {
+ apply plugin: "java"
+ apply plugin: "maven"
apply plugin: "idea"
apply plugin: "eclipse"
- apply plugin: "maven"
+ apply from: "${gradleScriptDir}/ide.gradle"
group = "org.springframework.data"
- version = "$sdRestVersion"
configurations.all {
exclude group: "commons-logging"
exclude module: "slf4j-log4j12"
}
+ project.sourceCompatibility = 1.6
+ project.targetCompatibility = 1.6
+
+ [compileJava, compileTestJava]*.options*.compilerArgs = ["-Xlint:none", "-g"]
+
+ sourceSets.test.resources.srcDirs = ["src/test/resources", "src/test/java"]
+
repositories {
- //maven { url "http://repo.springsource.org/libs-snapshot" }
- //maven { url "http://repo.springsource.org/libs-milestone" }
maven { url "http://repo.springsource.org/libs-release" }
+ //maven { url "http://repo.springsource.org/libs-milestone" }
+ maven { url "http://repo.springsource.org/libs-snapshot" }
}
+ dependencies {
+ // Logging
+ compile "org.slf4j:slf4j-api:$slf4jVersion"
+ runtime "org.slf4j:jcl-over-slf4j:$slf4jVersion"
+
+ // Testing
+ testCompile "junit:junit-dep:$junitVersion"
+ testCompile "org.hamcrest:hamcrest-library:$hamcrestVersion"
+ testCompile "org.jmock:jmock-junit4:$jmockVersion"
+ testCompile "org.jmock:jmock-legacy:$jmockVersion"
+ testCompile "org.springframework:spring-test:$springVersion"
+ testRuntime "org.springframework:spring-context-support:$springVersion"
+ testRuntime "ch.qos.logback:logback-classic:$logbackVersion"
+ }
}
configure(subprojects) { subproject ->
- apply plugin: "java"
- apply plugin: "groovy"
- apply from: "${rootProject.projectDir}/maven.gradle"
-
- configurations {
- compile.extendsFrom providedCompile
- }
-
- [compileJava, compileTestJava]*.options*.compilerArgs = ["-Xlint:unchecked"]
-
- project.sourceCompatibility = 1.6
- project.targetCompatibility = 1.6
+ apply from: "${gradleScriptDir}/maven.gradle"
javadoc {
options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
options.author = true
options.header = subproject.name
- //options.overview = "${projectDir}/src/main/java/overview.html"
+ //options.overview = "${projectDir}/src/api/overview.html"
}
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = "sources"
from sourceSets.main.allJava
}
-
task javadocJar(type: Jar) {
classifier = "javadoc"
from javadoc
@@ -56,68 +111,230 @@ configure(subprojects) { subproject ->
archives sourcesJar
archives javadocJar
}
+}
+
+project("spring-data-rest-core") {
+ description = "Spring Data REST core components."
+
+ configurations {
+ compile.extendsFrom providedCompile
+ }
dependencies {
- groovy "org.codehaus.groovy:groovy:$groovyVersion"
-
- // Logging
- compile "org.slf4j:slf4j-api:$slf4jVersion"
- runtime "org.slf4j:jcl-over-slf4j:$slf4jVersion"
- runtime "ch.qos.logback:logback-classic:$logbackVersion"
-
- // Jackson JSON
- compile "org.codehaus.jackson:jackson-mapper-asl:$jacksonVersion"
+ // Google Guava
+ compile "com.google.guava:guava:$guavaVersion"
// Spring
- compile("org.springframework:spring-beans:$springVersion") { force = true }
- 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:$cglibVersion"
- // Testing
- testCompile("org.codehaus.groovy:groovy-all:$groovyVersion") { force = true }
- testCompile "org.spockframework:spock-core:$spockVersion"
- testCompile "org.spockframework:spock-spring:$spockVersion"
- testCompile "org.hamcrest:hamcrest-library:1.3"
- testCompile "org.springframework:spring-test:$springVersion"
- testRuntime "org.springframework:spring-context-support:$springVersion"
- testCompile "org.mockito:mockito-core:1.8.5"
- }
+ // Spring HATEOAS
+ compile("org.springframework.hateoas:spring-hateoas:$hateoasVersion") {
+ exclude module: "spring-webmvc"
+ }
+ // Spring Data Commons
+ providedCompile("org.springframework.data:spring-data-commons:$sdCommonsVersion") {
+ exclude module: "slf4j-api"
+ exclude module: "jcl-over-slf4j"
+ }
+ }
+}
+
+project("spring-data-rest-repository") {
+ description = "Spring Data REST Repository integration."
+
+ dependencies {
+ // Exporter core
+ compile project(":spring-data-rest-core")
+
+ // JSR-305
+ compile "com.google.code.findbugs:jsr305:2.0.1"
+
+ // JODA
+ compile("joda-time:joda-time:$jodaVersion", optional)
+
+ // Jackson JSON
+ compile "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
+ compile("com.fasterxml.jackson.datatype:jackson-datatype-joda:$jacksonVersion", optional)
+ compile("com.fasterxml.jackson.datatype:jackson-datatype-hibernate4:$jacksonVersion", optional)
+
+ // ROME
+ //compile "rome:rome:1.0"
+
+ // Spring
+ compile("org.springframework:spring-tx:$springVersion") { force = true }
+
+ // JPA
+ compile("org.hibernate.javax.persistence:hibernate-jpa-2.0-api:1.0.1.Final", optional)
+
+ // Spring Plugin
+ compile "org.springframework.plugin:spring-plugin-core:$springPluginVersion"
+
+ // Spring Data
+ compile("org.springframework.data:spring-data-jpa:$sdJpaVersion", optional)
+ compile("org.springframework.data:spring-data-mongodb:$sdMongoVersion", optional)
+
+ // JSR 303 Validation
+ compile "javax.validation:validation-api:1.0.0.GA"
+
+ // Testing
+ testCompile "org.hibernate:hibernate-entitymanager:$hibernateVersion"
+ testCompile "org.hsqldb:hsqldb:2.2.8"
+ testRuntime "org.hibernate:hibernate-validator:$hibernateValidatorVersion"
+ }
+}
+
+project("spring-data-rest-webmvc") {
+ description = "Spring Data REST MVC components."
+
+ dependencies {
+ // Repository Exporter support
+ compile project(":spring-data-rest-repository")
+
+ // Spring
+ compile("org.springframework:spring-webmvc:$springVersion") { force = true }
+
+ // APIS
+ compile("javax.servlet:javax.servlet-api:3.0.1", provided)
+
+ // Testing
+ testCompile "org.eclipse.jetty:jetty-servlet:$jettyVersion"
+ testCompile "org.eclipse.jetty:jetty-webapp:$jettyVersion"
+ testCompile "org.mozilla:rhino:1.7R4"
+ }
+}
+
+project("spring-data-rest-example") {
+ apply plugin: "war"
+ description = "Spring Data REST example web application."
+
+ dependencies {
+ compile project(":spring-data-rest-webmvc")
+
+ // Logging
+ runtime "ch.qos.logback:logback-classic:$logbackVersion"
+
+ // JPA
+ compile "org.hibernate.javax.persistence:hibernate-jpa-2.0-api:1.0.1.Final"
+
+ // Spring Data
+ compile "org.springframework.data:spring-data-jpa:$sdJpaVersion"
+ compile "org.springframework.data:spring-data-mongodb:$sdMongoVersion"
+ compile "org.springframework.data:spring-data-gemfire:$sdGemfireVersion"
+
+ // Spring Security
+ compile "org.springframework.security:spring-security-config:$springSecurityVersion"
+ compile "org.springframework.security:spring-security-web:$springSecurityVersion"
+
+ // JODA
+ compile "joda-time:joda-time:$jodaVersion"
+
+ // Jackson
+ compile "com.fasterxml.jackson.datatype:jackson-datatype-joda:$jacksonVersion"
+ compile "com.fasterxml.jackson.datatype:jackson-datatype-hibernate4:$jacksonVersion"
+
+ // Hibernate
+ runtime "org.hibernate:hibernate-entitymanager:$hibernateVersion"
+ runtime "org.hibernate:hibernate-validator:$hibernateValidatorVersion"
+
+ // HSQL
+ runtime "org.hsqldb:hsqldb:2.2.8"
+ }
}
configure(rootProject) {
+ apply plugin: "docbook-reference"
- task javadoc(type: Javadoc) {
- title = "Spring Data REST ${version} API"
- source subprojects.collect { project -> project.sourceSets.main.allJava }
- classpath = files(subprojects.collect { project -> project.sourceSets.main.compileClasspath })
- destinationDir = new File(buildDir, "javadoc")
+ description = "Spring Data REST Exporter"
+
+ reference {
+ sourceDir = file("src/reference/docbook")
+ pdfFilename = "spring-data-rest-reference.pdf"
+ }
+
+ // don"t publish the default jar for the root project
+ configurations.archives.artifacts.clear()
+
+ task api(type: Javadoc) {
+ group = "Documentation"
+ description = "Generates aggregated Javadoc API documentation."
+ title = "${rootProject.description} ${version} API"
options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
options.author = true
- options.header = "Spring Data REST ${version} API"
- //options.overview = "${projectDir}/src/main/java/overview.html"
+ options.header = rootProject.description
+ options.overview = "src/api/overview.html"
+ options.splitIndex = true
+ options.linksOffline "http://docs.oracle.com/javase/6/docs/api/", "http://docs.oracle.com/javase/6/docs/api/"
+ options.linksOffline "http://static.springsource.org/spring/docs/3.1.x/javadoc-api/", "http://static.springsource.org/spring/docs/3.1.x/javadoc-api/"
+ options.linksOffline "http://static.springsource.org/spring-data/data-commons/docs/current/api/", "http://static.springsource.org/spring-data/data-commons/docs/current/api/"
+
+ source subprojects.collect { project ->
+ project.sourceSets.main.allJava
+ }
+
+ destinationDir = new File(buildDir, "api")
+ classpath = files(subprojects.collect { project ->
+ project.sourceSets.main.compileClasspath
+ })
+ maxMemory = "1024m"
}
-}
+ task docsZip(type: Zip) {
+ group = "Distribution"
+ baseName = "spring-data-rest"
+ classifier = "docs"
+ description = "Builds -${classifier} archive containing api and reference " +
+ "for deployment to http://static.springframework.org/spring-data-rest/docs."
-task wrapper(type: Wrapper) { gradleVersion = "1.1" }
-
-idea {
- module {
- downloadJavadoc = false
- downloadSources = true
+ from("src/dist") {
+ include "changelog.txt"
+ }
+ from(api) {
+ into "api"
+ }
+ from(reference) {
+ into "reference"
+ }
}
- project {
- ipr {
- withXml { xml ->
- xml.node.component.find { it.@name == "VcsDirectoryMappings" }.mapping.@vcs = "Git"
- xml.node.component.find { it.@name == "ProjectRootManager" }.output.@url = "file://\$PROJECT_DIR\$/build"
+
+ dependencies {
+ // For integration testing
+ testCompile project(":spring-data-rest-core")
+ testCompile project(":spring-data-rest-repository")
+ testCompile project(":spring-data-rest-webmvc")
+ }
+
+ idea {
+ module {
+ downloadJavadoc = false
+ downloadSources = true
+ }
+ project {
+ ipr {
+ withXml { xml ->
+ xml.node.component.find { it.@name == "VcsDirectoryMappings" }.mapping.@vcs = "Git"
+ xml.node.component.find { it.@name == "ProjectRootManager" }.output.@url = "file://\$PROJECT_DIR\$/build"
+ }
}
}
}
}
+
+task wrapper(type: Wrapper) {
+ description = "Generates gradlew[.bat] scripts"
+ gradleVersion = "1.3"
+
+ doLast() {
+ def gradleOpts = "-XX:MaxPermSize=1024m -Xmx1024m"
+ def gradleBatOpts = "$gradleOpts -XX:MaxHeapSize=256m"
+ File wrapperFile = file("gradlew")
+ wrapperFile.text = wrapperFile.text.replace("DEFAULT_JVM_OPTS=",
+ "GRADLE_OPTS=\"$gradleOpts \$GRADLE_OPTS\"\nDEFAULT_JVM_OPTS=")
+ File wrapperBatFile = file("gradlew.bat")
+ wrapperBatFile.text = wrapperBatFile.text.replace("set DEFAULT_JVM_OPTS=",
+ "set GRADLE_OPTS=$gradleBatOpts %GRADLE_OPTS%\nset DEFAULT_JVM_OPTS=")
+ }
+}
diff --git a/doc/add_to_existing_app.md b/doc/add_to_existing_app.md
deleted file mode 100644
index 242ca598a..000000000
--- a/doc/add_to_existing_app.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# Adding Spring Data REST to an existing Spring MVC Application
-
-If you have an existing Spring MVC application and you'd like to integrate Spring Data REST, it's actually very easy.
-
-Somewhere in your Spring MVC configuration (most likely where you configure your MVC resources) add a bean reference to the JavaConfig class that is responsible for configuring the `RepositoryRestController`. The class name is `org.springframework.data.rest.webmvc.RepositoryRestMvcConfiguration`. In XML this would look like:
-
-
-
-When your ApplicationContext comes across this bean definition it will bootstrap the necessary Spring MVC resources to fully-configure the controller for exporting the Repositories it finds in that ApplicationContext and any parent contexts.
-
-### More on required configuration
-
-There are a couple Spring MVC resources that Spring Data REST depends on that must be configured correctly for it to work inside an existing Spring MVC application. We've tried to isolate those resources from whatever similar resources already exist within your application, but it may be that you want to customize some of the behavior of Spring Data REST by modifying these MVC components.
-
-The most important things that we configure especially for use by Spring Data REST include:
-
-#### RepositoryRestHandlerMapping
-
-We register a custom `HandlerMapping` instance that responds only to the `RepositoryRestController` and only if a path is meant to be handled by Spring Data REST. In order to keep paths that are meant to be handled by your application separate from those handled by Spring Data REST, this custom HandlerMapping inspects the URL path and checks to see if a Repository has been exported under that name. If it has, it allows the request to be handled by Spring Data REST. If there is no Repository exported under that name, it returns `null`, which just means "let other HandlerMapping instances try to service this request".
-
-Basically this means that Spring Data REST will always be first in line when it comes time to map a URL path and your existing application will never get a chance to service a request that is meant for a Repository. For example, if you have a Repository exported under the name "person", then all requests to your application that start with "/person" will be handled by Spring Data REST and your application will never see that request. If your Repository is exported under a different name, however (like "people"), then requests to "/people" will go to Spring Data REST and requests to "/person" will be handled by your application.
\ No newline at end of file
diff --git a/doc/changing_json.md b/doc/changing_json.md
deleted file mode 100644
index 28d50edf0..000000000
--- a/doc/changing_json.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# Customizing the JSON output
-
-Sometimes in your application you need to provide links to other resources from a particular entity. For example, a
-`Customer` response might be enriched with links to a current shopping cart, or links to manage resources related to
-that entity. Spring Data REST provides integration with [Spring HATEOAS](https://github.com/SpringSource/spring-hateoas)
-and provides an extension hook for users to alter the representation of resources going out to the client.
-
-### The ResourceProcessor interface
-
-Spring HATEOAS defines a `ResourceProcessor>` interface for processing entities. All beans of type
-`ResourceProcessor>` will be automatically picked up by the Spring Data REST exporter and triggered when
-serializing an entity of type `T`. For example, to define a processor for a `Person` entity, add a `@Bean` to your
-ApplicationContext like the following (which is taken from the Spring Data REST tests):
-
- @Bean public ResourceProcessor> personProcessor() {
- return new ResourceProcessor>() {
- @Override public Resource process(Resource resource) {
- resource.add(new Link("http://localhost:8080/people", "added-link"));
- return resource;
- }
- };
- }
-
-
-### Adding Links
-
-It's possible to add links to the default representation of an entity by simply calling `resource.add(Link)` like the
-example above. Any links you add to the `Resource` will be added to the final output.
-
-### Customizing the representation
-
-The Spring Data REST exporter executes any discovered `ResourceProcessor`s before it creates the output representation.
-It does this by registering a `Converter` instance with an internal `ConversionService`. This is the
-component responsible for creating the links to referenced entities (e.g. those objects under the "links" property in
-the object's JSON representation). It takes an `@Entity` and iterates over its properties, creating links for those
-properties that are managed by a `Repository` and copying across any embedded or simple properties.
-
-If your project needs to have output in a different format, however, it's possible to completely replace the default
-outgoing JSON representation with your own. If you register your own `ConversionService` in the ApplicationContext and
-register your own `Converter`, then you can return a `Resource` implementation of your choosing.
diff --git a/doc/configuring_json.md b/doc/configuring_json.md
deleted file mode 100644
index c44decac4..000000000
--- a/doc/configuring_json.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# Adding custom (de)serializers to Jackson's ObjectMapper
-
-Sometimes the behavior of the Spring Data REST's ObjectMapper, which has been specially configured to use intelligent serializers that can turn domain objects into links and back again, may not handle your domain model correctly. There are so many ways one can structure your data that you may find your own domain model isn't being translated to JSON correctly. It's also sometimes not practical in these cases to try and support a complex domain model in a generic way. Sometimes, depending on the complexity, it's not even possible to offer a generic solution.
-
-So to accommodate the largest percentage of the use cases, Spring Data REST tries very hard to render your object graph correctly. It will try and serialize unmanaged beans as normal POJOs and it will try and create links to managed beans where that's necessary. But if your domain model doesn't easily lend itself to reading or writing plain JSON, you may want to configure Jackson's ObjectMapper with your own custom type mappings and (de)serializers.
-
-### Abstract class registration
-
-One key configuration point you might need to hook into is when you're using an abstract class (or an interface) in your domain model. Jackson won't know by default what implementation to create for an interface. Take the following example:
-
- @Entity
- public class MyEntity {
-
- @OneToMany
- private List interfaces;
-
- }
-
-In a default configuration, Jackson has no idea what class to instantiate when POSTing new data to the exporter. This is something you'll need to tell Jackson either through an annotation, or, more cleanly, by registering a type mapping using a [Module](http://wiki.fasterxml.com/JacksonFeatureModules).
-
-Any `Module` bean declared within the scope of your `ApplicationContext` will be picked up by the exporter and registered with its `ObjectMapper`. To add this special abstract class type mapping, create a `Module` bean and in the `setupModule` method, add an appropriate `TypeResolver`:
-
- public class MyCustomModule extends SimpleModule {
-
- private MyCustomModule() {
- super("MyCustomModule", new Version(1, 0, 0, "SNAPSHOT"));
- }
-
- @Override public void setupModule(SetupContext context) {
- context.addAbstractTypeResolver(
- new SimpleAbstractTypeResolver().addMapping(MyInterface.class, MyInterfaceImpl.class)
- );
- }
-
- }
-
-Once you have access to the `SetupContext` object in your `Module`, you can do all sorts of cool things to configure Jackon's JSON mapping. You can read more about how `Module`s work on Jackson's wiki: [http://wiki.fasterxml.com/JacksonFeatureModules](http://wiki.fasterxml.com/JacksonFeatureModules)
-
-### Adding custom serializers for domain types
-
-If you want to (de)serialize a domain type in a special way, you can register your own implementations with Jackson's `ObjectMapper` and the Spring Data REST exporter will transparently handle those domain objects correctly.
-
-To add serializers, from your `setupModule` method implementation, do something like the following:
-
- @Override public void setupModule(SetupContext context) {
- SimpleSerializers serializers = new SimpleSerializers();
- SimpleDeserializers deserializers = new SimpleDeserializers();
-
- serializers.addSerializer(MyEntity.class, new MyEntitySerializer());
- deserializers.addDeserializer(MyEntity.class, mew MyEntityDeserializer());
-
- context.addSerializers(serializers);
- context.addDeserializers(deserializers);
- }
-
-Now Spring Data REST will correctly handle your domain objects in case they are too complex for the 80% generic use case that Spring Data REST tries to cover.
\ No newline at end of file
diff --git a/doc/configuring_path.md b/doc/configuring_path.md
deleted file mode 100644
index 91b47c95e..000000000
--- a/doc/configuring_path.md
+++ /dev/null
@@ -1,148 +0,0 @@
-# Configuring the REST URL path
-
-Configuring the segments of the URL path under which the resources of a JPA Repository are exported is simple. You just add an annotation at the class level and/or at the query method level.
-
-By default, the exporter will expose your CrudRepository using the class name stripped of the word "Repository". So a Repository defined as follows:
-
- public interface PersonRepository extends CrudRepository {}
-
-Will, by default, be exposed under the URL:
-
- http://localhost:8080/person/
-
-To change how the Repository is exported, add a `@RestResource` annotation at the class level:
-
- @RestResource(path = "people")
- public interface PersonRepository extends CrudRepository {}
-
-Now the Repository will be accessible under the URL:
-
- http://localhost:8080/people/
-
-If you have query methods defined, those also default to be exposed by their name:
-
- public interface PersonRepository extends CrudRepository {
-
- public List findByName(String name);
-
- }
-
-This would be exposed under the URL:
-
- http://localhost:8080/person/search/findByName
-
-_NOTE: All query method resources are exposed under the resource `search`._
-
-To change the segment of the URL under which this query method is exposed, use the `@RestResource` annotation again:
-
- @RestResource(path = "people")
- public interface PersonRepository extends CrudRepository {
-
- @RestResource(path = "names")
- public List findByName(String name);
-
- }
-
-Now this query method will be exposed under the URL:
-
- http://localhost:8080/people/search/names
-
-### Handling rels
-
-Since these resources are all discoverable, you can also affect how the "rel" attribute is displayed in the links sent out by the exporter.
-
-For instance, in the default configuration, if you issue a request to `http://localhost:8080/person/search` to find out what query methods are exposed, you'll get back a list of links:
-
- {
- "_links" : [ {
- "rel" : "person.findByName",
- "href" : "http://localhost:8080/person/search/findByName"
- } ]
- }
-
-To change the rel value, use the `rel` property on the `@RestResource` annotation:
-
- @RestResource(path = "people")
- public interface PersonRepository extends CrudRepository {
-
- @RestResource(path = "names", rel = "names")
- public List findByName(String name);
-
- }
-
-This would result in a link value of:
-
- {
- "_links" : [ {
- "rel" : "person.names",
- "href" : "http://localhost:8080/people/search/names"
- } ]
- }
-
-The Repository's rel can also be changed by using the `@RestResource` property:
-
- @RestResource(path = "people", rel = "people")
- public interface PersonRepository extends CrudRepository {
-
- @RestResource(path = "names", rel = "names")
- public List findByName(String name);
-
- }
-
-This would result in a link value of:
-
- {
- "_links" : [ {
- "rel" : "people.names",
- "href" : "http://localhost:8080/people/search/names"
- } ]
- }
-
-### Hiding certain Repositories, query methods, or fields
-
-You may not want a certain Repository, a query method on a Repository, or a field of your entity to be exported at all. To tell the exporter to not export these items, annotate them with `@RestResource` and set `exported = false`.
-
-For example, to skip exporting a Repository:
-
- @RestResource(exported = false)
- public interface PersonRepository extends CrudRepository {
- }
-
-To skip exporting a query method:
-
- @RestResource(path = "people", rel = "people")
- public interface PersonRepository extends CrudRepository {
-
- @RestResource(exported = false)
- public List findByName(String name);
-
- }
-
-Or to skip exporting a field:
-
- @Entity
- public class Person {
- @Id @GeneratedValue private Long id;
- @OneToMany
- @RestResource(exported = false)
- private Map profiles;
- }
-
-### Hiding Repository CRUD methods
-
-If you don't want to expose a save or delete method on your `CrudRepository`, you can use the `@RestResource(exported = false)` setting by overriding the method you want to turn off and placing the annotation on the overriden version. For example, to prevent HTTP users from invoking the delete methods of `CrudRepository`, override all of them and add the annotation to the overriden methods.
-
- @RestResource(path = "people", rel = "people")
- public interface PersonRepository extends CrudRepository {
-
- @Override
- @RestResource(exported = false)
- void delete(Long id);
-
- @Override
- @RestResource(exported = false)
- void delete(Person entity);
-
- }
-
-NOTE: It is important that you override _both_ delete methods as the exporter currently uses a somewhat naive algorithm for determing which CRUD method to use in the interest of faster runtime performance. It's not currently possible to turn off the version of delete which takes an ID but leave exported the version that takes an entity instance. For the time being, you can either export the delete methods or not. If you want turn them off, then just keep in mind you have to annotate both versions with `exported = false`.
\ No newline at end of file
diff --git a/doc/curl_usage.md b/doc/curl_usage.md
deleted file mode 100644
index d42a4fc75..000000000
--- a/doc/curl_usage.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# Example API usage with curl
-
-Here is some example usage of the REST API with `curl`. First we'll add a `Family`:
-
- $ curl -v -d '{"surname" : "Doe"}' -H "Content-Type: application/json" http://localhost:8080/family
-
- HTTP/1.1 201 Created
- Location: http://localhost:8080/family/1
- Content-Length: 0
-
-Now we'll add a `Person`:
-
- $ curl -v -d '{"name" : "John Doe"}' -H "Content-Type: application/json" http://localhost:8080/people
-
- HTTP/1.1 201 Created
- Location: http://localhost:8080/people/1
- Content-Length: 0
-
-Now we'll add this person to the "Doe" family we added above:
-
- $ curl -v -d 'http://localhost:8080/people/1' -H "Content-Type: text/uri-list" http://localhost:8080/family/1/members
-
- HTTP/1.1 201 Created
- Content-Length: 0
-
-Notice that we don't return a `Location` when we add items to a referenced collection because we can add N numbers of items (here we're just adding one) so the `Location` header wouldn't be very meaningful as you couldn't match which URL you POSTed with the corresponding URL in the header.
-
-Now that we have some links created, let's query them so our user agent can keep track of them:
-
- $ curl -v http://localhost:8080/family/1/members
-
- HTTP/1.1 200 OK
- Content-Type: application/json;charset=ISO-8859-1
- Content-Length: 118
-
- {
- "_links" : [ {
- "rel" : "family.Family.Person.1",
- "href" : "http://localhost:8080/family/1/members/1"
- } ]
- }
-
-We can continue adding other top-level entities by sending JSON data and can add links to referenced entities by sending `text/uri-list` data with the URIs to those other top-level entities.
diff --git a/doc/embedded_entities.md b/doc/embedded_entities.md
deleted file mode 100644
index f21b3443c..000000000
--- a/doc/embedded_entities.md
+++ /dev/null
@@ -1,62 +0,0 @@
-# Embedded Entity references in complex object graphs
-
-Sometimes it's necessary to populate the incoming JSON with references to pre-existing @Entity objects. Often, this is because of referential integrity constraints. Consider the following relationship between two entities:
-
- @Entity
- public class Person {
- // ... person's properties
- }
-
- @Entity
- public class Address {
-
- @OneToOne(optional = false)
- private Person person;
-
- }
-
-Because of the `optional = false` on the `@OneToOne` annotation, I have to include a reference to an existing `Person` entity if I want to create a new `Address`.
-
-If you pull up the list of `Person` links using your user agent (Javascript, for instance), you'll want to save the link object that refers to the `Person` instance you're interested in. For example, if I list the `Person`s in the database and use the compact JSON format:
-
- curl -v -H "Accept: application/x-spring-data-compact+json" http://localhost:8080/people
-
-I'll get back the link objects I need to reference this entity again (this link is the same as the "self" link that appears in other places):
-
- {
- "links" : [ {
- "rel" : "people.Person",
- "href" : "http://localhost:8080/people/2"
- }, {
- "rel" : "people.Person",
- "href" : "http://localhost:8080/people/1"
- }, {
- "rel" : "people.search",
- "href" : "http://localhost:8080/people/search"
- } ],
- "content" : [ ],
- "page" : {
- "number" : 1,
- "size" : 20,
- "totalPages" : 1,
- "totalElements" : 2
- }
- }
-
-If you present the user with, for example, a drop-down combo box of the `people.Person` type links, and keep track of their selection, then you could just include that object in a new JSON object something like the following:
-
- {
- "postalCode": "12345",
- "province": "MO",
- "lines": ["1 W 1st St."],
- "city": "Univille",
- "person": {
- "rel" : "people.Person",
- "href" : "http://localhost:8080/people/1"
- }
- }
-
-You'll remember from the entity definition that the relationship between `Address` and `Person` is not optional. In that case, you'd simply include a JSON link object that refers to the entity you're interested in wherever that entity is supposed to appear. In this case, as the value of the "person" property. You can now POST this JSON to the server to create a new `Address` instance, with the "person" properly populated:
-
- curl -v -X POST -d '...json data...' http://localhost:8080/address
-
diff --git a/doc/handling_events.md b/doc/handling_events.md
deleted file mode 100644
index 04d52b556..000000000
--- a/doc/handling_events.md
+++ /dev/null
@@ -1,110 +0,0 @@
-# Handling ApplicationEvents in the REST Exporter
-
-There are six different events that the REST exporter emits throughout the process of working with an entity. Those are:
-
-* BeforeSaveEvent
-* AfterSaveEvent
-* BeforeLinkSaveEvent
-* AfterLinkSaveEvent
-* BeforeDeleteEvent
-* AfterDeleteEvent
-
-### ApplicationListener
-
-There is an abstract class you can subclass which listens for these kinds of events and calls
-the appropriate method based on the event type. You just override the methods for
-the events you're interested in.
-
- public class BeforeSaveEventListener extends AbstractRepositoryEventListener {
-
- @Override public void onBeforeSave(Object entity) {
- ... logic to handle inspecting the entity before the Repository saves it
- }
-
- @Override public void onAfterDelete(Object entity) {
- ... send a message that this entity has been deleted
- }
-
- }
-
-One thing to note with this approach, however, is that it makes no distinction based on
-the type of the entity. You'll have to inspect that yourself.
-
-### Annotated Handler
-
-Another approach is to use an annotated handler, which does filter events based on domain type.
-
-To declare a handler, create a POJO and put the `@RepositoryEventHandler` annotation on it.
-This tells the classpath scanner that this class needs to be inspected for handler methods.
-
-Once it finds a class with this annotation, it iterates over the exposed methods and looks for
-annotations that correspond to the event you're interested in. For example, to handle BeforeSaveEvents
-in an annotated POJO for different kinds of domain types, you'd define your class like this:
-
- @RepositoryEventHandler
- public class PersonEventHandler {
-
- @HandleBeforeSave(Person.class) public void handlePersonSave(Person p) {
- ... you can now deal with Person in a type-safe way
- }
-
- @HandleBeforeSave(Profile.class) public void handleProfileSave(Profile p) {
- ... you can now deal with Profile in a type-safe way
- }
-
- }
-
-You can also declare the domain type at the class level:
-
- @RepositoryEventHandler(Person.class)
- public class PersonEventHandler {
-
- @HandleBeforeSave public void handleBeforeSave(Person p) {
- ...
- }
-
- @HandleAfterDelete public void handleAfterDelete(Person p) {
- ...
- }
-
- }
-
-To actually get your handler invoked, however, you need to declare an instance of it in your
-ApplicationContext. The classpath scanner will look for event handlers and build up information
-about them, but it won't actually wire a handler to accept events unless there's an instance of
-it declared in your ApplicationContext.
-
-(In JavaConfig style):
-
- @Configuration
- public class RepositoryConfiguration {
-
- @Bean PersonEventHandler personEventHandler() {
- return new PersonEventHandler();
- }
-
- }
-
-When you have your beans properly declared, you need to declare an instance of the ApplicationListener.
-You can pass the base package of the packages you want searched for handlers in the constructor.
-
- @Configuration
- public class RepositoryConfiguration {
-
- @Bean PersonEventHandler personEventHandler() {
- return new PersonEventHandler();
- }
-
- @Bean AnnotatedHandlerRepositoryEventListener repositoryEventListener() {
- return new AnnotatedHandlerRepositoryEventListener("com.mycompany.repository.handlers");
- }
-
- }
-
-(In XML style):
-
-
-
-
-
-
diff --git a/doc/jsonp.md b/doc/jsonp.md
deleted file mode 100644
index 945be04d7..000000000
--- a/doc/jsonp.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# JSONP Support in Spring Data REST
-
-Spring Data REST supports [JSONP](http://en.wikipedia.org/wiki/JSONP) for doing safe cross-domain Ajax. JSONP support is integrated into the exporter so all you need to do to take advantage of it is pass the appropriate URL parameter. The default parameter is `callback`. So to get query method results wrapped with a call to your Javascript function, add `?callback=my_jsonp_callback` to the URL:
-
- curl -v http://localhost:8080/people/search/findByName?name=John+Doe&callback=my_json_callback
-
-Which will result in:
-
- HTTP/1.1 200 OK
- Content-Type: application/javascript
- Content-Length: ...
-
- my_jsonp_callback({
- "results": [ ... ],
- "_links": [ ... ]
- })
-
-### Configuring the URL parameter
-
-To configure what URL parameter is used, set the `jsonpParamName` property on your `org.springframework.data.rest.webmvc.RepositoryRestConfiguration` bean definition. In JavaConfig this would look like:
-
- @Bean public RepositoryRestConfiguration restConfig() {
- return new RepositoryRestConfiguration().
- setJsonpParamName("jsonp");
- }
-
-This would mean the above URL would become:
-
- curl -v http://localhost:8080/people/search/findByName?name=John+Doe&jsonp=my_json_callback
-
-## JSONP-E Handling Errors
-
-It's usually not possible to easily handle server errors with JSONP. This is because many JSONP frameworks use a script tag insertion to perform cross-domain Ajax. If the JSONP request results in an HTTP 400 Bad Request, for example, no javascript will be evaluated because the page is considered in error.
-
-To deftly handle server errors using JSONP, you need to set a value on the `jsonpOnErrParamName` REST exporter configuration property (which is defaulted to `null`, which means don't handle errors). If this value is set, the exception handling code will look for a URL query string parameter of that name and use that javascript function to call as the error handler. The way it does this is by changing the HTTP status code from, for example 400, to 200 (OK). It then wraps the error message with a call to your javascript function and sends the original HTTP status code as the first parameter.
-
-For example, if a call to POST a new entity results in a validation error, the server will return a 400 Bad Request. If the `jsonpOnErrParamName` is specified and you send that URL parameter, it will instead return a 200 and call your javascript function. Assuming I have `jsonpOnErrParamName` set to "errback", I would trigger this error handling like this:
-
- curl -v -d '...bad json data...' http://localhost:8080/people?errback=my_jsonp_error_handler
-
-Which would result in:
-
- HTTP/1.1 200 OK
- Content-Type: application/javascript
- Content-Length: ...
-
- my_jsonp_error_handler(400, {
- "message": "Validation failed on property 'name'!",
- "cause": { ... }
- })
\ No newline at end of file
diff --git a/doc/main_wiki.md b/doc/main_wiki.md
deleted file mode 100644
index 929538d5b..000000000
--- a/doc/main_wiki.md
+++ /dev/null
@@ -1,301 +0,0 @@
-# Spring Data JPA Repository Web Exporter
-
-The Spring Data JPA Repository Web Exporter allows you to export your [JPA Repositories](http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html/#jpa.repositories) as a RESTful web application. The exporter exposes the CRUD methods of a [CrudRepository](http://static.springsource.org/spring-data/data-commons/docs/current/api/org/springframework/data/repository/CrudRepository.html) for doing basic entity management. Relationships can also be managed between linked entities. The exporter is deployed as a traditional Spring MVC Controller, which means all the traditional Spring MVC tools are available to work with the Web Exporter (like Spring Security, for instance).
-
-### Installation
-
-#### Servlet environment
-
-Deployment of the Spring Data Web Exporter is extremely flexible. You can build a WAR file for deploying in a Servlet 2.5 or Servlet 3.0 environment. You can drop the spring-data-rest-webmvc.war artifact into an existing Servlet 3.0 application.
-
-Start by cloning the base web application project: [https://github.com/SpringSource/spring-data-rest-webmvc](https://github.com/SpringSource/spring-data-rest-webmvc). This sample application contains a `web.xml` file in `src/main/webapp/WEB-INF/servlet-2.5-web.xml` for deployment to pre-servlet-3 containers. The prefered way to configure the exporter, though, is using the XML-free Servlet 3.0 version. Tomcat 7 and Jetty 8 both support deploying this project directly.
-
- git clone https://github.com/SpringSource/spring-data-rest-webmvc.git
- cd spring-data-rest-webmvc
- ./gradlew war
-
-Deploy the built WAR file to your servlet container:
-
- cp build/libs/spring-data-rest-webmvc-1.0.0.RELEASE.war $TOMCAT_HOME/webapps/data.war
- cd $TOMCAT_HOME
- bin/catalina.sh run
-
-You can also run the project directly a Tomcat web container embedded in the build:
-
- ./gradlew tomcatRun
-
- The WAR file has a couple example domain classes and exposes a couple repositories by default. You can verify that this configuration is working by issuing an HTTP GET to the root of the web application:
-
- curl -v http://localhost:8080/spring-data-rest-webmvc/
-
- In return, you should see:
-
- > GET /data/ HTTP/1.1
- > User-Agent: curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8r zlib/1.2.3
- > Host: localhost:8080
- > Accept: */*
- >
- < HTTP/1.1 200 OK
- < Content-Type: application/json;charset=ISO-8859-1
- < Content-Length: 257
- <
- {
- "links" : [ {
- "rel" : "address",
- "href" : "http://localhost:8080/spring-data-rest-webmvc/address"
- }, {
- "rel" : "person",
- "href" : "http://localhost:8080/spring-data-rest-webmvc/person"
- }, {
- "rel" : "profile",
- "href" : "http://localhost:8080/spring-data-rest-webmvc/profile"
- } ]
- }
-
-### Export Repositories
-
-The preferred method to configure the Spring Data REST Exporter is to use the JavaConfig annotations. There is an example ApplicationConfig in the example application you can follow. You want to make sure the configuration class with the `@EnableJpaRepositores` annotation on it is loaded by the servlet context loader. Either use the special `RepositoryRestExporterServlet` or a `DispatcherServlet` the the appropriate `contextConfigLocation` set (refer to the `RepositoryRestExporterServlet` for more information).
-
- @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;
- }
-
- }
-
-Your `WebApplicationInitializer` class would look like this:
-
- public class RestExporterWebInitializer implements WebApplicationInitializer {
-
- @Override public void onStartup(ServletContext ctx) throws ServletException {
-
- AnnotationConfigWebApplicationContext rootCtx = new AnnotationConfigWebApplicationContext();
- rootCtx.register(ApplicationConfig.class);
-
- ctx.addListener(new ContextLoaderListener(rootCtx));
-
- RepositoryRestExporterServlet exporter = new RepositoryRestExporterServlet();
-
- ServletRegistration.Dynamic reg = ctx.addServlet("rest-exporter", exporter);
- reg.setLoadOnStartup(1);
- reg.addMapping("/*");
-
- }
-
- }
-
-The REST exporter will also load any XML config files it finds under the path `META-INF/spring-data-rest/*-export.xml`. If you have XML configuration (Spring Integration configuration, for example), then just put your XML files in this location and they will also be bootstrapped in the ApplicationContext.
-
-
-
-
-
-
-
-
-
-
-### Including your domain artifacts
-
-To expose your domain objects (your JPA entities, Repositories) and Spring configuration using the web exporter, you need to copy those resources to the web exporter's `WEB-INF/lib` or `WEB-INF/classes` directory. There are potentially other ways to deploy these artifacts without modifying the web exporter's WAR file, but those methods are considerably more complicated and prone to classpath problems. The easiest and most reliable way to deploy your user artifacts are by deploying them alongside the web exporter's artifacts.
-
-### Exposing your repositories
-
-By default, any repositories found are exported using the bean name of the repository in the Spring configuration (minus the word "Repository", if it appears in the bean name).
-
-If you have a JPA entity in your domain model that looks like...
-
- @Entity
- public class Person {
- @Id @GeneratedValue
- private Long id;
- private String name;
- @Version
- private Long version;
- @OneToMany
- private List addresses;
- @OneToMany
- private Map profiles;
- }
-
-...and an appropriate CrudRepository interface defined like...
-
- public interface PersonRepository extends CrudRepository {
- }
-
-...your PersonRepository will by default be declared in the ApplicationContext with a bean name of "personRepository". The web exporter will strip the word "Repository" from it and expose a resource named "person". The resulting URL of this repository (assuming the exporter webapp is deployed at context path `/data` in your servlet container) will be `http://localhost:8080/spring-data-rest-webmvc/person`.
-
-You can configure under what path, or whether a resource is exported at all, by using the `@RestResource` annotation. Details are here: [Configuring the REST URL path](../wiki/Configuring-the-REST-URL-path)
-
-### Using the rest-shell
-
-There is a command-line utility to make REST interaction easier. It includes history support and has helper commands to reduce the amount of typing you need to do to effect interactions with your REST services. It's called the `rest-shell`. You can download the binary package or the source from GitHub here: [https://github.com/jbrisbin/rest-shell](https://github.com/jbrisbin/rest-shell).
-
-### Discoverability
-
-The Web Exporter implements some aspects of the [HATEOAS](http://en.wikipedia.org/wiki/HATEOAS) methodology. That means all the services of the web exporter are discoverable and exposed to the client using links.
-
-If you issue an HTTP request to the root of the exporter:
-
- curl -v http://localhost:8080/spring-data-rest-webmvc/
-
-You'll get back a chunk of JSON that points your user agent to the locations of the exported repositories:
-
- {
- "links" : [{
- "rel" : "person",
- "href" : "http://localhost:8080/spring-data-rest-webmvc/person"
- }]
- }
-
-The "rel" of the link will match the exported name of the repository. Your application should keep track of this rel value as the key to this repository.
-
-Similarly, if you issue a GET to `http://localhost:8080/spring-data-rest-webmvc/person`, you should get back a list of entities exported at this resource (as returned by the CrudRepository.findAll method). See the wiki for more information about the paging and sorting options.
-
- curl -v http://localhost:8080/spring-data-rest-webmvc/person
-
- {
- "content": [ ],
- "links" : [ {
- "rel" : "person.search",
- "href" : "http://localhost:8080/spring-data-rest-webmvc/person/search"
- } ]
- }
-
-The default "rel" of these links will be the rel of the repository plus a dot '.' plus the simple class name of the entity managed by this repository. The rel value can be configured using the `@RestResource` annotation, discussed on [Configuring the REST URL path](../wiki/Configuring-the-REST-URL-path).
-
-Following these links will give your user agent a chunk of JSON that represents the entity. Besides properly handling nested objects and simple values, the web exporter will show relationships between entities using links just like those presented previously.
-
- curl -v http://localhost:8080/spring-data-rest-webmvc/person/1
-
- {
- "name" : "John Doe",
- "links" : [ {
- "rel" : "profiles",
- "href" : "http://localhost:8080/spring-data-rest-webmvc/person/1/profiles"
- }, {
- "rel" : "addresses",
- "href" : "http://localhost:8080/spring-data-rest-webmvc/person/1/addresses"
- }, {
- "rel" : "self",
- "href" : "http://localhost:8080/spring-data-rest-webmvc/person/1"
- } ],
- "version" : 1
- }
-
-This entity has a simple String value called "name", and two relationships to other entities ("profiles", and "addresses"). Note that the "rel" value of the link corresponds to the property name of the @Entity.
-
-The "self" link will always point to the resource for this entity. Use the "self" link to access the entity itself if you wish to update or delete the entity.
-
-Following the links for the "profiles" property gives us a list of links to the actual entities that are referenced by this relationship:
-
- curl -v http://localhost:8080/spring-data-rest-webmvc/person/1/profiles
-
- {
- "profiles" : [ {
- "rel" : "twitter",
- "href" : "http://localhost:8080/spring-data-rest-webmvc/person/1/profiles/1"
- }, {
- "rel" : "facebook",
- "href" : "http://localhost:8080/spring-data-rest-webmvc/person/1/profiles/2"
- } ]
- }
-
-In this case, the "profiles" property is a Map, so the "rel" value of the links is the key in the Map. The resource link, however, does not use the Map key in the URL. It is consistent with all other links to child resources and uses the ID of the child entity as the last component of the URL.
-
-Retrieving the linked entity gives us a JSON representation of the entity, as well as the "self" link necessary to update and delete the entity.
-
- curl -v http://localhost:8080/spring-data-rest-webmvc/person/1/profiles/1
-
- {
- "links" : [ {
- "rel" : "self",
- "href" : "http://localhost:8080/spring-data-rest-webmvc/profile/1"
- } ],
- "type" : "twitter",
- "url" : "#!/johndoe"
- }
-
-### Updating relationships
-
-To maintain a relationship between two entities, access the resource of the relationship by using the id of the entity as the last element in the resource path. For example, to add a link to a Profile with id 3 to a Person with id 1, issue a POST to the "profiles" resource and include in the body of the request a list of resource paths to entities you want to link to (make sure to use the [special Content-Type "text/uri-list"](http://www.ietf.org/rfc/rfc2483.txt) which, as the name implies, is a representation of a list of URIs):
-
- curl -v -X POST -H "Content-Type: text/uri-list" -d "http://localhost:8080/spring-data-rest-webmvc/profile/3" http://localhost:8080/spring-data-rest-webmvc/person/1/profiles
-
-You can also delete a relationship by issuing a DELETE request to the resource path that represents the relationship between parent and child entities. For example, to delete a relationship between a Profile entity with an id of 2 and a Person with an id of 1:
-
- curl -v -X DELETE http://localhost:8080/spring-data-rest-webmvc/person/1/profiles/2
-
-### Calling Query methods
-
-Starting with Spring Data REST 1.0.0.M2, the exporter exposes Repository query methods under the special URL path `/repository/search/*`.
-
-To see what query methods are exported, issue a GET request to the entity resource URL and add the segment "search". You'll get back a list of links to the exported search methods.
-
- curl -v http://localhost:8080/spring-data-rest-webmvc/person/search
-
- {
- "links" : [ {
- "rel" : "person.findByName",
- "href" : "http://localhost:8080/spring-data-rest-webmvc/person/search/findByName"
- } ]
- }
-
-To query for entities using this search method, add a query parameter to the URL. The response will be a list of links to the top-level URL for that resource.
-
- curl -v http://localhost:8080/spring-data-rest-webmvc/person/search/findByName?name=John+Doe
-
- [ {
- "rel" : "person.Person",
- "href" : "http://localhost:8080/spring-data-rest-webmvc/person/1"
- } ]
-
-To change the URL under which the query method is exported or set the name of the query parameter containing the search term, use the `@RestResource` annotation.
-
- @RestResource(path = "people")
- public interface PersonRepository extends CrudRepository {
-
- @RestResource(path = "name", rel = "names")
- public List findByName(@Param("name") String name);
-
- }
-
-This changes the path the PersonRepository is exported under to `/people`, changes the rel of the search URL to `people.names`, changes the path under with the query method is exported to `/name`, and sets the query parameter containing the search term to `name`. To search the Repository using this method, issue a GET request.
-
- curl -v http://localhost:8080/spring-data-rest-webmvc/people/search/name?name=John+Doe
\ No newline at end of file
diff --git a/doc/paging_and_sorting.md b/doc/paging_and_sorting.md
deleted file mode 100644
index 6c463be22..000000000
--- a/doc/paging_and_sorting.md
+++ /dev/null
@@ -1,53 +0,0 @@
-# Paging and Sorting
-
-_This documents Spring Data REST's usage of the Spring Data Repository paging and sorting abstractions. To familiarize yourself with those features, please see the Spring Data documentation for the Repository implementation you're using._
-
-## Paging
-
-Rather than return everything from a large result set, Spring Data REST recognizes some URL parameters that will influence the page size and starting page number.
-
-To add paging support to your Repositories, you need to extend the `PagingAndSortingRepository` interface rather than the basic `CrudRepository` interface. This adds methods that accept a `Pageable` to control the number and page of results returned.
-
- public Page findAll(Pageable pageable);
-
-If you extend `PagingAndSortingRepository` and access the list of all entities, you'll get links to the first 20 entities. To set the page size to any other number, add a `limit` parameter:
-
- http://localhost:8080/people/?limit=50
-
-This will set the page size to 50.
-
-To use paging in your own query methods, you need to change the method signature to accept an additional `Pageable` parameter and return a `Page` rather than a `List`. For example, the following query method will be exported to `/people/search/nameStartsWith` and will support paging:
-
- @RestResource(path = "nameStartsWith", rel = "nameStartsWith")
- public Page findByNameStartsWith(@Param("name") String name, Pageable p);
-
-The Spring Data REST exporter will recognize the returned `Page` and give you the results in the body of the response, just as it would with a non-paged response, but additional links will be added to the resource to represent the "previous" and "next" pages of data.
-
-### Previous and Next Links
-
-Each paged response will return links to the previous and next pages of results based on the current page. If you are currently at the first page of results, however, no "previous" link will be rendered. The same is true for the last page of results: no "next" link will be rendered if you are on the last page of results. The "rel" value of the link will end with ".next" for next links and ".prev" for previous links.
-
- {
- "rel" : "people.next",
- "href" : "http://localhost:8080/people?page=2&limit=20"
- }
-
-### Header metadata when paging
-
-As a convenience to the user agent, Spring Data REST sets a few special HTTP headers when doing paging. To help the UA understand where it is within the entire set of available pages, three headers are set when returning paged responses:
-
- x-springdata-meta-total-count: 125
- x-springdata-meta-current-page: 1
- x-springdata-meta-total-pages: 7
-
-The UA can use these values to keep track of where the paging stands in relation to the entire result set. This information is useful if you are providing a Javascript slider, for instance. You would be able to easily set the number of "notches" in the slider to the total number of pages and easily indicate to the user exactly where the current page of data falls in the context of the whole.
-
-## Sorting
-
-Spring Data REST also recognizes sorting parameters that will use the Repository sorting support.
-
-To have your results sorted on a particular property, add a `sort` URL parameter with the name of the property you want to sort the results on. You can control the direction of the sort by specifying a URL parameter composed of the property name plus `.dir` and setting that value to either `asc` or `desc`. The following would use the `findByNameStartsWith` query method defined on the `PersonRepository` for all `Person` entities with names starting with the letter "K" and add sort data that orders the results on the `name` property in descending order:
-
- curl -v http://localhost:8080/people/search/nameStartsWith?name=K&sort=name&name.dir=desc
-
-To sort the results by more than one property, keep adding as many `sort=PROPERTY` parameters as you need. They will be added to the `Pageable` in the order they appear in the query string.
\ No newline at end of file
diff --git a/doc/validation.md b/doc/validation.md
deleted file mode 100644
index 555b87d36..000000000
--- a/doc/validation.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Validation in Spring Data REST
-
-Integrating validation with the Spring Data REST Exporter is as easy as simply defining an instance of a [Validator](http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/validation/Validator.html). There is an ApplicationListener that that looks for these Validator instances on startup and wires them to the correct RepositoryEvent based on the bean name.
-
-For example, to validate entities before they are saved to the Repository, you only need to define a Validator instance in your ApplicationContext with a name that starts with "beforeSave".
-
-
-
-
-All the events dicussed in [Handling ApplicationEvents in the REST Exporter](wiki/Handling-ApplicationEvents-in-the-REST-Exporter) can be validated.
-
-If any errors are found during validation, a [RepositoryConstraintViolationException](blob/master/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryConstraintViolationException.java) will be thrown, resulting in a 400 Bad Request.
-
-### Advanced Configuration
-
-If you need a little more control over how the Validators are wired, you can instantiate a [ValidatingRepositoryEventListener](blob/master/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/ValidatingRepositoryEventListener.java) yourself and use a Map of Validators to their event names:
-
-
-
-
-
-
diff --git a/gradle.properties b/gradle.properties
index 4f38d9257..2a49e68a9 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,26 +1 @@
-# Logging
-slf4jVersion = 1.6.6
-logbackVersion = 1.0.6
-
-# Libraries
-springVersion = 3.1.2.RELEASE
-cglibVersion = 2.2.2
-
-# Languages
-groovyVersion = 1.8.8
-
-# Supporting libraries
-sdCommonsVersion = 1.4.0.RELEASE
-sdJpaVersion = 1.2.0.RELEASE
-hateoasVersion = 0.3.0.RELEASE
-jacksonVersion = 1.9.10
-hibernateVersion = 4.1.6.Final
-
-# Testing
-spockVersion = 0.6-groovy-1.8
-
-## OSGi ranges
-spring.range = "[3.0.7, 4.0.0)"
-jackson.range = "[1.9, 2.0.0)"
-
-sdRestVersion = 1.1.0.BUILD-SNAPSHOT
+version = 1.1.0.BUILD-SNAPSHOT
diff --git a/gradle/ide.gradle b/gradle/ide.gradle
new file mode 100644
index 000000000..e69de29bb
diff --git a/gradle/maven.gradle b/gradle/maven.gradle
new file mode 100644
index 000000000..f2b14fe47
--- /dev/null
+++ b/gradle/maven.gradle
@@ -0,0 +1,65 @@
+apply plugin: 'maven'
+
+ext.optionalDeps = []
+ext.providedDeps = []
+
+ext.optional = { optionalDeps << it }
+ext.provided = { providedDeps << it }
+
+install {
+ repositories.mavenInstaller {
+ customizePom(pom, project)
+ }
+}
+
+def customizePom(pom, gradleProject) {
+ pom.whenConfigured { generatedPom ->
+ // respect 'optional' and 'provided' dependencies
+ gradleProject.optionalDeps.each { dep ->
+ generatedPom.dependencies.find { it.artifactId == dep.name }?.optional = true
+ }
+ gradleProject.providedDeps.each { dep ->
+ generatedPom.dependencies.find { it.artifactId == dep.name }?.scope = 'provided'
+ }
+
+ // eliminate test-scoped dependencies (no need in maven central poms)
+ generatedPom.dependencies.removeAll { dep ->
+ dep.scope == 'test'
+ }
+
+ // add all items necessary for maven central publication
+ generatedPom.project {
+ name = "Spring Data REST"
+ description = "Directly export Spring Data-managed PersistentEntities to the web."
+ url = 'http://github.com/SpringSource/spring-data-rest'
+ organization {
+ name = 'SpringSource'
+ url = 'http://www.springsource.org/spring-data/rest'
+ }
+ licenses {
+ license {
+ name 'The Apache Software License, Version 2.0'
+ url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+ distribution 'repo'
+ }
+ }
+ scm {
+ url = 'http://github.com/SpringSource/spring-data-rest'
+ connection = 'scm:git:git://github.com/SpringSource/spring-data-rest'
+ developerConnection = 'scm:git:git://github.com/SpringSource/spring-data-rest'
+ }
+ developers {
+ developer {
+ id = 'jbrisbin'
+ name = 'Jon Brisbin'
+ email = 'jbrisbin@vmware.com'
+ }
+ developer {
+ id = 'ogierke'
+ name = 'Oliver Gierke'
+ email = 'ogierke@vmware.com'
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 7f1e239c8..b6b646b0c 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 7b8984830..dfaae9ee9 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Aug 15 14:17:51 CDT 2012
+#Wed Dec 05 08:31:21 CST 2012
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.1-bin.zip
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.3-bin.zip
diff --git a/gradlew b/gradlew
index e61422d06..a1787c628 100755
--- a/gradlew
+++ b/gradlew
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
##############################################################################
##
@@ -7,6 +7,7 @@
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+GRADLE_OPTS="-XX:MaxPermSize=1024m -Xmx1024m $GRADLE_OPTS"
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
@@ -61,9 +62,9 @@ while [ -h "$PRG" ] ; do
fi
done
SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/"
+cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
-cd "$SAVED"
+cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
diff --git a/gradlew.bat b/gradlew.bat
index aec99730b..e6e6d5ce3 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,90 +1,91 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windowz variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set GRADLE_OPTS=-XX:MaxPermSize=1024m -Xmx1024m -XX:MaxHeapSize=256m %GRADLE_OPTS%
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/maven.gradle b/maven.gradle
deleted file mode 100644
index 399dc0c8e..000000000
--- a/maven.gradle
+++ /dev/null
@@ -1,60 +0,0 @@
-apply plugin: 'maven'
-
-ext.optionalDeps = []
-ext.providedDeps = []
-
-ext.optional = { optionalDeps << it }
-ext.provided = { providedDeps << it }
-
-install {
- repositories.mavenInstaller {
- customizePom(pom, project)
- }
-}
-
-def customizePom(pom, gradleProject) {
- pom.whenConfigured { generatedPom ->
- // respect 'optional' and 'provided' dependencies
- gradleProject.optionalDeps.each { dep ->
- generatedPom.dependencies.find { it.artifactId == dep.name }?.optional = true
- }
- gradleProject.providedDeps.each { dep ->
- generatedPom.dependencies.find { it.artifactId == dep.name }?.scope = 'provided'
- }
-
- // eliminate test-scoped dependencies (no need in maven central poms)
- generatedPom.dependencies.removeAll { dep ->
- dep.scope == 'test'
- }
-
- // add all items necessary for maven central publication
- generatedPom.project {
- name = "Spring Data REST"
- description = "Directly export Spring Data managed JPA Entities to the web."
- url = 'http://github.com/SpringSource/spring-data-rest'
- organization {
- name = 'SpringSource'
- url = 'http://www.springsource.org/spring-data/rest'
- }
- licenses {
- license {
- name 'The Apache Software License, Version 2.0'
- url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
- distribution 'repo'
- }
- }
- scm {
- url = 'http://github.com/SpringSource/spring-data-rest'
- connection = 'scm:git:git://github.com/SpringSource/spring-data-rest'
- developerConnection = 'scm:git:git://github.com/SpringSource/spring-data-rest'
- }
- developers {
- developer {
- id = 'jbrisbin'
- name = 'Jon Brisbin'
- email = 'jbrisbin@vmware.com'
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index a030b0942..a48202765 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,3 +1,6 @@
+rootProject.name = "spring-data-rest"
+
include "spring-data-rest-core",
"spring-data-rest-repository",
- "spring-data-rest-webmvc"
+ "spring-data-rest-webmvc",
+ "spring-data-rest-example"
\ No newline at end of file
diff --git a/spring-data-rest-core/build.gradle b/spring-data-rest-core/build.gradle
deleted file mode 100644
index a222d5dbf..000000000
--- a/spring-data-rest-core/build.gradle
+++ /dev/null
@@ -1,6 +0,0 @@
-dependencies {
-
- // Google Guava
- compile "com.google.guava:guava:12.0"
-
-}
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/convert/DelegatingConversionService.java
similarity index 86%
rename from spring-data-rest-core/src/main/java/org/springframework/data/rest/core/convert/DelegatingConversionService.java
rename to spring-data-rest-core/src/main/java/org/springframework/data/rest/convert/DelegatingConversionService.java
index 83c2c20bc..b3ec428cd 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/convert/DelegatingConversionService.java
@@ -1,4 +1,4 @@
-package org.springframework.data.rest.core.convert;
+package org.springframework.data.rest.convert;
import java.util.Stack;
@@ -7,14 +7,13 @@ import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.TypeDescriptor;
/**
- * 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.
+ * This {@link ConversionService} implementation delegates the actual conversion to the {@literal ConversionService} it
+ * finds in its internal {@link Stack} that claims to be able to convert a given class. It will roll through the
+ * {@literal ConversionService}s 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();
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/convert/ISO8601DateConverter.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/convert/ISO8601DateConverter.java
new file mode 100644
index 000000000..98a547003
--- /dev/null
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/convert/ISO8601DateConverter.java
@@ -0,0 +1,76 @@
+package org.springframework.data.rest.convert;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.springframework.core.convert.ConversionFailedException;
+import org.springframework.core.convert.TypeDescriptor;
+import org.springframework.core.convert.converter.ConditionalGenericConverter;
+import org.springframework.core.convert.converter.Converter;
+
+/**
+ * @author Jon Brisbin
+ */
+public class ISO8601DateConverter implements ConditionalGenericConverter,
+ Converter {
+
+ public static final ConditionalGenericConverter INSTANCE = new ISO8601DateConverter();
+
+ private static final Set CONVERTIBLE_PAIRS = new HashSet();
+
+ static {
+ CONVERTIBLE_PAIRS.add(new ConvertiblePair(String.class, Date.class));
+ CONVERTIBLE_PAIRS.add(new ConvertiblePair(Date.class, String.class));
+ }
+
+ @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
+ if(String.class.isAssignableFrom(sourceType.getType())) {
+ return Date.class.isAssignableFrom(targetType.getType());
+ }
+
+ return Date.class.isAssignableFrom(sourceType.getType())
+ && String.class.isAssignableFrom(targetType.getType());
+ }
+
+ @Override public Set getConvertibleTypes() {
+ return CONVERTIBLE_PAIRS;
+ }
+
+ @Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
+ DateFormat dateFmt = iso8601DateFormat();
+ if(String.class.isAssignableFrom(sourceType.getType())) {
+ return dateFmt.format(source);
+ } else {
+ try {
+ return dateFmt.parse(source.toString());
+ } catch(ParseException e) {
+ throw new ConversionFailedException(sourceType, targetType, source, e);
+ }
+ }
+ }
+
+ @Override public Date convert(String[] source) {
+ if(source.length > 0) {
+ try {
+ return iso8601DateFormat().parse(source[0]);
+ } catch(ParseException e) {
+ throw new ConversionFailedException(
+ TypeDescriptor.valueOf(String[].class),
+ TypeDescriptor.valueOf(Date.class),
+ source[0],
+ new IllegalArgumentException("Source does not conform to ISO8601 date format (YYYY-MM-DDTHH:MM:SS-0000")
+ );
+ }
+ }
+ return null;
+ }
+
+ private DateFormat iso8601DateFormat() {
+ return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
+ }
+
+}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/convert/UUIDConverter.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/convert/UUIDConverter.java
new file mode 100644
index 000000000..5b2ccf31a
--- /dev/null
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/convert/UUIDConverter.java
@@ -0,0 +1,46 @@
+package org.springframework.data.rest.convert;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+import org.springframework.core.convert.TypeDescriptor;
+import org.springframework.core.convert.converter.ConditionalGenericConverter;
+
+/**
+ * For converting a {@link UUID} into a {@link String}.
+ *
+ * @author Jon Brisbin
+ */
+public class UUIDConverter implements ConditionalGenericConverter {
+
+ public static final UUIDConverter INSTANCE = new UUIDConverter();
+ private static final Set CONVERTIBLE_PAIRS = new HashSet();
+
+ static {
+ CONVERTIBLE_PAIRS.add(new ConvertiblePair(String.class, UUID.class));
+ CONVERTIBLE_PAIRS.add(new ConvertiblePair(UUID.class, String.class));
+ }
+
+ @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
+ if(String.class.isAssignableFrom(sourceType.getType())) {
+ return UUID.class.isAssignableFrom(targetType.getType());
+ }
+
+ return UUID.class.isAssignableFrom(sourceType.getType())
+ && String.class.isAssignableFrom(targetType.getType());
+ }
+
+ @Override public Set getConvertibleTypes() {
+ return CONVERTIBLE_PAIRS;
+ }
+
+ @Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
+ if(String.class.isAssignableFrom(sourceType.getType())) {
+ return UUID.fromString(source.toString());
+ } else {
+ return source.toString();
+ }
+ }
+
+}
diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/convert/package-info.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/convert/package-info.java
new file mode 100644
index 000000000..2a539f0cc
--- /dev/null
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/convert/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * {@link org.springframework.core.convert.ConversionService} and {@link org.springframework.core.convert.converter.Converter} integration for Spring Data REST.
+ */
+package org.springframework.data.rest.convert;
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
deleted file mode 100644
index 2d4e0566d..000000000
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/Handler.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.springframework.data.rest.core;
-
-/**
- * Generic interface used as a callback in any place you need extensibility.
- *
- * @author Jon Brisbin
- */
-public interface Handler {
-
- /**
- * Accept an argument and possibly produce a result.
- *
- * @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/UriResolver.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/UriResolver.java
deleted file mode 100644
index 31500475f..000000000
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/UriResolver.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.springframework.data.rest.core;
-
-import java.net.URI;
-
-/**
- * Implementations of this interface are responsible for turning {@link URI}s into real objects.
- *
- * @author Jon Brisbin
- */
-public interface UriResolver {
-
- /**
- * Take a {@link URI} and resolve it to an actual object.
- *
- * @param baseUri
- * The base URI that this resource is relative to.
- * @param uri
- * The URI id of the resource.
- *
- * @return The resolved object or {@literal null} if not found.
- */
- T resolve(URI baseUri, URI uri);
-
-}
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
deleted file mode 100644
index d90c91d5f..000000000
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/convert/StringToUUIDConverter.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.springframework.data.rest.core.convert;
-
-import java.util.UUID;
-
-import org.springframework.core.convert.converter.Converter;
-
-/**
- * @author Jon Brisbin
- */
-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
deleted file mode 100644
index 51df846ab..000000000
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/convert/UUIDToStringConverter.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.springframework.data.rest.core.convert;
-
-import java.util.UUID;
-
-import org.springframework.core.convert.converter.Converter;
-
-/**
- * @author Jon Brisbin
- */
-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/package-info.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/package-info.java
new file mode 100644
index 000000000..ba9ee30c4
--- /dev/null
+++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Core components used across Spring Data REST.
+ */
+package org.springframework.data.rest.core;
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
deleted file mode 100644
index d3d1149a4..000000000
--- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/BeanUtils.java
+++ /dev/null
@@ -1,213 +0,0 @@
-package org.springframework.data.rest.core.util;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ExecutionException;
-
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.common.util.concurrent.UncheckedExecutionException;
-import org.springframework.core.convert.support.ConfigurableConversionService;
-import org.springframework.core.convert.support.DefaultConversionService;
-import org.springframework.util.ClassUtils;
-import org.springframework.util.ReflectionUtils;
-import org.springframework.util.StringUtils;
-
-/**
- * @author Jon Brisbin
- */
-public abstract class BeanUtils {
-
- private BeanUtils() {
- }
-
- public static ConfigurableConversionService CONVERSION_SERVICE = new DefaultConversionService();
-
- private static final LoadingCache