Merge branch 'eugenp:master' into master
This commit is contained in:
commit
af7b39c438
@ -0,0 +1,36 @@
|
||||
package com.baeldung.java9.delimiters;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DelimiterDemo {
|
||||
|
||||
public static List<String> scannerWithDelimiter(String input, String delimiter) {
|
||||
try (Scanner scan = new Scanner(input)) {
|
||||
scan.useDelimiter(delimiter);
|
||||
List<String> result = new ArrayList<String>();
|
||||
scan.forEachRemaining(result::add);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> scannerWithDelimiterUsingPattern(String input, Pattern delimiter) {
|
||||
try (Scanner scan = new Scanner(input)) {
|
||||
scan.useDelimiter(delimiter);
|
||||
List<String> result = new ArrayList<String>();
|
||||
scan.forEachRemaining(result::add);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> baseScanner(String input) {
|
||||
try (Scanner scan = new Scanner(input)) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
scan.forEachRemaining(result::add);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package com.baeldung.java9.delimiters;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class DelimiterDemoUnitTest {
|
||||
|
||||
@Test
|
||||
void givenSimpleCharacterDelimiter_whenScannerWithDelimiter_ThenInputIsCorrectlyParsed() {
|
||||
checkOutput(DelimiterDemo::scannerWithDelimiter, "Welcome to Baeldung", "\\s", Arrays.asList("Welcome", "to", "Baeldung"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenStringDelimiter_whenScannerWithDelimiter_ThenInputIsCorrectlyParsed() {
|
||||
checkOutput(DelimiterDemo::scannerWithDelimiter, "HelloBaeldungHelloWorld", "Hello", Arrays.asList("Baeldung", "World"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenVariousPossibleDelimiters_whenScannerWithDelimiter_ThenInputIsCorrectlyParsed() {
|
||||
checkOutput(DelimiterDemo::scannerWithDelimiter, "Welcome to Baeldung.\nThank you for reading.\nThe team", "\n|\\s", Arrays.asList("Welcome", "to", "Baeldung.", "Thank", "you", "for", "reading.", "The", "team"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenWildcardRegexDelimiter_whenScannerWithDelimiter_ThenInputIsCorrectlyParsed() {
|
||||
checkOutput(DelimiterDemo::scannerWithDelimiter, "1aaaaaaa2aa3aaa4", "a+", Arrays.asList("1", "2", "3", "4"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenSimpleCharacterDelimiter_whenScannerWithDelimiterUsingPattern_ThenInputIsCorrectlyParsed() {
|
||||
checkOutput(DelimiterDemo::scannerWithDelimiterUsingPattern, "Welcome to Baeldung", Pattern.compile("\\s"), Arrays.asList("Welcome", "to", "Baeldung"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenStringDelimiter_whenScannerWithDelimiterUsingPattern_ThenInputIsCorrectlyParsed() {
|
||||
checkOutput(DelimiterDemo::scannerWithDelimiterUsingPattern, "HelloBaeldungHelloWorld", Pattern.compile("Hello"), Arrays.asList("Baeldung", "World"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenVariousPossibleDelimiters_whenScannerWithDelimiterUsingPattern_ThenInputIsCorrectlyParsed() {
|
||||
checkOutput(DelimiterDemo::scannerWithDelimiterUsingPattern, "Welcome to Baeldung.\nThank you for reading.\nThe team", Pattern.compile("\n|\\s"), Arrays.asList("Welcome", "to", "Baeldung.", "Thank", "you", "for", "reading.", "The", "team"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenWildcardRegexDelimiters_whenScannerWithDelimiterUsingPattern_ThenInputIsCorrectlyParsed() {
|
||||
checkOutput(DelimiterDemo::scannerWithDelimiterUsingPattern, "1aaaaaaa2aa3aaa4", Pattern.compile("a*"), Arrays.asList("1", "2", "3", "4"));
|
||||
}
|
||||
|
||||
void checkOutput(BiFunction<String, String, List<String>> function, String input, String delimiter, List<String> expectedOutput) {
|
||||
assertEquals(expectedOutput, function.apply(input, delimiter));
|
||||
}
|
||||
|
||||
void checkOutput(BiFunction<String, Pattern, List<String>> function, String input, Pattern delimiter, List<String> expectedOutput) {
|
||||
assertEquals(expectedOutput, function.apply(input, delimiter));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenBaseScanner_ThenWhitespacesAreUsedAsDelimiters() {
|
||||
assertEquals(List.of("Welcome", "at", "Baeldung"), DelimiterDemo.baseScanner("Welcome at Baeldung"));
|
||||
}
|
||||
|
||||
}
|
@ -3,4 +3,4 @@
|
||||
This module contains articles about Java HttpClient
|
||||
|
||||
### Relevant articles
|
||||
- TODO
|
||||
- [Posting with Java HttpClient](https://www.baeldung.com/java-httpclient-post)
|
||||
|
@ -0,0 +1,12 @@
|
||||
package com.baeldung.http;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.time.Duration;
|
||||
|
||||
public class JavaHttpClientTimeout {
|
||||
static HttpClient getHttpClientWithTimeout(int seconds) {
|
||||
return HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofSeconds(seconds))
|
||||
.build();
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package com.baeldung.http;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpConnectTimeoutException;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import static com.baeldung.http.JavaHttpClientTimeout.getHttpClientWithTimeout;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class JavaHttpClientTimeoutIntegrationTest {
|
||||
|
||||
private HttpClient httpClient;
|
||||
private HttpRequest httpRequest;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
httpClient = getHttpClientWithTimeout(3);
|
||||
httpClient.connectTimeout().map(Duration::toSeconds)
|
||||
.ifPresent(sec -> System.out.println("Timeout in seconds: " + sec));
|
||||
|
||||
httpRequest = HttpRequest.newBuilder().uri(URI.create("http://10.255.255.1")).GET().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowExceptionWhenMakingSyncCall() {
|
||||
HttpConnectTimeoutException thrown = assertThrows(
|
||||
HttpConnectTimeoutException.class,
|
||||
() -> httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()),
|
||||
"Expected doThing() to throw, but it didn't"
|
||||
);
|
||||
assertTrue(thrown.getMessage().contains("timed out"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowExceptionWhenMakingASyncCall() throws ExecutionException, InterruptedException, TimeoutException {
|
||||
CompletableFuture<String> completableFuture =
|
||||
httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString())
|
||||
.thenApply(HttpResponse::body)
|
||||
.exceptionally(Throwable::getMessage);
|
||||
String response = completableFuture.get(5, TimeUnit.SECONDS);
|
||||
assertTrue(response.contains("timed out"));
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package com.baeldung.searchfilesbywildcards;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.FileVisitor;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.PathMatcher;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SearchFileByWildcard {
|
||||
public static List<String> matchesList = new ArrayList<String>();
|
||||
public List<String> searchWithWc(Path rootDir, String pattern) throws IOException {
|
||||
matchesList.clear();
|
||||
FileVisitor<Path> matcherVisitor = new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attribs) throws IOException {
|
||||
FileSystem fs = FileSystems.getDefault();
|
||||
PathMatcher matcher = fs.getPathMatcher(pattern);
|
||||
Path name = file.getFileName();
|
||||
if (matcher.matches(name)) {
|
||||
matchesList.add(name.toString());
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
};
|
||||
Files.walkFileTree(rootDir, matcherVisitor);
|
||||
return matchesList;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.baeldung.searchfilesbywildcards;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class SearchFileByWildcardUnitTest {
|
||||
@Test
|
||||
public void whenFourFilenameMatch_thenListOfFour() throws IOException {
|
||||
SearchFileByWildcard sfbw = new SearchFileByWildcard();
|
||||
List<String> actual = sfbw.searchWithWc(Paths.get("src/test/resources/sfbw"), "glob:*.{txt,docx}");
|
||||
|
||||
assertEquals(new HashSet<>(Arrays.asList("six.txt", "three.txt", "two.docx", "one.txt")), new HashSet<>(actual));
|
||||
}
|
||||
@Test
|
||||
public void whenOneFilenameMatch_thenListOfOne() throws IOException {
|
||||
SearchFileByWildcard sfbw = new SearchFileByWildcard();
|
||||
List<String> actual = sfbw.searchWithWc(Paths.get("src/test/resources/sfbw"), "glob:????.{csv}");
|
||||
|
||||
assertEquals(new HashSet<>(Arrays.asList("five.csv")), new HashSet<>(actual));
|
||||
}
|
||||
}
|
Binary file not shown.
@ -0,0 +1,7 @@
|
||||
package com.baeldung.reflection.proxy;
|
||||
|
||||
public interface AdvancedOperation {
|
||||
int multiply(int a, int b);
|
||||
|
||||
int divide(int a, int b);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.baeldung.reflection.proxy;
|
||||
|
||||
public interface BasicOperation {
|
||||
int add(int a, int b);
|
||||
|
||||
int subtract(int a, int b);
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package com.baeldung.reflection.proxy;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class DollarProxyUnitTest {
|
||||
@Test
|
||||
public void givenProxy_whenInvokingGetProxyClass_thenGeneratingProxyClass() {
|
||||
// Java 8: -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
|
||||
// Java 9 or later: -Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true
|
||||
// Note: System.setProperty() doesn't work here
|
||||
// because ProxyGenerator.saveGeneratedFiles read its property only once.
|
||||
// The @Test annotation in this method will generate a $Proxy class.
|
||||
|
||||
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
|
||||
Class<?>[] interfaces = {BasicOperation.class, AdvancedOperation.class};
|
||||
Class<?> proxyClass = Proxy.getProxyClass(classLoader, interfaces);
|
||||
|
||||
boolean isProxyClass = Proxy.isProxyClass(proxyClass);
|
||||
assertTrue(isProxyClass);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenReflection_whenReadingAnnotation_thenGeneratingProxyClass() {
|
||||
FunctionalInterface instance = Consumer.class.getDeclaredAnnotation(FunctionalInterface.class);
|
||||
Class<?> clazz = instance.getClass();
|
||||
|
||||
boolean isProxyClass = Proxy.isProxyClass(clazz);
|
||||
assertTrue(isProxyClass);
|
||||
}
|
||||
}
|
@ -30,6 +30,12 @@
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${apache-commons-lang3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>${assertj.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -0,0 +1,38 @@
|
||||
package com.baeldung.checkvowels;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class CheckVowels {
|
||||
private static final String VOWELS = "aeiouAEIOU";
|
||||
private static final Pattern VOWELS_PATTERN = Pattern.compile("[aeiou]", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
public static boolean isInVowelsString(char c) {
|
||||
return VOWELS.indexOf(c) != -1;
|
||||
}
|
||||
|
||||
public static boolean isInVowelsString(String c) {
|
||||
return VOWELS.contains(c);
|
||||
}
|
||||
|
||||
public static boolean isVowelBySwitch(char c) {
|
||||
switch (c) {
|
||||
case 'a':
|
||||
case 'e':
|
||||
case 'i':
|
||||
case 'o':
|
||||
case 'u':
|
||||
case 'A':
|
||||
case 'E':
|
||||
case 'I':
|
||||
case 'O':
|
||||
case 'U':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isVowelByRegex(String c) {
|
||||
return VOWELS_PATTERN.matcher(c).matches();
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package com.baeldung.checkvowels;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static com.baeldung.checkvowels.CheckVowels.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
class CheckVowelsUnitTest {
|
||||
|
||||
@Test
|
||||
void givenAVowelCharacter_thenInVowelString() {
|
||||
assertThat(isInVowelsString('e')).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenAConsonantCharacter_thenNotInVowelString() {
|
||||
assertThat(isInVowelsString('z')).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenAVowelString_thenInVowelString() {
|
||||
assertThat(isInVowelsString("e")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenAConsonantString_thenNotInVowelString() {
|
||||
assertThat(isInVowelsString("z")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenAVowelCharacter_thenInVowelSwitch() {
|
||||
assertThat(isVowelBySwitch('e')).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenAConsonantCharacter_thenNotInVowelSwitch() {
|
||||
assertThat(isVowelBySwitch('z')).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenAVowelString_thenInVowelPattern() {
|
||||
assertThat(isVowelByRegex("e")).isTrue();
|
||||
assertThat(isVowelByRegex("E")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenAVowelCharacter_thenInVowelPattern() {
|
||||
assertThat(isVowelByRegex(Character.toString('e'))).isTrue();
|
||||
assertThat(isVowelByRegex("E")).isTrue();
|
||||
}
|
||||
}
|
@ -8,3 +8,4 @@
|
||||
- [Convert Byte Size Into a Human-Readable Format in Java](https://www.baeldung.com/java-human-readable-byte-size)
|
||||
- [Convert boolean to int in Java](https://www.baeldung.com/java-boolean-to-int)
|
||||
- [Generate a Random Value From an Enum](https://www.baeldung.com/java-enum-random-value)
|
||||
- [Reverse a Number in Java](https://www.baeldung.com/java-reverse-number)
|
||||
|
29
lightrun/README.md
Normal file
29
lightrun/README.md
Normal file
@ -0,0 +1,29 @@
|
||||
# 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
|
||||
```
|
33
lightrun/api-service/.gitignore
vendored
Normal file
33
lightrun/api-service/.gitignore
vendored
Normal file
@ -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/
|
45
lightrun/api-service/pom.xml
Normal file
45
lightrun/api-service/pom.xml
Normal file
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.6.7</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>api-service</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>api-service</name>
|
||||
<description>Aggregator Service for LightRun Article</description>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package com.baeldung.apiservice;
|
||||
|
||||
import org.slf4j.MDC;
|
||||
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.HttpServletResponse;
|
||||
import java.util.UUID;
|
||||
|
||||
@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());
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
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) {
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package com.baeldung.apiservice.adapters.http;
|
||||
|
||||
import com.baeldung.apiservice.adapters.tasks.Task;
|
||||
import com.baeldung.apiservice.adapters.tasks.TaskRepository;
|
||||
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("/")
|
||||
@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() {
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package com.baeldung.apiservice.adapters.http;
|
||||
|
||||
public class UnknownTaskException extends RuntimeException {
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package com.baeldung.apiservice.adapters.http;
|
||||
|
||||
public record UserResponse(String id, String name) {
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
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) {
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package com.baeldung.apiservice.adapters.users;
|
||||
|
||||
public record User(String id, String name) {
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
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.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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
users-service.url=http://localhost:8081
|
||||
tasks-service.url=http://localhost:8082
|
17
lightrun/pom.xml
Normal file
17
lightrun/pom.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.baelduung</groupId>
|
||||
<artifactId>lightrun-demo</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>lightrun</name>
|
||||
<description>Services for LightRun Article</description>
|
||||
|
||||
<modules>
|
||||
<module>tasks-service</module>
|
||||
<module>users-service</module>
|
||||
<module>api-service</module>
|
||||
</modules>
|
||||
</project>
|
33
lightrun/tasks-service/.gitignore
vendored
Normal file
33
lightrun/tasks-service/.gitignore
vendored
Normal file
@ -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/
|
67
lightrun/tasks-service/pom.xml
Normal file
67
lightrun/tasks-service/pom.xml
Normal file
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.6.7</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>tasks-service</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>tasks-service</name>
|
||||
<description>Tasks Service for LightRun Article</description>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-artemis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.activemq</groupId>
|
||||
<artifactId>artemis-jms-server</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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) {
|
||||
}
|
@ -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<String> status, Optional<String> assignedTo) {
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/****************************************************************************************************************
|
||||
*
|
||||
* 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) {
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/****************************************************************************************************************
|
||||
*
|
||||
* 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 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.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;
|
||||
|
||||
@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<TaskResponse> searchTasks(@RequestParam("status") Optional<String> status,
|
||||
@RequestParam("createdBy") Optional<String> 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() {
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
{
|
||||
"local-tasks": {
|
||||
"host": "localhost:8082"
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.baeldung.tasksservice.adapters.jms;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jms.annotation.JmsListener;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -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 javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import java.time.Instant;
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
@ -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<TaskRecord, String> {
|
||||
List<TaskRecord> findByStatus(String status);
|
||||
|
||||
List<TaskRecord> findByCreatedBy(String createdBy);
|
||||
|
||||
List<TaskRecord> findByStatusAndCreatedBy(String status, String createdBy);
|
||||
|
||||
List<TaskRecord> findByAssignedTo(String assignedTo);
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.baeldung.tasksservice.service;
|
||||
|
||||
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.stereotype.Service;
|
||||
|
||||
@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");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/****************************************************************************************************************
|
||||
*
|
||||
* 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 javax.transaction.Transactional;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.baeldung.tasksservice.adapters.repository.TaskRecord;
|
||||
import com.baeldung.tasksservice.adapters.repository.TasksRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@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<TaskRecord> search(Optional<String> createdBy, Optional<String> 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<String> newStatus, Optional<String> 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
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
|
||||
|
@ -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');
|
33
lightrun/users-service/.gitignore
vendored
Normal file
33
lightrun/users-service/.gitignore
vendored
Normal file
@ -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/
|
29
lightrun/users-service/README.md
Normal file
29
lightrun/users-service/README.md
Normal file
@ -0,0 +1,29 @@
|
||||
# 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
|
||||
```
|
63
lightrun/users-service/pom.xml
Normal file
63
lightrun/users-service/pom.xml
Normal file
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.6.7</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>users-service</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>users-service</name>
|
||||
<description>Users Service for LightRun Article</description>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-artemis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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) {
|
||||
}
|
@ -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<String> name) {
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
/****************************************************************************************************************
|
||||
*
|
||||
* 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) {
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/****************************************************************************************************************
|
||||
*
|
||||
* 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 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.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;
|
||||
|
||||
@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() {
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
{
|
||||
"local-users": {
|
||||
"host": "localhost:8081"
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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<UserRecord, String> {
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/****************************************************************************************************************
|
||||
*
|
||||
* 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 javax.transaction.Transactional;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.baeldung.usersservice.adapters.jms.JmsSender;
|
||||
import com.baeldung.usersservice.adapters.repository.UserRecord;
|
||||
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
|
||||
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<String> 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;
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
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
|
||||
|
@ -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');
|
4
persistence-modules/core-java-persistence-2/example.csv
Normal file
4
persistence-modules/core-java-persistence-2/example.csv
Normal file
@ -0,0 +1,4 @@
|
||||
Username,Id,First name,Last name
|
||||
doe1,7173,John,Doe
|
||||
smith3,3722,Dana,Smith
|
||||
john22,5490,John,Wang
|
|
@ -1,7 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.baeldung.core-java-persistence-2</groupId>
|
||||
<artifactId>core-java-persistence-2</artifactId>
|
||||
@ -41,6 +39,20 @@
|
||||
<artifactId>mssql-jdbc</artifactId>
|
||||
<version>${mssql.driver.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jooq</groupId>
|
||||
<artifactId>jooq</artifactId>
|
||||
<version>3.11.11</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20220320</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
|
@ -0,0 +1,137 @@
|
||||
package com.baeldung.resultset2json;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.jooq.Record;
|
||||
import org.jooq.RecordMapper;
|
||||
import org.jooq.impl.DSL;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONArray;
|
||||
|
||||
public class ResultSet2JSON {
|
||||
|
||||
public static void main(String... args) throws ClassNotFoundException, SQLException {
|
||||
|
||||
ResultSet2JSON testClass = new ResultSet2JSON();
|
||||
testClass.convertWithoutJOOQ();
|
||||
}
|
||||
|
||||
public void convertWithoutJOOQ() throws ClassNotFoundException, SQLException {
|
||||
Class.forName("org.h2.Driver");
|
||||
Connection dbConnection = DriverManager.getConnection("jdbc:h2:mem:rs2jdbc", "user", "password");
|
||||
|
||||
// Create a table
|
||||
Statement stmt = dbConnection.createStatement();
|
||||
stmt.execute("CREATE TABLE words AS SELECT * FROM CSVREAD('./example.csv')");
|
||||
ResultSet resultSet = stmt.executeQuery("SELECT * FROM words");
|
||||
|
||||
JSONArray result1 = resultSet2JdbcWithoutJOOQ(resultSet);
|
||||
System.out.println(result1);
|
||||
|
||||
resultSet.close();
|
||||
}
|
||||
|
||||
public void convertUsingJOOQDefaultApproach() throws ClassNotFoundException, SQLException {
|
||||
Class.forName("org.h2.Driver");
|
||||
Connection dbConnection = DriverManager.getConnection("jdbc:h2:mem:rs2jdbc", "user", "password");
|
||||
// Create a table
|
||||
Statement stmt = dbConnection.createStatement();
|
||||
stmt.execute("CREATE TABLE words AS SELECT * FROM CSVREAD('./example.csv')");
|
||||
ResultSet resultSet = stmt.executeQuery("SELECT * FROM words");
|
||||
|
||||
JSONObject result1 = resultSet2JdbcUsingJOOQDefaultApproach(resultSet, dbConnection);
|
||||
System.out.println(result1);
|
||||
|
||||
resultSet.close();
|
||||
}
|
||||
|
||||
public void convertUsingCustomisedJOOQ() throws ClassNotFoundException, SQLException {
|
||||
Class.forName("org.h2.Driver");
|
||||
Connection dbConnection = DriverManager.getConnection("jdbc:h2:mem:rs2jdbc", "user", "password");
|
||||
// Create a table
|
||||
Statement stmt = dbConnection.createStatement();
|
||||
stmt.execute("CREATE TABLE words AS SELECT * FROM CSVREAD('./example.csv')");
|
||||
ResultSet resultSet = stmt.executeQuery("SELECT * FROM words");
|
||||
|
||||
JSONArray result1 = resultSet2JdbcUsingCustomisedJOOQ(resultSet, dbConnection);
|
||||
System.out.println(result1);
|
||||
|
||||
resultSet.close();
|
||||
}
|
||||
|
||||
public static JSONArray resultSet2JdbcWithoutJOOQ(ResultSet resultSet) throws SQLException {
|
||||
ResultSetMetaData md = resultSet.getMetaData();
|
||||
int numCols = md.getColumnCount();
|
||||
List<String> colNames = IntStream.range(0, numCols)
|
||||
.mapToObj(i -> {
|
||||
try {
|
||||
return md.getColumnName(i + 1);
|
||||
} catch (SQLException e) {
|
||||
|
||||
e.printStackTrace();
|
||||
return "?";
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
JSONArray result = new JSONArray();
|
||||
while (resultSet.next()) {
|
||||
JSONObject row = new JSONObject();
|
||||
colNames.forEach(cn -> {
|
||||
try {
|
||||
row.put(cn, resultSet.getObject(cn));
|
||||
} catch (JSONException | SQLException e) {
|
||||
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
result.put(row);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static JSONObject resultSet2JdbcUsingJOOQDefaultApproach(ResultSet resultSet, Connection dbConnection) throws SQLException {
|
||||
JSONObject result = new JSONObject(DSL.using(dbConnection)
|
||||
.fetch(resultSet)
|
||||
.formatJSON());
|
||||
return result;
|
||||
}
|
||||
|
||||
public static JSONArray resultSet2JdbcUsingCustomisedJOOQ(ResultSet resultSet, Connection dbConnection) throws SQLException {
|
||||
ResultSetMetaData md = resultSet.getMetaData();
|
||||
int numCols = md.getColumnCount();
|
||||
List<String> colNames = IntStream.range(0, numCols)
|
||||
.mapToObj(i -> {
|
||||
try {
|
||||
return md.getColumnName(i + 1);
|
||||
} catch (SQLException e) {
|
||||
|
||||
e.printStackTrace();
|
||||
return "?";
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<JSONObject> json = DSL.using(dbConnection)
|
||||
.fetch(resultSet)
|
||||
.map(new RecordMapper<Record, JSONObject>() {
|
||||
|
||||
@Override
|
||||
public JSONObject map(Record r) {
|
||||
JSONObject obj = new JSONObject();
|
||||
colNames.forEach(cn -> obj.put(cn, r.get(cn)));
|
||||
return obj;
|
||||
}
|
||||
});
|
||||
return new JSONArray(json);
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package com.baeldung.resultset2json;
|
||||
|
||||
import static com.baeldung.resultset2json.ResultSet2JSON.resultSet2JdbcWithoutJOOQ;
|
||||
import static com.baeldung.resultset2json.ResultSet2JSON.resultSet2JdbcUsingJOOQDefaultApproach;
|
||||
import static com.baeldung.resultset2json.ResultSet2JSON.resultSet2JdbcUsingCustomisedJOOQ;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class ResultSet2JSONUnitTest {
|
||||
JSONObject object = new JSONObject(
|
||||
"{\"records\":[[\"doe1\",\"7173\",\"John\",\"Doe\"],[\"smith3\",\"3722\",\"Dana\",\"Smith\"],[\"john22\",\"5490\",\"John\",\"Wang\"]],\"fields\":[{\"schema\":\"PUBLIC\",\"name\":\"USERNAME\",\"type\":\"VARCHAR\",\"table\":\"WORDS\"},{\"schema\":\"PUBLIC\",\"name\":\"ID\",\"type\":\"VARCHAR\",\"table\":\"WORDS\"},{\"schema\":\"PUBLIC\",\"name\":\"First name\",\"type\":\"VARCHAR\",\"table\":\"WORDS\"},{\"schema\":\"PUBLIC\",\"name\":\"Last name\",\"type\":\"VARCHAR\",\"table\":\"WORDS\"}]}");
|
||||
|
||||
JSONArray array = new JSONArray(
|
||||
"[{\"USERNAME\":\"doe1\",\"First name\":\"John\",\"ID\":\"7173\",\"Last name\":\"Doe\"},{\"USERNAME\":\"smith3\",\"First name\":\"Dana\",\"ID\":\"3722\",\"Last name\":\"Smith\"},{\"USERNAME\":\"john22\",\"First name\":\"John\",\"ID\":\"5490\",\"Last name\":\"Wang\"}]");
|
||||
|
||||
@Test
|
||||
void whenResultSetConvertedWithoutJOOQ_shouldMatchJSON() throws SQLException, ClassNotFoundException {
|
||||
Class.forName("org.h2.Driver");
|
||||
Connection dbConnection = DriverManager.getConnection("jdbc:h2:mem:rs2jdbc1", "user", "password");
|
||||
|
||||
// Create a table
|
||||
Statement stmt = dbConnection.createStatement();
|
||||
stmt.execute("CREATE TABLE words AS SELECT * FROM CSVREAD('./example.csv')");
|
||||
ResultSet resultSet = stmt.executeQuery("SELECT * FROM words");
|
||||
|
||||
JSONArray result1 = resultSet2JdbcWithoutJOOQ(resultSet);
|
||||
|
||||
resultSet.close();
|
||||
|
||||
assertTrue(array.similar(result1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenResultSetConvertedUsingJOOQDefaultApproach_shouldMatchJSON() throws SQLException, ClassNotFoundException {
|
||||
Class.forName("org.h2.Driver");
|
||||
Connection dbConnection = DriverManager.getConnection("jdbc:h2:mem:rs2jdbc2", "user", "password");
|
||||
// Create a table
|
||||
Statement stmt = dbConnection.createStatement();
|
||||
stmt.execute("CREATE TABLE words AS SELECT * FROM CSVREAD('./example.csv')");
|
||||
ResultSet resultSet = stmt.executeQuery("SELECT * FROM words");
|
||||
|
||||
JSONObject result2 = resultSet2JdbcUsingJOOQDefaultApproach(resultSet, dbConnection);
|
||||
|
||||
resultSet.close();
|
||||
|
||||
assertTrue(object.similar(result2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenResultSetConvertedUsingCustomisedJOOQ_shouldMatchJSON() throws SQLException, ClassNotFoundException {
|
||||
Class.forName("org.h2.Driver");
|
||||
Connection dbConnection = DriverManager.getConnection("jdbc:h2:mem:rs2jdbc3", "user", "password");
|
||||
// Create a table
|
||||
Statement stmt = dbConnection.createStatement();
|
||||
stmt.execute("CREATE TABLE words AS SELECT * FROM CSVREAD('./example.csv')");
|
||||
ResultSet resultSet = stmt.executeQuery("SELECT * FROM words");
|
||||
|
||||
JSONArray result3 = resultSet2JdbcUsingCustomisedJOOQ(resultSet, dbConnection);
|
||||
|
||||
resultSet.close();
|
||||
|
||||
assertTrue(array.similar(result3));
|
||||
}
|
||||
|
||||
}
|
2
pom.xml
2
pom.xml
@ -1329,6 +1329,7 @@
|
||||
<module>spring-boot-modules/spring-boot-camel</module>
|
||||
<module>testing-modules/testing-assertions</module>
|
||||
<module>persistence-modules/fauna</module>
|
||||
<module>lightrun</module>
|
||||
</modules>
|
||||
</profile>
|
||||
|
||||
@ -1396,6 +1397,7 @@
|
||||
<module>spring-boot-modules/spring-boot-camel</module>
|
||||
<module>testing-modules/testing-assertions</module>
|
||||
<module>persistence-modules/fauna</module>
|
||||
<module>lightrun</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
@ -1,4 +1,4 @@
|
||||
saml.keystore.location=classpath:/saml/samlKeystore.jks
|
||||
saml.keystore.location=classpath:/saml/saml-keystore
|
||||
# Password for Java keystore and item therein
|
||||
saml.keystore.password=<key_pass>
|
||||
saml.keystore.alias=<key_alias>
|
||||
@ -6,3 +6,4 @@ saml.keystore.alias=<key_alias>
|
||||
# SAML Entity ID extracted from top of SAML metadata file
|
||||
saml.idp=<idp_issuer_url>
|
||||
saml.sp=http://localhost:8080/saml/metadata
|
||||
spring.main.allow-circular-references=true
|
@ -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!";
|
||||
}
|
||||
}
|
@ -1 +1,2 @@
|
||||
server.servlet.context-path=/spring-mvc-basics
|
||||
request.value=welcome
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user