diff --git a/spring-cloud/spring-cloud-bootstrap/application-config/rating-service.properties b/spring-cloud/spring-cloud-bootstrap/application-config/rating-service.properties
index b7cbb6fbd6..6ee1a73d29 100644
--- a/spring-cloud/spring-cloud-bootstrap/application-config/rating-service.properties
+++ b/spring-cloud/spring-cloud-bootstrap/application-config/rating-service.properties
@@ -16,5 +16,8 @@ logging.level.org.springframework.security=debug
spring.redis.host=localhost
spring.redis.port=6379
+cache.redis.hostName=localhost
+cache.redis.port=6379
+
spring.sleuth.sampler.percentage=1.0
spring.sleuth.web.skipPattern=(^cleanup.*)
diff --git a/spring-cloud/spring-cloud-bootstrap/svc-rating/pom.xml b/spring-cloud/spring-cloud-bootstrap/svc-rating/pom.xml
index 7879031a2a..736a6114cf 100644
--- a/spring-cloud/spring-cloud-bootstrap/svc-rating/pom.xml
+++ b/spring-cloud/spring-cloud-bootstrap/svc-rating/pom.xml
@@ -1,78 +1,87 @@
- 4.0.0
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
- com.baeldung.spring.cloud
- svc-rating
- 1.0.0-SNAPSHOT
+ com.baeldung.spring.cloud
+ svc-rating
+ 1.0.0-SNAPSHOT
-
- parent-boot-4
- com.baeldung
- 0.0.1-SNAPSHOT
- ../../../parent-boot-4
-
+
+ parent-boot-4
+ com.baeldung
+ 0.0.1-SNAPSHOT
+ ../../../parent-boot-4
+
-
-
- org.springframework.cloud
- spring-cloud-starter-config
-
-
- org.springframework.cloud
- spring-cloud-starter-eureka
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.springframework.boot
- spring-boot-starter-security
-
+
+
+ org.springframework.cloud
+ spring-cloud-starter-config
+
+
+ org.springframework.cloud
+ spring-cloud-starter-eureka
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
-
- org.springframework.session
- spring-session
-
-
- org.springframework.boot
- spring-boot-starter-data-redis
-
+
+ org.springframework.session
+ spring-session
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
-
- org.springframework.boot
- spring-boot-starter-data-jpa
-
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
-
- com.h2database
- h2
- runtime
-
+
+ org.springframework.cloud
+ spring-cloud-starter-hystrix
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
-
- org.springframework.cloud
- spring-cloud-starter-zipkin
-
+
+ com.h2database
+ h2
+ runtime
+
-
+
+ org.springframework.cloud
+ spring-cloud-starter-zipkin
+
-
-
-
- org.springframework.cloud
- spring-cloud-dependencies
- ${spring-cloud-dependencies.version}
- pom
- import
-
-
-
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud-dependencies.version}
+ pom
+ import
+
+
+
+
+
+ Brixton.SR7
+
-
- Brixton.SR7
-
-
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/CacheConfig.java b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/CacheConfig.java
new file mode 100644
index 0000000000..c06571a8fa
--- /dev/null
+++ b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/CacheConfig.java
@@ -0,0 +1,30 @@
+package com.baeldung.spring.cloud.bootstrap.svcrating;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
+
+@Configuration
+@EnableConfigurationProperties({CacheProperties.class})
+public class CacheConfig {
+
+ @Autowired
+ CacheProperties cacheProperties;
+
+ @Bean(name="cacheConnectionFactory")
+ @Qualifier("cacheJedisConnectionFactory")
+ JedisConnectionFactory cacheConnectionFactory() {
+ System.out.println(">>>>>>>>>>>>>>>>>>>>>Qualified Jedis Conn.Name.."+cacheProperties.hostName);
+ System.out.println(">>>>>>>>>>>>>>>>>>>>>Qualified Jedis Conn.Port.."+cacheProperties.port);
+ JedisConnectionFactory factory = new JedisConnectionFactory();
+ factory.setHostName(cacheProperties.hostName);
+ factory.setPort(cacheProperties.port);
+ factory.setUsePool(true);
+ factory.afterPropertiesSet();
+ return factory;
+ }
+
+}
diff --git a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/CacheProperties.java b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/CacheProperties.java
new file mode 100644
index 0000000000..0016522281
--- /dev/null
+++ b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/CacheProperties.java
@@ -0,0 +1,24 @@
+package com.baeldung.spring.cloud.bootstrap.svcrating;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties(prefix="cache.redis")
+public class CacheProperties {
+public String hostName;
+public int port;
+
+public String getHostName() {
+ return hostName;
+}
+public void setHostName(String hostName) {
+ this.hostName = hostName;
+}
+public int getPort() {
+ return port;
+}
+public void setPort(int port) {
+ this.port = port;
+}
+
+
+}
diff --git a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/RatingServiceApplication.java b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/RatingServiceApplication.java
index 0d85c5c825..c1d92376e3 100644
--- a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/RatingServiceApplication.java
+++ b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/RatingServiceApplication.java
@@ -1,21 +1,25 @@
package com.baeldung.spring.cloud.bootstrap.svcrating;
-import com.netflix.appinfo.InstanceInfo;
-import com.netflix.discovery.EurekaClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.sleuth.metric.SpanMetricReporter;
import org.springframework.cloud.sleuth.zipkin.HttpZipkinSpanReporter;
import org.springframework.cloud.sleuth.zipkin.ZipkinProperties;
import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter;
import org.springframework.context.annotation.Bean;
+
+import com.netflix.appinfo.InstanceInfo;
+import com.netflix.discovery.EurekaClient;
+
import zipkin.Span;
@SpringBootApplication
@EnableEurekaClient
+@EnableHystrix
public class RatingServiceApplication {
@Autowired
private EurekaClient eurekaClient;
diff --git a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/SecurityConfig.java b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/SecurityConfig.java
index 171fbba7af..a20c81f244 100644
--- a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/SecurityConfig.java
+++ b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/SecurityConfig.java
@@ -20,17 +20,21 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
- http.httpBasic()
- .disable()
+ http
.authorizeRequests()
.regexMatchers("^/ratings\\?bookId.*$").authenticated()
.antMatchers(HttpMethod.POST,"/ratings").authenticated()
+ .antMatchers(HttpMethod.GET,"/ratings/**").authenticated()
.antMatchers(HttpMethod.PATCH,"/ratings/*").hasRole("ADMIN")
.antMatchers(HttpMethod.DELETE,"/ratings/*").hasRole("ADMIN")
.antMatchers(HttpMethod.GET,"/ratings").hasRole("ADMIN")
+ .antMatchers(HttpMethod.GET,"/hystrix*").permitAll()
.anyRequest().authenticated()
.and()
+ .httpBasic().and()
.csrf()
.disable();
+
+
}
}
diff --git a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/SessionConfig.java b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/SessionConfig.java
index 62bc701868..6e8fcd10d4 100644
--- a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/SessionConfig.java
+++ b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/SessionConfig.java
@@ -1,10 +1,28 @@
package com.baeldung.spring.cloud.bootstrap.svcrating;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.env.Environment;
+import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
@Configuration
@EnableRedisHttpSession
public class SessionConfig extends AbstractHttpSessionApplicationInitializer {
+ @Autowired
+ Environment properties;
+
+ @Bean
+ @Primary
+ public JedisConnectionFactory connectionFactory() {
+ JedisConnectionFactory factory = new JedisConnectionFactory();
+ factory.setHostName(properties.getProperty("spring.redis.host","localhost"));
+ factory.setPort(properties.getProperty("spring.redis.port", Integer.TYPE,6379));
+ factory.afterPropertiesSet();
+ factory.setUsePool(true);
+ return factory;
+ }
}
diff --git a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/Rating.java b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/Rating.java
index c37099f3c1..348d854b5c 100644
--- a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/Rating.java
+++ b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/Rating.java
@@ -1,22 +1,34 @@
package com.baeldung.spring.cloud.bootstrap.svcrating.rating;
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
+import javax.persistence.Transient;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@Entity
@JsonIgnoreProperties(ignoreUnknown = true)
-public class Rating {
+public class Rating implements Serializable{
+ /**
+ *
+ */
+ private static final long serialVersionUID = 3308900941650386473L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Long bookId;
private int stars;
+ @Transient
+ private boolean fromCache;
+ @Transient
+ private Long cachedTS=-1L;
+
public Rating() {
}
@@ -54,4 +66,40 @@ public class Rating {
public void setStars(int stars) {
this.stars = stars;
}
+
+ public boolean isFromCache() {
+ return fromCache;
+ }
+
+ public void setFromCache(boolean fromCache) {
+ this.fromCache = fromCache;
+ }
+
+ public Long getCachedTS() {
+ return cachedTS;
+ }
+
+ public void setCachedTS(Long cachedTS) {
+ this.cachedTS = cachedTS;
+ }
+
+ @Override
+ public String toString() {
+ return "Rating [" + id + "," + bookId + "," + stars + "," + cachedTS + "]";
+ }
+
+ public static Rating fromString(String ratingAsStr){
+
+ if(ratingAsStr == null || ratingAsStr.isEmpty())
+ return null;
+ String[] attributeVals=ratingAsStr.substring(8,ratingAsStr.length()-1).split("[,]");
+
+ Rating rating=new Rating();
+ rating.setId(Long.valueOf(attributeVals[0]));
+ rating.setBookId(Long.valueOf(attributeVals[1]));
+ rating.setStars(Integer.valueOf(attributeVals[2]));
+ rating.setCachedTS(Long.valueOf(attributeVals[3]));
+
+ return rating;
+ }
}
diff --git a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingCacheRepository.java b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingCacheRepository.java
new file mode 100644
index 0000000000..a807a61862
--- /dev/null
+++ b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingCacheRepository.java
@@ -0,0 +1,13 @@
+package com.baeldung.spring.cloud.bootstrap.svcrating.rating;
+
+import java.util.List;
+
+public interface RatingCacheRepository {
+
+ List findCachedRatingsByBookId(Long bookId);
+ Rating findCachedRatingById(Long ratingId);
+ List findAllCachedRatings();
+ boolean createRating(Rating persisted);
+ boolean updateRating(Rating persisted);
+ boolean deleteRating(Long ratingId);
+}
diff --git a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingCacheRepositoryImpl.java b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingCacheRepositoryImpl.java
new file mode 100644
index 0000000000..78f83484b2
--- /dev/null
+++ b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingCacheRepositoryImpl.java
@@ -0,0 +1,77 @@
+package com.baeldung.spring.cloud.bootstrap.svcrating.rating;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
+import org.springframework.data.redis.core.SetOperations;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.core.ValueOperations;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class RatingCacheRepositoryImpl implements InitializingBean, RatingCacheRepository {
+
+
+ @Autowired
+ @Qualifier("cacheJedisConnectionFactory")
+ private JedisConnectionFactory cacheConnectionFactory;
+
+ private StringRedisTemplate redisTemplate;
+ private ValueOperations valueOps;
+ private SetOperations setOps;
+
+
+ public List findCachedRatingsByBookId(Long bookId){
+ return setOps.members("book-" + bookId)
+ .stream()
+ .map(rtId -> Rating.fromString(valueOps.get(rtId)))
+ .map(rt -> {
+ rt.setFromCache(true);
+ return rt;
+ })
+ .collect(Collectors.toList());
+ }
+
+ public Rating findCachedRatingById(Long ratingId) {
+ return Rating.fromString(valueOps.get("rating-" + ratingId));
+ }
+
+ public List findAllCachedRatings(){
+ return redisTemplate.keys("rating*")
+ .stream()
+ .map(rtId -> Rating.fromString(valueOps.get(rtId)))
+ .collect(Collectors.toList());
+ }
+
+ public boolean createRating(Rating persisted){
+ valueOps.set("rating-"+persisted.getId(), persisted.toString());
+ setOps.add("book-"+persisted.getBookId(), "rating-"+persisted.getId());
+ return true;
+ }
+
+ public boolean updateRating(Rating persisted){
+ valueOps.set("rating-"+persisted.getId(), persisted.toString());
+ return true;
+ }
+
+ public boolean deleteRating(Long ratingId){
+ Rating toDel=Rating.fromString(valueOps.get("rating-"+ratingId));
+ setOps.remove("book-"+toDel.getBookId(), "rating-"+ratingId);
+ redisTemplate.delete("rating-"+ratingId);
+ return true;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+
+ this.redisTemplate = new StringRedisTemplate(cacheConnectionFactory);
+
+ this.valueOps = redisTemplate.opsForValue();
+ this.setOps=redisTemplate.opsForSet();
+
+ }
+}
diff --git a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingService.java b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingService.java
index 6ab5e0acff..4f099da0df 100644
--- a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingService.java
+++ b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingService.java
@@ -1,46 +1,80 @@
package com.baeldung.spring.cloud.bootstrap.svcrating.rating;
-import com.google.common.base.Preconditions;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
+import com.google.common.base.Preconditions;
+import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
@Service
@Transactional(readOnly = true)
-public class RatingService {
+public class RatingService{
@Autowired
private RatingRepository ratingRepository;
+
+ @Autowired
+ private RatingCacheRepository cacheRepository;
+
+ @HystrixCommand(commandKey="ratingsByBookIdFromDB",fallbackMethod="findCachedRatingsByBookId")
+ public List findRatingsByBookId(Long bookId) {
+ if(bookId==2)
+ throw new IllegalArgumentException("BookID 2 redirects to cache");
+
+ return ratingRepository.findRatingsByBookId(bookId);
+ }
+
+ @HystrixCommand(commandKey="ratingsByBookIdFromCache",fallbackMethod="defaultRatingsByBookId")
+ public List findCachedRatingsByBookId(Long bookId){
+ return cacheRepository.findCachedRatingsByBookId(bookId);
+ }
+
+ public List defaultRatingsByBookId(Long bookId){
+ return Collections.emptyList();
+ }
+
+
+ @HystrixCommand(commandKey="ratingsFromDB",fallbackMethod="findAllCachedRatings")
+ public List findAllRatings() {
+ return ratingRepository.findAll();
+ }
+
+ public List findAllCachedRatings(){
+ return cacheRepository.findAllCachedRatings();
+ }
+
+ @HystrixCommand(commandKey = "ratingsByIdFromDB", fallbackMethod = "findCachedRatingById", ignoreExceptions = { RatingNotFoundException.class })
public Rating findRatingById(Long ratingId) {
return Optional.ofNullable(ratingRepository.findOne(ratingId))
.orElseThrow(() -> new RatingNotFoundException("Rating not found. ID: " + ratingId));
}
- public List findRatingsByBookId(Long bookId) {
- return ratingRepository.findRatingsByBookId(bookId);
+ public Rating findCachedRatingById(Long ratingId){
+ return cacheRepository.findCachedRatingById(ratingId);
}
-
- public List findAllRatings() {
- return ratingRepository.findAll();
- }
-
+
@Transactional(propagation = Propagation.REQUIRED)
public Rating createRating(Rating rating) {
- final Rating newRating = new Rating();
+ Rating newRating = new Rating();
newRating.setBookId(rating.getBookId());
newRating.setStars(rating.getStars());
- return ratingRepository.save(newRating);
+ Rating persisted=ratingRepository.save(newRating);
+ cacheRepository.createRating(persisted);
+ return persisted;
}
@Transactional(propagation = Propagation.REQUIRED)
public void deleteRating(Long ratingId) {
ratingRepository.delete(ratingId);
+ cacheRepository.deleteRating(ratingId);
}
@Transactional(propagation = Propagation.REQUIRED)
@@ -54,7 +88,9 @@ public class RatingService {
break;
}
});
- return ratingRepository.save(rating);
+ Rating persisted= ratingRepository.save(rating);
+ cacheRepository.updateRating(persisted);
+ return persisted;
}
@Transactional(propagation = Propagation.REQUIRED)
@@ -64,4 +100,5 @@ public class RatingService {
Preconditions.checkNotNull(ratingRepository.findOne(ratingId));
return ratingRepository.save(rating);
}
+
}