Merge branch 'eugenp:master' into master

This commit is contained in:
Wynn Teo 2024-02-05 08:28:55 +08:00 committed by GitHub
commit fda1afcf35
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
126 changed files with 56895 additions and 182 deletions

View File

@ -10,3 +10,4 @@ This module contains complete guides about arrays in Java
- [Creating a Generic Array in Java](https://www.baeldung.com/java-generic-array)
- [Maximum Size of Java Arrays](https://www.baeldung.com/java-arrays-max-size)
- [Merge Two Arrays and Remove Duplicates in Java](https://www.baeldung.com/java-merge-two-arrays-delete-duplicates)
- [Print a Java 2D Array](https://www.baeldung.com/java-2d-array-print)

View File

@ -0,0 +1,24 @@
package com.baeldung.equilibriumindex;
import java.util.ArrayList;
import java.util.List;
class EquilibriumIndexFinder {
List<Integer> findEquilibriumIndexes(int[] array) {
int[] partialSums = new int[array.length + 1];
partialSums[0] = 0;
for (int i = 0; i < array.length; i++) {
partialSums[i+1] = partialSums[i] + array[i];
}
List<Integer> equilibriumIndexes = new ArrayList<Integer>();
for (int i = 0; i < array.length; i++) {
if (partialSums[i] == (partialSums[array.length] - (partialSums[i+1]))) {
equilibriumIndexes.add(i);
}
}
return equilibriumIndexes;
}
}

View File

@ -0,0 +1,27 @@
package com.baeldung.equilibriumindex;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
class EquilibriumIndexFinderUnitTest {
@Test
void givenArrayHasEquilibriumIndexes_whenFindEquilibriumIndexes_thenListAllEquilibriumIndexes() {
int[] array = {1, -3, 0, 4, -5, 4, 0, 1, -2, -1};
assertThat(new EquilibriumIndexFinder().findEquilibriumIndexes(array)).containsExactly(1, 4, 9);
}
@Test
void givenArrayWithoutEquilibriumIndexes_whenFindEquilibriumIndexes_thenEmptyList() {
int[] array = {1, 2, 3};
assertThat(new EquilibriumIndexFinder().findEquilibriumIndexes(array)).isEmpty();
}
@Test
void givenArrayWithOneElement_whenFindEquilibriumIndexes_thenListFirstIndex() {
int[] array = {5};
assertThat(new EquilibriumIndexFinder().findEquilibriumIndexes(array)).containsExactly(0);
}
}

View File

@ -6,3 +6,4 @@ This module contains articles about Java Character Class
- [Character#isAlphabetic vs. Character#isLetter](https://www.baeldung.com/java-character-isletter-isalphabetic)
- [Difference Between Javas “char” and “String”](https://www.baeldung.com/java-char-vs-string)
- [Increment Character in Java](https://www.baeldung.com/java-char-sequence)
- [Creating Unicode Character From Its Code Point Hex String](https://www.baeldung.com/java-unicode-character-from-code-point-hex-string)

View File

@ -0,0 +1,33 @@
package com.baeldung.countdownlatchvssemaphore;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// Create a CountDownLatch with an initial count equal to the number of tasks to be completed
int numberOfTasks = 3;
CountDownLatch latch = new CountDownLatch(numberOfTasks);
// Simulate completion of tasks by worker threads
for (int i = 1; i <= numberOfTasks; i++) {
new Thread(() -> {
System.out.println("Task completed by Thread " + Thread.currentThread()
.getId());
// Decrement the latch count to signal completion of a task
latch.countDown();
}).start();
}
// Main thread waits until all tasks are completed
latch.await();
System.out.println("All tasks completed. Main thread proceeds.");
// Attempting to reset will have no effect
latch.countDown();
// Latch is already at zero, await() returns immediately
latch.await(); // This line won't block
System.out.println("Latch is already at zero and cannot be reset.");
}
}

View File

@ -0,0 +1,42 @@
package com.baeldung.countdownlatchvssemaphore;
import java.util.concurrent.Semaphore;
public class SemaphoreDemo {
public static void main(String[] args) {
// Create a Semaphore with a fixed number of permits
int NUM_PERMITS = 3;
Semaphore semaphore = new Semaphore(NUM_PERMITS);
// Simulate resource access by worker threads
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
try {
// Acquire a permit to access the resource
semaphore.acquire();
System.out.println("Thread " + Thread.currentThread().getId() + " accessing resource.");
// Simulate resource usage
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// Release the permit after resource access is complete
semaphore.release();
}
}).start();
}
// Simulate resetting the Semaphore by releasing additional permits after a delay
try {
Thread.sleep(5000);
// Resetting the semaphore permits to the initial count
semaphore.release(NUM_PERMITS);
System.out.println("Semaphore permits reset to initial count.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

View File

@ -3,4 +3,4 @@ This module contains articles about date operations in Java.
### Relevant Articles:
- [Calculate Number of Weekdays Between Two Dates in Java](https://www.baeldung.com/java-count-weekdays-between-two-dates)
- [Convert Long to Date in Java](https://www.baeldung.com/java-long-date-conversion)

View File

@ -3,4 +3,4 @@
This module contains articles about converting between Java date and time objects.
### Relevant Articles:
- [Convert Gregorian to Hijri Date in Java](https://www.baeldung.com/java-date-gregorian-hijri-conversion)

View File

@ -14,3 +14,4 @@ This module contains articles about core features in the Java language
- [Static Final Variables in Java](https://www.baeldung.com/java-static-final-variables)
- [What Is the Error: “Non-static method cannot be referenced from a static context”?](https://www.baeldung.com/java-non-static-method-cannot-be-referenced-from-a-static-context)
- [Recursively Sum the Integers in an Array](https://www.baeldung.com/java-recursive-sum-integer-array)
- [Set an Environment Variable at Runtime in Java](https://www.baeldung.com/java-set-environment-variable-runtime)

View File

@ -0,0 +1,55 @@
package com.baeldung.customurlconnection;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
public class CustomURLConnection extends URLConnection {
private final String simulatedData = "This is the simulated data from the resource.";
private URL url;
private boolean connected = false;
private String headerValue = "SimulatedHeaderValue";
protected CustomURLConnection(URL url) {
super(url);
this.url = url;
}
@Override
public void connect() throws IOException {
connected = true;
System.out.println("Connection established to: " + url);
}
@Override
public InputStream getInputStream() throws IOException {
if (!connected) {
connect();
}
return new ByteArrayInputStream(simulatedData.getBytes());
}
@Override
public OutputStream getOutputStream() throws IOException {
ByteArrayOutputStream simulatedOutput = new ByteArrayOutputStream();
return simulatedOutput;
}
@Override
public int getContentLength() {
return simulatedData.length();
}
@Override
public String getHeaderField(String name) {
if ("SimulatedHeader".equalsIgnoreCase(name)) { // Example header name
return headerValue;
} else {
return null; // Header not found
}
}
}

View File

@ -0,0 +1,15 @@
package com.baeldung.customurlconnection;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
public class CustomURLStreamHandler extends URLStreamHandler {
@Override
protected URLConnection openConnection(URL u) throws IOException {
// Create and return an instance of CustomURLConnection
return new CustomURLConnection(u);
}
}

View File

@ -0,0 +1,16 @@
package com.baeldung.customurlconnection;
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
public class CustomURLStreamHandlerFactory implements URLStreamHandlerFactory {
@Override
public URLStreamHandler createURLStreamHandler(String protocol) {
// Check if the requested protocol is our custom protocol
if ("myprotocol".equals(protocol)) {
return new CustomURLStreamHandler();
}
return null; // Let the default handler deal with other protocols
}
}

View File

@ -0,0 +1,34 @@
package com.baeldung.customurlconnection;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
URL.setURLStreamHandlerFactory(new CustomURLStreamHandlerFactory());
try {
URL url = new URL("myprotocol://example.com/resource");
CustomURLConnection customConnection = (CustomURLConnection) url.openConnection();
customConnection.connect();
InputStream inputStream = customConnection.getInputStream();
String content = new Scanner(inputStream).useDelimiter("\\A").next();
System.out.println(content);
int contentLength = customConnection.getContentLength();
System.out.println("Content Length: " + contentLength);
String headerValue = customConnection.getHeaderField("SimulatedHeader");
System.out.println("Header Value: " + headerValue);
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,43 @@
package com.baeldung.urlencoder;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.Test;
public class SpaceURLEncoderUnitTest {
@Test
void givenSpaceInString_whenUsingDefaultEncoding_thenReturnPlusSign() {
String originalString = "Welcome to the Baeldung Website!";
String encodedString = URLEncoder.encode(originalString);
assertEquals("Welcome+to+the+Baeldung+Website%21", encodedString);
}
@Test
void givenSpaceInString_whenUsingUTF8Encoding_thenReturnPlusSign() throws UnsupportedEncodingException {
String originalString = "Welcome to the Baeldung Website!";
String encodedString = URLEncoder.encode(originalString, StandardCharsets.UTF_8.toString());
assertEquals("Welcome+to+the+Baeldung+Website%21", encodedString);
}
@Test
void givenSpaceInString_whenUsingDefaultEncodingAndReplace_thenReturnPct20() throws UnsupportedEncodingException {
String originalString = "Welcome to the Baeldung Website!";
String encodedString = URLEncoder.encode(originalString)
.replace("+", "%20");
assertEquals("Welcome%20to%20the%20Baeldung%20Website%21", encodedString);
}
@Test
void givenSpaceInString_whenUsingDefaultEncodingAndReplaceAll_thenReturnPct20() throws UnsupportedEncodingException {
String originalString = "Welcome to the Baeldung Website!";
String encodedString = URLEncoder.encode(originalString)
.replaceAll("\\+", "%20");
assertEquals("Welcome%20to%20the%20Baeldung%20Website%21", encodedString);
}
}

View File

@ -22,7 +22,7 @@ public class URLNormalizationUnitTest {
String normalizedUri = originalUrl.split("\\?")[0];
assertEquals(expectedNormalizedUrl, normalizedUri);
} else {
throw new IllegalArgumentException("Invalid URL: " + originalUrl);
fail(originalUrl);
}
}
@ -35,7 +35,7 @@ public class URLNormalizationUnitTest {
}
@Test
public void givenOriginalUrl_whenUsingRegularExpression_thenNormalizedUrl() throws URISyntaxException, UnsupportedEncodingException {
public void givenOriginalUrl_whenUsingRegularExpression_thenNormalizedUrl() {
String regex = "^(https?://[^/]+/[^?#]+)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(originalUrl);
@ -44,7 +44,7 @@ public class URLNormalizationUnitTest {
String normalizedUrl = matcher.group(1);
assertEquals(expectedNormalizedUrl, normalizedUrl);
} else {
throw new IllegalArgumentException("Invalid URL: " + originalUrl);
fail(originalUrl);
}
}
}

View File

@ -0,0 +1,20 @@
package com.baeldung.reflection.innerclass;
public class Person {
String name;
Address address;
public Person() {
}
public class Address {
String zip;
public Address(String zip) {
this.zip = zip;
}
}
public static class Builder {
}
}

View File

@ -0,0 +1,67 @@
package com.baeldung.reflection.innerclass;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CreateInnerClassWithReflectionUnitTest {
static Logger logger = LoggerFactory.getLogger(CreateInnerClassWithReflectionUnitTest.class);
@Test
void givenInnerClass_whenUseReflection_thenShowConstructors() {
final String personBuilderClassName = "com.baeldung.reflection.innerclass.Person$Builder";
final String personAddressClassName = "com.baeldung.reflection.innerclass.Person$Address";
assertDoesNotThrow(() -> logConstructors(Class.forName(personAddressClassName)));
assertDoesNotThrow(() -> logConstructors(Class.forName(personBuilderClassName)));
}
private static void logConstructors(Class<?> clazz) {
Arrays.stream(clazz.getDeclaredConstructors())
.map(c -> formatConstructorSignature(c))
.forEach(logger::info);
}
private static String formatConstructorSignature(Constructor<?> constructor) {
String params = Arrays.stream(constructor.getParameters())
.map(parameter -> parameter.getType().getSimpleName() + " " + parameter.getName())
.collect(Collectors.joining(", "));
return constructor.getName() + "(" + params + ")";
}
@Test
void givenStaticInnerClass_whenUseReflection_thenInstantiate()
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
InstantiationException, IllegalAccessException {
final String personBuilderClassName = "com.baeldung.reflection.innerclass.Person$Builder";
Class<Person.Builder> personBuilderClass = (Class<Person.Builder>) Class.forName(personBuilderClassName);
Person.Builder personBuilderObj = personBuilderClass.getDeclaredConstructor().newInstance();
assertTrue(personBuilderObj instanceof Person.Builder);
}
@Test
void givenNonStaticInnerClass_whenUseReflection_thenInstantiate()
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
InstantiationException, IllegalAccessException {
final String personClassName = "com.baeldung.reflection.innerclass.Person";
final String personAddressClassName = "com.baeldung.reflection.innerclass.Person$Address";
Class<Person> personClass = (Class<Person>) Class.forName(personClassName);
Person personObj = personClass.getConstructor().newInstance();
Class<Person.Address> personAddressClass = (Class<Person.Address>) Class.forName(personAddressClassName);
assertThrows(NoSuchMethodException.class, () -> personAddressClass.getDeclaredConstructor(String.class));
Constructor<Person.Address> constructorOfPersonAddress = personAddressClass.getDeclaredConstructor(Person.class, String.class);
Person.Address personAddressObj = constructorOfPersonAddress.newInstance(personObj, "751003");
assertTrue(personAddressObj instanceof Person.Address);
}
}

View File

@ -0,0 +1,38 @@
package com.baeldung.stream.range;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class StreamRangeUnitTest {
@Test
public void whenRangeStreamUsingLimitSkip_thenPrintsRange() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> expectedRange = Arrays.asList(3, 4, 5, 6, 7);
List<Integer> range = numbers.stream()
.skip(2)
.limit(5)
.collect(Collectors.toList());
assertEquals(expectedRange, range);
}
@Test
public void whenRangeStreamUsingCollectingAndThen_thenPrintsRange() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> expectedRange = Arrays.asList(3, 4, 5, 6, 7);
List<Integer> range = numbers.stream()
.filter(n -> n >= 3 && n <= 7)
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
assertEquals(expectedRange, range);
}
}

View File

@ -0,0 +1,93 @@
package com.baeldung.string.runlength;
import org.junit.jupiter.api.Test;
import java.util.Collections;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class RunLengthEncodingUnitTest {
private static final String INPUT = "WWWWWWWWWWWWBAAACCDEEEEE";
private static final String RLE = "12W1B3A2C1D5E";
String runLengthEncode(String input) {
StringBuilder result = new StringBuilder();
int count = 1;
char[] chars = input.toCharArray();
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
if (i + 1 < chars.length && c == chars[i + 1]) {
count++;
} else {
result.append(count).append(c);
count = 1;
}
}
return result.toString();
}
String runLengthDecode(String rle) {
StringBuilder result = new StringBuilder();
char[] chars = rle.toCharArray();
int count = 0;
for (char c : chars) {
if (Character.isDigit(c)) {
count = 10 * count + Character.getNumericValue(c);
} else {
result.append(String.join("", Collections.nCopies(count, String.valueOf(c))));
count = 0;
}
}
return result.toString();
}
String runLengthEncodeByRegEx(String input) {
String[] arr = input.split("(?<=(\\D))(?!\\1)");
StringBuilder result = new StringBuilder();
for (String run : arr) {
result.append(run.length()).append(run.charAt(0));
}
return result.toString();
}
String runLengthDecodeByRegEx(String rle) {
if (rle.isEmpty()) {
return "";
}
String[] arr = rle.split("(?<=\\D)|(?=\\D+)");
if (arr.length % 2 != 0) {
throw new IllegalArgumentException("Not a RLE string");
}
StringBuilder result = new StringBuilder();
for (int i = 1; i <= arr.length; i += 2) {
int count = Integer.parseInt(arr[i - 1]);
String c = arr[i];
result.append(String.join("", Collections.nCopies(count, c)));
}
return result.toString();
}
@Test
void whenInvokingRunLengthEncode_thenGetExpectedResult() {
assertEquals(RLE, runLengthEncode(INPUT));
}
@Test
void whenInvokingRunLengthDecode_thenGetExpectedResult() {
assertEquals(INPUT, runLengthDecode(RLE));
}
@Test
void whenInvokingRunLengthEncodeByRegEx_thenGetExpectedResult() {
assertEquals(RLE, runLengthEncodeByRegEx(INPUT));
}
@Test
void whenInvokingRunLengthDecodeByRegEx_thenGetExpectedResult() {
assertEquals(INPUT, runLengthDecodeByRegEx(RLE));
}
}

View File

@ -14,3 +14,4 @@
- [UTF-8 Validation in Java](https://www.baeldung.com/java-utf-8-validation)
- [Simple Morse Code Translation in Java](https://www.baeldung.com/java-morse-code-english-translate)
- [How to Determine if a String Contains Invalid Encoded Characters](https://www.baeldung.com/java-check-string-contains-invalid-encoded-characters)
- [Regular Expression for Password Validation in Java](https://www.baeldung.com/java-regex-password-validation)

View File

@ -14,7 +14,7 @@ public class DecodeUUIDStringFromBase64UnitTest {
private final UUID originalUUID = UUID.fromString("cc5f93f7-8cf1-4a51-83c6-e740313a0c6c");
@Test
public void shouldDecodeUUIDUsingByteArrayAndBase64Decoder() {
public void givenEncodedString_whenDecodingUsingBase64Decoder_thenGiveExpectedUUID() {
String expectedEncodedString = "UUrxjPeTX8xsDDoxQOfGgw";
byte[] decodedBytes = Base64.getDecoder()
.decode(expectedEncodedString);
@ -23,7 +23,7 @@ public class DecodeUUIDStringFromBase64UnitTest {
}
@Test
public void shouldDecodeUUIDUsingByteBufferAndBase64UrlDecoder() {
public void givenEncodedString_whenDecodingUsingByteBufferAndBase64UrlDecoder_thenGiveExpectedUUID() {
String expectedEncodedString = "zF-T94zxSlGDxudAMToMbA";
byte[] decodedBytes = Base64.getUrlDecoder()
.decode(expectedEncodedString);
@ -35,7 +35,7 @@ public class DecodeUUIDStringFromBase64UnitTest {
}
@Test
public void shouldDecodeUUIDUsingApacheUtils() {
public void givenEncodedString_whenDecodingUsingApacheUtils_thenGiveExpectedUUID() {
String expectedEncodedString = "UUrxjPeTX8xsDDoxQOfGgw";
byte[] decodedBytes = decodeBase64(expectedEncodedString);
UUID uuid = Conversion.byteArrayToUuid(decodedBytes, 0);

View File

@ -14,7 +14,7 @@ public class EncodeUUIDToBase64StringUnitTest {
private final UUID originalUUID = UUID.fromString("cc5f93f7-8cf1-4a51-83c6-e740313a0c6c");
@Test
public void shouldEncodeUUIDUsingByteArrayAndBase64Encoder() {
public void givenUUID_whenEncodingUsingBase64Encoder_thenGiveExpectedEncodedString() {
String expectedEncodedString = "UUrxjPeTX8xsDDoxQOfGgw";
byte[] uuidBytes = convertToByteArray(originalUUID);
String encodedUUID = Base64.getEncoder().withoutPadding()
@ -23,7 +23,7 @@ public class EncodeUUIDToBase64StringUnitTest {
}
@Test
public void shouldEncodeUUIDUsingByteBufferAndBase64UrlEncoder() {
public void givenUUID_whenEncodingUsingByteBufferAndBase64UrlEncoder_thenGiveExpectedEncodedString() {
String expectedEncodedString = "zF-T94zxSlGDxudAMToMbA";
ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[16]);
byteBuffer.putLong(originalUUID.getMostSignificantBits());
@ -34,7 +34,7 @@ public class EncodeUUIDToBase64StringUnitTest {
}
@Test
public void shouldEncodeUUIDUsingApacheUtils() {
public void givenUUID_whenEncodingUsingApacheUtils_thenGiveExpectedEncodedString() {
String expectedEncodedString = "UUrxjPeTX8xsDDoxQOfGgw";
byte[] bytes = Conversion.uuidToByteArray(originalUUID, new byte[16], 0, 16);
String encodedUUID = encodeBase64URLSafeString(bytes);

View File

@ -0,0 +1,2 @@
### Relevant Articles:
- [Structured Logging in Java](https://www.baeldung.com/java-structured-logging)

View File

@ -8,6 +8,36 @@
<version>0.0.1-SNAPSHOT</version>
<name>maven-exec-plugin</name>
<dependencies>
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-invoker</artifactId>
<version>${apache.maven.invoker.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-embedder</artifactId>
<version>${apache.maven.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
@ -38,6 +68,9 @@
<properties>
<maven-compiler-plugin.version>3.12.1</maven-compiler-plugin.version>
<exec-maven-plugin.version>3.1.0</exec-maven-plugin.version>
<junit.jupiter.version>5.10.0</junit.jupiter.version>
<apache.maven.version>3.9.6</apache.maven.version>
<apache.maven.invoker.version>3.2.0</apache.maven.invoker.version>
</properties>
</project>

View File

@ -0,0 +1,17 @@
package com.baeldung.learningplatform;
public enum JavaVersion {
JAVA_8("1.8"), JAVA_9("9"), JAVA_17("17");
private final String version;
JavaVersion(String version) {
this.version = version;
}
public String getVersion() {
return this.version;
}
}

View File

@ -0,0 +1,13 @@
package com.baeldung.learningplatform;
import java.nio.file.Path;
public interface Maven {
String POM_XML = "pom.xml";
String COMPILE_GOAL = "compile";
String USE_CUSTOM_POM = "-f";
int OK = 0;
String MVN = "mvn";
void compile(Path projectFolder);
}

View File

@ -0,0 +1,12 @@
package com.baeldung.learningplatform;
public class MavenCompilationException extends RuntimeException {
public MavenCompilationException(String message, Throwable cause) {
super(message, cause);
}
public MavenCompilationException(String message) {
super(message);
}
}

View File

@ -0,0 +1,16 @@
package com.baeldung.learningplatform;
import java.nio.file.Path;
import org.apache.maven.cli.MavenCli;
public class MavenEmbedder implements Maven {
public static final String MVN_HOME = "maven.multiModuleProjectDirectory";
@Override
public void compile(Path projectFolder) {
MavenCli cli = new MavenCli();
System.setProperty(MVN_HOME, projectFolder.toString());
cli.doMain(new String[]{COMPILE_GOAL}, projectFolder.toString(), null, null);
}
}

View File

@ -0,0 +1,26 @@
package com.baeldung.learningplatform;
import java.io.IOException;
import java.nio.file.Path;
public abstract class MavenExecutorAdapter implements Maven {
@Override
public void compile(Path projectFolder) {
int exitCode;
try {
exitCode = execute(projectFolder, COMPILE_GOAL);
} catch (InterruptedException e) {
throw new MavenCompilationException("Interrupted during compilation", e);
} catch (IOException e) {
throw new MavenCompilationException("Incorrect execution", e);
}
if (exitCode != OK) {
throw new MavenCompilationException("Failure during compilation: " + exitCode);
}
}
protected abstract int execute(Path projectFolder, String compileGoal)
throws InterruptedException, IOException;
}

View File

@ -0,0 +1,30 @@
package com.baeldung.learningplatform;
import java.nio.file.Path;
import java.util.Collections;
import org.apache.maven.shared.invoker.DefaultInvocationRequest;
import org.apache.maven.shared.invoker.DefaultInvoker;
import org.apache.maven.shared.invoker.InvocationRequest;
import org.apache.maven.shared.invoker.InvocationResult;
import org.apache.maven.shared.invoker.Invoker;
import org.apache.maven.shared.invoker.MavenInvocationException;
public class MavenInvoker implements Maven {
@Override
public void compile(Path projectFolder) {
InvocationRequest request = new DefaultInvocationRequest();
request.setPomFile(projectFolder.resolve(POM_XML).toFile());
request.setGoals(Collections.singletonList(Maven.COMPILE_GOAL));
Invoker invoker = new DefaultInvoker();
try {
InvocationResult result = invoker.execute(request);
if (result.getExitCode() != 0) {
throw new MavenCompilationException("Build failed", result.getExecutionException());
}
} catch (MavenInvocationException e) {
throw new MavenCompilationException("Exception during Maven invocation", e);
}
}
}

View File

@ -0,0 +1,15 @@
package com.baeldung.learningplatform;
import java.io.IOException;
import java.nio.file.Path;
public class MavenProcessBuilder extends MavenExecutorAdapter {
private static final ProcessBuilder PROCESS_BUILDER = new ProcessBuilder();
protected int execute(Path projectFolder, String compileGoal) throws IOException, InterruptedException {
Process process = PROCESS_BUILDER
.command(MVN, USE_CUSTOM_POM, projectFolder.resolve(POM_XML).toString(), compileGoal)
.start();
return process.waitFor();
}
}

View File

@ -0,0 +1,13 @@
package com.baeldung.learningplatform;
import java.io.IOException;
import java.nio.file.Path;
public class MavenRuntimeExec extends MavenExecutorAdapter {
@Override
protected int execute(Path projectFolder, String compileGoal) throws InterruptedException, IOException {
String[] arguments = {MVN, USE_CUSTOM_POM, projectFolder.resolve(POM_XML).toString(), COMPILE_GOAL};
Process process = Runtime.getRuntime().exec(arguments);
return process.waitFor();
}
}

View File

@ -0,0 +1,100 @@
package com.baeldung.learningplatform;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.codehaus.plexus.util.xml.Xpp3Dom;
public class ProjectBuilder {
private static final String PACKAGE = "package ";
private static final String POM_XML = "pom.xml";
private static final String SRC_TEST = "src/test/";
private static final String SRC_MAIN_JAVA = "src/main/java/";
private static final String PACKAGE_DELIMITER = ".";
private static final String MAIN_JAVA = "Main.java";
private final List<Dependency> dependencies = new ArrayList<>();
private JavaVersion javaVersion = JavaVersion.JAVA_8;
public ProjectBuilder addDependency(String groupId, String artifactId, String version) {
Dependency dependency = new Dependency();
dependency.setGroupId(groupId);
dependency.setArtifactId(artifactId);
dependency.setVersion(version);
dependencies.add(dependency);
return this;
}
public ProjectBuilder setJavaVersion(JavaVersion version) {
this.javaVersion = version;
return this;
}
public void build(String userName, Path projectPath, String packageName) throws IOException {
Model model = new Model();
configureModel(userName, model);
dependencies.forEach(model::addDependency);
Build build = configureJavaVersion();
model.setBuild(build);
MavenXpp3Writer writer = new MavenXpp3Writer();
writer.write(new FileWriter(projectPath.resolve(POM_XML).toFile()), model);
generateFolders(projectPath, SRC_TEST);
Path generatedPackage = generateFolders(projectPath,
SRC_MAIN_JAVA +
packageName.replace(PACKAGE_DELIMITER, FileSystems.getDefault().getSeparator()));
String generatedClass = generateMainClass(PACKAGE + packageName);
Files.writeString(generatedPackage.resolve(MAIN_JAVA), generatedClass);
}
private static void configureModel(String userName, Model model) {
model.setModelVersion("4.0.0");
model.setArtifactId("com." + userName.toLowerCase());
model.setGroupId("learning-project");
model.setVersion("0.0.1-SNAPSHOT");
}
private static String generateMainClass(String packageName) {
return packageName + ";\n" +
"\n" +
"public class Main {\n" +
" public static void main(String[] args){\n" +
" System.out.println(\"Hello World!\");\n" +
" }\n" +
"}\n";
}
private static Path generateFolders(Path sourceFolder, String packageName) throws IOException {
return Files.createDirectories(sourceFolder.resolve(packageName));
}
private Build configureJavaVersion() {
Plugin plugin = new Plugin();
plugin.setGroupId("org.apache.maven.plugins");
plugin.setArtifactId("maven-compiler-plugin");
plugin.setVersion("3.8.1");
Xpp3Dom configuration = new Xpp3Dom("configuration");
Xpp3Dom source = new Xpp3Dom("source");
source.setValue(javaVersion.getVersion());
Xpp3Dom target = new Xpp3Dom("target");
target.setValue(javaVersion.getVersion());
configuration.addChild(source);
configuration.addChild(target);
plugin.setConfiguration(configuration);
Build build = new Build();
build.addPlugin(plugin);
return build;
}
}

View File

@ -0,0 +1,32 @@
package com.baeldung.learningplatform;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
public class FileUtil {
private FileUtil() {
}
public static void removeDirectoryRecursively(Path folder) throws IOException {
if (Files.exists(folder)) {
Files.walkFileTree(folder, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
}
}
}

View File

@ -0,0 +1,41 @@
package com.baeldung.learningplatform;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
class MavenRuntimeExecUnitTest {
private static final String PACKAGE_NAME = "com.baeldung.generatedcode";
private static final String USER_NAME = "john_doe";
@TempDir
private Path tempDir;
@BeforeEach
public void setUp() throws IOException {
ProjectBuilder projectBuilder = new ProjectBuilder();
projectBuilder.build(USER_NAME, tempDir, PACKAGE_NAME);
}
@ParameterizedTest
@MethodSource
void givenMavenInterface_whenCompileMavenProject_thenCreateTargetDirectory(Maven maven) {
maven.compile(tempDir);
assertTrue(Files.exists(tempDir));
}
static Stream<Maven> givenMavenInterface_whenCompileMavenProject_thenCreateTargetDirectory() {
return Stream.of(
new MavenRuntimeExec(),
new MavenProcessBuilder(),
new MavenEmbedder(),
new MavenInvoker());
}
}

View File

@ -0,0 +1,11 @@
package com.baeldung.learningplatform;
import java.nio.file.Path;
import java.nio.file.Paths;
public interface ProjectsPaths {
public static final Path PROJECT_DIR = Paths.get("src/test/resources/learning-project/");
public static final Path PROJECT_POM_XML = PROJECT_DIR.resolve("pom.xml");
public static final Path PROJECT_TARGET = PROJECT_DIR.resolve("target");
}

View File

@ -12,7 +12,7 @@ class ConnectionPerChannelPublisherLiveTest {
// host, workerCount, iterations, payloadSize
Stream.of(1,5,10,20,50,100,150)
.forEach(workers -> {
ConnectionPerChannelPublisher.main(new String[]{"192.168.99.100", Integer.toString(workers), "1000", "4096"});
ConnectionPerChannelPublisher.main(new String[]{"localhost", Integer.toString(workers), "1000", "4096"});
});
}

View File

@ -10,9 +10,9 @@ class SingleConnectionPublisherLiveTest {
@Test
void whenSingleChannel_thenRunBenchmark() throws Exception {
// host, workerCount, iterations, payloadSize
Stream.of(1,5,10,20,50,100,150)
Stream.of(1,5,10,20,50)
.forEach(workers -> {
SingleConnectionPublisher.main(new String[]{"192.168.99.100", Integer.toString(workers), "1000", "4096"});
SingleConnectionPublisher.main(new String[]{"localhost", Integer.toString(workers), "1000", "4096"});
});
}

View File

@ -56,11 +56,17 @@ public class DynamicQueueCreationLiveTest {
@Test
void givenQueueName_whenQueueDoesNotExist_thenCheckingIfQueueExists() throws IOException, TimeoutException {
try (Channel channel = connection.createChannel()) {
Channel temp = null;
try {
Channel channel = connection.createChannel();
assertThrows(IOException.class, () -> {
channel.queueDeclarePassive(QUEUE_NAME_NEW);
});
temp = channel;
} finally {
if(temp != null && temp.isOpen()) {
temp.close();
}
}
}

View File

@ -11,6 +11,7 @@
<groupId>io.micronaut.platform</groupId>
<artifactId>micronaut-parent</artifactId>
<version>4.1.2</version>
<relativePath/>
</parent>
<properties>
<packaging>jar</packaging>

View File

@ -1 +1,2 @@
### Relevant Articles:
- [Monkey Patching in Java](https://www.baeldung.com/java-monkey-patching)

View File

@ -6,6 +6,18 @@
<artifactId>core-java-persistence-3</artifactId>
<version>0.1.0-SNAPSHOT</version>
<name>core-java-persistence-3</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
<packaging>jar</packaging>
<parent>

View File

@ -0,0 +1,33 @@
package jdbcpagination;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class PaginationLogic {
public static ResultSet readPageWithLimitAndOffset(Connection connection, int offset, int pageSize) throws SQLException {
String sql = """
SELECT * FROM employees
LIMIT ? OFFSET ?
""";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, pageSize);
preparedStatement.setInt(2, offset);
return preparedStatement.executeQuery();
}
public static ResultSet readPageWithSortedKeys(Connection connection, int lastFetchedId, int pageSize) throws SQLException {
String sql = """
SELECT * FROM employees
WHERE id > ? LIMIT ?
""";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, lastFetchedId);
preparedStatement.setInt(2, pageSize);
return preparedStatement.executeQuery();
}
}

View File

@ -0,0 +1,134 @@
package jdbcpagination;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class PaginationLogicUnitTest {
private static Connection connection = null;
private static final String JDBC_URL = "jdbc:h2:mem:testDB";
private static final String USERNAME = "dbUser";
private static final String PASSWORD = "dbPassword";
@BeforeAll
public static void setup() throws Exception {
connection = connect(JDBC_URL, USERNAME, PASSWORD);
populateDB();
}
@AfterAll
public static void tearDown() throws SQLException {
destroyDB();
}
@Test
public void givenDBPopulated_WhenReadPaginatedWithLimitAndOffset_ThenReturnsPaginatedResult() throws SQLException {
int offset = 0; // offset is set to 0 and keep updating with pageSize
int pageSize = 100_000;
int totalPages = 0;
while (true) {
ResultSet resultSet = PaginationLogic.readPageWithLimitAndOffset(connection, offset, pageSize);
if (!resultSet.next())
break;
List<String> resultPage = new ArrayList<>();
do {
resultPage.add(resultSet.getString("first_name"));
} while (resultSet.next());
assertEquals("firstname" + (resultPage.size() * (totalPages + 1)), resultPage.get(resultPage.size() - 1));
offset += pageSize;
totalPages++;
}
assertEquals(10, totalPages);
}
@Test
public void givenDBPopulated_WhenReadPaginatedWithSortedKeys_ThenReturnsPaginatedResult() throws SQLException {
// find min and max ID
PreparedStatement preparedStatement = connection.prepareStatement("SELECT min(id) as min_id, max(id) as max_id FROM employees");
ResultSet resultSet = preparedStatement.executeQuery();
resultSet.next();
int minId = resultSet.getInt("min_id");
int maxId = resultSet.getInt("max_id");
int lastFetchedId = 0; // assign lastFetchedId to minId
int pageSize = 100_000;
int totalPages = 0;
while ((lastFetchedId + pageSize) <= maxId) {
resultSet = PaginationLogic.readPageWithSortedKeys(connection, lastFetchedId, pageSize);
if (!resultSet.next())
break;
List<String> resultPage = new ArrayList<>();
do {
resultPage.add(resultSet.getString("first_name"));
lastFetchedId = resultSet.getInt("id");
} while (resultSet.next());
assertEquals("firstname" + (resultPage.size() * (totalPages + 1)), resultPage.get(resultPage.size() - 1));
totalPages++;
}
assertEquals(10, totalPages);
}
private static void destroyDB() throws SQLException {
String destroy = """
DROP table IF EXISTS EMPLOYEES;
""";
connection.prepareStatement(destroy)
.execute();
}
private static void populateDB() throws SQLException {
String createTable = """
CREATE TABLE EMPLOYEES (
id SERIAL PRIMARY KEY,
first_name VARCHAR(50),
last_name VARCHAR(50),
salary DECIMAL(10, 2)
);
""";
PreparedStatement preparedStatement = connection.prepareStatement(createTable);
preparedStatement.execute();
String load = """
INSERT INTO EMPLOYEES (first_name, last_name, salary)
VALUES(?,?,?)
""";
IntStream.rangeClosed(1, 1_000_000)
.forEach(i -> {
PreparedStatement preparedStatement1 = null;
try {
preparedStatement1 = connection.prepareStatement(load);
preparedStatement1.setString(1, "firstname" + i);
preparedStatement1.setString(2, "lastname" + i);
preparedStatement1.setDouble(3, 100_000 + (1_000_000 - 100_000) + Math.random());
preparedStatement1.execute();
} catch (SQLException e) {
throw new RuntimeException(e);
}
});
}
public static Connection connect(String url, String user, String password) throws SQLException {
Connection connection = DriverManager.getConnection(url, user, password);
if (connection != null) {
System.out.println("Connected to database");
}
return connection;
}
}

View File

@ -102,7 +102,7 @@
<module>spring-data-solr</module>
<module>spring-data-shardingsphere</module>
<module>spring-hibernate-3</module>
<!-- <module>spring-hibernate-5</module> FAILED --> <!-- long running -->
<module>spring-hibernate-5</module>
<module>spring-hibernate-6</module>
<module>spring-jpa</module>
<module>spring-jpa-2</module>

View File

@ -1,3 +1,4 @@
## Relevant Articles
- [Scroll API in Spring Data JPA](https://www.baeldung.com/spring-data-jpa-scroll-api)
- [List vs. Set in @OneToMany JPA](https://www.baeldung.com/spring-jpa-onetomany-list-vs-set)
- [N+1 Problem in Hibernate and Spring Data JPA](https://www.baeldung.com/spring-hibernate-n1-problem)

View File

@ -6,4 +6,5 @@
- [TRUNCATE TABLE in Spring Data JPA](https://www.baeldung.com/spring-data-jpa-truncate-table)
- [When to Use the getReferenceById() and findById() Methods in Spring Data JPA](https://www.baeldung.com/spring-data-jpa-getreferencebyid-findbyid-methods)
- [Implementing Persistable-Only Entities in Spring Data JPA](https://www.baeldung.com/spring-data-persistable-only-entities)
- [Storing PostgreSQL JSONB Using Spring Boot and JPA](https://www.baeldung.com/spring-boot-jpa-storing-postgresql-jsonb)
- More articles: [[<-- prev]](../spring-data-jpa-repo-3)

View File

@ -125,6 +125,20 @@
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
--add-opens java.base/java.lang=ALL-UNNAMED
</argLine>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<!-- Spring -->
<org.springframework.version>5.0.2.RELEASE</org.springframework.version>
@ -132,7 +146,7 @@
<org.springframework.security.version>4.2.1.RELEASE</org.springframework.security.version>
<!-- persistence -->
<hibernate.version>5.6.15.Final</hibernate.version>
<hibernatesearch.version>5.8.2.Final</hibernatesearch.version>
<hibernatesearch.version>5.11.12.Final</hibernatesearch.version>
<mysql-connector-java.version>8.2.0</mysql-connector-java.version>
<tomcat-dbcp.version>9.0.0.M26</tomcat-dbcp.version>
<jta.version>1.1</jta.version>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
monitoring="autodetect" dynamicConfig="true">
<diskStore path="java.io.tmpdir/ehcache" />
<defaultCache maxEntriesLocalHeap="10000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120" diskSpoolBufferSizeMB="30"
maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" statistics="true">
<persistence strategy="localTempSwap" />
</defaultCache>
<cache name="org.hibernate.cache.internal.StandardQueryCache"
maxEntriesLocalHeap="5" eternal="false" timeToLiveSeconds="120">
<persistence strategy="localTempSwap" />
</cache>
</ehcache>

View File

@ -7,13 +7,13 @@ import javax.persistence.*;
@Entity
@Table(name = "FOO")
@Where(clause = "DELETED = 0")
public class Foo {
public class Fooo {
public Foo() {
public Fooo() {
super();
}
public Foo(final String name) {
public Fooo(final String name) {
super();
this.name = name;
}

View File

@ -14,7 +14,7 @@ import org.springframework.transaction.annotation.Transactional;
import com.baeldung.persistence.deletion.config.PersistenceJPAConfigDeletion;
import com.baeldung.persistence.deletion.model.Bar;
import com.baeldung.persistence.deletion.model.Baz;
import com.baeldung.persistence.deletion.model.Foo;
import com.baeldung.persistence.deletion.model.Fooo;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@ -41,29 +41,29 @@ public class DeletionIntegrationTest {
@Test
@Transactional
public final void givenEntityIsRemoved_thenItIsNotInDB() {
Foo foo = new Foo("foo");
entityManager.persist(foo);
Fooo fooo = new Fooo("foo");
entityManager.persist(fooo);
flushAndClear();
foo = entityManager.find(Foo.class, foo.getId());
assertThat(foo, notNullValue());
fooo = entityManager.find(Fooo.class, fooo.getId());
assertThat(fooo, notNullValue());
entityManager.remove(foo);
entityManager.remove(fooo);
flushAndClear();
assertThat(entityManager.find(Foo.class, foo.getId()), nullValue());
assertThat(entityManager.find(Fooo.class, fooo.getId()), nullValue());
}
@Test
@Transactional
public final void givenEntityIsRemovedAndReferencedByAnotherEntity_thenItIsNotRemoved() {
Bar bar = new Bar("bar");
Foo foo = new Foo("foo");
foo.setBar(bar);
entityManager.persist(foo);
Fooo fooo = new Fooo("foo");
fooo.setBar(bar);
entityManager.persist(fooo);
flushAndClear();
foo = entityManager.find(Foo.class, foo.getId());
fooo = entityManager.find(Fooo.class, fooo.getId());
bar = entityManager.find(Bar.class, bar.getId());
entityManager.remove(bar);
flushAndClear();
@ -71,8 +71,8 @@ public class DeletionIntegrationTest {
bar = entityManager.find(Bar.class, bar.getId());
assertThat(bar, notNullValue());
foo = entityManager.find(Foo.class, foo.getId());
foo.setBar(null);
fooo = entityManager.find(Fooo.class, fooo.getId());
fooo.setBar(null);
entityManager.remove(bar);
flushAndClear();
@ -83,16 +83,16 @@ public class DeletionIntegrationTest {
@Transactional
public final void givenEntityIsRemoved_thenRemovalIsCascaded() {
Bar bar = new Bar("bar");
Foo foo = new Foo("foo");
foo.setBar(bar);
entityManager.persist(foo);
Fooo fooo = new Fooo("foo");
fooo.setBar(bar);
entityManager.persist(fooo);
flushAndClear();
foo = entityManager.find(Foo.class, foo.getId());
entityManager.remove(foo);
fooo = entityManager.find(Fooo.class, fooo.getId());
entityManager.remove(fooo);
flushAndClear();
assertThat(entityManager.find(Foo.class, foo.getId()), nullValue());
assertThat(entityManager.find(Fooo.class, fooo.getId()), nullValue());
assertThat(entityManager.find(Bar.class, bar.getId()), nullValue());
}
@ -116,39 +116,39 @@ public class DeletionIntegrationTest {
@Test
@Transactional
public final void givenEntityIsDeletedWithJpaBulkDeleteStatement_thenItIsNotInDB() {
Foo foo = new Foo("foo");
entityManager.persist(foo);
Fooo fooo = new Fooo("foo");
entityManager.persist(fooo);
flushAndClear();
entityManager.createQuery("delete from Foo where id = :id").setParameter("id", foo.getId()).executeUpdate();
entityManager.createQuery("delete from Foo where id = :id").setParameter("id", fooo.getId()).executeUpdate();
assertThat(entityManager.find(Foo.class, foo.getId()), nullValue());
assertThat(entityManager.find(Fooo.class, fooo.getId()), nullValue());
}
@Test
@Transactional
public final void givenEntityIsDeletedWithNativeQuery_thenItIsNotInDB() {
Foo foo = new Foo("foo");
entityManager.persist(foo);
Fooo fooo = new Fooo("foo");
entityManager.persist(fooo);
flushAndClear();
entityManager.createNativeQuery("delete from FOO where ID = :id").setParameter("id", foo.getId()).executeUpdate();
entityManager.createNativeQuery("delete from FOO where ID = :id").setParameter("id", fooo.getId()).executeUpdate();
assertThat(entityManager.find(Foo.class, foo.getId()), nullValue());
assertThat(entityManager.find(Fooo.class, fooo.getId()), nullValue());
}
@Test
@Transactional
public final void givenEntityIsSoftDeleted_thenItIsNotReturnedFromQueries() {
Foo foo = new Foo("foo");
entityManager.persist(foo);
Fooo fooo = new Fooo("foo");
entityManager.persist(fooo);
flushAndClear();
foo = entityManager.find(Foo.class, foo.getId());
foo.setDeleted();
fooo = entityManager.find(Fooo.class, fooo.getId());
fooo.setDeleted();
flushAndClear();
assertThat(entityManager.find(Foo.class, foo.getId()), nullValue());
assertThat(entityManager.find(Fooo.class, fooo.getId()), nullValue());
}
private void flushAndClear() {

View File

@ -55,7 +55,7 @@
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.3</version>
<version>${jjwt.version}</version>
</dependency>
<!-- persistence -->
<dependency>
@ -107,6 +107,18 @@
<artifactId>jackson-core</artifactId>
<version>${jakson-core.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
@ -115,8 +127,11 @@
<spring-boot.version>3.1.0</spring-boot.version>
<!-- persistence -->
<tomcat-dbcp.version>10.1.9</tomcat-dbcp.version>
<jakson-databind.version>2.15.1</jakson-databind.version>
<jakson-core.version>2.14.2</jakson-core.version>
<jakson-databind.version>2.16.1</jakson-databind.version>
<jakson-core.version>2.16.1</jakson-core.version>
<jjwt.version>0.12.3</jjwt.version>
<logback.version>1.4.14</logback.version>
</properties>
</project>

View File

@ -1,5 +1,6 @@
package com.baeldung.multitenant.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
@ -18,6 +19,9 @@ import java.util.Properties;
@Configuration
public class MultitenantConfiguration {
@Value("${defaultTenant}")
private String defaultTenant;
@Bean
@ConfigurationProperties(prefix = "tenants")
public DataSource dataSource() {
@ -43,7 +47,7 @@ public class MultitenantConfiguration {
}
AbstractRoutingDataSource dataSource = new MultitenantDataSource();
dataSource.setDefaultTargetDataSource(resolvedDataSources.get("tenant_1"));
dataSource.setDefaultTargetDataSource(resolvedDataSources.get(defaultTenant));
dataSource.setTargetDataSources(resolvedDataSources);
dataSource.afterPropertiesSet();

View File

@ -1,26 +1,31 @@
package com.baeldung.multitenant.security;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Date;
public class AuthenticationService {
private static final long EXPIRATIONTIME = 864_000_00; // 1 day in milliseconds
private static final String SIGNINGKEY = "SecretKey";
private static final String SECRETKEY = "q3t6w9zCFJNcQfTjWnq3t6w9zCFJNcQfTjWnZr4u7xADGKaPd";
private static final SecretKey SIGNINGKEY = Keys.hmacShaKeyFor(SECRETKEY.getBytes(StandardCharsets.UTF_8));
private static final String PREFIX = "Bearer";
public static void addToken(HttpServletResponse res, String username, String tenant) {
String JwtToken = Jwts.builder().setSubject(username)
.setAudience(tenant)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATIONTIME))
.signWith(SignatureAlgorithm.HS512, SIGNINGKEY)
String JwtToken = Jwts.builder()
.subject(username)
.audience().add(tenant).and()
.issuedAt(new Date(System.currentTimeMillis()))
.expiration(new Date(System.currentTimeMillis() + EXPIRATIONTIME))
.signWith(SIGNINGKEY)
.compact();
res.addHeader("Authorization", PREFIX + " " + JwtToken);
}
@ -29,9 +34,8 @@ public class AuthenticationService {
String token = req.getHeader("Authorization");
if (token != null) {
String user = Jwts.parser()
.setSigningKey(SIGNINGKEY)
.build().parseClaimsJws(token.replace(PREFIX, ""))
.getBody()
.verifyWith(SIGNINGKEY)
.build().parseClaimsJws(token.replace(PREFIX, "").trim()).getPayload()
.getSubject();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, Collections.emptyList());
@ -48,7 +52,7 @@ public class AuthenticationService {
}
String tenant = Jwts.parser()
.setSigningKey(SIGNINGKEY)
.build().parseClaimsJws(token.replace(PREFIX, ""))
.build().parseClaimsJws(token.replace(PREFIX, "").trim())
.getBody()
.getAudience()
.iterator()

View File

@ -4,9 +4,9 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
@ -42,16 +42,21 @@ public class SecurityConfiguration {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
final AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
final AuthenticationManager authenticationManager = authenticationManager(http.getSharedObject(AuthenticationConfiguration.class));
http
.authorizeHttpRequests(authorize ->
authorize.requestMatchers("/login").permitAll().anyRequest().authenticated())
.sessionManagement(securityContext -> securityContext.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(new LoginFilter("/login", authenticationManager), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new AuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.csrf(AbstractHttpConfigurer::disable)
.csrf(csrf -> csrf.disable())
.headers(header -> header.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
.httpBasic(Customizer.withDefaults());

View File

@ -9,3 +9,7 @@ spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
#spring.datasource.username=mysqluser
#spring.datasource.password=mysqlpass
#spring.datasource.url=jdbc:mysql://localhost:3306/myDb?createDatabaseIfNotExist=true
# MultiTenantApplication
defaultTenant=tenant_1
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect

View File

@ -733,7 +733,7 @@
<module>jackson-simple</module>
<module>java-blockchain</module>
<module>java-jdi</module>
<!--<module>java-panama</module>--> <!--JAVA-27339-->
<module>java-panama</module>
<module>javafx</module>
<module>javax-sound</module>
<module>javaxval-2</module>
@ -973,7 +973,7 @@
<module>jackson-simple</module>
<module>java-blockchain</module>
<module>java-jdi</module>
<!--<module>java-panama</module>--> <!--JAVA-27339-->
<module>java-panama</module>
<module>javafx</module>
<module>javax-sound</module>
<module>javaxval-2</module>

View File

@ -19,6 +19,7 @@
<module>quarkus-jandex</module>
<module>quarkus-vs-springboot</module>
<module>quarkus-funqy</module>
<!-- requires Java 21 <module>quarkus-virtual-threads</module> -->
</modules>
</project>

View File

@ -0,0 +1,4 @@
*
!target/*-runner
!target/*-runner.jar
!target/lib/*

View File

@ -0,0 +1,160 @@
<?xml version="1.0"?>
<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.quarkus</groupId>
<artifactId>quarkus-virtual-threads</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>quarkus-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<compiler-plugin.version>3.11.0</compiler-plugin.version>
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.4.3</quarkus.platform.version>
<surefire-plugin.version>3.0.0</surefire-plugin.version>
<loom-unit.version>0.3.0</loom-unit.version>
<panache-common-deployment.version>3.6.0</panache-common-deployment.version>
<mockito-core.version>3.11.2</mockito-core.version>
</properties>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>me.escoffier.loom</groupId>
<artifactId>loom-unit</artifactId>
<version>${loom-unit.version}</version>
<scope>test</scope>
</dependency>
<!-- Quarkus Test framework -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-panache-common-deployment</artifactId>
<version>${panache-common-deployment.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito-core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-panache-mock</artifactId>
<version>${panache-common-deployment.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<properties>
<quarkus.package.type>native</quarkus.package.type>
</properties>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,97 @@
####
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
#
# Before building the container image run:
#
# ./mvnw package
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/virtual-threads-jvm .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/virtual-threads-jvm
#
# If you want to include the debug port into your docker image
# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
# when running the container
#
# Then run the container using :
#
# docker run -i --rm -p 8080:8080 quarkus/virtual-threads-jvm
#
# This image uses the `run-java.sh` script to run the application.
# This scripts computes the command line to execute your Java application, and
# includes memory/GC tuning.
# You can configure the behavior using the following environment properties:
# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
# in JAVA_OPTS (example: "-Dsome.property=foo")
# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
# used to calculate a default maximal heap memory based on a containers restriction.
# If used in a container without any memory constraints for the container then this
# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
# of the container available memory as set here. The default is `50` which means 50%
# of the available memory is used as an upper boundary. You can skip this mechanism by
# setting this value to `0` in which case no `-Xmx` option is added.
# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
# is used to calculate a default initial heap memory based on the maximum heap memory.
# If used in a container without any memory constraints for the container then this
# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
# is used as the initial heap size. You can skip this mechanism by setting this value
# to `0` in which case no `-Xms` option is added (example: "25")
# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
# This is used to calculate the maximum value of the initial heap memory. If used in
# a container without any memory constraints for the container then this option has
# no effect. If there is a memory constraint then `-Xms` is limited to the value set
# here. The default is 4096MB which means the calculated value of `-Xms` never will
# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
# when things are happening. This option, if set to true, will set
# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
# true").
# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
# (example: "20")
# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
# (example: "40")
# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
# (example: "4")
# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
# previous GC times. (example: "90")
# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
# contain the necessary JRE command-line options to specify the required GC, which
# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
# accessed directly. (example: "foo.example.com,bar.example.com")
#
###
FROM registry.access.redhat.com/ubi8/openjdk-21:1.18
ENV LANGUAGE='en_US:en'
# We make four distinct layers so if there are application changes the library layers can be re-used
COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/
COPY --chown=185 target/quarkus-app/*.jar /deployments/
COPY --chown=185 target/quarkus-app/app/ /deployments/app/
COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/
EXPOSE 8080
USER 185
ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]

View File

@ -0,0 +1,93 @@
####
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
#
# Before building the container image run:
#
# ./mvnw package -Dquarkus.package.type=legacy-jar
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/virtual-threads-legacy-jar .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/virtual-threads-legacy-jar
#
# If you want to include the debug port into your docker image
# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
# when running the container
#
# Then run the container using :
#
# docker run -i --rm -p 8080:8080 quarkus/virtual-threads-legacy-jar
#
# This image uses the `run-java.sh` script to run the application.
# This scripts computes the command line to execute your Java application, and
# includes memory/GC tuning.
# You can configure the behavior using the following environment properties:
# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
# in JAVA_OPTS (example: "-Dsome.property=foo")
# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
# used to calculate a default maximal heap memory based on a containers restriction.
# If used in a container without any memory constraints for the container then this
# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
# of the container available memory as set here. The default is `50` which means 50%
# of the available memory is used as an upper boundary. You can skip this mechanism by
# setting this value to `0` in which case no `-Xmx` option is added.
# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
# is used to calculate a default initial heap memory based on the maximum heap memory.
# If used in a container without any memory constraints for the container then this
# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
# is used as the initial heap size. You can skip this mechanism by setting this value
# to `0` in which case no `-Xms` option is added (example: "25")
# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
# This is used to calculate the maximum value of the initial heap memory. If used in
# a container without any memory constraints for the container then this option has
# no effect. If there is a memory constraint then `-Xms` is limited to the value set
# here. The default is 4096MB which means the calculated value of `-Xms` never will
# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
# when things are happening. This option, if set to true, will set
# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
# true").
# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
# (example: "20")
# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
# (example: "40")
# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
# (example: "4")
# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
# previous GC times. (example: "90")
# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
# contain the necessary JRE command-line options to specify the required GC, which
# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
# accessed directly. (example: "foo.example.com,bar.example.com")
#
###
FROM registry.access.redhat.com/ubi8/openjdk-21:1.18
ENV LANGUAGE='en_US:en'
COPY target/lib/* /deployments/lib/
COPY target/*-runner.jar /deployments/quarkus-run.jar
EXPOSE 8080
USER 185
ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]

View File

@ -0,0 +1,27 @@
####
# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
#
# Before building the container image run:
#
# ./mvnw package -Dnative
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile.native -t quarkus/virtual-threads .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/virtual-threads
#
###
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9
WORKDIR /work/
RUN chown 1001 /work \
&& chmod "g+rwX" /work \
&& chown 1001:root /work
COPY --chown=1001:root target/*-runner /work/application
EXPOSE 8080
USER 1001
ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]

View File

@ -0,0 +1,30 @@
####
# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
# It uses a micro base image, tuned for Quarkus native executables.
# It reduces the size of the resulting container image.
# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image.
#
# Before building the container image run:
#
# ./mvnw package -Dnative
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/virtual-threads .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/virtual-threads
#
###
FROM quay.io/quarkus/quarkus-micro-image:2.0
WORKDIR /work/
RUN chown 1001 /work \
&& chmod "g+rwX" /work \
&& chown 1001:root /work
COPY --chown=1001:root target/*-runner /work/application
EXPOSE 8080
USER 1001
ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]

View File

@ -0,0 +1,16 @@
package com.baeldung.quarkus;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/hello")
public class GreetingResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Hello from RESTEasy Reactive";
}
}

View File

@ -0,0 +1,14 @@
package com.baeldung.quarkus.rest;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
@RegisterRestClient(configKey = "remote-service")
public interface RemoteService {
@GET
@Path("/greetings")
default String greetings(){
return "Mocked Greeting";
};
}

View File

@ -0,0 +1,16 @@
package com.baeldung.quarkus.rest;
import io.smallrye.common.annotation.RunOnVirtualThread;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.rest.client.inject.RestClient;
@Path("/greetings")
public class VirtualThreadApp {
@RestClient RemoteService service;
@GET @RunOnVirtualThread
public String process() {
var response = service.greetings();
return response.toUpperCase();
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
remote-service/mp-rest/url=http://localhost:8080

View File

@ -0,0 +1,16 @@
package com.baeldung.quarkus;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/hello")
public class GreetingResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Hello from RESTEasy Reactive";
}
}

View File

@ -0,0 +1,14 @@
package com.baeldung.quarkus.rest;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
@RegisterRestClient(configKey = "remote-service")
public interface RemoteService {
@GET
@Path("/greetings")
default String greetings(){
return "Mocked Greeting";
};
}

View File

@ -0,0 +1,16 @@
package com.baeldung.quarkus.rest;
import io.smallrye.common.annotation.RunOnVirtualThread;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.rest.client.inject.RestClient;
@Path("/greetings")
public class VirtualThreadApp {
@RestClient RemoteService service;
@GET @RunOnVirtualThread
public String process() {
var response = service.greetings();
return response.toUpperCase();
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2,4 +2,5 @@
- [Spring Boot With Spring Batch](https://www.baeldung.com/spring-boot-spring-batch)
- [How to Trigger and Stop a Scheduled Spring Batch Job](https://www.baeldung.com/spring-batch-start-stop-job)
- [Access Job Parameters From ItemReader in Spring Batch](https://www.baeldung.com/spring-batch-itemreader-access-job-parameters)
- More articles [[<-- prev]](/spring-batch)

View File

@ -11,3 +11,4 @@ This module contains articles about Spring Boot customization 2
- [Guide to Spring Type Conversions](https://www.baeldung.com/spring-type-conversions)
- [Container Configuration in Spring Boot 2](https://www.baeldung.com/embeddedservletcontainercustomizer-configurableembeddedservletcontainer-spring-boot)
- [Speed up Spring Boot Startup Time](https://www.baeldung.com/spring-boot-startup-speed)
- More articles: [[<-- prev]](/spring-boot-modules/spring-boot-basic-customization)

View File

@ -9,9 +9,10 @@
<description>Module For Spring Boot Basic Customization 2</description>
<parent>
<groupId>com.baeldung.spring-boot-modules</groupId>
<artifactId>spring-boot-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-3</relativePath>
</parent>
<dependencies>

View File

@ -7,7 +7,7 @@ import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.ServletContextListener;
import jakarta.servlet.ServletContextListener;
@Configuration
public class WebConf {

View File

@ -4,7 +4,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import jakarta.servlet.*;
import java.io.IOException;
@Component

View File

@ -3,8 +3,8 @@ package com.baeldung.dispatchservlet.listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
public class CustomListener implements ServletContextListener {

View File

@ -4,11 +4,11 @@ import com.baeldung.dispatchservlet.filter.CustomFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CustomServlet extends HttpServlet {

View File

@ -3,10 +3,10 @@ package com.baeldung.onceperrequestfilter;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component

View File

@ -6,7 +6,7 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.context.request.async.DeferredResult;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

View File

@ -5,10 +5,10 @@ import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component

View File

@ -1,7 +1,7 @@
package com.baeldung.typeconversion.entity;
import javax.persistence.Entity;
import javax.persistence.Id;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@Entity
public class Employee {

View File

@ -11,3 +11,4 @@ This module contains articles about Spring Boot customization
- [Spring Boot: Configuring a Main Class](https://www.baeldung.com/spring-boot-main-class)
- [How to Define a Spring Boot Filter?](https://www.baeldung.com/spring-boot-add-filter)
- [Guide to the Favicon in Spring Boot](https://www.baeldung.com/spring-boot-favicon)
- More articles: [[next -->]](/spring-boot-modules/spring-boot-basic-customization-2)

View File

@ -9,9 +9,10 @@
<description>Module For Spring Boot Basic Customization</description>
<parent>
<groupId>com.baeldung.spring-boot-modules</groupId>
<artifactId>spring-boot-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-3</relativePath>
</parent>
<dependencies>
@ -81,7 +82,7 @@
</build>
<properties>
<spring-boot-maven-plugin.version>1.5.2.RELEASE</spring-boot-maven-plugin.version>
<spring-boot-maven-plugin.version>3.1.5</spring-boot-maven-plugin.version>
<!-- The main class to start by executing "java -jar" -->
<start-class>com.baeldung.changeport.CustomApplication</start-class>
</properties>

View File

@ -5,9 +5,14 @@ import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
@ -23,7 +28,7 @@ public class RequestResponseLoggingFilter implements Filter {
private final static Logger LOG = LoggerFactory.getLogger(RequestResponseLoggingFilter.class);
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
public void init(final FilterConfig filterConfig) {
LOG.info("Initializing filter :{}", this);
}

View File

@ -5,8 +5,13 @@ import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
/**

View File

@ -5,8 +5,8 @@ import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.http.HttpServletRequest;
@Controller
public class MyErrorController implements ErrorController {

View File

@ -4,7 +4,7 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Profile;
import javax.annotation.security.RolesAllowed;
import jakarta.annotation.security.RolesAllowed;
@Profile("failureanalyzer")
@SpringBootApplication(scanBasePackages = "com.baeldung.failureanalyzer")

View File

@ -2,7 +2,7 @@ package com.baeldung.failureanalyzer;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
@Service
public class MyService {

View File

@ -1,2 +1 @@
org.springframework.boot.diagnostics.FailureAnalyzer=com.baeldung.failureanalyzer.MyBeanNotOfRequiredTypeFailureAnalyzer

View File

@ -158,7 +158,7 @@
<properties>
<subethasmtp.version>3.1.7</subethasmtp.version>
<httpclient.version>4.5.8</httpclient.version>
<spring.cloud-version>2021.0.0</spring.cloud-version>
<spring.cloud-version>2023.0.0</spring.cloud-version>
</properties>
</project>

Binary file not shown.

View File

@ -8,4 +8,5 @@ This module contains articles about reactive Spring Boot.
- [Spring Boot Actuator](https://www.baeldung.com/spring-boot-actuators)
- [Reactive WebSockets with Spring 5](https://www.baeldung.com/spring-5-reactive-websockets)
- [A Guide to Spring Session Reactive Support: WebSession](https://www.baeldung.com/spring-session-reactive)
- [Custom JSON Deserialization Using Spring WebClient](https://www.baeldung.com/spring-webclient-json-custom-deserialization)
- More articles: [[<-- prev]](../spring-reactive-2)

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