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/RatingServiceApplication.java b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/RatingServiceApplication.java
index 0d85c5c825..31ca69c139 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,32 @@
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.AdviceMode;
import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import com.netflix.appinfo.InstanceInfo;
+import com.netflix.discovery.EurekaClient;
+import com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect;
+
import zipkin.Span;
@SpringBootApplication
@EnableEurekaClient
+@EnableHystrix
+@EnableTransactionManagement(order=Ordered.LOWEST_PRECEDENCE, mode=AdviceMode.ASPECTJ)
public class RatingServiceApplication {
@Autowired
private EurekaClient eurekaClient;
@@ -48,4 +59,11 @@ public class RatingServiceApplication {
}
};
}
+
+ @Bean
+ @Primary
+ @Order(value=Ordered.HIGHEST_PRECEDENCE)
+ public HystrixCommandAspect hystrixAspect() {
+ return new HystrixCommandAspect();
+ }
}
\ 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/SecurityConfig.java b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/SecurityConfig.java
index 171fbba7af..9b6afc8059 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,20 @@ 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.PATCH,"/ratings/*").hasRole("ADMIN")
.antMatchers(HttpMethod.DELETE,"/ratings/*").hasRole("ADMIN")
.antMatchers(HttpMethod.GET,"/ratings").hasRole("ADMIN")
+ .antMatchers(HttpMethod.GET,"/hystrix").authenticated()
.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..2c7069926c 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,21 @@ 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;
+ }
+
}
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..d9f3a3584e
--- /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,118 @@
+package com.baeldung.spring.cloud.bootstrap.svcrating.rating;
+
+import java.io.IOException;
+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.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 com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class RatingCacheRepository implements InitializingBean {
+
+ @Autowired
+ private JedisConnectionFactory cacheConnectionFactory;
+
+ private StringRedisTemplate redisTemplate;
+ private ValueOperations valueOps;
+ private SetOperations setOps;
+
+ private ObjectMapper jsonMapper;
+
+ public List findCachedRatingsByBookId(Long bookId) {
+ return setOps.members("book-" + bookId)
+ .stream()
+ .map(rtId -> {
+ try {
+ return jsonMapper.readValue(valueOps.get(rtId), Rating.class);
+ } catch (IOException ex) {
+ return null;
+ }
+ })
+ .collect(Collectors.toList());
+ }
+
+ public Rating findCachedRatingById(Long ratingId) {
+
+ try {
+ return jsonMapper.readValue(valueOps.get("rating-" + ratingId), Rating.class);
+ } catch (IOException e) {
+ return null;
+ }
+
+ }
+
+ public List findAllCachedRatings() {
+ List ratings = null;
+
+ ratings = redisTemplate.keys("rating*")
+ .stream()
+ .map(rtId -> {
+ try {
+
+ return jsonMapper.readValue(valueOps.get(rtId), Rating.class);
+
+ } catch (IOException e) {
+ return null;
+ }
+ })
+ .collect(Collectors.toList());
+
+ return ratings;
+ }
+
+ public boolean createRating(Rating persisted) {
+ try {
+ valueOps.set("rating-" + persisted.getId(), jsonMapper.writeValueAsString(persisted));
+ setOps.add("book-" + persisted.getBookId(), "rating-" + persisted.getId());
+ return true;
+ } catch (JsonProcessingException ex) {
+ return false;
+ }
+ }
+
+ public boolean updateRating(Rating persisted) {
+ try {
+ valueOps.set("rating-" + persisted.getId(), jsonMapper.writeValueAsString(persisted));
+ return true;
+ } catch (JsonProcessingException e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ public boolean deleteRating(Long ratingId) {
+ Rating toDel;
+ try {
+
+ toDel = jsonMapper.readValue(valueOps.get("rating-" + ratingId), Rating.class);
+ setOps.remove("book-" + toDel.getBookId(), "rating-" + ratingId);
+ redisTemplate.delete("rating-" + ratingId);
+ return true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+
+ this.redisTemplate = new StringRedisTemplate(cacheConnectionFactory);
+
+ this.valueOps = redisTemplate.opsForValue();
+ this.setOps = redisTemplate.opsForSet();
+
+ jsonMapper = new ObjectMapper();
+ jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ }
+}
\ 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/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..395ff50bd7 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,14 +1,16 @@
package com.baeldung.spring.cloud.bootstrap.svcrating.rating;
-import com.google.common.base.Preconditions;
+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)
@@ -17,30 +19,51 @@ public class RatingService {
@Autowired
private RatingRepository ratingRepository;
+ @Autowired
+ private RatingCacheRepository cacheRepository;
+
+ @HystrixCommand(commandKey = "ratingsByBookIdFromDB", fallbackMethod = "findCachedRatingsByBookId")
+ public List findRatingsByBookId(Long bookId) {
+ return ratingRepository.findRatingsByBookId(bookId);
+ }
+
+ public List findCachedRatingsByBookId(Long bookId) {
+ return cacheRepository.findCachedRatingsByBookId(bookId);
+ }
+
+ @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 List findAllRatings() {
- return ratingRepository.findAll();
+ public Rating findCachedRatingById(Long ratingId) {
+ return cacheRepository.findCachedRatingById(ratingId);
}
@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 +77,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 +89,5 @@ public class RatingService {
Preconditions.checkNotNull(ratingRepository.findOne(ratingId));
return ratingRepository.save(rating);
}
+
}