diff --git a/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/AnnotatedClass.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/AnnotatedClass.java new file mode 100644 index 0000000000..d5d9c88388 --- /dev/null +++ b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/AnnotatedClass.java @@ -0,0 +1,8 @@ +package com.baeldung.annotations; + +import javax.annotation.Generated; + +@RetentionAnnotation +@Generated("Available only on source code") +public class AnnotatedClass { +} diff --git a/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/RetentionAnnotation.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/RetentionAnnotation.java new file mode 100644 index 0000000000..4591334dde --- /dev/null +++ b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/RetentionAnnotation.java @@ -0,0 +1,12 @@ +package com.baeldung.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; + +@Target(TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface RetentionAnnotation { +} \ No newline at end of file diff --git a/core-java-modules/core-java-annotations/src/test/java/com/baeldung/annotations/AnnotatedClassUnitTest.java b/core-java-modules/core-java-annotations/src/test/java/com/baeldung/annotations/AnnotatedClassUnitTest.java new file mode 100644 index 0000000000..9bf0b78cbb --- /dev/null +++ b/core-java-modules/core-java-annotations/src/test/java/com/baeldung/annotations/AnnotatedClassUnitTest.java @@ -0,0 +1,18 @@ +package com.baeldung.annotations; + +import org.junit.Test; + +import java.lang.annotation.Annotation; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class AnnotatedClassUnitTest { + + @Test + public void whenAnnotationRetentionPolicyRuntime_shouldAccess() { + AnnotatedClass anAnnotatedClass = new AnnotatedClass(); + Annotation[] annotations = anAnnotatedClass.getClass().getAnnotations(); + assertThat(annotations.length, is(1)); + } +} diff --git a/core-java-modules/core-java-collections-4/pom.xml b/core-java-modules/core-java-collections-4/pom.xml index 0e3cabf40e..b4f8aa6320 100644 --- a/core-java-modules/core-java-collections-4/pom.xml +++ b/core-java-modules/core-java-collections-4/pom.xml @@ -25,7 +25,7 @@ - 3.18.0 + 3.19.0 diff --git a/core-java-modules/core-java-collections-4/src/main/java/com/baeldung/collections/dequestack/ArrayLifoStack.java b/core-java-modules/core-java-collections-4/src/main/java/com/baeldung/collections/dequestack/ArrayLifoStack.java new file mode 100644 index 0000000000..16fdec5b77 --- /dev/null +++ b/core-java-modules/core-java-collections-4/src/main/java/com/baeldung/collections/dequestack/ArrayLifoStack.java @@ -0,0 +1,91 @@ +package com.baeldung.collections.dequestack; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Deque; +import java.util.Iterator; + +public class ArrayLifoStack implements LifoStack { + private final Deque deque = new ArrayDeque<>(); + + @Override + public void push(E item) { + deque.addFirst(item); + } + + @Override + public E pop() { + return deque.removeFirst(); + } + + @Override + public E peek() { + return deque.peekFirst(); + } + + // implementing methods from the Collection interface + @Override + public int size() { + return deque.size(); + } + + @Override + public boolean isEmpty() { + return deque.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return deque.contains(o); + } + + @Override + public Iterator iterator() { + return deque.iterator(); + } + + @Override + public Object[] toArray() { + return deque.toArray(); + } + + @Override + public T[] toArray(T[] a) { + return deque.toArray(a); + } + + @Override + public boolean add(E e) { + return deque.add(e); + } + + @Override + public boolean remove(Object o) { + return deque.remove(o); + } + + @Override + public boolean containsAll(Collection c) { + return deque.containsAll(c); + } + + @Override + public boolean addAll(Collection c) { + return deque.addAll(c); + } + + @Override + public boolean removeAll(Collection c) { + return deque.removeAll(c); + } + + @Override + public boolean retainAll(Collection c) { + return deque.retainAll(c); + } + + @Override + public void clear() { + deque.clear(); + } +} diff --git a/core-java-modules/core-java-collections-4/src/main/java/com/baeldung/collections/dequestack/LifoStack.java b/core-java-modules/core-java-collections-4/src/main/java/com/baeldung/collections/dequestack/LifoStack.java new file mode 100644 index 0000000000..11e07e3555 --- /dev/null +++ b/core-java-modules/core-java-collections-4/src/main/java/com/baeldung/collections/dequestack/LifoStack.java @@ -0,0 +1,12 @@ +package com.baeldung.collections.dequestack; + +import java.util.Collection; + +public interface LifoStack extends Collection { + + E peek(); + + E pop(); + + void push(E item); +} \ No newline at end of file diff --git a/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/collections/dequestack/StackVsDequeUnitTest.java b/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/collections/dequestack/StackVsDequeUnitTest.java new file mode 100644 index 0000000000..ca3b0e8d54 --- /dev/null +++ b/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/collections/dequestack/StackVsDequeUnitTest.java @@ -0,0 +1,90 @@ +package com.baeldung.collections.dequestack; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; +import java.util.Stack; + +import static org.assertj.core.api.Assertions.assertThat; + +class StackVsDequeUnitTest { + + @Test + void givenAStack_whenAccessByIndex_thenElementCanBeRead() { + Stack myStack = new Stack<>(); + myStack.push("I am the 1st element."); //index 0 + myStack.push("I am the 2nd element."); //index 1 + myStack.push("I am the 3rd element."); //index 2 + //access by index + assertThat(myStack.get(0)).isEqualTo("I am the 1st element."); + } + + @Test + void givenAStack_whenIterate_thenFromBottomToTop() { + Stack myStack = new Stack<>(); + myStack.push("I am at the bottom."); + myStack.push("I am in the middle."); + myStack.push("I am at the top."); + + Iterator it = myStack.iterator(); + + assertThat(it).toIterable().containsExactly( + "I am at the bottom.", + "I am in the middle.", + "I am at the top."); + } + + @Test + void givenAStack_whenAddOrRemoveByIndex_thenElementCanBeAddedOrRemoved() { + Stack myStack = new Stack<>(); + myStack.push("I am the 1st element."); + myStack.push("I am the 3rd element."); + + assertThat(myStack.size()).isEqualTo(2); + + //insert by index + myStack.add(1, "I am the 2nd element."); + assertThat(myStack.size()).isEqualTo(3); + assertThat(myStack.get(1)).isEqualTo("I am the 2nd element."); + //remove by index + myStack.remove(1); + assertThat(myStack.size()).isEqualTo(2); + } + + @Test + void givenADeque_whenAddOrRemoveLastElement_thenTheLastElementCanBeAddedOrRemoved() { + Deque myStack = new ArrayDeque<>(); + myStack.push("I am the 1st element."); + myStack.push("I am the 2nd element."); + myStack.push("I am the 3rd element."); + + assertThat(myStack.size()).isEqualTo(3); + + //insert element to the bottom of the stack + myStack.addLast("I am the NEW element."); + assertThat(myStack.size()).isEqualTo(4); + assertThat(myStack.peek()).isEqualTo("I am the 3rd element."); + + //remove element from the bottom of the stack + String removedStr = myStack.removeLast(); + assertThat(myStack.size()).isEqualTo(3); + assertThat(removedStr).isEqualTo("I am the NEW element."); + } + + @Test + void givenADeque_whenIterate_thenFromTopToBottom() { + Deque myStack = new ArrayDeque<>(); + myStack.push("I am at the bottom."); + myStack.push("I am in the middle."); + myStack.push("I am at the top."); + + Iterator it = myStack.iterator(); + + assertThat(it).toIterable().containsExactly( + "I am at the top.", + "I am in the middle.", + "I am at the bottom."); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-collections-maps-3/README.md b/core-java-modules/core-java-collections-maps-3/README.md index 918c81fe4b..8b84ecbf81 100644 --- a/core-java-modules/core-java-collections-maps-3/README.md +++ b/core-java-modules/core-java-collections-maps-3/README.md @@ -7,4 +7,5 @@ This module contains articles about Map data structures in Java. - [Comparing Two HashMaps in Java](https://www.baeldung.com/java-compare-hashmaps) - [The Map.computeIfAbsent() Method](https://www.baeldung.com/java-map-computeifabsent) - [Collections.synchronizedMap vs. ConcurrentHashMap](https://www.baeldung.com/java-synchronizedmap-vs-concurrenthashmap) +- [Java HashMap Load Factor](https://www.baeldung.com/java-hashmap-load-factor) - More articles: [[<-- prev]](/core-java-modules/core-java-collections-maps-2) diff --git a/core-java-modules/core-java-jpms/decoupling-pattern2/consumermodule/pom.xml b/core-java-modules/core-java-jpms/decoupling-pattern2/consumermodule/pom.xml index e6b351b1b9..816f5cf9e8 100644 --- a/core-java-modules/core-java-jpms/decoupling-pattern2/consumermodule/pom.xml +++ b/core-java-modules/core-java-jpms/decoupling-pattern2/consumermodule/pom.xml @@ -17,7 +17,7 @@ com.baeldung.servicemodule - servicemodule + servicemodule2 ${servicemodule.version} @@ -41,4 +41,4 @@ 1.0 - \ No newline at end of file + diff --git a/core-java-modules/core-java-lang-oop-generics/README.md b/core-java-modules/core-java-lang-oop-generics/README.md index 9c9080ece3..720ba9dcfd 100644 --- a/core-java-modules/core-java-lang-oop-generics/README.md +++ b/core-java-modules/core-java-lang-oop-generics/README.md @@ -8,3 +8,4 @@ This module contains articles about generics in Java - [Raw Types in Java](https://www.baeldung.com/raw-types-java) - [Super Type Tokens in Java Generics](https://www.baeldung.com/java-super-type-tokens) - [Java Warning “unchecked conversion”](https://www.baeldung.com/java-unchecked-conversion) +- [Java Warning “Unchecked Cast”](https://www.baeldung.com/java-warning-unchecked-cast) diff --git a/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/uncheckedcast/UncheckedCast.java b/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/uncheckedcast/UncheckedCast.java new file mode 100644 index 0000000000..fe79488d11 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/uncheckedcast/UncheckedCast.java @@ -0,0 +1,26 @@ +package com.baeldung.uncheckedcast; + +import java.time.LocalDate; +import java.time.Month; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +public class UncheckedCast { + public static Map getRawMap() { + Map rawMap = new HashMap(); + rawMap.put("date 1", LocalDate.of(2021, Month.FEBRUARY, 10)); + rawMap.put("date 2", LocalDate.of(1992, Month.AUGUST, 8)); + rawMap.put("date 3", LocalDate.of(1976, Month.NOVEMBER, 18)); + return rawMap; + } + + public static Map getRawMapWithMixedTypes() { + Map rawMap = new HashMap(); + rawMap.put("date 1", LocalDate.of(2021, Month.FEBRUARY, 10)); + rawMap.put("date 2", LocalDate.of(1992, Month.AUGUST, 8)); + rawMap.put("date 3", LocalDate.of(1976, Month.NOVEMBER, 18)); + rawMap.put("date 4", new Date()); + return rawMap; + } +} diff --git a/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/uncheckedcast/UncheckedCastUnitTest.java b/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/uncheckedcast/UncheckedCastUnitTest.java new file mode 100644 index 0000000000..b1bd46555e --- /dev/null +++ b/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/uncheckedcast/UncheckedCastUnitTest.java @@ -0,0 +1,25 @@ +package com.baeldung.uncheckedcast; + +import org.junit.Assert; +import org.junit.Test; + +import java.time.LocalDate; +import java.time.Month; +import java.util.Map; + +public class UncheckedCastUnitTest { + + @Test + public void givenRawMap_whenCastToTypedMap_shouldHaveCompilerWarning() { + Map castFromRawMap = (Map) UncheckedCast.getRawMap(); + Assert.assertEquals(3, castFromRawMap.size()); + Assert.assertEquals(castFromRawMap.get("date 2"), LocalDate.of(1992, Month.AUGUST, 8)); + } + + @Test(expected = ClassCastException.class) + public void givenMixTypedRawMap_whenCastToTypedMap_shouldThrowClassCastException() { + Map castFromRawMap = (Map) UncheckedCast.getRawMapWithMixedTypes(); + Assert.assertEquals(4, castFromRawMap.size()); + Assert.assertTrue(castFromRawMap.get("date 4").isAfter(castFromRawMap.get("date 3"))); + } +} diff --git a/core-java-modules/core-java-nio-2/pom.xml b/core-java-modules/core-java-nio-2/pom.xml index 0c7c079406..7c4583476d 100644 --- a/core-java-modules/core-java-nio-2/pom.xml +++ b/core-java-modules/core-java-nio-2/pom.xml @@ -14,5 +14,12 @@ 0.0.1-SNAPSHOT ../ - + + + org.assertj + assertj-core + 3.6.1 + test + + \ No newline at end of file diff --git a/core-java-modules/core-java-nio-2/src/test/java/com/baeldung/selector/SelectorManualTest.java b/core-java-modules/core-java-nio-2/src/test/java/com/baeldung/selector/SelectorManualTest.java new file mode 100644 index 0000000000..7a6c9e6eab --- /dev/null +++ b/core-java-modules/core-java-nio-2/src/test/java/com/baeldung/selector/SelectorManualTest.java @@ -0,0 +1,64 @@ +package com.baeldung.selector; + +import org.junit.Test; +import java.io.IOException; +import java.nio.channels.Pipe; +import java.nio.channels.SelectableChannel; +import java.nio.channels.Selector; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +import static org.assertj.core.api.Assertions.assertThat; + +import static java.nio.channels.SelectionKey.OP_READ; + +public class SelectorManualTest { + + @Test + public void whenWakeUpCalledOnSelector_thenBlockedThreadReturns() throws IOException, InterruptedException { + Pipe pipe = Pipe.open(); + + Selector selector = Selector.open(); + SelectableChannel channel = pipe.source(); + channel.configureBlocking(false); + channel.register(selector, OP_READ); + + List invocationStepsTracker = Collections.synchronizedList(new ArrayList<>()); + + CountDownLatch latch = new CountDownLatch(1); + + Thread thread = new Thread(() -> { + invocationStepsTracker.add(">> Count down"); + latch.countDown(); + try { + invocationStepsTracker.add(">> Start select"); + selector.select(); + invocationStepsTracker.add(">> End select"); + } catch (IOException e) { + e.printStackTrace(); + } + }); + + invocationStepsTracker.add(">> Start await"); + thread.start(); + latch.await(); + invocationStepsTracker.add(">> End await"); + + invocationStepsTracker.add(">> Wakeup thread"); + + selector.wakeup(); + channel.close(); + + assertThat(invocationStepsTracker) + .containsExactly( + ">> Start await", + ">> Count down", + ">> Start select", + ">> End await", + ">> Wakeup thread", + ">> End select" + ); + } +} diff --git a/docker/README.md b/docker/README.md index ce7fe261c2..f1b4181e64 100644 --- a/docker/README.md +++ b/docker/README.md @@ -3,3 +3,4 @@ - [Introduction to Docker Compose](https://www.baeldung.com/docker-compose) - [Reusing Docker Layers with Spring Boot](https://www.baeldung.com/docker-layers-spring-boot) - [Running Spring Boot with PostgreSQL in Docker Compose](https://www.baeldung.com/spring-boot-postgresql-docker) +- [How To Configure Java Heap Size Inside a Docker Container](https://www.baeldung.com/ops/docker-jvm-heap-size) diff --git a/docker/heap-sizing/Dockerfile b/docker/heap-sizing/Dockerfile new file mode 100644 index 0000000000..c455e45c70 --- /dev/null +++ b/docker/heap-sizing/Dockerfile @@ -0,0 +1,4 @@ +FROM openjdk:8u92-jdk-alpine +COPY /src /src/ +RUN mkdir /app && ls /src && javac /src/main/java/com/baeldung/docker/heapsizing/PrintXmxXms.java -d /app +CMD java -version && java $JAVA_OPTS -cp /app com.baeldung.docker.heapsizing.PrintXmxXms \ No newline at end of file diff --git a/docker/heap-sizing/pom.xml b/docker/heap-sizing/pom.xml new file mode 100644 index 0000000000..a86a67fdcd --- /dev/null +++ b/docker/heap-sizing/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.2 + + + com.baeldung.docker + heap-sizing + 0.0.1-SNAPSHOT + heap-sizing + Demo project for Spring Boot + + 11 + + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.boot + spring-boot-starter-test + test + + + io.projectreactor + reactor-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + heapsizing-demo + + + + + com.google.cloud.tools + jib-maven-plugin + 2.7.1 + + + + heapsizing-demo-jib + + + + + + + diff --git a/docker/heap-sizing/src/main/java/com/baeldung/docker/heapsizing/HeapSizingApplication.java b/docker/heap-sizing/src/main/java/com/baeldung/docker/heapsizing/HeapSizingApplication.java new file mode 100644 index 0000000000..00bee376c5 --- /dev/null +++ b/docker/heap-sizing/src/main/java/com/baeldung/docker/heapsizing/HeapSizingApplication.java @@ -0,0 +1,19 @@ +package com.baeldung.docker.heapsizing; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import java.util.logging.Logger; + +import static com.baeldung.docker.heapsizing.PrintXmxXms.logMemory; + +@SpringBootApplication +public class HeapSizingApplication { + private static final Logger logger = Logger.getLogger(HeapSizingApplication.class.getName()); + + public static void main(String[] args) { + SpringApplication.run(HeapSizingApplication.class, args); + logMemory(logger); + } + +} diff --git a/docker/heap-sizing/src/main/java/com/baeldung/docker/heapsizing/PrintXmxXms.java b/docker/heap-sizing/src/main/java/com/baeldung/docker/heapsizing/PrintXmxXms.java new file mode 100644 index 0000000000..98f2f6455f --- /dev/null +++ b/docker/heap-sizing/src/main/java/com/baeldung/docker/heapsizing/PrintXmxXms.java @@ -0,0 +1,36 @@ +package com.baeldung.docker.heapsizing; + +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryPoolMXBean; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class PrintXmxXms { + + private static final Logger logger = Logger.getLogger(PrintXmxXms.class.getName()); + + public static void main(String[] args) { + logMemory(logger); + } + + /** + * We're reusing this method in HeapSizingApplication, therefore this method was extracted + * to avoid repetition. + */ + static void logMemory(Logger logger) { + float mb = 1024f * 1024f; + MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean(); + + // xmx controls the maximum size of the memory allocation pool, + // which includes the heap, the garbage collector's survivor space, and other pools. + float xmx = memoryBean.getHeapMemoryUsage().getMax() / mb; + float xms = memoryBean.getHeapMemoryUsage().getInit() / mb; + logger.log(Level.INFO, "Initial Memory (xms) : {0}mb", xms); + logger.log(Level.INFO, "Max Memory (xmx) : {0}mb", xmx); + + for (MemoryPoolMXBean mp : ManagementFactory.getMemoryPoolMXBeans()) { + logger.log(Level.INFO, "Pool: {0} (type {1}) = {2}", new Object[]{ mp.getName(), mp.getType(), mp.getUsage().getMax() / mb }); + } + } +} diff --git a/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/Member.java b/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/Member.java new file mode 100644 index 0000000000..22b6a61d4d --- /dev/null +++ b/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/Member.java @@ -0,0 +1,6 @@ +package com.baeldung.map.hashing; + +class Member { + Integer id; + String name; +} diff --git a/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/MemberWithBadHashing.java b/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/MemberWithBadHashing.java new file mode 100644 index 0000000000..2a69291742 --- /dev/null +++ b/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/MemberWithBadHashing.java @@ -0,0 +1,8 @@ +package com.baeldung.map.hashing; + +public class MemberWithBadHashing extends Member { + @Override + public int hashCode() { + return name.hashCode(); + } +} diff --git a/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/MemberWithGuavaHashing.java b/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/MemberWithGuavaHashing.java new file mode 100644 index 0000000000..bb33ace0b7 --- /dev/null +++ b/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/MemberWithGuavaHashing.java @@ -0,0 +1,18 @@ +package com.baeldung.map.hashing; + +import com.google.common.base.Charsets; +import com.google.common.hash.HashFunction; +import com.google.common.hash.Hashing; + +public class MemberWithGuavaHashing extends Member { + @Override + public int hashCode() { + HashFunction hashFunction = Hashing.murmur3_32(); + return hashFunction.newHasher() + .putInt(id) + .putString(name, Charsets.UTF_8) + .hash().hashCode(); + } + + +} diff --git a/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/MemberWithId.java b/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/MemberWithId.java new file mode 100644 index 0000000000..5d82eb0cb7 --- /dev/null +++ b/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/MemberWithId.java @@ -0,0 +1,18 @@ +package com.baeldung.map.hashing; + +public class MemberWithId extends Member { + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + MemberWithId that = (MemberWithId) o; + + return id.equals(that.id); + } + + @Override + public int hashCode() { + return id; + } +} diff --git a/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/MemberWithIdAndName.java b/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/MemberWithIdAndName.java new file mode 100644 index 0000000000..81ae48cf55 --- /dev/null +++ b/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/MemberWithIdAndName.java @@ -0,0 +1,23 @@ +package com.baeldung.map.hashing; + +import java.util.Objects; + +public class MemberWithIdAndName extends Member { + public static final int PRIME = 31; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MemberWithObjects that = (MemberWithObjects) o; + return Objects.equals(id, that.id) && + Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + int result = id.hashCode(); + result = PRIME * result + (name == null ? 0 : name.hashCode()); + return result; + } +} diff --git a/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/MemberWithObjects.java b/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/MemberWithObjects.java new file mode 100644 index 0000000000..b641035e4f --- /dev/null +++ b/java-collections-maps-3/src/main/java/com/baeldung/map/hashing/MemberWithObjects.java @@ -0,0 +1,19 @@ +package com.baeldung.map.hashing; + +import java.util.Objects; + +public class MemberWithObjects extends Member { + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MemberWithObjects that = (MemberWithObjects) o; + return Objects.equals(id, that.id) && + Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } +} diff --git a/java-collections-maps-3/src/test/java/com/baeldung/map/hashing/HashingUnitTest.java b/java-collections-maps-3/src/test/java/com/baeldung/map/hashing/HashingUnitTest.java new file mode 100644 index 0000000000..d28a3b50b5 --- /dev/null +++ b/java-collections-maps-3/src/test/java/com/baeldung/map/hashing/HashingUnitTest.java @@ -0,0 +1,76 @@ +package com.baeldung.map.hashing; + +import com.google.common.base.Stopwatch; +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.HashMap; +import java.util.SplittableRandom; +import java.util.function.Supplier; + +public class HashingUnitTest { + + public static final int SAMPLES = 1000000; + private SplittableRandom random = new SplittableRandom(); + + private String[] names = {"John", "Adam", "Suzie"}; + + @Test + void givenPrimitiveByteArrayKey_whenRetrievingFromMap_shouldRetrieveDifferentObjects() { + // bad hashing example is prohibitively slow for bigger samples +// Duration[] badHashing = testDuration(MemberWithBadHashing::new); + Duration[] withId = testDuration(MemberWithId::new); + Duration[] withObjects = testDuration(MemberWithObjects::new); + Duration[] withIdAndName = testDuration(MemberWithIdAndName::new); + +// System.out.println("Inserting with bad hashing:"); +// System.out.println(badHashing[0]); +// System.out.println("Getting with bad hashing:"); +// System.out.println(badHashing[1]); + + System.out.println("Inserting with id hashing:"); + System.out.println(withId[0]); + System.out.println("Getting with id hashing:"); + System.out.println(withId[1]); + + System.out.println("Inserting with id and name hashing:"); + System.out.println(withIdAndName[0]); + System.out.println("Getting with id and name hashing:"); + System.out.println(withIdAndName[1]); + + System.out.println("Inserting with Objects hashing:"); + System.out.println(withObjects[0]); + System.out.println("Getting with Objects hashing:"); + System.out.println(withObjects[1]); + } + + private String randomName() { + return names[random.nextInt(2)]; + } + + private Duration[] testDuration(Supplier factory) { + HashMap map = new HashMap<>(); + Stopwatch stopwatch = Stopwatch.createUnstarted(); + + stopwatch.start(); + for(int i = 0; i < SAMPLES; i++) { + T member = factory.get(); + member.id = i; + member.name = randomName(); + map.put(member, member.name); + } + stopwatch.stop(); + Duration elapsedInserting = stopwatch.elapsed(); + stopwatch.reset(); + + stopwatch.start(); + for (T key : map.keySet()) { + map.get(key); + } + stopwatch.stop(); + Duration elapsedGetting = stopwatch.elapsed(); + stopwatch.reset(); + + return new Duration[]{elapsedInserting, elapsedGetting}; + } +} diff --git a/jmeter/README.md b/jmeter/README.md index 81300afe7c..11351ffdda 100644 --- a/jmeter/README.md +++ b/jmeter/README.md @@ -7,7 +7,7 @@ It contains the code of a simple API for some CRUD operations built using Spring - Maven - JDK 8 -- MongoDB +- MongoDB (Note: for the Write Extracted Data to a File Using JMeter example MongoDB is not required) ### Running @@ -36,6 +36,14 @@ Or create a new one via a POST: $ curl -X POST -H "Content-Type:application/json" -d '{ "firstName" : "Dassi", "lastName" : "Orleando", "phoneNumber": "+237 545454545", "email": "mymail@yahoo.fr" }' localhost:8080/students ``` +### Available UUID API + +You can view the test response using curl: + +```bash +$ curl localhost:8080/api/uuid +``` + Now with default configurations it will be available at: [http://localhost:8080](http://localhost:8080) Enjoy it :) diff --git a/jmeter/src/main/java/com/baeldung/controller/RetrieveUuidController.java b/jmeter/src/main/java/com/baeldung/controller/RetrieveUuidController.java new file mode 100644 index 0000000000..32265c0170 --- /dev/null +++ b/jmeter/src/main/java/com/baeldung/controller/RetrieveUuidController.java @@ -0,0 +1,18 @@ +package com.baeldung.controller; + +import com.baeldung.model.Response; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.UUID; + +import static java.lang.String.format; + +@RestController +public class RetrieveUuidController { + + @GetMapping("/api/uuid") + public Response uuid() { + return new Response(format("Test message... %s.", UUID.randomUUID())); + } +} diff --git a/jmeter/src/main/java/com/baeldung/model/Response.java b/jmeter/src/main/java/com/baeldung/model/Response.java new file mode 100644 index 0000000000..547e5b536e --- /dev/null +++ b/jmeter/src/main/java/com/baeldung/model/Response.java @@ -0,0 +1,40 @@ +package com.baeldung.model; + +import java.time.Instant; +import java.util.UUID; + +public class Response { + private Instant timestamp; + private UUID uuid; + private String message; + + public Response(String message) { + this.timestamp = Instant.now(); + this.uuid = UUID.randomUUID(); + this.message = message; + } + + public Instant getTimestamp() { + return timestamp; + } + + public void setTimestamp(Instant timestamp) { + this.timestamp = timestamp; + } + + public UUID getUuid() { + return uuid; + } + + public void setUuid(UUID uuid) { + this.uuid = uuid; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/jmeter/src/main/resources/FileExtractionExample.jmx b/jmeter/src/main/resources/FileExtractionExample.jmx new file mode 100644 index 0000000000..961b6f143f --- /dev/null +++ b/jmeter/src/main/resources/FileExtractionExample.jmx @@ -0,0 +1,124 @@ + + + + + To run this test plan you must also be running the Spring application "JmeterApplication" That can be found in this directory + false + true + false + + + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + localhost + 8080 + http + + /api/test + GET + true + false + true + false + + + + + + + message + $.message + 1 + true + NOT_FOUND + + + + + + false + FileWriter fWriter = new FileWriter("/result.txt", true); +BufferedWriter buff = new BufferedWriter(fWriter); + +buff.write("Response Code : " + ctx.getPreviousResult().getResponseCode()); +buff.write(System.getProperty("line.separator")); +buff.write("Response Headers : " + ctx.getPreviousResult().getResponseHeaders()); +buff.write(System.getProperty("line.separator")); +buff.write("Response Body : " + new String(ctx.getPreviousResult().getResponseData())); + +buff.write("More complex extraction : " + vars.get("message")); + +buff.close(); +fWriter.close(); + + + + response + false + false + false + false + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + + + diff --git a/jmeter/src/test/java/com/baeldung/JmeterIntegrationTest.java b/jmeter/src/test/java/com/baeldung/JmeterIntegrationTest.java new file mode 100644 index 0000000000..a1494416ef --- /dev/null +++ b/jmeter/src/test/java/com/baeldung/JmeterIntegrationTest.java @@ -0,0 +1,35 @@ +package com.baeldung; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +class JmeterIntegrationTest { + + MockMvc mvc; + + public JmeterIntegrationTest(WebApplicationContext wac) { + this.mvc = MockMvcBuilders.webAppContextSetup(wac).build(); + } + + @Test + void whenCallingUUIDController_thenWeShouldRecieveRandomizedResponse() throws Exception { + MockHttpServletResponse response = mvc.perform(get("/api/uuid")) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn() + .getResponse(); + + assertThat(response.getContentAsString()) + .contains("Test message..."); + } +} diff --git a/maven-modules/pom.xml b/maven-modules/pom.xml index 86a7d5756c..c9a2b67a6c 100644 --- a/maven-modules/pom.xml +++ b/maven-modules/pom.xml @@ -27,6 +27,7 @@ versions-maven-plugin version-collision optional-dependencies + version-overriding-plugins diff --git a/maven-modules/version-overriding-plugins/README.md b/maven-modules/version-overriding-plugins/README.md new file mode 100644 index 0000000000..1542692ca0 --- /dev/null +++ b/maven-modules/version-overriding-plugins/README.md @@ -0,0 +1,5 @@ +Use `` mvn help:effective-pom`` to see the final generated pom. + +### Relevant Articles: + +- [Override Maven Plugin Configuration from Parent](https://www.baeldung.com/maven-plugin-override-parent) diff --git a/maven-modules/version-overriding-plugins/child-a/pom.xml b/maven-modules/version-overriding-plugins/child-a/pom.xml new file mode 100644 index 0000000000..780e1c4125 --- /dev/null +++ b/maven-modules/version-overriding-plugins/child-a/pom.xml @@ -0,0 +1,36 @@ + + + + version-overriding-plugins + com.baeldung + 0.0.1-SNAPSHOT + + 4.0.0 + pom + + child-a + + + + maven-resources-plugin + + + + child-a-resources + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + \ No newline at end of file diff --git a/maven-modules/version-overriding-plugins/child-b/pom.xml b/maven-modules/version-overriding-plugins/child-b/pom.xml new file mode 100644 index 0000000000..05f127bc5c --- /dev/null +++ b/maven-modules/version-overriding-plugins/child-b/pom.xml @@ -0,0 +1,15 @@ + + + + version-overriding-plugins + com.baeldung + 0.0.1-SNAPSHOT + + 4.0.0 + + child-b + + + \ No newline at end of file diff --git a/maven-modules/version-overriding-plugins/pom.xml b/maven-modules/version-overriding-plugins/pom.xml new file mode 100644 index 0000000000..8d703ab568 --- /dev/null +++ b/maven-modules/version-overriding-plugins/pom.xml @@ -0,0 +1,55 @@ + + + + maven-modules + com.baeldung + 0.0.1-SNAPSHOT + + + 3.8.0 + + 4.0.0 + + version-overriding-plugins + pom + + + child-a + child-b + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin} + + + + + + maven-resources-plugin + + + + parent-resources + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + 512m + + + + + \ No newline at end of file diff --git a/spring-5-reactive-client/README.md b/spring-5-reactive-client/README.md index b247a1669b..154a3cab0b 100644 --- a/spring-5-reactive-client/README.md +++ b/spring-5-reactive-client/README.md @@ -8,7 +8,6 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring ### Relevant Articles - [Logging Spring WebClient Calls](https://www.baeldung.com/spring-log-webclient-calls) - [Simultaneous Spring WebClient Calls](https://www.baeldung.com/spring-webclient-simultaneous-calls) -- [Logging Spring WebClient Calls](https://www.baeldung.com/spring-log-webclient-calls) - [Mocking a WebClient in Spring](https://www.baeldung.com/spring-mocking-webclient) - [Spring WebClient Filters](https://www.baeldung.com/spring-webclient-filters) - [Get List of JSON Objects with WebClient](https://www.baeldung.com/spring-webclient-json-list) diff --git a/spring-5-reactive-client/pom.xml b/spring-5-reactive-client/pom.xml index 5b773cc63f..7ae7ba6edd 100644 --- a/spring-5-reactive-client/pom.xml +++ b/spring-5-reactive-client/pom.xml @@ -176,7 +176,6 @@ 4.1 1.0.3 4.0.1 - 2.3.3.RELEASE diff --git a/spring-5-reactive-client/src/main/resources/application.properties b/spring-5-reactive-client/src/main/resources/application.properties index 2d93456aeb..05033054b1 100644 --- a/spring-5-reactive-client/src/main/resources/application.properties +++ b/spring-5-reactive-client/src/main/resources/application.properties @@ -1,3 +1,5 @@ logging.level.root=INFO -server.port=8081 \ No newline at end of file +server.port=8081 + +logging.level.reactor.netty.http.client.HttpClient=DEBUG \ No newline at end of file diff --git a/spring-5-reactive-client/src/test/java/com/baeldung/reactive/logging/WebClientLoggingIntegrationTest.java b/spring-5-reactive-client/src/test/java/com/baeldung/reactive/logging/WebClientLoggingIntegrationTest.java index 95c63f267f..bb4e682481 100644 --- a/spring-5-reactive-client/src/test/java/com/baeldung/reactive/logging/WebClientLoggingIntegrationTest.java +++ b/spring-5-reactive-client/src/test/java/com/baeldung/reactive/logging/WebClientLoggingIntegrationTest.java @@ -1,13 +1,13 @@ package com.baeldung.reactive.logging; -import ch.qos.logback.classic.spi.LoggingEvent; -import ch.qos.logback.core.Appender; -import com.baeldung.reactive.logging.filters.LogFilters; -import com.baeldung.reactive.logging.netty.CustomLogger; -import com.fasterxml.jackson.databind.ObjectMapper; +import static com.baeldung.reactive.logging.jetty.RequestLogEnhancer.enhance; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import java.net.URI; -import lombok.AllArgsConstructor; -import lombok.Data; + import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.jupiter.api.BeforeEach; @@ -17,14 +17,17 @@ import org.springframework.http.client.reactive.JettyClientHttpConnector; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.client.WebClient; -import reactor.netty.channel.BootstrapHandlers; -import reactor.netty.http.client.HttpClient; -import static com.baeldung.reactive.logging.jetty.RequestLogEnhancer.enhance; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import com.baeldung.reactive.logging.filters.LogFilters; +import com.fasterxml.jackson.databind.ObjectMapper; + +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.Appender; +import io.netty.handler.logging.LogLevel; +import lombok.AllArgsConstructor; +import lombok.Data; +import reactor.netty.http.client.HttpClient; +import reactor.netty.transport.logging.AdvancedByteBufFormat; public class WebClientLoggingIntegrationTest { @@ -114,21 +117,17 @@ public class WebClientLoggingIntegrationTest { @Test public void givenNettyHttpClientWithCustomLogger_whenEndpointIsConsumed_thenRequestAndResponseBodyLogged() { + reactor.netty.http.client.HttpClient httpClient = HttpClient.create() + .wiretap("reactor.netty.http.client.HttpClient", LogLevel.DEBUG, AdvancedByteBufFormat.TEXTUAL); - reactor.netty.http.client.HttpClient httpClient = HttpClient - .create() - .tcpConfiguration( - tc -> tc.bootstrap( - b -> BootstrapHandlers.updateLogSupport(b, new CustomLogger(HttpClient.class)))); - WebClient - .builder() - .clientConnector(new ReactorClientHttpConnector(httpClient)) - .build() - .post() - .uri(sampleUrl) - .body(BodyInserters.fromObject(post)) - .exchange() - .block(); + WebClient.builder() + .clientConnector(new ReactorClientHttpConnector(httpClient)) + .build() + .post() + .uri(sampleUrl) + .body(BodyInserters.fromObject(post)) + .exchange() + .block(); verify(nettyAppender).doAppend(argThat(argument -> (((LoggingEvent) argument).getFormattedMessage()).contains(sampleResponseBody))); } diff --git a/spring-5-reactive-client/src/test/java/com/baeldung/reactive/logging/netty/CustomLogger.java b/spring-5-reactive-client/src/test/java/com/baeldung/reactive/logging/netty/CustomLogger.java deleted file mode 100644 index 9f2a4d127f..0000000000 --- a/spring-5-reactive-client/src/test/java/com/baeldung/reactive/logging/netty/CustomLogger.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.baeldung.reactive.logging.netty; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.logging.LoggingHandler; -import java.nio.charset.Charset; - -import static io.netty.util.internal.PlatformDependent.allocateUninitializedArray; -import static java.lang.Math.max; -import static java.nio.charset.Charset.defaultCharset; - -public class CustomLogger extends LoggingHandler { - public CustomLogger(Class clazz) { - super(clazz); - } - - @Override - protected String format(ChannelHandlerContext ctx, String event, Object arg) { - if (arg instanceof ByteBuf) { - ByteBuf msg = (ByteBuf) arg; - return decode(msg, msg.readerIndex(), msg.readableBytes(), defaultCharset()); - } - return super.format(ctx, event, arg); - } - - private String decode(ByteBuf src, int readerIndex, int len, Charset charset) { - if (len != 0) { - byte[] array; - int offset; - if (src.hasArray()) { - array = src.array(); - offset = src.arrayOffset() + readerIndex; - } else { - array = allocateUninitializedArray(max(len, 1024)); - offset = 0; - src.getBytes(readerIndex, array, 0, len); - } - return new String(array, offset, len, charset); - } - return ""; - } -} diff --git a/spring-boot-modules/pom.xml b/spring-boot-modules/pom.xml index 263d2af089..4902368d7e 100644 --- a/spring-boot-modules/pom.xml +++ b/spring-boot-modules/pom.xml @@ -54,6 +54,7 @@ spring-boot-mvc-2 spring-boot-mvc-3 spring-boot-mvc-birt + spring-boot-mvc-jersey spring-boot-nashorn spring-boot-parent spring-boot-performance diff --git a/spring-boot-modules/spring-boot-admin/spring-boot-admin-client/pom.xml b/spring-boot-modules/spring-boot-admin/spring-boot-admin-client/pom.xml index eb40bfe8ea..f7a4b157e7 100644 --- a/spring-boot-modules/spring-boot-admin/spring-boot-admin-client/pom.xml +++ b/spring-boot-modules/spring-boot-admin/spring-boot-admin-client/pom.xml @@ -61,7 +61,7 @@ - 2.2.2 + 2.4.0 2.0.4.RELEASE diff --git a/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/pom.xml b/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/pom.xml index 558aed8b26..bcec12a14c 100644 --- a/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/pom.xml +++ b/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/pom.xml @@ -82,10 +82,9 @@ - 2.2.2 - 2.2.2 + 2.4.0 + 2.4.0 1.5.7 2.0.4.RELEASE - 2.3.3.RELEASE diff --git a/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/src/main/java/com/baeldung/springbootadminserver/SpringBootAdminServerApplication.java b/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/src/main/java/com/baeldung/springbootadminserver/SpringBootAdminServerApplication.java index e934086cf4..5a713c7e8f 100644 --- a/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/src/main/java/com/baeldung/springbootadminserver/SpringBootAdminServerApplication.java +++ b/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/src/main/java/com/baeldung/springbootadminserver/SpringBootAdminServerApplication.java @@ -1,11 +1,12 @@ package com.baeldung.springbootadminserver; +import de.codecentric.boot.admin.server.config.AdminServerHazelcastAutoConfiguration; import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @EnableAdminServer -@SpringBootApplication +@SpringBootApplication(exclude = AdminServerHazelcastAutoConfiguration.class) public class SpringBootAdminServerApplication { public static void main(String[] args) { diff --git a/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/src/main/java/com/baeldung/springbootadminserver/configs/HazelcastConfig.java b/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/src/main/java/com/baeldung/springbootadminserver/configs/HazelcastConfig.java index d38b0e933c..d53707c19a 100644 --- a/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/src/main/java/com/baeldung/springbootadminserver/configs/HazelcastConfig.java +++ b/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/src/main/java/com/baeldung/springbootadminserver/configs/HazelcastConfig.java @@ -1,12 +1,13 @@ package com.baeldung.springbootadminserver.configs; import com.hazelcast.config.Config; +import com.hazelcast.config.EvictionConfig; import com.hazelcast.config.EvictionPolicy; import com.hazelcast.config.InMemoryFormat; import com.hazelcast.config.MapConfig; import com.hazelcast.config.MergePolicyConfig; import com.hazelcast.config.TcpIpConfig; -import com.hazelcast.map.merge.PutIfAbsentMapMergePolicy; +import com.hazelcast.spi.merge.PutIfAbsentMergePolicy; import java.util.Collections; @@ -16,32 +17,27 @@ import org.springframework.context.annotation.Configuration; @Configuration public class HazelcastConfig { - @Bean - public Config hazelcast() { - MapConfig eventStoreMap = new MapConfig("spring-boot-admin-event-store").setInMemoryFormat(InMemoryFormat.OBJECT) - .setBackupCount(1) - .setEvictionPolicy(EvictionPolicy.NONE) - .setMergePolicyConfig(new MergePolicyConfig(PutIfAbsentMapMergePolicy.class.getName(), 100)); + @Bean + public Config hazelcast() { + MapConfig eventStoreMap = new MapConfig("spring-boot-admin-event-store") + .setInMemoryFormat(InMemoryFormat.OBJECT).setBackupCount(1) + .setEvictionConfig(new EvictionConfig().setEvictionPolicy(EvictionPolicy.NONE)) + .setMergePolicyConfig(new MergePolicyConfig(PutIfAbsentMergePolicy.class.getName(), 100)); - MapConfig sentNotificationsMap = new MapConfig("spring-boot-admin-application-store").setInMemoryFormat(InMemoryFormat.OBJECT) - .setBackupCount(1) - .setEvictionPolicy(EvictionPolicy.LRU) - .setMergePolicyConfig(new MergePolicyConfig(PutIfAbsentMapMergePolicy.class.getName(), 100)); + MapConfig sentNotificationsMap = new MapConfig("spring-boot-admin-application-store") + .setInMemoryFormat(InMemoryFormat.OBJECT).setBackupCount(1) + .setEvictionConfig(new EvictionConfig().setEvictionPolicy(EvictionPolicy.LRU)) + .setMergePolicyConfig(new MergePolicyConfig(PutIfAbsentMergePolicy.class.getName(), 100)); - Config config = new Config(); - config.addMapConfig(eventStoreMap); - config.addMapConfig(sentNotificationsMap); - config.setProperty("hazelcast.jmx", "true"); + Config config = new Config(); + config.addMapConfig(eventStoreMap); + config.addMapConfig(sentNotificationsMap); + config.setProperty("hazelcast.jmx", "true"); - config.getNetworkConfig() - .getJoin() - .getMulticastConfig() - .setEnabled(false); - TcpIpConfig tcpIpConfig = config.getNetworkConfig() - .getJoin() - .getTcpIpConfig(); - tcpIpConfig.setEnabled(true); - tcpIpConfig.setMembers(Collections.singletonList("127.0.0.1")); - return config; - } + config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false); + TcpIpConfig tcpIpConfig = config.getNetworkConfig().getJoin().getTcpIpConfig(); + tcpIpConfig.setEnabled(true); + tcpIpConfig.setMembers(Collections.singletonList("127.0.0.1")); + return config; + } } diff --git a/spring-boot-modules/spring-boot-data-2/README.md b/spring-boot-modules/spring-boot-data-2/README.md index d5020ce354..29d9dafe66 100644 --- a/spring-boot-modules/spring-boot-data-2/README.md +++ b/spring-boot-modules/spring-boot-data-2/README.md @@ -1,3 +1,4 @@ ### Relevant Articles: - [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) diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/noconverterfound/NoConverterFoundApplication.java b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/noconverterfound/NoConverterFoundApplication.java new file mode 100644 index 0000000000..7abfa29bf2 --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/noconverterfound/NoConverterFoundApplication.java @@ -0,0 +1,12 @@ +package com.baeldung.boot.noconverterfound; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class NoConverterFoundApplication { + + public static void main(String[] args) { + SpringApplication.run(NoConverterFoundApplication.class, args); + } +} diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/noconverterfound/controller/StudentRestController.java b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/noconverterfound/controller/StudentRestController.java new file mode 100644 index 0000000000..21cb98710d --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/noconverterfound/controller/StudentRestController.java @@ -0,0 +1,21 @@ +package com.baeldung.boot.noconverterfound.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.baeldung.boot.noconverterfound.model.Student; + +@RestController +@RequestMapping(value = "/api") +public class StudentRestController { + + @GetMapping("/student/{id}") + public ResponseEntity get(@PathVariable("id") int id) { + // Custom logic + return ResponseEntity.ok(new Student(id, "John", "Wiliams", "AA")); + } + +} diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/noconverterfound/model/Student.java b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/noconverterfound/model/Student.java new file mode 100644 index 0000000000..94ece02f8f --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/noconverterfound/model/Student.java @@ -0,0 +1,53 @@ +package com.baeldung.boot.noconverterfound.model; + +public class Student { + + private int id; + private String firstName; + private String lastName; + private String grade; + + public Student() { + + } + + public Student(int id, String firstName, String lastName, String grade) { + this.id = id; + this.firstName = firstName; + this.lastName = lastName; + this.grade = grade; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getGrade() { + return grade; + } + + public void setGrade(String grade) { + this.grade = grade; + } + +} diff --git a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/noconverterfound/NoConverterFoundIntegrationTest.java b/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/noconverterfound/NoConverterFoundIntegrationTest.java new file mode 100644 index 0000000000..f8ded91e65 --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/noconverterfound/NoConverterFoundIntegrationTest.java @@ -0,0 +1,52 @@ +package com.baeldung.boot.noconverterfound; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.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.http.converter.HttpMessageNotWritableException; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import com.baeldung.boot.noconverterfound.controller.StudentRestController; + +@RunWith(SpringRunner.class) +@WebMvcTest(StudentRestController.class) +public class NoConverterFoundIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + /* Remove Getters from Student class to successfully run this test case + * @Test + public void whenGettersNotDefined_thenThrowException() throws Exception { + + String url = "/api/student/1"; + + this.mockMvc.perform(get(url)) + .andExpect(status().isInternalServerError()) + .andExpect(result -> assertThat(result.getResolvedException()) + .isInstanceOf(HttpMessageNotWritableException.class)) + .andExpect(result -> assertThat(result.getResolvedException().getMessage()) + .contains("No converter found for return value of type")); + + } + */ + + @Test + public void whenGettersAreDefined_thenReturnObject() throws Exception { + + String url = "/api/student/2"; + + this.mockMvc.perform(get(url)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.firstName").value("John")); + + } + +} diff --git a/spring-boot-modules/spring-boot-mvc-jersey/README.md b/spring-boot-modules/spring-boot-mvc-jersey/README.md new file mode 100644 index 0000000000..07f9e78ea6 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-jersey/README.md @@ -0,0 +1,8 @@ +## Spring Boot Admin + +This module contains articles about Spring Boot: JAX-RS vs Spring + + +### Relevant Articles: + +- [REST API: JAX-RS vs Spring](https://www.baeldung.com/TBD) diff --git a/spring-boot-modules/spring-boot-mvc-jersey/pom.xml b/spring-boot-modules/spring-boot-mvc-jersey/pom.xml new file mode 100644 index 0000000000..ab117c4d2e --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-jersey/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + + com.baeldung.spring-boot-modules + spring-boot-modules + 1.0.0-SNAPSHOT + ../ + + + spring-boot-mvc-jersey + 0.0.1-SNAPSHOT + pom + + spring-boot-mvc-jersey + + + spring-boot-jersey + spring-boot-mvc + + diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/pom.xml b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/pom.xml new file mode 100644 index 0000000000..5756a27eca --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.4.2 + + + + com.baeldung.boot + spring-boot-jersey + 0.0.1-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-jersey + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/main/java/com/baeldung/boot/jersey/JerseyApplication.java b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/main/java/com/baeldung/boot/jersey/JerseyApplication.java new file mode 100644 index 0000000000..b8ee0d0115 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/main/java/com/baeldung/boot/jersey/JerseyApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.boot.jersey; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JerseyApplication { + + public static void main(String[] args) { + SpringApplication.run(JerseyApplication.class, args); + } + +} diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/main/java/com/baeldung/boot/jersey/controllers/HelloController.java b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/main/java/com/baeldung/boot/jersey/controllers/HelloController.java new file mode 100644 index 0000000000..2774e458de --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/main/java/com/baeldung/boot/jersey/controllers/HelloController.java @@ -0,0 +1,20 @@ +package com.baeldung.boot.jersey.controllers; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +@Path("/hello") +public class HelloController { + + @GET + @Path("/{name}") + @Produces(MediaType.TEXT_PLAIN) + public Response hello(@PathParam("name") String name) { + return Response.ok("Hello, " + name).build(); + } + +} diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/main/resources/application.properties b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/main/resources/application.properties new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/JerseyApplicationIntegrationTests.java b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/JerseyApplicationIntegrationTests.java new file mode 100644 index 0000000000..9bd6bbf029 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/JerseyApplicationIntegrationTests.java @@ -0,0 +1,13 @@ +package com.baeldung.boot.jersey; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class JerseyApplicationIntegrationTests { + + @Test + void contextLoads() { + } + +} diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/controllers/HelloControllerUnitTest.java b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/controllers/HelloControllerUnitTest.java new file mode 100644 index 0000000000..e0c48da5bd --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/controllers/HelloControllerUnitTest.java @@ -0,0 +1,27 @@ +package com.baeldung.boot.jersey.controllers; + +import javax.ws.rs.core.Response; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@Import(HelloController.class) +@ExtendWith(SpringExtension.class) +public class HelloControllerUnitTest { + + @Autowired + private HelloController helloController; + + @Test + public void whenHelloIsInvokedWithCaio_thenReturn200AsStatusAndHelloCaioAsBody() { + Response response = this.helloController.hello("Caio"); + assertThat(response.getStatus()).isEqualTo(200); + assertThat(response.getEntity()).isEqualTo("Hello, Caio"); + } + +} diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/pom.xml b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/pom.xml new file mode 100644 index 0000000000..69d355e574 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.4.2 + + + + com.baeldung.boot + spring-boot-mvc + 0.0.1-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/main/java/com/baeldung/boot/mvc/MvcApplication.java b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/main/java/com/baeldung/boot/mvc/MvcApplication.java new file mode 100644 index 0000000000..9c42c72fd7 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/main/java/com/baeldung/boot/mvc/MvcApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.boot.mvc; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class MvcApplication { + + public static void main(String[] args) { + SpringApplication.run(MvcApplication.class, args); + } + +} diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/main/java/com/baeldung/boot/mvc/controllers/HelloController.java b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/main/java/com/baeldung/boot/mvc/controllers/HelloController.java new file mode 100644 index 0000000000..0b4c569ba9 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/main/java/com/baeldung/boot/mvc/controllers/HelloController.java @@ -0,0 +1,21 @@ +package com.baeldung.boot.mvc.controllers; + + +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/hello") +public class HelloController { + + @GetMapping(value = "/{name}", produces = MediaType.TEXT_PLAIN_VALUE) + public ResponseEntity hello(@PathVariable String name) { + return new ResponseEntity<>("Hello, " + name, HttpStatus.OK); + } + +} diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/main/resources/application.properties b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/main/resources/application.properties new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/MvcApplicationIntegrationTests.java b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/MvcApplicationIntegrationTests.java new file mode 100644 index 0000000000..d05cb97e47 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/MvcApplicationIntegrationTests.java @@ -0,0 +1,13 @@ +package com.baeldung.boot.mvc; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class MvcApplicationIntegrationTests { + + @Test + void contextLoads() { + } + +} diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/controllers/HelloControllerUnitTest.java b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/controllers/HelloControllerUnitTest.java new file mode 100644 index 0000000000..3bef9df4fd --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/controllers/HelloControllerUnitTest.java @@ -0,0 +1,27 @@ +package com.baeldung.boot.mvc.controllers; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@Import(HelloController.class) +@ExtendWith(SpringExtension.class) +public class HelloControllerUnitTest { + + @Autowired + private HelloController helloController; + + @Test + public void whenHelloIsInvokedWithCaio_thenReturn200AsStatusAndHelloCaioAsBody() { + ResponseEntity response = this.helloController.hello("Caio"); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).isEqualTo("Hello, Caio"); + } + +} diff --git a/spring-boot-modules/spring-boot-runtime-2/README.md b/spring-boot-modules/spring-boot-runtime-2/README.md index f997f2473d..9f0d814d8d 100644 --- a/spring-boot-modules/spring-boot-runtime-2/README.md +++ b/spring-boot-modules/spring-boot-runtime-2/README.md @@ -3,4 +3,4 @@ This module contains articles about administering a Spring Boot runtime ### Relevant Articles: - - + - [Configure the Heap Size When Starting a Spring Boot Application](https://www.baeldung.com/spring-boot-heap-size) diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/config/MyCustomErrorAttributes.java b/spring-boot-rest/src/main/java/com/baeldung/web/config/MyCustomErrorAttributes.java index 1948d5552f..5e776c0e29 100644 --- a/spring-boot-rest/src/main/java/com/baeldung/web/config/MyCustomErrorAttributes.java +++ b/spring-boot-rest/src/main/java/com/baeldung/web/config/MyCustomErrorAttributes.java @@ -2,6 +2,7 @@ package com.baeldung.web.config; import java.util.Map; +import org.springframework.boot.web.error.ErrorAttributeOptions; import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; import org.springframework.stereotype.Component; import org.springframework.web.context.request.WebRequest; @@ -10,8 +11,8 @@ import org.springframework.web.context.request.WebRequest; public class MyCustomErrorAttributes extends DefaultErrorAttributes { @Override - public Map getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { - Map errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace); + public Map getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) { + Map errorAttributes = super.getErrorAttributes(webRequest, options); errorAttributes.put("locale", webRequest.getLocale() .toString()); errorAttributes.remove("error"); diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/config/MyErrorController.java b/spring-boot-rest/src/main/java/com/baeldung/web/config/MyErrorController.java index cf3f9c4dbd..05150716f6 100644 --- a/spring-boot-rest/src/main/java/com/baeldung/web/config/MyErrorController.java +++ b/spring-boot-rest/src/main/java/com/baeldung/web/config/MyErrorController.java @@ -4,7 +4,7 @@ import java.util.Map; import javax.servlet.http.HttpServletRequest; -import org.springframework.boot.autoconfigure.web.ErrorProperties; +import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController; import org.springframework.boot.web.servlet.error.ErrorAttributes; import org.springframework.http.HttpStatus; @@ -16,13 +16,13 @@ import org.springframework.web.bind.annotation.RequestMapping; @Component public class MyErrorController extends BasicErrorController { - public MyErrorController(ErrorAttributes errorAttributes) { - super(errorAttributes, new ErrorProperties()); + public MyErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties) { + super(errorAttributes, serverProperties.getError()); } @RequestMapping(produces = MediaType.APPLICATION_XML_VALUE) public ResponseEntity> xmlError(HttpServletRequest request) { - Map body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.APPLICATION_XML)); + Map body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.APPLICATION_XML)); body.put("xmlkey", "the XML response is different!"); HttpStatus status = getStatus(request); return new ResponseEntity<>(body, status); diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/controller/CustomerController.java b/spring-boot-rest/src/main/java/com/baeldung/web/controller/CustomerController.java index 2b7dc1eee1..79d3b972ff 100644 --- a/spring-boot-rest/src/main/java/com/baeldung/web/controller/CustomerController.java +++ b/spring-boot-rest/src/main/java/com/baeldung/web/controller/CustomerController.java @@ -50,7 +50,7 @@ public class CustomerController { } Link link = linkTo(methodOn(CustomerController.class).getOrdersForCustomer(customerId)).withSelfRel(); - CollectionModel result = new CollectionModel<>(orders, link); + CollectionModel result = CollectionModel.of(orders, link); return result; } @@ -72,7 +72,7 @@ public class CustomerController { } Link link = linkTo(CustomerController.class).withSelfRel(); - CollectionModel result = new CollectionModel<>(allCustomers, link); + CollectionModel result = CollectionModel.of(allCustomers, link); return result; } diff --git a/spring-boot-rest/src/main/resources/application.properties b/spring-boot-rest/src/main/resources/application.properties index 176deb4f49..1e985feed9 100644 --- a/spring-boot-rest/src/main/resources/application.properties +++ b/spring-boot-rest/src/main/resources/application.properties @@ -3,3 +3,4 @@ server.servlet.context-path=/spring-boot-rest ### Spring Boot default error handling configurations #server.error.whitelabel.enabled=false #server.error.include-stacktrace=always +server.error.include-message=always diff --git a/spring-boot-rest/src/test/java/com/baeldung/controllers/ExamplePostControllerRequestIntegrationTest.java b/spring-boot-rest/src/test/java/com/baeldung/controllers/ExamplePostControllerRequestIntegrationTest.java index a277d5a685..bb84b88bd9 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/controllers/ExamplePostControllerRequestIntegrationTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/controllers/ExamplePostControllerRequestIntegrationTest.java @@ -33,7 +33,7 @@ public class ExamplePostControllerRequestIntegrationTest { @Before public void preTest() { - MockitoAnnotations.initMocks(this); + MockitoAnnotations.openMocks(this); mockMvc = MockMvcBuilders .standaloneSetup(exampleController) .build(); diff --git a/spring-boot-rest/src/test/java/com/baeldung/controllers/ExamplePostControllerResponseIntegrationTest.java b/spring-boot-rest/src/test/java/com/baeldung/controllers/ExamplePostControllerResponseIntegrationTest.java index f8c70b0f4a..efc3310812 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/controllers/ExamplePostControllerResponseIntegrationTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/controllers/ExamplePostControllerResponseIntegrationTest.java @@ -34,7 +34,7 @@ public class ExamplePostControllerResponseIntegrationTest { @Before public void preTest() { - MockitoAnnotations.initMocks(this); + MockitoAnnotations.openMocks(this); mockMvc = MockMvcBuilders .standaloneSetup(exampleController) .build(); diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/StudentControllerIntegrationTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/StudentControllerIntegrationTest.java index 54ac69ebeb..d2d9181797 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/web/StudentControllerIntegrationTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/web/StudentControllerIntegrationTest.java @@ -8,12 +8,10 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.web.server.MediaTypeNotSupportedStatusException; import com.baeldung.web.controller.students.Student; import com.fasterxml.jackson.databind.ObjectMapper; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; diff --git a/spring-cloud/spring-cloud-openfeign/README.md b/spring-cloud/spring-cloud-openfeign/README.md index 735903db72..bcfd769d0c 100644 --- a/spring-cloud/spring-cloud-openfeign/README.md +++ b/spring-cloud/spring-cloud-openfeign/README.md @@ -2,3 +2,4 @@ - [Introduction to Spring Cloud OpenFeign](https://www.baeldung.com/spring-cloud-openfeign) - [Differences Between Netflix Feign and OpenFeign](https://www.baeldung.com/netflix-feign-vs-openfeign) +- [File Upload With Open Feign](https://www.baeldung.com/java-feign-file-upload) diff --git a/spring-cloud/spring-cloud-openfeign/pom.xml b/spring-cloud/spring-cloud-openfeign/pom.xml index c1f3f2dc30..bdde46fe96 100644 --- a/spring-cloud/spring-cloud-openfeign/pom.xml +++ b/spring-cloud/spring-cloud-openfeign/pom.xml @@ -37,12 +37,24 @@ io.github.openfeign feign-okhttp - + org.springframework.boot spring-boot-starter-web + + + io.github.openfeign.form + feign-form + 3.8.0 + + + io.github.openfeign.form + feign-form-spring + 3.8.0 + + org.springframework.boot spring-boot-starter-test diff --git a/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/fileupload/config/FeignSupportConfig.java b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/fileupload/config/FeignSupportConfig.java new file mode 100644 index 0000000000..943134213a --- /dev/null +++ b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/fileupload/config/FeignSupportConfig.java @@ -0,0 +1,22 @@ +package com.baeldung.cloud.openfeign.fileupload.config; + +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.cloud.openfeign.support.SpringEncoder; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; + +import feign.codec.Encoder; +import feign.form.spring.SpringFormEncoder; + +public class FeignSupportConfig { + @Bean + public Encoder multipartFormEncoder() { + return new SpringFormEncoder(new SpringEncoder(new ObjectFactory() { + @Override + public HttpMessageConverters getObject() { + return new HttpMessageConverters(new RestTemplate().getMessageConverters()); + } + })); + } +} diff --git a/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/fileupload/controller/FileController.java b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/fileupload/controller/FileController.java new file mode 100644 index 0000000000..ebdf7ff6c8 --- /dev/null +++ b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/fileupload/controller/FileController.java @@ -0,0 +1,28 @@ +package com.baeldung.cloud.openfeign.fileupload.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import com.baeldung.cloud.openfeign.fileupload.service.UploadService; + +@RestController +public class FileController { + + @Autowired + private UploadService service; + + @PostMapping(value = "/upload") + public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) { + return service.uploadFile(file); + } + + @PostMapping(value = "/upload-mannual-client") + public boolean handleFileUploadWithManualClient( + @RequestPart(value = "file") MultipartFile file) { + return service.uploadFileWithManualClient(file); + } + +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/fileupload/service/UploadClient.java b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/fileupload/service/UploadClient.java new file mode 100644 index 0000000000..63d17130e9 --- /dev/null +++ b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/fileupload/service/UploadClient.java @@ -0,0 +1,15 @@ +package com.baeldung.cloud.openfeign.fileupload.service; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; + +import com.baeldung.cloud.openfeign.fileupload.config.FeignSupportConfig; + +@FeignClient(name = "file", url = "http://localhost:8081", configuration = FeignSupportConfig.class) +public interface UploadClient { + @PostMapping(value = "/upload-file", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + String fileUpload(@RequestPart(value = "file") MultipartFile file); +} diff --git a/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/fileupload/service/UploadResource.java b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/fileupload/service/UploadResource.java new file mode 100644 index 0000000000..26e658a7f0 --- /dev/null +++ b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/fileupload/service/UploadResource.java @@ -0,0 +1,16 @@ +package com.baeldung.cloud.openfeign.fileupload.service; + +import org.springframework.web.multipart.MultipartFile; + +import feign.Headers; +import feign.Param; +import feign.RequestLine; +import feign.Response; + +public interface UploadResource { + + @RequestLine("POST /upload-file") + @Headers("Content-Type: multipart/form-data") + Response uploadFile(@Param("file") MultipartFile file); + +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/fileupload/service/UploadService.java b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/fileupload/service/UploadService.java new file mode 100644 index 0000000000..7dd7f5a89c --- /dev/null +++ b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/fileupload/service/UploadService.java @@ -0,0 +1,29 @@ +package com.baeldung.cloud.openfeign.fileupload.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import feign.Feign; +import feign.Response; +import feign.form.spring.SpringFormEncoder; + +@Service +public class UploadService { + private static final String HTTP_FILE_UPLOAD_URL = "http://localhost:8081"; + + @Autowired + private UploadClient client; + + public boolean uploadFileWithManualClient(MultipartFile file) { + UploadResource fileUploadResource = Feign.builder().encoder(new SpringFormEncoder()) + .target(UploadResource.class, HTTP_FILE_UPLOAD_URL); + Response response = fileUploadResource.uploadFile(file); + return response.status() == 200; + } + + public String uploadFile(MultipartFile file) { + return client.fileUpload(file); + } + +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-openfeign/src/main/resources/fileupload.txt b/spring-cloud/spring-cloud-openfeign/src/main/resources/fileupload.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spring-cloud/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/OpenFeignFileUploadLiveTest.java b/spring-cloud/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/OpenFeignFileUploadLiveTest.java new file mode 100644 index 0000000000..f558e07491 --- /dev/null +++ b/spring-cloud/spring-cloud-openfeign/src/test/java/com/baeldung/cloud/openfeign/OpenFeignFileUploadLiveTest.java @@ -0,0 +1,50 @@ +package com.baeldung.cloud.openfeign; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +import org.apache.commons.io.IOUtils; +import org.junit.Assert; +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.mock.web.MockMultipartFile; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.multipart.MultipartFile; + +import com.baeldung.cloud.openfeign.fileupload.service.UploadService; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class OpenFeignFileUploadLiveTest { + + @Autowired + private UploadService uploadService; + + private static String FILE_NAME = "fileupload.txt"; + + @Test + public void whenFeignBuilder_thenFileUploadSuccess() throws IOException { + ClassLoader classloader = Thread.currentThread().getContextClassLoader(); + File file = new File(classloader.getResource(FILE_NAME).getFile()); + Assert.assertTrue(file.exists()); + FileInputStream input = new FileInputStream(file); + MultipartFile multipartFile = new MockMultipartFile("file", file.getName(), "text/plain", + IOUtils.toByteArray(input)); + Assert.assertTrue(uploadService.uploadFileWithManualClient(multipartFile)); + } + + @Test + public void whenAnnotatedFeignClient_thenFileUploadSuccess() throws IOException { + ClassLoader classloader = Thread.currentThread().getContextClassLoader(); + File file = new File(classloader.getResource(FILE_NAME).getFile()); + Assert.assertTrue(file.exists()); + FileInputStream input = new FileInputStream(file); + MultipartFile multipartFile = new MockMultipartFile("file", file.getName(), "text/plain", + IOUtils.toByteArray(input)); + String uploadFile = uploadService.uploadFile(multipartFile); + Assert.assertNotNull(uploadFile); + } +} diff --git a/spring-core-5/README.md b/spring-core-5/README.md index 81669e46a7..4109faca67 100644 --- a/spring-core-5/README.md +++ b/spring-core-5/README.md @@ -4,4 +4,5 @@ This module contains articles about core Spring functionality ## Relevant Articles: -- More articles: [[<-- prev]](/spring-core-4) \ No newline at end of file +- [Spring @Component Annotation](https://www.baeldung.com/spring-component-annotation) +- More articles: [[<-- prev]](/spring-core-4) diff --git a/spring-data-rest/pom.xml b/spring-data-rest/pom.xml index dd96182264..bfbd66a280 100644 --- a/spring-data-rest/pom.xml +++ b/spring-data-rest/pom.xml @@ -35,7 +35,7 @@ org.springframework.data - spring-data-rest-hal-browser + spring-data-rest-hal-explorer org.springframework.boot @@ -99,7 +99,6 @@ com.baeldung.books.SpringDataRestApplication 1.0 - 2.3.3.RELEASE \ No newline at end of file diff --git a/spring-data-rest/src/main/java/com/baeldung/halbrowser/config/RestConfig.java b/spring-data-rest/src/main/java/com/baeldung/halbrowser/config/RestConfig.java index 73f7e0f26a..a322bf0027 100644 --- a/spring-data-rest/src/main/java/com/baeldung/halbrowser/config/RestConfig.java +++ b/spring-data-rest/src/main/java/com/baeldung/halbrowser/config/RestConfig.java @@ -4,11 +4,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener; -import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter; +import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer; import org.springframework.validation.Validator; @Configuration -public class RestConfig extends RepositoryRestConfigurerAdapter { +public class RestConfig implements RepositoryRestConfigurer { //access to global validator @Autowired diff --git a/spring-security-modules/spring-5-security/README.md b/spring-security-modules/spring-5-security/README.md index 1917d347fb..bad99c22d4 100644 --- a/spring-security-modules/spring-5-security/README.md +++ b/spring-security-modules/spring-5-security/README.md @@ -11,3 +11,4 @@ This module contains articles about Spring Security 5 - [Guide to the AuthenticationManagerResolver in Spring Security](https://www.baeldung.com/spring-security-authenticationmanagerresolver) - [Manual Logout With Spring Security](https://www.baeldung.com/spring-security-manual-logout) - [How to Disable Spring Security Logout Redirects](https://www.baeldung.com/spring-security-disable-logout-redirects) +- [Prevent Cross-Site Scripting (XSS) in a Spring Application](https://www.baeldung.com/spring-prevent-xss) diff --git a/spring-web-modules/spring-mvc-java/src/test/java/com/baeldung/web/controller/GreetControllerIntegrationTest.java b/spring-web-modules/spring-mvc-java/src/test/java/com/baeldung/web/controller/GreetControllerIntegrationTest.java index 3d34a46791..7b9da5707d 100644 --- a/spring-web-modules/spring-mvc-java/src/test/java/com/baeldung/web/controller/GreetControllerIntegrationTest.java +++ b/spring-web-modules/spring-mvc-java/src/test/java/com/baeldung/web/controller/GreetControllerIntegrationTest.java @@ -1,17 +1,13 @@ package com.baeldung.web.controller; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; - -import javax.servlet.ServletContext; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import com.baeldung.spring.web.config.WebConfig; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mock.web.MockServletContext; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -20,31 +16,34 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; -import com.baeldung.spring.web.config.WebConfig; +import javax.servlet.ServletContext; -@RunWith(SpringJUnit4ClassRunner.class) +import static org.junit.jupiter.api.Assertions.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = {WebConfig.class}) @WebAppConfiguration -@ContextConfiguration(classes = { WebConfig.class, WebConfig.class }) public class GreetControllerIntegrationTest { @Autowired - private WebApplicationContext wac; + private WebApplicationContext webApplicationContext; private MockMvc mockMvc; private static final String CONTENT_TYPE = "application/json"; - @Before - public void setup() throws Exception { - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + @BeforeEach + public void setup() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build(); } @Test public void givenWac_whenServletContext_thenItProvidesGreetController() { - final ServletContext servletContext = wac.getServletContext(); - Assert.assertNotNull(servletContext); - Assert.assertTrue(servletContext instanceof MockServletContext); - Assert.assertNotNull(wac.getBean("greetController")); + final ServletContext servletContext = webApplicationContext.getServletContext(); + assertNotNull(servletContext); + assertTrue(servletContext instanceof MockServletContext); + assertNotNull(webApplicationContext.getBean("greetController")); } @Test @@ -54,8 +53,12 @@ public class GreetControllerIntegrationTest { @Test public void givenGreetURI_whenMockMVC_thenVerifyResponse() throws Exception { - final MvcResult mvcResult = this.mockMvc.perform(MockMvcRequestBuilders.get("/greet")).andDo(print()).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$.message").value("Hello World!!!")).andReturn(); - Assert.assertEquals(CONTENT_TYPE, mvcResult.getResponse().getContentType()); + final MvcResult mvcResult = this.mockMvc.perform(MockMvcRequestBuilders.get("/greet")) + .andDo(print()) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("Hello World!!!")) + .andReturn(); + assertEquals(CONTENT_TYPE, mvcResult.getResponse().getContentType()); } @Test diff --git a/spring-web-modules/spring-mvc-java/src/test/java/com/baeldung/web/controller/GreetControllerRealIntegrationTest.java b/spring-web-modules/spring-mvc-java/src/test/java/com/baeldung/web/controller/GreetControllerRealIntegrationTest.java index 05c6313e76..825520526e 100644 --- a/spring-web-modules/spring-mvc-java/src/test/java/com/baeldung/web/controller/GreetControllerRealIntegrationTest.java +++ b/spring-web-modules/spring-mvc-java/src/test/java/com/baeldung/web/controller/GreetControllerRealIntegrationTest.java @@ -1,18 +1,15 @@ package com.baeldung.web.controller; import io.restassured.RestAssured; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringRunner; import static io.restassured.RestAssured.given; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; -@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = RANDOM_PORT) @TestPropertySource(properties = {"spring.main.allow-bean-definition-overriding=true", "server.servlet.context-path=/"}) public class GreetControllerRealIntegrationTest { @@ -20,7 +17,7 @@ public class GreetControllerRealIntegrationTest { @LocalServerPort private int port; - @Before + @BeforeEach public void setUp() { RestAssured.port = port; } @@ -28,7 +25,7 @@ public class GreetControllerRealIntegrationTest { @Test public void givenGreetURI_whenSendingReq_thenVerifyResponse() { given().get("/greet") - .then() - .statusCode(200); + .then() + .statusCode(200); } } diff --git a/spring-web-modules/spring-mvc-java/src/test/java/com/baeldung/web/controller/GreetControllerUnitTest.java b/spring-web-modules/spring-mvc-java/src/test/java/com/baeldung/web/controller/GreetControllerUnitTest.java index eacd256438..ecc55e8da2 100644 --- a/spring-web-modules/spring-mvc-java/src/test/java/com/baeldung/web/controller/GreetControllerUnitTest.java +++ b/spring-web-modules/spring-mvc-java/src/test/java/com/baeldung/web/controller/GreetControllerUnitTest.java @@ -1,25 +1,22 @@ package com.baeldung.web.controller; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; - -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + public class GreetControllerUnitTest { private MockMvc mockMvc; private static final String CONTENT_TYPE = "application/json"; - @Before - public void setup() { + @BeforeEach + void setUp() { this.mockMvc = MockMvcBuilders.standaloneSetup(new GreetController()).build(); } @@ -50,12 +47,12 @@ public class GreetControllerUnitTest { @Test public void givenGreetURIWithPost_whenMockMVC_thenVerifyResponse() throws Exception { - this.mockMvc.perform(MockMvcRequestBuilders.post("/greetWithPost")).andDo(print()).andExpect(status().isOk()).andExpect(content().contentType(CONTENT_TYPE)).andExpect(jsonPath("$.message").value("Hello World!!!")); + this.mockMvc.perform(post("/greetWithPost")).andDo(print()).andExpect(status().isOk()).andExpect(content().contentType(CONTENT_TYPE)).andExpect(jsonPath("$.message").value("Hello World!!!")); } @Test public void givenGreetURIWithPostAndFormData_whenMockMVC_thenVerifyResponse() throws Exception { - this.mockMvc.perform(MockMvcRequestBuilders.post("/greetWithPostAndFormData").param("id", "1").param("name", "John Doe")).andDo(print()).andExpect(status().isOk()).andExpect(content().contentType(CONTENT_TYPE)) + this.mockMvc.perform(post("/greetWithPostAndFormData").param("id", "1").param("name", "John Doe")).andDo(print()).andExpect(status().isOk()).andExpect(content().contentType(CONTENT_TYPE)) .andExpect(jsonPath("$.message").value("Hello World John Doe!!!")).andExpect(jsonPath("$.id").value(1)); } } diff --git a/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/sampleapp/web/controller/redirect/RedirectController.java b/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/sampleapp/web/controller/redirect/RedirectController.java index 321f3be3ef..1d77a07bea 100644 --- a/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/sampleapp/web/controller/redirect/RedirectController.java +++ b/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/sampleapp/web/controller/redirect/RedirectController.java @@ -64,5 +64,11 @@ public class RedirectController { public ModelAndView redirectedPostToPost() { return new ModelAndView("redirection"); } - + + @RequestMapping(value="/forwardWithParams", method = RequestMethod.GET) + public ModelAndView forwardWithParams(HttpServletRequest request) { + request.setAttribute("param1", "one"); + request.setAttribute("param2", "two"); + return new ModelAndView("forward:/forwardedWithParams"); + } } \ No newline at end of file diff --git a/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/sampleapp/web/controller/redirect/RedirectParamController.java b/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/sampleapp/web/controller/redirect/RedirectParamController.java new file mode 100644 index 0000000000..abe268b435 --- /dev/null +++ b/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/sampleapp/web/controller/redirect/RedirectParamController.java @@ -0,0 +1,25 @@ +package com.baeldung.sampleapp.web.controller.redirect; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import org.springframework.web.servlet.view.RedirectView; + +@Controller +@RequestMapping("/") +public class RedirectParamController { + + @RequestMapping(value = "/forwardedWithParams", method = RequestMethod.GET) + public RedirectView forwardedWithParams(final RedirectAttributes redirectAttributes, HttpServletRequest request) { + + redirectAttributes.addAttribute("param1", request.getAttribute("param1")); + redirectAttributes.addAttribute("param2", request.getAttribute("param2")); + + redirectAttributes.addAttribute("attribute", "forwardedWithParams"); + return new RedirectView("redirectedUrl"); + + } +} \ No newline at end of file diff --git a/testing-modules/README.md b/testing-modules/README.md deleted file mode 100644 index c6098d1210..0000000000 --- a/testing-modules/README.md +++ /dev/null @@ -1,3 +0,0 @@ -## Testing Modules - -This is an aggregator module containing multiple modules focused on testing libraries. diff --git a/testing-modules/pom.xml b/testing-modules/pom.xml index 0416423239..fd4a13d026 100644 --- a/testing-modules/pom.xml +++ b/testing-modules/pom.xml @@ -31,7 +31,7 @@ rest-assured rest-testing selenium-junit-testng - spring-testing + spring-testing spring-testing-2 test-containers testing-assertions @@ -41,9 +41,10 @@ junit-5-advanced xmlunit-2 junit-4 - testing-libraries - testing-libraries-2 + testing-libraries + testing-libraries-2 powermock + zerocode diff --git a/testing-modules/zerocode/pom.xml b/testing-modules/zerocode/pom.xml new file mode 100644 index 0000000000..9d765e6cb4 --- /dev/null +++ b/testing-modules/zerocode/pom.xml @@ -0,0 +1,102 @@ + + + 4.0.0 + + testing-modules + com.baeldung + 1.0.0-SNAPSHOT + + + zerocode + 1.0-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-web + ${spring.boot.version} + + + + org.springframework.boot + spring-boot-starter-test + ${spring.boot.version} + test + + + + + org.jsmart + zerocode-tdd + 1.3.27 + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + it + + + + + pre-integration-test + + start + + + ${skip.it} + + + + post-integration-test + + stop + + + ${skip.it} + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + 3.0.0-M5 + + ${skip.it} + + + + org.apache.maven.surefire + surefire-junit47 + 3.0.0-M5 + + + + + + integration-test + verify + + + + + + + + + UTF-8 + 8 + 8 + 2.4.2 + true + + + diff --git a/testing-modules/zerocode/src/main/java/com/baeldung/zerocode/User.java b/testing-modules/zerocode/src/main/java/com/baeldung/zerocode/User.java new file mode 100644 index 0000000000..3a2a853220 --- /dev/null +++ b/testing-modules/zerocode/src/main/java/com/baeldung/zerocode/User.java @@ -0,0 +1,31 @@ +package com.baeldung.zerocode; + +public class User { + private String id; + private String firstName; + private String lastName; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } +} diff --git a/testing-modules/zerocode/src/main/java/com/baeldung/zerocode/ZerocodeApplication.java b/testing-modules/zerocode/src/main/java/com/baeldung/zerocode/ZerocodeApplication.java new file mode 100644 index 0000000000..3218e97400 --- /dev/null +++ b/testing-modules/zerocode/src/main/java/com/baeldung/zerocode/ZerocodeApplication.java @@ -0,0 +1,38 @@ +package com.baeldung.zerocode; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; + +@SpringBootApplication +@RestController +@RequestMapping("/api/users") +public class ZerocodeApplication { + private List users = new ArrayList<>(); + + public static void main(String[] args) { + SpringApplication.run(ZerocodeApplication.class, args); + } + + @PostMapping + public ResponseEntity create(@RequestBody User user) { + if (!StringUtils.hasText(user.getFirstName())) { + return new ResponseEntity("firstName can't be empty!", HttpStatus.BAD_REQUEST); + } + if (!StringUtils.hasText(user.getLastName())) { + return new ResponseEntity("lastName can't be empty!", HttpStatus.BAD_REQUEST); + } + user.setId(UUID.randomUUID() + .toString()); + users.add(user); + return new ResponseEntity(user, HttpStatus.CREATED); + } + +} diff --git a/testing-modules/zerocode/src/test/java/com/baeldung/zerocode/rest/UserEndpointIT.java b/testing-modules/zerocode/src/test/java/com/baeldung/zerocode/rest/UserEndpointIT.java new file mode 100644 index 0000000000..cc461fd0fc --- /dev/null +++ b/testing-modules/zerocode/src/test/java/com/baeldung/zerocode/rest/UserEndpointIT.java @@ -0,0 +1,18 @@ +package com.baeldung.zerocode.rest; + +import org.jsmart.zerocode.core.domain.Scenario; +import org.jsmart.zerocode.core.domain.TargetEnv; +import org.jsmart.zerocode.core.runner.ZeroCodeUnitRunner; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(ZeroCodeUnitRunner.class) +@TargetEnv("rest_api.properties") +public class UserEndpointIT { + + @Test + @Scenario("rest/user_create_test.json") + public void test_user_creation_endpoint() { + } + +} diff --git a/testing-modules/zerocode/src/test/resources/rest/user_create_test.json b/testing-modules/zerocode/src/test/resources/rest/user_create_test.json new file mode 100644 index 0000000000..0e8ee66196 --- /dev/null +++ b/testing-modules/zerocode/src/test/resources/rest/user_create_test.json @@ -0,0 +1,39 @@ +{ + "scenarioName": "test user creation endpoint", + "steps": [ + { + "name": "test_successful_creation", + "url": "/api/users", + "method": "POST", + "request": { + "body": { + "firstName": "John", + "lastName": "Doe" + } + }, + "verify": { + "status": 201, + "body": { + "id": "$NOT.NULL", + "firstName": "John", + "lastName": "Doe" + } + } + }, + { + "name": "test_firstname_validation", + "url": "/api/users", + "method": "POST", + "request": { + "body": { + "firstName": "", + "lastName": "Doe" + } + }, + "assertions": { + "status": 400, + "rawBody": "firstName can't be empty!" + } + } + ] +} diff --git a/testing-modules/zerocode/src/test/resources/rest_api.properties b/testing-modules/zerocode/src/test/resources/rest_api.properties new file mode 100644 index 0000000000..724042ade7 --- /dev/null +++ b/testing-modules/zerocode/src/test/resources/rest_api.properties @@ -0,0 +1,3 @@ +web.application.endpoint.host=http://localhost +web.application.endpoint.port=8080 +web.application.endpoint.context= \ No newline at end of file