diff --git a/rest/pom.xml b/rest/pom.xml
index b3a65efa..02696b80 100644
--- a/rest/pom.xml
+++ b/rest/pom.xml
@@ -20,6 +20,7 @@
projections
security
headers
+ uri-customization
diff --git a/rest/uri-customization/pom.xml b/rest/uri-customization/pom.xml
new file mode 100644
index 00000000..3e4522ee
--- /dev/null
+++ b/rest/uri-customization/pom.xml
@@ -0,0 +1,34 @@
+
+ 4.0.0
+
+ spring-data-rest-uri-customizations
+
+ Spring Data REST - URI Customizations Example
+
+
+ org.springframework.data.examples
+ spring-data-rest-examples
+ 1.0.0.BUILD-SNAPSHOT
+
+
+
+ Hopper-BUILD-SNAPSHOT
+
+
+
+
+
+ org.springframework.data
+ spring-data-keyvalue
+
+
+
+ com.jayway.jsonpath
+ json-path
+ test
+
+
+
+
+
\ No newline at end of file
diff --git a/rest/uri-customization/readme.adoc b/rest/uri-customization/readme.adoc
new file mode 100644
index 00000000..7c08e1c7
--- /dev/null
+++ b/rest/uri-customization/readme.adoc
@@ -0,0 +1,25 @@
+= Spring Data REST URI customizations
+
+This example shows how to customize which property of the domain type shall be used to create URIs for item resources. This is achieved by implementing an `EntityLookup` and declaring it as Spring bean:
+
+[source, java]
+----
+@Component
+@RequiredArgsConstructor(onConstructor = @__(@Autowired) )
+public class UserEntityLookup extends EntityLookupSupport {
+
+ private final @NonNull UserRepository repository;
+
+ @Override
+ public Serializable getResourceIdentifier(User entity) {
+ return entity.getUsername();
+ }
+
+ @Override
+ public Object lookupEntity(Serializable id) {
+ return repository.findByUsername(id.toString());
+ }
+}
+----
+
+As you can see the customization consists of two methods that need to be symmetric in their functionality. `getResourceIdentifier(…)` returns the property that's supposed to be used in the URI while `lookupEntity(…)` uses the value to lookup an entity via a query method for exactly that property.
diff --git a/rest/uri-customization/src/main/java/example/springdata/rest/uris/Application.java b/rest/uri-customization/src/main/java/example/springdata/rest/uris/Application.java
new file mode 100644
index 00000000..3d8d5b91
--- /dev/null
+++ b/rest/uri-customization/src/main/java/example/springdata/rest/uris/Application.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package example.springdata.rest.uris;
+
+import javax.annotation.PostConstruct;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.data.map.repository.config.EnableMapRepositories;
+
+/**
+ * Applicatoin class to bootstrap the app.
+ *
+ * @author Oliver Gierke
+ */
+@SpringBootApplication
+@EnableMapRepositories
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+ @Autowired UserRepository repository;
+
+ @PostConstruct
+ public void init() {
+
+ repository.save(new User("olivergierke"));
+ repository.save(new User("starbucksman"));
+ }
+}
diff --git a/rest/uri-customization/src/main/java/example/springdata/rest/uris/User.java b/rest/uri-customization/src/main/java/example/springdata/rest/uris/User.java
new file mode 100644
index 00000000..9c8c3d63
--- /dev/null
+++ b/rest/uri-customization/src/main/java/example/springdata/rest/uris/User.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package example.springdata.rest.uris;
+
+import lombok.Value;
+
+import java.util.UUID;
+
+import org.springframework.data.annotation.Id;
+
+/**
+ * A {@link User}.
+ *
+ * @author Oliver Gierke
+ */
+@Value
+public class User {
+
+ private final @Id UUID id = UUID.randomUUID();
+ private final String username;
+}
diff --git a/rest/uri-customization/src/main/java/example/springdata/rest/uris/UserEntityLookup.java b/rest/uri-customization/src/main/java/example/springdata/rest/uris/UserEntityLookup.java
new file mode 100644
index 00000000..f30f0cc9
--- /dev/null
+++ b/rest/uri-customization/src/main/java/example/springdata/rest/uris/UserEntityLookup.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package example.springdata.rest.uris;
+
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+
+import java.io.Serializable;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.rest.core.support.EntityLookup;
+import org.springframework.data.rest.core.support.EntityLookupSupport;
+import org.springframework.stereotype.Component;
+
+/**
+ * Custom {@link EntityLookup} to replace the usage of the database identifier in item resource URIs with the username
+ * property of the {@link User}.
+ *
+ * @author Oliver Gierke
+ */
+@Component
+@RequiredArgsConstructor(onConstructor = @__(@Autowired) )
+public class UserEntityLookup extends EntityLookupSupport {
+
+ private final @NonNull UserRepository repository;
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.support.EntityLookup#getId(java.lang.Object)
+ */
+ @Override
+ public Serializable getResourceIdentifier(User entity) {
+ return entity.getUsername();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.core.support.EntityLookup#lookupEntity(java.io.Serializable)
+ */
+ @Override
+ public Object lookupEntity(Serializable id) {
+ return repository.findByUsername(id.toString());
+ }
+}
diff --git a/rest/uri-customization/src/main/java/example/springdata/rest/uris/UserRepository.java b/rest/uri-customization/src/main/java/example/springdata/rest/uris/UserRepository.java
new file mode 100644
index 00000000..75e0da8d
--- /dev/null
+++ b/rest/uri-customization/src/main/java/example/springdata/rest/uris/UserRepository.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package example.springdata.rest.uris;
+
+import java.util.Optional;
+import java.util.UUID;
+
+import org.springframework.data.repository.CrudRepository;
+
+/**
+ * Repository to manage {@link User} instances.
+ *
+ * @author Oliver Gierke
+ */
+public interface UserRepository extends CrudRepository {
+
+ /**
+ * Looks up a unique {@link User} by its user name.
+ *
+ * @param username
+ * @return
+ */
+ Optional findByUsername(String username);
+}
diff --git a/rest/uri-customization/src/test/java/example/springdata/rest/uris/WebIntegrationTests.java b/rest/uri-customization/src/test/java/example/springdata/rest/uris/WebIntegrationTests.java
new file mode 100644
index 00000000..e54cc79f
--- /dev/null
+++ b/rest/uri-customization/src/test/java/example/springdata/rest/uris/WebIntegrationTests.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package example.springdata.rest.uris;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+/**
+ * Integration tests to make sure the URI customizations are applied.
+ *
+ * @author Oliver Gierke
+ * @soundtrack Clueso - Gewinner (Stadtrandlichter Live)
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebAppConfiguration
+@SpringApplicationConfiguration(classes = Application.class)
+public class WebIntegrationTests {
+
+ @Autowired WebApplicationContext context;
+ @Autowired UserRepository users;
+
+ MockMvc mvc;
+
+ @Before
+ public void setUp() {
+
+ this.mvc = MockMvcBuilders.webAppContextSetup(context).//
+ build();
+ }
+
+ @Test
+ public void identifiesResourcesUsingUsername() throws Exception {
+
+ mvc.perform(get("/users/olivergierke")).//
+ andExpect(status().isOk()).//
+ andExpect(jsonPath("$._links.self.href", endsWith("olivergierke")));
+ }
+}
diff --git a/rest/uri-customization/src/test/resources/documentation.properties b/rest/uri-customization/src/test/resources/documentation.properties
new file mode 100644
index 00000000..bdb0fef8
--- /dev/null
+++ b/rest/uri-customization/src/test/resources/documentation.properties
@@ -0,0 +1 @@
+org.springframework.restdocs.outputDir=target/generated-snippets
\ No newline at end of file