diff --git a/apache-poi/pom.xml b/apache-poi/pom.xml index 1a0f77b025..546eedec5b 100644 --- a/apache-poi/pom.xml +++ b/apache-poi/pom.xml @@ -32,9 +32,27 @@ + + + + org.apache.maven.plugins + maven-resources-plugin + ${maven.resources.plugin.version} + + UTF-8 + + xlsx + xls + + + + + + 5.2.0 1.0.6 + 3.2.0 \ No newline at end of file diff --git a/apache-poi/src/main/resources/cellstyle/CellStyleBorderHandlerTest.xlsx b/apache-poi/src/main/resources/cellstyle/CellStyleBorderHandlerTest.xlsx new file mode 100644 index 0000000000..29f128211b Binary files /dev/null and b/apache-poi/src/main/resources/cellstyle/CellStyleBorderHandlerTest.xlsx differ diff --git a/apache-poi/src/test/java/com/baeldung/poi/excel/cellstyle/CellBorderHandlerUnitTest.java b/apache-poi/src/test/java/com/baeldung/poi/excel/cellstyle/CellBorderHandlerUnitTest.java index 3cabff2dff..2a36a6b1b2 100644 --- a/apache-poi/src/test/java/com/baeldung/poi/excel/cellstyle/CellBorderHandlerUnitTest.java +++ b/apache-poi/src/test/java/com/baeldung/poi/excel/cellstyle/CellBorderHandlerUnitTest.java @@ -12,7 +12,7 @@ import java.nio.file.Paths; import static org.junit.Assert.assertEquals; public class CellBorderHandlerUnitTest { - private static final String FILE_NAME = "cellstyle/CellStyleHandlerTest.xlsx"; + private static final String FILE_NAME = "cellstyle/CellStyleBorderHandlerTest.xlsx"; private static final int SHEET_INDEX = 0; private static CellBordersHandler cellBordersHandler; diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/game/RockPaperScissorsGame.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/game/RockPaperScissorsGame.java new file mode 100644 index 0000000000..fc9299f12d --- /dev/null +++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/game/RockPaperScissorsGame.java @@ -0,0 +1,66 @@ +package com.baeldung.game; + +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.Scanner; + +class RockPaperScissorsGame { + + private static Map movesMap = new HashMap() {{ + put(0, "rock"); + put(1, "paper"); + put(2, "scissors"); + }}; + + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + int wins = 0; + int losses = 0; + + System.out.println("Welcome to Rock-Paper-Scissors! Please enter \"rock\", \"paper\", \"scissors\", or \"quit\" to exit."); + + while (true) { + System.out.println("-------------------------"); + System.out.print("Enter your move: "); + String playerMove = scanner.nextLine(); + + if (playerMove.equals("quit")) { + System.out.println("You won " + wins + " times and lost " + losses + " times."); + System.out.println("Thanks for playing! See you again."); + break; + } + + if (!movesMap.containsValue(playerMove)) { + System.out.println("Your move isn't valid!"); + continue; + } + + String computerMove = getComputerMove(); + + if (playerMove.equals(computerMove)) { + System.out.println("It's a tie!"); + } else if (isPlayerWin(playerMove, computerMove)) { + System.out.println("You won!"); + wins++; + } else { + System.out.println("You lost!"); + losses++; + } + } + } + + private static boolean isPlayerWin(String playerMove, String computerMove) { + return playerMove.equals("rock") && computerMove.equals("scissors") + || (playerMove.equals("scissors") && computerMove.equals("paper")) + || (playerMove.equals("paper") && computerMove.equals("rock")); + } + + private static String getComputerMove() { + Random random = new Random(); + int randomNumber = random.nextInt(3); + String computerMove = movesMap.get(randomNumber); + System.out.println("Computer move: " + computerMove); + return computerMove; + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/uuid/UuidHelper.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/uuid/UuidHelper.java new file mode 100644 index 0000000000..5807262094 --- /dev/null +++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/uuid/UuidHelper.java @@ -0,0 +1,33 @@ +package com.baeldung.uuid; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.UUID; + +public class UuidHelper { + + public static byte[] convertUUIDToBytes(UUID uuid) { + ByteBuffer bb = ByteBuffer.wrap(new byte[16]); + bb.putLong(uuid.getMostSignificantBits()); + bb.putLong(uuid.getLeastSignificantBits()); + return bb.array(); + } + + public static UUID convertBytesToUUID(byte[] bytes) { + ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); + long high = byteBuffer.getLong(); + long low = byteBuffer.getLong(); + return new UUID(high, low); + } + + public static void main(String[] args) { + UUID uuid = UUID.randomUUID(); + System.out.println("Original UUID: " + uuid); + + byte[] bytes = convertUUIDToBytes(uuid); + System.out.println("Converted byte array: " + Arrays.toString(bytes)); + + UUID uuidNew = convertBytesToUUID(bytes); + System.out.println("Converted UUID: " + uuidNew); + } +} diff --git a/core-java-modules/core-java-collections-4/src/main/java/com/baeldung/maps/initialize/EmptyMapInitializer.java b/core-java-modules/core-java-collections-4/src/main/java/com/baeldung/maps/initialize/EmptyMapInitializer.java new file mode 100644 index 0000000000..3dc644f1af --- /dev/null +++ b/core-java-modules/core-java-collections-4/src/main/java/com/baeldung/maps/initialize/EmptyMapInitializer.java @@ -0,0 +1,56 @@ +package com.baeldung.maps.initialize; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.NavigableMap; +import java.util.SortedMap; +import java.util.TreeMap; + +public class EmptyMapInitializer { + + public static Map articleMap; + static { + articleMap = new HashMap<>(); + } + + public static Map createEmptyMap() { + return Collections.emptyMap(); + } + + public void createMapUsingConstructors() { + Map hashMap = new HashMap(); + Map linkedHashMap = new LinkedHashMap(); + Map treeMap = new TreeMap(); + } + + public Map createEmptyMapUsingMapsObject() { + Map emptyMap = Maps.newHashMap(); + return emptyMap; + } + + public Map createGenericEmptyMapUsingMapsObject() { + Map genericEmptyMap = Maps.newHashMap(); + return genericEmptyMap; + } + + public static Map createMapUsingGuava() { + Map emptyMapUsingGuava = + Maps.newHashMap(ImmutableMap.of()); + return emptyMapUsingGuava; + } + + public SortedMap createEmptySortedMap() { + SortedMap sortedMap = Collections.emptySortedMap(); + return sortedMap; + } + + public NavigableMap createEmptyNavigableMap() { + NavigableMap navigableMap = Collections.emptyNavigableMap(); + return navigableMap; + } + +} diff --git a/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/maps/initialize/EmptyMapInitializerUnitTest.java b/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/maps/initialize/EmptyMapInitializerUnitTest.java new file mode 100644 index 0000000000..cc25205ba7 --- /dev/null +++ b/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/maps/initialize/EmptyMapInitializerUnitTest.java @@ -0,0 +1,31 @@ +package com.baeldung.maps.initialize; + +import java.util.Map; +import org.junit.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class EmptyMapInitializerUnitTest { + + @Test(expected=UnsupportedOperationException.class) + public void givenEmptyMap_whenAddingEntries_throwsException() { + Map map = EmptyMapInitializer.createEmptyMap(); + map.put("key", "value"); + } + + @Test + public void givenEmptyMap_whenChecked_returnsTrue() { + assertTrue(EmptyMapInitializer.articleMap.isEmpty()); + } + + @Test + public void givenEmptyMap_whenCreatedUsingGuava_returnsEmptyOrNot() { + Map emptyMapUsingGuava = + EmptyMapInitializer.createMapUsingGuava(); + assertTrue(emptyMapUsingGuava.isEmpty()); + emptyMapUsingGuava.put("key", "value"); + assertFalse(emptyMapUsingGuava.isEmpty()); + } + +} diff --git a/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/mapandhashmap/Main.java b/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/mapandhashmap/Main.java new file mode 100644 index 0000000000..1024399a98 --- /dev/null +++ b/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/mapandhashmap/Main.java @@ -0,0 +1,25 @@ +package com.baeldung.mapandhashmap; + +import com.baeldung.mapandhashmap.printer.HashMapPrinter; +import com.baeldung.mapandhashmap.printer.MapPrinter; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +public class Main { + public static void main(String[] args) { + Map map = new HashMap<>(); + HashMap hashMap = new HashMap<>(); + TreeMap treeMap = new TreeMap<>(); + + HashMapPrinter hashMapPrinter = new HashMapPrinter(); + hashMapPrinter.printMap(hashMap); +// hashMapPrinter.printMap(treeMap); Compile time error +// hashMapPrinter.printMap(map); Compile time error + + MapPrinter mapPrinter = new MapPrinter(); + mapPrinter.printMap(hashMap); + mapPrinter.printMap(treeMap); + mapPrinter.printMap(map); + } +} diff --git a/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/mapandhashmap/printer/HashMapPrinter.java b/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/mapandhashmap/printer/HashMapPrinter.java new file mode 100644 index 0000000000..53c78bfc55 --- /dev/null +++ b/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/mapandhashmap/printer/HashMapPrinter.java @@ -0,0 +1,13 @@ +package com.baeldung.mapandhashmap.printer; + +import java.util.HashMap; +import java.util.Map.Entry; + +public class HashMapPrinter { + + public void printMap(final HashMap map) { + for (final Entry entry : map.entrySet()) { + System.out.println(entry.getKey() + " " + entry.getValue()); + } + } +} diff --git a/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/mapandhashmap/printer/MapPrinter.java b/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/mapandhashmap/printer/MapPrinter.java new file mode 100644 index 0000000000..e5c0ab49cd --- /dev/null +++ b/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/mapandhashmap/printer/MapPrinter.java @@ -0,0 +1,13 @@ +package com.baeldung.mapandhashmap.printer; + +import java.util.Map; +import java.util.Map.Entry; + +public class MapPrinter { + + public void printMap(final Map map) { + for (final Entry entry : map.entrySet()) { + System.out.println(entry.getKey() + " " + entry.getValue()); + } + } +} diff --git a/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/mapandhashmap/printer/MapReporter.java b/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/mapandhashmap/printer/MapReporter.java new file mode 100644 index 0000000000..fd7347c2d1 --- /dev/null +++ b/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/mapandhashmap/printer/MapReporter.java @@ -0,0 +1,19 @@ +package com.baeldung.mapandhashmap.printer; + +import java.util.Map; +import java.util.Map.Entry; + +public class MapReporter { + + private final Map map; + + public MapReporter(final Map map) { + this.map = map; + } + + public void printMap() { + for (final Entry entry : this.map.entrySet()) { + System.out.println(entry.getKey() + " " + entry.getValue()); + } + } +} diff --git a/core-java-modules/core-java-collections-maps-4/src/test/java/com/baeldung/mapandhashmap/printer/HashMapPrinterUnitTest.java b/core-java-modules/core-java-collections-maps-4/src/test/java/com/baeldung/mapandhashmap/printer/HashMapPrinterUnitTest.java new file mode 100644 index 0000000000..e1147f6e00 --- /dev/null +++ b/core-java-modules/core-java-collections-maps-4/src/test/java/com/baeldung/mapandhashmap/printer/HashMapPrinterUnitTest.java @@ -0,0 +1,56 @@ +package com.baeldung.mapandhashmap.printer; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.HashMap; +import java.util.LinkedHashMap; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class HashMapPrinterUnitTest { + + private final HashMapPrinter mapPrinter = new HashMapPrinter(); + private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); + + @BeforeEach + public void setUp() { + System.setOut(new PrintStream(outputStreamCaptor)); + } + + @Test + @DisplayName("Test hash map printer with HashMap") + void testPrintHashMap() { + // given + String key = "HashMap"; + String value = "Main default implementation for the Map interface"; + String expected = key + " " + value; + HashMap map = new HashMap<>(); + map.put(key, value); + // when + mapPrinter.printMap(map); + // then + String actual = outputStreamCaptor.toString().trim(); + assertThat(actual).isEqualTo(expected); + + } + + @Test + @DisplayName("Test hash map printer with LinkedHash") + void testPrintLinkedHashMap() { + // given + String key = "LinkedHashMap"; + String value = "Use this implementation if you need keep the order of elements"; + String expected = key + " " + value; + LinkedHashMap map = new LinkedHashMap<>(); + map.put(key, value); + // when + mapPrinter.printMap(map); + // then + String actual = outputStreamCaptor.toString().trim(); + assertThat(actual).isEqualTo(expected); + + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-collections-maps-4/src/test/java/com/baeldung/mapandhashmap/printer/MapPrinterUnitTest.java b/core-java-modules/core-java-collections-maps-4/src/test/java/com/baeldung/mapandhashmap/printer/MapPrinterUnitTest.java new file mode 100644 index 0000000000..8c45758bdf --- /dev/null +++ b/core-java-modules/core-java-collections-maps-4/src/test/java/com/baeldung/mapandhashmap/printer/MapPrinterUnitTest.java @@ -0,0 +1,71 @@ +package com.baeldung.mapandhashmap.printer; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.TreeMap; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class MapPrinterUnitTest { + + private final MapPrinter mapPrinter = new MapPrinter(); + private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); + + @BeforeEach + public void setUp() { + System.setOut(new PrintStream(outputStreamCaptor)); + } + + @Test + @DisplayName("Test printer with TreeMap") + void testPrintTreeMap() { + // given + String key = "TreeMap"; + String value = "Used when sorting is needed"; + String expected = key + " " + value; + TreeMap map = new TreeMap<>(); + map.put(key, value); + // when + mapPrinter.printMap(map); + // then + String actual = outputStreamCaptor.toString().trim(); + assertThat(actual).isEqualTo(expected); + } + + @Test + @DisplayName("Test printer with HashMap") + void testPrintHashMap() { + // given + String key = "HashMap"; + String value = "Main default implementation for the Map interface"; + String expected = key + " " + value; + HashMap map = new HashMap<>(); + map.put(key, value); + // when + mapPrinter.printMap(map); + // then + String actual = outputStreamCaptor.toString().trim(); + assertThat(actual).isEqualTo(expected); + } + + @Test + @DisplayName("Test printer with LinkedHash") + void testPrintLinkedHashMap() { + // given + String key = "LinkedHashMap"; + String value = "Use this implementation if you need keep the order of elements"; + String expected = key + " " + value; + LinkedHashMap map = new LinkedHashMap<>(); + map.put(key, value); + // when + mapPrinter.printMap(map); + // then + String actual = outputStreamCaptor.toString().trim(); + assertThat(actual).isEqualTo(expected); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-collections-maps-4/src/test/java/com/baeldung/mapandhashmap/printer/MapReporterUnitTest.java b/core-java-modules/core-java-collections-maps-4/src/test/java/com/baeldung/mapandhashmap/printer/MapReporterUnitTest.java new file mode 100644 index 0000000000..8f858a75c4 --- /dev/null +++ b/core-java-modules/core-java-collections-maps-4/src/test/java/com/baeldung/mapandhashmap/printer/MapReporterUnitTest.java @@ -0,0 +1,74 @@ +package com.baeldung.mapandhashmap.printer; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.TreeMap; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class MapReporterUnitTest { + + private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); + + @BeforeEach + public void setUp() { + System.setOut(new PrintStream(outputStreamCaptor)); + } + + @Test + @DisplayName("Test reporter with TreeMap") + void testPrintTreeMap() { + // given + String key = "TreeMap"; + String value = "Used when sorting is needed"; + String expected = key + " " + value; + TreeMap map = new TreeMap<>(); + map.put(key, value); + // when + MapReporter mapReporter = new MapReporter(map); + mapReporter.printMap(); + // then + String actual = outputStreamCaptor.toString().trim(); + assertThat(actual).isEqualTo(expected); + } + + @Test + @DisplayName("Test reporter with HashMap") + void testPrintHashMap() { + // given + String key = "HashMap"; + String value = "Main default implementation for the Map interface"; + String expected = key + " " + value; + HashMap map = new HashMap<>(); + map.put(key, value); + // when + MapReporter mapReporter = new MapReporter(map); + mapReporter.printMap(); + // then + String actual = outputStreamCaptor.toString().trim(); + assertThat(actual).isEqualTo(expected); + } + + @Test + @DisplayName("Test reporter with LinkedHash") + void testPrintLinkedHashMap() { + // given + String key = "LinkedHashMap"; + String value = "Use this implementation if you need keep the order of elements"; + String expected = key + " " + value; + LinkedHashMap map = new LinkedHashMap<>(); + map.put(key, value); + // when + MapReporter mapReporter = new MapReporter(map); + mapReporter.printMap(); + // then + String actual = outputStreamCaptor.toString().trim(); + assertThat(actual).isEqualTo(expected); + } + +} \ No newline at end of file diff --git a/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/atomicstampedreference/StampedAccount.java b/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/atomicstampedreference/StampedAccount.java index 415b24738a..69aed0b979 100644 --- a/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/atomicstampedreference/StampedAccount.java +++ b/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/atomicstampedreference/StampedAccount.java @@ -1,25 +1,22 @@ package com.baeldung.atomicstampedreference; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicStampedReference; public class StampedAccount { - private AtomicInteger stamp = new AtomicInteger(0); - private AtomicStampedReference account = new AtomicStampedReference<>(0, 0); - - public int getBalance() { - return account.getReference(); - } - - public int getStamp() { - return account.getStamp(); - } + private final AtomicInteger stamp = new AtomicInteger(0); + private final AtomicStampedReference account = new AtomicStampedReference<>(0, 0); public boolean deposit(int funds) { int[] stamps = new int[1]; int current = this.account.get(stamps); int newStamp = this.stamp.incrementAndGet(); + + // Thread is paused here to allow other threads to update the stamp and amount (for testing only) + sleep(); + return this.account.compareAndSet(current, current + funds, stamps[0], newStamp); } @@ -29,4 +26,19 @@ public class StampedAccount { int newStamp = this.stamp.incrementAndGet(); return this.account.compareAndSet(current, current - funds, stamps[0], newStamp); } + + public int getBalance() { + return account.getReference(); + } + + public int getStamp() { + return account.getStamp(); + } + + private static void sleep() { + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException ignored) { + } + } } diff --git a/core-java-modules/core-java-concurrency-advanced-3/src/test/java/com/baeldung/atomicstampedreference/ThreadStampedAccountUnitTest.java b/core-java-modules/core-java-concurrency-advanced-3/src/test/java/com/baeldung/atomicstampedreference/ThreadStampedAccountUnitTest.java index ce83355073..2840c25cf6 100644 --- a/core-java-modules/core-java-concurrency-advanced-3/src/test/java/com/baeldung/atomicstampedreference/ThreadStampedAccountUnitTest.java +++ b/core-java-modules/core-java-concurrency-advanced-3/src/test/java/com/baeldung/atomicstampedreference/ThreadStampedAccountUnitTest.java @@ -1,21 +1,38 @@ package com.baeldung.atomicstampedreference; -import org.junit.Assert; import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + public class ThreadStampedAccountUnitTest { @Test public void givenMultiThread_whenStampedAccount_thenSetBalance() throws InterruptedException { StampedAccount account = new StampedAccount(); + Thread t = new Thread(() -> { - while (!account.withdrawal(100)) + while (!account.deposit(100)) { Thread.yield(); + } }); t.start(); - Assert.assertTrue(account.deposit(100)); - t.join(1_000); - Assert.assertFalse(t.isAlive()); - Assert.assertSame(0, account.getBalance()); + + Thread t2 = new Thread(() -> { + while (!account.withdrawal(100)) { + Thread.yield(); + } + }); + t2.start(); + + t.join(10_000); + t2.join(10_000); + + assertFalse(t.isAlive()); + assertFalse(t2.isAlive()); + + assertEquals(0, account.getBalance()); + assertTrue(account.getStamp() > 0); } } diff --git a/core-java-modules/core-java-concurrency-advanced/src/main/java/com/baeldung/concurrent/phaser/LongRunningAction.java b/core-java-modules/core-java-concurrency-advanced/src/main/java/com/baeldung/concurrent/phaser/LongRunningAction.java index dd1487b5cb..c022c02085 100644 --- a/core-java-modules/core-java-concurrency-advanced/src/main/java/com/baeldung/concurrent/phaser/LongRunningAction.java +++ b/core-java-modules/core-java-concurrency-advanced/src/main/java/com/baeldung/concurrent/phaser/LongRunningAction.java @@ -16,12 +16,17 @@ class LongRunningAction implements Runnable { public void run() { System.out.println("This is phase " + ph.getPhase()); System.out.println("Thread " + threadName + " before long running action"); - ph.arriveAndAwaitAdvance(); + try { - Thread.sleep(20); + Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } + + System.out.println("Thread " + threadName + " action completed and waiting for others"); + ph.arriveAndAwaitAdvance(); + System.out.println("Thread " + threadName + " proceeding in phase " + ph.getPhase()); + ph.arriveAndDeregister(); } } \ No newline at end of file diff --git a/core-java-modules/core-java-concurrency-advanced/src/test/java/com/baeldung/concurrent/phaser/PhaserUnitTest.java b/core-java-modules/core-java-concurrency-advanced/src/test/java/com/baeldung/concurrent/phaser/PhaserUnitTest.java index a7b56e1151..88c18b5149 100644 --- a/core-java-modules/core-java-concurrency-advanced/src/test/java/com/baeldung/concurrent/phaser/PhaserUnitTest.java +++ b/core-java-modules/core-java-concurrency-advanced/src/test/java/com/baeldung/concurrent/phaser/PhaserUnitTest.java @@ -26,13 +26,20 @@ public class PhaserUnitTest { executorService.submit(new LongRunningAction("thread-3", ph)); //then + System.out.println("Thread " + Thread.currentThread().getName() + " waiting for others"); ph.arriveAndAwaitAdvance(); + System.out.println("Thread " + Thread.currentThread().getName() + " proceeding in phase " + ph.getPhase()); + assertEquals(1, ph.getPhase()); //and executorService.submit(new LongRunningAction("thread-4", ph)); executorService.submit(new LongRunningAction("thread-5", ph)); + + System.out.println("Thread " + Thread.currentThread().getName() + " waiting for others"); ph.arriveAndAwaitAdvance(); + System.out.println("Thread " + Thread.currentThread().getName() + " proceeding in phase " + ph.getPhase()); + assertEquals(2, ph.getPhase()); diff --git a/core-java-modules/core-java-exceptions-4/src/main/java/com/baeldung/exception/missingreturnstatement/MissingReturnStatement.java b/core-java-modules/core-java-exceptions-4/src/main/java/com/baeldung/exception/missingreturnstatement/MissingReturnStatement.java new file mode 100644 index 0000000000..d76ae76a0e --- /dev/null +++ b/core-java-modules/core-java-exceptions-4/src/main/java/com/baeldung/exception/missingreturnstatement/MissingReturnStatement.java @@ -0,0 +1,41 @@ +package com.baeldung.exception.missingreturnstatement; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class MissingReturnStatement { + public static void main(String[] args) { + int a = -12; + int result = pow(a); + System.out.println(result); + Map dictionary = createDictionary(); + dictionary.forEach((s, integer) -> System.out.println(s + " " + integer)); + } + + public static int pow(int number) { + int pow = number * number; + return pow; + } + + public static String checkNumber(int number) { + if (number == 0) { + return "It's equals to zero"; + } + + for (int i = 0; i < number; i++) { + if (i > 100) { + return "It's a big number"; + } + } + return "It's a negative number"; + } + + public static Map createDictionary() { + List words = Arrays.asList("Hello", "World"); + return words.stream() + .collect(Collectors.toMap(s -> s, s -> 1)); + } + +} diff --git a/core-java-modules/core-java-exceptions-4/src/test/java/com/baeldung/exception/missingreturnstatement/MissingReturnStatementUnitTest.java b/core-java-modules/core-java-exceptions-4/src/test/java/com/baeldung/exception/missingreturnstatement/MissingReturnStatementUnitTest.java new file mode 100644 index 0000000000..97a050a3f0 --- /dev/null +++ b/core-java-modules/core-java-exceptions-4/src/test/java/com/baeldung/exception/missingreturnstatement/MissingReturnStatementUnitTest.java @@ -0,0 +1,38 @@ +package com.baeldung.exception.missingreturnstatement; + +import static org.junit.Assert.assertEquals; + +import java.util.Map; + +import org.junit.Test; + +public class MissingReturnStatementUnitTest { + + @Test + public void givenANumber_thenReturnItsPow() { + int number = 10; + int pow = MissingReturnStatement.pow(number); + assertEquals(100, pow); + } + + @Test + public void givenABigNumber_thenReturnItsType() { + int number = 200; + String type = MissingReturnStatement.checkNumber(number); + assertEquals("It's a big number", type); + } + + @Test + public void givenANegativeNumber_thenReturnItsType() { + int number = -10; + String type = MissingReturnStatement.checkNumber(number); + assertEquals("It's a negative number", type); + } + + @Test + public void getStringDictionary_thenPrintValues() { + Map dictionary = MissingReturnStatement.createDictionary(); + assertEquals(2, dictionary.size()); + dictionary.forEach((s, integer) -> System.out.println(s + " - " + integer)); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-jar/pom.xml b/core-java-modules/core-java-jar/pom.xml index da107c745f..714a370287 100644 --- a/core-java-modules/core-java-jar/pom.xml +++ b/core-java-modules/core-java-jar/pom.xml @@ -259,110 +259,7 @@ - - - buildAgentLoader - - - - org.apache.maven.plugins - maven-jar-plugin - - - package - - jar - - - agentLoader - target/classes - - - true - - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - - - com/baeldung/instrumentation/application/AgentLoader.class - com/baeldung/instrumentation/application/Launcher.class - - - - - - - - - - buildApplication - - - - org.apache.maven.plugins - maven-jar-plugin - - - package - - jar - - - application - target/classes - - - true - - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - - - com/baeldung/instrumentation/application/MyAtm.class - com/baeldung/instrumentation/application/MyAtmApplication.class - com/baeldung/instrumentation/application/Launcher.class - - - - - - - - - - buildAgent - - - - org.apache.maven.plugins - maven-jar-plugin - - - package - - jar - - - agent - target/classes - - - true - - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - - - com/baeldung/instrumentation/agent/AtmTransformer.class - com/baeldung/instrumentation/agent/MyInstrumentationAgent.class - - - - - - - - + diff --git a/core-java-modules/core-java-jvm/README.md b/core-java-modules/core-java-jvm/README.md index 431cf021c8..e9266dea3b 100644 --- a/core-java-modules/core-java-jvm/README.md +++ b/core-java-modules/core-java-jvm/README.md @@ -15,3 +15,13 @@ This module contains articles about working with the Java Virtual Machine (JVM). - [What Causes java.lang.OutOfMemoryError: unable to create new native thread](https://www.baeldung.com/java-outofmemoryerror-unable-to-create-new-native-thread) - [View Bytecode of a Class File in Java](https://www.baeldung.com/java-class-view-bytecode) - More articles: [[next -->]](/core-java-modules/core-java-jvm-2) + + +To run the code for the Instrumentation: https://www.baeldung.com/java-instrumentation article: +1- build the module +2- run the module 3 times to build the 3 jars: + mvn install -PbuildAgentLoader + mvn install -PbuildApplication + mvn install -PbuildAgent +3- update the commands in the article with the exact names of the jars generated in the target folder +4- update the path in the AgentLoader class with the path of the agent on your system \ No newline at end of file diff --git a/core-java-modules/core-java-jvm/pom.xml b/core-java-modules/core-java-jvm/pom.xml index c47765e43d..e4c0f949c2 100644 --- a/core-java-modules/core-java-jvm/pom.xml +++ b/core-java-modules/core-java-jvm/pom.xml @@ -57,6 +57,11 @@ jol-core ${jol-core.version} + + org.slf4j + log4j-over-slf4j + ${org.slf4j.version} + @@ -66,5 +71,114 @@ 8.0.1 6.5.0 + + + + buildAgentLoader + + + + org.apache.maven.plugins + maven-jar-plugin + + + package + + jar + + + agentLoader + target/classes + + + true + com.baeldung.instrumentation.application.Launcher + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + com/baeldung/instrumentation/application/AgentLoader.class + com/baeldung/instrumentation/application/Launcher.class + + + + + + + + + + buildApplication + + + + org.apache.maven.plugins + maven-jar-plugin + + + package + + jar + + + application + target/classes + + + true + com.baeldung.instrumentation.application.Launcher + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + com/baeldung/instrumentation/application/MyAtm.class + com/baeldung/instrumentation/application/MyAtmApplication.class + com/baeldung/instrumentation/application/Launcher.class + + + + + + + + + + buildAgent + + + + org.apache.maven.plugins + maven-jar-plugin + + + package + + jar + + + agent + target/classes + + + true + com.baeldung.instrumentation.application.Launcher + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + com/baeldung/instrumentation/agent/AtmTransformer.class + com/baeldung/instrumentation/agent/MyInstrumentationAgent.class + + + + + + + + + \ No newline at end of file diff --git a/core-java-modules/core-java-jvm/src/main/resources/META-INF/MANIFEST.MF b/core-java-modules/core-java-jvm/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..d16a93ec83 --- /dev/null +++ b/core-java-modules/core-java-jvm/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,5 @@ +Agent-Class: com.baeldung.instrumentation.agent.MyInstrumentationAgent +Can-Redefine-Classes: true +Can-Retransform-Classes: true +Premain-Class: com.baeldung.instrumentation.agent.MyInstrumentationAgent +Main-Class: com.baeldung.instrumentation.application.Launcher \ No newline at end of file diff --git a/core-java-modules/core-java-security-3/src/test/java/com/baeldung/truststore/TrustStoreUnitTest.java b/core-java-modules/core-java-security-3/src/test/java/com/baeldung/truststore/TrustStoreUnitTest.java new file mode 100644 index 0000000000..2c3d4c8959 --- /dev/null +++ b/core-java-modules/core-java-security-3/src/test/java/com/baeldung/truststore/TrustStoreUnitTest.java @@ -0,0 +1,29 @@ +package com.baeldung.truststore; + +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.PKIXParameters; + +public class TrustStoreUnitTest { + + @Test + public void whenOpeningTrustStore_thenExceptionIsThrown() throws Exception { + KeyStore keyStore = getKeyStore(); + InvalidAlgorithmParameterException invalidAlgorithmParameterException = + Assertions.assertThrows(InvalidAlgorithmParameterException.class, () -> new PKIXParameters(keyStore)); + Assertions.assertEquals("the trustAnchors parameter must be non-empty", invalidAlgorithmParameterException.getMessage()); + } + + private KeyStore getKeyStore() throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException { + KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + ks.load(null, "changeIt".toCharArray()); + return ks; + } +} \ No newline at end of file diff --git a/docker/docker-include-outside-build-context/projects/config/Dockerfile b/docker/docker-include-outside-build-context/projects/config/Dockerfile new file mode 100644 index 0000000000..754f9f9be3 --- /dev/null +++ b/docker/docker-include-outside-build-context/projects/config/Dockerfile @@ -0,0 +1,2 @@ +FROM nginx:latest +COPY nginx.conf /etc/nginx/nginx.conf diff --git a/docker/docker-include-outside-build-context/projects/config/nginx.conf b/docker/docker-include-outside-build-context/projects/config/nginx.conf new file mode 100644 index 0000000000..944da2f112 --- /dev/null +++ b/docker/docker-include-outside-build-context/projects/config/nginx.conf @@ -0,0 +1,8 @@ +events {} + +http { + server { + listen 80; + index index.html; + } +} diff --git a/docker/docker-include-outside-build-context/projects/sample-site/docker/Dockerfile b/docker/docker-include-outside-build-context/projects/sample-site/docker/Dockerfile new file mode 100644 index 0000000000..bda0bbe257 --- /dev/null +++ b/docker/docker-include-outside-build-context/projects/sample-site/docker/Dockerfile @@ -0,0 +1,3 @@ +FROM nginx:latest +COPY sample-site/html/* /etc/nginx/html/ +COPY config/nginx.conf /etc/nginx/nginx.conf diff --git a/docker/docker-include-outside-build-context/projects/sample-site/docker/Dockerfile-from-base b/docker/docker-include-outside-build-context/projects/sample-site/docker/Dockerfile-from-base new file mode 100644 index 0000000000..1b72414bc0 --- /dev/null +++ b/docker/docker-include-outside-build-context/projects/sample-site/docker/Dockerfile-from-base @@ -0,0 +1,2 @@ +FROM sample-site-base:latest +COPY html/* /etc/nginx/html/ diff --git a/docker/docker-include-outside-build-context/projects/sample-site/docker/Dockerfile-script b/docker/docker-include-outside-build-context/projects/sample-site/docker/Dockerfile-script new file mode 100644 index 0000000000..572028f19f --- /dev/null +++ b/docker/docker-include-outside-build-context/projects/sample-site/docker/Dockerfile-script @@ -0,0 +1,3 @@ +FROM nginx:latest +COPY html/* /etc/nginx/html/ +COPY config/nginx.conf /etc/nginx/nginx.conf diff --git a/docker/docker-include-outside-build-context/projects/sample-site/docker/build-docker.sh b/docker/docker-include-outside-build-context/projects/sample-site/docker/build-docker.sh new file mode 100755 index 0000000000..e84687b7b7 --- /dev/null +++ b/docker/docker-include-outside-build-context/projects/sample-site/docker/build-docker.sh @@ -0,0 +1,6 @@ +mkdir tmp-context +cp -R ../html tmp-context/ +cp -R ../../config tmp-context/ +cp Dockerfile-script tmp-context/Dockerfile +docker build -t sample-site:latest tmp-context +rm -rf tmp-context diff --git a/docker/docker-include-outside-build-context/projects/sample-site/html/index.html b/docker/docker-include-outside-build-context/projects/sample-site/html/index.html new file mode 100644 index 0000000000..85f287fad7 --- /dev/null +++ b/docker/docker-include-outside-build-context/projects/sample-site/html/index.html @@ -0,0 +1,10 @@ + + + + + Sample Site + + +

Welcome to the first page of the site

+ + diff --git a/gradle/gradle-source-vs-target-compatibility/build.gradle b/gradle/gradle-source-vs-target-compatibility/build.gradle new file mode 100644 index 0000000000..8cc50c15c6 --- /dev/null +++ b/gradle/gradle-source-vs-target-compatibility/build.gradle @@ -0,0 +1,12 @@ +plugins { + id 'java' +} + +description = 'gradle-source-vs-target-compatibility' + +group 'com.baeldung' + +java { + sourceCompatibility = "1.6" + targetCompatibility = "1.8" +} diff --git a/gradle/gradle-source-vs-target-compatibility/src/main/java/com/baeldung/helloworld/HelloWorldApp.java b/gradle/gradle-source-vs-target-compatibility/src/main/java/com/baeldung/helloworld/HelloWorldApp.java new file mode 100644 index 0000000000..a951a72dd6 --- /dev/null +++ b/gradle/gradle-source-vs-target-compatibility/src/main/java/com/baeldung/helloworld/HelloWorldApp.java @@ -0,0 +1,9 @@ +package com.baeldung.helloworld; + +public class HelloWorldApp { + + public static void main(String[] args) { + System.out.println("Hello World!"); + } + +} diff --git a/gradle/settings.gradle b/gradle/settings.gradle index 59300f9281..d1eb10f4d2 100644 --- a/gradle/settings.gradle +++ b/gradle/settings.gradle @@ -6,5 +6,6 @@ include 'greeter' include 'gradletaskdemo' include 'junit5' include 'gradle-employee-app' +include 'gradle-source-vs-target-compatibility' println 'This will be executed during the initialization phase.' diff --git a/java-collections-maps-3/src/main/java/com/baeldung/map/invert/InvertHashMapExample.java b/java-collections-maps-3/src/main/java/com/baeldung/map/invert/InvertHashMapExample.java new file mode 100644 index 0000000000..a203fd17c4 --- /dev/null +++ b/java-collections-maps-3/src/main/java/com/baeldung/map/invert/InvertHashMapExample.java @@ -0,0 +1,59 @@ +package com.baeldung.map.invert; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +public class InvertHashMapExample { + + public static void main(String[] args) { + + Map map = new HashMap<>(); + map.put("first", 1); + map.put("second", 2); + System.out.println(map); + + invertMapUsingForLoop(map); + invertMapUsingStreams(map); + invertMapUsingMapper(map); + + map.put("two", 2); + invertMapUsingGroupingBy(map); + } + + public static Map invertMapUsingForLoop(Map map) { + Map inversedMap = new HashMap(); + for (Entry entry : map.entrySet()) { + inversedMap.put(entry.getValue(), entry.getKey()); + } + System.out.println(inversedMap); + return inversedMap; + } + + public static Map invertMapUsingStreams(Map map) { + Map inversedMap = map.entrySet() + .stream() + .collect(Collectors.toMap(Entry::getValue, Entry::getKey)); + System.out.println(inversedMap); + return inversedMap; + } + + public static Map invertMapUsingMapper(Map sourceMap) { + Map inversedMap = sourceMap.entrySet() + .stream() + .collect(Collectors.toMap(Entry::getValue, Entry::getKey, (oldValue, newValue) -> oldValue)); + System.out.println(inversedMap); + return inversedMap; + } + + public static Map> invertMapUsingGroupingBy(Map map) { + Map> inversedMap = map.entrySet() + .stream() + .collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList()))); + System.out.println(inversedMap); + return inversedMap; + } + +} diff --git a/java-collections-maps-3/src/test/java/com/baeldung/map/invert/InvertHashMapUnitTest.java b/java-collections-maps-3/src/test/java/com/baeldung/map/invert/InvertHashMapUnitTest.java new file mode 100644 index 0000000000..6870cdef23 --- /dev/null +++ b/java-collections-maps-3/src/test/java/com/baeldung/map/invert/InvertHashMapUnitTest.java @@ -0,0 +1,72 @@ +package com.baeldung.map.invert; + +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class InvertHashMapUnitTest { + + Map sourceMap; + + @BeforeEach + void setup() { + sourceMap = new HashMap<>(); + sourceMap.put("Sunday", 0); + sourceMap.put("Monday", 1); + sourceMap.put("Tuesday", 2); + sourceMap.put("Wednesday", 3); + sourceMap.put("Thursday", 4); + sourceMap.put("Friday", 5); + sourceMap.put("Saturday", 6); + } + + @Test + void givenSourceMap_whenUsingForLoop_returnsInvertedMap() { + Map inversedMap = InvertHashMapExample.invertMapUsingForLoop(sourceMap); + + assertNotNull(inversedMap); + assertEquals(sourceMap.size(), inversedMap.size()); + assertEquals("Monday", inversedMap.get(1)); + } + + @Test + void givenSourceMap_whenUsingStreams_returnsInvertedMap() { + Map inversedMap = InvertHashMapExample.invertMapUsingStreams(sourceMap); + + assertNotNull(inversedMap); + assertEquals(sourceMap.size(), inversedMap.size()); + assertEquals("Monday", inversedMap.get(1)); + } + + @Test + void givenSourceMap_whenUsingMapper_returnsInvertedMap() { + Map inversedMap = InvertHashMapExample.invertMapUsingMapper(sourceMap); + + assertNotNull(inversedMap); + assertEquals(sourceMap.size(), inversedMap.size()); + assertEquals("Monday", inversedMap.get(1)); + } + + @Test + void givenSourceMapWithDuplicateValues_whenUsingGroupBy_returnsInvertedMap() { + sourceMap.put("MONDAY", 1); + Map> inversedMap = InvertHashMapExample.invertMapUsingGroupingBy(sourceMap); + + assertNotNull(inversedMap); + assertNotEquals(sourceMap.size(), inversedMap.size()); // duplicate keys are merged now + assertEquals(2, inversedMap.get(1).size()); + assertTrue(inversedMap.get(1).contains("Monday")); + assertTrue(inversedMap.get(1).contains("MONDAY")); + } + +} diff --git a/java-native/src/main/cpp/com_baeldung_jni_RegisterNativesHelloWorldJNI.cpp b/java-native/src/main/cpp/com_baeldung_jni_RegisterNativesHelloWorldJNI.cpp new file mode 100644 index 0000000000..37adb35333 --- /dev/null +++ b/java-native/src/main/cpp/com_baeldung_jni_RegisterNativesHelloWorldJNI.cpp @@ -0,0 +1,21 @@ +#include "com_baeldung_jni_RegisterNativesHelloWorldJNI.h" +#include + + +JNIEXPORT jstring JNICALL hello (JNIEnv* env, jobject thisObject) { + std::string hello = "Hello from registered native C++ !!"; + std::cout << hello << std::endl; + return env->NewStringUTF(hello.c_str()); +} + +static JNINativeMethod methods[] = { + {"sayHello", "()Ljava/lang/String;", (void*) &hello }, +}; + + +JNIEXPORT void JNICALL Java_com_baeldung_jni_RegisterNativesHelloWorldJNI_register (JNIEnv* env, jobject thsObject) { + jclass clazz = env->FindClass("com/baeldung/jni/RegisterNativesHelloWorldJNI"); + + (env)->RegisterNatives(clazz, methods, sizeof(methods)/sizeof(methods[0])); +} + diff --git a/java-native/src/main/cpp/com_baeldung_jni_RegisterNativesHelloWorldJNI.h b/java-native/src/main/cpp/com_baeldung_jni_RegisterNativesHelloWorldJNI.h new file mode 100644 index 0000000000..bcca780208 --- /dev/null +++ b/java-native/src/main/cpp/com_baeldung_jni_RegisterNativesHelloWorldJNI.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class com_baeldung_jni_RegisterNativesHelloWorldJNI */ + +#ifndef _Included_com_baeldung_jni_RegisterNativesHelloWorldJNI +#define _Included_com_baeldung_jni_RegisterNativesHelloWorldJNI +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: com_baeldung_jni_RegisterNativesHelloWorldJNI + * Method: sayHello + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_baeldung_jni_RegisterNativesHelloWorldJNI_sayHello + (JNIEnv *, jobject); + +/* + * Class: com_baeldung_jni_RegisterNativesHelloWorldJNI + * Method: register + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_baeldung_jni_RegisterNativesHelloWorldJNI_register + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/java-native/src/main/cpp/generateNativeLibMac.sh b/java-native/src/main/cpp/generateNativeLibMac.sh old mode 100644 new mode 100755 index d11dcc7c01..834a07acc8 --- a/java-native/src/main/cpp/generateNativeLibMac.sh +++ b/java-native/src/main/cpp/generateNativeLibMac.sh @@ -3,4 +3,5 @@ g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin com_baeldung_jni_HelloWorldJNI.cpp -o com_baeldung_jni_HelloWorldJNI.o g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin com_baeldung_jni_ExampleParametersJNI.cpp -o com_baeldung_jni_ExampleParametersJNI.o g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin com_baeldung_jni_ExampleObjectsJNI.cpp -o com_baeldung_jni_ExampleObjectsJNI.o -g++ -dynamiclib -o ../../../native/macos/libnative.dylib com_baeldung_jni_HelloWorldJNI.o com_baeldung_jni_ExampleParametersJNI.o com_baeldung_jni_ExampleObjectsJNI.o -lc \ No newline at end of file +g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin com_baeldung_jni_RegisterNativesHelloWorldJNI.cpp -o com_baeldung_jni_RegisterNativesHelloWorldJNI.o +g++ -dynamiclib -o ../../../native/macos/libnative.dylib com_baeldung_jni_HelloWorldJNI.o com_baeldung_jni_ExampleParametersJNI.o com_baeldung_jni_ExampleObjectsJNI.o com_baeldung_jni_RegisterNativesHelloWorldJNI.o -lc \ No newline at end of file diff --git a/java-native/src/main/java/com/baeldung/jni/RegisterNativesHelloWorldJNI.java b/java-native/src/main/java/com/baeldung/jni/RegisterNativesHelloWorldJNI.java new file mode 100644 index 0000000000..c5461aafef --- /dev/null +++ b/java-native/src/main/java/com/baeldung/jni/RegisterNativesHelloWorldJNI.java @@ -0,0 +1,18 @@ +package com.baeldung.jni; + +public class RegisterNativesHelloWorldJNI { + + static { + System.loadLibrary("native"); + } + + public static void main(String[] args) { + RegisterNativesHelloWorldJNI helloWorldJNI = new RegisterNativesHelloWorldJNI(); + helloWorldJNI.register(); + helloWorldJNI.sayHello(); + } + + public native String sayHello(); + + public native void register(); +} diff --git a/java-native/src/test/java/com/baeldung/jni/registernatives/JNIRegisterNativesManualTest.java b/java-native/src/test/java/com/baeldung/jni/registernatives/JNIRegisterNativesManualTest.java new file mode 100644 index 0000000000..96e15bd822 --- /dev/null +++ b/java-native/src/test/java/com/baeldung/jni/registernatives/JNIRegisterNativesManualTest.java @@ -0,0 +1,26 @@ +package com.baeldung.jni.registernatives; + +import com.baeldung.jni.RegisterNativesHelloWorldJNI; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class JNIRegisterNativesManualTest { + @Before + public void setup() { + System.loadLibrary("native"); + } + + @Test + public void whenRegisteredNativeHelloWorld_thenOutputIsAsExpected() { + RegisterNativesHelloWorldJNI helloWorld = new RegisterNativesHelloWorldJNI(); + helloWorld.register(); + + String helloFromNative = helloWorld.sayHello(); + + assertNotNull(helloFromNative); + assertTrue(helloFromNative.equals("Hello from registered native C++ !!")); + } +} diff --git a/parent-spring-5/pom.xml b/parent-spring-5/pom.xml index 175e8d58f9..b2d581c9c1 100644 --- a/parent-spring-5/pom.xml +++ b/parent-spring-5/pom.xml @@ -24,7 +24,7 @@ - 5.3.13 + 5.3.16 5.6.0 1.5.10.RELEASE diff --git a/persistence-modules/hibernate-queries/src/main/java/com/baeldung/hibernate/scalarmethod/HibernateScalarExample.java b/persistence-modules/hibernate-queries/src/main/java/com/baeldung/hibernate/scalarmethod/HibernateScalarExample.java new file mode 100644 index 0000000000..69aaaae19d --- /dev/null +++ b/persistence-modules/hibernate-queries/src/main/java/com/baeldung/hibernate/scalarmethod/HibernateScalarExample.java @@ -0,0 +1,47 @@ +package com.baeldung.hibernate.scalarmethod; + +import java.util.List; + +import org.hibernate.Session; +import org.hibernate.type.StandardBasicTypes; + +public class HibernateScalarExample { + + private Session session; + + public HibernateScalarExample(Session session) { + this.session = session; + } + + public List fetchColumnWithNativeQuery() { + return session.createNativeQuery("SELECT * FROM Student student") + .list(); + } + + public List fetchColumnWithScalar() { + return session.createNativeQuery("SELECT * FROM Student student") + .addScalar("studentId", StandardBasicTypes.LONG) + .addScalar("name", StandardBasicTypes.STRING) + .addScalar("age", StandardBasicTypes.INTEGER) + .list(); + } + + public List fetchLimitedColumnWithScalar() { + return session.createNativeQuery("SELECT * FROM Student student") + .addScalar("name", StandardBasicTypes.STRING) + .list(); + } + + public List fetchColumnWithOverloadedScalar() { + return session.createNativeQuery("SELECT * FROM Student student") + .addScalar("name", StandardBasicTypes.STRING) + .addScalar("age") + .list(); + } + + public Integer fetchAvgAgeWithScalar() { + return (Integer) session.createNativeQuery("SELECT AVG(age) as avgAge FROM Student student") + .addScalar("avgAge") + .uniqueResult(); + } +} diff --git a/persistence-modules/hibernate-queries/src/test/java/com/baeldung/hibernate/scalarmethod/HibernateScalarExampleUnitTest.java b/persistence-modules/hibernate-queries/src/test/java/com/baeldung/hibernate/scalarmethod/HibernateScalarExampleUnitTest.java new file mode 100644 index 0000000000..ca01c5cb84 --- /dev/null +++ b/persistence-modules/hibernate-queries/src/test/java/com/baeldung/hibernate/scalarmethod/HibernateScalarExampleUnitTest.java @@ -0,0 +1,83 @@ +package com.baeldung.hibernate.scalarmethod; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.List; +import java.util.Random; + +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.baeldung.hibernate.HibernateUtil; +import com.baeldung.hibernate.pojo.Student; + +public class HibernateScalarExampleUnitTest { + + private static Session session; + private static Transaction transaction; + private static final int DATA_SIZE = 50000; + private static HibernateScalarExample scalarExample; + + @BeforeClass + public static void setUp() throws IOException { + session = HibernateUtil.getSessionFactory().openSession(); + transaction = session.beginTransaction(); + scalarExample = new HibernateScalarExample(session); + session.createNativeQuery("delete from Student").executeUpdate(); + for (int i = 0; i < DATA_SIZE; i++) { + Student student = new Student("John-" + i, generateRandomAge(5, 24)); + session.persist(student); + } + transaction.commit(); + transaction = session.beginTransaction(); + } + + @AfterClass + public static void tearDown() { + transaction.rollback(); + session.close(); + } + + @Test + public void givenNativeQuery_whenNoScalarUsed_ThenFetchAll() { + List list = scalarExample.fetchColumnWithNativeQuery(); + assertEquals(DATA_SIZE, list.size()); + } + + @Test + public void givenNativeQuery_whenScalarUsed_ThenFetchAll() { + List list = scalarExample.fetchColumnWithScalar(); + assertEquals(DATA_SIZE, list.size()); + } + + @Test + public void givenNativeQuery_whenScalarUsed_ThenFetchLimitedColumns() { + List list = scalarExample.fetchLimitedColumnWithScalar(); + for (String colValue : list) { + assertTrue(colValue.startsWith("John")); + } + } + + @Test + public void givenNativeQuery_whenScalarUsedForSingleResult_ThenSingleValueReturned() { + List list = scalarExample.fetchColumnWithOverloadedScalar(); + for (Object[] colArray : list) { + assertEquals(2, colArray.length); + } + } + + @Test + public void whenScalarUsedForAvgAge_ThenSingleValueReturned() { + Integer avgAge = scalarExample.fetchAvgAgeWithScalar(); + assertEquals(true, (avgAge >= 5 && avgAge <= 24)); + } + + private static int generateRandomAge(int min, int max) { + return new Random().nextInt(max - min + 1) + min; + } +} diff --git a/persistence-modules/spring-data-jdbc/README.md b/persistence-modules/spring-data-jdbc/README.md index 0e54d0ba88..b9ff9417a9 100644 --- a/persistence-modules/spring-data-jdbc/README.md +++ b/persistence-modules/spring-data-jdbc/README.md @@ -1,3 +1,4 @@ ### Relevant Articles: +- [Configure and Use Multiple DataSources in Spring Boot](https://www.baeldung.com/spring-boot-configure-multiple-datasources) - [Introduction to Spring Data JDBC](https://www.baeldung.com/spring-data-jdbc-intro) diff --git a/spring-boot-modules/pom.xml b/spring-boot-modules/pom.xml index 4925530a35..c1a96a0604 100644 --- a/spring-boot-modules/pom.xml +++ b/spring-boot-modules/pom.xml @@ -51,6 +51,7 @@ spring-boot-libraries spring-boot-libraries-2 + spring-boot-libraries-comparison spring-boot-logging-log4j2 spring-boot-multiple-datasources spring-boot-mvc diff --git a/spring-boot-modules/spring-boot-data-2/README.md b/spring-boot-modules/spring-boot-data-2/README.md index 875ee502b4..b99356492b 100644 --- a/spring-boot-modules/spring-boot-data-2/README.md +++ b/spring-boot-modules/spring-boot-data-2/README.md @@ -1,5 +1,6 @@ ### Relevant Articles: +- [HttpMessageNotWritableException: No Converter for [class …] With Preset Content-Type](https://www.baeldung.com/spring-no-converter-with-preset) - [Spring Boot: Customize the Jackson ObjectMapper](https://www.baeldung.com/spring-boot-customize-jackson-objectmapper) - [“HttpMessageNotWritableException: No converter found for return value of type”](https://www.baeldung.com/spring-no-converter-found) - [Creating a Read-Only Repository with Spring Data](https://www.baeldung.com/spring-data-read-only-repository) diff --git a/spring-boot-modules/spring-boot-libraries-comparison/README.md b/spring-boot-modules/spring-boot-libraries-comparison/README.md new file mode 100644 index 0000000000..3efdac2a4c --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-comparison/README.md @@ -0,0 +1,7 @@ +## Spring Boot Libraries + +This module contains articles about various Spring Boot libraries Comparison + +### Relevant Articles: + +- [GraphQL vs REST](https://www.baeldung.com/graphql-vs-rest/) \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-libraries-comparison/pom.xml b/spring-boot-modules/spring-boot-libraries-comparison/pom.xml new file mode 100644 index 0000000000..59d0e75be3 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-comparison/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + spring-boot-libraries-comparison + + + com.baeldung.spring-boot-modules + spring-boot-modules + 1.0.0-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.data + spring-data-jpa + + + com.graphql-java + graphql-spring-boot-starter + ${graphql-spring-boot-starter.version} + + + com.graphql-java + graphql-java-tools + ${graphql-java-tools.version} + + + com.graphql-java + graphiql-spring-boot-starter + ${graphql-spring-boot-starter.version} + + + + + + 5.0.2 + 5.2.4 + + + \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/GraphqlVsRestApplication.java b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/GraphqlVsRestApplication.java new file mode 100644 index 0000000000..29a3ef1e0f --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/GraphqlVsRestApplication.java @@ -0,0 +1,19 @@ +package com.baeldung.graphqlvsrest; + +import com.baeldung.graphqlvsrest.configuration.GraphqlConfiguration; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.context.annotation.Import; + +@SpringBootApplication +@Import(GraphqlConfiguration.class) +@EnableAutoConfiguration(exclude = {SecurityAutoConfiguration.class}) +public class GraphqlVsRestApplication { + + public static void main(String[] args) { + SpringApplication.run(GraphqlVsRestApplication.class, args); + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/configuration/GraphqlConfiguration.java b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/configuration/GraphqlConfiguration.java new file mode 100644 index 0000000000..c100a03143 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/configuration/GraphqlConfiguration.java @@ -0,0 +1,35 @@ +package com.baeldung.graphqlvsrest.configuration; + +import com.baeldung.graphqlvsrest.repository.OrderRepository; +import com.baeldung.graphqlvsrest.resolver.Mutation; +import com.baeldung.graphqlvsrest.resolver.ProductResolver; +import com.baeldung.graphqlvsrest.resolver.Query; +import com.baeldung.graphqlvsrest.repository.ProductRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class GraphqlConfiguration { + + @Autowired + ProductRepository productRepository; + + @Autowired + OrderRepository orderRepository; + + @Bean + public Query query() { + return new Query(productRepository); + } + + @Bean + public ProductResolver productResolver(){ + return new ProductResolver(orderRepository); + } + + @Bean + public Mutation mutation() { + return new Mutation(productRepository); + } +} diff --git a/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/controller/OrderController.java b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/controller/OrderController.java new file mode 100644 index 0000000000..14f0468bbd --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/controller/OrderController.java @@ -0,0 +1,25 @@ +package com.baeldung.graphqlvsrest.controller; + +import com.baeldung.graphqlvsrest.entity.Order; +import com.baeldung.graphqlvsrest.entity.Product; +import com.baeldung.graphqlvsrest.model.ProductModel; +import com.baeldung.graphqlvsrest.repository.OrderRepository; +import com.baeldung.graphqlvsrest.repository.ProductRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("order") +public class OrderController { + + @Autowired + OrderRepository orderRepository; + + @GetMapping() + public List getOrders(@RequestParam("product-id") Integer productId){ + return orderRepository.getOrdersByProduct(productId); + } +} diff --git a/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/controller/ProductController.java b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/controller/ProductController.java new file mode 100644 index 0000000000..2fdee8765a --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/controller/ProductController.java @@ -0,0 +1,38 @@ +package com.baeldung.graphqlvsrest.controller; + +import com.baeldung.graphqlvsrest.entity.Product; +import com.baeldung.graphqlvsrest.model.ProductModel; +import com.baeldung.graphqlvsrest.repository.ProductRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("product") +public class ProductController { + + @Autowired + ProductRepository productRepository; + + @GetMapping + public List getProducts(Pageable pageable){ + return productRepository.getProducts(pageable.getPageSize(), pageable.getPageNumber()); + } + + @GetMapping("/{product-id}") + public Product getProducts(@PathVariable("product-id") Integer productId){ + return productRepository.getProduct(productId); + } + + @PostMapping + public Product save(@RequestBody ProductModel productModel){ + return productRepository.save(productModel); + } + + @PutMapping("/{product-id}") + public Product update(@PathVariable("product-id") Integer productId, @RequestBody ProductModel productModel){ + return productRepository.update(productId, productModel); + } +} diff --git a/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/entity/Order.java b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/entity/Order.java new file mode 100644 index 0000000000..89606e9897 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/entity/Order.java @@ -0,0 +1,58 @@ +package com.baeldung.graphqlvsrest.entity; + +public class Order { + private Integer id; + private Integer product_id; + private String customer_uuid; + private String status; + private String address; + private String creation_date; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getProduct_id() { + return product_id; + } + + public void setProduct_id(Integer product_id) { + this.product_id = product_id; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getCustomer_uuid() { + return customer_uuid; + } + + public void setCustomer_uuid(String customer_uuid) { + this.customer_uuid = customer_uuid; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getCreation_date() { + return creation_date; + } + + public void setCreation_date(String creation_date) { + this.creation_date = creation_date; + } +} diff --git a/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/entity/Product.java b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/entity/Product.java new file mode 100644 index 0000000000..2da9244c92 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/entity/Product.java @@ -0,0 +1,115 @@ +package com.baeldung.graphqlvsrest.entity; + +import com.baeldung.graphqlvsrest.model.ProductModel; + +import java.util.List; + +public class Product { + private Integer id; + private String name; + private String description; + private String status; + private String currency; + private Double price; + private List image_url; + private List video_url; + private Integer stock; + private Float average_rating; + + public Product(Integer id, ProductModel productModel) { + this.id = id; + this.name = productModel.getName(); + this.description = productModel.getDescription(); + this.currency = productModel.getCurrency(); + this.price = productModel.getPrice(); + this.stock = productModel.getStock(); + this.image_url = productModel.getImage_url(); + this.video_url = productModel.getVideo_url(); + this.average_rating = 0F; + this.status = productModel.getStatus(); + } + + public Product(){ + + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public List getImage_url() { + return image_url; + } + + public void setImage_url(List image_url) { + this.image_url = image_url; + } + + public List getVideo_url() { + return video_url; + } + + public void setVideo_url(List video_url) { + this.video_url = video_url; + } + + public Integer getStock() { + return stock; + } + + public void setStock(Integer stock) { + this.stock = stock; + } + + public Float getAverage_rating() { + return average_rating; + } + + public void setAverage_rating(Float average_rating) { + this.average_rating = average_rating; + } +} diff --git a/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/model/ProductModel.java b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/model/ProductModel.java new file mode 100644 index 0000000000..db7a3ba54e --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/model/ProductModel.java @@ -0,0 +1,92 @@ +package com.baeldung.graphqlvsrest.model; + +import java.util.List; + +public class ProductModel { + private String name; + private String description; + private String status; + private String currency; + private Double price; + private List image_url; + private List video_url; + private Integer stock; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public List getImage_url() { + return image_url; + } + + public void setImage_url(List image_url) { + this.image_url = image_url; + } + + public List getVideo_url() { + return video_url; + } + + public void setVideo_url(List video_url) { + this.video_url = video_url; + } + + public Integer getStock() { + return stock; + } + + public void setStock(Integer stock) { + this.stock = stock; + } + + @Override + public String toString() { + return "ProductModel{" + + "name='" + name + '\'' + + ", description='" + description + '\'' + + ", status='" + status + '\'' + + ", currency='" + currency + '\'' + + ", price=" + price + + ", image_url=" + image_url + + ", video_url=" + video_url + + ", stock=" + stock + + '}'; + } +} diff --git a/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/repository/OrderRepository.java b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/repository/OrderRepository.java new file mode 100644 index 0000000000..92cc288426 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/repository/OrderRepository.java @@ -0,0 +1,11 @@ +package com.baeldung.graphqlvsrest.repository; + +import com.baeldung.graphqlvsrest.entity.Order; +import com.baeldung.graphqlvsrest.entity.Product; +import com.baeldung.graphqlvsrest.model.ProductModel; + +import java.util.List; + +public interface OrderRepository { + List getOrdersByProduct(Integer productId); +} diff --git a/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/repository/ProductRepository.java b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/repository/ProductRepository.java new file mode 100644 index 0000000000..c0fb12327f --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/repository/ProductRepository.java @@ -0,0 +1,14 @@ +package com.baeldung.graphqlvsrest.repository; + +import com.baeldung.graphqlvsrest.entity.Product; +import com.baeldung.graphqlvsrest.model.ProductModel; + +import java.util.List; + +public interface ProductRepository { + List getProducts(Integer pageSize, Integer pageNumber); + Product getProduct(Integer id); + Product save(ProductModel productModel); + Product update(Integer productId, ProductModel productModel); + +} diff --git a/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/repository/impl/OrderRepositoryImpl.java b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/repository/impl/OrderRepositoryImpl.java new file mode 100644 index 0000000000..e4f316c865 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/repository/impl/OrderRepositoryImpl.java @@ -0,0 +1,36 @@ +package com.baeldung.graphqlvsrest.repository.impl; + +import com.baeldung.graphqlvsrest.entity.Order; +import com.baeldung.graphqlvsrest.repository.OrderRepository; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +@Repository +public class OrderRepositoryImpl implements OrderRepository { + + private static List orderList = new ArrayList<>(); + + public OrderRepositoryImpl() { + for (int i = 1; i <= 100; i++){ + Order order = new Order(); + order.setId(i); + order.setProduct_id(i%10); + order.setAddress(UUID.randomUUID().toString()); + order.setCustomer_uuid(UUID.randomUUID().toString()); + order.setCreation_date(new Date(System.currentTimeMillis()).toString()); + order.setStatus("Delivered"); + orderList.add(order); + } + } + + + @Override + public List getOrdersByProduct(Integer productId) { + return orderList.stream().filter(order -> order.getProduct_id().equals(productId)).collect(Collectors.toList()); + } +} diff --git a/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/repository/impl/ProductRepositoryImpl.java b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/repository/impl/ProductRepositoryImpl.java new file mode 100644 index 0000000000..845472faea --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/repository/impl/ProductRepositoryImpl.java @@ -0,0 +1,74 @@ +package com.baeldung.graphqlvsrest.repository.impl; + +import com.baeldung.graphqlvsrest.entity.Product; +import com.baeldung.graphqlvsrest.model.ProductModel; +import com.baeldung.graphqlvsrest.repository.ProductRepository; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Repository +public class ProductRepositoryImpl implements ProductRepository { + + private static List productList = new ArrayList<>(); + + public ProductRepositoryImpl() { + for (int i = 1; i <= 10; i++){ + Product product = new Product(); + product.setId(i); + product.setName(String.format("Product %d", i)); + product.setDescription(String.format("Product %d description", i)); + product.setCurrency(String.format("Product %d currency", i)); + product.setPrice(Double.valueOf(i^2)); + product.setStock(10); + product.setAverage_rating(0F); + product.setImage_url(Arrays.asList(String.format("www.baeldung.com/imageurl/%d", i))); + product.setVideo_url(Arrays.asList(String.format("www.baeldung.com/videourl/%d", i))); + productList.add(product); + } + } + + @Override + public List getProducts(Integer pageSize, Integer pageNumber) { + return productList.stream().skip(pageSize*pageNumber).limit(pageSize).collect(Collectors.toList()); + } + + @Override + public Product getProduct(Integer id) { + return productList.stream().filter(product -> product.getId().equals(id)).findFirst().orElse(null); + } + + @Override + public Product save(ProductModel productModel) { + Product product = new Product(productList.size()+1, productModel); + productList.add(product); + return product; + } + + @Override + public Product update(Integer productId, ProductModel productModel) { + Product product = getProduct(productId); + if (product != null){ + update(product, productModel); + } + return product; + } + + private void update(Product product, ProductModel productModel){ + if (productModel != null) { + System.out.println(productModel.toString()); + Optional.ofNullable(productModel.getName()).ifPresent(product::setName); + Optional.ofNullable(productModel.getDescription()).ifPresent(product::setDescription); + Optional.ofNullable(productModel.getCurrency()).ifPresent(product::setCurrency); + Optional.ofNullable(productModel.getImage_url()).ifPresent(product::setImage_url); + Optional.ofNullable(productModel.getStock()).ifPresent(product::setStock); + Optional.ofNullable(productModel.getStatus()).ifPresent(product::setStatus); + Optional.ofNullable(productModel.getVideo_url()).ifPresent(product::setVideo_url); + Optional.ofNullable(productModel.getPrice()).ifPresent(product::setPrice); + } + } +} diff --git a/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/resolver/Mutation.java b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/resolver/Mutation.java new file mode 100644 index 0000000000..3d643f97e6 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/resolver/Mutation.java @@ -0,0 +1,22 @@ +package com.baeldung.graphqlvsrest.resolver; + +import com.baeldung.graphqlvsrest.entity.Product; +import com.baeldung.graphqlvsrest.model.ProductModel; +import com.baeldung.graphqlvsrest.repository.ProductRepository; +import com.coxautodev.graphql.tools.GraphQLMutationResolver; + +public class Mutation implements GraphQLMutationResolver { + + private ProductRepository productRepository; + public Mutation(ProductRepository productRepository){ + this.productRepository = productRepository; + } + + public Product saveProduct(ProductModel productModel) { + return productRepository.save(productModel); + } + + public Product updateProduct(Integer productId, ProductModel productModel) { + return productRepository.update(productId, productModel); + } +} diff --git a/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/resolver/ProductResolver.java b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/resolver/ProductResolver.java new file mode 100644 index 0000000000..f20b8d5920 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/resolver/ProductResolver.java @@ -0,0 +1,18 @@ +package com.baeldung.graphqlvsrest.resolver; + +import com.baeldung.graphqlvsrest.entity.Order; +import com.baeldung.graphqlvsrest.entity.Product; +import com.baeldung.graphqlvsrest.repository.OrderRepository; +import com.coxautodev.graphql.tools.GraphQLResolver; + +import java.util.List; + +public class ProductResolver implements GraphQLResolver { + private OrderRepository orderRepository; + public ProductResolver(OrderRepository orderRepository){ + this.orderRepository = orderRepository; + } + public List getOrders(Product product){ + return orderRepository.getOrdersByProduct(product.getId()); + } +} diff --git a/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/resolver/Query.java b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/resolver/Query.java new file mode 100644 index 0000000000..0d218261b2 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-comparison/src/main/java/com/baeldung/graphqlvsrest/resolver/Query.java @@ -0,0 +1,27 @@ +package com.baeldung.graphqlvsrest.resolver; + +import com.baeldung.graphqlvsrest.entity.Order; +import com.baeldung.graphqlvsrest.entity.Product; +import com.baeldung.graphqlvsrest.repository.OrderRepository; +import com.baeldung.graphqlvsrest.repository.ProductRepository; +import com.coxautodev.graphql.tools.GraphQLQueryResolver; + +import java.util.List; + +public class Query implements GraphQLQueryResolver { + + private ProductRepository productRepository; + public Query(ProductRepository productRepository){ + this.productRepository = productRepository; + } + + public List getProducts(int pageSize, int pageNumber) { + return productRepository.getProducts(pageSize, pageNumber); + } + + public Product getProduct(int id) { + return productRepository.getProduct(id); + } + + +} diff --git a/spring-boot-modules/spring-boot-libraries-comparison/src/main/resources/application.properties b/spring-boot-modules/spring-boot-libraries-comparison/src/main/resources/application.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spring-boot-modules/spring-boot-libraries-comparison/src/main/resources/graphql/schema.graphqls b/spring-boot-modules/spring-boot-libraries-comparison/src/main/resources/graphql/schema.graphqls new file mode 100644 index 0000000000..2709510d72 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-comparison/src/main/resources/graphql/schema.graphqls @@ -0,0 +1,57 @@ +type Product { + id: ID + name: String! + description: String + status: String + currency: String! + price: Float + image_url: [String] + video_url: [String] + stock: Int + average_rating: Float + orders:[Order] +} + +type Order{ + id:ID + product_id:Int + customer_uuid:String + address:String + status:String + creation_date:String +} + +input ProductModel { + name: String! + description: String + status: String + currency: String! + price: Float + image_url: [String] + video_url: [String] + stock: Int +} + +input ProductUpdateModel { + name: String + description: String + status: String + currency: String + price: Float + image_url: [String] + video_url: [String] + stock: Int +} + + +# The Root Query for the application +type Query { + products(size: Int, page: Int): [Product]! + product(id: Int): Product! +} + +# The Root Mutation for the application +type Mutation { + saveProduct(product: ProductModel) : Product! + updateProduct(id: Int, product: ProductUpdateModel) : Product! +} diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/AntMatchersExampleApplication.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/AntMatchersExampleApplication.java new file mode 100644 index 0000000000..0a7828eff4 --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/AntMatchersExampleApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.antmatchers; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AntMatchersExampleApplication { + + public static void main(String[] args) { + SpringApplication.run(AntMatchersExampleApplication.class, args); + } + +} diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/config/SecurityConfiguration.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/config/SecurityConfiguration.java new file mode 100644 index 0000000000..43e8c73bf0 --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/config/SecurityConfiguration.java @@ -0,0 +1,39 @@ +package com.baeldung.antmatchers.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class SecurityConfiguration extends WebSecurityConfigurerAdapter { + + @Override + public void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.inMemoryAuthentication() + .withUser("admin").password(passwordEncoder().encode("password")).roles("USER", "ADMIN") + .and() + .withUser("user").password(passwordEncoder().encode("password")).roles("USER"); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .antMatchers("/products/**").permitAll() + .and() + .authorizeRequests() + .antMatchers("/customers/**").hasRole("ADMIN") + .anyRequest().authenticated() + .and() + .httpBasic(); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/controllers/CustomerController.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/controllers/CustomerController.java new file mode 100644 index 0000000000..a031449797 --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/controllers/CustomerController.java @@ -0,0 +1,15 @@ +package com.baeldung.antmatchers.controllers; + +import com.baeldung.antmatchers.dtos.Customer; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class CustomerController { + + @GetMapping("/customers/{id}") + public Customer getCustomerById(@PathVariable("id") String id) { + return new Customer("Customer 1", "Address 1", "Phone 1"); + } +} diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/controllers/ProductController.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/controllers/ProductController.java new file mode 100644 index 0000000000..2968f3c025 --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/controllers/ProductController.java @@ -0,0 +1,21 @@ +package com.baeldung.antmatchers.controllers; + +import com.baeldung.antmatchers.dtos.Product; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@RestController +public class ProductController { + + @GetMapping("/products") + public List getProducts() { + return new ArrayList<>(Arrays.asList( + new Product("Product 1", "Description 1", 1.0), + new Product("Product 2", "Description 2", 2.0) + )); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/dtos/Customer.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/dtos/Customer.java new file mode 100644 index 0000000000..64bd95b095 --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/dtos/Customer.java @@ -0,0 +1,39 @@ +package com.baeldung.antmatchers.dtos; + +import java.io.Serializable; + +public class Customer implements Serializable { + private String name; + private String address; + private String phone; + + public Customer(String name, String address, String phone) { + this.name = name; + this.address = address; + this.phone = phone; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } +} diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/dtos/Product.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/dtos/Product.java new file mode 100644 index 0000000000..84073ee8c0 --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/antmatchers/dtos/Product.java @@ -0,0 +1,39 @@ +package com.baeldung.antmatchers.dtos; + +import java.io.Serializable; + +public class Product implements Serializable { + private String name; + private String description; + private double price; + + public Product(String name, String description, double price) { + this.name = name; + this.description = description; + this.price = price; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public double getPrice() { + return price; + } + + public void setPrice(double price) { + this.price = price; + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/antmatchers/controllers/CustomerControllerTest.java b/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/antmatchers/controllers/CustomerControllerTest.java new file mode 100644 index 0000000000..de13dc0aea --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/antmatchers/controllers/CustomerControllerTest.java @@ -0,0 +1,38 @@ +package com.baeldung.antmatchers.controllers; + +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@WebMvcTest(value = CustomerController.class) +class CustomerControllerIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Test + void getCustomerByIdUnauthorized() throws Exception { + mockMvc.perform(get("/customers/1")).andExpect(status().isUnauthorized()); + } + + @Test + void getCustomerByIdForbidden() throws Exception { + mockMvc.perform(get("/customers/1").with(user("user").roles("USER"))) + .andExpect(status().isForbidden()); + } + + @Test + void getCustomerByIdOk() throws Exception { + mockMvc.perform(get("/customers/1").with(user("admin").roles("ADMIN"))) + .andExpect(status().isOk()); + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/antmatchers/controllers/ProductControllerTest.java b/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/antmatchers/controllers/ProductControllerTest.java new file mode 100644 index 0000000000..370e5f1597 --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/antmatchers/controllers/ProductControllerTest.java @@ -0,0 +1,25 @@ +package com.baeldung.antmatchers.controllers; + +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@WebMvcTest(value = ProductController.class) +class ProductControllerIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Test + void getProducts() throws Exception { + mockMvc.perform(get("/products")) + .andExpect(status().isOk()); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-swagger/generated/swagger-ui/swagger.json b/spring-boot-modules/spring-boot-swagger/generated/swagger-ui/swagger.json deleted file mode 100644 index 4ebe165384..0000000000 --- a/spring-boot-modules/spring-boot-swagger/generated/swagger-ui/swagger.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "swagger" : "2.0", - "info" : { - "description" : "This is a Baeldung Document Enum Sample Code", - "version" : "v1", - "title" : "Baeldung - Document Enum", - "contact" : { - "name" : "Parikshit Murria", - "email" : "pmurria@baeldung.com" - }, - "license" : { - "name" : "Apache 2.0", - "url" : "https://www.apache.org/licenses/LICENSE-2.0.html" - } - }, - "host" : "baeldung.com", - "basePath" : "/api", - "schemes" : [ "http", "https" ], - "paths" : { - "/hire" : { - "post" : { - "summary" : "This method is used to hire employee with a specific role", - "description" : "", - "operationId" : "hireEmployee", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "description" : "role", - "required" : true, - "schema" : { - "$ref" : "#/definitions/Employee" - } - } ], - "responses" : { - "200" : { - "description" : "successful operation", - "schema" : { - "type" : "string" - } - } - } - } - } - }, - "definitions" : { - "Employee" : { - "type" : "object", - "properties" : { - "role" : { - "type" : "string", - "enum" : [ "Engineer", "Clerk", "Driver", "Janitor" ] - } - } - } - } -} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-swagger/pom.xml b/spring-boot-modules/spring-boot-swagger/pom.xml index 2c0e52b671..b6ed50534e 100644 --- a/spring-boot-modules/spring-boot-swagger/pom.xml +++ b/spring-boot-modules/spring-boot-swagger/pom.xml @@ -63,7 +63,7 @@ Apache 2.0 - generated/swagger-ui + ${basedir}/target/swagger-ui diff --git a/spring-cloud/spring-cloud-gateway/pom.xml b/spring-cloud/spring-cloud-gateway/pom.xml index dcad2b7da6..a352bbd4e4 100644 --- a/spring-cloud/spring-cloud-gateway/pom.xml +++ b/spring-cloud/spring-cloud-gateway/pom.xml @@ -177,5 +177,21 @@ + + + gateway-url-rewrite + + + + org.springframework.boot + spring-boot-maven-plugin + + com.baeldung.springcloudgateway.rewrite.URLRewriteGatewayApplication + -Dspring.profiles.active=url-rewrite + + + + + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-gateway/src/main/java/com/baeldung/springcloudgateway/rewrite/URLRewriteGatewayApplication.java b/spring-cloud/spring-cloud-gateway/src/main/java/com/baeldung/springcloudgateway/rewrite/URLRewriteGatewayApplication.java new file mode 100644 index 0000000000..46541b4826 --- /dev/null +++ b/spring-cloud/spring-cloud-gateway/src/main/java/com/baeldung/springcloudgateway/rewrite/URLRewriteGatewayApplication.java @@ -0,0 +1,25 @@ +/** + * + */ +package com.baeldung.springcloudgateway.rewrite; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.cloud.gateway.filter.GatewayFilter; + +import reactor.core.publisher.Mono; + +/** + * @author Baeldung + * + */ +@SpringBootApplication +public class URLRewriteGatewayApplication { + + public static void main(String[] args) { + new SpringApplicationBuilder(URLRewriteGatewayApplication.class) + .profiles("url-rewrite") + .run(args); + } + +} diff --git a/spring-cloud/spring-cloud-gateway/src/main/java/com/baeldung/springcloudgateway/rewrite/routes/DynamicRewriteRoute.java b/spring-cloud/spring-cloud-gateway/src/main/java/com/baeldung/springcloudgateway/rewrite/routes/DynamicRewriteRoute.java new file mode 100644 index 0000000000..29d40d7021 --- /dev/null +++ b/spring-cloud/spring-cloud-gateway/src/main/java/com/baeldung/springcloudgateway/rewrite/routes/DynamicRewriteRoute.java @@ -0,0 +1,43 @@ +package com.baeldung.springcloudgateway.rewrite.routes; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.http.server.reactive.ServerHttpRequest; + +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR; +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl; + +import java.util.Random; + +@Configuration +@Profile("url-rewrite") +public class DynamicRewriteRoute { + + @Value("${rewrite.backend.uri}") + private String backendUri; + private static Random rnd = new Random(); + + @Bean + public RouteLocator dynamicZipCodeRoute(RouteLocatorBuilder builder) { + return builder.routes() + .route("dynamicRewrite", r -> + r.path("/v2/zip/**") + .filters(f -> f.filter((exchange, chain) -> { + ServerHttpRequest req = exchange.getRequest(); + addOriginalRequestUrl(exchange, req.getURI()); + String path = req.getURI().getRawPath(); + String newPath = path.replaceAll( + "/v2/zip/(?.*)", + "/api/zip/${zipcode}-" + String.format("%03d", rnd.nextInt(1000))); + ServerHttpRequest request = req.mutate().path(newPath).build(); + exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, request.getURI()); + return chain.filter(exchange.mutate().request(request).build()); + })) + .uri(backendUri)) + .build(); + } +} diff --git a/spring-cloud/spring-cloud-gateway/src/main/java/com/baeldung/springcloudgateway/webfilters/WebFilterGatewayApplication.java b/spring-cloud/spring-cloud-gateway/src/main/java/com/baeldung/springcloudgateway/webfilters/WebFilterGatewayApplication.java index 852e5cadba..9e212cc4bf 100644 --- a/spring-cloud/spring-cloud-gateway/src/main/java/com/baeldung/springcloudgateway/webfilters/WebFilterGatewayApplication.java +++ b/spring-cloud/spring-cloud-gateway/src/main/java/com/baeldung/springcloudgateway/webfilters/WebFilterGatewayApplication.java @@ -8,7 +8,7 @@ public class WebFilterGatewayApplication { public static void main(String[] args) { new SpringApplicationBuilder(WebFilterGatewayApplication.class) - .profiles("webfilters") + .profiles("url-rewrite") .run(args); } diff --git a/spring-cloud/spring-cloud-gateway/src/main/resources/application-nosecurity.yml b/spring-cloud/spring-cloud-gateway/src/main/resources/application-nosecurity.yml new file mode 100644 index 0000000000..40a52ded0f --- /dev/null +++ b/spring-cloud/spring-cloud-gateway/src/main/resources/application-nosecurity.yml @@ -0,0 +1,8 @@ +# Enable this profile to disable security +spring: + autoconfigure: + exclude: + - org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration + - org.springframework.boot.actuate.autoconfigure.ManagementSecurityAutoConfiguration + - org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration + - org.springframework.boot.actuate.autoconfigure.security.reactive.ReactiveManagementWebSecurityAutoConfiguration \ No newline at end of file diff --git a/spring-cloud/spring-cloud-gateway/src/main/resources/application-url-rewrite.yml b/spring-cloud/spring-cloud-gateway/src/main/resources/application-url-rewrite.yml new file mode 100644 index 0000000000..b2656151db --- /dev/null +++ b/spring-cloud/spring-cloud-gateway/src/main/resources/application-url-rewrite.yml @@ -0,0 +1,11 @@ +spring: + cloud: + gateway: + routes: + - id: rewrite_v1 + uri: ${rewrite.backend.uri:http://example.com} + predicates: + - Path=/v1/customer/** + filters: + - RewritePath=/v1/customer/(?.*),/api/$\{segment} + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-gateway/src/test/java/com/baeldung/springcloudgateway/rewrite/URLRewriteGatewayApplicationLiveTest.java b/spring-cloud/spring-cloud-gateway/src/test/java/com/baeldung/springcloudgateway/rewrite/URLRewriteGatewayApplicationLiveTest.java new file mode 100644 index 0000000000..41fe37045c --- /dev/null +++ b/spring-cloud/spring-cloud-gateway/src/test/java/com/baeldung/springcloudgateway/rewrite/URLRewriteGatewayApplicationLiveTest.java @@ -0,0 +1,109 @@ +package com.baeldung.springcloudgateway.rewrite; + +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.OutputStream; +import java.net.InetSocketAddress; + +import org.junit.AfterClass; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.web.reactive.server.WebTestClient; + +import com.sun.net.httpserver.HttpServer; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@ActiveProfiles({ "nosecurity", "url-rewrite" }) +class URLRewriteGatewayApplicationLiveTest { + + // NOTE for Eclipse users: By default, Eclipse will complain about com.sun.** classes. + // To solve this issue, follow instructions available at the : + // https://stackoverflow.com/questions/13155734/eclipse-cant-recognize-com-sun-net-httpserver-httpserver-package + private static HttpServer mockServer; + private static Logger log = LoggerFactory.getLogger(URLRewriteGatewayApplicationLiveTest.class); + + // Create a running HttpServer that echoes back the request URL. + private static HttpServer startTestServer() { + + try { + log.info("[I26] Starting mock server"); + mockServer = HttpServer.create(); + mockServer.bind(new InetSocketAddress(0), 0); + mockServer.createContext("/api", (xchg) -> { + String uri = xchg.getRequestURI() + .toString(); + log.info("[I23] Backend called: uri={}", uri); + xchg.getResponseHeaders() + .add("Content-Type", "text/plain"); + xchg.sendResponseHeaders(200, 0); + OutputStream os = xchg.getResponseBody(); + os.write(uri.getBytes()); + os.flush(); + os.close(); + }); + + mockServer.start(); + InetSocketAddress localAddr = mockServer.getAddress(); + log.info("[I36] mock server started: local address={}", localAddr); + + return mockServer; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + + } + + // TIP: https://www.baeldung.com/spring-dynamicpropertysource + @DynamicPropertySource + static void registerBackendServer(DynamicPropertyRegistry registry) { + registry.add("rewrite.backend.uri", () -> { + HttpServer s = startTestServer(); + return "http://localhost:" + s.getAddress().getPort(); + }); + } + + @AfterClass + public static void stopMockBackend() throws Exception { + log.info("[I40] Shutdown mock http server"); + mockServer.stop(5); + } + + @LocalServerPort + private int localPort; + + @Test + void testWhenApiCall_thenRewriteSuccess(@Autowired WebTestClient webClient) { + webClient.get() + .uri("http://localhost:" + localPort + "/v1/customer/customer1") + .exchange() + .expectBody() + .consumeWith((result) -> { + String body = new String(result.getResponseBody()); + log.info("[I99] body={}", body); + assertEquals("/api/customer1", body); + }); + } + + @Test + void testWhenDslCall_thenRewriteSuccess(@Autowired WebTestClient webClient) { + webClient.get() + .uri("http://localhost:" + localPort + "/v2/zip/123456") + .exchange() + .expectBody() + .consumeWith((result) -> { + String body = new String(result.getResponseBody()); + log.info("[I99] body={}", body); + assertTrue(body.matches("/api/zip/123456-\\d{3}")); + }); + } + +} diff --git a/spring-cloud/spring-cloud-openfeign/pom.xml b/spring-cloud/spring-cloud-openfeign/pom.xml index 3e59f512db..480663eb1c 100644 --- a/spring-cloud/spring-cloud-openfeign/pom.xml +++ b/spring-cloud/spring-cloud-openfeign/pom.xml @@ -48,7 +48,15 @@ io.github.openfeign.form feign-form-spring - + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.security + spring-security-oauth2-client + org.springframework.boot spring-boot-starter-test diff --git a/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/client/PaymentClient.java b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/client/PaymentClient.java new file mode 100644 index 0000000000..91932fb7d8 --- /dev/null +++ b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/client/PaymentClient.java @@ -0,0 +1,16 @@ +package com.baeldung.cloud.openfeign.client; + +import com.baeldung.cloud.openfeign.model.Payment; +import com.baeldung.cloud.openfeign.oauthfeign.OAuthFeignConfig; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.util.List; + +@FeignClient(name = "payment-client", url = "http://localhost:8081/resource-server-jwt", configuration = OAuthFeignConfig.class) +public interface PaymentClient { + + @RequestMapping(value = "/payments", method = RequestMethod.GET) + List getPayments(); +} diff --git a/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/controller/PaymentController.java b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/controller/PaymentController.java new file mode 100644 index 0000000000..97c9437ce1 --- /dev/null +++ b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/controller/PaymentController.java @@ -0,0 +1,24 @@ +package com.baeldung.cloud.openfeign.controller; + +import com.baeldung.cloud.openfeign.client.PaymentClient; +import com.baeldung.cloud.openfeign.model.Payment; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +public class PaymentController { + + private final PaymentClient paymentClient; + + public PaymentController(PaymentClient paymentClient) { + this.paymentClient = paymentClient; + } + + @GetMapping("/payments") + public List getPayments() { + List payments = paymentClient.getPayments(); + return payments; + } +} diff --git a/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/model/Payment.java b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/model/Payment.java new file mode 100644 index 0000000000..c6d45bedbd --- /dev/null +++ b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/model/Payment.java @@ -0,0 +1,23 @@ +package com.baeldung.cloud.openfeign.model; + +public class Payment { + + private String id; + private double amount; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public double getAmount() { + return amount; + } + + public void setAmount(double amount) { + this.amount = amount; + } +} diff --git a/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/oauthfeign/OAuth2WebSecurityConfigurerAdapter.java b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/oauthfeign/OAuth2WebSecurityConfigurerAdapter.java new file mode 100644 index 0000000000..af60c3849b --- /dev/null +++ b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/oauthfeign/OAuth2WebSecurityConfigurerAdapter.java @@ -0,0 +1,19 @@ +package com.baeldung.cloud.openfeign.oauthfeign; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +public class OAuth2WebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .csrf() + .disable() + .oauth2Client(); + http + .authorizeRequests().anyRequest().permitAll(); + } +} diff --git a/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/oauthfeign/OAuthClientCredentialsFeignManager.java b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/oauthfeign/OAuthClientCredentialsFeignManager.java new file mode 100644 index 0000000000..5e958a93de --- /dev/null +++ b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/oauthfeign/OAuthClientCredentialsFeignManager.java @@ -0,0 +1,85 @@ +package com.baeldung.cloud.openfeign.oauthfeign; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; +import org.springframework.security.oauth2.client.registration.ClientRegistration; + +import java.util.Collection; +import java.util.Collections; + +import static java.util.Objects.isNull; + +public class OAuthClientCredentialsFeignManager { + + private static final Logger logger = LoggerFactory.getLogger(OAuthClientCredentialsFeignManager.class); + + private final OAuth2AuthorizedClientManager manager; + private final Authentication principal; + private final ClientRegistration clientRegistration; + + public OAuthClientCredentialsFeignManager(OAuth2AuthorizedClientManager manager, ClientRegistration clientRegistration) { + this.manager = manager; + this.clientRegistration = clientRegistration; + this.principal = createPrincipal(); + } + + private Authentication createPrincipal() { + return new Authentication() { + @Override + public Collection getAuthorities() { + return Collections.emptySet(); + } + + @Override + public Object getCredentials() { + return null; + } + + @Override + public Object getDetails() { + return null; + } + + @Override + public Object getPrincipal() { + return this; + } + + @Override + public boolean isAuthenticated() { + return false; + } + + @Override + public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { + } + + @Override + public String getName() { + return clientRegistration.getClientId(); + } + }; + } + + public String getAccessToken() { + try { + OAuth2AuthorizeRequest oAuth2AuthorizeRequest = OAuth2AuthorizeRequest + .withClientRegistrationId(clientRegistration.getRegistrationId()) + .principal(principal) + .build(); + OAuth2AuthorizedClient client = manager.authorize(oAuth2AuthorizeRequest); + if (isNull(client)) { + throw new IllegalStateException("client credentials flow on " + clientRegistration.getRegistrationId() + " failed, client is null"); + } + return client.getAccessToken().getTokenValue(); + } catch (Exception exp) { + logger.error("client credentials error " + exp.getMessage()); + } + return null; + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/oauthfeign/OAuthFeignConfig.java b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/oauthfeign/OAuthFeignConfig.java new file mode 100644 index 0000000000..70ca45d86e --- /dev/null +++ b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/oauthfeign/OAuthFeignConfig.java @@ -0,0 +1,46 @@ +package com.baeldung.cloud.openfeign.oauthfeign; + +import feign.RequestInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.oauth2.client.*; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; + +@Configuration +public class OAuthFeignConfig { + + public static final String CLIENT_REGISTRATION_ID = "keycloak"; + + private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService; + private final ClientRegistrationRepository clientRegistrationRepository; + + public OAuthFeignConfig(OAuth2AuthorizedClientService oAuth2AuthorizedClientService, + ClientRegistrationRepository clientRegistrationRepository) { + this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService; + this.clientRegistrationRepository = clientRegistrationRepository; + } + + @Bean + public RequestInterceptor requestInterceptor() { + ClientRegistration clientRegistration = clientRegistrationRepository.findByRegistrationId(CLIENT_REGISTRATION_ID); + OAuthClientCredentialsFeignManager clientCredentialsFeignManager = + new OAuthClientCredentialsFeignManager(authorizedClientManager(), clientRegistration); + return requestTemplate -> { + requestTemplate.header("Authorization", "Bearer " + clientCredentialsFeignManager.getAccessToken()); + }; + } + + @Bean + OAuth2AuthorizedClientManager authorizedClientManager() { + OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder() + .clientCredentials() + .build(); + + AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager = + new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, oAuth2AuthorizedClientService); + authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); + return authorizedClientManager; + } + +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-openfeign/src/main/resources/application.properties b/spring-cloud/spring-cloud-openfeign/src/main/resources/application.properties index 41bbbde2c3..7188b74c9b 100644 --- a/spring-cloud/spring-cloud-openfeign/src/main/resources/application.properties +++ b/spring-cloud/spring-cloud-openfeign/src/main/resources/application.properties @@ -1,3 +1,10 @@ +server.port=8085 +spring.main.allow-bean-definition-overriding=true spring.application.name= openfeign logging.level.com.baeldung.cloud.openfeign.client: DEBUG feign.hystrix.enabled=true + +spring.security.oauth2.client.registration.keycloak.authorization-grant-type=client_credentials +spring.security.oauth2.client.registration.keycloak.client-id=payment-app +spring.security.oauth2.client.registration.keycloak.client-secret=863e9de4-33d4-4471-b35e-f8d2434385bb +spring.security.oauth2.client.provider.keycloak.token-uri=http://localhost:8083/auth/realms/master/protocol/openid-connect/token diff --git a/spring-cloud/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/PaymentClientUnitTest.java b/spring-cloud/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/PaymentClientUnitTest.java new file mode 100644 index 0000000000..0372728515 --- /dev/null +++ b/spring-cloud/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/PaymentClientUnitTest.java @@ -0,0 +1,30 @@ +package com.baeldung.cloud.openfeign; + +import com.baeldung.cloud.openfeign.client.PaymentClient; +import com.baeldung.cloud.openfeign.model.Payment; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.List; + +import static org.junit.Assert.assertFalse; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class PaymentClientUnitTest { + + @Autowired + private PaymentClient paymentClient; + + @Test + public void whenGetPayment_thenListPayments() { + + List payments = paymentClient.getPayments(); + + assertFalse(payments.isEmpty()); + } + +} diff --git a/spring-web-modules/spring-rest-testing/pom.xml b/spring-web-modules/spring-rest-testing/pom.xml index 1390898bf9..67b3115a6f 100644 --- a/spring-web-modules/spring-rest-testing/pom.xml +++ b/spring-web-modules/spring-rest-testing/pom.xml @@ -64,11 +64,6 @@ org.springframework.data spring-data-commons - - - org.springframework.boot - spring-boot-starter-tomcat - org.apache.httpcomponents @@ -154,17 +149,9 @@ org.codehaus.cargo - cargo-maven2-plugin - ${cargo-maven2-plugin.version} + cargo-maven3-plugin + ${cargo-maven3-plugin.version} - true - - jetty8x - embedded - - - - 8082 @@ -194,15 +181,32 @@ - live + cargo-integration + + org.apache.maven.plugins + maven-surefire-plugin + + + integration-test + + test + + + + none + + + **/*LiveTest.java + + + + + org.codehaus.cargo - cargo-maven2-plugin - - false - + cargo-maven3-plugin start-server @@ -232,7 +236,7 @@ 3.25.0-GA - 1.6.1 + 1.9.9 1.1.3 diff --git a/spring-web-modules/spring-rest-testing/src/test/java/com/baeldung/cargo/CargoPluginLiveTest.java b/spring-web-modules/spring-rest-testing/src/test/java/com/baeldung/cargo/CargoPluginLiveTest.java new file mode 100644 index 0000000000..8d6dd6a823 --- /dev/null +++ b/spring-web-modules/spring-rest-testing/src/test/java/com/baeldung/cargo/CargoPluginLiveTest.java @@ -0,0 +1,13 @@ +package com.baeldung.cargo; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class CargoPluginLiveTest { + + @Test + void givenCargoProfile_expectTestRuns() { + assertTrue(true); + } +}