diff --git a/boot-ultimate-redis/pom.xml b/boot-ultimate-redis/pom.xml index d80d144b0..7c731ffdc 100644 --- a/boot-ultimate-redis/pom.xml +++ b/boot-ultimate-redis/pom.xml @@ -38,6 +38,10 @@ org.springframework.boot spring-boot-starter-data-redis + + org.apache.commons + commons-pool2 + commons-io commons-io diff --git a/boot-ultimate-redis/src/main/java/com/example/ultimateredis/UltimateRedisApplication.java b/boot-ultimate-redis/src/main/java/com/example/ultimateredis/UltimateRedisApplication.java index 6d83a854c..493083125 100644 --- a/boot-ultimate-redis/src/main/java/com/example/ultimateredis/UltimateRedisApplication.java +++ b/boot-ultimate-redis/src/main/java/com/example/ultimateredis/UltimateRedisApplication.java @@ -2,9 +2,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cache.annotation.EnableCaching; -@EnableCaching @SpringBootApplication public class UltimateRedisApplication { diff --git a/boot-ultimate-redis/src/main/java/com/example/ultimateredis/config/CacheConfig.java b/boot-ultimate-redis/src/main/java/com/example/ultimateredis/config/CacheConfig.java index 796f0621c..d67b8efcb 100644 --- a/boot-ultimate-redis/src/main/java/com/example/ultimateredis/config/CacheConfig.java +++ b/boot-ultimate-redis/src/main/java/com/example/ultimateredis/config/CacheConfig.java @@ -1,89 +1,52 @@ package com.example.ultimateredis.config; +import io.lettuce.core.RedisURI; import java.time.Duration; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer; +import org.springframework.boot.autoconfigure.data.redis.LettuceClientConfigurationBuilderCustomizer; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurer; +import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.CacheErrorHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; import org.springframework.data.redis.cache.RedisCacheConfiguration; -import org.springframework.data.redis.cache.RedisCacheManager; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.connection.RedisStandaloneConfiguration; -import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.RedisSerializationContext; @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(CacheConfigurationProperties.class) +@EnableCaching @Slf4j public class CacheConfig implements CachingConfigurer { @Bean - @Primary - RedisCacheConfiguration defaultCacheConfig() { + RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer( + CacheConfigurationProperties cacheConfigurationProperties) { RedisCacheGZIPSerializer serializerGzip = new RedisCacheGZIPSerializer(); - - return RedisCacheConfiguration.defaultCacheConfig() - .serializeValuesWith( - RedisSerializationContext.SerializationPair.fromSerializer(serializerGzip)); - } - - @Bean - LettuceConnectionFactory redisConnectionFactory(CacheConfigurationProperties properties) { - log.info( - "Redis (/Lettuce) configuration enabled. With cache timeout {} seconds.", - properties.getTimeoutSeconds()); - - RedisStandaloneConfiguration redisStandaloneConfiguration = - new RedisStandaloneConfiguration(); - redisStandaloneConfiguration.setHostName(properties.getRedisHost()); - redisStandaloneConfiguration.setPort(properties.getRedisPort()); - return new LettuceConnectionFactory(redisStandaloneConfiguration); - } - - @Bean - RedisTemplate redisTemplate(RedisConnectionFactory cf) { - RedisTemplate redisTemplate = new RedisTemplate<>(); - redisTemplate.setConnectionFactory(cf); - return redisTemplate; + return builder -> { + builder.cacheDefaults() + .disableCachingNullValues() + .serializeValuesWith( + RedisSerializationContext.SerializationPair.fromSerializer( + serializerGzip)); + cacheConfigurationProperties + .getCacheExpirations() + .forEach( + (cacheName, timeout) -> + builder.withCacheConfiguration( + cacheName, + RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofSeconds(timeout)))); + }; } @Bean - RedisCacheConfiguration defaultCacheConfiguration(CacheConfigurationProperties properties) { - return createCacheConfiguration(properties.getTimeoutSeconds()); - } - - private RedisCacheConfiguration createCacheConfiguration(long timeoutSeconds) { - return RedisCacheConfiguration.defaultCacheConfig() - .entryTtl(Duration.ofSeconds(timeoutSeconds)); - } - - @Bean - CacheManager cacheManager( - RedisConnectionFactory redisConnectionFactory, - CacheConfigurationProperties properties, - RedisCacheConfiguration defaultCacheConfiguration) { - Map cacheConfigurations = new HashMap<>(); - - for (Entry cacheNameAndTimeout : - properties.getCacheExpirations().entrySet()) { - cacheConfigurations.put( - cacheNameAndTimeout.getKey(), - createCacheConfiguration(cacheNameAndTimeout.getValue())); - } - - return RedisCacheManager.builder(redisConnectionFactory) - .cacheDefaults(defaultCacheConfiguration) - .withInitialCacheConfigurations(cacheConfigurations) - .build(); + LettuceClientConfigurationBuilderCustomizer lettuceClientConfigurationBuilderCustomizer( + CacheConfigurationProperties properties) { + return clientConfigurationBuilder -> + clientConfigurationBuilder.apply(RedisURI.create(properties.getRedisURI())); } @Override diff --git a/boot-ultimate-redis/src/main/java/com/example/ultimateredis/config/CacheConfigurationProperties.java b/boot-ultimate-redis/src/main/java/com/example/ultimateredis/config/CacheConfigurationProperties.java index 3a6fedd2f..7d1931cea 100644 --- a/boot-ultimate-redis/src/main/java/com/example/ultimateredis/config/CacheConfigurationProperties.java +++ b/boot-ultimate-redis/src/main/java/com/example/ultimateredis/config/CacheConfigurationProperties.java @@ -10,8 +10,7 @@ public class CacheConfigurationProperties { private long timeoutSeconds = 60; - private int redisPort = 6379; - private String redisHost = "localhost"; + private String redisURI; // Mapping of cacheNames to expire-after-write timeout in seconds private Map cacheExpirations = new HashMap<>(); } diff --git a/boot-ultimate-redis/src/main/java/com/example/ultimateredis/repository/ActorRepository.java b/boot-ultimate-redis/src/main/java/com/example/ultimateredis/repository/ActorRepository.java index 58de28a4a..e2414cc9d 100644 --- a/boot-ultimate-redis/src/main/java/com/example/ultimateredis/repository/ActorRepository.java +++ b/boot-ultimate-redis/src/main/java/com/example/ultimateredis/repository/ActorRepository.java @@ -7,6 +7,7 @@ @Repository public interface ActorRepository extends CrudRepository { + Optional findByName(String name); Optional findByNameAndAge(String name, Integer age); diff --git a/boot-ultimate-redis/src/main/resources/application.properties b/boot-ultimate-redis/src/main/resources/application.properties index 551a221fb..42ab39786 100644 --- a/boot-ultimate-redis/src/main/resources/application.properties +++ b/boot-ultimate-redis/src/main/resources/application.properties @@ -1,3 +1,6 @@ cache.timeout=60 cache.cacheExpirations.myControlledCache=180 +cache.redis-uri=redis://localhost:6379 + +spring.mvc.problemdetails.enabled=true spring.threads.virtual.enabled=true \ No newline at end of file diff --git a/boot-ultimate-redis/src/test/java/com/example/ultimateredis/TestUltimateRedisApplication.java b/boot-ultimate-redis/src/test/java/com/example/ultimateredis/TestUltimateRedisApplication.java index 3e354561c..5541ff0ca 100644 --- a/boot-ultimate-redis/src/test/java/com/example/ultimateredis/TestUltimateRedisApplication.java +++ b/boot-ultimate-redis/src/test/java/com/example/ultimateredis/TestUltimateRedisApplication.java @@ -19,8 +19,7 @@ RedisContainer redisContainer(DynamicPropertyRegistry dynamicPropertyRegistry) { new RedisContainer(DockerImageName.parse("redis").withTag("7.2.5-alpine")) .withStartupAttempts(5) .withStartupTimeout(Duration.ofMinutes(10)); - dynamicPropertyRegistry.add("cache.redis-port", redisContainer::getRedisPort); - dynamicPropertyRegistry.add("cache.redis-host", redisContainer::getRedisHost); + dynamicPropertyRegistry.add("cache.redis-uri", redisContainer::getRedisURI); return redisContainer; } diff --git a/httpClients/boot-restclient/src/main/java/com/example/restclient/bootrestclient/config/RestClientConfiguration.java b/httpClients/boot-restclient/src/main/java/com/example/restclient/bootrestclient/config/RestClientConfiguration.java index 1b3869c67..1b20b257b 100644 --- a/httpClients/boot-restclient/src/main/java/com/example/restclient/bootrestclient/config/RestClientConfiguration.java +++ b/httpClients/boot-restclient/src/main/java/com/example/restclient/bootrestclient/config/RestClientConfiguration.java @@ -45,15 +45,15 @@ RestClientCustomizer restClientCustomizer( httpHeaders -> { httpHeaders.setContentType(MediaType.APPLICATION_JSON); httpHeaders.setAccept(List.of(MediaType.APPLICATION_JSON)); - }) - .requestFactory(bufferingClientHttpRequestFactory) - .requestInterceptor( - (request, body, execution) -> { - logRequest(request, body); - ClientHttpResponse response = execution.execute(request, body); - logResponse(response); - return response; }); + // .requestFactory(bufferingClientHttpRequestFactory) + // .requestInterceptor( + // (request, body, execution) -> { + // logRequest(request, body); + // ClientHttpResponse response = execution.execute(request, body); + // logResponse(response); + // return response; + // }); } private void logResponse(ClientHttpResponse response) throws IOException { diff --git a/httpClients/boot-restclient/src/test/java/com/example/restclient/bootrestclient/services/PostServiceTest.java b/httpClients/boot-restclient/src/test/java/com/example/restclient/bootrestclient/services/PostServiceTest.java new file mode 100644 index 000000000..f6dbecd17 --- /dev/null +++ b/httpClients/boot-restclient/src/test/java/com/example/restclient/bootrestclient/services/PostServiceTest.java @@ -0,0 +1,52 @@ +package com.example.restclient.bootrestclient.services; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; + +import com.example.restclient.bootrestclient.config.RestClientConfiguration; +import com.example.restclient.bootrestclient.model.response.PostDto; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Optional; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.client.RestClientTest; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.MockRestServiceServer; + +@RestClientTest( + components = {PostService.class, HttpClientService.class, RestClientConfiguration.class}) +class PostServiceTest { + + @Autowired private MockRestServiceServer mockRestServiceServer; + + @Autowired private ObjectMapper objectMapper; + + @Autowired private PostService postService; + + @Test + void findPostById() throws JsonProcessingException { + + PostDto postDto = new PostDto(1000L, 583L, "JunitTitle", "Response from RestClientTest"); + this.mockRestServiceServer + .expect(requestTo("https://jsonplaceholder.typicode.com/posts/1")) + .andExpect(method(HttpMethod.GET)) + .andExpect(header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)) + .andExpect(header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)) + .andExpect(header("apiKey", "123456")) + .andRespond( + withSuccess( + objectMapper.writeValueAsString(postDto), + MediaType.APPLICATION_JSON)); + + Optional optionalPostDto = postService.findPostById(1L); + + assertThat(optionalPostDto).isPresent(); + assertThat(optionalPostDto.get().title()).isEqualTo("JunitTitle"); + + mockRestServiceServer.verify(); + } +}