diff --git a/apache-tapestry/pom.xml b/apache-tapestry/pom.xml
index 7a4c2b53b5..201b807d9f 100644
--- a/apache-tapestry/pom.xml
+++ b/apache-tapestry/pom.xml
@@ -3,12 +3,17 @@
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4.0.0
- com.baeldung
apache-tapestry
0.0.1-SNAPSHOT
apache-tapestry
war
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+
+
diff --git a/lightrun/README.md b/lightrun/README.md
new file mode 100644
index 0000000000..18d4ccc12f
--- /dev/null
+++ b/lightrun/README.md
@@ -0,0 +1,36 @@
+# Lightrun Example Application - Tasks Management
+
+This application exists as an example for the Lightrun series of articles.
+
+## Building
+
+This application requires [Apache Maven](https://maven.apache.org/) and [Java 17+](https://www.oracle.com/java/technologies/downloads/).
+
+Building the code is done by executing:
+
+```
+$ mvn install
+```
+
+from the top level.
+
+## Running
+
+The application consists of three services:
+
+* Tasks
+* Users
+* API
+
+These are all Spring Boot applications.
+
+The Tasks and Users services exist as microservices for managing one facet of data. Each uses a database, and utilise a JMS queue between them as well. For convenience this infrastructure is all embedded in the applications.
+
+This does mean that the startup order is important. The JMS queue exists within the Tasks service and is connected to from the Users service. As such, the Tasks service must be started before the others.
+
+Each service can be started either by executing `mvn spring-boot:run` from within the appropriate directory. Alternatively, as Spring Boot applications, the build will produce an executable JAR file within the `target` directory that can be executed as, for
+example:
+
+```
+$ java -jar ./target/tasks-service-0.0.1-SNAPSHOT.jar
+```
diff --git a/lightrun/api-service/.gitignore b/lightrun/api-service/.gitignore
new file mode 100644
index 0000000000..549e00a2a9
--- /dev/null
+++ b/lightrun/api-service/.gitignore
@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
diff --git a/lightrun/api-service/pom.xml b/lightrun/api-service/pom.xml
new file mode 100644
index 0000000000..3423c490f1
--- /dev/null
+++ b/lightrun/api-service/pom.xml
@@ -0,0 +1,45 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.6.7
+
+
+ com.baeldung
+ api-service
+ 0.0.1-SNAPSHOT
+ api-service
+ Aggregator Service for LightRun Article
+
+ 17
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/lightrun/api-service/src/main/java/com/baeldung/apiservice/ApiServiceApplication.java b/lightrun/api-service/src/main/java/com/baeldung/apiservice/ApiServiceApplication.java
new file mode 100644
index 0000000000..a9b29cbd4b
--- /dev/null
+++ b/lightrun/api-service/src/main/java/com/baeldung/apiservice/ApiServiceApplication.java
@@ -0,0 +1,13 @@
+package com.baeldung.apiservice;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class ApiServiceApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ApiServiceApplication.class, args);
+ }
+
+}
diff --git a/lightrun/api-service/src/main/java/com/baeldung/apiservice/RequestIdGenerator.java b/lightrun/api-service/src/main/java/com/baeldung/apiservice/RequestIdGenerator.java
new file mode 100644
index 0000000000..f15738c1e6
--- /dev/null
+++ b/lightrun/api-service/src/main/java/com/baeldung/apiservice/RequestIdGenerator.java
@@ -0,0 +1,33 @@
+package com.baeldung.apiservice;
+
+import java.util.UUID;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.MDC;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+@Component
+public class RequestIdGenerator implements HandlerInterceptor {
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+ String requestId = UUID.randomUUID()
+ .toString();
+
+ MDC.put(RequestIdGenerator.class.getCanonicalName(), requestId);
+ response.addHeader("X-Request-Id", requestId);
+
+ return true;
+ }
+
+ @Override
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+ MDC.remove(RequestIdGenerator.class.getCanonicalName());
+ }
+
+ public static String getRequestId() {
+ return MDC.get(RequestIdGenerator.class.getCanonicalName());
+ }
+}
diff --git a/lightrun/api-service/src/main/java/com/baeldung/apiservice/RestTemplateConfig.java b/lightrun/api-service/src/main/java/com/baeldung/apiservice/RestTemplateConfig.java
new file mode 100644
index 0000000000..1582ba5953
--- /dev/null
+++ b/lightrun/api-service/src/main/java/com/baeldung/apiservice/RestTemplateConfig.java
@@ -0,0 +1,20 @@
+package com.baeldung.apiservice;
+
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+@Configuration
+public class RestTemplateConfig {
+ @Bean
+ public RestTemplate restTemplate(RestTemplateBuilder builder) {
+ return builder.additionalInterceptors((request, body, execution) -> {
+ request.getHeaders()
+ .add("X-Request-Id", RequestIdGenerator.getRequestId());
+
+ return execution.execute(request, body);
+ })
+ .build();
+ }
+}
diff --git a/lightrun/api-service/src/main/java/com/baeldung/apiservice/WebConfig.java b/lightrun/api-service/src/main/java/com/baeldung/apiservice/WebConfig.java
new file mode 100644
index 0000000000..9edfcff6f6
--- /dev/null
+++ b/lightrun/api-service/src/main/java/com/baeldung/apiservice/WebConfig.java
@@ -0,0 +1,17 @@
+package com.baeldung.apiservice;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class WebConfig implements WebMvcConfigurer {
+ @Autowired
+ private RequestIdGenerator requestIdGenerator;
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(requestIdGenerator);
+ }
+}
diff --git a/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/http/TaskResponse.java b/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/http/TaskResponse.java
new file mode 100644
index 0000000000..875390fdcd
--- /dev/null
+++ b/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/http/TaskResponse.java
@@ -0,0 +1,6 @@
+package com.baeldung.apiservice.adapters.http;
+
+import java.time.Instant;
+
+public record TaskResponse(String id, String title, Instant created, UserResponse createdBy, UserResponse assignedTo, String status) {
+}
diff --git a/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/http/TasksController.java b/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/http/TasksController.java
new file mode 100644
index 0000000000..55b449f249
--- /dev/null
+++ b/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/http/TasksController.java
@@ -0,0 +1,56 @@
+package com.baeldung.apiservice.adapters.http;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+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.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.apiservice.adapters.tasks.Task;
+import com.baeldung.apiservice.adapters.tasks.TaskRepository;
+import com.baeldung.apiservice.adapters.users.UserRepository;
+
+@RequestMapping("/")
+@RestController
+public class TasksController {
+ @Autowired
+ private TaskRepository taskRepository;
+ @Autowired
+ private UserRepository userRepository;
+
+ @GetMapping("/{id}")
+ public TaskResponse getTaskById(@PathVariable("id") String id) {
+ Task task = taskRepository.getTaskById(id);
+
+ if (task == null) {
+ throw new UnknownTaskException();
+ }
+
+ return buildResponse(task);
+ }
+
+ private TaskResponse buildResponse(Task task) {
+ return new TaskResponse(task.id(), task.title(), task.created(), getUser(task.createdBy()), getUser(task.assignedTo()), task.status());
+ }
+
+ private UserResponse getUser(String userId) {
+ if (userId == null) {
+ return null;
+ }
+
+ var user = userRepository.getUserById(userId);
+ if (user == null) {
+ return null;
+ }
+
+ return new UserResponse(user.id(), user.name());
+ }
+
+ @ExceptionHandler(UnknownTaskException.class)
+ @ResponseStatus(HttpStatus.NOT_FOUND)
+ public void handleUnknownTask() {
+ }
+}
diff --git a/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/http/UnknownTaskException.java b/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/http/UnknownTaskException.java
new file mode 100644
index 0000000000..1635ca8796
--- /dev/null
+++ b/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/http/UnknownTaskException.java
@@ -0,0 +1,4 @@
+package com.baeldung.apiservice.adapters.http;
+
+public class UnknownTaskException extends RuntimeException {
+}
diff --git a/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/http/UserResponse.java b/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/http/UserResponse.java
new file mode 100644
index 0000000000..f311b895a8
--- /dev/null
+++ b/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/http/UserResponse.java
@@ -0,0 +1,4 @@
+package com.baeldung.apiservice.adapters.http;
+
+public record UserResponse(String id, String name) {
+}
diff --git a/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/tasks/Task.java b/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/tasks/Task.java
new file mode 100644
index 0000000000..188d3e951c
--- /dev/null
+++ b/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/tasks/Task.java
@@ -0,0 +1,6 @@
+package com.baeldung.apiservice.adapters.tasks;
+
+import java.time.Instant;
+
+public record Task(String id, String title, Instant created, String createdBy, String assignedTo, String status) {
+}
diff --git a/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/tasks/TaskRepository.java b/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/tasks/TaskRepository.java
new file mode 100644
index 0000000000..9260a125af
--- /dev/null
+++ b/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/tasks/TaskRepository.java
@@ -0,0 +1,30 @@
+package com.baeldung.apiservice.adapters.tasks;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Repository;
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.util.UriComponentsBuilder;
+
+@Repository
+public class TaskRepository {
+ @Autowired
+ private RestTemplate restTemplate;
+
+ @Value("${tasks-service.url}")
+ private String tasksService;
+
+ public Task getTaskById(String id) {
+ var uri = UriComponentsBuilder.fromUriString(tasksService)
+ .path(id)
+ .build()
+ .toUri();
+
+ try {
+ return restTemplate.getForObject(uri, Task.class);
+ } catch (HttpClientErrorException.NotFound e) {
+ return null;
+ }
+ }
+}
diff --git a/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/users/User.java b/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/users/User.java
new file mode 100644
index 0000000000..a3a53c0805
--- /dev/null
+++ b/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/users/User.java
@@ -0,0 +1,4 @@
+package com.baeldung.apiservice.adapters.users;
+
+public record User(String id, String name) {
+}
diff --git a/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/users/UserRepository.java b/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/users/UserRepository.java
new file mode 100644
index 0000000000..d5625f6efc
--- /dev/null
+++ b/lightrun/api-service/src/main/java/com/baeldung/apiservice/adapters/users/UserRepository.java
@@ -0,0 +1,30 @@
+package com.baeldung.apiservice.adapters.users;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Repository;
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.util.UriComponentsBuilder;
+
+@Repository
+public class UserRepository {
+ @Autowired
+ private RestTemplate restTemplate;
+
+ @Value("${users-service.url}")
+ private String usersService;
+
+ public User getUserById(String id) {
+ var uri = UriComponentsBuilder.fromUriString(usersService)
+ .path(id)
+ .build()
+ .toUri();
+
+ try {
+ return restTemplate.getForObject(uri, User.class);
+ } catch (HttpClientErrorException.NotFound e) {
+ return null;
+ }
+ }
+}
diff --git a/lightrun/api-service/src/main/resources/application.properties b/lightrun/api-service/src/main/resources/application.properties
new file mode 100644
index 0000000000..f3f227f46f
--- /dev/null
+++ b/lightrun/api-service/src/main/resources/application.properties
@@ -0,0 +1,2 @@
+users-service.url=http://localhost:8081
+tasks-service.url=http://localhost:8082
\ No newline at end of file
diff --git a/lightrun/pom.xml b/lightrun/pom.xml
new file mode 100644
index 0000000000..3ce71069a9
--- /dev/null
+++ b/lightrun/pom.xml
@@ -0,0 +1,17 @@
+
+
+ 4.0.0
+ com.baelduung
+ lightrun
+ 0.0.1-SNAPSHOT
+ pom
+ lightrun
+ Services for LightRun Article
+
+
+ tasks-service
+ users-service
+ api-service
+
+
diff --git a/lightrun/tasks-service/.gitignore b/lightrun/tasks-service/.gitignore
new file mode 100644
index 0000000000..549e00a2a9
--- /dev/null
+++ b/lightrun/tasks-service/.gitignore
@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
diff --git a/lightrun/tasks-service/pom.xml b/lightrun/tasks-service/pom.xml
new file mode 100644
index 0000000000..441b4d3713
--- /dev/null
+++ b/lightrun/tasks-service/pom.xml
@@ -0,0 +1,67 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.6.7
+
+
+ com.baeldung
+ tasks-service
+ 0.0.1-SNAPSHOT
+ tasks-service
+ Tasks Service for LightRun Article
+
+ 17
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-starter-artemis
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.flywaydb
+ flyway-core
+
+
+
+ com.h2database
+ h2
+ runtime
+
+
+ org.apache.activemq
+ artemis-jms-server
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/ArtemisConfig.java b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/ArtemisConfig.java
new file mode 100644
index 0000000000..56ee3bbc93
--- /dev/null
+++ b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/ArtemisConfig.java
@@ -0,0 +1,23 @@
+package com.baeldung.tasksservice;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.jms.artemis.ArtemisConfigurationCustomizer;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class ArtemisConfig implements ArtemisConfigurationCustomizer {
+ @Value("${spring.artemis.host}")
+ private String hostname;
+
+ @Value("${spring.artemis.port}")
+ private int port;
+
+ @Override
+ public void customize(org.apache.activemq.artemis.core.config.Configuration configuration) {
+ try {
+ configuration.addAcceptorConfiguration("remote", "tcp://" + hostname + ":" + port);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to configure Artemis listener", e);
+ }
+ }
+}
diff --git a/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/TasksServiceApplication.java b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/TasksServiceApplication.java
new file mode 100644
index 0000000000..dfd9859674
--- /dev/null
+++ b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/TasksServiceApplication.java
@@ -0,0 +1,13 @@
+package com.baeldung.tasksservice;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class TasksServiceApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(TasksServiceApplication.class, args);
+ }
+
+}
diff --git a/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/CreateTaskRequest.java b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/CreateTaskRequest.java
new file mode 100644
index 0000000000..64acea8b1b
--- /dev/null
+++ b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/CreateTaskRequest.java
@@ -0,0 +1,15 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.tasksservice.adapters.http;
+
+public record CreateTaskRequest(String title, String createdBy) {
+}
diff --git a/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/PatchTaskRequest.java b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/PatchTaskRequest.java
new file mode 100644
index 0000000000..20974b1c1d
--- /dev/null
+++ b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/PatchTaskRequest.java
@@ -0,0 +1,17 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.tasksservice.adapters.http;
+
+import java.util.Optional;
+
+public record PatchTaskRequest(Optional status, Optional assignedTo) {
+}
diff --git a/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/TaskResponse.java b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/TaskResponse.java
new file mode 100644
index 0000000000..4e01a649b1
--- /dev/null
+++ b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/TaskResponse.java
@@ -0,0 +1,17 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.tasksservice.adapters.http;
+
+import java.time.Instant;
+
+public record TaskResponse(String id, String title, Instant created, String createdBy, String assignedTo, String status) {
+}
diff --git a/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/TasksController.java b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/TasksController.java
new file mode 100644
index 0000000000..a4145a2243
--- /dev/null
+++ b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/TasksController.java
@@ -0,0 +1,84 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.tasksservice.adapters.http;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PatchMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.tasksservice.adapters.repository.TaskRecord;
+import com.baeldung.tasksservice.service.TasksService;
+import com.baeldung.tasksservice.service.UnknownTaskException;
+
+@RestController
+@RequestMapping("/")
+class TasksController {
+ @Autowired
+ private TasksService tasksService;
+
+ @PostMapping
+ @ResponseStatus(HttpStatus.CREATED)
+ public TaskResponse createTask(@RequestBody CreateTaskRequest body) {
+ var task = tasksService.createTask(body.title(), body.createdBy());
+ return buildResponse(task);
+ }
+
+ @GetMapping
+ public List searchTasks(@RequestParam("status") Optional status, @RequestParam("createdBy") Optional createdBy) {
+ var tasks = tasksService.search(status, createdBy);
+
+ return tasks.stream()
+ .map(this::buildResponse)
+ .collect(Collectors.toList());
+ }
+
+ @GetMapping("/{id}")
+ public TaskResponse getTask(@PathVariable("id") String id) {
+ var task = tasksService.getTaskById(id);
+ return buildResponse(task);
+ }
+
+ @DeleteMapping("/{id}")
+ public void deleteTask(@PathVariable("id") String id) {
+ tasksService.deleteTaskById(id);
+ }
+
+ @PatchMapping("/{id}")
+ public TaskResponse patchTask(@PathVariable("id") String id, @RequestBody PatchTaskRequest body) {
+ var task = tasksService.updateTask(id, body.status(), body.assignedTo());
+
+ return buildResponse(task);
+ }
+
+ private TaskResponse buildResponse(final TaskRecord task) {
+ return new TaskResponse(task.getId(), task.getTitle(), task.getCreated(), task.getCreatedBy(), task.getAssignedTo(), task.getStatus());
+ }
+
+ @ExceptionHandler(UnknownTaskException.class)
+ @ResponseStatus(HttpStatus.NOT_FOUND)
+ public void handleUnknownTask() {
+ }
+}
diff --git a/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/http-client.env.json b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/http-client.env.json
new file mode 100644
index 0000000000..f27fc4325b
--- /dev/null
+++ b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/http-client.env.json
@@ -0,0 +1,5 @@
+{
+ "local-tasks": {
+ "host": "localhost:8082"
+ }
+}
\ No newline at end of file
diff --git a/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/tasks.http b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/tasks.http
new file mode 100644
index 0000000000..eb7d578ac9
--- /dev/null
+++ b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/http/tasks.http
@@ -0,0 +1,35 @@
+GET http://{{host}}/createdemoapplication1 HTTP/1.1
+
+###
+GET http://{{host}}/unknown HTTP/1.1
+
+###
+GET http://{{host}}?status=PENDING
+
+###
+GET http://{{host}}?createdBy=baeldung
+
+###
+GET http://{{host}}?createdBy=baeldung&status=COMPLETE
+
+###
+DELETE http://{{host}}/createdemoapplication1 HTTP/1.1
+
+###
+DELETE http://{{host}}/unknown HTTP/1.1
+
+###
+POST http://{{host}} HTTP/1.1
+Content-Type: application/json
+
+{
+ "title": "My Task",
+ "createdBy": "graham"
+}
+###
+PATCH http://{{host}}/createdemoapplication1 HTTP/1.1
+Content-Type: application/json
+
+{
+ "status": "COMPLETE"
+}
diff --git a/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/jms/JmsConsumer.java b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/jms/JmsConsumer.java
new file mode 100644
index 0000000000..c380a16acc
--- /dev/null
+++ b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/jms/JmsConsumer.java
@@ -0,0 +1,18 @@
+package com.baeldung.tasksservice.adapters.jms;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jms.annotation.JmsListener;
+import org.springframework.stereotype.Service;
+
+import com.baeldung.tasksservice.service.DeletedUserService;
+
+@Service
+public class JmsConsumer {
+ @Autowired
+ private DeletedUserService deletedUserService;
+
+ @JmsListener(destination = "deleted_user")
+ public void receive(String user) {
+ deletedUserService.handleDeletedUser(user);
+ }
+}
diff --git a/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/repository/TaskRecord.java b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/repository/TaskRecord.java
new file mode 100644
index 0000000000..dee3017a59
--- /dev/null
+++ b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/repository/TaskRecord.java
@@ -0,0 +1,80 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.tasksservice.adapters.repository;
+
+import java.time.Instant;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "tasks")
+public class TaskRecord {
+ @Id
+ @Column(name = "task_id")
+ private String id;
+ private String title;
+ @Column(name = "created_at")
+ private Instant created;
+ @Column(name = "created_by")
+ private String createdBy;
+ @Column(name = "assigned_to")
+ private String assignedTo;
+ private String status;
+
+ public TaskRecord(final String id, final String title, final Instant created, final String createdBy, final String assignedTo, final String status) {
+ this.id = id;
+ this.title = title;
+ this.created = created;
+ this.createdBy = createdBy;
+ this.assignedTo = assignedTo;
+ this.status = status;
+ }
+
+ private TaskRecord() {
+ // Needed for JPA
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public Instant getCreated() {
+ return created;
+ }
+
+ public String getCreatedBy() {
+ return createdBy;
+ }
+
+ public String getAssignedTo() {
+ return assignedTo;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setAssignedTo(final String assignedTo) {
+ this.assignedTo = assignedTo;
+ }
+
+ public void setStatus(final String status) {
+ this.status = status;
+ }
+}
\ No newline at end of file
diff --git a/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/repository/TasksRepository.java b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/repository/TasksRepository.java
new file mode 100644
index 0000000000..9b6f041fd2
--- /dev/null
+++ b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/adapters/repository/TasksRepository.java
@@ -0,0 +1,28 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.tasksservice.adapters.repository;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface TasksRepository extends JpaRepository {
+ List findByStatus(String status);
+
+ List findByCreatedBy(String createdBy);
+
+ List findByStatusAndCreatedBy(String status, String createdBy);
+
+ List findByAssignedTo(String assignedTo);
+}
diff --git a/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/service/DeletedUserService.java b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/service/DeletedUserService.java
new file mode 100644
index 0000000000..fa0c3572fb
--- /dev/null
+++ b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/service/DeletedUserService.java
@@ -0,0 +1,27 @@
+package com.baeldung.tasksservice.service;
+
+import javax.transaction.Transactional;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.baeldung.tasksservice.adapters.repository.TaskRecord;
+import com.baeldung.tasksservice.adapters.repository.TasksRepository;
+
+@Service
+public class DeletedUserService {
+ @Autowired
+ private TasksRepository tasksRepository;
+
+ @Transactional
+ public void handleDeletedUser(String user) {
+ var ownedByUser = tasksRepository.findByCreatedBy(user);
+ tasksRepository.deleteAll(ownedByUser);
+
+ var assignedToUser = tasksRepository.findByAssignedTo(user);
+ for (TaskRecord record : assignedToUser) {
+ record.setAssignedTo(null);
+ record.setStatus("PENDING");
+ }
+ }
+}
diff --git a/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/service/TasksService.java b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/service/TasksService.java
new file mode 100644
index 0000000000..3539dbbc3c
--- /dev/null
+++ b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/service/TasksService.java
@@ -0,0 +1,73 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.tasksservice.service;
+
+import java.time.Instant;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+import javax.transaction.Transactional;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.baeldung.tasksservice.adapters.repository.TaskRecord;
+import com.baeldung.tasksservice.adapters.repository.TasksRepository;
+
+@Service
+public class TasksService {
+ @Autowired
+ private TasksRepository tasksRepository;
+
+ public TaskRecord getTaskById(String id) {
+ return tasksRepository.findById(id)
+ .orElseThrow(() -> new UnknownTaskException(id));
+ }
+
+ @Transactional
+ public void deleteTaskById(String id) {
+ var task = tasksRepository.findById(id)
+ .orElseThrow(() -> new UnknownTaskException(id));
+ tasksRepository.delete(task);
+ }
+
+ public List search(Optional createdBy, Optional status) {
+ if (createdBy.isPresent() && status.isPresent()) {
+ return tasksRepository.findByStatusAndCreatedBy(status.get(), createdBy.get());
+ } else if (createdBy.isPresent()) {
+ return tasksRepository.findByCreatedBy(createdBy.get());
+ } else if (status.isPresent()) {
+ return tasksRepository.findByStatus(status.get());
+ } else {
+ return tasksRepository.findAll();
+ }
+ }
+
+ @Transactional
+ public TaskRecord updateTask(String id, Optional newStatus, Optional newAssignedTo) {
+ var task = tasksRepository.findById(id)
+ .orElseThrow(() -> new UnknownTaskException(id));
+
+ newStatus.ifPresent(task::setStatus);
+ newAssignedTo.ifPresent(task::setAssignedTo);
+
+ return task;
+ }
+
+ public TaskRecord createTask(String title, String createdBy) {
+ var task = new TaskRecord(UUID.randomUUID()
+ .toString(), title, Instant.now(), createdBy, null, "PENDING");
+ tasksRepository.save(task);
+ return task;
+ }
+}
diff --git a/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/service/UnknownTaskException.java b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/service/UnknownTaskException.java
new file mode 100644
index 0000000000..fd9fce4d16
--- /dev/null
+++ b/lightrun/tasks-service/src/main/java/com/baeldung/tasksservice/service/UnknownTaskException.java
@@ -0,0 +1,24 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.tasksservice.service;
+
+public class UnknownTaskException extends RuntimeException {
+ private final String id;
+
+ public UnknownTaskException(final String id) {
+ this.id = id;
+ }
+
+ public String getId() {
+ return id;
+ }
+}
diff --git a/lightrun/tasks-service/src/main/resources/application.properties b/lightrun/tasks-service/src/main/resources/application.properties
new file mode 100644
index 0000000000..88326ed10e
--- /dev/null
+++ b/lightrun/tasks-service/src/main/resources/application.properties
@@ -0,0 +1,9 @@
+server.port=8082
+spring.artemis.mode=EMBEDDED
+spring.artemis.host=localhost
+spring.artemis.port=61616
+spring.artemis.embedded.enabled=true
+spring.jms.template.default-destination=my-queue-1
+logging.level.org.apache.activemq.audit.base=WARN
+logging.level.org.apache.activemq.audit.message=WARN
+
diff --git a/lightrun/tasks-service/src/main/resources/db/migration/V1_0_0__create-tasks-table.sql b/lightrun/tasks-service/src/main/resources/db/migration/V1_0_0__create-tasks-table.sql
new file mode 100644
index 0000000000..f2365c2687
--- /dev/null
+++ b/lightrun/tasks-service/src/main/resources/db/migration/V1_0_0__create-tasks-table.sql
@@ -0,0 +1,13 @@
+CREATE TABLE tasks (
+ task_id VARCHAR(36) PRIMARY KEY,
+ title VARCHAR(100) NOT NULL,
+ created_at DATETIME NOT NULL,
+ created_by VARCHAR(36) NOT NULL,
+ assigned_to VARCHAR(36) NULL,
+ status VARCHAR(20) NOT NULL
+);
+
+INSERT INTO tasks(task_id, title, created_at, created_by, assigned_to, status) VALUES
+ ('createdemoapplication1', 'Create demo applications - Tasks', '2022-05-05 12:34:56', 'baeldung', 'coxg', 'IN_PROGRESS'),
+ ('createdemoapplication2', 'Create demo applications - Users', '2022-05-05 12:34:56', 'baeldung', NULL, 'PENDING'),
+ ('createdemoapplication3', 'Create demo applications - API', '2022-05-05 12:34:56', 'baeldung', NULL, 'PENDING');
\ No newline at end of file
diff --git a/lightrun/users-service/.gitignore b/lightrun/users-service/.gitignore
new file mode 100644
index 0000000000..549e00a2a9
--- /dev/null
+++ b/lightrun/users-service/.gitignore
@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
diff --git a/lightrun/users-service/README.md b/lightrun/users-service/README.md
new file mode 100644
index 0000000000..e7faae858f
--- /dev/null
+++ b/lightrun/users-service/README.md
@@ -0,0 +1,36 @@
+# Lightrun Example Application - Tasks Management
+
+This application exists as an example for the Lightrun series of articles.
+
+## Building
+
+This application requires [Apache Maven](https://maven.apache.org/) and [Java 17+](https://www.oracle.com/java/technologies/downloads/). It does use the Maven Wrapper, so it can be built with only Java available on the path.
+
+As such, building the code is done by executing:
+
+```
+$ ./mvnw install
+```
+
+from the top level.
+
+## Running
+
+The application consists of three services:
+
+* Tasks
+* Users
+* API
+
+These are all Spring Boot applications.
+
+The Tasks and Users services exist as microservices for managing one facet of data. Each uses a database, and utilise a JMS queue between them as well. For convenience this infrastructure is all embedded in the applications.
+
+This does mean that the startup order is important. The JMS queue exists within the Tasks service and is connected to from the Users service. As such, the Tasks service must be started before the others.
+
+Each service can be started either by executing `mvn spring-boot:run` from within the appropriate directory. Alternatively, as Spring Boot applications, the build will produce an executable JAR file within the `target` directory that can be executed as, for
+example:
+
+```
+$ java -jar ./target/tasks-service-0.0.1-SNAPSHOT.jar
+```
diff --git a/lightrun/users-service/pom.xml b/lightrun/users-service/pom.xml
new file mode 100644
index 0000000000..63596ed67b
--- /dev/null
+++ b/lightrun/users-service/pom.xml
@@ -0,0 +1,63 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.6.7
+
+
+ com.baeldung
+ users-service
+ 0.0.1-SNAPSHOT
+ users-service
+ Users Service for LightRun Article
+
+ 17
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-starter-artemis
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.flywaydb
+ flyway-core
+
+
+
+ com.h2database
+ h2
+ runtime
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/lightrun/users-service/src/main/java/com/baeldung/usersservice/JmsConfig.java b/lightrun/users-service/src/main/java/com/baeldung/usersservice/JmsConfig.java
new file mode 100644
index 0000000000..c803c9af13
--- /dev/null
+++ b/lightrun/users-service/src/main/java/com/baeldung/usersservice/JmsConfig.java
@@ -0,0 +1,29 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.usersservice;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
+import org.springframework.jms.support.converter.MessageConverter;
+import org.springframework.jms.support.converter.MessageType;
+
+@Configuration
+public class JmsConfig {
+ @Bean
+ public MessageConverter jacksonJmsMessageConverter() {
+ MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
+ converter.setTargetType(MessageType.TEXT);
+ converter.setTypeIdPropertyName("_type");
+ return converter;
+ }
+}
diff --git a/lightrun/users-service/src/main/java/com/baeldung/usersservice/UsersServiceApplication.java b/lightrun/users-service/src/main/java/com/baeldung/usersservice/UsersServiceApplication.java
new file mode 100644
index 0000000000..3910960282
--- /dev/null
+++ b/lightrun/users-service/src/main/java/com/baeldung/usersservice/UsersServiceApplication.java
@@ -0,0 +1,13 @@
+package com.baeldung.usersservice;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class UsersServiceApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(UsersServiceApplication.class, args);
+ }
+
+}
diff --git a/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/CreateUserRequest.java b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/CreateUserRequest.java
new file mode 100644
index 0000000000..c3dfc8d068
--- /dev/null
+++ b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/CreateUserRequest.java
@@ -0,0 +1,15 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.usersservice.adapters.http;
+
+public record CreateUserRequest(String name) {
+}
diff --git a/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/PatchUserRequest.java b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/PatchUserRequest.java
new file mode 100644
index 0000000000..acaf7e635f
--- /dev/null
+++ b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/PatchUserRequest.java
@@ -0,0 +1,17 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.usersservice.adapters.http;
+
+import java.util.Optional;
+
+public record PatchUserRequest(Optional name) {
+}
diff --git a/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/UserResponse.java b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/UserResponse.java
new file mode 100644
index 0000000000..e74ede65d5
--- /dev/null
+++ b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/UserResponse.java
@@ -0,0 +1,15 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.usersservice.adapters.http;
+
+public record UserResponse(String id, String name) {
+}
diff --git a/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/UsersController.java b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/UsersController.java
new file mode 100644
index 0000000000..795d240fe9
--- /dev/null
+++ b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/UsersController.java
@@ -0,0 +1,70 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.usersservice.adapters.http;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PatchMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.usersservice.adapters.repository.UserRecord;
+import com.baeldung.usersservice.service.UnknownUserException;
+import com.baeldung.usersservice.service.UsersService;
+
+@RestController
+@RequestMapping("/")
+class UsersController {
+ @Autowired
+ private UsersService usersService;
+
+ @GetMapping("/{id}")
+ public UserResponse getUser(@PathVariable("id") String id) {
+ var user = usersService.getUserById(id);
+ return buildResponse(user);
+ }
+
+ @DeleteMapping("/{id}")
+ public void deleteUser(@PathVariable("id") String id) {
+ usersService.deleteUserById(id);
+ }
+
+ @PostMapping
+ @ResponseStatus(HttpStatus.CREATED)
+ public UserResponse createUser(@RequestBody CreateUserRequest body) {
+ var user = usersService.createUser(body.name());
+ return buildResponse(user);
+ }
+
+ @PatchMapping("/{id}")
+ public UserResponse patchUser(@PathVariable("id") String id, @RequestBody PatchUserRequest body) {
+ var user = usersService.updateUser(id, body.name());
+
+ return buildResponse(user);
+ }
+
+ private UserResponse buildResponse(final UserRecord user) {
+ return new UserResponse(user.getId(), user.getName());
+ }
+
+ @ExceptionHandler(UnknownUserException.class)
+ @ResponseStatus(HttpStatus.NOT_FOUND)
+ public void handleUnknownUser() {
+ }
+}
diff --git a/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/http-client.env.json b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/http-client.env.json
new file mode 100644
index 0000000000..3be2f710ff
--- /dev/null
+++ b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/http-client.env.json
@@ -0,0 +1,5 @@
+{
+ "local-users": {
+ "host": "localhost:8081"
+ }
+}
\ No newline at end of file
diff --git a/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/users.http b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/users.http
new file mode 100644
index 0000000000..904c5f1cf1
--- /dev/null
+++ b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/http/users.http
@@ -0,0 +1,25 @@
+GET http://{{host}}/baeldung HTTP/1.1
+
+###
+GET http://{{host}}/unknown HTTP/1.1
+
+###
+DELETE http://{{host}}/baeldung HTTP/1.1
+
+###
+DELETE http://{{host}}/unknown HTTP/1.1
+
+###
+POST http://{{host}} HTTP/1.1
+Content-Type: application/json
+
+{
+ "name": "Testing"
+}
+###
+PATCH http://{{host}}/coxg HTTP/1.1
+Content-Type: application/json
+
+{
+ "name": "Test Name"
+}
diff --git a/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/jms/JmsSender.java b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/jms/JmsSender.java
new file mode 100644
index 0000000000..f5d5d7900f
--- /dev/null
+++ b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/jms/JmsSender.java
@@ -0,0 +1,26 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.usersservice.adapters.jms;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jms.core.JmsTemplate;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class JmsSender {
+ @Autowired
+ private JmsTemplate jmsTemplate;
+
+ public void sendDeleteUserMessage(String userId) {
+ jmsTemplate.convertAndSend("deleted_user", userId);
+ }
+}
diff --git a/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/repository/UserRecord.java b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/repository/UserRecord.java
new file mode 100644
index 0000000000..fe87ac3ffe
--- /dev/null
+++ b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/repository/UserRecord.java
@@ -0,0 +1,47 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.usersservice.adapters.repository;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "users")
+public class UserRecord {
+ @Id
+ @Column(name = "user_id")
+ private String id;
+ private String name;
+
+ public UserRecord(final String id, final String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ private UserRecord() {
+ // Needed for JPA
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+}
\ No newline at end of file
diff --git a/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/repository/UsersRepository.java b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/repository/UsersRepository.java
new file mode 100644
index 0000000000..ed193a4955
--- /dev/null
+++ b/lightrun/users-service/src/main/java/com/baeldung/usersservice/adapters/repository/UsersRepository.java
@@ -0,0 +1,19 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.usersservice.adapters.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface UsersRepository extends JpaRepository {
+}
diff --git a/lightrun/users-service/src/main/java/com/baeldung/usersservice/service/UnknownUserException.java b/lightrun/users-service/src/main/java/com/baeldung/usersservice/service/UnknownUserException.java
new file mode 100644
index 0000000000..d0ca79850c
--- /dev/null
+++ b/lightrun/users-service/src/main/java/com/baeldung/usersservice/service/UnknownUserException.java
@@ -0,0 +1,24 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.usersservice.service;
+
+public class UnknownUserException extends RuntimeException {
+ private final String id;
+
+ public UnknownUserException(final String id) {
+ this.id = id;
+ }
+
+ public String getId() {
+ return id;
+ }
+}
diff --git a/lightrun/users-service/src/main/java/com/baeldung/usersservice/service/UsersService.java b/lightrun/users-service/src/main/java/com/baeldung/usersservice/service/UsersService.java
new file mode 100644
index 0000000000..5115a5a77b
--- /dev/null
+++ b/lightrun/users-service/src/main/java/com/baeldung/usersservice/service/UsersService.java
@@ -0,0 +1,64 @@
+/****************************************************************************************************************
+ *
+ * Copyright (c) 2022 OCLC, Inc. All Rights Reserved.
+ *
+ * OCLC proprietary information: the enclosed materials contain
+ * proprietary information of OCLC, Inc. and shall not be disclosed in whole or in
+ * any part to any third party or used by any person for any purpose, without written
+ * consent of OCLC, Inc. Duplication of any portion of these materials shall include this notice.
+ *
+ ******************************************************************************************************************/
+
+package com.baeldung.usersservice.service;
+
+import java.util.Optional;
+import java.util.UUID;
+
+import javax.transaction.Transactional;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.baeldung.usersservice.adapters.jms.JmsSender;
+import com.baeldung.usersservice.adapters.repository.UserRecord;
+import com.baeldung.usersservice.adapters.repository.UsersRepository;
+
+@Service
+public class UsersService {
+ @Autowired
+ private UsersRepository usersRepository;
+
+ @Autowired
+ private JmsSender jmsSender;
+
+ public UserRecord getUserById(String id) {
+ return usersRepository.findById(id)
+ .orElseThrow(() -> new UnknownUserException(id));
+ }
+
+ @Transactional
+ public void deleteUserById(String id) {
+ var user = usersRepository.findById(id)
+ .orElseThrow(() -> new UnknownUserException(id));
+ usersRepository.delete(user);
+
+ jmsSender.sendDeleteUserMessage(id);
+ }
+
+ @Transactional
+ public UserRecord updateUser(String id, Optional newName) {
+ var user = usersRepository.findById(id)
+ .orElseThrow(() -> new UnknownUserException(id));
+
+ newName.ifPresent(user::setName);
+
+ return user;
+ }
+
+ public UserRecord createUser(String name) {
+ var user = new UserRecord(UUID.randomUUID()
+ .toString(), name);
+ usersRepository.save(user);
+ return user;
+ }
+}
diff --git a/lightrun/users-service/src/main/resources/application.properties b/lightrun/users-service/src/main/resources/application.properties
new file mode 100644
index 0000000000..616131c42e
--- /dev/null
+++ b/lightrun/users-service/src/main/resources/application.properties
@@ -0,0 +1,7 @@
+server.port=8081
+spring.artemis.host=localhost
+spring.artemis.port=61616
+spring.jms.template.default-destination=my-queue-1
+logging.level.org.apache.activemq.audit.base=WARN
+logging.level.org.apache.activemq.audit.message=WARN
+
diff --git a/lightrun/users-service/src/main/resources/db/migration/V1_0_0__create-users-table.sql b/lightrun/users-service/src/main/resources/db/migration/V1_0_0__create-users-table.sql
new file mode 100644
index 0000000000..d1ec9387e6
--- /dev/null
+++ b/lightrun/users-service/src/main/resources/db/migration/V1_0_0__create-users-table.sql
@@ -0,0 +1,8 @@
+CREATE TABLE users (
+ user_id VARCHAR(36) PRIMARY KEY,
+ name VARCHAR(100) NOT NULL
+);
+
+INSERT INTO users(user_id, name) VALUES
+ ('baeldung', 'Baeldung'),
+ ('coxg', 'Graham');
\ No newline at end of file
diff --git a/logging-modules/flogger/src/test/java/com/baeldung/flogger/FloggerIntegrationTest.java b/logging-modules/flogger/src/test/java/com/baeldung/flogger/FloggerIntegrationTest.java
index 80fa0edd96..d3b73637a4 100644
--- a/logging-modules/flogger/src/test/java/com/baeldung/flogger/FloggerIntegrationTest.java
+++ b/logging-modules/flogger/src/test/java/com/baeldung/flogger/FloggerIntegrationTest.java
@@ -5,7 +5,6 @@ import com.google.common.flogger.LoggerConfig;
import com.google.common.flogger.StackSize;
import org.junit.Test;
-import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.stream.IntStream;
@@ -25,13 +24,6 @@ public class FloggerIntegrationTest {
});
}
- @Test
- public void givenATimeInterval_shouldLogAfterEveryTimeInterval() {
- IntStream.range(0, 1_000_0000).forEach(value -> {
- logger.atInfo().atMostEvery(10, TimeUnit.SECONDS).log("This log shows [every 10 seconds] => %d", value);
- });
- }
-
@Test
public void givenAnObject_shouldLogTheObject() {
User user = new User();
diff --git a/logging-modules/flogger/src/test/java/com/baeldung/flogger/FloggerManualTest.java b/logging-modules/flogger/src/test/java/com/baeldung/flogger/FloggerManualTest.java
new file mode 100644
index 0000000000..a3444e596e
--- /dev/null
+++ b/logging-modules/flogger/src/test/java/com/baeldung/flogger/FloggerManualTest.java
@@ -0,0 +1,25 @@
+package com.baeldung.flogger;
+
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+
+import org.junit.Test;
+
+import com.google.common.flogger.FluentLogger;
+
+public class FloggerManualTest {
+ static {
+// System.setProperty("flogger.backend_factory", "com.google.common.flogger.backend.log4j.Log4jBackendFactory#getInstance");
+ System.setProperty("flogger.backend_factory",
+ "com.google.common.flogger.backend.slf4j.Slf4jBackendFactory#getInstance");
+ }
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ @Test
+ public void givenATimeInterval_shouldLogAfterEveryTimeInterval() {
+ IntStream.range(0, 1_000_0000).forEach(value -> {
+ logger.atInfo().atMostEvery(10, TimeUnit.SECONDS).log("This log shows [every 10 seconds] => %d", value);
+ });
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 106bb7516a..a8bd79f3f5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -366,7 +366,7 @@
atomix
aws-modules
-
+
axon
azure
@@ -836,7 +836,7 @@
atomix
aws-modules
-
+
axon
azure
@@ -895,7 +895,7 @@
helidon
apache-httpclient
httpclient-simple
- hystrix
+ hystrix
jackson-modules
jackson-simple
@@ -1209,6 +1209,7 @@
jenkins/plugins
jhipster
+ jhipster-5
jws
libraries
@@ -1326,9 +1327,10 @@
quarkus-vs-springboot
quarkus-jandex
spring-boot-modules/spring-boot-cassandre
- spring-boot-modules/spring-boot-camel
+ spring-boot-modules/spring-boot-camel
testing-modules/testing-assertions
persistence-modules/fauna
+ lightrun
@@ -1396,6 +1398,7 @@
spring-boot-modules/spring-boot-camel
testing-modules/testing-assertions
persistence-modules/fauna
+ lightrun
diff --git a/restx/src/main/resources/restx/demo/settings.properties b/restx/src/main/resources/restx/demo/settings.properties
index a03c2eea97..3b2b591922 100644
--- a/restx/src/main/resources/restx/demo/settings.properties
+++ b/restx/src/main/resources/restx/demo/settings.properties
@@ -1 +1,2 @@
-app.name=restx-demo
\ No newline at end of file
+app.name=restx-demo
+restx.stats.share.enable=false
\ No newline at end of file
diff --git a/spring-5/src/test/java/com/baeldung/Example1IntegrationTest.java b/spring-5/src/test/java/com/baeldung/Example1ManualTest.java
similarity index 93%
rename from spring-5/src/test/java/com/baeldung/Example1IntegrationTest.java
rename to spring-5/src/test/java/com/baeldung/Example1ManualTest.java
index 8b9e66213f..c3330b4213 100644
--- a/spring-5/src/test/java/com/baeldung/Example1IntegrationTest.java
+++ b/spring-5/src/test/java/com/baeldung/Example1ManualTest.java
@@ -7,7 +7,7 @@ import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
-public class Example1IntegrationTest {
+public class Example1ManualTest {
@Test
public void test1a() {
diff --git a/spring-5/src/test/java/com/baeldung/Example2IntegrationTest.java b/spring-5/src/test/java/com/baeldung/Example2ManualTest.java
similarity index 93%
rename from spring-5/src/test/java/com/baeldung/Example2IntegrationTest.java
rename to spring-5/src/test/java/com/baeldung/Example2ManualTest.java
index 6ed53ca4e9..9c47b17fdb 100644
--- a/spring-5/src/test/java/com/baeldung/Example2IntegrationTest.java
+++ b/spring-5/src/test/java/com/baeldung/Example2ManualTest.java
@@ -7,7 +7,7 @@ import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
-public class Example2IntegrationTest {
+public class Example2ManualTest {
@Test
public void test1a() {
diff --git a/spring-5/src/test/java/com/baeldung/ParallelIntegrationTest.java b/spring-5/src/test/java/com/baeldung/ParallelManualTest.java
similarity index 62%
rename from spring-5/src/test/java/com/baeldung/ParallelIntegrationTest.java
rename to spring-5/src/test/java/com/baeldung/ParallelManualTest.java
index 1ce96de4ef..5c3a111c62 100644
--- a/spring-5/src/test/java/com/baeldung/ParallelIntegrationTest.java
+++ b/spring-5/src/test/java/com/baeldung/ParallelManualTest.java
@@ -5,18 +5,18 @@ import org.junit.experimental.ParallelComputer;
import org.junit.runner.Computer;
import org.junit.runner.JUnitCore;
-public class ParallelIntegrationTest {
+public class ParallelManualTest {
@Test
public void runTests() {
- final Class>[] classes = { Example1IntegrationTest.class, Example2IntegrationTest.class };
+ final Class>[] classes = { Example1ManualTest.class, Example2ManualTest.class };
JUnitCore.runClasses(new Computer(), classes);
}
@Test
public void runTestsInParallel() {
- final Class>[] classes = { Example1IntegrationTest.class, Example2IntegrationTest.class };
+ final Class>[] classes = { Example1ManualTest.class, Example2ManualTest.class };
JUnitCore.runClasses(new ParallelComputer(true, true), classes);
}
diff --git a/spring-5/src/test/java/com/baeldung/jupiter/Spring5JUnit5ParallelIntegrationTest.java b/spring-5/src/test/java/com/baeldung/jupiter/Spring5JUnit5ParallelManualTest.java
similarity index 60%
rename from spring-5/src/test/java/com/baeldung/jupiter/Spring5JUnit5ParallelIntegrationTest.java
rename to spring-5/src/test/java/com/baeldung/jupiter/Spring5JUnit5ParallelManualTest.java
index 55b0fcf267..b9ed87ed73 100644
--- a/spring-5/src/test/java/com/baeldung/jupiter/Spring5JUnit5ParallelIntegrationTest.java
+++ b/spring-5/src/test/java/com/baeldung/jupiter/Spring5JUnit5ParallelManualTest.java
@@ -1,24 +1,24 @@
package com.baeldung.jupiter;
-import com.baeldung.Example1IntegrationTest;
-import com.baeldung.Example2IntegrationTest;
+import com.baeldung.Example1ManualTest;
+import com.baeldung.Example2ManualTest;
import org.junit.experimental.ParallelComputer;
import org.junit.jupiter.api.Test;
import org.junit.runner.Computer;
import org.junit.runner.JUnitCore;
-class Spring5JUnit5ParallelIntegrationTest {
+class Spring5JUnit5ParallelManualTest {
@Test
void givenTwoTestClasses_whenJUnitRunParallel_thenTheTestsExecutingParallel() {
- final Class>[] classes = { Example1IntegrationTest.class, Example2IntegrationTest.class };
+ final Class>[] classes = { Example1ManualTest.class, Example2ManualTest.class };
JUnitCore.runClasses(new ParallelComputer(true, true), classes);
}
@Test
void givenTwoTestClasses_whenJUnitRunParallel_thenTheTestsExecutingLinear() {
- final Class>[] classes = { Example1IntegrationTest.class, Example2IntegrationTest.class };
+ final Class>[] classes = { Example1ManualTest.class, Example2ManualTest.class };
JUnitCore.runClasses(new Computer(), classes);
}
diff --git a/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/CustomUserAttrController.java b/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/CustomUserAttrController.java
index 1959590e5a..5b267ae19e 100644
--- a/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/CustomUserAttrController.java
+++ b/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/CustomUserAttrController.java
@@ -24,6 +24,8 @@ public class CustomUserAttrController {
final Principal principal = (Principal) authentication.getPrincipal();
String dob = "";
+ String userIdByToken = "";
+ String userIdByMapper = "";
if (principal instanceof KeycloakPrincipal) {
@@ -31,6 +33,9 @@ public class CustomUserAttrController {
IDToken token = kPrincipal.getKeycloakSecurityContext()
.getIdToken();
+ userIdByToken = token.getSubject();
+ userIdByMapper = token.getOtherClaims().get("user_id").toString();
+
Map customClaims = token.getOtherClaims();
if (customClaims.containsKey("DOB")) {
@@ -39,6 +44,8 @@ public class CustomUserAttrController {
}
model.addAttribute("username", principal.getName());
+ model.addAttribute("userIDByToken", userIdByToken);
+ model.addAttribute("userIDByMapper", userIdByMapper);
model.addAttribute("dob", dob);
return "userInfo";
}
diff --git a/spring-boot-modules/spring-boot-keycloak/src/main/resources/application-embedded.properties b/spring-boot-modules/spring-boot-keycloak/src/main/resources/application-embedded.properties
new file mode 100644
index 0000000000..7e1985f0ad
--- /dev/null
+++ b/spring-boot-modules/spring-boot-keycloak/src/main/resources/application-embedded.properties
@@ -0,0 +1,9 @@
+### server port
+server.port=8080
+
+#Keycloak Configuration
+keycloak.auth-server-url=http://localhost:8083/auth
+keycloak.realm=baeldung
+keycloak.resource=customerClient
+keycloak.public-client=true
+keycloak.principal-attribute=preferred_username
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-keycloak/src/main/resources/templates/userInfo.html b/spring-boot-modules/spring-boot-keycloak/src/main/resources/templates/userInfo.html
index 1446fe2124..7f772398c1 100644
--- a/spring-boot-modules/spring-boot-keycloak/src/main/resources/templates/userInfo.html
+++ b/spring-boot-modules/spring-boot-keycloak/src/main/resources/templates/userInfo.html
@@ -7,6 +7,12 @@
Hello, --name--.
+
+ User ID By Token: --userID--.
+
+
+ User ID By Mapper: --userID--.
+
Your Date of Birth as per our records is .
diff --git a/spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/properties/reloading/PropertiesReloadIntegrationTest.java b/spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/properties/reloading/PropertiesReloadManualTest.java
similarity index 99%
rename from spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/properties/reloading/PropertiesReloadIntegrationTest.java
rename to spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/properties/reloading/PropertiesReloadManualTest.java
index 0c28cb085b..88e22af4ba 100644
--- a/spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/properties/reloading/PropertiesReloadIntegrationTest.java
+++ b/spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/properties/reloading/PropertiesReloadManualTest.java
@@ -24,7 +24,7 @@ import org.springframework.web.context.WebApplicationContext;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringBootPropertiesTestApplication.class)
-public class PropertiesReloadIntegrationTest {
+public class PropertiesReloadManualTest {
protected MockMvc mvc;
diff --git a/spring-security-modules/spring-security-saml/src/main/resources/application.properties b/spring-security-modules/spring-security-saml/src/main/resources/application.properties
index 1d93a12737..fd7798dda9 100644
--- a/spring-security-modules/spring-security-saml/src/main/resources/application.properties
+++ b/spring-security-modules/spring-security-saml/src/main/resources/application.properties
@@ -1,8 +1,9 @@
-saml.keystore.location=classpath:/saml/samlKeystore.jks
+saml.keystore.location=classpath:/saml/saml-keystore
# Password for Java keystore and item therein
saml.keystore.password=
saml.keystore.alias=
# SAML Entity ID extracted from top of SAML metadata file
saml.idp=
-saml.sp=http://localhost:8080/saml/metadata
\ No newline at end of file
+saml.sp=http://localhost:8080/saml/metadata
+spring.main.allow-circular-references=true
\ No newline at end of file
diff --git a/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/requestmappingvalue/WelcomeController.java b/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/requestmappingvalue/WelcomeController.java
new file mode 100644
index 0000000000..bbc978ccd4
--- /dev/null
+++ b/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/requestmappingvalue/WelcomeController.java
@@ -0,0 +1,15 @@
+package com.baeldung.requestmappingvalue;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/${request.value}")
+public class WelcomeController {
+
+ @GetMapping
+ public String getWelcomeMessage() {
+ return "Welcome to Baeldung!";
+ }
+}
diff --git a/spring-web-modules/spring-mvc-basics-5/src/main/resources/application.properties b/spring-web-modules/spring-mvc-basics-5/src/main/resources/application.properties
index 935f91554b..61a0755b93 100644
--- a/spring-web-modules/spring-mvc-basics-5/src/main/resources/application.properties
+++ b/spring-web-modules/spring-mvc-basics-5/src/main/resources/application.properties
@@ -1 +1,2 @@
server.servlet.context-path=/spring-mvc-basics
+request.value=welcome
diff --git a/spring-web-modules/spring-mvc-basics-5/src/test/java/com/baeldung/requestmappingvalue/WelcomeControllerUnitTest.java b/spring-web-modules/spring-mvc-basics-5/src/test/java/com/baeldung/requestmappingvalue/WelcomeControllerUnitTest.java
new file mode 100644
index 0000000000..ac5140733b
--- /dev/null
+++ b/spring-web-modules/spring-mvc-basics-5/src/test/java/com/baeldung/requestmappingvalue/WelcomeControllerUnitTest.java
@@ -0,0 +1,25 @@
+package com.baeldung.requestmappingvalue;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.jupiter.api.Test;
+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.web.servlet.MockMvc;
+
+@SpringBootTest
+@AutoConfigureMockMvc
+class WelcomeControllerUnitTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Test
+ public void whenUserAccessToWelcome_thenReturnOK() throws Exception {
+ this.mockMvc.perform(get("/welcome"))
+ .andExpect(status().isOk());
+ }
+
+}
\ No newline at end of file