diff --git a/pom.xml b/pom.xml index d96b6c77..9431c5d4 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ UTF-8 UTF-8 - -ea -Xms128m -Xmx512m -Dspring.profiles.active=test,metrics -Dfile.encoding=UTF-8 + -ea -Xms128m -Xmx512m -Dspring.profiles.active=test,metrics,embeddedcache -Dfile.encoding=UTF-8 -Djava.awt.headless=true -XX:CompileThreshold=1500 @@ -69,6 +69,9 @@ 1.8.13 + + 2.9.0 + 2.0.0.RELEASE 5.0.4.RELEASE @@ -77,6 +80,7 @@ 5.0.3.RELEASE 4.0.1.RELEASE 2.0.0.RELEASE + 2.0.5.RELEASE 3.7 2.7.0 @@ -200,6 +204,20 @@ ${aspectjrt.version} + + + + + org.springframework.data + spring-data-redis + ${spring-data-redis.version} + + + redis.clients + jedis + ${jedis.version} + + diff --git a/src/main/java/manon/Application.java b/src/main/java/manon/Application.java index 70317d84..0fc4fe95 100644 --- a/src/main/java/manon/Application.java +++ b/src/main/java/manon/Application.java @@ -11,6 +11,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.EnableCaching; import org.springframework.core.env.Environment; import org.springframework.data.mongodb.config.EnableMongoAuditing; import org.springframework.scheduling.annotation.EnableScheduling; @@ -20,12 +22,14 @@ import java.util.List; import static manon.app.config.SpringProfiles.METRICS; +import static manon.app.info.service.InfoServiceImpl.CACHE_GET_APPVERSION; import static manon.app.trace.document.AppTrace.Event.APP_START; import static manon.app.trace.document.AppTrace.Level.INFO; @SpringBootApplication @EnableMongoAuditing @EnableScheduling +@EnableCaching @Slf4j @RequiredArgsConstructor public class Application extends SpringBootServletInitializer { @@ -45,6 +49,7 @@ protected SpringApplicationBuilder configure(SpringApplicationBuilder applicatio } @PostConstruct + @CacheEvict(value = CACHE_GET_APPVERSION, allEntries = true) public void initApp() throws UserExistsException { String initAppEvent = "Admin username is " + userAdminService.ensureAdmin().getUsername(); appTraceService.log(INFO, APP_START, initAppEvent); diff --git a/src/main/java/manon/app/cache/CommonCacheConfig.java b/src/main/java/manon/app/cache/CommonCacheConfig.java new file mode 100644 index 00000000..98d742e4 --- /dev/null +++ b/src/main/java/manon/app/cache/CommonCacheConfig.java @@ -0,0 +1,28 @@ +package manon.app.cache; + +import org.jetbrains.annotations.NotNull; +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class CommonCacheConfig { + + /** Key generator name: generate a unique key of the class name, the method name, and all method parameters appended. */ + public static final String KEY_GENERATOR_FULL = "KEY_GENERATOR_FULL"; + + /** Generate a unique key of the class name, the method name, and all method parameters appended. */ + @NotNull + @Bean(name = KEY_GENERATOR_FULL) + public KeyGenerator keyGenerator() { + return (o, method, objects) -> { + StringBuilder sb = new StringBuilder(); + sb.append(o.getClass().getName()); + sb.append(method.getName()); + for (Object obj : objects) { + sb.append(obj.toString()); + } + return sb.toString(); + }; + } +} diff --git a/src/main/java/manon/app/cache/EmbeddedCacheConfig.java b/src/main/java/manon/app/cache/EmbeddedCacheConfig.java new file mode 100644 index 00000000..f9f6d6e1 --- /dev/null +++ b/src/main/java/manon/app/cache/EmbeddedCacheConfig.java @@ -0,0 +1,22 @@ +package manon.app.cache; + +import org.jetbrains.annotations.NotNull; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.concurrent.ConcurrentMapCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +import static manon.app.config.SpringProfiles.EMBEDDED_CACHE; + +@Configuration +@Profile(EMBEDDED_CACHE) +public class EmbeddedCacheConfig extends CachingConfigurerSupport { + + @NotNull + @Bean + public CacheManager cacheManager() { + return new ConcurrentMapCacheManager(); + } +} diff --git a/src/main/java/manon/app/cache/RedisCacheConfig.java b/src/main/java/manon/app/cache/RedisCacheConfig.java new file mode 100644 index 00000000..34bf2f4e --- /dev/null +++ b/src/main/java/manon/app/cache/RedisCacheConfig.java @@ -0,0 +1,28 @@ +package manon.app.cache; + +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.core.RedisTemplate; + +import java.time.Duration; + +import static manon.app.config.SpringProfiles.REDIS_CACHE; + +@Configuration +@Profile(REDIS_CACHE) +public class RedisCacheConfig extends CachingConfigurerSupport { + + @Bean + public RedisCacheManager cacheManager(RedisTemplate redisTemplate) { + RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofDays(1)) + .prefixKeysWith("manon_"); + return RedisCacheManager.builder(redisTemplate.getConnectionFactory()) + .cacheDefaults(config) + .build(); + } +} diff --git a/src/main/java/manon/app/config/SpringProfiles.java b/src/main/java/manon/app/config/SpringProfiles.java index 7071b870..1477b79e 100644 --- a/src/main/java/manon/app/config/SpringProfiles.java +++ b/src/main/java/manon/app/config/SpringProfiles.java @@ -7,5 +7,11 @@ public interface SpringProfiles { String METRICS = "metrics"; /** {@value}. */ - String NO_METRICS = "!metrics"; + String NOT_METRICS = "!metrics"; + + /** {@value}. */ + String REDIS_CACHE = "!embeddedcache"; + + /** {@value}. */ + String EMBEDDED_CACHE = "embeddedcache"; } diff --git a/src/main/java/manon/app/config/WebConfig.java b/src/main/java/manon/app/config/WebConfig.java index 8f5ab1ed..8238fffb 100644 --- a/src/main/java/manon/app/config/WebConfig.java +++ b/src/main/java/manon/app/config/WebConfig.java @@ -6,13 +6,13 @@ import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.view.InternalResourceViewResolver; @Configuration @EnableWebMvc @EnableAsync -public class WebConfig extends WebMvcConfigurerAdapter { +public class WebConfig implements WebMvcConfigurer { public WebConfig() { super(); diff --git a/src/main/java/manon/app/info/service/InfoServiceImpl.java b/src/main/java/manon/app/info/service/InfoServiceImpl.java index 27758ae2..b9899464 100644 --- a/src/main/java/manon/app/info/service/InfoServiceImpl.java +++ b/src/main/java/manon/app/info/service/InfoServiceImpl.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.Cacheable; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Service; @@ -10,9 +11,12 @@ @PropertySource(value = "classpath:info.properties") public class InfoServiceImpl implements InfoService { + public static final String CACHE_GET_APPVERSION = "CACHE_GET_APPVERSION"; + @Value("${version}") private String version; + @Cacheable(CACHE_GET_APPVERSION) @Override public String getAppVersion() { return version; diff --git a/src/main/java/manon/app/stats/service/NoPerformanceRecorder.java b/src/main/java/manon/app/stats/service/NoPerformanceRecorder.java index ca2da416..64f2dc94 100644 --- a/src/main/java/manon/app/stats/service/NoPerformanceRecorder.java +++ b/src/main/java/manon/app/stats/service/NoPerformanceRecorder.java @@ -3,10 +3,10 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import static manon.app.config.SpringProfiles.NO_METRICS; +import static manon.app.config.SpringProfiles.NOT_METRICS; @Service -@Profile(NO_METRICS) +@Profile(NOT_METRICS) public class NoPerformanceRecorder implements PerformanceRecorder { @Override diff --git a/src/main/java/manon/user/friendship/service/FriendshipServiceImpl.java b/src/main/java/manon/user/friendship/service/FriendshipServiceImpl.java index 15484d23..6a03ae18 100644 --- a/src/main/java/manon/user/friendship/service/FriendshipServiceImpl.java +++ b/src/main/java/manon/user/friendship/service/FriendshipServiceImpl.java @@ -20,7 +20,7 @@ public class FriendshipServiceImpl implements FriendshipService { private final UserService userService; @Override - public void keepEvents(String... ids) throws UserNotFoundException { + public void keepEvents(String... ids) { for (String id : ids) { userRepository.keepEvents(id, MAX_EVENTS); } @@ -54,19 +54,19 @@ public void acceptFriendshipRequest(String userIdFrom, String userIdTo) } @Override - public void rejectFriendshipRequest(String userIdFrom, String userIdTo) throws UserNotFoundException { + public void rejectFriendshipRequest(String userIdFrom, String userIdTo) { userRepository.rejectFriendshipRequest(userIdFrom, userIdTo); keepEvents(userIdFrom, userIdTo); } @Override - public void cancelFriendshipRequest(String userIdFrom, String userIdTo) throws UserNotFoundException { + public void cancelFriendshipRequest(String userIdFrom, String userIdTo) { userRepository.cancelFriendshipRequest(userIdFrom, userIdTo); keepEvents(userIdFrom, userIdTo); } @Override - public void revokeFriendship(String userIdFrom, String userIdTo) throws UserNotFoundException { + public void revokeFriendship(String userIdFrom, String userIdTo) { userRepository.revokeFriendship(userIdFrom, userIdTo); keepEvents(userIdFrom, userIdTo); } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 40fb18fe..93937624 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -15,3 +15,7 @@ spring: password: woot url: jdbc:mysql://127.0.0.1:3307/manon_dev?useUnicode=true&characterEncoding=utf8&autoReconnect=true jpa.hibernate.ddl-auto: create-drop + redis: + database: 2 + host: localhost + port: 6379 diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index f314824e..292fad40 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -9,6 +9,10 @@ spring: username: ${MANON_PROD_MONGODB_USERNAME} password: ${MANON_PROD_MONGODB_PASSWORD} authentication-database: ${MANON_PROD_MONGODB_AUTH_DB_NAME} + redis: + database: ${MANON_PROD_REDIS_DB_NUMBER} + host: ${MANON_PROD_REDIS_HOST} + port: ${MANON_PROD_REDIS_PORT} manon: admin: diff --git a/src/test/java/manon/app/actuator/ActuatorTest.java b/src/test/java/manon/app/actuator/ActuatorTest.java index 93801dbe..e22d9c1a 100644 --- a/src/test/java/manon/app/actuator/ActuatorTest.java +++ b/src/test/java/manon/app/actuator/ActuatorTest.java @@ -15,6 +15,7 @@ import static org.apache.http.HttpStatus.SC_UNAUTHORIZED; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalToIgnoringWhiteSpace; +import static org.hamcrest.Matchers.not; import static org.springframework.http.HttpMethod.GET; import static org.springframework.http.HttpMethod.POST; @@ -67,7 +68,8 @@ public void shouldGetFullHealthActuatorWhenAdmin() { containsString("\"status\""), containsString("\"diskSpace\""), containsString("\"mongo\""), - containsString("\"db\"") + containsString("\"db\""), + not(containsString("\"redis\"")) // tests use embedded cache, not Redis ); } diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index 0a1a78e4..771888c6 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -7,3 +7,5 @@ manon: security.bcrypt.strength: 4 batch: user-snapshot.chunk: 10 + +management.health.redis.enabled: false # disable Redis health check since we prefer embedded cache provider