Reformatted code

This commit is contained in:
Graham Cox 2022-05-19 10:14:05 +01:00
parent 177f912393
commit 762d5f0f56
27 changed files with 278 additions and 283 deletions

View File

@ -3,16 +3,21 @@
This application exists as an example for the Lightrun series of articles. This application exists as an example for the Lightrun series of articles.
## Building ## Building
This application requires [Apache Maven](https://maven.apache.org/) and [Java 17+](https://www.oracle.com/java/technologies/downloads/). 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: Building the code is done by executing:
``` ```
$ mvn install $ mvn install
``` ```
from the top level. from the top level.
## Running ## Running
The application consists of three services: The application consists of three services:
* Tasks * Tasks
* Users * Users
* API * API
@ -23,7 +28,9 @@ The Tasks and Users services exist as microservices for managing one facet of da
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. 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: 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 $ java -jar ./target/tasks-service-0.0.1-SNAPSHOT.jar
``` ```

View File

@ -1,45 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?> <?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" <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version> <version>2.6.7</version>
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
<groupId>com.baeldung</groupId> <groupId>com.baeldung</groupId>
<artifactId>api-service</artifactId> <artifactId>api-service</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<name>api-service</name> <name>api-service</name>
<description>Aggregator Service for LightRun Article</description> <description>Aggregator Service for LightRun Article</description>
<properties> <properties>
<java.version>17</java.version> <java.version>17</java.version>
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId> <artifactId>spring-boot-starter-actuator</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -6,8 +6,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication @SpringBootApplication
public class ApiServiceApplication { public class ApiServiceApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(ApiServiceApplication.class, args); SpringApplication.run(ApiServiceApplication.class, args);
} }
} }

View File

@ -1,20 +1,20 @@
package com.baeldung.apiservice; package com.baeldung.apiservice;
import org.slf4j.MDC; import java.util.UUID;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.util.UUID;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component @Component
public class RequestIdGenerator implements HandlerInterceptor { public class RequestIdGenerator implements HandlerInterceptor {
@Override @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestId = UUID.randomUUID().toString(); String requestId = UUID.randomUUID()
.toString();
MDC.put(RequestIdGenerator.class.getCanonicalName(), requestId); MDC.put(RequestIdGenerator.class.getCanonicalName(), requestId);
response.addHeader("X-Request-Id", requestId); response.addHeader("X-Request-Id", requestId);

View File

@ -9,12 +9,12 @@ import org.springframework.web.client.RestTemplate;
public class RestTemplateConfig { public class RestTemplateConfig {
@Bean @Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) { public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder return builder.additionalInterceptors((request, body, execution) -> {
.additionalInterceptors((request, body, execution) -> { request.getHeaders()
request.getHeaders().add("X-Request-Id", RequestIdGenerator.getRequestId()); .add("X-Request-Id", RequestIdGenerator.getRequestId());
return execution.execute(request, body); return execution.execute(request, body);
}) })
.build(); .build();
} }
} }

View File

@ -2,10 +2,5 @@ package com.baeldung.apiservice.adapters.http;
import java.time.Instant; import java.time.Instant;
public record TaskResponse(String id, public record TaskResponse(String id, String title, Instant created, UserResponse createdBy, UserResponse assignedTo, String status) {
String title,
Instant created,
UserResponse createdBy,
UserResponse assignedTo,
String status) {
} }

View File

@ -1,11 +1,17 @@
package com.baeldung.apiservice.adapters.http; 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.Task;
import com.baeldung.apiservice.adapters.tasks.TaskRepository; import com.baeldung.apiservice.adapters.tasks.TaskRepository;
import com.baeldung.apiservice.adapters.users.UserRepository; import com.baeldung.apiservice.adapters.users.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
@RequestMapping("/") @RequestMapping("/")
@RestController @RestController
@ -27,15 +33,10 @@ public class TasksController {
} }
private TaskResponse buildResponse(Task task) { private TaskResponse buildResponse(Task task) {
return new TaskResponse(task.id(), return new TaskResponse(task.id(), task.title(), task.created(), getUser(task.createdBy()), getUser(task.assignedTo()), task.status());
task.title(),
task.created(),
getUser(task.createdBy()),
getUser(task.assignedTo()),
task.status());
} }
private UserResponse getUser(String userId) { private UserResponse getUser(String userId) {
if (userId == null) { if (userId == null) {
return null; return null;
} }
@ -47,6 +48,7 @@ public class TasksController {
return new UserResponse(user.id(), user.name()); return new UserResponse(user.id(), user.name());
} }
@ExceptionHandler(UnknownTaskException.class) @ExceptionHandler(UnknownTaskException.class)
@ResponseStatus(HttpStatus.NOT_FOUND) @ResponseStatus(HttpStatus.NOT_FOUND)
public void handleUnknownTask() { public void handleUnknownTask() {

View File

@ -2,10 +2,5 @@ package com.baeldung.apiservice.adapters.tasks;
import java.time.Instant; import java.time.Instant;
public record Task(String id, public record Task(String id, String title, Instant created, String createdBy, String assignedTo, String status) {
String title,
Instant created,
String createdBy,
String assignedTo,
String status) {
} }

View File

@ -17,9 +17,9 @@ public class TaskRepository {
public Task getTaskById(String id) { public Task getTaskById(String id) {
var uri = UriComponentsBuilder.fromUriString(tasksService) var uri = UriComponentsBuilder.fromUriString(tasksService)
.path(id) .path(id)
.build() .build()
.toUri(); .toUri();
try { try {
return restTemplate.getForObject(uri, Task.class); return restTemplate.getForObject(uri, Task.class);

View File

@ -1,6 +1,5 @@
package com.baeldung.apiservice.adapters.users; package com.baeldung.apiservice.adapters.users;
import com.baeldung.apiservice.adapters.users.User;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@ -18,9 +17,9 @@ public class UserRepository {
public User getUserById(String id) { public User getUserById(String id) {
var uri = UriComponentsBuilder.fromUriString(usersService) var uri = UriComponentsBuilder.fromUriString(usersService)
.path(id) .path(id)
.build() .build()
.toUri(); .toUri();
try { try {
return restTemplate.getForObject(uri, User.class); return restTemplate.getForObject(uri, User.class);

View File

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?> <?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" <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.baelduung</groupId> <groupId>com.baelduung</groupId>
<artifactId>lightrun-demo</artifactId> <artifactId>lightrun-demo</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>lightrun</name> <name>lightrun</name>
<description>Services for LightRun Article</description> <description>Services for LightRun Article</description>
<modules> <modules>
<module>tasks-service</module> <module>tasks-service</module>

View File

@ -1,67 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?> <?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" <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version> <version>2.6.7</version>
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
<groupId>com.baeldung</groupId> <groupId>com.baeldung</groupId>
<artifactId>tasks-service</artifactId> <artifactId>tasks-service</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<name>tasks-service</name> <name>tasks-service</name>
<description>Tasks Service for LightRun Article</description> <description>Tasks Service for LightRun Article</description>
<properties> <properties>
<java.version>17</java.version> <java.version>17</java.version>
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId> <artifactId>spring-boot-starter-actuator</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-artemis</artifactId> <artifactId>spring-boot-starter-artemis</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId> <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.flywaydb</groupId> <groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId> <artifactId>flyway-core</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>
<artifactId>h2</artifactId> <artifactId>h2</artifactId>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.activemq</groupId> <groupId>org.apache.activemq</groupId>
<artifactId>artemis-jms-server</artifactId> <artifactId>artemis-jms-server</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -6,8 +6,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication @SpringBootApplication
public class TasksServiceApplication { public class TasksServiceApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(TasksServiceApplication.class, args); SpringApplication.run(TasksServiceApplication.class, args);
} }
} }

View File

@ -13,10 +13,5 @@ package com.baeldung.tasksservice.adapters.http;
import java.time.Instant; import java.time.Instant;
public record TaskResponse(String id, public record TaskResponse(String id, String title, Instant created, String createdBy, String assignedTo, String status) {
String title,
Instant created,
String createdBy,
String assignedTo,
String status) {
} }

View File

@ -15,9 +15,6 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.baeldung.tasksservice.adapters.repository.TaskRecord;
import com.baeldung.tasksservice.service.TasksService;
import com.baeldung.tasksservice.service.UnknownTaskException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
@ -32,6 +29,10 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController; 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 @RestController
@RequestMapping("/") @RequestMapping("/")
class TasksController { class TasksController {
@ -46,13 +47,12 @@ class TasksController {
} }
@GetMapping @GetMapping
public List<TaskResponse> searchTasks(@RequestParam("status") Optional<String> status, public List<TaskResponse> searchTasks(@RequestParam("status") Optional<String> status, @RequestParam("createdBy") Optional<String> createdBy) {
@RequestParam("createdBy") Optional<String> createdBy) {
var tasks = tasksService.search(status, createdBy); var tasks = tasksService.search(status, createdBy);
return tasks.stream() return tasks.stream()
.map(this::buildResponse) .map(this::buildResponse)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@GetMapping("/{id}") @GetMapping("/{id}")
@ -67,16 +67,14 @@ class TasksController {
} }
@PatchMapping("/{id}") @PatchMapping("/{id}")
public TaskResponse patchTask(@PathVariable("id") String id, public TaskResponse patchTask(@PathVariable("id") String id, @RequestBody PatchTaskRequest body) {
@RequestBody PatchTaskRequest body) {
var task = tasksService.updateTask(id, body.status(), body.assignedTo()); var task = tasksService.updateTask(id, body.status(), body.assignedTo());
return buildResponse(task); return buildResponse(task);
} }
private TaskResponse buildResponse(final TaskRecord task) { private TaskResponse buildResponse(final TaskRecord task) {
return new TaskResponse(task.getId(), task.getTitle(), task.getCreated(), task.getCreatedBy(), return new TaskResponse(task.getId(), task.getTitle(), task.getCreated(), task.getCreatedBy(), task.getAssignedTo(), task.getStatus());
task.getAssignedTo(), task.getStatus());
} }
@ExceptionHandler(UnknownTaskException.class) @ExceptionHandler(UnknownTaskException.class)

View File

@ -1,8 +1,8 @@
package com.baeldung.tasksservice.adapters.jms; package com.baeldung.tasksservice.adapters.jms;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener; import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Service;
import com.baeldung.tasksservice.service.DeletedUserService; import com.baeldung.tasksservice.service.DeletedUserService;

View File

@ -11,11 +11,12 @@
package com.baeldung.tasksservice.adapters.repository; package com.baeldung.tasksservice.adapters.repository;
import java.time.Instant;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.Table; import javax.persistence.Table;
import java.time.Instant;
@Entity @Entity
@Table(name = "tasks") @Table(name = "tasks")
@ -32,8 +33,7 @@ public class TaskRecord {
private String assignedTo; private String assignedTo;
private String status; private String status;
public TaskRecord(final String id, final String title, final Instant created, final String createdBy, public TaskRecord(final String id, final String title, final Instant created, final String createdBy, final String assignedTo, final String status) {
final String assignedTo, final String status) {
this.id = id; this.id = id;
this.title = title; this.title = title;
this.created = created; this.created = created;

View File

@ -2,26 +2,26 @@ package com.baeldung.tasksservice.service;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import com.baeldung.tasksservice.adapters.repository.TaskRecord;
import com.baeldung.tasksservice.adapters.repository.TasksRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.baeldung.tasksservice.adapters.repository.TaskRecord;
import com.baeldung.tasksservice.adapters.repository.TasksRepository;
@Service @Service
public class DeletedUserService { public class DeletedUserService {
@Autowired @Autowired
private TasksRepository tasksRepository; private TasksRepository tasksRepository;
@Transactional @Transactional
public void handleDeletedUser(String user) { public void handleDeletedUser(String user) {
var ownedByUser = tasksRepository.findByCreatedBy(user); var ownedByUser = tasksRepository.findByCreatedBy(user);
tasksRepository.deleteAll(ownedByUser); tasksRepository.deleteAll(ownedByUser);
var assignedToUser = tasksRepository.findByAssignedTo(user); var assignedToUser = tasksRepository.findByAssignedTo(user);
for (TaskRecord record : assignedToUser) { for (TaskRecord record : assignedToUser) {
record.setAssignedTo(null); record.setAssignedTo(null);
record.setStatus("PENDING"); record.setStatus("PENDING");
}
} }
}
} }

View File

@ -11,29 +11,33 @@
package com.baeldung.tasksservice.service; package com.baeldung.tasksservice.service;
import javax.transaction.Transactional;
import java.time.Instant; import java.time.Instant;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import com.baeldung.tasksservice.adapters.repository.TaskRecord; import javax.transaction.Transactional;
import com.baeldung.tasksservice.adapters.repository.TasksRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.baeldung.tasksservice.adapters.repository.TaskRecord;
import com.baeldung.tasksservice.adapters.repository.TasksRepository;
@Service @Service
public class TasksService { public class TasksService {
@Autowired @Autowired
private TasksRepository tasksRepository; private TasksRepository tasksRepository;
public TaskRecord getTaskById(String id) { public TaskRecord getTaskById(String id) {
return tasksRepository.findById(id).orElseThrow(() -> new UnknownTaskException(id)); return tasksRepository.findById(id)
.orElseThrow(() -> new UnknownTaskException(id));
} }
@Transactional @Transactional
public void deleteTaskById(String id) { public void deleteTaskById(String id) {
var task = tasksRepository.findById(id).orElseThrow(() -> new UnknownTaskException(id)); var task = tasksRepository.findById(id)
.orElseThrow(() -> new UnknownTaskException(id));
tasksRepository.delete(task); tasksRepository.delete(task);
} }
@ -51,7 +55,8 @@ public class TasksService {
@Transactional @Transactional
public TaskRecord updateTask(String id, Optional<String> newStatus, Optional<String> newAssignedTo) { public TaskRecord updateTask(String id, Optional<String> newStatus, Optional<String> newAssignedTo) {
var task = tasksRepository.findById(id).orElseThrow(() -> new UnknownTaskException(id)); var task = tasksRepository.findById(id)
.orElseThrow(() -> new UnknownTaskException(id));
newStatus.ifPresent(task::setStatus); newStatus.ifPresent(task::setStatus);
newAssignedTo.ifPresent(task::setAssignedTo); newAssignedTo.ifPresent(task::setAssignedTo);
@ -60,12 +65,8 @@ public class TasksService {
} }
public TaskRecord createTask(String title, String createdBy) { public TaskRecord createTask(String title, String createdBy) {
var task = new TaskRecord(UUID.randomUUID().toString(), var task = new TaskRecord(UUID.randomUUID()
title, .toString(), title, Instant.now(), createdBy, null, "PENDING");
Instant.now(),
createdBy,
null,
"PENDING");
tasksRepository.save(task); tasksRepository.save(task);
return task; return task;
} }

View File

@ -1,13 +1,9 @@
server.port=8082 server.port=8082
spring.artemis.mode=EMBEDDED spring.artemis.mode=EMBEDDED
spring.artemis.host=localhost spring.artemis.host=localhost
spring.artemis.port=61616 spring.artemis.port=61616
spring.artemis.embedded.enabled=true spring.artemis.embedded.enabled=true
spring.jms.template.default-destination=my-queue-1 spring.jms.template.default-destination=my-queue-1
logging.level.org.apache.activemq.audit.base=WARN logging.level.org.apache.activemq.audit.base=WARN
logging.level.org.apache.activemq.audit.message=WARN logging.level.org.apache.activemq.audit.message=WARN

View File

@ -3,16 +3,21 @@
This application exists as an example for the Lightrun series of articles. This application exists as an example for the Lightrun series of articles.
## Building ## 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. 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: As such, building the code is done by executing:
``` ```
$ ./mvnw install $ ./mvnw install
``` ```
from the top level. from the top level.
## Running ## Running
The application consists of three services: The application consists of three services:
* Tasks * Tasks
* Users * Users
* API * API
@ -23,7 +28,9 @@ The Tasks and Users services exist as microservices for managing one facet of da
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. 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: 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 $ java -jar ./target/tasks-service-0.0.1-SNAPSHOT.jar
``` ```

View File

@ -1,63 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?> <?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" <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version> <version>2.6.7</version>
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
<groupId>com.baeldung</groupId> <groupId>com.baeldung</groupId>
<artifactId>users-service</artifactId> <artifactId>users-service</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<name>users-service</name> <name>users-service</name>
<description>Users Service for LightRun Article</description> <description>Users Service for LightRun Article</description>
<properties> <properties>
<java.version>17</java.version> <java.version>17</java.version>
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId> <artifactId>spring-boot-starter-actuator</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-artemis</artifactId> <artifactId>spring-boot-starter-artemis</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId> <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.flywaydb</groupId> <groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId> <artifactId>flyway-core</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>
<artifactId>h2</artifactId> <artifactId>h2</artifactId>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -6,8 +6,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication @SpringBootApplication
public class UsersServiceApplication { public class UsersServiceApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(UsersServiceApplication.class, args); SpringApplication.run(UsersServiceApplication.class, args);
} }
} }

View File

@ -11,6 +11,5 @@
package com.baeldung.usersservice.adapters.http; package com.baeldung.usersservice.adapters.http;
public record UserResponse(String id, public record UserResponse(String id, String name) {
String name) {
} }

View File

@ -11,9 +11,6 @@
package com.baeldung.usersservice.adapters.http; package com.baeldung.usersservice.adapters.http;
import com.baeldung.usersservice.adapters.repository.UserRecord;
import com.baeldung.usersservice.service.UsersService;
import com.baeldung.usersservice.service.UnknownUserException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
@ -27,6 +24,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController; 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 @RestController
@RequestMapping("/") @RequestMapping("/")
class UsersController { class UsersController {
@ -52,12 +53,12 @@ class UsersController {
} }
@PatchMapping("/{id}") @PatchMapping("/{id}")
public UserResponse patchUser(@PathVariable("id") String id, public UserResponse patchUser(@PathVariable("id") String id, @RequestBody PatchUserRequest body) {
@RequestBody PatchUserRequest body) {
var user = usersService.updateUser(id, body.name()); var user = usersService.updateUser(id, body.name());
return buildResponse(user); return buildResponse(user);
} }
private UserResponse buildResponse(final UserRecord user) { private UserResponse buildResponse(final UserRecord user) {
return new UserResponse(user.getId(), user.getName()); return new UserResponse(user.getId(), user.getName());
} }

View File

@ -11,18 +11,17 @@
package com.baeldung.usersservice.service; package com.baeldung.usersservice.service;
import javax.transaction.Transactional;
import java.time.Instant;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; 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.jms.JmsSender;
import com.baeldung.usersservice.adapters.repository.UserRecord; import com.baeldung.usersservice.adapters.repository.UserRecord;
import com.baeldung.usersservice.adapters.repository.UsersRepository; import com.baeldung.usersservice.adapters.repository.UsersRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Service;
@Service @Service
public class UsersService { public class UsersService {
@ -33,12 +32,14 @@ public class UsersService {
private JmsSender jmsSender; private JmsSender jmsSender;
public UserRecord getUserById(String id) { public UserRecord getUserById(String id) {
return usersRepository.findById(id).orElseThrow(() -> new UnknownUserException(id)); return usersRepository.findById(id)
.orElseThrow(() -> new UnknownUserException(id));
} }
@Transactional @Transactional
public void deleteUserById(String id) { public void deleteUserById(String id) {
var user = usersRepository.findById(id).orElseThrow(() -> new UnknownUserException(id)); var user = usersRepository.findById(id)
.orElseThrow(() -> new UnknownUserException(id));
usersRepository.delete(user); usersRepository.delete(user);
jmsSender.sendDeleteUserMessage(id); jmsSender.sendDeleteUserMessage(id);
@ -46,7 +47,8 @@ public class UsersService {
@Transactional @Transactional
public UserRecord updateUser(String id, Optional<String> newName) { public UserRecord updateUser(String id, Optional<String> newName) {
var user = usersRepository.findById(id).orElseThrow(() -> new UnknownUserException(id)); var user = usersRepository.findById(id)
.orElseThrow(() -> new UnknownUserException(id));
newName.ifPresent(user::setName); newName.ifPresent(user::setName);
@ -54,7 +56,8 @@ public class UsersService {
} }
public UserRecord createUser(String name) { public UserRecord createUser(String name) {
var user = new UserRecord(UUID.randomUUID().toString(), name); var user = new UserRecord(UUID.randomUUID()
.toString(), name);
usersRepository.save(user); usersRepository.save(user);
return user; return user;
} }

View File

@ -1,10 +1,7 @@
server.port=8081 server.port=8081
spring.artemis.host=localhost spring.artemis.host=localhost
spring.artemis.port=61616 spring.artemis.port=61616
spring.jms.template.default-destination=my-queue-1 spring.jms.template.default-destination=my-queue-1
logging.level.org.apache.activemq.audit.base=WARN logging.level.org.apache.activemq.audit.base=WARN
logging.level.org.apache.activemq.audit.message=WARN logging.level.org.apache.activemq.audit.message=WARN