Merge pull request #1 from eugenp/master

update
This commit is contained in:
Hamid Reza Sharifi 2021-02-27 15:32:47 +03:30 committed by GitHub
commit 2d442fed14
151 changed files with 3353 additions and 202 deletions

View File

@ -5,7 +5,7 @@ This module contains articles about Java 10 core features
### Relevant Articles:
- [Java 10 LocalVariable Type-Inference](http://www.baeldung.com/java-10-local-variable-type-inference)
- [Guide to Java 10](http://www.baeldung.com/java-10-overview)
- [New Features in Java 10](https://www.baeldung.com/java-10-overview)
- [Copy a List to Another List in Java](http://www.baeldung.com/java-copy-list-to-another)
- [Deep Dive Into the New Java JIT Compiler Graal](https://www.baeldung.com/graal-java-jit-compiler)
- [Copying Sets in Java](https://www.baeldung.com/java-copy-sets)

View File

@ -1,5 +1,4 @@
## Relevant Articles:
- [String API Updates in Java 12](https://www.baeldung.com/java12-string-api)
- [Java 12 New Features](https://www.baeldung.com/java-12-new-features)
- [New Features in Java 12](https://www.baeldung.com/java-12-new-features)

View File

@ -1,4 +1,4 @@
### Relevant articles:
- [Java Switch Statement](https://www.baeldung.com/java-switch)
- [New Java 13 Features](https://www.baeldung.com/java-13-new-features)
- [New Features in Java 13](https://www.baeldung.com/java-13-new-features)

View File

@ -10,4 +10,4 @@ This module contains articles about Java 14.
- [Helpful NullPointerExceptions in Java 14](https://www.baeldung.com/java-14-nullpointerexception)
- [Foreign Memory Access API in Java 14](https://www.baeldung.com/java-foreign-memory-access)
- [Java 14 Record Keyword](https://www.baeldung.com/java-record-keyword)
- [Java 14 New Features](https://www.baeldung.com/java-14-new-features)
- [New Features in Java 14](https://www.baeldung.com/java-14-new-features)

View File

@ -4,7 +4,7 @@ This module contains articles about core Java features that have been introduced
### Relevant Articles:
- [Java 9 New Features](https://www.baeldung.com/new-java-9)
- [New Features in Java 9](https://www.baeldung.com/new-java-9)
- [Java 9 Variable Handles Demystified](http://www.baeldung.com/java-variable-handles)
- [Exploring the New HTTP Client in Java 9 and 11](http://www.baeldung.com/java-9-http-client)
- [Multi-Release Jar Files](https://www.baeldung.com/java-multi-release-jar)

View File

@ -0,0 +1,8 @@
package com.baeldung.annotations;
import javax.annotation.Generated;
@RetentionAnnotation
@Generated("Available only on source code")
public class AnnotatedClass {
}

View File

@ -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 {
}

View File

@ -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));
}
}

View File

@ -3,4 +3,4 @@
This module contains articles about Java Character Class
### Relevant Articles:
- [Character#isAlphabetic vs Character#isLetter](https://www.baeldung.com/java-character-isletter-isalphabetic)
- [Character#isAlphabetic vs. Character#isLetter](https://www.baeldung.com/java-character-isletter-isalphabetic)

View File

@ -25,7 +25,7 @@
</dependencies>
<properties>
<assertj.version>3.18.0</assertj.version>
<assertj.version>3.19.0</assertj.version>
</properties>
</project>

View File

@ -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<E> implements LifoStack<E> {
private final Deque<E> 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<E> iterator() {
return deque.iterator();
}
@Override
public Object[] toArray() {
return deque.toArray();
}
@Override
public <T> 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<? extends E> 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();
}
}

View File

@ -0,0 +1,12 @@
package com.baeldung.collections.dequestack;
import java.util.Collection;
public interface LifoStack<E> extends Collection<E> {
E peek();
E pop();
void push(E item);
}

View File

@ -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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> it = myStack.iterator();
assertThat(it).toIterable().containsExactly(
"I am at the top.",
"I am in the middle.",
"I am at the bottom.");
}
}

View File

@ -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)

View File

@ -20,6 +20,11 @@
<artifactId>jmh-core</artifactId>
<version>${jmh-core.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
</dependencies>
<properties>

View File

@ -0,0 +1,39 @@
package com.baeldung.map.propertieshashmap;
import com.google.common.collect.Maps;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
public class PropertiesToHashMapConverter {
@SuppressWarnings({"rawtypes", "unchecked"})
public static HashMap<String, String> typeCastConvert(Properties prop) {
Map step1 = prop;
Map<String, String> step2 = (Map<String, String>) step1;
return new HashMap<>(step2);
}
public static HashMap<String, String> loopConvert(Properties prop) {
HashMap<String, String> retMap = new HashMap<>();
for (Map.Entry<Object, Object> entry : prop.entrySet()) {
retMap.put(String.valueOf(entry.getKey()), String.valueOf(entry.getValue()));
}
return retMap;
}
public static HashMap<String, String> streamConvert(Properties prop) {
return prop.entrySet().stream().collect(
Collectors.toMap(
e -> String.valueOf(e.getKey()),
e -> String.valueOf(e.getValue()),
(prev, next) -> next, HashMap::new
));
}
public static HashMap<String, String> guavaConvert(Properties prop) {
return Maps.newHashMap(Maps.fromProperties(prop));
}
}

View File

@ -0,0 +1,192 @@
package com.baeldung.map.propertieshashmap;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Properties;
import static org.junit.jupiter.api.Assertions.*;
class PropertiesToHashMapConverterUnitTest {
private Properties properties;
private final static String propertyFileName = "toHashMap.properties";
@BeforeEach
public void setup() throws IOException {
properties = new Properties();
try (InputStream is = getClass().getClassLoader().getResourceAsStream(propertyFileName)) {
if (is != null) {
properties.load(is);
}
}
}
@Test
public void havingPropertiesLoaded_whenCheck_thenEquals() {
assertEquals(3, properties.size());
assertEquals("str_value", properties.get("property1"));
assertEquals("123", properties.get("property2"));
assertEquals("", properties.get("property3"));
}
@Test
public void whenPropertiesModified_thenTypeSafeIssues() {
compromiseProperties(properties);
assertEquals(5, properties.size());
assertNull(properties.getProperty("property4"));
assertNotEquals(String.class, properties.get("property4").getClass());
assertEquals(456, properties.get("property4"));
assertNull(properties.getProperty("5"));
assertNotEquals(String.class, properties.get(5).getClass());
assertEquals(10.11, properties.get(5));
}
@Test
public void havingNonModifiedProperties_whenTypeCastConvert_thenNoTypeSafeIssues() {
HashMap<String, String> hMap = PropertiesToHashMapConverter.typeCastConvert(properties);
assertEquals(3, hMap.size());
assertEquals(String.class, hMap.get("property1").getClass());
assertEquals(properties.get("property1"), hMap.get("property1"));
assertEquals(String.class, hMap.get("property2").getClass());
assertEquals(properties.get("property2"), hMap.get("property2"));
assertEquals(String.class, hMap.get("property3").getClass());
assertEquals(properties.get("property3"), hMap.get("property3"));
}
@Test
public void havingModifiedProperties_whenTypeCastConvert_thenClassCastException() {
compromiseProperties(properties);
HashMap<String, String> hMap = PropertiesToHashMapConverter.typeCastConvert(properties);
assertEquals(5, hMap.size());
assertThrows(ClassCastException.class, () -> {
String s = hMap.get("property4");
});
assertEquals(Integer.class, ((Object) hMap.get("property4")).getClass());
assertNull(hMap.get("5"));
assertNotNull(hMap.get(5));
assertThrows(ClassCastException.class, () -> {
String s = hMap.get(5);
});
assertEquals(Double.class, ((Object) hMap.get(5)).getClass());
}
@Test
public void havingNonModifiedProperties_whenLoopConvert_thenNoTypeSafeIssues() {
HashMap<String, String> hMap = PropertiesToHashMapConverter.loopConvert(properties);
assertEquals(3, hMap.size());
assertEquals(String.class, hMap.get("property1").getClass());
assertEquals(properties.get("property1"), hMap.get("property1"));
assertEquals(String.class, hMap.get("property2").getClass());
assertEquals(properties.get("property2"), hMap.get("property2"));
assertEquals(String.class, hMap.get("property3").getClass());
assertEquals(properties.get("property3"), hMap.get("property3"));
}
@Test
public void havingModifiedProperties_whenLoopConvert_thenNoClassCastException() {
compromiseProperties(properties);
HashMap<String, String> hMap = PropertiesToHashMapConverter.loopConvert(properties);
assertEquals(5, hMap.size());
assertDoesNotThrow(() -> {
String s = hMap.get("property4");
});
assertEquals(String.class, hMap.get("property4").getClass());
assertEquals("456", hMap.get("property4"));
assertDoesNotThrow(() -> {
String s = hMap.get("5");
});
assertEquals("10.11", hMap.get("5"));
}
@Test
public void havingNonModifiedProperties_whenStreamConvert_thenNoTypeSafeIssues() {
HashMap<String, String> hMap = PropertiesToHashMapConverter.streamConvert(properties);
assertEquals(3, hMap.size());
assertEquals(String.class, hMap.get("property1").getClass());
assertEquals(properties.get("property1"), hMap.get("property1"));
assertEquals(String.class, hMap.get("property2").getClass());
assertEquals(properties.get("property2"), hMap.get("property2"));
assertEquals(String.class, hMap.get("property3").getClass());
assertEquals(properties.get("property3"), hMap.get("property3"));
}
@Test
public void havingModifiedProperties_whenStreamConvert_thenNoClassCastException() {
compromiseProperties(properties);
HashMap<String, String> hMap = PropertiesToHashMapConverter.streamConvert(properties);
assertEquals(5, hMap.size());
assertDoesNotThrow(() -> {
String s = hMap.get("property4");
});
assertEquals(String.class, hMap.get("property4").getClass());
assertEquals("456", hMap.get("property4"));
assertDoesNotThrow(() -> {
String s = hMap.get("5");
});
assertEquals("10.11", hMap.get("5"));
}
@Test
public void havingModifiedProperties_whenLoopConvertAndStreamConvert_thenHashMapsSame() {
compromiseProperties(properties);
HashMap<String, String> hMap1 = PropertiesToHashMapConverter.loopConvert(properties);
HashMap<String, String> hMap2 = PropertiesToHashMapConverter.streamConvert(properties);
assertEquals(hMap2, hMap1);
}
@Test
public void havingNonModifiedProperties_whenGuavaConvert_thenNoTypeSafeIssues() {
HashMap<String, String> hMap = PropertiesToHashMapConverter.guavaConvert(properties);
assertEquals(3, hMap.size());
assertEquals(String.class, hMap.get("property1").getClass());
assertEquals(properties.get("property1"), hMap.get("property1"));
assertEquals(String.class, hMap.get("property2").getClass());
assertEquals(properties.get("property2"), hMap.get("property2"));
assertEquals(String.class, hMap.get("property3").getClass());
assertEquals(properties.get("property3"), hMap.get("property3"));
}
@Test
public void havingModifiedProperties_whenGuavaConvert_thenUnableToConvertAndThrowException() {
compromiseProperties(properties);
assertThrows(Exception.class, () -> PropertiesToHashMapConverter.guavaConvert(properties));
}
@Test
public void havingModifiedPropertiesWithNoIntegerValue_whenGuavaConvert_thenNullPointerException() {
properties.put("property4", 456);
assertThrows(NullPointerException.class, () -> PropertiesToHashMapConverter.guavaConvert(properties));
}
@Test
public void havingModifiedPropertiesWithNoIntegerKey_whenGuavaConvert_thenClassCastException() {
properties.put(5, 10.11);
assertThrows(ClassCastException.class, () -> PropertiesToHashMapConverter.guavaConvert(properties));
}
private void compromiseProperties(Properties prop) {
prop.put("property4", 456);
prop.put(5, 10.11);
}
}

View File

@ -0,0 +1,3 @@
property1=str_value
property2=123
property3=

View File

@ -17,7 +17,7 @@
<dependencies>
<dependency>
<groupId>com.baeldung.servicemodule</groupId>
<artifactId>servicemodule</artifactId>
<artifactId>servicemodule2</artifactId>
<version>${servicemodule.version}</version>
</dependency>
<dependency>
@ -41,4 +41,4 @@
<providermodule.version>1.0</providermodule.version>
</properties>
</project>
</project>

View File

@ -11,4 +11,5 @@ This module contains articles about core features in the Java language
- [The transient Keyword in Java](https://www.baeldung.com/java-transient-keyword)
- [How to Access an Iteration Counter in a For Each Loop](https://www.baeldung.com/java-foreach-counter)
- [Comparing Doubles in Java](https://www.baeldung.com/java-comparing-doubles)
- [Guide to Implementing the compareTo Method](https://www.baeldung.com/java-compareto)
- [[<-- Prev]](/core-java-modules/core-java-lang-2)

View File

@ -4,6 +4,5 @@
### Relevant articles:
- [Calculate Factorial in Java](https://www.baeldung.com/java-calculate-factorial)
- [Evaluating a Math Expression in Java](https://www.baeldung.com/java-evaluate-math-expression-string)
- More articles: [[<-- Prev]](/core-java-modules/core-java-lang-math-2)

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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<String, LocalDate> castFromRawMap = (Map<String, LocalDate>) 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<String, LocalDate> castFromRawMap = (Map<String, LocalDate>) UncheckedCast.getRawMapWithMixedTypes();
Assert.assertEquals(4, castFromRawMap.size());
Assert.assertTrue(castFromRawMap.get("date 4").isAfter(castFromRawMap.get("date 3")));
}
}

View File

@ -14,5 +14,12 @@
<version>0.0.1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.6.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -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<String> 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"
);
}
}

View File

@ -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)

View File

@ -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

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.baeldung.docker</groupId>
<artifactId>heap-sizing</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>heap-sizing</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>heapsizing-demo</name>
</image>
</configuration>
</plugin>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>2.7.1</version>
<configuration>
<to>
<image>heapsizing-demo-jib</image>
</to>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -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);
}
}

View File

@ -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 });
}
}
}

View File

@ -1,3 +1,3 @@
### Relevant Articles:
- [Kotlin vs Java](https://www.baeldung.com/kotlin/kotlin-vs-java)
- [Kotlin vs Java](https://www.baeldung.com/kotlin/vs-java)

View File

@ -0,0 +1,6 @@
package com.baeldung.map.hashing;
class Member {
Integer id;
String name;
}

View File

@ -0,0 +1,8 @@
package com.baeldung.map.hashing;
public class MemberWithBadHashing extends Member {
@Override
public int hashCode() {
return name.hashCode();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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 <T extends Member> Duration[] testDuration(Supplier<T> factory) {
HashMap<T, String> 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};
}
}

View File

@ -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 :)

View File

@ -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()));
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.4.1">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
<stringProp name="TestPlan.comments">To run this test plan you must also be running the Spring application &quot;JmeterApplication&quot; That can be found in this directory</stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<stringProp name="LoopController.loops">1</stringProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">1</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
</ThreadGroup>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Call GET Test endpoint " enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">localhost</stringProp>
<stringProp name="HTTPSampler.port">8080</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">/api/test</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree>
<JSONPostProcessor guiclass="JSONPostProcessorGui" testclass="JSONPostProcessor" testname="JSON Extractor" enabled="true">
<stringProp name="JSONPostProcessor.referenceNames">message</stringProp>
<stringProp name="JSONPostProcessor.jsonPathExprs">$.message</stringProp>
<stringProp name="JSONPostProcessor.match_numbers">1</stringProp>
<boolProp name="JSONPostProcessor.compute_concat">true</boolProp>
<stringProp name="JSONPostProcessor.defaultValues">NOT_FOUND</stringProp>
</JSONPostProcessor>
<hashTree/>
<BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Response to file using BeanShell PostProcessor" enabled="true">
<stringProp name="filename"></stringProp>
<stringProp name="parameters"></stringProp>
<boolProp name="resetInterpreter">false</boolProp>
<stringProp name="script">FileWriter fWriter = new FileWriter(&quot;<path>/result.txt&quot;, true);
BufferedWriter buff = new BufferedWriter(fWriter);
buff.write(&quot;Response Code : &quot; + ctx.getPreviousResult().getResponseCode());
buff.write(System.getProperty(&quot;line.separator&quot;));
buff.write(&quot;Response Headers : &quot; + ctx.getPreviousResult().getResponseHeaders());
buff.write(System.getProperty(&quot;line.separator&quot;));
buff.write(&quot;Response Body : &quot; + new String(ctx.getPreviousResult().getResponseData()));
buff.write(&quot;More complex extraction : &quot; + vars.get(&quot;message&quot;));
buff.close();
fWriter.close();</stringProp>
</BeanShellPostProcessor>
<hashTree/>
<ResultSaver guiclass="ResultSaverGui" testclass="ResultSaver" testname="Response to file using file write Listener" enabled="true">
<stringProp name="FileSaver.filename">response</stringProp>
<boolProp name="FileSaver.errorsonly">false</boolProp>
<boolProp name="FileSaver.successonly">false</boolProp>
<boolProp name="FileSaver.skipsuffix">false</boolProp>
<boolProp name="FileSaver.skipautonumber">false</boolProp>
</ResultSaver>
<hashTree/>
</hashTree>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>

View File

@ -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...");
}
}

View File

@ -27,6 +27,7 @@
<module>versions-maven-plugin</module>
<module>version-collision</module>
<module>optional-dependencies</module>
<module>version-overriding-plugins</module>
</modules>
</project>

View File

@ -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)

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>version-overriding-plugins</artifactId>
<groupId>com.baeldung</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<artifactId>child-a</artifactId>
<build>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<resources combine.self="override">
<resource>
<directory>child-a-resources</directory>
</resource>
</resources>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>version-overriding-plugins</artifactId>
<groupId>com.baeldung</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>child-b</artifactId>
</project>

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>maven-modules</artifactId>
<groupId>com.baeldung</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<properties>
<maven.compiler.plugin>3.8.0</maven.compiler.plugin>
</properties>
<modelVersion>4.0.0</modelVersion>
<artifactId>version-overriding-plugins</artifactId>
<packaging>pom</packaging>
<modules>
<module>child-a</module>
<module>child-b</module>
</modules>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.plugin}</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<resources combine.children="append">
<resource>
<directory>parent-resources</directory>
</resource>
</resources>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
<maxmem>512m</maxmem>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -631,6 +631,7 @@
<module>spring-core-2</module>
<module>spring-core-3</module>
<module>spring-core-4</module>
<module>spring-core-5</module>
<module>spring-cucumber</module>
<module>spring-data-rest</module>
@ -1082,6 +1083,7 @@
<module>spring-core-2</module>
<module>spring-core-3</module>
<module>spring-core-4</module>
<module>spring-core-5</module>
<module>spring-cucumber</module>
<module>spring-data-rest</module>

View File

@ -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)

View File

@ -176,7 +176,6 @@
<commons-collections4.version>4.1</commons-collections4.version>
<jetty-reactive-httpclient.version>1.0.3</jetty-reactive-httpclient.version>
<okhttp.version>4.0.1</okhttp.version>
<spring-boot.version>2.3.3.RELEASE</spring-boot.version>
</properties>
</project>

View File

@ -1,3 +1,5 @@
logging.level.root=INFO
server.port=8081
server.port=8081
logging.level.reactor.netty.http.client.HttpClient=DEBUG

View File

@ -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)));
}

View File

@ -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 "";
}
}

View File

@ -11,8 +11,6 @@ import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
@ -41,6 +39,7 @@ import com.baeldung.web.reactive.client.Foo;
import com.baeldung.web.reactive.client.WebClientApplication;
import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutException;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import reactor.core.publisher.Mono;
@ -265,17 +264,18 @@ public class WebClientIntegrationTest {
.addHandlerLast(new WriteTimeoutHandler(1000, TimeUnit.MILLISECONDS)));
WebClient timeoutClient = WebClient.builder()
.baseUrl("http://localhost:" + port)
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
BodyInserter<Publisher<String>, ReactiveHttpOutputMessage> inserterCompleteSuscriber = BodyInserters.fromPublisher(Subscriber::onComplete, String.class);
RequestHeadersSpec<?> headersSpecInserterCompleteSuscriber = timeoutClient.post()
RequestHeadersSpec<?> neverendingMonoBodyRequest = timeoutClient.post()
.uri("/resource")
.body(inserterCompleteSuscriber);
.body(Mono.never(), String.class);
StepVerifier.create(headersSpecInserterCompleteSuscriber.retrieve()
StepVerifier.create(neverendingMonoBodyRequest.retrieve()
.bodyToMono(String.class))
.expectTimeout(Duration.ofMillis(2000))
.expectErrorMatches(ex -> WebClientRequestException.class.isAssignableFrom(ex.getClass()) && ReadTimeoutException.class.isAssignableFrom(ex.getCause()
.getClass()))
.verify();
}

View File

@ -11,3 +11,5 @@ This module contains articles about Spring 5
- [Spring Assert Statements](https://www.baeldung.com/spring-assert)
- [Difference between \<context:annotation-config> vs \<context:component-scan>](https://www.baeldung.com/spring-contextannotation-contextcomponentscan)
- [Finding the Spring Version](https://www.baeldung.com/spring-find-version)
- [Spring 5 Testing with @EnabledIf Annotation](https://www.baeldung.com/spring-5-enabledIf)
- [Configuring a Hikari Connection Pool with Spring Boot](https://www.baeldung.com/spring-boot-hikari)

View File

@ -0,0 +1,3 @@
### Relevant Articles:
- [Concurrent Test Execution in Spring 5](https://www.baeldung.com/spring-5-concurrent-tests)

View File

@ -54,6 +54,7 @@
<module>spring-boot-mvc-2</module>
<module>spring-boot-mvc-3</module>
<module>spring-boot-mvc-birt</module>
<module>spring-boot-mvc-jersey</module>
<module>spring-boot-nashorn</module>
<module>spring-boot-parent</module>
<module>spring-boot-performance</module>

View File

@ -61,7 +61,7 @@
</build>
<properties>
<spring-boot-admin-starter-client.version>2.2.2</spring-boot-admin-starter-client.version>
<spring-boot-admin-starter-client.version>2.4.0</spring-boot-admin-starter-client.version>
<spring-boot-maven-plugin.version>2.0.4.RELEASE</spring-boot-maven-plugin.version>
</properties>
</project>

View File

@ -82,10 +82,9 @@
</build>
<properties>
<spring-boot-admin-server.version>2.2.2</spring-boot-admin-server.version>
<spring-boot-admin-starter-client.version>2.2.2</spring-boot-admin-starter-client.version>
<spring-boot-admin-server.version>2.4.0</spring-boot-admin-server.version>
<spring-boot-admin-starter-client.version>2.4.0</spring-boot-admin-starter-client.version>
<spring-boot-admin-server-ui-login.version>1.5.7</spring-boot-admin-server-ui-login.version>
<spring-boot-maven-plugin.version>2.0.4.RELEASE</spring-boot-maven-plugin.version>
<spring-boot.version>2.3.3.RELEASE</spring-boot.version>
</properties>
</project>

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -1,4 +1,4 @@
### Relevant Articles:
- [Spring Boot: Customize the Jackson ObjectMapper](https://www.baeldung.com/spring-boot-customize-jackson-objectmapper)
- [Configuring a Hikari Connection Pool with Spring Boot](https://www.baeldung.com/spring-boot-hikari)
- [“HttpMessageNotWritableException: No converter found for return value of type”](https://www.baeldung.com/spring-no-converter-found)

View File

@ -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);
}
}

View File

@ -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<Student> get(@PathVariable("id") int id) {
// Custom logic
return ResponseEntity.ok(new Student(id, "John", "Wiliams", "AA"));
}
}

View File

@ -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;
}
}

View File

@ -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"));
}
}

View File

@ -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)

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.baeldung.spring-boot-modules</groupId>
<artifactId>spring-boot-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
<artifactId>spring-boot-mvc-jersey</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>spring-boot-mvc-jersey</name>
<modules>
<module>spring-boot-jersey</module>
<module>spring-boot-mvc</module>
</modules>
</project>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.baeldung.boot</groupId>
<artifactId>spring-boot-jersey</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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() {
}
}

View File

@ -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");
}
}

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.baeldung.boot</groupId>
<artifactId>spring-boot-mvc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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() {
}
}

View File

@ -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");
}
}

View File

@ -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)

View File

@ -2,3 +2,4 @@
- [Documenting a Spring REST API Using OpenAPI 3.0](https://www.baeldung.com/spring-rest-openapi-documentation)
- [Spring REST Docs vs OpenAPI](https://www.baeldung.com/spring-rest-docs-vs-openapi)
- [Hiding Endpoints From Swagger Documentation in Spring Boot](https://www.baeldung.com/spring-swagger-hiding-endpoints)

View File

@ -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<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace);
public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, options);
errorAttributes.put("locale", webRequest.getLocale()
.toString());
errorAttributes.remove("error");

View File

@ -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<Map<String, Object>> xmlError(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.APPLICATION_XML));
Map<String, Object> 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);

View File

@ -50,7 +50,7 @@ public class CustomerController {
}
Link link = linkTo(methodOn(CustomerController.class).getOrdersForCustomer(customerId)).withSelfRel();
CollectionModel<Order> result = new CollectionModel<>(orders, link);
CollectionModel<Order> result = CollectionModel.of(orders, link);
return result;
}
@ -72,7 +72,7 @@ public class CustomerController {
}
Link link = linkTo(CustomerController.class).withSelfRel();
CollectionModel<Customer> result = new CollectionModel<>(allCustomers, link);
CollectionModel<Customer> result = CollectionModel.of(allCustomers, link);
return result;
}

View File

@ -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

View File

@ -33,7 +33,7 @@ public class ExamplePostControllerRequestIntegrationTest {
@Before
public void preTest() {
MockitoAnnotations.initMocks(this);
MockitoAnnotations.openMocks(this);
mockMvc = MockMvcBuilders
.standaloneSetup(exampleController)
.build();

View File

@ -34,7 +34,7 @@ public class ExamplePostControllerResponseIntegrationTest {
@Before
public void preTest() {
MockitoAnnotations.initMocks(this);
MockitoAnnotations.openMocks(this);
mockMvc = MockMvcBuilders
.standaloneSetup(exampleController)
.build();

View File

@ -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;

View File

@ -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)

View File

@ -37,12 +37,24 @@
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>3.8.0</version>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
<version>3.8.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

View File

@ -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<HttpMessageConverters>() {
@Override
public HttpMessageConverters getObject() {
return new HttpMessageConverters(new RestTemplate().getMessageConverters());
}
}));
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
}

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