Merge branch 'eugenp:master' into master

This commit is contained in:
cesarevalenti90
2023-02-28 10:06:11 +01:00
committed by GitHub
510 changed files with 5213 additions and 3043 deletions
+1
View File
@@ -84,6 +84,7 @@
<module>spring-boot-data-2</module>
<module>spring-boot-validation</module>
<module>spring-boot-data-3</module>
<module>spring-caching</module>
</modules>
<dependencyManagement>
@@ -1 +0,0 @@
/output/
@@ -1,30 +0,0 @@
## Spring Boot Camel
This module contains articles about Spring Boot with Apache Camel
### Example for the Article on Camel API with SpringBoot
To start, run:
`mvn spring-boot:run`
Then, make a POST http request to:
`http://localhost:8080/camel/api/bean`
Include the HEADER: Content-Type: application/json,
and a BODY Payload like:
`{"id": 1,"name": "World"}`
We will get a return code of 201 and the response: `Hello, World` - if the transform() method from Application class is uncommented and the process() method is commented
or return code of 201 and the response: `{"id": 10,"name": "Hello, World"}` - if the transform() method from Application class is commented and the process() method is uncommented
## Relevant articles:
- [Apache Camel with Spring Boot](https://www.baeldung.com/apache-camel-spring-boot)
- [Apache Camel Routes Testing in Spring Boot](https://www.baeldung.com/spring-boot-apache-camel-routes-testing)
- [Apache Camel Conditional Routing](https://www.baeldung.com/spring-apache-camel-conditional-routing)
- [Apache Camel Exception Handling](https://www.baeldung.com/java-apache-camel-exception-handling)
@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>spring-boot-camel</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-camel</name>
<parent>
<groupId>com.baeldung.spring-boot-modules</groupId>
<artifactId>spring-boot-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-servlet-starter</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-jackson-starter</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-swagger-java-starter</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test-spring-junit5</artifactId>
<version>${camel.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<defaultGoal>spring-boot:run</defaultGoal>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<mainClass>com.baeldung.camel.boot.testing.GreetingsFileSpringApplication</mainClass>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<java.version>11</java.version>
<camel.version>3.15.0</camel.version>
</properties>
</project>
@@ -1,107 +0,0 @@
package com.baeldung.camel;
import javax.ws.rs.core.MediaType;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.servlet.CamelHttpTransportServlet;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.model.rest.RestBindingMode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration;
import org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration;
import org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
@SpringBootApplication(exclude = { WebSocketServletAutoConfiguration.class, AopAutoConfiguration.class, OAuth2ResourceServerAutoConfiguration.class, EmbeddedWebServerFactoryCustomizerAutoConfiguration.class })
@ComponentScan(basePackages = "com.baeldung.camel")
public class Application {
@Value("${server.port}")
String serverPort;
@Value("${baeldung.api.path}")
String contextPath;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
ServletRegistrationBean servletRegistrationBean() {
ServletRegistrationBean servlet = new ServletRegistrationBean(new CamelHttpTransportServlet(), contextPath + "/*");
servlet.setName("CamelServlet");
return servlet;
}
@Component
class RestApi extends RouteBuilder {
@Override
public void configure() {
CamelContext context = new DefaultCamelContext();
// http://localhost:8080/camel/api-doc
restConfiguration().contextPath(contextPath) //
.port(serverPort)
.enableCORS(true)
.apiContextPath("/api-doc")
.apiProperty("api.title", "Test REST API")
.apiProperty("api.version", "v1")
.apiProperty("cors", "true") // cross-site
.apiContextRouteId("doc-api")
.component("servlet")
.bindingMode(RestBindingMode.json)
.dataFormatProperty("prettyPrint", "true");
/**
The Rest DSL supports automatic binding json/xml contents to/from
POJOs using Camels Data Format.
By default the binding mode is off, meaning there is no automatic
binding happening for incoming and outgoing messages.
You may want to use binding if you develop POJOs that maps to
your REST services request and response types.
*/
rest("/api/").description("Teste REST Service")
.id("api-route")
.post("/bean")
.produces(MediaType.APPLICATION_JSON)
.consumes(MediaType.APPLICATION_JSON)
// .get("/hello/{place}")
.bindingMode(RestBindingMode.auto)
.type(MyBean.class)
.enableCORS(true)
// .outType(OutBean.class)
.to("direct:remoteService");
from("direct:remoteService").routeId("direct-route")
.tracing()
.log(">>> ${body.id}")
.log(">>> ${body.name}")
// .transform().simple("blue ${in.body.name}")
.process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
MyBean bodyIn = (MyBean) exchange.getIn()
.getBody();
ExampleServices.example(bodyIn);
exchange.getIn()
.setBody(bodyIn);
}
})
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(201));
}
}
}
@@ -1,15 +0,0 @@
package com.baeldung.camel;
/**
* a Mock class to show how some other layer
* (a persistence layer, for instance)
* could be used insida a Camel
*
*/
public class ExampleServices {
public static void example(MyBean bodyIn) {
bodyIn.setName( "Hello, " + bodyIn.getName() );
bodyIn.setId(bodyIn.getId()*10);
}
}
@@ -1,18 +0,0 @@
package com.baeldung.camel;
public class MyBean {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@@ -1,19 +0,0 @@
package com.baeldung.camel.boot.testing;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
@Component
public class GreetingsFileRouter extends RouteBuilder {
@Override
public void configure() throws Exception {
from("direct:start")
.routeId("greetings-route")
.setBody(constant("Hello Baeldung Readers!"))
.to("file:output");
}
}
@@ -1,13 +0,0 @@
package com.baeldung.camel.boot.testing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GreetingsFileSpringApplication {
public static void main(String[] args) {
SpringApplication.run(GreetingsFileSpringApplication.class, args);
}
}
@@ -1,23 +0,0 @@
package com.baeldung.camel.conditional;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
@Component
public class ConditionalBeanRouter extends RouteBuilder {
@Override
public void configure() throws Exception {
from("direct:start-conditional-bean")
.routeId("conditional-bean-route")
.choice()
.when(method(FruitBean.class, "isApple"))
.setHeader("favourite", simple("Apples"))
.to("mock:result")
.otherwise()
.setHeader("favourite", header("fruit"))
.to("mock:result")
.end();
}
}
@@ -1,24 +0,0 @@
package com.baeldung.camel.conditional;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
@Component
public class ConditionalBodyRouter extends RouteBuilder {
@Override
public void configure() throws Exception {
from("direct:start-conditional")
.routeId("conditional-body-route")
.choice()
.when(body().contains("Baeldung"))
.setBody(simple("Goodbye, Baeldung!"))
.to("mock:result-body")
.otherwise()
.to("mock:result-body")
.end();
}
}
@@ -1,23 +0,0 @@
package com.baeldung.camel.conditional;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
@Component
public class ConditionalHeaderRouter extends RouteBuilder {
@Override
public void configure() throws Exception {
from("direct:start-conditional-header")
.routeId("conditional-header-route")
.choice()
.when(header("fruit").isEqualTo("Apple"))
.setHeader("favourite", simple("Apples"))
.to("mock:result")
.otherwise()
.setHeader("favourite", header("fruit"))
.to("mock:result")
.end();
}
}
@@ -1,13 +0,0 @@
package com.baeldung.camel.conditional;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ConditionalRoutingSpringApplication {
public static void main(String[] args) {
SpringApplication.run(ConditionalRoutingSpringApplication.class, args);
}
}
@@ -1,15 +0,0 @@
package com.baeldung.camel.conditional;
import org.apache.camel.Exchange;
public class FruitBean {
private FruitBean() {
}
public static boolean isApple(Exchange exchange) {
return "Apple".equals(exchange.getIn()
.getHeader("fruit"));
}
}
@@ -1,13 +0,0 @@
package com.baeldung.camel.exception;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ExceptionHandlingSpringApplication {
public static void main(String[] args) {
SpringApplication.run(ExceptionHandlingSpringApplication.class, args);
}
}
@@ -1,26 +0,0 @@
package com.baeldung.camel.exception;
import java.io.IOException;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
@Component
public class ExceptionHandlingWithDoTryRoute extends RouteBuilder {
@Override
public void configure() throws Exception {
from("direct:start-handling-exception")
.routeId("exception-handling-route")
.doTry()
.process(new IllegalArgumentExceptionThrowingProcessor())
.to("mock:received")
.doCatch(IOException.class, IllegalArgumentException.class)
.to("mock:caught")
.doFinally()
.to("mock:finally")
.end();
}
}
@@ -1,24 +0,0 @@
package com.baeldung.camel.exception;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ExceptionHandlingWithExceptionClauseRoute extends RouteBuilder {
@Autowired
private ExceptionLoggingProcessor exceptionLogger;
@Override
public void configure() throws Exception {
onException(IllegalArgumentException.class).process(exceptionLogger)
.handled(true)
.to("mock:handled");
from("direct:start-exception-clause")
.routeId("exception-clause-route")
.process(new IllegalArgumentExceptionThrowingProcessor())
.to("mock:received");
}
}
@@ -1,30 +0,0 @@
package com.baeldung.camel.exception;
import java.util.Map;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class ExceptionLoggingProcessor implements Processor {
private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionLoggingProcessor.class);
@Override
public void process(Exchange exchange) throws Exception {
Map<String, Object> headersMap = exchange.getIn().getHeaders();
if (!headersMap.isEmpty()) {
headersMap.entrySet()
.stream()
.forEach(e -> LOGGER.info("Header key [{}] -||- Header value [{}]", e.getKey(), e.getValue()));
} else {
LOGGER.info("Empty header");
}
}
}
@@ -1,30 +0,0 @@
package com.baeldung.camel.exception;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class ExceptionThrowingRoute extends RouteBuilder {
private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionThrowingRoute.class);
@Override
public void configure() throws Exception {
from("direct:start-exception")
.routeId("exception-throwing-route")
.process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
LOGGER.error("Exception Thrown");
throw new IllegalArgumentException("An exception happened on purpose");
}
}).to("mock:received");
}
}
@@ -1,20 +0,0 @@
package com.baeldung.camel.exception;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class IllegalArgumentExceptionThrowingProcessor implements Processor {
private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionLoggingProcessor.class);
@Override
public void process(Exchange exchange) throws Exception {
LOGGER.error("Exception Thrown");
throw new IllegalArgumentException("An exception happened on purpose");
}
}
@@ -1,17 +0,0 @@
logging.config=classpath:logback.xml
# the options from org.apache.camel.spring.boot.CamelConfigurationProperties can be configured here
camel.springboot.name=MyCamel
# lets listen on all ports to ensure we can be invoked from the pod IP
server.address=0.0.0.0
management.address=0.0.0.0
# lets use a different management port in case you need to listen to HTTP requests on 8080
management.port=8081
# disable all management enpoints except health
endpoints.enabled = true
endpoints.health.enabled = true
spring.main.allow-bean-definition-overriding=true
@@ -1,27 +0,0 @@
server:
port: 8080
# for example purposes of Camel version 2.18 and below
baeldung:
api:
path: '/camel'
camel:
springboot:
# The Camel context name
name: ServicesRest
# Binding health checks to a different port
management:
port: 8081
# disable all management enpoints except health
endpoints:
enabled: false
health:
enabled: true
# The application configuration properties
quickstart:
generateOrderPeriod: 10s
processOrderPeriod: 30s
@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>
@@ -1,17 +0,0 @@
package com.baeldung;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.baeldung.camel.Application;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class SpringContextTest {
@Test
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
}
}
@@ -1,32 +0,0 @@
package com.baeldung.camel.boot.testing;
import org.apache.camel.EndpointInject;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
import org.apache.camel.test.spring.junit5.MockEndpoints;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@CamelSpringBootTest
@MockEndpoints("file:output")
class GreetingsFileRouterUnitTest {
@Autowired
private ProducerTemplate template;
@EndpointInject("mock:file:output")
private MockEndpoint mock;
@Test
void whenSendBody_thenGreetingReceivedSuccessfully() throws InterruptedException {
mock.expectedBodiesReceived("Hello Baeldung Readers!");
template.sendBody("direct:start", null);
mock.assertIsSatisfied();
}
}
@@ -1,30 +0,0 @@
package com.baeldung.camel.conditional;
import org.apache.camel.EndpointInject;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@CamelSpringBootTest
class ConditionalBeanRouterUnitTest {
@Autowired
private ProducerTemplate template;
@EndpointInject("mock:result")
private MockEndpoint mock;
@Test
void whenSendBodyWithFruit_thenFavouriteHeaderReceivedSuccessfully() throws InterruptedException {
mock.expectedHeaderReceived("favourite", "Apples");
template.sendBodyAndHeader("direct:start-conditional-bean", null, "fruit", "Apple");
mock.assertIsSatisfied();
}
}
@@ -1,30 +0,0 @@
package com.baeldung.camel.conditional;
import org.apache.camel.EndpointInject;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@CamelSpringBootTest
class ConditionalBodyRouterUnitTest {
@Autowired
private ProducerTemplate template;
@EndpointInject("mock:result-body")
private MockEndpoint mock;
@Test
void whenSendBodyWithBaeldung_thenGoodbyeMessageReceivedSuccessfully() throws InterruptedException {
mock.expectedBodiesReceived("Goodbye, Baeldung!");
template.sendBody("direct:start-conditional", "Hello Baeldung Readers!");
mock.assertIsSatisfied();
}
}
@@ -1,29 +0,0 @@
package com.baeldung.camel.conditional;
import org.apache.camel.EndpointInject;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@CamelSpringBootTest
class ConditionalHeaderRouterUnitTest {
@Autowired
private ProducerTemplate template;
@EndpointInject("mock:result")
private MockEndpoint mock;
@Test
void whenSendBodyWithFruit_thenFavouriteHeaderReceivedSuccessfully() throws InterruptedException {
mock.expectedHeaderReceived("favourite", "Banana");
template.sendBodyAndHeader("direct:start-conditional-header", null, "fruit", "Banana");
mock.assertIsSatisfied();
}
}
@@ -1,30 +0,0 @@
package com.baeldung.camel.exception;
import org.apache.camel.EndpointInject;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@CamelSpringBootTest
class ExceptionHandlingWithDoTryRouteUnitTest {
@Autowired
private ProducerTemplate template;
@EndpointInject("mock:caught")
private MockEndpoint mock;
@Test
void whenSendHeaders_thenExceptionRaisedAndHandledSuccessfully() throws Exception {
mock.expectedMessageCount(1);
template.sendBodyAndHeader("direct:start-handling-exception", null, "fruit", "Banana");
mock.assertIsSatisfied();
}
}
@@ -1,30 +0,0 @@
package com.baeldung.camel.exception;
import org.apache.camel.EndpointInject;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@CamelSpringBootTest
class ExceptionHandlingWithExceptionClauseRouteUnitTest {
@Autowired
private ProducerTemplate template;
@EndpointInject("mock:handled")
private MockEndpoint mock;
@Test
void whenSendHeaders_thenExceptionRaisedAndHandledSuccessfully() throws Exception {
mock.expectedMessageCount(1);
template.sendBodyAndHeader("direct:start-exception-clause", null, "fruit", "Banana");
mock.assertIsSatisfied();
}
}
@@ -1,36 +0,0 @@
package com.baeldung.camel.exception;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.ExchangePattern;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@CamelSpringBootTest
class ExceptionThrowingRouteUnitTest {
@Autowired
private ProducerTemplate template;
@Test
void whenSendBody_thenExceptionRaisedSuccessfully() {
CamelContext context = template.getCamelContext();
Exchange exchange = context.getEndpoint("direct:start-exception")
.createExchange(ExchangePattern.InOut);
exchange.getIn().setBody("Hello Baeldung");
Exchange out = template.send("direct:start-exception", exchange);
assertTrue(out.isFailed(), "Should be failed");
assertTrue(out.getException() instanceof IllegalArgumentException, "Should be IllegalArgumentException");
assertEquals("An exception happened on purpose", out.getException().getMessage());
}
}
@@ -1,16 +0,0 @@
package com.baeldung.camel.exception;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class IllegalArgumentExceptionThrowingProcessorUnitTest {
@Test
void whenProcessed_thenIllegalArgumentExceptionRaisedSuccessfully() {
assertThrows(IllegalArgumentException.class, () -> {
new IllegalArgumentExceptionThrowingProcessor().process(null);
});
}
}
@@ -17,18 +17,6 @@
<relativePath>../../parent-boot-2</relativePath>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak.bom</groupId>
<artifactId>keycloak-adapter-bom</artifactId>
<version>${keycloak-adapter-bom.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -39,8 +27,12 @@
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -58,8 +50,4 @@
</plugins>
</build>
<properties>
<keycloak-adapter-bom.version>15.0.2</keycloak-adapter-bom.version>
</properties>
</project>
@@ -1,14 +0,0 @@
package com.baeldung.disablingkeycloak;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class KeycloakConfiguration {
@Bean
public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
}
@@ -1,38 +1,34 @@
package com.baeldung.disablingkeycloak;
import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
@KeycloakConfiguration
@Configuration
@EnableWebSecurity
@ConditionalOnProperty(name = "keycloak.enabled", havingValue = "true", matchIfMissing = true)
public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
public class KeycloakSecurityConfig {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(keycloakAuthenticationProvider());
@Bean
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new NullAuthenticatedSessionStrategy();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.authorizeRequests()
.anyRequest()
.authenticated();
.authorizeHttpRequests(auth -> auth.anyRequest()
.authenticated());
http.oauth2Login();
http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
return http.build();
}
}
@@ -1,7 +0,0 @@
# Keycloak authentication is enabled for production.
keycloak.enabled=true
keycloak.realm=SpringBootKeycloak
keycloak.auth-server-url=http://localhost:8180/auth
keycloak.resource=login-app
keycloak.bearer-only=true
keycloak.ssl-required=external
@@ -0,0 +1,10 @@
server.port=8081
keycloak.enabled=true
spring.security.oauth2.client.registration.keycloak.client-id=login-app
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.scope=openid
spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8080/realms/SpringBootKeycloak
spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/SpringBootKeycloak
@@ -8,34 +8,13 @@
<packaging>jar</packaging>
<description>Demo project for Spring Boot Logging with Log4J2</description>
<!-- this needs to use the boot parent directly in order to not inherit logback dependencies -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath />
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-2</relativePath>
</parent>
<dependencyManagement>
<dependencies>
<!-- for junit5 to successfully be able to discover junit4 tests, we need 4.12+ version of -->
<!-- junit:junit in the classpath. hence forcing the latest 4.13.2 available version for -->
<!-- junit5 compatibility -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>${junit-jupiter.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -62,7 +41,6 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -96,10 +74,6 @@
<start-class>com.baeldung.springbootlogging.SpringBootLoggingApplication</start-class>
<spring-boot-starter-log4j.version>1.3.8.RELEASE</spring-boot-starter-log4j.version>
<gelfj.version>1.1.16</gelfj.version>
<lombok.version>1.18.24</lombok.version>
<!-- used only in dependency management to force this version, not included as a direct dependency -->
<junit.version>4.13.2</junit.version>
<junit-jupiter.version>5.8.1</junit-jupiter.version>
<log4j2.version>2.17.1</log4j2.version>
</properties>
@@ -2,4 +2,5 @@
- [Swagger: Specify Two Responses with the Same Response Code](https://www.baeldung.com/swagger-two-responses-one-response-code)
- [Specify an Array of Strings as Body Parameters in Swagger](https://www.baeldung.com/swagger-body-array-of-strings)
- [Swagger @ApiParam vs @ApiModelProperty](https://www.baeldung.com/swagger-apiparam-vs-apimodelproperty)
- [Swagger @ApiParam vs @ApiModelProperty](https://www.baeldung.com/swagger-apiparam-vs-apimodelproperty)
- [Map Date Types With OpenAPI Generator](https://www.baeldung.com/openapi-map-date-types)
@@ -0,0 +1,3 @@
## Relevant articles
- [Spring Boot Cache with Redis](https://www.baeldung.com/spring-boot-redis-cache)
- [Setting Time-To-Live Value for Caching](https://www.baeldung.com/spring-setting-ttl-value-cache)
@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-caching-2</artifactId>
<version>0.1-SNAPSHOT</version>
<name>spring-caching-2</name>
<packaging>jar</packaging>
<parent>
<groupId>com.baeldung.spring-boot-modules</groupId>
<artifactId>spring-boot-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>it.ozimov</groupId>
<artifactId>embedded-redis</artifactId>
<version>${embedded.redis.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<embedded.redis.version>0.7.3</embedded.redis.version>
</properties>
</project>
@@ -0,0 +1,33 @@
package com.baeldung.caching.redis;
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import java.time.Duration;
import static org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;
@Configuration
public class CacheConfig {
@Bean
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
return (builder) -> builder
.withCacheConfiguration("itemCache",
RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10)))
.withCacheConfiguration("customerCache",
RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)));
}
@Bean
public RedisCacheConfiguration cacheConfiguration() {
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(60))
.disableCachingNullValues()
.serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
}
}
@@ -0,0 +1,21 @@
package com.baeldung.caching.redis;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.io.Serializable;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class Item implements Serializable {
@Id
String id;
String description;
}
@@ -0,0 +1,19 @@
package com.baeldung.caching.redis;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
@AllArgsConstructor
public class ItemController {
private final ItemService itemService;
@GetMapping("/item/{id}")
public Item getItemById(@PathVariable String id) {
return itemService.getItemForId(id);
}
}
@@ -0,0 +1,6 @@
package com.baeldung.caching.redis;
import org.springframework.data.repository.CrudRepository;
public interface ItemRepository extends CrudRepository<Item, String> {
}
@@ -0,0 +1,19 @@
package com.baeldung.caching.redis;
import lombok.AllArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
@AllArgsConstructor
public class ItemService {
private final ItemRepository itemRepository;
@Cacheable(value = "itemCache")
public Item getItemForId(String id) {
return itemRepository.findById(id)
.orElseThrow(RuntimeException::new);
}
}
@@ -0,0 +1,14 @@
package com.baeldung.caching.redis;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching
public class RedisCacheApplication {
public static void main(String[] args) {
SpringApplication.run(RedisCacheApplication.class, args);
}
}
@@ -0,0 +1,13 @@
package com.baeldung.caching.ttl;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class CachingTTLApplication {
public static void main(String[] args) {
SpringApplication.run(CachingTTLApplication.class, args);
}
}
@@ -0,0 +1,17 @@
package com.baeldung.caching.ttl.config;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableCaching
public class SpringCachingConfig {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("hotels");
}
}
@@ -0,0 +1,29 @@
package com.baeldung.caching.ttl.controller;
import com.baeldung.caching.ttl.service.HotelService;
import com.baeldung.caching.ttl.model.Hotel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/hotel")
public class HotelController {
private final HotelService hotelService;
@Autowired
public HotelController(HotelService hotelService) {
this.hotelService = hotelService;
}
@GetMapping
@ResponseStatus(HttpStatus.OK)
public List<Hotel> getAllHotels() {
return hotelService.getAllHotels();
}
}
@@ -0,0 +1,26 @@
package com.baeldung.caching.ttl.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import java.util.LinkedHashMap;
import java.util.Map;
@ControllerAdvice
public class ControllerAdvisor extends ResponseEntityExceptionHandler {
@ExceptionHandler(ElementNotFoundException.class)
public ResponseEntity<Object> handleNodataFoundException(
ElementNotFoundException ex) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("status", 404);
body.put("error", "Not Found");
body.put("message", ex.getMessage());
return new ResponseEntity<>(body, HttpStatus.NOT_FOUND);
}
}
@@ -0,0 +1,13 @@
package com.baeldung.caching.ttl.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ElementNotFoundException extends RuntimeException {
private static final long serialVersionUID = -5218143265247846948L;
public ElementNotFoundException(String message) {
super(message);
}
}
@@ -0,0 +1,65 @@
package com.baeldung.caching.ttl.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.io.Serializable;
import java.util.Objects;
@Entity
public class City implements Serializable {
private static final long serialVersionUID = 3252591505029724236L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double cityCentreLattitude;
private double cityCentreLongitude;
public City() {}
public City(Long id, String name, double cityCentreLatitude, double cityCentreLongitude) {
this.id = id;
this.name = name;
this.cityCentreLattitude = cityCentreLatitude;
this.cityCentreLongitude = cityCentreLongitude;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public double getCityCentreLatitude() {
return cityCentreLattitude;
}
public double getCityCentreLongitude() {
return cityCentreLongitude;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
City city = (City) o;
if (Double.compare(city.cityCentreLattitude, cityCentreLattitude) != 0) return false;
if (Double.compare(city.cityCentreLongitude, cityCentreLongitude) != 0) return false;
if (!Objects.equals(id, city.id)) return false;
return Objects.equals(name, city.name);
}
}
@@ -0,0 +1,131 @@
package com.baeldung.caching.ttl.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Objects;
@Entity
public class Hotel implements Serializable {
private static final long serialVersionUID = 5560221391479816650L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Double rating;
@ManyToOne(fetch = FetchType.EAGER)
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
private City city;
private String address;
private double lattitude;
private double longitude;
private boolean deleted = false;
public Hotel() {}
public Hotel(
Long id,
String name,
Double rating,
City city,
String address,
double lattitude,
double longitude,
boolean deleted) {
this.id = id;
this.name = name;
this.rating = rating;
this.city = city;
this.address = address;
this.lattitude = lattitude;
this.longitude = longitude;
this.deleted = deleted;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getRating() {
return rating;
}
public void setRating(Double rating) {
this.rating = rating;
}
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public double getLatitude() {
return lattitude;
}
public void setLatitude(double latitude) {
this.lattitude = latitude;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Hotel hotel = (Hotel) o;
if (Double.compare(hotel.lattitude, lattitude) != 0) return false;
if (Double.compare(hotel.longitude, longitude) != 0) return false;
if (deleted != hotel.deleted) return false;
if (!Objects.equals(id, hotel.id)) return false;
if (!Objects.equals(name, hotel.name)) return false;
if (!Objects.equals(rating, hotel.rating)) return false;
if (!Objects.equals(city, hotel.city)) return false;
return Objects.equals(address, hotel.address);
}
}
@@ -0,0 +1,6 @@
package com.baeldung.caching.ttl.repository;
import com.baeldung.caching.ttl.model.City;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CityRepository extends JpaRepository<City, Long> {}
@@ -0,0 +1,19 @@
package com.baeldung.caching.ttl.repository;
import com.baeldung.caching.ttl.model.Hotel;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface HotelRepository extends JpaRepository<Hotel, Long> {
default List<Hotel> getAllHotels() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return findAll();
}
}
@@ -0,0 +1,35 @@
package com.baeldung.caching.ttl.service;
import com.baeldung.caching.ttl.repository.HotelRepository;
import com.baeldung.caching.ttl.model.Hotel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class HotelService {
private final HotelRepository hotelRepository;
Logger logger = LoggerFactory.getLogger(HotelService.class);
HotelService(HotelRepository hotelRepository) {
this.hotelRepository = hotelRepository;
}
@Cacheable("hotels")
public List<Hotel> getAllHotels() {
return hotelRepository.getAllHotels();
}
@CacheEvict(value = "hotels", allEntries = true)
@Scheduled(fixedRateString = "${caching.spring.hotelListTTL}")
public void emptyHotelsCache() {
logger.info("emptying Hotels cache");
}
}
@@ -0,0 +1,16 @@
package com.baeldung.caching.ttl.service;
import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizer;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.stereotype.Component;
import static java.util.Arrays.asList;
@Component
public class SpringCacheCustomizer implements CacheManagerCustomizer<ConcurrentMapCacheManager> {
@Override
public void customize(ConcurrentMapCacheManager cacheManager) {
cacheManager.setCacheNames(asList("hotels"));
}
}
@@ -0,0 +1,13 @@
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# Enabling H2 Console
spring.h2.console.enabled=true
spring.h2.console.path=/h2
spring.jpa.hibernate.ddl-auto=update
#setting cache TTL
caching.spring.hotelListTTL=43200
# Connection details
#spring.redis.host=localhost
#spring.redis.port=6379
@@ -0,0 +1,67 @@
DROP TABLE IF EXISTS ITEM;
create table ITEM
(
ID CHARACTER VARYING not null,
DESCRIPTION CHARACTER VARYING,
constraint ITEM_PK
primary key (ID)
);
DROP TABLE IF EXISTS HOTEL;
DROP TABLE IF EXISTS CITY;
create table CITY
(
id bigint,
name varchar,
city_centre_lattitude double,
city_centre_longitude double,
constraint city_pk
primary key (id)
);
create table hotel
(
id bigint auto_increment,
name varchar,
deleted boolean,
rating double,
city_id bigint,
address varchar,
lattitude varchar,
longitude varchar,
constraint hotel_pk
primary key (id),
constraint "hotel_CITY_null_fk"
foreign key (city_id) references CITY (id)
);
INSERT INTO ITEM VALUES('abc','ITEM1');
INSERT INTO city(id, name, city_centre_lattitude, city_centre_longitude)
VALUES (1, 'Amsterdam', 52.368780, 4.903303);
INSERT INTO city(id, name, city_centre_lattitude, city_centre_longitude)
VALUES (2, 'Manchester', 53.481062, -2.237706);
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
VALUES ('Monaghan Hotel', false, 9.2, 1, 'Weesperbuurt en Plantage', 52.364799, 4.908971);
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
VALUES ('The Thornton Council Hotel', false, 6.3, 1, 'Waterlooplein', 52.3681563, 4.9010029);
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
VALUES ('McZoe Trescothiks Hotel', false, 9.8, 1, 'Oude Stad, Harlem', 52.379577, 4.633547);
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
VALUES ('Stay Schmtay Hotel', false, 8.7, 1, 'Jan van Galenstraat', 52.3756755, 4.8668628);
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
VALUES ('Fitting Image Hotel', false, NULL, 1, 'Staatsliedenbuurt', 52.380936, 4.8708297);
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
VALUES ('Raymond of Amsterdam Hotel', false, NULL, 1, '22 High Avenue', 52.3773989, 4.8846443);
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
VALUES ('201 Deansgate Hotel', false, 7.3, 2, '201 Deansgate', 53.4788305, -2.2484721);
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
VALUES ('Fountain Street Hotel', true, 3.0, 2, '35 Fountain Street', 53.4811298, -2.2402227);
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
VALUES ('Sunlight House', false, 4.3, 2, 'Little Quay St', 53.4785129, -2.2505943);
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
VALUES ('St Georges House', false, 9.6, 2, '56 Peter St', 53.477822, -2.2462002);
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
VALUES ('Marriot Bonvoy', false, 9.6, 1, 'Hans Zimmerstraat', 53.477872, -2.2462003);
@@ -0,0 +1,84 @@
package com.baeldung.caching.redis;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import redis.embedded.RedisServer;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@Import({ CacheConfig.class, ItemService.class })
@ExtendWith(SpringExtension.class)
@ImportAutoConfiguration(classes = { CacheAutoConfiguration.class, RedisAutoConfiguration.class })
@EnableCaching
class ItemServiceCachingIntegrationTest {
private static final String AN_ID = "id-1";
private static final String A_DESCRIPTION = "an item";
@MockBean
private ItemRepository mockItemRepository;
@Autowired
private ItemService itemService;
@Autowired
private CacheManager cacheManager;
@Test
void givenRedisCaching_whenFindItemById_thenItemReturnedFromCache() {
Item anItem = new Item(AN_ID, A_DESCRIPTION);
given(mockItemRepository.findById(AN_ID))
.willReturn(Optional.of(anItem));
Item itemCacheMiss = itemService.getItemForId(AN_ID);
Item itemCacheHit = itemService.getItemForId(AN_ID);
assertThat(itemCacheMiss).isEqualTo(anItem);
assertThat(itemCacheHit).isEqualTo(anItem);
verify(mockItemRepository, times(1)).findById(AN_ID);
assertThat(itemFromCache()).isEqualTo(anItem);
}
private Object itemFromCache() {
return cacheManager.getCache("itemCache").get(AN_ID).get();
}
@TestConfiguration
static class EmbeddedRedisConfiguration {
private final RedisServer redisServer;
public EmbeddedRedisConfiguration() {
this.redisServer = new RedisServer();
}
@PostConstruct
public void startRedis() {
redisServer.start();
}
@PreDestroy
public void stopRedis() {
this.redisServer.stop();
}
}
}
@@ -0,0 +1,43 @@
package com.baeldung.caching.ttl;
import com.baeldung.caching.ttl.repository.HotelRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import static org.hamcrest.Matchers.hasSize;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@AutoConfigureMockMvc
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:data.sql")
@SlowTest
class HotelControllerIntegrationTest {
@Autowired private MockMvc mockMvc;
@Autowired private HotelRepository repository;
@Test
@DisplayName("When all hotels requested then request is successful")
void whenAllHotelsRequested_thenRequestIsSuccessful() throws Exception {
mockMvc
.perform(get("/hotel"))
.andExpect(status().isOk());
}
@Test
@DisplayName("When all hotels are requested then they correct number of hotels is returned")
void whenAllHotelsRequested_thenReturnAllHotels() throws Exception {
mockMvc
.perform(get("/hotel"))
.andExpect(jsonPath("$", hasSize((int) repository.findAll().stream().count())));
}
}
@@ -0,0 +1,4 @@
package com.baeldung.caching.ttl;
public @interface SlowTest {
}
@@ -0,0 +1,15 @@
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# Enabling H2 Console
spring.h2.console.enabled=true
spring.h2.console.path=/h2
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=false
caching.spring.hotelListTTL=43200
# Connection details
#spring.redis.host=localhost
#spring.redis.port=6379
server.port=8000
@@ -0,0 +1,8 @@
### Relevant articles:
- [Introduction To Ehcache](http://www.baeldung.com/ehcache)
- [A Guide To Caching in Spring](http://www.baeldung.com/spring-cache-tutorial)
- [Spring Cache Creating a Custom KeyGenerator](http://www.baeldung.com/spring-cache-custom-keygenerator)
- [Cache Eviction in Spring Boot](https://www.baeldung.com/spring-boot-evict-cache)
- [Using Multiple Cache Managers in Spring](https://www.baeldung.com/spring-multiple-cache-managers)
- [Testing @Cacheable on Spring Data Repositories](https://www.baeldung.com/spring-data-testing-cacheable)
- [Spring Boot Ehcache Example](https://www.baeldung.com/spring-boot-ehcache)
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-caching</artifactId>
<version>0.1-SNAPSHOT</version>
<name>spring-caching</name>
<packaging>war</packaging>
<parent>
<groupId>com.baeldung.spring-boot-modules</groupId>
<artifactId>spring-boot-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
</dependencies>
<properties>
<ehcache.version>3.5.2</ehcache.version>
</properties>
</project>
@@ -0,0 +1,13 @@
package com.baeldung.cachetest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@@ -0,0 +1,10 @@
package com.baeldung.cachetest.config;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableCaching
public class CacheConfig {
}
@@ -0,0 +1,17 @@
package com.baeldung.cachetest.config;
import org.ehcache.event.CacheEvent;
import org.ehcache.event.CacheEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CacheEventLogger implements CacheEventListener<Object, Object> {
private static final Logger log = LoggerFactory.getLogger(CacheEventLogger.class);
@Override
public void onEvent(CacheEvent<? extends Object, ? extends Object> cacheEvent) {
log.info("Cache event {} for item with key {}. Old value = {}, New value = {}", cacheEvent.getType(), cacheEvent.getKey(), cacheEvent.getOldValue(), cacheEvent.getNewValue());
}
}
@@ -0,0 +1,29 @@
package com.baeldung.cachetest.rest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.baeldung.cachetest.service.NumberService;
@RestController
@RequestMapping(path = "/number", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class NumberController {
private final static Logger log = LoggerFactory.getLogger(NumberController.class);
@Autowired
private NumberService numberService;
@GetMapping(path = "/square/{number}")
public String getThing(@PathVariable Long number) {
log.info("call numberService to square {}", number);
return String.format("{\"square\": %s}", numberService.square(number));
}
}
@@ -0,0 +1,23 @@
package com.baeldung.cachetest.service;
import java.math.BigDecimal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class NumberService {
private final static Logger log = LoggerFactory.getLogger(NumberService.class);
@Cacheable(value = "squareCache", key = "#number", condition = "#number>10")
public BigDecimal square(Long number) {
BigDecimal square = BigDecimal.valueOf(number)
.multiply(BigDecimal.valueOf(number));
log.info("square of {} is {}", number, square);
return square;
}
}
@@ -0,0 +1,18 @@
package com.baeldung.caching.boot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
//import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@EnableCaching
//to run any module like ehcache,caching or multiplecachemanager in local machine
//add it's package for ComponentScan like below
//@ComponentScan("com.baeldung.multiplecachemanager")
public class CacheApplication {
public static void main(String[] args) {
SpringApplication.run(CacheApplication.class, args);
}
}
@@ -0,0 +1,24 @@
package com.baeldung.caching.boot;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizer;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.stereotype.Component;
import static java.util.Arrays.asList;
@Component
public class SimpleCacheCustomizer implements CacheManagerCustomizer<ConcurrentMapCacheManager> {
private static final Logger LOGGER = LoggerFactory.getLogger(SimpleCacheCustomizer.class);
static final String USERS_CACHE = "users";
static final String TRANSACTIONS_CACHE = "transactions";
@Override
public void customize(ConcurrentMapCacheManager cacheManager) {
LOGGER.info("Customizing Cache Manager");
cacheManager.setCacheNames(asList(USERS_CACHE, TRANSACTIONS_CACHE));
}
}
@@ -0,0 +1,30 @@
package com.baeldung.caching.config;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
@EnableCaching
@Configuration
public class ApplicationCacheConfig {
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
Cache booksCache = new ConcurrentMapCache("books");
cacheManager.setCaches(Arrays.asList(booksCache));
return cacheManager;
}
@Bean("customKeyGenerator")
public KeyGenerator keyGenerator() {
return new CustomKeyGenerator();
}
}
@@ -0,0 +1,25 @@
package com.baeldung.caching.config;
import java.util.Arrays;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableCaching
@ComponentScan("com.baeldung.caching.example")
public class CachingConfig {
@Bean
public CacheManager cacheManager() {
final SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("directory"), new ConcurrentMapCache("addresses")));
return cacheManager;
}
}
@@ -0,0 +1,14 @@
package com.baeldung.caching.config;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
public class CustomKeyGenerator implements KeyGenerator {
public Object generate(Object target, Method method, Object... params) {
return target.getClass().getSimpleName() + "_" + method.getName() + "_"
+ StringUtils.arrayToDelimitedString(params, "_");
}
}
@@ -0,0 +1,18 @@
package com.baeldung.caching.eviction.controllers;
import com.baeldung.caching.eviction.service.CachingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CachingController {
@Autowired
CachingService cachingService;
@GetMapping("clearAllCaches")
public void clearAllCaches() {
cachingService.evictAllCaches();
}
}
@@ -0,0 +1,53 @@
package com.baeldung.caching.eviction.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class CachingService {
@Autowired
CacheManager cacheManager;
public void putToCache(String cacheName, String key, String value) {
cacheManager.getCache(cacheName).put(key, value);
}
public String getFromCache(String cacheName, String key) {
String value = null;
if (cacheManager.getCache(cacheName).get(key) != null) {
value = cacheManager.getCache(cacheName).get(key).get().toString();
}
return value;
}
@CacheEvict(value = "first", key = "#cacheKey")
public void evictSingleCacheValue(String cacheKey) {
}
@CacheEvict(value = "first", allEntries = true)
public void evictAllCacheValues() {
}
public void evictSingleCacheValue(String cacheName, String cacheKey) {
cacheManager.getCache(cacheName).evict(cacheKey);
}
public void evictAllCacheValues(String cacheName) {
cacheManager.getCache(cacheName).clear();
}
public void evictAllCaches() {
cacheManager.getCacheNames()
.parallelStream()
.forEach(cacheName -> cacheManager.getCache(cacheName).clear());
}
@Scheduled(fixedRate = 6000)
public void evictAllcachesAtIntervals() {
evictAllCaches();
}
}
@@ -0,0 +1,76 @@
package com.baeldung.caching.example;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
public abstract class AbstractService {
// this method configuration is equivalent to xml configuration
@Cacheable(value = "addresses", key = "#customer.name")
public String getAddress(final Customer customer) {
return customer.getAddress();
}
/**
* The method returns the customer's address,
only it doesn't find it the cache- addresses and directory.
*
* @param customer the customer
* @return the address
*/
@Cacheable({ "addresses", "directory" })
public String getAddress1(final Customer customer) {
return customer.getAddress();
}
/**
* The method returns the customer's address,
but refreshes all the entries in the cache to load new ones.
*
* @param customer the customer
* @return the address
*/
@CacheEvict(value = "addresses", allEntries = true)
public String getAddress2(final Customer customer) {
return customer.getAddress();
}
/**
* The method returns the customer's address,
but not before selectively evicting the cache as per specified paramters.
*
* @param customer the customer
* @return the address
*/
@Caching(evict = { @CacheEvict("addresses"), @CacheEvict(value = "directory", key = "#customer.name") })
public String getAddress3(final Customer customer) {
return customer.getAddress();
}
/**
* The method uses the class level cache to look up for entries.
*
* @param customer the customer
* @return the address
*/
@Cacheable
// parameter not required as we have declared it using @CacheConfig
public String getAddress4(final Customer customer) {
return customer.getAddress();
}
/**
* The method selectively caches the results that meet the predefined criteria.
*
* @param customer the customer
* @return the address
*/
@CachePut(value = "addresses", condition = "#customer.name=='Tom'")
// @CachePut(value = "addresses", unless = "#result.length>64")
public String getAddress5(final Customer customer) {
return customer.getAddress();
}
}
@@ -0,0 +1,21 @@
package com.baeldung.caching.example;
import com.baeldung.caching.model.Book;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class BookService {
@Cacheable(value="books", keyGenerator="customKeyGenerator")
public List<Book> getBooks() {
List<Book> books = new ArrayList<Book>();
books.add(new Book(1, "The Counterfeiters", "André Gide"));
books.add(new Book(2, "Peer Gynt and Hedda Gabler", "Henrik Ibsen"));
return books;
}
}
@@ -0,0 +1,48 @@
package com.baeldung.caching.example;
public class Customer {
private int id;
private String name;
private String address;
public Customer() {
super();
}
public Customer(final String name, final String address) {
this.name = name;
this.address = address;
}
//
public int getId() {
return id;
}
public void setId(final int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(final String address) {
this.address = address;
}
public void setCustomerAddress(final String address) {
this.address = name + "," + address;
}
}
@@ -0,0 +1,79 @@
package com.baeldung.caching.example;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Component;
@Component
@CacheConfig(cacheNames = { "addresses" })
public class CustomerDataService {
// this method configuration is equivalent to xml configuration
@Cacheable(value = "addresses", key = "#customer.name")
public String getAddress(final Customer customer) {
return customer.getAddress();
}
/**
* The method returns the customer's address,
only it doesn't find it the cache- addresses and directory.
*
* @param customer the customer
* @return the address
*/
@Cacheable({ "addresses", "directory" })
public String getAddress1(final Customer customer) {
return customer.getAddress();
}
/**
* The method returns the customer's address,
but refreshes all the entries in the cache to load new ones.
*
* @param customer the customer
* @return the address
*/
@CacheEvict(value = "addresses", allEntries = true)
public String getAddress2(final Customer customer) {
return customer.getAddress();
}
/**
* The method returns the customer's address,
but not before selectively evicting the cache as per specified paramters.
*
* @param customer the customer
* @return the address
*/
@Caching(evict = { @CacheEvict("addresses"), @CacheEvict(value = "directory", key = "#customer.name") })
public String getAddress3(final Customer customer) {
return customer.getAddress();
}
/**
* The method uses the class level cache to look up for entries.
*
* @param customer the customer
* @return the address
*/
@Cacheable
// parameter not required as we have declared it using @CacheConfig
public String getAddress4(final Customer customer) {
return customer.getAddress();
}
/**
* The method selectively caches the results that meet the predefined criteria.
*
* @param customer the customer
* @return the address
*/
@CachePut(value = "addresses", condition = "#customer.name=='Tom'")
// @CachePut(value = "addresses", unless = "#result.length>64")
public String getAddress5(final Customer customer) {
return customer.getAddress();
}
}
@@ -0,0 +1,10 @@
package com.baeldung.caching.example;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.stereotype.Component;
@Component
@CacheConfig(cacheNames = { "addresses" })
public class CustomerServiceWithParent extends AbstractService {
//
}
@@ -0,0 +1,41 @@
package com.baeldung.caching.model;
public class Book {
private int id;
private String author;
private String title;
public Book() {
}
public Book(int id, String author, String title) {
this.id = id;
this.author = author;
this.title = title;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
@@ -0,0 +1,28 @@
package com.baeldung.ehcache.calculator;
import com.baeldung.ehcache.config.CacheHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SquaredCalculator {
private static final Logger LOGGER = LoggerFactory.getLogger(SquaredCalculator.class);
private CacheHelper cache;
public int getSquareValueOfNumber(int input) {
if (cache.getSquareNumberCache().containsKey(input)) {
return cache.getSquareNumberCache().get(input);
}
LOGGER.debug("Calculating square value of {} and caching result.", input);
int squaredValue = (int) Math.pow(input, 2);
cache.getSquareNumberCache().put(input, squaredValue);
return squaredValue;
}
public void setCache(CacheHelper cache) {
this.cache = cache;
}
}
@@ -0,0 +1,29 @@
package com.baeldung.ehcache.config;
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
public class CacheHelper {
private CacheManager cacheManager;
private Cache<Integer, Integer> squareNumberCache;
public CacheHelper() {
cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build();
cacheManager.init();
squareNumberCache = cacheManager.createCache("squaredNumber", CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, Integer.class, ResourcePoolsBuilder.heap(10)));
}
public Cache<Integer, Integer> getSquareNumberCache() {
return squareNumberCache;
}
public Cache<Integer, Integer> getSquareNumberCacheFromCacheManager() {
return cacheManager.getCache("squaredNumber", Integer.class, Integer.class);
}
}
@@ -0,0 +1,28 @@
package com.baeldung.multiplecachemanager.bo;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import com.baeldung.multiplecachemanager.entity.Customer;
import com.baeldung.multiplecachemanager.entity.Order;
import com.baeldung.multiplecachemanager.repository.CustomerDetailRepository;
@Component
public class CustomerDetailBO {
@Autowired
private CustomerDetailRepository customerDetailRepository;
@Cacheable(cacheNames = "customers")
public Customer getCustomerDetail(Integer customerId) {
return customerDetailRepository.getCustomerDetail(customerId);
}
@Cacheable(cacheNames = "customerOrders", cacheManager = "alternateCacheManager")
public List<Order> getCustomerOrders(Integer customerId) {
return customerDetailRepository.getCustomerOrders(customerId);
}
}
@@ -0,0 +1,25 @@
package com.baeldung.multiplecachemanager.bo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import com.baeldung.multiplecachemanager.entity.Order;
import com.baeldung.multiplecachemanager.repository.OrderDetailRepository;
@Component
public class OrderDetailBO {
@Autowired
private OrderDetailRepository orderDetailRepository;
@Cacheable(cacheNames = "orders", cacheResolver = "cacheResolver")
public Order getOrderDetail(Integer orderId) {
return orderDetailRepository.getOrderDetail(orderId);
}
@Cacheable(cacheNames = "orderprice", cacheResolver = "cacheResolver")
public double getOrderPrice(Integer orderId) {
return orderDetailRepository.getOrderPrice(orderId);
}
}
@@ -0,0 +1,40 @@
package com.baeldung.multiplecachemanager.config;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//import org.springframework.context.annotation.Primary;
import com.github.benmanes.caffeine.cache.Caffeine;
@Configuration
@EnableCaching
public class MultipleCacheManagerConfig extends CachingConfigurerSupport {
@Bean
//@Primary
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager("customers", "orders");
cacheManager.setCaffeine(Caffeine.newBuilder()
.initialCapacity(200)
.maximumSize(500)
.weakKeys()
.recordStats());
return cacheManager;
}
@Bean
public CacheManager alternateCacheManager() {
return new ConcurrentMapCacheManager("customerOrders", "orderprice");
}
@Bean
public CacheResolver cacheResolver() {
return new MultipleCacheResolver(alternateCacheManager(), cacheManager());
}
}
@@ -0,0 +1,38 @@
package com.baeldung.multiplecachemanager.config;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
import org.springframework.cache.interceptor.CacheResolver;
public class MultipleCacheResolver implements CacheResolver {
private final CacheManager simpleCacheManager;
private final CacheManager caffeineCacheManager;
private static final String ORDER_CACHE = "orders";
private static final String ORDER_PRICE_CACHE = "orderprice";
public MultipleCacheResolver(CacheManager simpleCacheManager, CacheManager caffeineCacheManager) {
this.simpleCacheManager = simpleCacheManager;
this.caffeineCacheManager = caffeineCacheManager;
}
@Override
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
Collection<Cache> caches = new ArrayList<Cache>();
if ("getOrderDetail".equals(context.getMethod()
.getName())) {
caches.add(caffeineCacheManager.getCache(ORDER_CACHE));
} else {
caches.add(simpleCacheManager.getCache(ORDER_PRICE_CACHE));
}
return caches;
}
}
@@ -0,0 +1,43 @@
package com.baeldung.multiplecachemanager.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.baeldung.multiplecachemanager.bo.CustomerDetailBO;
import com.baeldung.multiplecachemanager.bo.OrderDetailBO;
import com.baeldung.multiplecachemanager.entity.Customer;
import com.baeldung.multiplecachemanager.entity.Order;
@RestController
public class MultipleCacheManagerController {
@Autowired
private CustomerDetailBO customerDetailBO;
@Autowired
private OrderDetailBO orderDetailBO;
@GetMapping(value = "/getCustomer/{customerid}")
public Customer getCustomer(@PathVariable Integer customerid) {
return customerDetailBO.getCustomerDetail(customerid);
}
@GetMapping(value = "/getCustomerOrders/{customerid}")
public List<Order> getCustomerOrders(@PathVariable Integer customerid) {
return customerDetailBO.getCustomerOrders(customerid);
}
@GetMapping(value = "/getOrder/{orderid}")
public Order getOrder(@PathVariable Integer orderid) {
return orderDetailBO.getOrderDetail(orderid);
}
@GetMapping(value = "/getOrderPrice/{orderid}")
public double getOrderPrice(@PathVariable Integer orderid) {
return orderDetailBO.getOrderPrice(orderid);
}
}
@@ -0,0 +1,24 @@
package com.baeldung.multiplecachemanager.entity;
public class Customer {
private int customerId;
private String customerName;
public int getCustomerId() {
return customerId;
}
public void setCustomerId(int customerId) {
this.customerId = customerId;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
}
@@ -0,0 +1,34 @@
package com.baeldung.multiplecachemanager.entity;
public class Item {
private int itemId;
private String itemDesc;
private double itemPrice;
public int getItemId() {
return itemId;
}
public void setItemId(int itemId) {
this.itemId = itemId;
}
public String getItemDesc() {
return itemDesc;
}
public void setItemDesc(String itemDesc) {
this.itemDesc = itemDesc;
}
public double getItemPrice() {
return itemPrice;
}
public void setItemPrice(double itemPrice) {
this.itemPrice = itemPrice;
}
}
@@ -0,0 +1,44 @@
package com.baeldung.multiplecachemanager.entity;
public class Order {
private int orderId;
private int itemId;
private int quantity;
private int customerId;
public int getOrderId() {
return orderId;
}
public void setOrderId(int orderId) {
this.orderId = orderId;
}
public int getItemId() {
return itemId;
}
public void setItemId(int itemId) {
this.itemId = itemId;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public int getCustomerId() {
return customerId;
}
public void setCustomerId(int customerId) {
this.customerId = customerId;
}
}
@@ -0,0 +1,49 @@
package com.baeldung.multiplecachemanager.repository;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.stereotype.Repository;
import com.baeldung.multiplecachemanager.entity.Customer;
import com.baeldung.multiplecachemanager.entity.Order;
@Repository
public class CustomerDetailRepository {
@Autowired
private JdbcTemplate jdbcTemplate;
public Customer getCustomerDetail(Integer customerId) {
String customerQuery = "select * from customer where customerid = ? ";
Customer customer = new Customer();
jdbcTemplate.query(customerQuery, new Object[] { customerId }, new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
customer.setCustomerId(rs.getInt("customerid"));
customer.setCustomerName(rs.getString("customername"));
}
});
return customer;
}
public List<Order> getCustomerOrders(Integer customerId) {
String customerOrderQuery = "select * from orderdetail where customerid = ? ";
List<Order> orders = new ArrayList<Order>();
jdbcTemplate.query(customerOrderQuery, new Object[] { customerId }, new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
Order order = new Order();
order.setCustomerId(rs.getInt("customerid"));
order.setItemId(rs.getInt("orderid"));
order.setOrderId(rs.getInt("orderid"));
order.setQuantity(rs.getInt("quantity"));
orders.add(order);
}
});
return orders;
}
}
@@ -0,0 +1,53 @@
package com.baeldung.multiplecachemanager.repository;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.stereotype.Repository;
import com.baeldung.multiplecachemanager.entity.Item;
import com.baeldung.multiplecachemanager.entity.Order;
@Repository
public class OrderDetailRepository {
@Autowired
private JdbcTemplate jdbcTemplate;
public Order getOrderDetail(Integer orderId) {
String orderDetailQuery = "select * from orderdetail where orderid = ? ";
Order order = new Order();
jdbcTemplate.query(orderDetailQuery, new Object[] { orderId }, new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
order.setCustomerId(rs.getInt("customerid"));
order.setOrderId(rs.getInt("orderid"));
order.setItemId(rs.getInt("itemid"));
order.setQuantity(rs.getInt("quantity"));
}
});
return order;
}
public double getOrderPrice(Integer orderId) {
String orderItemJoinQuery = "select * from ( select * from orderdetail where orderid = ? ) o left join item on o.itemid = ITEM.itemid";
Order order = new Order();
Item item = new Item();
jdbcTemplate.query(orderItemJoinQuery, new Object[] { orderId }, new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
order.setCustomerId(rs.getInt("customerid"));
order.setOrderId(rs.getInt("orderid"));
order.setItemId(rs.getInt("itemid"));
order.setQuantity(rs.getInt("quantity"));
item.setItemDesc("itemdesc");
item.setItemId(rs.getInt("itemid"));
item.setItemPrice(rs.getDouble("price"));
}
});
return order.getQuantity() * item.getItemPrice();
}
}
@@ -0,0 +1,23 @@
package com.baeldung.springdatacaching.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.io.Serializable;
import java.util.UUID;
@Data
@Entity
@NoArgsConstructor
@AllArgsConstructor
public class Book implements Serializable {
@Id
private UUID id;
private String title;
}
@@ -0,0 +1,15 @@
package com.baeldung.springdatacaching.repositories;
import com.baeldung.springdatacaching.model.Book;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.repository.CrudRepository;
import java.util.Optional;
import java.util.UUID;
public interface BookRepository extends CrudRepository<Book, UUID> {
@Cacheable(value = "books", unless = "#a0=='Foundation'")
Optional<Book> findFirstByTitle(String title);
}
@@ -0,0 +1,11 @@
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# Enabling H2 Console
spring.h2.console.enabled=true
spring.h2.console.path=/h2
#ehcache
spring.cache.jcache.config=classpath:ehcache.xml
@@ -0,0 +1,40 @@
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
"
>
<cache:annotation-driven/>
<!-- <context:annotation-config/> -->
<!-- the service that you wish to make cacheable. -->
<bean id="customerDataService" class="com.baeldung.caching.example.CustomerDataService"/>
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" name="directory"/>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" name="addresses"/>
</set>
</property>
</bean>
<!-- define caching behavior -->
<cache:advice id="cachingBehavior" cache-manager="cacheManager">
<cache:caching cache="addresses">
<cache:cacheable method="getAddress" key="#customer.name"/>
</cache:caching>
</cache:advice>
<!-- apply the behavior to all the implementations of CustomerDataService -->
<aop:config>
<aop:advisor advice-ref="cachingBehavior" pointcut="execution(* com.baeldung.caching.example.CustomerDataService.*(..))"/>
</aop:config>
</beans>

Some files were not shown because too many files have changed in this diff Show More