diff --git a/spring-cloud/spring-cloud-rest/pom.xml b/spring-cloud/spring-cloud-rest/pom.xml
index fef6d93cfc..3424d988a9 100644
--- a/spring-cloud/spring-cloud-rest/pom.xml
+++ b/spring-cloud/spring-cloud-rest/pom.xml
@@ -8,7 +8,8 @@
1.0.0-SNAPSHOT
spring-cloud-rest-server
- spring-cloud-rest-client
+ spring-cloud-rest-client-1
+ spring-cloud-rest-client-2
pom
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client/pom.xml b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/pom.xml
similarity index 98%
rename from spring-cloud/spring-cloud-rest/spring-cloud-rest-client/pom.xml
rename to spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/pom.xml
index 21c19be834..f50ae7e8f2 100644
--- a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client/pom.xml
+++ b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/pom.xml
@@ -4,7 +4,7 @@
4.0.0
org.baeldung
- spring-cloud-rest-client
+ spring-cloud-rest-client-1
0.0.1-SNAPSHOT
jar
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/main/java/org/baeldung/SessionConfig.java b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/main/java/org/baeldung/SessionConfig.java
similarity index 100%
rename from spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/main/java/org/baeldung/SessionConfig.java
rename to spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/main/java/org/baeldung/SessionConfig.java
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/main/java/org/baeldung/SpringCloudRestClientApplication.java b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/main/java/org/baeldung/SpringCloudRestClientApplication.java
similarity index 100%
rename from spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/main/java/org/baeldung/SpringCloudRestClientApplication.java
rename to spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/main/java/org/baeldung/SpringCloudRestClientApplication.java
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/main/java/org/baeldung/persistence/dao/BookRepository.java b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/main/java/org/baeldung/persistence/dao/BookRepository.java
similarity index 100%
rename from spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/main/java/org/baeldung/persistence/dao/BookRepository.java
rename to spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/main/java/org/baeldung/persistence/dao/BookRepository.java
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/main/java/org/baeldung/persistence/model/Book.java b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/main/java/org/baeldung/persistence/model/Book.java
similarity index 100%
rename from spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/main/java/org/baeldung/persistence/model/Book.java
rename to spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/main/java/org/baeldung/persistence/model/Book.java
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/main/resources/application.properties b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/main/resources/application.properties
similarity index 100%
rename from spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/main/resources/application.properties
rename to spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/main/resources/application.properties
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/test/java/org/baeldung/RestApiLiveTest.java b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/test/java/org/baeldung/RestApiLiveTest.java
similarity index 100%
rename from spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/test/java/org/baeldung/RestApiLiveTest.java
rename to spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/test/java/org/baeldung/RestApiLiveTest.java
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/test/java/org/baeldung/SessionLiveTest.java b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/test/java/org/baeldung/SessionLiveTest.java
similarity index 100%
rename from spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/test/java/org/baeldung/SessionLiveTest.java
rename to spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/test/java/org/baeldung/SessionLiveTest.java
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/test/java/org/baeldung/SpringCloudRestClientApplicationTests.java b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/test/java/org/baeldung/SpringCloudRestClientApplicationTests.java
similarity index 100%
rename from spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/test/java/org/baeldung/SpringCloudRestClientApplicationTests.java
rename to spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/test/java/org/baeldung/SpringCloudRestClientApplicationTests.java
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/test/resources/application.properties b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/test/resources/application.properties
similarity index 100%
rename from spring-cloud/spring-cloud-rest/spring-cloud-rest-client/src/test/resources/application.properties
rename to spring-cloud/spring-cloud-rest/spring-cloud-rest-client-1/src/test/resources/application.properties
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/pom.xml b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/pom.xml
new file mode 100644
index 0000000000..359045829b
--- /dev/null
+++ b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/pom.xml
@@ -0,0 +1,107 @@
+
+
+ 4.0.0
+
+ org.baeldung
+ spring-cloud-rest-client-2
+ 0.0.1-SNAPSHOT
+ jar
+
+ spring-cloud-rest
+ Demo project for Spring Boot
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 1.4.3.RELEASE
+
+
+
+
+ UTF-8
+ UTF-8
+ 1.8
+ 3.0.1
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-eureka
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ com.h2database
+ h2
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-data-rest
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.session
+ spring-session
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ io.rest-assured
+ rest-assured
+ ${rest-assured.version}
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ Camden.SR4
+ pom
+ import
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ **/*IntegrationTest.java
+ **/*LiveTest.java
+
+
+
+
+
+
+
+
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/main/java/org/baeldung/SessionConfig.java b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/main/java/org/baeldung/SessionConfig.java
new file mode 100644
index 0000000000..bd1c0013ca
--- /dev/null
+++ b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/main/java/org/baeldung/SessionConfig.java
@@ -0,0 +1,8 @@
+package org.baeldung;
+
+import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
+import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
+
+@EnableRedisHttpSession
+public class SessionConfig extends AbstractHttpSessionApplicationInitializer {
+}
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/main/java/org/baeldung/SpringCloudRestClientApplication.java b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/main/java/org/baeldung/SpringCloudRestClientApplication.java
new file mode 100644
index 0000000000..a13aaa54bc
--- /dev/null
+++ b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/main/java/org/baeldung/SpringCloudRestClientApplication.java
@@ -0,0 +1,14 @@
+package org.baeldung;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+
+@SpringBootApplication
+@EnableEurekaClient
+public class SpringCloudRestClientApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringCloudRestClientApplication.class, args);
+ }
+}
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/main/java/org/baeldung/persistence/dao/BookReviewRepository.java b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/main/java/org/baeldung/persistence/dao/BookReviewRepository.java
new file mode 100644
index 0000000000..2107786d62
--- /dev/null
+++ b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/main/java/org/baeldung/persistence/dao/BookReviewRepository.java
@@ -0,0 +1,13 @@
+package org.baeldung.persistence.dao;
+
+import org.baeldung.persistence.model.BookReview;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.query.Param;
+import org.springframework.data.rest.core.annotation.RepositoryRestResource;
+
+@RepositoryRestResource(collectionResourceRel = "reviews", path = "reviews")
+public interface BookReviewRepository extends CrudRepository {
+ Page findByBookId(@Param("bookId") long bookId, Pageable pageable);
+}
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/main/java/org/baeldung/persistence/model/BookReview.java b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/main/java/org/baeldung/persistence/model/BookReview.java
new file mode 100644
index 0000000000..5341df7cb6
--- /dev/null
+++ b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/main/java/org/baeldung/persistence/model/BookReview.java
@@ -0,0 +1,128 @@
+package org.baeldung.persistence.model;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+@Entity
+public class BookReview {
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private long id;
+
+ private String content;
+
+ private int rating;
+
+ @Column(nullable = false)
+ private Long bookId;
+
+ //
+
+ public BookReview() {
+ super();
+ }
+
+ public BookReview(String content, int rating, long bookId) {
+ super();
+ this.content = content;
+ this.rating = rating;
+ this.bookId = bookId;
+ }
+
+ //
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public int getRating() {
+ return rating;
+ }
+
+ public void setRating(int rating) {
+ this.rating = rating;
+ }
+
+ public Long getBookId() {
+ return bookId;
+ }
+
+ public void setBookId(Long bookId) {
+ this.bookId = bookId;
+ }
+
+ //
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = (prime * result) + (int) (bookId ^ (bookId >>> 32));
+ result = (prime * result) + ((content == null) ? 0 : content.hashCode());
+ result = (prime * result) + (int) (id ^ (id >>> 32));
+ result = (prime * result) + rating;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final BookReview other = (BookReview) obj;
+ if (bookId != other.bookId) {
+ return false;
+ }
+ if (content == null) {
+ if (other.content != null) {
+ return false;
+ }
+ } else if (!content.equals(other.content)) {
+ return false;
+ }
+ if (id != other.id) {
+ return false;
+ }
+ if (rating != other.rating) {
+ return false;
+ }
+ return true;
+ }
+
+ //
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("BookReview [id=")
+ .append(id)
+ .append(", content=")
+ .append(content)
+ .append(", rating=")
+ .append(rating)
+ .append(", bookId=")
+ .append(bookId)
+ .append("]");
+ return builder.toString();
+ }
+
+}
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/main/resources/application.properties b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/main/resources/application.properties
new file mode 100644
index 0000000000..65fcb7b71e
--- /dev/null
+++ b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/main/resources/application.properties
@@ -0,0 +1,19 @@
+#### cloud
+spring.application.name=spring-cloud-eureka-client
+server.port=0
+eureka.client.serviceUrl.defaultZone=${EUREKA_URI:http://system:systemPass@localhost:8761/eureka}
+eureka.instance.preferIpAddress=true
+
+#### persistence
+spring.datasource.driver-class-name=org.h2.Driver
+spring.datasource.url=jdbc:h2:mem:cloud_rest;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
+spring.datasource.username=sa
+spring.datasource.password=
+
+#### security
+security.basic.enabled=true
+security.basic.path=/**
+security.user.name=user
+security.user.password=userPass
+security.user.role=USER
+security.sessions=always
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/test/java/org/baeldung/RestApiLiveTest.java b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/test/java/org/baeldung/RestApiLiveTest.java
new file mode 100644
index 0000000000..022648ffa1
--- /dev/null
+++ b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/test/java/org/baeldung/RestApiLiveTest.java
@@ -0,0 +1,162 @@
+package org.baeldung;
+
+import static io.restassured.RestAssured.preemptive;
+import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
+import static org.apache.commons.lang3.RandomStringUtils.randomNumeric;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import io.restassured.RestAssured;
+import io.restassured.response.Response;
+
+import org.baeldung.persistence.model.BookReview;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = { SpringCloudRestClientApplication.class }, webEnvironment = WebEnvironment.DEFINED_PORT)
+public class RestApiLiveTest {
+
+ private static final String API_URI = "http://localhost:8084/reviews";
+
+ @Before
+ public void setUp() {
+ RestAssured.authentication = preemptive().basic("user", "userPass");
+ }
+
+ // GET
+
+ @Test
+ public void whenGetAllReviews_thenOK() {
+ final Response response = RestAssured.get(API_URI);
+ assertEquals(HttpStatus.OK.value(), response.getStatusCode());
+ }
+
+ @Test
+ public void whenGetCreatedReviewById_thenOK() {
+ final BookReview review = createRandomReview();
+ final String location = createReviewAsUri(review);
+
+ final Response response = RestAssured.get(location);
+ assertEquals(HttpStatus.OK.value(), response.getStatusCode());
+ assertEquals(review.getContent(), response.jsonPath()
+ .get("content"));
+ }
+
+ @Test
+ public void whenGetCreatedReviewByName_thenOK() {
+ final BookReview review = createRandomReview();
+ createReviewAsUri(review);
+
+ final Response response = RestAssured.get(API_URI + "/search/findByBookId?bookId=" + review.getBookId());
+ assertEquals(HttpStatus.OK.value(), response.getStatusCode());
+ assertTrue(response.jsonPath()
+ .getLong("page.totalElements") > 0);
+ }
+
+ @Test
+ public void whenGetNotExistReviewById_thenNotFound() {
+ final Response response = RestAssured.get(API_URI + "/" + randomNumeric(4));
+ assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode());
+ }
+
+ @Test
+ public void whenGetNotExistReviewByName_thenNotFound() {
+ final Response response = RestAssured.get(API_URI + "/search/findByBookId?bookId=" + randomNumeric(4));
+ assertEquals(HttpStatus.OK.value(), response.getStatusCode());
+ assertTrue(response.jsonPath()
+ .getLong("page.totalElements") == 0);
+ }
+
+ // POST
+ @Test
+ public void whenCreateNewReview_thenCreated() {
+ final BookReview review = createRandomReview();
+
+ final Response response = RestAssured.given()
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .body(review)
+ .post(API_URI);
+ assertEquals(HttpStatus.CREATED.value(), response.getStatusCode());
+ }
+
+ @Test
+ public void whenInvalidReview_thenError() {
+ final BookReview review = createRandomReview();
+ review.setBookId(null);
+
+ final Response response = RestAssured.given()
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .body(review)
+ .post(API_URI);
+ assertEquals(HttpStatus.CONFLICT.value(), response.getStatusCode());
+ }
+
+ @Test
+ public void whenUpdateCreatedReview_thenUpdated() {
+ // create
+ final BookReview review = createRandomReview();
+ final String location = createReviewAsUri(review);
+
+ // update
+ review.setRating(4);
+ Response response = RestAssured.given()
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .body(review)
+ .put(location);
+ assertEquals(HttpStatus.OK.value(), response.getStatusCode());
+
+ // check if changes saved
+ response = RestAssured.get(location);
+ assertEquals(HttpStatus.OK.value(), response.getStatusCode());
+ assertEquals(4, response.jsonPath()
+ .getInt("rating"));
+
+ }
+
+ @Test
+ public void whenDeleteCreatedReview_thenOk() {
+ // create
+ final BookReview review = createRandomReview();
+ final String location = createReviewAsUri(review);
+
+ // delete
+ Response response = RestAssured.delete(location);
+ assertEquals(HttpStatus.NO_CONTENT.value(), response.getStatusCode());
+
+ // confirm it was deleted
+ response = RestAssured.get(location);
+ assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode());
+ }
+
+ @Test
+ public void whenDeleteNotExistReview_thenError() {
+ final Response response = RestAssured.delete(API_URI + "/" + randomNumeric(4));
+ assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode());
+ }
+
+ // =============================== Util
+
+ private BookReview createRandomReview() {
+ final BookReview review = new BookReview();
+ review.setContent(randomAlphabetic(10));
+ review.setRating(3);
+ review.setBookId(1L);
+ return review;
+ }
+
+ private String createReviewAsUri(BookReview review) {
+ final Response response = RestAssured.given()
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .body(review)
+ .post(API_URI);
+ return response.jsonPath()
+ .get("_links.self.href");
+ }
+
+}
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/test/java/org/baeldung/SpringCloudRestClientApplicationTests.java b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/test/java/org/baeldung/SpringCloudRestClientApplicationTests.java
new file mode 100644
index 0000000000..c16adfbb34
--- /dev/null
+++ b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/test/java/org/baeldung/SpringCloudRestClientApplicationTests.java
@@ -0,0 +1,16 @@
+package org.baeldung;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class SpringCloudRestClientApplicationTests {
+
+ @Test
+ public void contextLoads() {
+ }
+
+}
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/test/resources/application.properties b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/test/resources/application.properties
new file mode 100644
index 0000000000..ece9ca1d94
--- /dev/null
+++ b/spring-cloud/spring-cloud-rest/spring-cloud-rest-client-2/src/test/resources/application.properties
@@ -0,0 +1,19 @@
+#### cloud
+spring.application.name=spring-cloud-eureka-client
+server.port=8084
+eureka.client.serviceUrl.defaultZone=${EUREKA_URI:http://system:systemPass@localhost:8761/eureka}
+eureka.instance.preferIpAddress=true
+
+#### persistence
+spring.datasource.driver-class-name=org.h2.Driver
+spring.datasource.url=jdbc:h2:mem:cloud_rest;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
+spring.datasource.username=sa
+spring.datasource.password=
+
+#### security
+security.basic.enabled=true
+security.basic.path=/**
+security.user.name=user
+security.user.password=userPass
+security.user.role=USER
+security.sessions=always
\ No newline at end of file