Merge remote-tracking branch 'eugenp/master'
This commit is contained in:
commit
f32f471a0a
|
@ -0,0 +1,33 @@
|
|||
package com.baeldung.concurrent.countdownlatch;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public class CountdownLatchCountExample {
|
||||
|
||||
private int count;
|
||||
|
||||
public CountdownLatchCountExample(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public boolean callTwiceInSameThread() {
|
||||
CountDownLatch countDownLatch = new CountDownLatch(count);
|
||||
Thread t = new Thread(() -> {
|
||||
countDownLatch.countDown();
|
||||
countDownLatch.countDown();
|
||||
});
|
||||
t.start();
|
||||
|
||||
try {
|
||||
countDownLatch.await();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return countDownLatch.getCount() == 0;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
CountdownLatchCountExample ex = new CountdownLatchCountExample(2);
|
||||
System.out.println("Is CountDown Completed : " + ex.callTwiceInSameThread());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package com.baeldung.concurrent.countdownlatch;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class CountdownLatchResetExample {
|
||||
|
||||
private int count;
|
||||
private int threadCount;
|
||||
private final AtomicInteger updateCount;
|
||||
|
||||
CountdownLatchResetExample(int count, int threadCount) {
|
||||
updateCount = new AtomicInteger(0);
|
||||
this.count = count;
|
||||
this.threadCount = threadCount;
|
||||
}
|
||||
|
||||
public int countWaits() {
|
||||
CountDownLatch countDownLatch = new CountDownLatch(count);
|
||||
ExecutorService es = Executors.newFixedThreadPool(threadCount);
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
es.execute(() -> {
|
||||
long prevValue = countDownLatch.getCount();
|
||||
countDownLatch.countDown();
|
||||
if (countDownLatch.getCount() != prevValue) {
|
||||
updateCount.incrementAndGet();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
es.shutdown();
|
||||
return updateCount.get();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
CountdownLatchResetExample ex = new CountdownLatchResetExample(5, 20);
|
||||
System.out.println("Count : " + ex.countWaits());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package com.baeldung.concurrent.cyclicbarrier;
|
||||
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class CyclicBarrierCompletionMethodExample {
|
||||
|
||||
private int count;
|
||||
private int threadCount;
|
||||
private final AtomicInteger updateCount;
|
||||
|
||||
CyclicBarrierCompletionMethodExample(int count, int threadCount) {
|
||||
updateCount = new AtomicInteger(0);
|
||||
this.count = count;
|
||||
this.threadCount = threadCount;
|
||||
}
|
||||
|
||||
public int countTrips() {
|
||||
|
||||
CyclicBarrier cyclicBarrier = new CyclicBarrier(count, () -> {
|
||||
updateCount.incrementAndGet();
|
||||
});
|
||||
|
||||
ExecutorService es = Executors.newFixedThreadPool(threadCount);
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
es.execute(() -> {
|
||||
try {
|
||||
cyclicBarrier.await();
|
||||
} catch (InterruptedException | BrokenBarrierException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
es.shutdown();
|
||||
return updateCount.get();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
CyclicBarrierCompletionMethodExample ex = new CyclicBarrierCompletionMethodExample(5, 20);
|
||||
System.out.println("Count : " + ex.countTrips());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package com.baeldung.concurrent.cyclicbarrier;
|
||||
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
|
||||
public class CyclicBarrierCountExample {
|
||||
|
||||
private int count;
|
||||
|
||||
public CyclicBarrierCountExample(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public boolean callTwiceInSameThread() {
|
||||
CyclicBarrier cyclicBarrier = new CyclicBarrier(count);
|
||||
Thread t = new Thread(() -> {
|
||||
try {
|
||||
cyclicBarrier.await();
|
||||
cyclicBarrier.await();
|
||||
} catch (InterruptedException | BrokenBarrierException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
return cyclicBarrier.isBroken();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
CyclicBarrierCountExample ex = new CyclicBarrierCountExample(7);
|
||||
System.out.println("Count : " + ex.callTwiceInSameThread());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package com.baeldung.concurrent.cyclicbarrier;
|
||||
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class CyclicBarrierResetExample {
|
||||
|
||||
private int count;
|
||||
private int threadCount;
|
||||
private final AtomicInteger updateCount;
|
||||
|
||||
CyclicBarrierResetExample(int count, int threadCount) {
|
||||
updateCount = new AtomicInteger(0);
|
||||
this.count = count;
|
||||
this.threadCount = threadCount;
|
||||
}
|
||||
|
||||
public int countWaits() {
|
||||
|
||||
CyclicBarrier cyclicBarrier = new CyclicBarrier(count);
|
||||
|
||||
ExecutorService es = Executors.newFixedThreadPool(threadCount);
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
es.execute(() -> {
|
||||
try {
|
||||
if (cyclicBarrier.getNumberWaiting() > 0) {
|
||||
updateCount.incrementAndGet();
|
||||
}
|
||||
cyclicBarrier.await();
|
||||
} catch (InterruptedException | BrokenBarrierException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
es.shutdown();
|
||||
return updateCount.get();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
CyclicBarrierResetExample ex = new CyclicBarrierResetExample(7, 20);
|
||||
System.out.println("Count : " + ex.countWaits());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.baeldung.concurrent.countdownlatch;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class CountdownLatchCountExampleUnitTest {
|
||||
|
||||
@Test
|
||||
public void whenCountDownLatch_completed() {
|
||||
CountdownLatchCountExample ex = new CountdownLatchCountExample(2);
|
||||
boolean isCompleted = ex.callTwiceInSameThread();
|
||||
assertTrue(isCompleted);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.baeldung.concurrent.countdownlatch;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class CountdownLatchResetExampleUnitTest {
|
||||
|
||||
@Test
|
||||
public void whenCountDownLatch_noReset() {
|
||||
CountdownLatchResetExample ex = new CountdownLatchResetExample(7,20);
|
||||
int lineCount = ex.countWaits();
|
||||
assertTrue(lineCount <= 7);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.baeldung.concurrent.cyclicbarrier;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class CyclicBarrierCompletionMethodExampleUnitTest {
|
||||
|
||||
@Test
|
||||
public void whenCyclicBarrier_countTrips() {
|
||||
CyclicBarrierCompletionMethodExample ex = new CyclicBarrierCompletionMethodExample(7,20);
|
||||
int lineCount = ex.countTrips();
|
||||
assertEquals(2, lineCount);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.baeldung.concurrent.cyclicbarrier;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class CyclicBarrierCountExampleUnitTest {
|
||||
|
||||
@Test
|
||||
public void whenCyclicBarrier_notCompleted() {
|
||||
CyclicBarrierCountExample ex = new CyclicBarrierCountExample(2);
|
||||
boolean isCompleted = ex.callTwiceInSameThread();
|
||||
assertFalse(isCompleted);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.baeldung.concurrent.cyclicbarrier;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class CyclicBarrierResetExampleUnitTest {
|
||||
|
||||
@Test
|
||||
public void whenCyclicBarrier_reset() {
|
||||
CyclicBarrierResetExample ex = new CyclicBarrierResetExample(7,20);
|
||||
int lineCount = ex.countWaits();
|
||||
assertTrue(lineCount > 7);
|
||||
}
|
||||
}
|
|
@ -66,6 +66,12 @@
|
|||
<artifactId>mail</artifactId>
|
||||
<version>${javax.mail.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>nl.jqno.equalsverifier</groupId>
|
||||
<artifactId>equalsverifier</artifactId>
|
||||
<version>${equalsverifier.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -424,6 +430,7 @@
|
|||
<maven-shade-plugin.version>3.1.1</maven-shade-plugin.version>
|
||||
<spring-boot-maven-plugin.version>2.0.3.RELEASE</spring-boot-maven-plugin.version>
|
||||
<exec-maven-plugin.version>1.6.0</exec-maven-plugin.version>
|
||||
<equalsverifier.version>3.0.3</equalsverifier.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package com.baeldung.equalshashcode;
|
||||
|
||||
class Money {
|
||||
|
||||
int amount;
|
||||
String currencyCode;
|
||||
|
||||
Money(int amount, String currencyCode) {
|
||||
this.amount = amount;
|
||||
this.currencyCode = currencyCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
if (!(o instanceof Money))
|
||||
return false;
|
||||
Money other = (Money)o;
|
||||
boolean currencyCodeEquals = (this.currencyCode == null && other.currencyCode == null)
|
||||
|| (this.currencyCode != null && this.currencyCode.equals(other.currencyCode));
|
||||
return this.amount == other.amount
|
||||
&& currencyCodeEquals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 17;
|
||||
result = 31 * result + amount;
|
||||
if (currencyCode != null) {
|
||||
result = 31 * result + currencyCode.hashCode();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package com.baeldung.equalshashcode;
|
||||
|
||||
class Team {
|
||||
|
||||
final String city;
|
||||
final String department;
|
||||
|
||||
Team(String city, String department) {
|
||||
this.city = city;
|
||||
this.department = department;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
if (!(o instanceof Team))
|
||||
return false;
|
||||
Team otherTeam = (Team)o;
|
||||
boolean cityEquals = (this.city == null && otherTeam.city == null)
|
||||
|| this.city != null && this.city.equals(otherTeam.city);
|
||||
boolean departmentEquals = (this.department == null && otherTeam.department == null)
|
||||
|| this.department != null && this.department.equals(otherTeam.department);
|
||||
return cityEquals && departmentEquals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
int result = 17;
|
||||
if (city != null) {
|
||||
result = 31 * result + city.hashCode();
|
||||
}
|
||||
if (department != null) {
|
||||
result = 31 * result + department.hashCode();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.baeldung.equalshashcode;
|
||||
|
||||
class Voucher {
|
||||
|
||||
private Money value;
|
||||
private String store;
|
||||
|
||||
Voucher(int amount, String currencyCode, String store) {
|
||||
this.value = new Money(amount, currencyCode);
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
if (!(o instanceof Voucher))
|
||||
return false;
|
||||
Voucher other = (Voucher)o;
|
||||
boolean valueEquals = (this.value == null && other.value == null)
|
||||
|| (this.value != null && this.value.equals(other.value));
|
||||
boolean storeEquals = (this.store == null && other.store == null)
|
||||
|| (this.store != null && this.store.equals(other.store));
|
||||
return valueEquals && storeEquals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 17;
|
||||
if (this.value != null) {
|
||||
result = 31 * result + value.hashCode();
|
||||
}
|
||||
if (this.store != null) {
|
||||
result = 31 * result + store.hashCode();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package com.baeldung.equalshashcode;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* This class overrides equals, but it doesn't override hashCode.
|
||||
*
|
||||
* To see which problems this leads to:
|
||||
* TeamUnitTest.givenMapKeyWithoutHashCode_whenSearched_thenReturnsWrongValue
|
||||
*/
|
||||
class WrongTeam {
|
||||
|
||||
String city;
|
||||
String department;
|
||||
|
||||
WrongTeam(String city, String department) {
|
||||
this.city = city;
|
||||
this.department = department;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
if (!(o instanceof WrongTeam))
|
||||
return false;
|
||||
WrongTeam otherTeam = (WrongTeam)o;
|
||||
return this.city == otherTeam.city
|
||||
&& this.department == otherTeam.department;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package com.baeldung.equalshashcode;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* This class extends the Money class that has overridden the equals method and once again overrides the equals method.
|
||||
*
|
||||
* To see which problems this leads to:
|
||||
* MoneyUnitTest.givenMoneyAndVoucherInstances_whenEquals_thenReturnValuesArentSymmetric
|
||||
*/
|
||||
class WrongVoucher extends Money {
|
||||
|
||||
private String store;
|
||||
|
||||
WrongVoucher(int amount, String currencyCode, String store) {
|
||||
super(amount, currencyCode);
|
||||
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
if (!(o instanceof WrongVoucher))
|
||||
return false;
|
||||
WrongVoucher other = (WrongVoucher)o;
|
||||
boolean currencyCodeEquals = (this.currencyCode == null && other.currencyCode == null)
|
||||
|| (this.currencyCode != null && this.currencyCode.equals(other.currencyCode));
|
||||
boolean storeEquals = (this.store == null && other.store == null)
|
||||
|| (this.store != null && this.store.equals(other.store));
|
||||
return this.amount == other.amount
|
||||
&& currencyCodeEquals
|
||||
&& storeEquals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 17;
|
||||
result = 31 * result + amount;
|
||||
if (this.currencyCode != null) {
|
||||
result = 31 * result + currencyCode.hashCode();
|
||||
}
|
||||
if (this.store != null) {
|
||||
result = 31 * result + store.hashCode();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package com.baeldung.equalshashcode;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class MoneyUnitTest {
|
||||
|
||||
@Test
|
||||
public void givenMoneyInstancesWithSameAmountAndCurrency_whenEquals_thenReturnsTrue() {
|
||||
Money income = new Money(55, "USD");
|
||||
Money expenses = new Money(55, "USD");
|
||||
|
||||
assertTrue(income.equals(expenses));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenMoneyAndVoucherInstances_whenEquals_thenReturnValuesArentSymmetric() {
|
||||
Money cash = new Money(42, "USD");
|
||||
WrongVoucher voucher = new WrongVoucher(42, "USD", "Amazon");
|
||||
|
||||
assertFalse(voucher.equals(cash));
|
||||
assertTrue(cash.equals(voucher));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package com.baeldung.equalshashcode;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import nl.jqno.equalsverifier.EqualsVerifier;
|
||||
|
||||
public class TeamUnitTest {
|
||||
|
||||
@Test
|
||||
public void givenMapKeyWithHashCode_whenSearched_thenReturnsCorrectValue() {
|
||||
Map<Team,String> leaders = new HashMap<>();
|
||||
leaders.put(new Team("New York", "development"), "Anne");
|
||||
leaders.put(new Team("Boston", "development"), "Brian");
|
||||
leaders.put(new Team("Boston", "marketing"), "Charlie");
|
||||
|
||||
Team myTeam = new Team("New York", "development");
|
||||
String myTeamleader = leaders.get(myTeam);
|
||||
|
||||
assertEquals("Anne", myTeamleader);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenMapKeyWithoutHashCode_whenSearched_thenReturnsWrongValue() {
|
||||
Map<WrongTeam,String> leaders = new HashMap<>();
|
||||
leaders.put(new WrongTeam("New York", "development"), "Anne");
|
||||
leaders.put(new WrongTeam("Boston", "development"), "Brian");
|
||||
leaders.put(new WrongTeam("Boston", "marketing"), "Charlie");
|
||||
|
||||
WrongTeam myTeam = new WrongTeam("New York", "development");
|
||||
String myTeamleader = leaders.get(myTeam);
|
||||
|
||||
assertFalse("Anne".equals(myTeamleader));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsContract() {
|
||||
EqualsVerifier.forClass(Team.class).verify();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
package org.baeldung.convertarraytostring;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ArrayToStringUnitTest {
|
||||
|
||||
// convert with Java
|
||||
|
||||
@Test
|
||||
public void givenAStringArray_whenConvertBeforeJava8_thenReturnString() {
|
||||
|
||||
String[] strArray = { "Convert", "Array", "With", "Java" };
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < strArray.length; i++) {
|
||||
stringBuilder.append(strArray[i]);
|
||||
}
|
||||
String joinedString = stringBuilder.toString();
|
||||
|
||||
assertThat(joinedString, instanceOf(String.class));
|
||||
assertEquals("ConvertArrayWithJava", joinedString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAString_whenConvertBeforeJava8_thenReturnStringArray() {
|
||||
|
||||
String input = "lorem ipsum dolor sit amet";
|
||||
String[] strArray = input.split(" ");
|
||||
|
||||
assertThat(strArray, instanceOf(String[].class));
|
||||
assertEquals(5, strArray.length);
|
||||
|
||||
input = "loremipsum";
|
||||
strArray = input.split("");
|
||||
assertThat(strArray, instanceOf(String[].class));
|
||||
assertEquals(10, strArray.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAnIntArray_whenConvertBeforeJava8_thenReturnString() {
|
||||
|
||||
int[] strArray = { 1, 2, 3, 4, 5 };
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < strArray.length; i++) {
|
||||
stringBuilder.append(Integer.valueOf(strArray[i]));
|
||||
}
|
||||
String joinedString = stringBuilder.toString();
|
||||
|
||||
assertThat(joinedString, instanceOf(String.class));
|
||||
assertEquals("12345", joinedString);
|
||||
}
|
||||
|
||||
// convert with Java Stream API
|
||||
|
||||
@Test
|
||||
public void givenAStringArray_whenConvertWithJavaStream_thenReturnString() {
|
||||
|
||||
String[] strArray = { "Convert", "With", "Java", "Streams" };
|
||||
String joinedString = Arrays.stream(strArray)
|
||||
.collect(Collectors.joining());
|
||||
assertThat(joinedString, instanceOf(String.class));
|
||||
assertEquals("ConvertWithJavaStreams", joinedString);
|
||||
|
||||
joinedString = Arrays.stream(strArray)
|
||||
.collect(Collectors.joining(","));
|
||||
assertThat(joinedString, instanceOf(String.class));
|
||||
assertEquals("Convert,With,Java,Streams", joinedString);
|
||||
}
|
||||
|
||||
|
||||
// convert with Apache Commons
|
||||
|
||||
@Test
|
||||
public void givenAStringArray_whenConvertWithApacheCommons_thenReturnString() {
|
||||
|
||||
String[] strArray = { "Convert", "With", "Apache", "Commons" };
|
||||
String joinedString = StringUtils.join(strArray);
|
||||
|
||||
assertThat(joinedString, instanceOf(String.class));
|
||||
assertEquals("ConvertWithApacheCommons", joinedString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAString_whenConvertWithApacheCommons_thenReturnStringArray() {
|
||||
|
||||
String input = "lorem ipsum dolor sit amet";
|
||||
String[] strArray = StringUtils.split(input, " ");
|
||||
|
||||
assertThat(strArray, instanceOf(String[].class));
|
||||
assertEquals(5, strArray.length);
|
||||
}
|
||||
|
||||
|
||||
// convert with Guava
|
||||
|
||||
@Test
|
||||
public void givenAStringArray_whenConvertWithGuava_thenReturnString() {
|
||||
|
||||
String[] strArray = { "Convert", "With", "Guava", null };
|
||||
String joinedString = Joiner.on("")
|
||||
.skipNulls()
|
||||
.join(strArray);
|
||||
|
||||
assertThat(joinedString, instanceOf(String.class));
|
||||
assertEquals("ConvertWithGuava", joinedString);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void givenAString_whenConvertWithGuava_thenReturnStringArray() {
|
||||
|
||||
String input = "lorem ipsum dolor sit amet";
|
||||
|
||||
List<String> resultList = Splitter.on(' ')
|
||||
.trimResults()
|
||||
.omitEmptyStrings()
|
||||
.splitToList(input);
|
||||
String[] strArray = resultList.toArray(new String[0]);
|
||||
|
||||
assertThat(strArray, instanceOf(String[].class));
|
||||
assertEquals(5, strArray.length);
|
||||
}
|
||||
}
|
|
@ -37,3 +37,4 @@
|
|||
- [Using indexOf to Find All Occurrences of a Word in a String](https://www.baeldung.com/java-indexOf-find-string-occurrences)
|
||||
- [Java Base64 Encoding and Decoding](https://www.baeldung.com/java-base64-encode-and-decode)
|
||||
- [Generate a Secure Random Password in Java](https://www.baeldung.com/java-generate-secure-password)
|
||||
- [Removing Repeated Characters from a String](https://www.baeldung.com/java-remove-repeated-char)
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
package com.baeldung.string;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.primitives.Chars;
|
||||
import com.google.common.primitives.Ints;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.CharBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
public class StringFromPrimitiveArrayUnitTest {
|
||||
|
||||
private int[] intArray = {1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
|
||||
private char[] charArray = {'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
|
||||
private char separatorChar = '-';
|
||||
|
||||
private String separator = String.valueOf(separatorChar);
|
||||
|
||||
private String expectedIntString = "1-2-3-4-5-6-7-8-9";
|
||||
|
||||
private String expectedCharString = "a-b-c-d-e-f";
|
||||
|
||||
@Test
|
||||
public void givenIntArray_whenJoinBySeparator_thenReturnsString_through_Java8CollectorsJoining() {
|
||||
assertThat(Arrays.stream(intArray)
|
||||
.mapToObj(String::valueOf)
|
||||
.collect(Collectors.joining(separator)))
|
||||
.isEqualTo(expectedIntString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCharArray_whenJoinBySeparator_thenReturnsString_through_Java8CollectorsJoining() {
|
||||
assertThat(CharBuffer.wrap(charArray).chars()
|
||||
.mapToObj(intChar -> String.valueOf((char) intChar))
|
||||
.collect(Collectors.joining(separator)))
|
||||
.isEqualTo(expectedCharString);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void giveIntArray_whenJoinBySeparator_thenReturnsString_through_Java8StringJoiner() {
|
||||
StringJoiner intStringJoiner = new StringJoiner(separator);
|
||||
|
||||
Arrays.stream(intArray)
|
||||
.mapToObj(String::valueOf)
|
||||
.forEach(intStringJoiner::add);
|
||||
|
||||
assertThat(intStringJoiner.toString()).isEqualTo(expectedIntString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCharArray_whenJoinBySeparator_thenReturnsString_through_Java8StringJoiner() {
|
||||
StringJoiner charStringJoiner = new StringJoiner(separator);
|
||||
|
||||
CharBuffer.wrap(charArray).chars()
|
||||
.mapToObj(intChar -> String.valueOf((char) intChar))
|
||||
.forEach(charStringJoiner::add);
|
||||
|
||||
assertThat(charStringJoiner.toString()).isEqualTo(expectedCharString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenIntArray_whenJoinBySeparator_thenReturnsString_through_CommonsLang() {
|
||||
assertThat(StringUtils.join(intArray, separatorChar)).isEqualTo(expectedIntString);
|
||||
assertThat(StringUtils.join(ArrayUtils.toObject(intArray), separator)).isEqualTo(expectedIntString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCharArray_whenJoinBySeparator_thenReturnsString_through_CommonsLang() {
|
||||
assertThat(StringUtils.join(charArray, separatorChar)).isEqualTo(expectedCharString);
|
||||
assertThat(StringUtils.join(ArrayUtils.toObject(charArray), separator)).isEqualTo(expectedCharString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenIntArray_whenJoinBySeparator_thenReturnsString_through_GuavaJoiner() {
|
||||
assertThat(Joiner.on(separator).join(Ints.asList(intArray))).isEqualTo(expectedIntString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCharArray_whenJoinBySeparator_thenReturnsString_through_GuavaJoiner() {
|
||||
assertThat(Joiner.on(separator).join(Chars.asList(charArray))).isEqualTo(expectedCharString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenIntArray_whenJoinBySeparator_thenReturnsString_through_Java7StringBuilder() {
|
||||
assertThat(joinIntArrayWithStringBuilder(intArray, separator)).isEqualTo(expectedIntString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCharArray_whenJoinBySeparator_thenReturnsString_through_Java7StringBuilder() {
|
||||
assertThat(joinCharArrayWithStringBuilder(charArray, separator)).isEqualTo(expectedCharString);
|
||||
}
|
||||
|
||||
private String joinIntArrayWithStringBuilder(int[] array, String separator) {
|
||||
if (array.length == 0) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (int i = 0; i < array.length - 1; i++) {
|
||||
stringBuilder.append(array[i]);
|
||||
stringBuilder.append(separator);
|
||||
}
|
||||
stringBuilder.append(array[array.length - 1]);
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
private String joinCharArrayWithStringBuilder(char[] array, String separator) {
|
||||
if (array.length == 0) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (int i = 0; i < array.length - 1; i++) {
|
||||
stringBuilder.append(array[i]);
|
||||
stringBuilder.append(separator);
|
||||
}
|
||||
stringBuilder.append(array[array.length - 1]);
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package com.baeldung.hibernate.findall;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.hibernate.Session;
|
||||
|
||||
import com.baeldung.hibernate.pojo.Student;
|
||||
|
||||
public class FindAll {
|
||||
|
||||
private Session session;
|
||||
|
||||
public FindAll(Session session) {
|
||||
super();
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public List<Student> findAllWithJpql() {
|
||||
return session.createQuery("SELECT a FROM Student a", Student.class).getResultList();
|
||||
}
|
||||
|
||||
public List<Student> findAllWithCriteriaQuery() {
|
||||
CriteriaBuilder cb = session.getCriteriaBuilder();
|
||||
CriteriaQuery<Student> cq = cb.createQuery(Student.class);
|
||||
Root<Student> rootEntry = cq.from(Student.class);
|
||||
CriteriaQuery<Student> all = cq.select(rootEntry);
|
||||
TypedQuery<Student> allQuery = session.createQuery(all);
|
||||
return allQuery.getResultList();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package com.baeldung.hibernate.findall;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.Transaction;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.baeldung.hibernate.HibernateUtil;
|
||||
import com.baeldung.hibernate.pojo.Student;
|
||||
|
||||
public class FindAllUnitTest {
|
||||
|
||||
private Session session;
|
||||
private Transaction transaction;
|
||||
|
||||
private FindAll findAll;
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
|
||||
session = HibernateUtil.getSessionFactory().openSession();
|
||||
transaction = session.beginTransaction();
|
||||
findAll = new FindAll(session);
|
||||
|
||||
session.createNativeQuery("delete from Student").executeUpdate();
|
||||
|
||||
Student student1 = new Student();
|
||||
session.persist(student1);
|
||||
|
||||
Student student2 = new Student();
|
||||
session.persist(student2);
|
||||
|
||||
Student student3 = new Student();
|
||||
session.persist(student3);
|
||||
|
||||
transaction.commit();
|
||||
transaction = session.beginTransaction();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
transaction.rollback();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCriteriaQuery_WhenFindAll_ThenGetAllPersons() {
|
||||
List<Student> list = findAll.findAllWithCriteriaQuery();
|
||||
assertEquals(3, list.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenJpql_WhenFindAll_ThenGetAllPersons() {
|
||||
List<Student> list = findAll.findAllWithJpql();
|
||||
assertEquals(3, list.size());
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package com.baeldung.limit;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
interface PassengerRepository extends JpaRepository<Passenger, Long>, CustomPassengerRepository {
|
||||
|
||||
Passenger findFirstByOrderBySeatNumberAsc();
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.baeldung.limit;
|
||||
package com.baeldung.passenger;
|
||||
|
||||
import java.util.List;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.baeldung.limit;
|
||||
package com.baeldung.passenger;
|
||||
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Column;
|
||||
|
@ -17,7 +17,7 @@ class Passenger {
|
|||
|
||||
@Basic(optional = false)
|
||||
@Column(nullable = false)
|
||||
private String fistName;
|
||||
private String firstName;
|
||||
|
||||
@Basic(optional = false)
|
||||
@Column(nullable = false)
|
||||
|
@ -27,8 +27,8 @@ class Passenger {
|
|||
@Column(nullable = false)
|
||||
private int seatNumber;
|
||||
|
||||
private Passenger(String fistName, String lastName, int seatNumber) {
|
||||
this.fistName = fistName;
|
||||
private Passenger(String firstName, String lastName, int seatNumber) {
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
this.seatNumber = seatNumber;
|
||||
}
|
||||
|
@ -44,20 +44,20 @@ class Passenger {
|
|||
if (object == null || getClass() != object.getClass())
|
||||
return false;
|
||||
Passenger passenger = (Passenger) object;
|
||||
return getSeatNumber() == passenger.getSeatNumber() && Objects.equals(getFistName(), passenger.getFistName())
|
||||
return getSeatNumber() == passenger.getSeatNumber() && Objects.equals(getFirstName(), passenger.getFirstName())
|
||||
&& Objects.equals(getLastName(), passenger.getLastName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getFistName(), getLastName(), getSeatNumber());
|
||||
return Objects.hash(getFirstName(), getLastName(), getSeatNumber());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder toStringBuilder = new StringBuilder(getClass().getSimpleName());
|
||||
toStringBuilder.append("{ id=").append(id);
|
||||
toStringBuilder.append(", fistName='").append(fistName).append('\'');
|
||||
toStringBuilder.append(", firstName='").append(firstName).append('\'');
|
||||
toStringBuilder.append(", lastName='").append(lastName).append('\'');
|
||||
toStringBuilder.append(", seatNumber=").append(seatNumber);
|
||||
toStringBuilder.append('}');
|
||||
|
@ -68,8 +68,8 @@ class Passenger {
|
|||
return id;
|
||||
}
|
||||
|
||||
String getFistName() {
|
||||
return fistName;
|
||||
String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
String getLastName() {
|
|
@ -0,0 +1,17 @@
|
|||
package com.baeldung.passenger;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
interface PassengerRepository extends JpaRepository<Passenger, Long>, CustomPassengerRepository {
|
||||
|
||||
Passenger findFirstByOrderBySeatNumberAsc();
|
||||
|
||||
List<Passenger> findByOrderBySeatNumberAsc();
|
||||
|
||||
List<Passenger> findByLastNameOrderBySeatNumberAsc(String lastName);
|
||||
|
||||
List<Passenger> findByLastName(String lastName, Sort sort);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.baeldung.limit;
|
||||
package com.baeldung.passenger;
|
||||
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.baeldung.limit;
|
||||
package com.baeldung.passenger;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -14,11 +14,13 @@ import javax.persistence.EntityManager;
|
|||
import javax.persistence.PersistenceContext;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@DataJpaTest
|
||||
@RunWith(SpringRunner.class)
|
||||
public class LimitIntegrationTest {
|
||||
public class PassengerRepositoryIntegrationTest {
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager entityManager;
|
||||
|
@ -66,4 +68,31 @@ public class LimitIntegrationTest {
|
|||
Passenger actual = page.getContent().get(0);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenPassengers_whenOrderedBySeatNumberAsc_thenCorrectOrder() {
|
||||
Passenger fred = Passenger.from("Fred", "Bloggs", 22);
|
||||
Passenger ricki = Passenger.from("Ricki", "Bobbie", 36);
|
||||
Passenger jill = Passenger.from("Jill", "Smith", 50);
|
||||
Passenger siya = Passenger.from("Siya", "Kolisi", 85);
|
||||
Passenger eve = Passenger.from("Eve", "Jackson", 95);
|
||||
|
||||
List<Passenger> passengers = repository.findByOrderBySeatNumberAsc();
|
||||
|
||||
assertThat(passengers, contains(fred, ricki, jill, siya, eve));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenPassengers_whenFindAllWithSortBySeatNumberAsc_thenCorrectOrder() {
|
||||
Passenger fred = Passenger.from("Fred", "Bloggs", 22);
|
||||
Passenger ricki = Passenger.from("Ricki", "Bobbie", 36);
|
||||
Passenger jill = Passenger.from("Jill", "Smith", 50);
|
||||
Passenger siya = Passenger.from("Siya", "Kolisi", 85);
|
||||
Passenger eve = Passenger.from("Eve", "Jackson", 95);
|
||||
|
||||
List<Passenger> passengers = repository.findAll(Sort.by(Sort.Direction.ASC, "seatNumber"));
|
||||
|
||||
assertThat(passengers, contains(fred, ricki, jill, siya, eve));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package com.baeldung.debugging.consumer;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
|
||||
import reactor.core.publisher.Hooks;
|
||||
|
||||
@SpringBootApplication(exclude = MongoReactiveAutoConfiguration.class)
|
||||
@EnableScheduling
|
||||
public class ConsumerSSEApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Hooks.onOperatorDebug();
|
||||
SpringApplication app = new SpringApplication(ConsumerSSEApplication.class);
|
||||
app.setDefaultProperties(Collections.singletonMap("server.port", "8082"));
|
||||
app.run(args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
http.authorizeExchange()
|
||||
.anyExchange()
|
||||
.permitAll();
|
||||
return http.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
package com.baeldung.debugging.consumer.chronjobs;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import com.baeldung.debugging.consumer.model.Foo;
|
||||
import com.baeldung.debugging.consumer.model.FooDto;
|
||||
import com.baeldung.debugging.consumer.service.FooService;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
@Component
|
||||
public class ChronJobs {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ChronJobs.class);
|
||||
private WebClient client = WebClient.create("http://localhost:8081");
|
||||
|
||||
@Autowired
|
||||
private FooService service;
|
||||
|
||||
@Scheduled(fixedRate = 10000)
|
||||
public void consumeInfiniteFlux() {
|
||||
Flux<Foo> fluxFoo = client.get()
|
||||
.uri("/functional-reactive/periodic-foo")
|
||||
.accept(MediaType.TEXT_EVENT_STREAM)
|
||||
.retrieve()
|
||||
.bodyToFlux(FooDto.class)
|
||||
.delayElements(Duration.ofMillis(100))
|
||||
.map(dto -> {
|
||||
logger.debug("process 1 with dto id {} name{}", dto.getId(), dto.getName());
|
||||
return new Foo(dto);
|
||||
});
|
||||
Integer random = ThreadLocalRandom.current()
|
||||
.nextInt(0, 3);
|
||||
switch (random) {
|
||||
case 0:
|
||||
logger.info("process 1 with approach 1");
|
||||
service.processFoo(fluxFoo);
|
||||
break;
|
||||
case 1:
|
||||
logger.info("process 1 with approach 1 EH");
|
||||
service.processUsingApproachOneWithErrorHandling(fluxFoo);
|
||||
break;
|
||||
default:
|
||||
logger.info("process 1 with approach 2");
|
||||
service.processFooInAnotherScenario(fluxFoo);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 20000)
|
||||
public void consumeFiniteFlux2() {
|
||||
Flux<Foo> fluxFoo = client.get()
|
||||
.uri("/functional-reactive/periodic-foo-2")
|
||||
.accept(MediaType.TEXT_EVENT_STREAM)
|
||||
.retrieve()
|
||||
.bodyToFlux(FooDto.class)
|
||||
.delayElements(Duration.ofMillis(100))
|
||||
.map(dto -> {
|
||||
logger.debug("process 2 with dto id {} name{}", dto.getId(), dto.getName());
|
||||
return new Foo(dto);
|
||||
});
|
||||
Integer random = ThreadLocalRandom.current()
|
||||
.nextInt(0, 3);
|
||||
switch (random) {
|
||||
case 0:
|
||||
logger.info("process 2 with approach 1");
|
||||
service.processFoo(fluxFoo);
|
||||
break;
|
||||
case 1:
|
||||
logger.info("process 2 with approach 1 EH");
|
||||
service.processUsingApproachOneWithErrorHandling(fluxFoo);
|
||||
break;
|
||||
default:
|
||||
logger.info("process 2 with approach 2");
|
||||
service.processFooInAnotherScenario(fluxFoo);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 20000)
|
||||
public void consumeFiniteFlux3() {
|
||||
Flux<Foo> fluxFoo = client.get()
|
||||
.uri("/functional-reactive/periodic-foo-2")
|
||||
.accept(MediaType.TEXT_EVENT_STREAM)
|
||||
.retrieve()
|
||||
.bodyToFlux(FooDto.class)
|
||||
.delayElements(Duration.ofMillis(100))
|
||||
.map(dto -> {
|
||||
logger.debug("process 3 with dto id {} name{}", dto.getId(), dto.getName());
|
||||
return new Foo(dto);
|
||||
});
|
||||
logger.info("process 3 with approach 3");
|
||||
service.processUsingApproachThree(fluxFoo);
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 20000)
|
||||
public void consumeFiniteFluxWithCheckpoint4() {
|
||||
Flux<Foo> fluxFoo = client.get()
|
||||
.uri("/functional-reactive/periodic-foo-2")
|
||||
.accept(MediaType.TEXT_EVENT_STREAM)
|
||||
.retrieve()
|
||||
.bodyToFlux(FooDto.class)
|
||||
.delayElements(Duration.ofMillis(100))
|
||||
.map(dto -> {
|
||||
logger.debug("process 4 with dto id {} name{}", dto.getId(), dto.getName());
|
||||
return new Foo(dto);
|
||||
});
|
||||
logger.info("process 4 with approach 4");
|
||||
service.processUsingApproachFourWithCheckpoint(fluxFoo);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package com.baeldung.debugging.consumer.controllers;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import reactor.core.publisher.Hooks;
|
||||
|
||||
@RestController
|
||||
public class ReactiveConfigsToggleRestController {
|
||||
|
||||
@GetMapping("/debug-hook-on")
|
||||
public String setReactiveDebugOn() {
|
||||
Hooks.onOperatorDebug();
|
||||
return "DEBUG HOOK ON";
|
||||
}
|
||||
|
||||
@GetMapping("/debug-hook-off")
|
||||
public String setReactiveDebugOff() {
|
||||
Hooks.resetOnOperatorDebug();
|
||||
return "DEBUG HOOK OFF";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.baeldung.debugging.consumer.model;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class Foo {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
private String formattedName;
|
||||
private Integer quantity;
|
||||
|
||||
public Foo(FooDto dto) {
|
||||
this.id = (ThreadLocalRandom.current()
|
||||
.nextInt(0, 100) == 0) ? null : dto.getId();
|
||||
this.formattedName = dto.getName();
|
||||
this.quantity = ThreadLocalRandom.current()
|
||||
.nextInt(0, 10);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package com.baeldung.debugging.consumer.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class FooDto {
|
||||
|
||||
private Integer id;
|
||||
private String name;
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package com.baeldung.debugging.consumer.service;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import com.baeldung.debugging.consumer.model.Foo;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
public class FooNameHelper {
|
||||
|
||||
public static Flux<Foo> concatAndSubstringFooName(Flux<Foo> flux) {
|
||||
flux = concatFooName(flux);
|
||||
flux = substringFooName(flux);
|
||||
return flux;
|
||||
}
|
||||
|
||||
public static Flux<Foo> concatFooName(Flux<Foo> flux) {
|
||||
flux = flux.map(foo -> {
|
||||
String processedName = null;
|
||||
Integer random = ThreadLocalRandom.current()
|
||||
.nextInt(0, 80);
|
||||
processedName = (random != 0) ? foo.getFormattedName() : foo.getFormattedName() + "-bael";
|
||||
foo.setFormattedName(processedName);
|
||||
return foo;
|
||||
});
|
||||
return flux;
|
||||
}
|
||||
|
||||
public static Flux<Foo> substringFooName(Flux<Foo> flux) {
|
||||
return flux.map(foo -> {
|
||||
String processedName;
|
||||
Integer random = ThreadLocalRandom.current()
|
||||
.nextInt(0, 100);
|
||||
|
||||
processedName = (random == 0) ? foo.getFormattedName()
|
||||
.substring(10, 15)
|
||||
: foo.getFormattedName()
|
||||
.substring(0, 5);
|
||||
|
||||
foo.setFormattedName(processedName);
|
||||
return foo;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package com.baeldung.debugging.consumer.service;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import com.baeldung.debugging.consumer.model.Foo;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
public class FooQuantityHelper {
|
||||
|
||||
public static Flux<Foo> processFooReducingQuantity(Flux<Foo> flux) {
|
||||
flux = flux.map(foo -> {
|
||||
Integer result;
|
||||
Integer random = ThreadLocalRandom.current()
|
||||
.nextInt(0, 90);
|
||||
result = (random == 0) ? result = 0 : foo.getQuantity() + 2;
|
||||
foo.setQuantity(result);
|
||||
return foo;
|
||||
});
|
||||
return divideFooQuantity(flux);
|
||||
}
|
||||
|
||||
public static Flux<Foo> divideFooQuantity(Flux<Foo> flux) {
|
||||
return flux.map(foo -> {
|
||||
Integer result = Math.round(5 / foo.getQuantity());
|
||||
foo.setQuantity(result);
|
||||
return foo;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.baeldung.debugging.consumer.service;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.baeldung.debugging.consumer.model.Foo;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
public class FooReporter {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(FooReporter.class);
|
||||
|
||||
public static Flux<Foo> reportResult(Flux<Foo> input, String approach) {
|
||||
return input.map(foo -> {
|
||||
if (foo.getId() == null)
|
||||
throw new IllegalArgumentException("Null id is not valid!");
|
||||
logger.info("Reporting for approach {}: Foo with id '{}' name '{}' and quantity '{}'", approach, foo.getId(), foo.getFormattedName(), foo.getQuantity());
|
||||
return foo;
|
||||
});
|
||||
}
|
||||
|
||||
public static Flux<Foo> reportResult(Flux<Foo> input) {
|
||||
return reportResult(input, "default");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
package com.baeldung.debugging.consumer.service;
|
||||
|
||||
import static com.baeldung.debugging.consumer.service.FooNameHelper.concatAndSubstringFooName;
|
||||
import static com.baeldung.debugging.consumer.service.FooNameHelper.substringFooName;
|
||||
import static com.baeldung.debugging.consumer.service.FooQuantityHelper.divideFooQuantity;
|
||||
import static com.baeldung.debugging.consumer.service.FooQuantityHelper.processFooReducingQuantity;
|
||||
import static com.baeldung.debugging.consumer.service.FooReporter.reportResult;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.baeldung.debugging.consumer.model.Foo;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
@Component
|
||||
public class FooService {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(FooService.class);
|
||||
|
||||
public void processFoo(Flux<Foo> flux) {
|
||||
flux = FooNameHelper.concatFooName(flux);
|
||||
flux = FooNameHelper.substringFooName(flux);
|
||||
flux = flux.log();
|
||||
flux = FooReporter.reportResult(flux);
|
||||
flux = flux.doOnError(error -> {
|
||||
logger.error("The following error happened on processFoo method!", error);
|
||||
});
|
||||
flux.subscribe();
|
||||
}
|
||||
|
||||
public void processFooInAnotherScenario(Flux<Foo> flux) {
|
||||
flux = FooNameHelper.substringFooName(flux);
|
||||
flux = FooQuantityHelper.divideFooQuantity(flux);
|
||||
flux.subscribe();
|
||||
}
|
||||
|
||||
public void processUsingApproachOneWithErrorHandling(Flux<Foo> flux) {
|
||||
logger.info("starting approach one w error handling!");
|
||||
flux = concatAndSubstringFooName(flux);
|
||||
flux = concatAndSubstringFooName(flux);
|
||||
flux = substringFooName(flux);
|
||||
flux = processFooReducingQuantity(flux);
|
||||
flux = processFooReducingQuantity(flux);
|
||||
flux = processFooReducingQuantity(flux);
|
||||
flux = reportResult(flux, "ONE w/ EH");
|
||||
flux = flux.doOnError(error -> {
|
||||
logger.error("Approach 1 with Error Handling failed!", error);
|
||||
});
|
||||
flux.subscribe();
|
||||
}
|
||||
|
||||
public void processUsingApproachThree(Flux<Foo> flux) {
|
||||
logger.info("starting approach three!");
|
||||
flux = concatAndSubstringFooName(flux);
|
||||
flux = reportResult(flux, "THREE");
|
||||
flux = flux.doOnError(error -> {
|
||||
logger.error("Approach 3 failed!", error);
|
||||
});
|
||||
flux.subscribe();
|
||||
}
|
||||
|
||||
public void processUsingApproachFourWithCheckpoint(Flux<Foo> flux) {
|
||||
logger.info("starting approach four!");
|
||||
flux = concatAndSubstringFooName(flux);
|
||||
flux = flux.checkpoint("CHECKPOINT 1");
|
||||
flux = concatAndSubstringFooName(flux);
|
||||
flux = divideFooQuantity(flux);
|
||||
flux = flux.checkpoint("CHECKPOINT 2", true);
|
||||
flux = reportResult(flux, "FOUR");
|
||||
flux = concatAndSubstringFooName(flux).doOnError(error -> {
|
||||
logger.error("Approach 4 failed!", error);
|
||||
});
|
||||
flux.subscribe();
|
||||
}
|
||||
|
||||
public void processUsingApproachFourWithInitialCheckpoint(Flux<Foo> flux) {
|
||||
logger.info("starting approach four!");
|
||||
flux = concatAndSubstringFooName(flux);
|
||||
flux = flux.checkpoint("CHECKPOINT 1", true);
|
||||
flux = concatAndSubstringFooName(flux);
|
||||
flux = divideFooQuantity(flux);
|
||||
flux = reportResult(flux, "FOUR");
|
||||
flux = flux.doOnError(error -> {
|
||||
logger.error("Approach 4-2 failed!", error);
|
||||
});
|
||||
flux.subscribe();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package com.baeldung.debugging.server;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||
|
||||
@EnableWebFlux
|
||||
@SpringBootApplication
|
||||
public class ServerSSEApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication app = new SpringApplication(ServerSSEApplication.class);
|
||||
app.setDefaultProperties(Collections.singletonMap("server.port", "8081"));
|
||||
app.run(args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
http.authorizeExchange()
|
||||
.anyExchange()
|
||||
.permitAll();
|
||||
return http.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package com.baeldung.debugging.server.handlers;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
import com.baeldung.debugging.server.model.Foo;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Component
|
||||
public class ServerHandler {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ServerHandler.class);
|
||||
|
||||
public Mono<ServerResponse> useHandler(final ServerRequest request) {
|
||||
// there are chances that something goes wrong here...
|
||||
return ServerResponse.ok()
|
||||
.contentType(MediaType.TEXT_EVENT_STREAM)
|
||||
.body(Flux.interval(Duration.ofSeconds(1))
|
||||
.map(sequence -> {
|
||||
logger.info("retrieving Foo. Sequence: {}", sequence);
|
||||
if (ThreadLocalRandom.current()
|
||||
.nextInt(0, 50) == 1) {
|
||||
throw new RuntimeException("There was an error retrieving the Foo!");
|
||||
}
|
||||
return new Foo(sequence, "name" + sequence);
|
||||
|
||||
}), Foo.class);
|
||||
}
|
||||
|
||||
public Mono<ServerResponse> useHandlerFinite(final ServerRequest request) {
|
||||
return ServerResponse.ok()
|
||||
.contentType(MediaType.TEXT_EVENT_STREAM)
|
||||
.body(Flux.range(0, 50)
|
||||
.map(sequence -> {
|
||||
return new Foo(new Long(sequence), "theFooNameNumber" + sequence);
|
||||
}), Foo.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package com.baeldung.debugging.server.model;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class Foo {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
private String name;
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package com.baeldung.debugging.server.routers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.function.server.RequestPredicates;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
import com.baeldung.debugging.server.handlers.ServerHandler;
|
||||
|
||||
@Configuration
|
||||
public class ServerRouter {
|
||||
|
||||
@Bean
|
||||
public RouterFunction<ServerResponse> responseRoute(@Autowired ServerHandler handler) {
|
||||
return RouterFunctions.route(RequestPredicates.GET("/functional-reactive/periodic-foo"), handler::useHandler)
|
||||
.andRoute(RequestPredicates.GET("/functional-reactive/periodic-foo-2"), handler::useHandlerFinite);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,2 +1 @@
|
|||
logging.level.root=INFO
|
||||
|
||||
logging.level.root=INFO
|
|
@ -0,0 +1,65 @@
|
|||
package com.baeldung.debugging.consumer;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.baeldung.debugging.consumer.model.Foo;
|
||||
import com.baeldung.debugging.consumer.service.FooService;
|
||||
import com.baeldung.debugging.consumer.utils.ListAppender;
|
||||
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.classic.spi.IThrowableProxy;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Hooks;
|
||||
|
||||
public class ConsumerFooServiceIntegrationTest {
|
||||
|
||||
FooService service = new FooService();
|
||||
|
||||
@BeforeEach
|
||||
public void clearLogList() {
|
||||
Hooks.onOperatorDebug();
|
||||
ListAppender.clearEventList();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenFooWithNullId_whenProcessFoo_thenLogsWithDebugTrace() {
|
||||
Foo one = new Foo(1, "nameverylong", 8);
|
||||
Foo two = new Foo(null, "nameverylong", 4);
|
||||
Flux<Foo> flux = Flux.just(one, two);
|
||||
|
||||
service.processFoo(flux);
|
||||
|
||||
Collection<String> allLoggedEntries = ListAppender.getEvents()
|
||||
.stream()
|
||||
.map(ILoggingEvent::getFormattedMessage)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Collection<String> allSuppressedEntries = ListAppender.getEvents()
|
||||
.stream()
|
||||
.map(ILoggingEvent::getThrowableProxy)
|
||||
.flatMap(t -> {
|
||||
return Optional.ofNullable(t)
|
||||
.map(IThrowableProxy::getSuppressed)
|
||||
.map(Arrays::stream)
|
||||
.orElse(Stream.empty());
|
||||
})
|
||||
.map(IThrowableProxy::getMessage)
|
||||
.collect(Collectors.toList());
|
||||
assertThat(allLoggedEntries).anyMatch(entry -> entry.contains("The following error happened on processFoo method!"))
|
||||
.anyMatch(entry -> entry.contains("| onSubscribe"))
|
||||
.anyMatch(entry -> entry.contains("| cancel()"));
|
||||
|
||||
assertThat(allSuppressedEntries).anyMatch(entry -> entry.contains("Assembly trace from producer"))
|
||||
.anyMatch(entry -> entry.contains("Error has been observed by the following operator(s)"));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package com.baeldung.debugging.consumer;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec;
|
||||
|
||||
import com.baeldung.debugging.consumer.service.FooService;
|
||||
|
||||
public class ConsumerFooServiceLiveTest {
|
||||
|
||||
FooService service = new FooService();
|
||||
|
||||
private static final String BASE_URL = "http://localhost:8082";
|
||||
private static final String DEBUG_HOOK_ON = BASE_URL + "/debug-hook-on";
|
||||
private static final String DEBUG_HOOK_OFF = BASE_URL + "/debug-hook-off";
|
||||
|
||||
private static WebTestClient client;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() {
|
||||
client = WebTestClient.bindToServer()
|
||||
.baseUrl(BASE_URL)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestingDebugHookOn_thenObtainExpectedMessage() {
|
||||
ResponseSpec response = client.get()
|
||||
.uri(DEBUG_HOOK_ON)
|
||||
.exchange();
|
||||
response.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(String.class)
|
||||
.isEqualTo("DEBUG HOOK ON");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestingDebugHookOff_thenObtainExpectedMessage() {
|
||||
ResponseSpec response = client.get()
|
||||
.uri(DEBUG_HOOK_OFF)
|
||||
.exchange();
|
||||
response.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(String.class)
|
||||
.isEqualTo("DEBUG HOOK OFF");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package com.baeldung.debugging.consumer.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.core.AppenderBase;
|
||||
|
||||
public class ListAppender extends AppenderBase<ILoggingEvent> {
|
||||
|
||||
static private List<ILoggingEvent> events = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
protected void append(ILoggingEvent eventObject) {
|
||||
events.add(eventObject);
|
||||
}
|
||||
|
||||
public static List<ILoggingEvent> getEvents() {
|
||||
return events;
|
||||
}
|
||||
|
||||
public static void clearEventList() {
|
||||
events.clear();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<include
|
||||
resource="org/springframework/boot/logging/logback/base.xml" />
|
||||
<appender name="LISTAPPENDER"
|
||||
class="com.baeldung.debugging.consumer.utils.ListAppender">
|
||||
</appender>
|
||||
<logger
|
||||
name="com.baeldung.debugging.consumer.service.FooService">
|
||||
<appender-ref ref="LISTAPPENDER" />
|
||||
</logger>
|
||||
<root level="info">
|
||||
<appender-ref ref="CONSOLE" />
|
||||
<appender-ref ref="LISTAPPENDER" />
|
||||
</root>
|
||||
</configuration>
|
|
@ -1,3 +1 @@
|
|||
org.springframework.boot.diagnostics.FailureAnalyzer=com.baeldung.failureanalyzer.MyBeanNotOfRequiredTypeFailureAnalyzer
|
||||
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.baeldung.autoconfiguration.MySQLAutoconfiguration
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.baeldung.autoconfiguration.MySQLAutoconfiguration
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
package com.baeldung.mongodb;
|
||||
|
||||
import com.baeldung.mongodb.daos.UserRepository;
|
||||
import com.baeldung.mongodb.models.User;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package com.baeldung.mongodb.daos;
|
||||
|
||||
|
||||
import com.baeldung.mongodb.models.User;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
|
||||
|
||||
public interface UserRepository extends MongoRepository<User, Long> {
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package com.baeldung.mongodb.events;
|
||||
|
||||
|
||||
import com.baeldung.mongodb.models.User;
|
||||
import com.baeldung.mongodb.services.SequenceGeneratorService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
|
||||
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
@Component
|
||||
public class UserModelListener extends AbstractMongoEventListener<User> {
|
||||
|
||||
private SequenceGeneratorService sequenceGenerator;
|
||||
|
||||
@Autowired
|
||||
public UserModelListener(SequenceGeneratorService sequenceGenerator) {
|
||||
this.sequenceGenerator = sequenceGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeforeConvert(BeforeConvertEvent<User> event) {
|
||||
event.getSource().setId(sequenceGenerator.generateSequence(User.SEQUENCE_NAME));
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package com.baeldung.mongodb.models;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
|
||||
@Document(collection = "database_sequences")
|
||||
public class DatabaseSequence {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
private long seq;
|
||||
|
||||
public DatabaseSequence() {}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public long getSeq() {
|
||||
return seq;
|
||||
}
|
||||
|
||||
public void setSeq(long seq) {
|
||||
this.seq = seq;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package com.baeldung.mongodb.models;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
|
||||
@Document(collection = "users")
|
||||
public class User {
|
||||
|
||||
@Transient
|
||||
public static final String SEQUENCE_NAME = "users_sequence";
|
||||
|
||||
@Id
|
||||
private long id;
|
||||
|
||||
private String firstName;
|
||||
|
||||
private String lastName;
|
||||
|
||||
private String email;
|
||||
|
||||
public User() { }
|
||||
|
||||
public User(String firstName, String lastName, String email) {
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long 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 getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User{" +
|
||||
"id=" + id +
|
||||
", firstName='" + firstName + '\'' +
|
||||
", lastName='" + lastName + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package com.baeldung.mongodb.services;
|
||||
|
||||
import com.baeldung.mongodb.models.DatabaseSequence;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.springframework.data.mongodb.core.FindAndModifyOptions.options;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.where;
|
||||
import static org.springframework.data.mongodb.core.query.Query.query;
|
||||
|
||||
|
||||
@Service
|
||||
public class SequenceGeneratorService {
|
||||
|
||||
|
||||
private MongoOperations mongoOperations;
|
||||
|
||||
@Autowired
|
||||
public SequenceGeneratorService(MongoOperations mongoOperations) {
|
||||
this.mongoOperations = mongoOperations;
|
||||
}
|
||||
|
||||
public long generateSequence(String seqName) {
|
||||
|
||||
DatabaseSequence counter = mongoOperations.findAndModify(query(where("_id").is(seqName)),
|
||||
new Update().inc("seq",1), options().returnNew(true).upsert(true),
|
||||
DatabaseSequence.class);
|
||||
return !Objects.isNull(counter) ? counter.getSeq() : 1;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package com.baeldung.mongodb;
|
||||
|
||||
import com.baeldung.mongodb.daos.UserRepository;
|
||||
import com.baeldung.mongodb.models.User;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
public class MongoDbAutoGeneratedFieldIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Test
|
||||
public void contextLoads() {}
|
||||
|
||||
@Test
|
||||
public void givenUserObject_whenSave_thenCreateNewUser() {
|
||||
|
||||
User user = new User();
|
||||
user.setFirstName("John");
|
||||
user.setLastName("Doe");
|
||||
user.setEmail("john.doe@example.com");
|
||||
userRepository.save(user);
|
||||
|
||||
assertThat(userRepository.findAll().size()).isGreaterThan(0);
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue