Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Amit Bhave 2021-02-09 19:19:03 +05:30
commit 4bc5986a42
101 changed files with 1963 additions and 170 deletions

View File

@ -26,7 +26,7 @@ public class Product {
return tags;
}
public Product addTagsOfOtherProdcut(Product product) {
public Product addTagsOfOtherProduct(Product product) {
this.tags.addAll(product.getTags());
return this;
}
@ -100,11 +100,11 @@ public class Product {
HashMap<String, Product> productsByName = new HashMap<>();
Product eBike2 = new Product("E-Bike", "A bike with a battery");
eBike2.getTags().add("sport");
productsByName.merge("E-Bike", eBike2, Product::addTagsOfOtherProdcut);
productsByName.merge("E-Bike", eBike2, Product::addTagsOfOtherProduct);
//Prior to Java 8:
if(productsByName.containsKey("E-Bike")) {
productsByName.get("E-Bike").addTagsOfOtherProdcut(eBike2);
productsByName.get("E-Bike").addTagsOfOtherProduct(eBike2);
} else {
productsByName.put("E-Bike", eBike2);
}
@ -117,7 +117,7 @@ public class Product {
productsByName.compute("E-Bike", (k,v) -> {
if(v != null) {
return v.addTagsOfOtherProdcut(eBike2);
return v.addTagsOfOtherProduct(eBike2);
} else {
return eBike2;
}
@ -125,7 +125,7 @@ public class Product {
//Prior to Java 8:
if(productsByName.containsKey("E-Bike")) {
productsByName.get("E-Bike").addTagsOfOtherProdcut(eBike2);
productsByName.get("E-Bike").addTagsOfOtherProduct(eBike2);
} else {
productsByName.put("E-Bike", eBike2);
}

View File

@ -1,3 +1,4 @@
### Relevant Articles:
- [Binary Semaphore vs Reentrant Lock](https://www.baeldung.com/java-binary-semaphore-vs-reentrant-lock)
- [Bad Practices With Synchronization](https://www.baeldung.com/java-synchronization-bad-practices)

View File

@ -0,0 +1,35 @@
package com.baeldung.synchronizationbadpractices;
public class AnimalBadPractice {
private String name;
private String owner;
public String getName() {
return name;
}
public String getOwner() {
return owner;
}
public synchronized void setName(String name) {
this.name = name;
}
public void setOwner(String owner) {
synchronized(this) {
this.owner = owner;
}
}
public AnimalBadPractice() {
}
public AnimalBadPractice(String name, String owner) {
this.name = name;
this.owner = owner;
}
}

View File

@ -0,0 +1,42 @@
package com.baeldung.synchronizationbadpractices;
public class AnimalSolution {
private final Object objLock1 = new Object();
private final Object objLock2 = new Object();
private String name;
private String owner;
public String getName() {
return name;
}
public String getOwner() {
return owner;
}
public void setName(String name) {
synchronized(objLock1) {
this.name = name;
}
}
public void setOwner(String owner) {
synchronized(objLock2) {
this.owner = owner;
}
}
public AnimalSolution() {
}
public AnimalSolution(String name, String owner) {
this.name = name;
this.owner = owner;
}
}

View File

@ -0,0 +1,51 @@
package com.baeldung.synchronizationbadpractices;
public class SynchronizationBadPracticeExample {
public void stringBadPractice1() {
String stringLock = "LOCK_STRING";
synchronized (stringLock) {
// ...
}
}
private final String stringLock = "LOCK_STRING";
public void stringBadPractice2() {
synchronized (stringLock) {
// ...
}
}
private final String internedStringLock = new String("LOCK_STRING").intern();
public void stringBadPractice3() {
synchronized (internedStringLock) {
// ...
}
}
private final Boolean booleanLock = Boolean.FALSE;
public void booleanBadPractice() {
synchronized (booleanLock) {
// ...
}
}
private int count = 0;
private final Integer intLock = count;
public void boxedPrimitiveBadPractice() {
synchronized (intLock) {
count++;
// ...
}
}
public void classBadPractice() throws InterruptedException {
AnimalBadPractice animalObj = new AnimalBadPractice("Tommy", "John");
synchronized(animalObj) {
while (true) {
Thread.sleep(Integer.MAX_VALUE);
}
}
}
}

View File

@ -0,0 +1,30 @@
package com.baeldung.synchronizationbadpractices;
public class SynchronizationSolutionExample {
private final String stringLock = new String("LOCK_STRING");
public void stringSolution() {
synchronized (stringLock) {
// ...
}
}
private int count = 0;
private final Integer intLock = new Integer(count);
public void boxedPrimitiveSolution() {
synchronized(intLock) {
count++;
// ...
}
}
private static int staticCount = 0;
private static final Object staticObjLock = new Object();
public void staticVariableSolution() {
synchronized(staticObjLock) {
staticCount++;
// ...
}
}
}

View File

@ -11,4 +11,5 @@ This module contains articles about working with the Java Virtual Machine (JVM).
- [Where Is the Array Length Stored in JVM?](https://www.baeldung.com/java-jvm-array-length)
- [Memory Address of Objects in Java](https://www.baeldung.com/java-object-memory-address)
- [List All Classes Loaded in a Specific Class Loader](https://www.baeldung.com/java-list-classes-class-loader)
- [An Introduction to the Constant Pool in the JVM](https://www.baeldung.com/jvm-constant-pool)
- More articles: [[<-- prev]](/core-java-modules/core-java-jvm)

View File

@ -0,0 +1,16 @@
package com.baeldung.compareto;
public class BankAccount implements Comparable<BankAccount> {
private final int balance;
public BankAccount(int balance) {
this.balance = balance;
}
@Override
public int compareTo(BankAccount anotherAccount) {
return this.balance - anotherAccount.balance;
}
}

View File

@ -0,0 +1,16 @@
package com.baeldung.compareto;
public class BankAccountFix implements Comparable<BankAccountFix> {
private final int balance;
public BankAccountFix(int balance) {
this.balance = balance;
}
@Override
public int compareTo(BankAccountFix anotherAccount) {
return Integer.compare(this.balance, anotherAccount.balance);
}
}

View File

@ -0,0 +1,32 @@
package com.baeldung.compareto;
public class FootballPlayer implements Comparable<FootballPlayer> {
private final String name;
private final int goalsScored;
public FootballPlayer(String name, int goalsScored) {
this.name = name;
this.goalsScored = goalsScored;
}
public String getName() {
return name;
}
@Override
public int compareTo(FootballPlayer anotherPlayer) {
return Integer.compare(this.goalsScored, anotherPlayer.goalsScored);
}
@Override
public boolean equals(Object object) {
if (this == object)
return true;
if (object == null || getClass() != object.getClass())
return false;
FootballPlayer player = (FootballPlayer) object;
return name.equals(player.name);
}
}

View File

@ -0,0 +1,12 @@
package com.baeldung.compareto;
public class HandballPlayer {
private final String name;
private final int height;
public HandballPlayer(String name, int height) {
this.name = name;
this.height = height;
}
}

View File

@ -0,0 +1,25 @@
package com.baeldung.compareto;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import static org.assertj.core.api.Assertions.assertThat;
public class ArraysSortingUnitTest {
@Test
public void givenArrayOfNumbers_whenSortingArray_thenNumbersAreSortedAscending() {
int[] numbers = new int[] {5, 3, 9, 11, 1, 7};
Arrays.sort(numbers);
assertThat(numbers).containsExactly(1, 3, 5, 7, 9, 11);
}
@Test
public void givenArrayOfStrings_whenSortingArray_thenStringsAreSortedAlphabetically() {
String[] players = new String[] {"ronaldo", "modric", "ramos", "messi"};
Arrays.sort(players);
assertThat(players).containsExactly("messi", "modric", "ramos", "ronaldo");
}
}

View File

@ -0,0 +1,25 @@
package com.baeldung.compareto;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class BankAccountFixUnitTest {
@Test
public void givenComparisonBasedImpl_whenUsingSmallIntegers_thenComparisonWorks() {
BankAccountFix accountOne = new BankAccountFix(5000);
BankAccountFix accountTwo = new BankAccountFix(1000);
int comparison = accountOne.compareTo(accountTwo);
assertThat(comparison).isPositive();
}
@Test
public void givenComparisonBasedImpl_whenUsingLargeIntegers_thenComparisonWorks() {
BankAccountFix accountOne = new BankAccountFix(1900000000);
BankAccountFix accountTwo = new BankAccountFix(-2000000000);
int comparison = accountOne.compareTo(accountTwo);
assertThat(comparison).isPositive();
}
}

View File

@ -0,0 +1,25 @@
package com.baeldung.compareto;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;
public class BankAccountUnitTest {
@Test
public void givenSubtractionBasedImpl_whenUsingSmallIntegers_thenComparisonWorks() {
BankAccount accountOne = new BankAccount(5000);
BankAccount accountTwo = new BankAccount(1000);
int comparison = accountOne.compareTo(accountTwo);
assertThat(comparison).isPositive();
}
@Test
public void givenSubtractionBasedImpl_whenUsingLargeIntegers_thenComparisonBreaks() {
BankAccount accountOne = new BankAccount(1900000000);
BankAccount accountTwo = new BankAccount(-2000000000);
int comparison = accountOne.compareTo(accountTwo);
assertThat(comparison).isNegative();
}
}

View File

@ -0,0 +1,57 @@
package com.baeldung.compareto;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import static org.assertj.core.api.Assertions.assertThat;
public class FootballPlayerUnitTest {
@Test
public void givenInconsistentCompareToAndEqualsImpl_whenUsingSortedSet_thenSomeElementsAreNotAdded() {
FootballPlayer messi = new FootballPlayer("Messi", 800);
FootballPlayer ronaldo = new FootballPlayer("Ronaldo", 800);
TreeSet<FootballPlayer> set = new TreeSet<>();
set.add(messi);
set.add(ronaldo);
assertThat(set).hasSize(1);
assertThat(set).doesNotContain(ronaldo);
}
@Test
public void givenCompareToImpl_whenUsingCustomComparator_thenComparatorLogicIsApplied() {
FootballPlayer ronaldo = new FootballPlayer("Ronaldo", 900);
FootballPlayer messi = new FootballPlayer("Messi", 800);
FootballPlayer modric = new FootballPlayer("Modric", 100);
List<FootballPlayer> players = Arrays.asList(ronaldo, messi, modric);
Comparator<FootballPlayer> nameComparator = Comparator.comparing(FootballPlayer::getName);
Collections.sort(players, nameComparator);
assertThat(players).containsExactly(messi, modric, ronaldo);
}
@Test
public void givenCompareToImpl_whenSavingElementsInTreeMap_thenKeysAreSortedUsingCompareTo() {
FootballPlayer ronaldo = new FootballPlayer("Ronaldo", 900);
FootballPlayer messi = new FootballPlayer("Messi", 800);
FootballPlayer modric = new FootballPlayer("Modric", 100);
Map<FootballPlayer, String> players = new TreeMap<>();
players.put(ronaldo, "forward");
players.put(messi, "forward");
players.put(modric, "midfielder");
assertThat(players.keySet()).containsExactly(modric, messi, ronaldo);
}
}

View File

@ -0,0 +1,20 @@
package com.baeldung.compareto;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
public class HandballPlayerUnitTest {
@Test
public void givenComparableIsNotImplemented_whenSortingArray_thenExceptionIsThrown() {
HandballPlayer duvnjak = new HandballPlayer("Duvnjak", 197);
HandballPlayer hansen = new HandballPlayer("Hansen", 196);
HandballPlayer[] players = new HandballPlayer[] {duvnjak, hansen};
assertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> Arrays.sort(players));
}
}

View File

@ -7,3 +7,4 @@ This module contains articles about generics in Java
- [Type Erasure in Java Explained](https://www.baeldung.com/java-type-erasure)
- [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)

View File

@ -13,4 +13,32 @@
<name>core-java-lang-oop-generics</name>
<packaging>jar</packaging>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<compilerArguments>
<Xlint:unchecked/>
</compilerArguments>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
</project>

View File

@ -0,0 +1,45 @@
package com.baeldung.uncheckedconversion;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
public class UncheckedConversion {
public static List getRawList() {
List result = new ArrayList();
result.add("I am the 1st String.");
result.add("I am the 2nd String.");
result.add("I am the 3rd String.");
return result;
}
public static List getRawListWithMixedTypes() {
List result = new ArrayList();
result.add("I am the 1st String.");
result.add("I am the 2nd String.");
result.add("I am the 3rd String.");
result.add(new Date());
return result;
}
public static <T> List<T> castList(Class<? extends T> clazz, Collection<?> rawCollection) {
List<T> result = new ArrayList<>(rawCollection.size());
for (Object o : rawCollection) {
try {
result.add(clazz.cast(o));
} catch (ClassCastException e) {
// log the exception or other error handling
}
}
return result;
}
public static <T> List<T> castList2(Class<? extends T> clazz, Collection<?> rawCollection) throws ClassCastException {
List<T> result = new ArrayList<>(rawCollection.size());
for (Object o : rawCollection) {
result.add(clazz.cast(o));
}
return result;
}
}

View File

@ -0,0 +1,39 @@
package com.baeldung.uncheckedconversion;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
public class UncheckedConversionUnitTest {
@Test
public void givenRawList_whenAssignToTypedList_shouldHaveCompilerWarning() {
List<String> fromRawList = UncheckedConversion.getRawList();
Assert.assertEquals(3, fromRawList.size());
Assert.assertEquals("I am the 1st String.", fromRawList.get(0));
}
@Test(expected = ClassCastException.class)
public void givenRawList_whenListHasMixedType_shouldThrowClassCastException() {
List<String> fromRawList = UncheckedConversion.getRawListWithMixedTypes();
Assert.assertEquals(4, fromRawList.size());
Assert.assertFalse(fromRawList.get(3).endsWith("String."));
}
@Test
public void givenRawList_whenAssignToTypedListAfterCallingCastList_shouldOnlyHaveElementsWithExpectedType() {
List rawList = UncheckedConversion.getRawListWithMixedTypes();
List<String> strList = UncheckedConversion.castList(String.class, rawList);
Assert.assertEquals(4, rawList.size());
Assert.assertEquals("One element with the wrong type has been filtered out.", 3, strList.size());
Assert.assertTrue(strList.stream().allMatch(el -> el.endsWith("String.")));
}
@Test(expected = ClassCastException.class)
public void givenRawListWithWrongType_whenAssignToTypedListAfterCallingCastList2_shouldThrowException() {
List rawList = UncheckedConversion.getRawListWithMixedTypes();
UncheckedConversion.castList2(String.class, rawList);
}
}

View File

@ -0,0 +1,4 @@
package com.baeldung.reflection.check.abstractclass;
public interface InterfaceExample {
}

View File

@ -1,16 +1,36 @@
package com.baeldung.reflection.check.abstractclass;
import java.lang.reflect.Modifier;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Modifier;
import java.util.Date;
class AbstractExampleUnitTest {
@Test
void givenAbstractClass_whenCheckModifierIsAbstract_thenTrue() throws Exception {
void givenAbstractClass_whenCheckModifierIsAbstract_thenTrue() {
Class<AbstractExample> clazz = AbstractExample.class;
Assertions.assertTrue(Modifier.isAbstract(clazz.getModifiers()));
}
@Test
void givenInterface_whenCheckModifierIsAbstract_thenTrue() {
Class<InterfaceExample> clazz = InterfaceExample.class;
Assertions.assertTrue(Modifier.isAbstract(clazz.getModifiers()));
}
@Test
void givenAbstractClass_whenCheckIsAbstractClass_thenTrue() {
Class<AbstractExample> clazz = AbstractExample.class;
int mod = clazz.getModifiers();
Assertions.assertTrue(Modifier.isAbstract(mod) && !Modifier.isInterface(mod));
}
@Test
void givenConcreteClass_whenCheckIsAbstractClass_thenFalse() {
Class<Date> clazz = Date.class;
int mod = clazz.getModifiers();
Assertions.assertFalse(Modifier.isAbstract(mod) && !Modifier.isInterface(mod));
}
}

View File

@ -0,0 +1,23 @@
package com.baeldung.egd;
import java.security.SecureRandom;
/**
* JavaSecurityEgdTester - run this with JVM parameter java.security.egd, e.g.:
* java -Djava.security.egd=file:/dev/urandom -cp . com.baeldung.egd.JavaSecurityEgdTester
*/
public class JavaSecurityEgdTester {
public static final double NANOSECS = 1000000000.0;
public static final String JAVA_SECURITY_EGD = "java.security.egd";
public static void main(String[] args) {
SecureRandom secureRandom = new SecureRandom();
long start = System.nanoTime();
byte[] randomBytes = new byte[256];
secureRandom.nextBytes(randomBytes);
double duration = (System.nanoTime() - start) / NANOSECS;
String message = String.format("java.security.egd=%s took %.3f seconds and used the %s algorithm", System.getProperty(JAVA_SECURITY_EGD), duration, secureRandom.getAlgorithm());
System.out.println(message);
}
}

View File

@ -2,3 +2,4 @@
- [Java Map With Case-Insensitive Keys](https://www.baeldung.com/java-map-with-case-insensitive-keys)
- [Using a Byte Array as Map Key in Java](https://www.baeldung.com/java-map-key-byte-array)
- [Using the Map.Entry Java Class](https://www.baeldung.com/java-map-entry)

View File

@ -0,0 +1,35 @@
package com.baeldung.map.entry;
public class Book {
private String title;
private String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "Book{" +
"title='" + title + '\'' +
", author='" + author + '\'' +
'}';
}
}

View File

@ -0,0 +1,34 @@
package com.baeldung.map.entry;
import java.util.HashMap;
import java.util.Map;
public class MapEntryEfficiencyExample {
public static void main(String[] args) {
MapEntryEfficiencyExample mapEntryEfficiencyExample = new MapEntryEfficiencyExample();
Map<String, String> map = new HashMap<>();
map.put("Robert C. Martin", "Clean Code");
map.put("Joshua Bloch", "Effective Java");
System.out.println("Iterating Using Map.KeySet - 2 operations");
mapEntryEfficiencyExample.usingKeySet(map);
System.out.println("Iterating Using Map.Entry - 1 operation");
mapEntryEfficiencyExample.usingEntrySet(map);
}
public void usingKeySet(Map<String, String> bookMap) {
for (String key : bookMap.keySet()) {
System.out.println("key: " + key + " value: " + bookMap.get(key));
}
}
public void usingEntrySet(Map<String, String> bookMap) {
for (Map.Entry<String, String> book: bookMap.entrySet()) {
System.out.println("key: " + book.getKey() + " value: " + book.getValue());
}
}
}

View File

@ -0,0 +1,25 @@
package com.baeldung.map.entry;
import java.util.*;
public class MapEntryTupleExample {
public static void main(String[] args) {
Map.Entry<String, Book> tuple1;
Map.Entry<String, Book> tuple2;
Map.Entry<String, Book> tuple3;
tuple1 = new AbstractMap.SimpleEntry<>("9780134685991", new Book("Effective Java 3d Edition", "Joshua Bloch"));
tuple2 = new AbstractMap.SimpleEntry<>("9780132350884", new Book("Clean Code", "Robert C Martin"));
tuple3 = new AbstractMap.SimpleEntry<>("9780132350884", new Book("Clean Code", "Robert C Martin"));
List<Map.Entry<String, Book>> orderedTuples = new ArrayList<>();
orderedTuples.add(tuple1);
orderedTuples.add(tuple2);
orderedTuples.add(tuple3);
for (Map.Entry<String, Book> tuple : orderedTuples) {
System.out.println("key: " + tuple.getKey() + " value: " + tuple.getValue());
}
}
}

View File

@ -0,0 +1,33 @@
package com.baeldung.map.entry;
import org.junit.Test;
import java.util.*;
import static org.junit.Assert.assertEquals;
public class MapEntryUnitTest {
@Test
public void givenSimpleEntryList_whenAddDuplicateKey_thenDoesNotOverwriteExistingKey() {
List<Map.Entry<String, Book>> orderedTuples = new ArrayList<>();
orderedTuples.add(new AbstractMap.SimpleEntry<>("9780134685991", new Book("Effective Java 3d Edition", "Joshua Bloch")));
orderedTuples.add(new AbstractMap.SimpleEntry<>("9780132350884", new Book("Clean Code", "Robert C Martin")));
orderedTuples.add(new AbstractMap.SimpleEntry<>("9780132350884", new Book("Clean Code", "Robert C Martin")));
assertEquals(3, orderedTuples.size());
assertEquals("9780134685991", orderedTuples.get(0).getKey());
assertEquals("9780132350884", orderedTuples.get(1).getKey());
assertEquals("9780132350884", orderedTuples.get(2).getKey());
}
@Test
public void givenRegularMap_whenAddDuplicateKey_thenOverwritesExistingKey() {
Map<String, Book> entries = new HashMap<>();
entries.put("9780134685991", new Book("Effective Java 3d Edition", "Joshua Bloch"));
entries.put("9780132350884", new Book("Clean Code", "Robert C Martin"));
entries.put("9780132350884", new Book("Clean Code", "Robert C Martin"));
assertEquals(2, entries.size());
}
}

View File

@ -120,7 +120,7 @@
<httpclient.version>4.5.3</httpclient.version>
<!-- <jackson.version>2.9.8</jackson.version>-->
<assertj.version>3.6.2</assertj.version>
<com.squareup.okhttp3.version>3.14.2</com.squareup.okhttp3.version>
<com.squareup.okhttp3.version>4.9.1</com.squareup.okhttp3.version>
<googleclient.version>1.23.0</googleclient.version>
<async.http.client.version>2.2.0</async.http.client.version>
<retrofit.version>2.3.0</retrofit.version>

View File

@ -6,6 +6,7 @@ This module contains articles about performance testing.
- [Performance of Java Mapping Frameworks](https://www.baeldung.com/java-performance-mapping-frameworks)
- [Performance Effects of Exceptions in Java](https://www.baeldung.com/java-exceptions-performance)
- [Is Java a Compiled or Interpreted Language?](https://www.baeldung.com/java-compiled-interpreted)
### Running

View File

@ -1,11 +1,8 @@
package com.baeldung.zoneddatetime.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
@ -13,12 +10,7 @@ import org.springframework.data.mongodb.repository.config.EnableMongoRepositorie
import com.baeldung.zoneddatetime.converter.ZonedDateTimeReadConverter;
import com.baeldung.zoneddatetime.converter.ZonedDateTimeWriteConverter;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
@Configuration
@EnableMongoRepositories(basePackages = { "com.baeldung" })
public class MongoConfig extends AbstractMongoClientConfiguration {
@ -29,20 +21,6 @@ public class MongoConfig extends AbstractMongoClientConfiguration {
return "test";
}
@Override
public MongoClient mongoClient() {
final ConnectionString connectionString = new ConnectionString("mongodb://localhost:27017/test");
final MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
.applyConnectionString(connectionString)
.build();
return MongoClients.create(mongoClientSettings);
}
@Override
public Collection<String> getMappingBasePackages() {
return Collections.singleton("com.baeldung");
}
@Override
public MongoCustomConversions customConversions() {
converters.add(new ZonedDateTimeReadConverter());

View File

@ -24,8 +24,8 @@
<constructor-arg ref="mongoConverter" />
</bean>
<mongo:mapping-converter id="mongoConverter" base-package="com.baeldung.converter">
<mongo:custom-converters base-package="com.baeldung.converter" />
<mongo:mapping-converter id="mongoConverter" base-package="com.baeldung.zoneddatetime.converter">
<mongo:custom-converters base-package="com.baeldung.zoneddatetime.converter" />
</mongo:mapping-converter>
</beans>

View File

@ -11,3 +11,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
- [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

@ -0,0 +1,18 @@
package com.baeldung.webclient.json;
import com.baeldung.webclient.json.model.Book;
import java.util.List;
public interface ReaderConsumerService {
List<Book> processReaderDataFromObjectArray();
List<Book> processReaderDataFromReaderArray();
List<Book> processReaderDataFromReaderList();
List<String> processNestedReaderDataFromReaderArray();
List<String> processNestedReaderDataFromReaderList();
}

View File

@ -0,0 +1,90 @@
package com.baeldung.webclient.json;
import com.baeldung.webclient.json.model.Book;
import com.baeldung.webclient.json.model.Reader;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ReaderConsumerServiceImpl implements ReaderConsumerService {
private final WebClient webClient;
private static final ObjectMapper mapper = new ObjectMapper();
public ReaderConsumerServiceImpl(WebClient webClient) {
this.webClient = webClient;
}
@Override
public List<Book> processReaderDataFromObjectArray() {
Mono<Object[]> response = webClient.get()
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Object[].class).log();
Object[] objects = response.block();
return Arrays.stream(objects)
.map(object -> mapper.convertValue(object, Reader.class))
.map(Reader::getFavouriteBook)
.collect(Collectors.toList());
}
@Override
public List<Book> processReaderDataFromReaderArray() {
Mono<Reader[]> response =
webClient.get()
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Reader[].class).log();
Reader[] readers = response.block();
return Arrays.stream(readers)
.map(Reader::getFavouriteBook)
.collect(Collectors.toList());
}
@Override
public List<Book> processReaderDataFromReaderList() {
Mono<List<Reader>> response = webClient.get()
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(new ParameterizedTypeReference<List<Reader>>() {});
List<Reader> readers = response.block();
return readers.stream()
.map(Reader::getFavouriteBook)
.collect(Collectors.toList());
}
@Override
public List<String> processNestedReaderDataFromReaderArray() {
Mono<Reader[]> response = webClient.get()
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Reader[].class).log();
Reader[] readers = response.block();
return Arrays.stream(readers)
.flatMap(reader -> reader.getBooksRead().stream())
.map(Book::getAuthor)
.collect(Collectors.toList());
}
@Override
public List<String> processNestedReaderDataFromReaderList() {
Mono<List<Reader>> response = webClient.get()
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(new ParameterizedTypeReference<List<Reader>>() {});
List<Reader> readers = response.block();
return readers.stream()
.flatMap(reader -> reader.getBooksRead().stream())
.map(Book::getAuthor)
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,22 @@
package com.baeldung.webclient.json.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Book {
private final String author;
private final String title;
@JsonCreator
public Book(
@JsonProperty("author") String author,
@JsonProperty("title") String title) {
this.author = author;
this.title = title;
}
public String getAuthor() {
return this.author;
}
}

View File

@ -0,0 +1,33 @@
package com.baeldung.webclient.json.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Reader {
private final int id;
private final String name;
private final Book favouriteBook;
private final List<Book> booksRead;
@JsonCreator
public Reader(
@JsonProperty("id") int id,
@JsonProperty("name") String name,
@JsonProperty("favouriteBook") Book favouriteBook,
@JsonProperty("booksRead") List<Book> booksRead) {
this.id = id;
this.name = name;
this.favouriteBook = favouriteBook;
this.booksRead =booksRead;
}
public Book getFavouriteBook() {
return favouriteBook;
}
public List<Book> getBooksRead() { return booksRead; }
}

View File

@ -0,0 +1,88 @@
package com.baeldung.webclient.json;
import com.baeldung.webclient.json.model.Book;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.hasProperty;
public class ReaderConsumerServiceImplUnitTest {
private static String READER_JSON = "[{\"id\":1,\"name\":\"reader1\",\"favouriteBook\":{\"author\":\"Milan Kundera\",\"title\":\"The Unbearable Lightness of Being\"}," +
"\"booksRead\":[{\"author\":\"Charles Dickens\",\"title\":\"Oliver Twist\"},{\"author\":\"Milan Kundera\",\"title\":\"The Unbearable Lightness of Being\"}]}," +
"{\"id\":2,\"name\":\"reader2\",\"favouriteBook\":{\"author\":\"Douglas Adams\",\"title\":\"The Hitchhiker\'s Guide to the Galaxy\"}," +
"\"booksRead\":[{\"author\":\"J.R.R. Tolkien\",\"title\":\"Lord of the Rings\"}, " +
"{\"author\":\"Douglas Adams\",\"title\":\"The Hitchhiker\'s Guide to the Galaxy\"}]}]";
private static String BASE_URL = "http://localhost:8080/readers";
WebClient webClientMock = WebClient.builder().baseUrl(BASE_URL)
.exchangeFunction(clientRequest -> Mono.just(ClientResponse.create(HttpStatus.OK)
.header("content-type", "application/json")
.body(READER_JSON)
.build()))
.build();
private final ReaderConsumerService tested = new ReaderConsumerServiceImpl(webClientMock);
@Test
void when_processReaderDataFromObjectArray_then_OK() {
String expectedAuthor1 = "Milan Kundera";
String expectedAuthor2 = "Douglas Adams";
List<Book> actual = tested.processReaderDataFromObjectArray();
assertThat(actual, hasItems(hasProperty("author", is(expectedAuthor1)),
hasProperty("author", is(expectedAuthor2))));
}
@Test
void when_processReaderDataFromReaderArray_then_OK() {
String expectedAuthor1 = "Milan Kundera";
String expectedAuthor2 = "Douglas Adams";
List<Book> actual = tested.processReaderDataFromReaderArray();
assertThat(actual, hasItems(hasProperty("author", is(expectedAuthor1)),
hasProperty("author", is(expectedAuthor2))));
}
@Test
void when_processReaderDataFromReaderList_then_OK() {
String expectedAuthor1 = "Milan Kundera";
String expectedAuthor2 = "Douglas Adams";
List<Book> actual = tested.processReaderDataFromReaderList();
assertThat(actual, hasItems(hasProperty("author", is(expectedAuthor1)),
hasProperty("author", is(expectedAuthor2))));
}
@Test
void when_processNestedReaderDataFromReaderArray_then_OK() {
List<String> expected = Arrays.asList(
"Milan Kundera",
"Charles Dickens",
"J.R.R. Tolkien",
"Douglas Adams");
List<String> actual = tested.processNestedReaderDataFromReaderArray();
assertThat(actual, hasItems(expected.get(0), expected.get(1), expected.get(2), expected.get(3)));
}
@Test
void when_processNestedReaderDataFromReaderList_then_OK() {
List<String> expected = Arrays.asList(
"Milan Kundera",
"Charles Dickens",
"J.R.R. Tolkien",
"Douglas Adams");
List<String> actual = tested.processNestedReaderDataFromReaderList();
assertThat(actual, hasItems(expected.get(0), expected.get(1), expected.get(2), expected.get(3)));
}
}

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-5-reactive</artifactId>
<version>0.0.1-SNAPSHOT</version>
@ -73,6 +74,12 @@
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring WebFlux WebSession -->
<dependency>

View File

@ -11,7 +11,7 @@ import java.util.concurrent.atomic.AtomicLong;
import static org.springframework.web.reactive.function.BodyExtractors.toDataBuffers;
import static org.springframework.web.reactive.function.BodyExtractors.toFormData;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.BodyInserters.fromValue;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
public class FormHandler {
@ -29,7 +29,7 @@ public class FormHandler {
Mono<ServerResponse> handleUpload(ServerRequest request) {
return request.body(toDataBuffers())
.collectList()
.flatMap(dataBuffers -> ok().body(fromObject(extractData(dataBuffers).toString())));
.flatMap(dataBuffers -> ok().body(fromValue(extractData(dataBuffers).toString())));
}
private AtomicLong extractData(List<DataBuffer> dataBuffers) {

View File

@ -1,6 +1,6 @@
package com.baeldung.functional;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.BodyInserters.fromValue;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
@ -44,7 +44,7 @@ public class FunctionalSpringBootApplication {
.doOnNext(actors::add)
.then(ok().build()));
return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld")))
return route(GET("/test"), serverRequest -> ok().body(fromValue("helloworld")))
.andRoute(POST("/login"), formHandler::handleLogin)
.andRoute(POST("/upload"), formHandler::handleUpload)
.and(RouterFunctions.resources("/files/**", new ClassPathResource("files/")))

View File

@ -1,6 +1,6 @@
package com.baeldung.functional;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.BodyInserters.fromValue;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
@ -42,7 +42,7 @@ public class FunctionalWebApplication {
.doOnNext(actors::add)
.then(ok().build()));
return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))).andRoute(POST("/login"), formHandler::handleLogin)
return route(GET("/test"), serverRequest -> ok().body(fromValue("helloworld"))).andRoute(POST("/login"), formHandler::handleLogin)
.andRoute(POST("/upload"), formHandler::handleUpload)
.and(RouterFunctions.resources("/files/**", new ClassPathResource("files/")))
.andNest(accept(MediaType.APPLICATION_JSON), restfulRouter)

View File

@ -2,7 +2,7 @@ package com.baeldung.functional;
import static org.springframework.web.reactive.function.BodyExtractors.toDataBuffers;
import static org.springframework.web.reactive.function.BodyExtractors.toFormData;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.BodyInserters.fromValue;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
@ -46,7 +46,7 @@ public class RootServlet extends ServletHttpHandlerAdapter {
private static RouterFunction<?> routingFunction() {
return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))).andRoute(POST("/login"), serverRequest -> serverRequest.body(toFormData())
return route(GET("/test"), serverRequest -> ok().body(fromValue("helloworld"))).andRoute(POST("/login"), serverRequest -> serverRequest.body(toFormData())
.map(MultiValueMap::toSingleValueMap)
.map(formData -> {
System.out.println("form data: " + formData.toString());
@ -65,7 +65,7 @@ public class RootServlet extends ServletHttpHandlerAdapter {
dataBuffers.forEach(d -> atomicLong.addAndGet(d.asByteBuffer()
.array().length));
System.out.println("data length:" + atomicLong.get());
return ok().body(fromObject(atomicLong.toString()))
return ok().body(fromValue(atomicLong.toString()))
.block();
}))
.and(RouterFunctions.resources("/files/**", new ClassPathResource("files/")))

View File

@ -2,7 +2,8 @@
package com.baeldung.reactive.errorhandling;
import java.util.Map;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
@ -18,6 +19,7 @@ import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
@ -26,7 +28,7 @@ public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHan
public GlobalErrorWebExceptionHandler(GlobalErrorAttributes g, ApplicationContext applicationContext,
ServerCodecConfigurer serverCodecConfigurer) {
super(g, new ResourceProperties(), applicationContext);
super(g, new WebProperties.Resources(), applicationContext);
super.setMessageWriters(serverCodecConfigurer.getWriters());
super.setMessageReaders(serverCodecConfigurer.getReaders());
}
@ -41,8 +43,8 @@ public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHan
final Map<String, Object> errorPropertiesMap = getErrorAttributes(request, ErrorAttributeOptions.defaults());
return ServerResponse.status(HttpStatus.BAD_REQUEST)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(errorPropertiesMap));
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorPropertiesMap));
}
}

View File

@ -14,7 +14,7 @@ public class Handler1 {
return sayHello(request).onErrorReturn("Hello, Stranger")
.flatMap(s -> ServerResponse.ok()
.contentType(MediaType.TEXT_PLAIN)
.syncBody(s));
.bodyValue(s));
}
private Mono<String> sayHello(ServerRequest request) {

View File

@ -15,11 +15,11 @@ public Mono<ServerResponse> handleRequest2(ServerRequest request) {
sayHello(request)
.flatMap(s -> ServerResponse.ok()
.contentType(MediaType.TEXT_PLAIN)
.syncBody(s))
.bodyValue(s))
.onErrorResume(e -> sayHelloFallback()
.flatMap(s -> ServerResponse.ok()
.contentType(MediaType.TEXT_PLAIN)
.syncBody(s)));
.bodyValue(s)));
}
private Mono<String> sayHello(ServerRequest request) {

View File

@ -15,11 +15,11 @@ public class Handler3 {
sayHello(request)
.flatMap(s -> ServerResponse.ok()
.contentType(MediaType.TEXT_PLAIN)
.syncBody(s))
.bodyValue(s))
.onErrorResume(e -> (Mono.just("Hi, I looked around for your name but found: " +
e.getMessage())).flatMap(s -> ServerResponse.ok()
.contentType(MediaType.TEXT_PLAIN)
.syncBody(s)));
.bodyValue(s)));
}
private Mono<String> sayHello(ServerRequest request) {

View File

@ -1,6 +1,6 @@
package com.baeldung.reactive.urlmatch;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.BodyInserters.fromValue;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;
@ -24,10 +24,10 @@ public class ExploreSpring5URLPatternUsingRouterFunctions {
private RouterFunction<ServerResponse> routingFunction() {
return route(GET("/p?ths"), serverRequest -> ok().body(fromObject("/p?ths"))).andRoute(GET("/test/{*id}"), serverRequest -> ok().body(fromObject(serverRequest.pathVariable("id"))))
.andRoute(GET("/*card"), serverRequest -> ok().body(fromObject("/*card path was accessed")))
.andRoute(GET("/{var1}_{var2}"), serverRequest -> ok().body(fromObject(serverRequest.pathVariable("var1") + " , " + serverRequest.pathVariable("var2"))))
.andRoute(GET("/{baeldung:[a-z]+}"), serverRequest -> ok().body(fromObject("/{baeldung:[a-z]+} was accessed and baeldung=" + serverRequest.pathVariable("baeldung"))))
return route(GET("/p?ths"), serverRequest -> ok().body(fromValue("/p?ths"))).andRoute(GET("/test/{*id}"), serverRequest -> ok().body(fromValue(serverRequest.pathVariable("id"))))
.andRoute(GET("/*card"), serverRequest -> ok().body(fromValue("/*card path was accessed")))
.andRoute(GET("/{var1}_{var2}"), serverRequest -> ok().body(fromValue(serverRequest.pathVariable("var1") + " , " + serverRequest.pathVariable("var2"))))
.andRoute(GET("/{baeldung:[a-z]+}"), serverRequest -> ok().body(fromValue("/{baeldung:[a-z]+} was accessed and baeldung=" + serverRequest.pathVariable("baeldung"))))
.and(RouterFunctions.resources("/files/{*filepaths}", new ClassPathResource("files/")));
}

View File

@ -11,7 +11,7 @@ import java.util.concurrent.atomic.AtomicLong;
import static org.springframework.web.reactive.function.BodyExtractors.toDataBuffers;
import static org.springframework.web.reactive.function.BodyExtractors.toFormData;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.BodyInserters.fromValue;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
public class FormHandler {
@ -29,7 +29,7 @@ public class FormHandler {
Mono<ServerResponse> handleUpload(ServerRequest request) {
return request.body(toDataBuffers())
.collectList()
.flatMap(dataBuffers -> ok().body(fromObject(extractData(dataBuffers).toString())));
.flatMap(dataBuffers -> ok().body(fromValue(extractData(dataBuffers).toString())));
}
private AtomicLong extractData(List<DataBuffer> dataBuffers) {

View File

@ -1,6 +1,6 @@
package com.baeldung.reactive.urlmatch;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.BodyInserters.fromValue;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
@ -40,7 +40,7 @@ public class FunctionalWebApplication {
.doOnNext(actors::add)
.then(ok().build()));
return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))).andRoute(POST("/login"), formHandler::handleLogin)
return route(GET("/test"), serverRequest -> ok().body(fromValue("helloworld"))).andRoute(POST("/login"), formHandler::handleLogin)
.andRoute(POST("/upload"), formHandler::handleUpload)
.and(RouterFunctions.resources("/files/**", new ClassPathResource("files/")))
.andNest(path("/actor"), restfulRouter)

View File

@ -0,0 +1,24 @@
package com.baeldung.web.reactive.client;
public class Foo {
private String name;
public Foo() {
super();
}
public Foo(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -2,9 +2,10 @@ package com.baeldung.web.reactive.client;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration;
@SpringBootApplication
public class WebClientApplication{
@SpringBootApplication(exclude = { ReactiveSecurityAutoConfiguration.class })
public class WebClientApplication {
public static void main(String[] args) {
SpringApplication.run(WebClientApplication.class, args);

View File

@ -1,36 +1,18 @@
package com.baeldung.web.reactive.client;
import java.net.URI;
import java.nio.charset.Charset;
import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.http.client.reactive.ClientHttpRequest;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
@RestController
public class WebClientController {
@ -43,74 +25,18 @@ public class WebClientController {
return response;
}
public void demonstrateWebClient() {
// request
WebClient.UriSpec<WebClient.RequestBodySpec> request1 = createWebClientWithServerURLAndDefaultValues().method(HttpMethod.POST);
WebClient.UriSpec<WebClient.RequestBodySpec> request2 = createWebClientWithServerURLAndDefaultValues().post();
// request body specifications
WebClient.RequestBodySpec uri1 = createWebClientWithServerURLAndDefaultValues().method(HttpMethod.POST)
.uri("/resource");
WebClient.RequestBodySpec uri2 = createWebClientWithServerURLAndDefaultValues().post()
.uri(URI.create("/resource"));
// request header specification
WebClient.RequestHeadersSpec<?> requestSpec1 = uri1.body(BodyInserters.fromPublisher(Mono.just("data"), String.class));
WebClient.RequestHeadersSpec<?> requestSpec2 = uri2.body(BodyInserters.fromValue("data"));
// inserters
BodyInserter<Publisher<String>, ReactiveHttpOutputMessage> inserter1 = BodyInserters.fromPublisher(Subscriber::onComplete, String.class);
LinkedMultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("key1", "value1");
map.add("key2", "value2");
BodyInserter<MultiValueMap<String, Object>, ClientHttpRequest> inserter2 = BodyInserters.fromMultipartData(map);
BodyInserter<Object, ReactiveHttpOutputMessage> inserter3 = BodyInserters.fromValue(new Object());
BodyInserter<String, ReactiveHttpOutputMessage> inserter4 = BodyInserters.fromValue("body");
// responses
WebClient.ResponseSpec response1 = uri1.body(inserter3)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
.acceptCharset(Charset.forName("UTF-8"))
.ifNoneMatch("*")
.ifModifiedSince(ZonedDateTime.now())
.retrieve();
String response2 = uri1.exchangeToMono(response -> response.bodyToMono(String.class))
.block();
String response3 = uri2.retrieve()
.bodyToMono(String.class)
.block();
WebClient.ResponseSpec response4 = requestSpec2.retrieve();
@PostMapping("/resource")
public Mono<String> postStringResource(@RequestBody Mono<String> bodyString) {
return bodyString.map(body -> "processed-" + body);
}
private WebClient createWebClient() {
return WebClient.create();
@PostMapping("/resource-foo")
public Mono<String> postFooResource(@RequestBody Mono<Foo> bodyFoo) {
return bodyFoo.map(foo -> "processedFoo-" + foo.getName());
}
private WebClient createWebClientWithServerURL() {
return WebClient.create("http://localhost:8081");
@PostMapping(value = "/resource-multipart", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String handleFormUpload(@RequestPart("key1") String value1, @RequestPart("key2") String value2) {
return "processed-" + value1 + "-" + value2;
}
private WebClient createWebClientConfiguringTimeout() {
HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS))
.addHandlerLast(new WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS)));
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
}
private WebClient createWebClientWithServerURLAndDefaultValues() {
return WebClient.builder()
.baseUrl("http://localhost:8081")
.defaultCookie("cookieKey", "cookieValue")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultUriVariables(Collections.singletonMap("url", "http://localhost:8080"))
.build();
}
}

View File

@ -1,17 +1,15 @@
package com.baeldung.functional;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.BodyInserters.fromValue;
import static org.springframework.web.reactive.function.BodyInserters.fromResource;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.boot.web.server.WebServer;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@ -115,7 +113,7 @@ public class FunctionalWebApplicationIntegrationTest {
client.post()
.uri("/actor")
.body(fromObject(new Actor("Clint", "Eastwood")))
.body(fromValue(new Actor("Clint", "Eastwood")))
.exchange()
.expectStatus()
.isOk();

View File

@ -133,7 +133,7 @@ public class ErrorHandlingIntegrationTest {
.expectStatus()
.isBadRequest()
.expectHeader()
.contentType(MediaType.APPLICATION_JSON_UTF8)
.contentType(MediaType.APPLICATION_JSON)
.expectBody()
.jsonPath("$.message")
.isNotEmpty()
@ -164,7 +164,7 @@ public class ErrorHandlingIntegrationTest {
.expectStatus()
.isBadRequest()
.expectHeader()
.contentType(MediaType.APPLICATION_JSON_UTF8)
.contentType(MediaType.APPLICATION_JSON)
.expectBody()
.jsonPath("$.message")
.isNotEmpty()

View File

@ -0,0 +1,331 @@
package com.baeldung.web.client;
import static org.assertj.core.api.Assertions.assertThat;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.Collections;
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;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.codec.CodecException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.http.client.reactive.ClientHttpRequest;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec;
import org.springframework.web.reactive.function.client.WebClient.RequestBodyUriSpec;
import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec;
import org.springframework.web.reactive.function.client.WebClient.RequestHeadersUriSpec;
import org.springframework.web.reactive.function.client.WebClient.ResponseSpec;
import org.springframework.web.reactive.function.client.WebClientRequestException;
import com.baeldung.web.reactive.client.Foo;
import com.baeldung.web.reactive.client.WebClientApplication;
import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import reactor.test.StepVerifier;
@SpringBootTest(classes = WebClientApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public class WebClientIntegrationTest {
@LocalServerPort
private int port;
private static final String BODY_VALUE = "bodyValue";
private static final ParameterizedTypeReference<Map<String, String>> MAP_RESPONSE_REF = new ParameterizedTypeReference<Map<String, String>>() {
};
@Test
public void givenDifferentWebClientCreationMethods_whenUsed_thenObtainExpectedResponse() {
// WebClient creation
WebClient client1 = WebClient.create();
WebClient client2 = WebClient.create("http://localhost:" + port);
WebClient client3 = WebClient.builder()
.baseUrl("http://localhost:" + port)
.defaultCookie("cookieKey", "cookieValue")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultUriVariables(Collections.singletonMap("url", "http://localhost:8080"))
.build();
// response assertions
StepVerifier.create(retrieveResponse(client1.post()
.uri("http://localhost:" + port + "/resource")))
.expectNext("processed-bodyValue")
.verifyComplete();
StepVerifier.create(retrieveResponse(client2))
.expectNext("processed-bodyValue")
.verifyComplete();
StepVerifier.create(retrieveResponse(client3))
.expectNext("processed-bodyValue")
.verifyComplete();
// assert response without specifying URI
StepVerifier.create(retrieveResponse(client1))
.expectErrorMatches(ex -> WebClientRequestException.class.isAssignableFrom(ex.getClass()) && ex.getMessage()
.contains("Connection refused"))
.verify();
}
@Test
public void givenDifferentMethodSpecifications_whenUsed_thenObtainExpectedResponse() {
// request specification
RequestBodyUriSpec uriSpecPost1 = createDefaultClient().method(HttpMethod.POST);
RequestBodyUriSpec uriSpecPost2 = createDefaultClient().post();
RequestHeadersUriSpec<?> requestGet = createDefaultClient().get();
// response assertions
StepVerifier.create(retrieveResponse(uriSpecPost1))
.expectNext("processed-bodyValue")
.verifyComplete();
StepVerifier.create(retrieveResponse(uriSpecPost2))
.expectNext("processed-bodyValue")
.verifyComplete();
StepVerifier.create(retrieveGetResponse(requestGet))
.expectNextMatches(nextMap -> nextMap.get("field")
.equals("value"))
.verifyComplete();
}
@Test
public void givenDifferentUriSpecifications_whenUsed_thenObtainExpectedResponse() {
// uri specification
RequestBodySpec bodySpecUsingString = createDefaultPostRequest().uri("/resource");
RequestBodySpec bodySpecUsingUriBuilder = createDefaultPostRequest().uri(uriBuilder -> uriBuilder.pathSegment("resource")
.build());
RequestBodySpec bodySpecusingURI = createDefaultPostRequest().uri(URI.create("http://localhost:" + port + "/resource"));
RequestBodySpec bodySpecOverridenBaseUri = createDefaultPostRequest().uri(URI.create("/resource"));
RequestBodySpec bodySpecOverridenBaseUri2 = WebClient.builder()
.baseUrl("http://localhost:" + port)
.build()
.post()
.uri(URI.create("/resource"));
// response assertions
StepVerifier.create(retrieveResponse(bodySpecUsingString))
.expectNext("processed-bodyValue")
.verifyComplete();
StepVerifier.create(retrieveResponse(bodySpecUsingUriBuilder))
.expectNext("processed-bodyValue")
.verifyComplete();
StepVerifier.create(retrieveResponse(bodySpecusingURI))
.expectNext("processed-bodyValue")
.verifyComplete();
// assert sending request overriding base URI
StepVerifier.create(retrieveResponse(bodySpecOverridenBaseUri))
.expectErrorMatches(ex -> WebClientRequestException.class.isAssignableFrom(ex.getClass()) && ex.getMessage()
.contains("Connection refused"))
.verify();
StepVerifier.create(retrieveResponse(bodySpecOverridenBaseUri2))
.expectErrorMatches(ex -> WebClientRequestException.class.isAssignableFrom(ex.getClass()) && ex.getMessage()
.contains("Connection refused"))
.verify();
}
@Test
public void givenDifferentBodySpecifications_whenUsed_thenObtainExpectedResponse() {
// request body specifications
RequestHeadersSpec<?> headersSpecPost1 = createDefaultPostResourceRequest().body(BodyInserters.fromPublisher(Mono.just(BODY_VALUE), String.class));
RequestHeadersSpec<?> headersSpecPost2 = createDefaultPostResourceRequest().body(BodyInserters.fromValue(BODY_VALUE));
RequestHeadersSpec<?> headersSpecPost3 = createDefaultPostResourceRequest().bodyValue(BODY_VALUE);
RequestHeadersSpec<?> headersSpecFooPost = createDefaultPostRequest().uri("/resource-foo")
.body(Mono.just(new Foo("fooName")), Foo.class);
BodyInserter<Object, ReactiveHttpOutputMessage> inserterPlainObject = BodyInserters.fromValue(new Object());
RequestHeadersSpec<?> headersSpecPlainObject = createDefaultPostResourceRequest().body(inserterPlainObject);
// request body specifications - using other inserter method (multipart request)
LinkedMultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("key1", "multipartValue1");
map.add("key2", "multipartValue2");
BodyInserter<MultiValueMap<String, Object>, ClientHttpRequest> inserterMultipart = BodyInserters.fromMultipartData(map);
RequestHeadersSpec<?> headersSpecInserterMultipart = createDefaultPostRequest().uri("/resource-multipart")
.body(inserterMultipart);
// response assertions
StepVerifier.create(retrieveResponse(headersSpecPost1))
.expectNext("processed-bodyValue")
.verifyComplete();
StepVerifier.create(retrieveResponse(headersSpecPost2))
.expectNext("processed-bodyValue")
.verifyComplete();
StepVerifier.create(retrieveResponse(headersSpecPost3))
.expectNext("processed-bodyValue")
.verifyComplete();
StepVerifier.create(retrieveResponse(headersSpecFooPost))
.expectNext("processedFoo-fooName")
.verifyComplete();
StepVerifier.create(retrieveResponse(headersSpecInserterMultipart))
.expectNext("processed-multipartValue1-multipartValue2")
.verifyComplete();
// assert error plain `new Object()` as request body
StepVerifier.create(retrieveResponse(headersSpecPlainObject))
.expectError(CodecException.class)
.verify();
// assert response for request with no body
Mono<Map<String, String>> responsePostWithNoBody = createDefaultPostResourceRequest().exchangeToMono(responseHandler -> {
assertThat(responseHandler.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
return responseHandler.bodyToMono(MAP_RESPONSE_REF);
});
StepVerifier.create(responsePostWithNoBody)
.expectNextMatches(nextMap -> nextMap.get("error")
.equals("Bad Request"))
.verifyComplete();
}
@Test
public void givenPostSpecifications_whenHeadersAdded_thenObtainExpectedResponse() {
// request header specification
RequestHeadersSpec<?> headersSpecInserterStringWithHeaders = createDefaultPostResourceRequestResponse().header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
.acceptCharset(StandardCharsets.UTF_8)
.ifNoneMatch("*")
.ifModifiedSince(ZonedDateTime.now());
// response assertions
StepVerifier.create(retrieveResponse(headersSpecInserterStringWithHeaders))
.expectNext("processed-bodyValue")
.verifyComplete();
}
@Test
public void givenDifferentResponseSpecifications_whenUsed_thenObtainExpectedResponse() {
ResponseSpec responseSpecPostString = createDefaultPostResourceRequestResponse().retrieve();
Mono<String> responsePostString = responseSpecPostString.bodyToMono(String.class);
Mono<String> responsePostString2 = createDefaultPostResourceRequestResponse().exchangeToMono(response -> {
if (response.statusCode()
.equals(HttpStatus.OK)) {
return response.bodyToMono(String.class);
} else if (response.statusCode()
.is4xxClientError()) {
return Mono.just("Error response");
} else {
return response.createException()
.flatMap(Mono::error);
}
});
Mono<String> responsePostNoBody = createDefaultPostResourceRequest().exchangeToMono(response -> {
if (response.statusCode()
.equals(HttpStatus.OK)) {
return response.bodyToMono(String.class);
} else if (response.statusCode()
.is4xxClientError()) {
return Mono.just("Error response");
} else {
return response.createException()
.flatMap(Mono::error);
}
});
Mono<Map<String, String>> responseGet = createDefaultClient().get()
.uri("/resource")
.retrieve()
.bodyToMono(MAP_RESPONSE_REF);
// response assertions
StepVerifier.create(responsePostString)
.expectNext("processed-bodyValue")
.verifyComplete();
StepVerifier.create(responsePostString2)
.expectNext("processed-bodyValue")
.verifyComplete();
StepVerifier.create(responsePostNoBody)
.expectNext("Error response")
.verifyComplete();
StepVerifier.create(responseGet)
.expectNextMatches(nextMap -> nextMap.get("field")
.equals("value"))
.verifyComplete();
}
@Test
public void givenWebClientWithTimeoutConfigurations_whenRequestUsingWronglyConfiguredPublisher_thenObtainTimeout() {
HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000)
.responseTimeout(Duration.ofMillis(1000))
.doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(1000, TimeUnit.MILLISECONDS))
.addHandlerLast(new WriteTimeoutHandler(1000, TimeUnit.MILLISECONDS)));
WebClient timeoutClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
BodyInserter<Publisher<String>, ReactiveHttpOutputMessage> inserterCompleteSuscriber = BodyInserters.fromPublisher(Subscriber::onComplete, String.class);
RequestHeadersSpec<?> headersSpecInserterCompleteSuscriber = timeoutClient.post()
.uri("/resource")
.body(inserterCompleteSuscriber);
StepVerifier.create(headersSpecInserterCompleteSuscriber.retrieve()
.bodyToMono(String.class))
.expectTimeout(Duration.ofMillis(2000))
.verify();
}
// helper methods to create default instances
private WebClient createDefaultClient() {
return WebClient.create("http://localhost:" + port);
}
private RequestBodyUriSpec createDefaultPostRequest() {
return createDefaultClient().post();
}
private RequestBodySpec createDefaultPostResourceRequest() {
return createDefaultPostRequest().uri("/resource");
}
private RequestHeadersSpec<?> createDefaultPostResourceRequestResponse() {
return createDefaultPostResourceRequest().bodyValue(BODY_VALUE);
}
// helper methods to retrieve a response based on different steps of the process (specs)
private Mono<String> retrieveResponse(WebClient client) {
return client.post()
.uri("/resource")
.bodyValue(BODY_VALUE)
.retrieve()
.bodyToMono(String.class);
}
private Mono<String> retrieveResponse(RequestBodyUriSpec spec) {
return spec.uri("/resource")
.bodyValue(BODY_VALUE)
.retrieve()
.bodyToMono(String.class);
}
private Mono<Map<String, String>> retrieveGetResponse(RequestHeadersUriSpec<?> spec) {
return spec.uri("/resource")
.retrieve()
.bodyToMono(MAP_RESPONSE_REF);
}
private Mono<String> retrieveResponse(RequestBodySpec spec) {
return spec.bodyValue(BODY_VALUE)
.retrieve()
.bodyToMono(String.class);
}
private Mono<String> retrieveResponse(RequestHeadersSpec<?> spec) {
return spec.retrieve()
.bodyToMono(String.class);
}
}

View File

@ -11,3 +11,4 @@ This module contains articles about Spring aspect oriented programming (AOP)
- [Introduction to Pointcut Expressions in Spring](https://www.baeldung.com/spring-aop-pointcut-tutorial)
- [Introduction to Advice Types in Spring](https://www.baeldung.com/spring-aop-advice-tutorial)
- [When Does Java Throw UndeclaredThrowableException?](https://www.baeldung.com/java-undeclaredthrowableexception)
- [Get Advised Method Info in Spring AOP](https://www.baeldung.com/spring-aop-get-advised-method-info)

View File

@ -62,6 +62,7 @@
<module>spring-boot-properties-3</module>
<module>spring-boot-property-exp</module>
<module>spring-boot-runtime</module>
<module>spring-boot-runtime-2</module>
<module>spring-boot-security</module>
<module>spring-boot-springdoc</module>
<module>spring-boot-swagger</module>

View File

@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

View File

@ -1,4 +1,6 @@
management.health.probes.enabled=true
management.endpoint.health.probes.enabled=true
management.health.livenessState.enabled=true
management.health.readinessState.enabled=true
management.endpoint.health.show-details=always
management.endpoint.health.status.http-mapping.down=500
management.endpoint.health.status.http-mapping.out_of_service=503
@ -8,4 +10,4 @@ management.endpoint.health.status.http-mapping.warning=500
info.app.name=Spring Sample Application
info.app.description=This is my first spring boot application G1
info.app.version=1.0.0
info.java-vendor = ${java.specification.vendor}
info.java-vendor = ${java.specification.vendor}

View File

@ -10,3 +10,4 @@ This module contains articles about Spring Boot annotations
- [Spring Core Annotations](https://www.baeldung.com/spring-core-annotations)
- [Spring Bean Annotations](https://www.baeldung.com/spring-bean-annotations)
- [Difference Between @ComponentScan and @EnableAutoConfiguration in Spring Boot](https://www.baeldung.com/spring-componentscan-vs-enableautoconfiguration)
- [Where Should the Spring @Service Annotation Be Kept?](https://www.baeldung.com/spring-service-annotation-placement)

View File

@ -0,0 +1,22 @@
package com.baeldung.annotations.service;
import com.baeldung.annotations.service.abstracts.AbstractAuthenticationService;
import com.baeldung.annotations.service.interfaces.AuthenticationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AuthApplication {
@Autowired
private AuthenticationService inMemoryAuthService;
@Autowired
private AbstractAuthenticationService ldapAuthService;
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}
}

View File

@ -0,0 +1,12 @@
package com.baeldung.annotations.service.abstracts;
import org.springframework.stereotype.Service;
@Service
public abstract class AbstractAuthenticationService {
public boolean authenticate(String username, String password) {
return false;
}
}

View File

@ -0,0 +1,14 @@
package com.baeldung.annotations.service.concretes;
import com.baeldung.annotations.service.interfaces.AuthenticationService;
import org.springframework.stereotype.Service;
@Service
public class InMemoryAuthenticationService implements AuthenticationService {
@Override
public boolean authenticate(String username, String password) {
return false;
}
}

View File

@ -0,0 +1,14 @@
package com.baeldung.annotations.service.concretes;
import com.baeldung.annotations.service.abstracts.AbstractAuthenticationService;
import org.springframework.stereotype.Service;
@Service
public class LdapAuthenticationService extends AbstractAuthenticationService {
@Override
public boolean authenticate(String username, String password) {
return true;
}
}

View File

@ -0,0 +1,10 @@
package com.baeldung.annotations.service.interfaces;
import org.springframework.stereotype.Service;
@Service
public interface AuthenticationService {
boolean authenticate(String username, String password);
}

View File

@ -6,7 +6,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertAll;
public class EmployeeApplicationTest {
public class EmployeeApplicationUnitTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withUserConfiguration(EmployeeApplication.class);

View File

@ -0,0 +1,51 @@
package com.baeldung.annotations.service;
import com.baeldung.annotations.service.abstracts.AbstractAuthenticationService;
import com.baeldung.annotations.service.config.AbstractsAnnotatedTestConfiguration;
import com.baeldung.annotations.service.config.ConcreteClassesAnnotatedTestConfiguration;
import com.baeldung.annotations.service.config.InterfacesAnnotatedTestConfiguration;
import com.baeldung.annotations.service.interfaces.AuthenticationService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
public class AuthApplicationUnitTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
@Test
void whenOnlyInterfacesAnnotated_noSuchBeanDefinitionExceptionThrown() {
contextRunner
.withUserConfiguration(InterfacesAnnotatedTestConfiguration.class)
.run(context -> {
Assertions.assertThrows(NoSuchBeanDefinitionException.class, () -> {
context.getBean(AuthenticationService.class);
});
});
}
@Test
void whenOnlyAbstractClassesAnnotated_noSuchBeanDefinitionExceptionThrown() {
contextRunner
.withUserConfiguration(AbstractsAnnotatedTestConfiguration.class)
.run(context -> {
Assertions.assertThrows(NoSuchBeanDefinitionException.class, () -> {
context.getBean(AbstractAuthenticationService.class);
});
});
}
@Test
void whenConcreteClassesAnnotated_noExceptionThrown() {
contextRunner
.withUserConfiguration(ConcreteClassesAnnotatedTestConfiguration.class)
.run(context -> {
AuthenticationService inMemoryAuthService = context.getBean(AuthenticationService.class);
AbstractAuthenticationService ldapAuthService = context.getBean(AbstractAuthenticationService.class);
Assertions.assertNotNull(inMemoryAuthService);
Assertions.assertNotNull(ldapAuthService);
});
}
}

View File

@ -0,0 +1,10 @@
package com.baeldung.annotations.service.config;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.ComponentScan;
@TestConfiguration
@ComponentScan("com.baeldung.annotations.service.abstracts")
public class AbstractsAnnotatedTestConfiguration {
}

View File

@ -0,0 +1,10 @@
package com.baeldung.annotations.service.config;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.ComponentScan;
@TestConfiguration
@ComponentScan("com.baeldung.annotations.service.concretes")
public class ConcreteClassesAnnotatedTestConfiguration {
}

View File

@ -0,0 +1,10 @@
package com.baeldung.annotations.service.config;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.ComponentScan;
@TestConfiguration
@ComponentScan("com.baeldung.annotations.service.interfaces")
public class InterfacesAnnotatedTestConfiguration {
}

View File

@ -0,0 +1,6 @@
## Spring Boot Runtime 2
This module contains articles about administering a Spring Boot runtime
### Relevant Articles:
-

View File

@ -0,0 +1,66 @@
<?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-runtime-2</artifactId>
<packaging>jar</packaging>
<name>spring-boot-runtime-2</name>
<description>Demo project for Spring Boot</description>
<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>
<finalName>${project.artifactId}</finalName>
<resources>
<resource>
<directory>src/main/resources/heap</directory>
<targetPath>${project.build.directory}</targetPath>
<filtering>true</filtering>
<includes>
<include>${project.name}.conf</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<configuration>
<mainClass>com.baeldung.heap.HeapSizeDemoApplication</mainClass>
</configuration>
</execution>
</executions>
<configuration>
<executable>true</executable>
<jvmArguments>
-Xms256m
-Xmx1g
</jvmArguments>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,12 @@
package com.baeldung.heap;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class HeapSizeDemoApplication {
public static void main(String[] args) {
SpringApplication.run(HeapSizeDemoApplication.class, args);
}
}

View File

@ -0,0 +1,31 @@
package com.baeldung.heap;
public class MemoryStats {
private long heapSize;
private long heapMaxSize;
private long heapFreeSize;
public long getHeapSize() {
return heapSize;
}
public void setHeapSize(long heapSize) {
this.heapSize = heapSize;
}
public long getHeapMaxSize() {
return heapMaxSize;
}
public void setHeapMaxSize(long heapMaxSize) {
this.heapMaxSize = heapMaxSize;
}
public long getHeapFreeSize() {
return heapFreeSize;
}
public void setHeapFreeSize(long heapFreeSize) {
this.heapFreeSize = heapFreeSize;
}
}

View File

@ -0,0 +1,20 @@
package com.baeldung.heap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MemoryStatusController {
@GetMapping("memory-status")
public MemoryStats getMemoryStatistics() {
MemoryStats stats = new MemoryStats();
stats.setHeapSize(Runtime.getRuntime()
.totalMemory());
stats.setHeapMaxSize(Runtime.getRuntime()
.maxMemory());
stats.setHeapFreeSize(Runtime.getRuntime()
.freeMemory());
return stats;
}
}

View File

@ -0,0 +1 @@
JAVA_OPTS="-Xms512m -Xmx1024m"

View File

@ -0,0 +1,32 @@
package com.baeldung.heap;
import static org.hamcrest.Matchers.notANumber;
import static org.hamcrest.Matchers.not;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
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.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
@RunWith(SpringRunner.class)
@WebMvcTest(MemoryStatusController.class)
public class MemoryStatusControllerIntegrationTest {
@Autowired
private MockMvc mvc;
@Test
public void whenGetMemoryStatistics_thenReturnJsonArray() throws Exception {
mvc.perform(get("/memory-status").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("heapSize", not(notANumber())))
.andExpect(jsonPath("heapMaxSize", not(notANumber())))
.andExpect(jsonPath("heapFreeSize", not(notANumber())));
}
}

View File

@ -0,0 +1,31 @@
package com.baeldung.springdoc.controller;
import java.time.LocalDate;
import java.time.LocalTime;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Hidden;
@Hidden
@RestController
public class RegularRestController {
@Hidden
@GetMapping("/getAuthor")
public String getAuthor() {
return "Umang Budhwar";
}
@GetMapping("/getDate")
public LocalDate getDate() {
return LocalDate.now();
}
@GetMapping("/getTime")
public LocalTime getTime() {
return LocalTime.now();
}
}

View File

@ -7,3 +7,4 @@ This module contains articles about Spring MVC Forms using JSP
- [Getting Started with Forms in Spring MVC](https://www.baeldung.com/spring-mvc-form-tutorial)
- [Form Validation with AngularJS and Spring MVC](https://www.baeldung.com/validation-angularjs-spring-mvc)
- [A Guide to the JSTL Library](https://www.baeldung.com/jstl)
- [Multiple Submit Buttons on a Form](https://www.baeldung.com/spring-form-multiple-submit-buttons)

View File

@ -26,7 +26,7 @@ public class EmployeeController {
return employeeMap.get(Id);
}
@RequestMapping(value = "/addEmployee", method = RequestMethod.POST)
@RequestMapping(value = "/addEmployee", method = RequestMethod.POST, params = "submit")
public String submit(@Valid @ModelAttribute("employee") final Employee employee, final BindingResult result, final ModelMap model) {
if (result.hasErrors()) {
return "error";
@ -37,5 +37,11 @@ public class EmployeeController {
employeeMap.put(employee.getId(), employee);
return "employeeView";
}
@RequestMapping(value = "/addEmployee", method = RequestMethod.POST, params = "cancel")
public String cancel(@Valid @ModelAttribute("employee") final Employee employee, final BindingResult result, final ModelMap model) {
model.addAttribute("message", "You clicked cancel, please re-enter employee details:");
return "employeeHome";
}
}

View File

@ -7,6 +7,7 @@
</head>
<body>
<h3>Welcome, Enter The Employee Details</h3>
<h4>${message}</h4>
<form:form method="POST" action="${pageContext.request.contextPath}/addEmployee" modelAttribute="employee">
<table>
@ -23,7 +24,8 @@
<td><form:input path="contactNumber" /></td>
</tr>
<tr>
<td><input type="submit" value="Submit" /></td>
<td><input type="submit" name="submit" value="Submit" /></td>
<td><input type="submit" name="cancel" value="Cancel" /></td>
</tr>
</table>
</form:form>

View File

@ -8,3 +8,4 @@ The "REST With Spring 2" Classes: http://bit.ly/restwithspring
### Relevant Articles:
- [How to Turn Off Swagger-ui in Production](https://www.baeldung.com/swagger-ui-turn-off-in-production)
- [Setting a Request Timeout for a Spring REST API](https://www.baeldung.com/spring-rest-timeout)

View File

@ -10,3 +10,5 @@ This module contains articles about Spring RestTemplate
- [RestTemplate Post Request with JSON](https://www.baeldung.com/spring-resttemplate-post-json)
- [How to Compress Requests Using the Spring RestTemplate](https://www.baeldung.com/spring-resttemplate-compressing-requests)
- [Get list of JSON objects with Spring RestTemplate](https://www.baeldung.com/spring-resttemplate-json-list)
- [A Guide To Spring Redirects](https://www.baeldung.com/spring-redirect-and-forward)
- [Spring RestTemplate Exception: “Not enough variables available to expand”](https://www.baeldung.com/spring-not-enough-variables-available)

View File

@ -11,7 +11,7 @@
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>parent-boot-2/pom.xml</relativePath>
<relativePath>../../parent-boot-2/pom.xml</relativePath>
</parent>
<dependencies>

View File

@ -0,0 +1,13 @@
package com.baeldung.resttemplateexception;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RestTemplateExceptionApplication {
public static void main(String[] args) {
SpringApplication.run(RestTemplateExceptionApplication.class, args);
}
}

View File

@ -0,0 +1,46 @@
package com.baeldung.resttemplateexception.controller;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.baeldung.resttemplateexception.model.Criterion;
import com.baeldung.resttemplateexception.model.Product;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@RestController
@RequestMapping("/api")
public class ProductApi {
private List<Product> productList = new ArrayList<>(Arrays.asList(new Product(1, "Acer Aspire 5", 437), new Product(2, "ASUS VivoBook", 650), new Product(3, "Lenovo Legion", 990)));
@GetMapping("/get")
public Product get(@RequestParam String criterion) throws JsonMappingException, JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
Criterion crt = objectMapper.readValue(criterion, Criterion.class);
if (crt.getProp().equals("name"))
return findByName(crt.getValue());
// Search by other properties (id,price)
return null;
}
private Product findByName(String name) {
for (Product product : this.productList) {
if (product.getName().equals(name)) {
return product;
}
}
return null;
}
// Other methods
}

View File

@ -0,0 +1,28 @@
package com.baeldung.resttemplateexception.model;
public class Criterion {
private String prop;
private String value;
public Criterion() {
}
public String getProp() {
return prop;
}
public void setProp(String prop) {
this.prop = prop;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

View File

@ -0,0 +1,43 @@
package com.baeldung.resttemplateexception.model;
public class Product {
private int id;
private String name;
private double price;
public Product() {
}
public Product(int id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"
>
<context:component-scan base-package="com.baeldung.sampleapp.web" />
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" >
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
<!-- <bean class="org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter"/> -->
</mvc:message-converters>
</mvc:annotation-driven>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" />
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location">
<value>/WEB-INF/spring-views.xml</value>
</property>
<property name="order" value="0" />
</bean>
<bean id="contentNegotiationManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="defaultContentType" value="application/json" />
</bean>
</beans>

View File

@ -0,0 +1,10 @@
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"
>
<bean id="RedirectedUrl" class="org.springframework.web.servlet.view.RedirectView">
<property name="url" value="redirectedUrl" />
</bean>
</beans>

View File

@ -0,0 +1,47 @@
package com.baeldung.resttemplateexception;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import com.baeldung.resttemplateexception.model.Product;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { RestTemplate.class, RestTemplateExceptionApplication.class })
public class RestTemplateExceptionLiveTest {
@Autowired
RestTemplate restTemplate;
@Test(expected = IllegalArgumentException.class)
public void givenGetUrl_whenJsonIsPassed_thenThrowException() {
String url = "http://localhost:8080/spring-rest/api/get?criterion={\"prop\":\"name\",\"value\":\"ASUS VivoBook\"}";
Product product = restTemplate.getForObject(url, Product.class);
}
@Test
public void givenGetUrl_whenJsonIsPassed_thenGetProduct() {
String criterion = "{\"prop\":\"name\",\"value\":\"ASUS VivoBook\"}";
String url = "http://localhost:8080/spring-rest/api/get?criterion={criterion}";
Product product = restTemplate.getForObject(url, Product.class, criterion);
assertEquals(product.getPrice(), 650, 0);
}
@Test
public void givenGetUrl_whenJsonIsPassed_thenReturnProduct() {
String criterion = "{\"prop\":\"name\",\"value\":\"Acer Aspire 5\"}";
String url = "http://localhost:8080/spring-rest/api/get";
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(url).queryParam("criterion", criterion);
Product product = restTemplate.getForObject(builder.build().toUri(), Product.class);
assertEquals(product.getId(), 1, 0);
}
}

View File

@ -188,7 +188,7 @@
<artifactId>cargo-maven2-plugin</artifactId>
<version>${cargo-maven2-plugin.version}</version>
<configuration>
<!--<wait>true</wait> caused errors on commit -->
<wait>true</wait>
<container>
<containerId>tomcat8x</containerId>
<type>embedded</type>
@ -297,7 +297,7 @@
<guava.version>20.0</guava.version>
<!-- Maven plugins -->
<cargo-maven2-plugin.version>1.6.0</cargo-maven2-plugin.version>
<cargo-maven2-plugin.version>1.6.1</cargo-maven2-plugin.version>
<findbugs-maven-plugin.version>3.0.4</findbugs-maven-plugin.version>
<!-- okhttp -->

View File

@ -1,4 +1,4 @@
package com.baeldung.resttemplate.web.service;
package com.baeldung.mock;
import com.baeldung.resttemplate.web.model.Employee;
import org.slf4j.Logger;

View File

@ -18,6 +18,7 @@ import org.springframework.web.client.RestTemplate;
import okhttp3.Request;
import okhttp3.RequestBody;
// This test needs RestTemplateConfigurationApplication to be up and running
public class TestRestTemplateBasicLiveTest {
private RestTemplate restTemplate;

View File

@ -1,4 +1,4 @@
package com.baeldung.web.service;
package com.baeldung.mock;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
@ -7,8 +7,9 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat
import java.net.URI;
import com.baeldung.SpringTestConfig;
import com.baeldung.mock.EmployeeService;
import com.baeldung.resttemplate.web.model.Employee;
import com.baeldung.resttemplate.web.service.EmployeeService;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

View File

@ -1,7 +1,8 @@
package com.baeldung.web.service;
package com.baeldung.mock;
import com.baeldung.mock.EmployeeService;
import com.baeldung.resttemplate.web.model.Employee;
import com.baeldung.resttemplate.web.service.EmployeeService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

View File

@ -38,6 +38,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.google.common.base.Charsets;
// This test needs RestTemplateConfigurationApplication to be up and running
public class RestTemplateBasicLiveTest {
private RestTemplate restTemplate;

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