Merge branch 'master' of https://github.com/eugenp/tutorials
This commit is contained in:
commit
f630aeed0b
|
@ -0,0 +1,56 @@
|
||||||
|
package com.baeldung.java14.helpfulnullpointerexceptions;
|
||||||
|
|
||||||
|
public class HelpfulNullPointerException {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Employee employee = null;
|
||||||
|
employee.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmployeeEmailAddress(Employee employee) {
|
||||||
|
String emailAddress = employee.getPersonalDetails().getEmailAddress().toLowerCase();
|
||||||
|
return emailAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Employee {
|
||||||
|
String name;
|
||||||
|
PersonalDetails personalDetails;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PersonalDetails getPersonalDetails() {
|
||||||
|
return personalDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPersonalDetails(PersonalDetails personalDetails) {
|
||||||
|
this.personalDetails = personalDetails;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class PersonalDetails {
|
||||||
|
String emailAddress;
|
||||||
|
String phone;
|
||||||
|
|
||||||
|
public String getEmailAddress() {
|
||||||
|
return emailAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmailAddress(String emailAddress) {
|
||||||
|
this.emailAddress = emailAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPhone() {
|
||||||
|
return phone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhone(String phone) {
|
||||||
|
this.phone = phone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.baeldung.java14.helpfulnullpointerexceptions;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static com.baeldung.java14.helpfulnullpointerexceptions.HelpfulNullPointerException.Employee;
|
||||||
|
import static com.baeldung.java14.helpfulnullpointerexceptions.HelpfulNullPointerException.PersonalDetails;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
public class HelpfulNullPointerExceptionUnitTest {
|
||||||
|
|
||||||
|
@Test (expected = NullPointerException.class)
|
||||||
|
public void givenAnEmptyPersonalDetails_whenEmailAddressIsAccessed_thenThrowNPE() {
|
||||||
|
var helpfulNPE = new HelpfulNullPointerException();
|
||||||
|
|
||||||
|
var employee = new Employee();
|
||||||
|
employee.setName("Eduard");
|
||||||
|
employee.setPersonalDetails(new PersonalDetails());
|
||||||
|
helpfulNPE.getEmployeeEmailAddress(employee);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenCompletePersonalDetails_whenEmailAddressIsAccessed_thenSuccess() {
|
||||||
|
var helpfulNPE = new HelpfulNullPointerException();
|
||||||
|
var emailAddress = "eduard@gmx.com";
|
||||||
|
|
||||||
|
var employee = new Employee();
|
||||||
|
employee.setName("Eduard");
|
||||||
|
|
||||||
|
var personalDetails = new PersonalDetails();
|
||||||
|
personalDetails.setEmailAddress(emailAddress.toUpperCase());
|
||||||
|
personalDetails.setPhone("1234");
|
||||||
|
employee.setPersonalDetails(personalDetails);
|
||||||
|
|
||||||
|
assertThat(helpfulNPE.getEmployeeEmailAddress(employee)).isEqualTo(emailAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.baeldung.concurrent.volatilekeyword;
|
||||||
|
|
||||||
|
public class TaskRunner {
|
||||||
|
|
||||||
|
private static int number;
|
||||||
|
private volatile static boolean ready;
|
||||||
|
|
||||||
|
private static class Reader extends Thread {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (!ready) {
|
||||||
|
Thread.yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new Reader().start();
|
||||||
|
number = 42;
|
||||||
|
ready = true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,17 @@
|
||||||
package com.baeldung.java8;
|
package com.baeldung.java8;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import com.baeldung.java8.entity.Human;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import com.baeldung.java8.entity.Human;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.common.primitives.Ints;
|
|
||||||
|
|
||||||
public class Java8SortUnitTest {
|
public class Java8SortUnitTest {
|
||||||
|
|
||||||
|
@ -164,4 +163,48 @@ public class Java8SortUnitTest {
|
||||||
Assert.assertThat(reverseSortedHumans.get(0), equalTo(new Human("Sarah", 10)));
|
Assert.assertThat(reverseSortedHumans.get(0), equalTo(new Human("Sarah", 10)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = NullPointerException.class)
|
||||||
|
public final void givenANullElement_whenSortingEntitiesByName_thenThrowsNPE() {
|
||||||
|
final List<Human> humans = Lists.newArrayList(null, new Human("Jack", 12));
|
||||||
|
|
||||||
|
humans.sort((h1, h2) -> h1.getName().compareTo(h2.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public final void givenANullElement_whenSortingEntitiesByNameManually_thenMovesTheNullToLast() {
|
||||||
|
final List<Human> humans = Lists.newArrayList(null, new Human("Jack", 12), null);
|
||||||
|
|
||||||
|
humans.sort((h1, h2) -> {
|
||||||
|
if (h1 == null) return h2 == null ? 0 : 1;
|
||||||
|
else if (h2 == null) return -1;
|
||||||
|
|
||||||
|
return h1.getName().compareTo(h2.getName());
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.assertNotNull(humans.get(0));
|
||||||
|
Assert.assertNull(humans.get(1));
|
||||||
|
Assert.assertNull(humans.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public final void givenANullElement_whenSortingEntitiesByName_thenMovesTheNullToLast() {
|
||||||
|
final List<Human> humans = Lists.newArrayList(null, new Human("Jack", 12), null);
|
||||||
|
|
||||||
|
humans.sort(Comparator.nullsLast(Comparator.comparing(Human::getName)));
|
||||||
|
|
||||||
|
Assert.assertNotNull(humans.get(0));
|
||||||
|
Assert.assertNull(humans.get(1));
|
||||||
|
Assert.assertNull(humans.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public final void givenANullElement_whenSortingEntitiesByName_thenMovesTheNullToStart() {
|
||||||
|
final List<Human> humans = Lists.newArrayList(null, new Human("Jack", 12), null);
|
||||||
|
|
||||||
|
humans.sort(Comparator.nullsFirst(Comparator.comparing(Human::getName)));
|
||||||
|
|
||||||
|
Assert.assertNull(humans.get(0));
|
||||||
|
Assert.assertNull(humans.get(1));
|
||||||
|
Assert.assertNotNull(humans.get(2));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
package com.baeldung.comparelong;
|
package com.baeldung.comparelong;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class CompareLongUnitTest {
|
public class CompareLongUnitTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -32,6 +36,33 @@ public class CompareLongUnitTest {
|
||||||
assertThat(l1.equals(l2)).isTrue();
|
assertThat(l1.equals(l2)).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenLongValuesLessThan128_whenUsingObjectsEquals_thenSuccess() {
|
||||||
|
|
||||||
|
Long l1 = 127L;
|
||||||
|
Long l2 = 127L;
|
||||||
|
|
||||||
|
assertThat(Objects.equals(l1, l2)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenLongValuesGreaterOrEqualsThan128_whenUsingObjectsEquals_thenSuccess() {
|
||||||
|
|
||||||
|
Long l1 = 128L;
|
||||||
|
Long l2 = 128L;
|
||||||
|
|
||||||
|
assertThat(Objects.equals(l1, l2)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenNullReference_whenUsingObjectsEquals_thenNoException() {
|
||||||
|
|
||||||
|
Long l1 = null;
|
||||||
|
Long l2 = 128L;
|
||||||
|
|
||||||
|
assertThatCode(() -> Objects.equals(l1, l2)).doesNotThrowAnyException();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenLongValuesGreaterOrEqualsThan128_whenUsingComparisonOperator_andLongValue_thenSuccess() {
|
public void givenLongValuesGreaterOrEqualsThan128_whenUsingComparisonOperator_andLongValue_thenSuccess() {
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
## Core Java Security
|
||||||
|
|
||||||
|
This module contains articles about core Java Security
|
||||||
|
|
||||||
### Relevant Articles:
|
### Relevant Articles:
|
||||||
|
|
||||||
- [Guide To The Java Authentication And Authorization Service (JAAS)](https://www.baeldung.com/java-authentication-authorization-service)
|
- [Guide To The Java Authentication And Authorization Service (JAAS)](https://www.baeldung.com/java-authentication-authorization-service)
|
||||||
|
- [MD5 Hashing in Java](http://www.baeldung.com/java-md5)
|
||||||
|
- [Hashing a Password in Java](https://www.baeldung.com/java-password-hashing)
|
||||||
|
- [SHA-256 and SHA3-256 Hashing in Java](https://www.baeldung.com/sha-256-hashing-java)
|
||||||
|
- More articles: [[<-- prev]](/core-java-modules/core-java-security)
|
||||||
|
|
|
@ -16,4 +16,45 @@
|
||||||
<relativePath>../../parent-java</relativePath>
|
<relativePath>../../parent-java</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
<version>${commons-codec.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcprov-jdk15on</artifactId>
|
||||||
|
<version>${bouncycastle.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- test scoped -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.assertj</groupId>
|
||||||
|
<artifactId>assertj-core</artifactId>
|
||||||
|
<version>${assertj-core.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.xml.bind</groupId>
|
||||||
|
<artifactId>jaxb-api</artifactId>
|
||||||
|
<version>2.3.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<!-- util -->
|
||||||
|
<bouncycastle.version>1.60</bouncycastle.version>
|
||||||
|
<commons-codec.version>1.11</commons-codec.version>
|
||||||
|
|
||||||
|
<!-- testing -->
|
||||||
|
<assertj-core.version>3.10.0</assertj-core.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.baeldung.checksums;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.zip.CRC32;
|
||||||
|
import java.util.zip.CheckedInputStream;
|
||||||
|
import java.util.zip.Checksum;
|
||||||
|
|
||||||
|
public class ChecksumUtils {
|
||||||
|
|
||||||
|
public static long getChecksumCRC32(byte[] bytes) {
|
||||||
|
Checksum crc32 = new CRC32();
|
||||||
|
crc32.update(bytes, 0, bytes.length);
|
||||||
|
return crc32.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getChecksumCRC32(InputStream stream, int bufferSize) throws IOException {
|
||||||
|
CheckedInputStream checkedInputStream = new CheckedInputStream(stream, new CRC32());
|
||||||
|
byte[] buffer = new byte[bufferSize];
|
||||||
|
while (checkedInputStream.read(buffer, 0, buffer.length) >= 0) {}
|
||||||
|
return checkedInputStream.getChecksum().getValue();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package com.baeldung.checksums;
|
||||||
|
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class ChecksumUtilsUnitTest {
|
||||||
|
|
||||||
|
byte[] arr;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
void setUp() {
|
||||||
|
arr = new byte[]{0,10,21,20,35,40,120,56,72,22};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenByteArray_whenChecksumCreated_checkCorrect() {
|
||||||
|
|
||||||
|
long checksum = ChecksumUtils.getChecksumCRC32(arr);
|
||||||
|
|
||||||
|
assertEquals(3915397664L, checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenTwoDifferentStrings_whenChecksumCreated_checkCollision() {
|
||||||
|
|
||||||
|
String plumless = "plumless";
|
||||||
|
String buckeroo = "buckeroo";
|
||||||
|
|
||||||
|
long plumlessChecksum = ChecksumUtils.getChecksumCRC32(plumless.getBytes());
|
||||||
|
long buckerooChecksum = ChecksumUtils.getChecksumCRC32(buckeroo.getBytes());
|
||||||
|
|
||||||
|
assertEquals(plumlessChecksum, buckerooChecksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenInputString_whenChecksumCreated_checkCorrect() throws IOException {
|
||||||
|
|
||||||
|
InputStream inputStream = new ByteArrayInputStream(arr);
|
||||||
|
long checksum = ChecksumUtils.getChecksumCRC32(inputStream, 10);
|
||||||
|
|
||||||
|
assertEquals(3915397664L, checksum);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,17 +3,16 @@
|
||||||
This module contains articles about core Java Security
|
This module contains articles about core Java Security
|
||||||
|
|
||||||
### Relevant Articles:
|
### Relevant Articles:
|
||||||
- [MD5 Hashing in Java](http://www.baeldung.com/java-md5)
|
|
||||||
- [Guide to the Cipher Class](http://www.baeldung.com/java-cipher-class)
|
- [Guide to the Cipher Class](http://www.baeldung.com/java-cipher-class)
|
||||||
- [Introduction to SSL in Java](http://www.baeldung.com/java-ssl)
|
- [Introduction to SSL in Java](http://www.baeldung.com/java-ssl)
|
||||||
- [Java KeyStore API](http://www.baeldung.com/java-keystore)
|
- [Java KeyStore API](http://www.baeldung.com/java-keystore)
|
||||||
- [Encrypting and Decrypting Files in Java](http://www.baeldung.com/java-cipher-input-output-stream)
|
- [Encrypting and Decrypting Files in Java](http://www.baeldung.com/java-cipher-input-output-stream)
|
||||||
- [Hashing a Password in Java](https://www.baeldung.com/java-password-hashing)
|
|
||||||
- [SSL Handshake Failures](https://www.baeldung.com/java-ssl-handshake-failures)
|
- [SSL Handshake Failures](https://www.baeldung.com/java-ssl-handshake-failures)
|
||||||
- [SHA-256 and SHA3-256 Hashing in Java](https://www.baeldung.com/sha-256-hashing-java)
|
|
||||||
- [Enabling TLS v1.2 in Java 7](https://www.baeldung.com/java-7-tls-v12)
|
- [Enabling TLS v1.2 in Java 7](https://www.baeldung.com/java-7-tls-v12)
|
||||||
- [The Java SecureRandom Class](https://www.baeldung.com/java-secure-random)
|
- [The Java SecureRandom Class](https://www.baeldung.com/java-secure-random)
|
||||||
- [An Introduction to Java SASL](https://www.baeldung.com/java-sasl)
|
- [An Introduction to Java SASL](https://www.baeldung.com/java-sasl)
|
||||||
- [A Guide to Java GSS API](https://www.baeldung.com/java-gss)
|
- [A Guide to Java GSS API](https://www.baeldung.com/java-gss)
|
||||||
- [Intro to the Java SecurityManager](https://www.baeldung.com/java-security-manager)
|
- [Intro to the Java SecurityManager](https://www.baeldung.com/java-security-manager)
|
||||||
|
- More articles: [[next -->]](/core-java-modules/core-java-security-2)
|
||||||
|
|
||||||
|
|
|
@ -24,24 +24,9 @@
|
||||||
<version>${assertj-core.version}</version>
|
<version>${assertj-core.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>commons-codec</groupId>
|
|
||||||
<artifactId>commons-codec</artifactId>
|
|
||||||
<version>${commons-codec.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.bouncycastle</groupId>
|
|
||||||
<artifactId>bcprov-jdk15on</artifactId>
|
|
||||||
<version>${bouncycastle.version}</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<!-- util -->
|
|
||||||
<bouncycastle.version>1.60</bouncycastle.version>
|
|
||||||
<commons-codec.version>1.11</commons-codec.version>
|
|
||||||
|
|
||||||
<!-- testing -->
|
<!-- testing -->
|
||||||
<assertj-core.version>3.10.0</assertj-core.version>
|
<assertj-core.version>3.10.0</assertj-core.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.baeldung.streams.bigdecimals;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class AddNumbersUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenIntStream_whenSum_thenResultIsCorrect() {
|
||||||
|
IntStream intNumbers = IntStream.range(0, 3);
|
||||||
|
assertEquals(3, intNumbers.sum());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenCollectionOfDouble_whenUsingMapToDoubleToSum_thenResultIsCorrect() {
|
||||||
|
List<Double> doubleNumbers = Arrays.asList(23.48, 52.26, 13.5);
|
||||||
|
double result = doubleNumbers.stream()
|
||||||
|
.mapToDouble(Double::doubleValue)
|
||||||
|
.sum();
|
||||||
|
assertEquals(89.24, result, .1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void givenStreamOfIntegers_whenUsingReduceToSum_thenResultIsCorrect() {
|
||||||
|
Stream<Integer> intNumbers = Stream.of(0, 1, 2);
|
||||||
|
int result = intNumbers.reduce(0, Integer::sum);
|
||||||
|
assertEquals(106, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void givenStreamOfBigDecimals_whenUsingReduceToSum_thenResultIsCorrect() {
|
||||||
|
Stream<BigDecimal> bigDecimalNumber = Stream.of(BigDecimal.ZERO, BigDecimal.ONE, BigDecimal.TEN);
|
||||||
|
BigDecimal result = bigDecimalNumber.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
assertEquals(11, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.baeldung.guava.mapmaker;
|
||||||
|
|
||||||
|
public class Profile {
|
||||||
|
private long id;
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
public Profile(long id, String type) {
|
||||||
|
this.id = id;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.baeldung.guava.mapmaker;
|
||||||
|
|
||||||
|
public class Session {
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
public Session(long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.baeldung.guava.mapmaker;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
private long id;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public User(long id, String name) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package com.baeldung.guava.mapmaker;
|
||||||
|
|
||||||
|
import com.google.common.collect.MapMaker;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
public class GuavaMapMakerUnitTest {
|
||||||
|
@Test
|
||||||
|
public void whenCreateCaches_thenCreated() {
|
||||||
|
ConcurrentMap<User, Session> sessionCache = new MapMaker().makeMap();
|
||||||
|
assertNotNull(sessionCache);
|
||||||
|
|
||||||
|
ConcurrentMap<User, Profile> profileCache = new MapMaker().makeMap();
|
||||||
|
assertNotNull(profileCache);
|
||||||
|
|
||||||
|
User userA = new User(1, "UserA");
|
||||||
|
|
||||||
|
sessionCache.put(userA, new Session(100));
|
||||||
|
Assert.assertThat(sessionCache.size(), equalTo(1));
|
||||||
|
|
||||||
|
profileCache.put(userA, new Profile(1000, "Personal"));
|
||||||
|
Assert.assertThat(profileCache.size(), equalTo(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenCreateCacheWithInitialCapacity_thenCreated() {
|
||||||
|
ConcurrentMap<User, Profile> profileCache = new MapMaker().initialCapacity(100).makeMap();
|
||||||
|
assertNotNull(profileCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenCreateCacheWithConcurrencyLevel_thenCreated() {
|
||||||
|
ConcurrentMap<User, Session> sessionCache = new MapMaker().concurrencyLevel(10).makeMap();
|
||||||
|
assertNotNull(sessionCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenCreateCacheWithWeakKeys_thenCreated() {
|
||||||
|
ConcurrentMap<User, Session> sessionCache = new MapMaker().weakKeys().makeMap();
|
||||||
|
assertNotNull(sessionCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenCreateCacheWithWeakValues_thenCreated() {
|
||||||
|
ConcurrentMap<User, Profile> profileCache = new MapMaker().weakValues().makeMap();
|
||||||
|
assertNotNull(profileCache);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,5 +13,4 @@ This module contains articles about Map data structures in Java.
|
||||||
- [Sort a HashMap in Java](https://www.baeldung.com/java-hashmap-sort)
|
- [Sort a HashMap in Java](https://www.baeldung.com/java-hashmap-sort)
|
||||||
- [Finding the Highest Value in a Java Map](https://www.baeldung.com/java-find-map-max)
|
- [Finding the Highest Value in a Java Map](https://www.baeldung.com/java-find-map-max)
|
||||||
- [Initialize a HashMap in Java](https://www.baeldung.com/java-initialize-hashmap)
|
- [Initialize a HashMap in Java](https://www.baeldung.com/java-initialize-hashmap)
|
||||||
- [Java TreeMap vs HashMap](https://www.baeldung.com/java-treemap-vs-hashmap)
|
- More articles: [[<-- prev]](/java-collections-maps) [[next -->]](/java-collections-maps-3)
|
||||||
- More articles: [[<-- prev>]](/../java-collections-maps)
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
## Java Collections Cookbooks and Examples
|
||||||
|
|
||||||
|
This module contains articles about Map data structures in Java.
|
||||||
|
|
||||||
|
### Relevant Articles:
|
||||||
|
- [Java TreeMap vs HashMap](https://www.baeldung.com/java-treemap-vs-hashmap)
|
||||||
|
- [Comparing Two HashMaps in Java](https://www.baeldung.com/java-compare-hashmaps)
|
||||||
|
- More articles: [[<-- prev]](/java-collections-maps-2)
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?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>
|
||||||
|
<artifactId>java-collections-maps-3</artifactId>
|
||||||
|
<version>0.1.0-SNAPSHOT</version>
|
||||||
|
<name>java-collections-maps-3</name>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>parent-java</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<relativePath>../parent-java</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
</project>
|
|
@ -10,8 +10,7 @@ This module contains articles about Map data structures in Java.
|
||||||
- [How to Store Duplicate Keys in a Map in Java?](https://www.baeldung.com/java-map-duplicate-keys)
|
- [How to Store Duplicate Keys in a Map in Java?](https://www.baeldung.com/java-map-duplicate-keys)
|
||||||
- [Get the Key for a Value from a Java Map](https://www.baeldung.com/java-map-key-from-value)
|
- [Get the Key for a Value from a Java Map](https://www.baeldung.com/java-map-key-from-value)
|
||||||
- [How to Check If a Key Exists in a Map](https://www.baeldung.com/java-map-key-exists)
|
- [How to Check If a Key Exists in a Map](https://www.baeldung.com/java-map-key-exists)
|
||||||
- [Comparing Two HashMaps in Java](https://www.baeldung.com/java-compare-hashmaps)
|
|
||||||
- [Immutable Map Implementations in Java](https://www.baeldung.com/java-immutable-maps)
|
- [Immutable Map Implementations in Java](https://www.baeldung.com/java-immutable-maps)
|
||||||
- [Guide to Apache Commons MultiValuedMap](https://www.baeldung.com/apache-commons-multi-valued-map)
|
- [Guide to Apache Commons MultiValuedMap](https://www.baeldung.com/apache-commons-multi-valued-map)
|
||||||
- [The Java HashMap Under the Hood](https://www.baeldung.com/java-hashmap-advanced)
|
- [The Java HashMap Under the Hood](https://www.baeldung.com/java-hashmap-advanced)
|
||||||
- More articles: [[next -->]](/../java-collections-maps-2)
|
- More articles: [[next -->]](/java-collections-maps-2)
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.baeldung.abstractnumber;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class AbstractNumberUnitTest {
|
||||||
|
|
||||||
|
private final static double DOUBLE_VALUE = 9999.999;
|
||||||
|
private final static float FLOAT_VALUE = 101.99F;
|
||||||
|
private final static long LONG_VALUE = 1000L;
|
||||||
|
private final static int INTEGER_VALUE = 100;
|
||||||
|
private final static short SHORT_VALUE = 127;
|
||||||
|
private final static byte BYTE_VALUE = 120;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenDoubleValue_whenShortValueUsed_thenShortValueReturned() {
|
||||||
|
Double doubleValue = Double.valueOf(DOUBLE_VALUE);
|
||||||
|
assertEquals(9999, doubleValue.shortValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenFloatValue_whenByteValueUsed_thenByteValueReturned() {
|
||||||
|
Float floatValue = Float.valueOf(FLOAT_VALUE);
|
||||||
|
assertEquals(101, floatValue.byteValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenLongValue_whenInitValueUsed_thenInitValueReturned() {
|
||||||
|
Long longValue = Long.valueOf(LONG_VALUE);
|
||||||
|
assertEquals(1000, longValue.intValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenIntegerValue_whenLongValueUsed_thenLongValueReturned() {
|
||||||
|
Integer integerValue = Integer.valueOf(INTEGER_VALUE);
|
||||||
|
assertEquals(100, integerValue.longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenShortValue_whenFloatValueUsed_thenFloatValueReturned() {
|
||||||
|
Short shortValue = Short.valueOf(SHORT_VALUE);
|
||||||
|
assertEquals(127.0F, shortValue.floatValue(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenByteValue_whenDoubleValueUsed_thenDoubleValueReturned() {
|
||||||
|
Byte byteValue = Byte.valueOf(BYTE_VALUE);
|
||||||
|
assertEquals(120.0, byteValue.doubleValue(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
<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>coroutines-with-quasar</artifactId>
|
||||||
|
<name>coroutines-with-quasar</name>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>libraries-concurrency</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>co.paralleluniverse</groupId>
|
||||||
|
<artifactId>quasar-core</artifactId>
|
||||||
|
<version>0.8.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>co.paralleluniverse</groupId>
|
||||||
|
<artifactId>quasar-actors</artifactId>
|
||||||
|
<version>0.8.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>co.paralleluniverse</groupId>
|
||||||
|
<artifactId>quasar-reactive-streams</artifactId>
|
||||||
|
<version>0.8.0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<version>3.1.2</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>getClasspathFilenames</id>
|
||||||
|
<goals>
|
||||||
|
<goal>properties</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
|
<version>1.6.0</version>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>com.baeldung.quasar.App</mainClass>
|
||||||
|
<workingDirectory>target/classes</workingDirectory>
|
||||||
|
<executable>java</executable>
|
||||||
|
<arguments>
|
||||||
|
<!-- Turn off before production -->
|
||||||
|
<argument>-Dco.paralleluniverse.fibers.verifyInstrumentation=true</argument>
|
||||||
|
|
||||||
|
<!-- Quasar Agent -->
|
||||||
|
<argument>-javaagent:${co.paralleluniverse:quasar-core:jar}</argument>
|
||||||
|
|
||||||
|
<!-- Classpath -->
|
||||||
|
<argument>-classpath</argument>
|
||||||
|
<classpath />
|
||||||
|
|
||||||
|
<!-- Main class -->
|
||||||
|
<argument>com.baeldung.quasar.App</argument>
|
||||||
|
</arguments>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.8.0</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>12</source>
|
||||||
|
<target>12</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.baeldung.quasar;
|
||||||
|
|
||||||
|
import co.paralleluniverse.fibers.Fiber;
|
||||||
|
|
||||||
|
public class App {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new Fiber<Void>(() -> {
|
||||||
|
System.out.println("Inside fiber coroutine...");
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?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>
|
||||||
|
<artifactId>libraries-concurrency</artifactId>
|
||||||
|
<name>libraries-concurrency</name>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<artifactId>parent-modules</artifactId>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<!-- <module>coroutines-with-quasar</module> --><!-- we haven't upgraded to Java 12 -->
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,6 @@
|
||||||
|
## Netty
|
||||||
|
|
||||||
|
This module contains articles about Netty.
|
||||||
|
|
||||||
|
### Relevant Articles:
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?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>
|
||||||
|
<artifactId>netty</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>netty</name>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>parent-modules</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-all</artifactId>
|
||||||
|
<version>${netty.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.conscrypt</groupId>
|
||||||
|
<artifactId>conscrypt-openjdk-uber</artifactId>
|
||||||
|
<version>2.4.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<netty.version>4.1.48.Final</netty.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,135 @@
|
||||||
|
package com.baeldung.netty.http2;
|
||||||
|
|
||||||
|
import static io.netty.handler.logging.LogLevel.INFO;
|
||||||
|
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
|
||||||
|
import com.baeldung.netty.http2.client.Http2ClientResponseHandler;
|
||||||
|
import com.baeldung.netty.http2.client.Http2SettingsHandler;
|
||||||
|
import com.baeldung.netty.http2.server.Http2ServerResponseHandler;
|
||||||
|
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelPipeline;
|
||||||
|
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
|
import io.netty.handler.codec.http.HttpHeaderValues;
|
||||||
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
|
import io.netty.handler.codec.http.HttpScheme;
|
||||||
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
|
import io.netty.handler.codec.http2.DefaultHttp2Connection;
|
||||||
|
import io.netty.handler.codec.http2.DelegatingDecompressorFrameListener;
|
||||||
|
import io.netty.handler.codec.http2.Http2Connection;
|
||||||
|
import io.netty.handler.codec.http2.Http2FrameCodecBuilder;
|
||||||
|
import io.netty.handler.codec.http2.Http2FrameLogger;
|
||||||
|
import io.netty.handler.codec.http2.Http2SecurityUtil;
|
||||||
|
import io.netty.handler.codec.http2.HttpConversionUtil;
|
||||||
|
import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandler;
|
||||||
|
import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder;
|
||||||
|
import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder;
|
||||||
|
import io.netty.handler.ssl.ApplicationProtocolConfig;
|
||||||
|
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
|
||||||
|
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
|
||||||
|
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
|
||||||
|
import io.netty.handler.ssl.ApplicationProtocolNames;
|
||||||
|
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
|
||||||
|
import io.netty.handler.ssl.SslContext;
|
||||||
|
import io.netty.handler.ssl.SslContextBuilder;
|
||||||
|
import io.netty.handler.ssl.SslProvider;
|
||||||
|
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
|
||||||
|
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||||
|
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||||
|
|
||||||
|
public class Http2Util {
|
||||||
|
public static SslContext createSSLContext(boolean isServer) throws SSLException, CertificateException {
|
||||||
|
|
||||||
|
SslContext sslCtx;
|
||||||
|
|
||||||
|
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||||
|
|
||||||
|
if (isServer) {
|
||||||
|
sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
||||||
|
.sslProvider(SslProvider.JDK)
|
||||||
|
.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
|
||||||
|
.applicationProtocolConfig(new ApplicationProtocolConfig(Protocol.ALPN,
|
||||||
|
SelectorFailureBehavior.NO_ADVERTISE,
|
||||||
|
SelectedListenerFailureBehavior.ACCEPT, ApplicationProtocolNames.HTTP_2, ApplicationProtocolNames.HTTP_1_1))
|
||||||
|
.build();
|
||||||
|
} else {
|
||||||
|
sslCtx = SslContextBuilder.forClient()
|
||||||
|
.sslProvider(SslProvider.JDK)
|
||||||
|
.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
|
||||||
|
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||||
|
.applicationProtocolConfig(new ApplicationProtocolConfig(Protocol.ALPN,
|
||||||
|
SelectorFailureBehavior.NO_ADVERTISE,
|
||||||
|
SelectedListenerFailureBehavior.ACCEPT, ApplicationProtocolNames.HTTP_2))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
return sslCtx;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ApplicationProtocolNegotiationHandler getServerAPNHandler() {
|
||||||
|
ApplicationProtocolNegotiationHandler serverAPNHandler = new ApplicationProtocolNegotiationHandler(ApplicationProtocolNames.HTTP_2) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {
|
||||||
|
if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
|
||||||
|
ctx.pipeline()
|
||||||
|
.addLast(Http2FrameCodecBuilder.forServer()
|
||||||
|
.build(), new Http2ServerResponseHandler());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Protocol: " + protocol + " not supported");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return serverAPNHandler;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ApplicationProtocolNegotiationHandler getClientAPNHandler(int maxContentLength, Http2SettingsHandler settingsHandler, Http2ClientResponseHandler responseHandler) {
|
||||||
|
final Http2FrameLogger logger = new Http2FrameLogger(INFO, Http2Util.class);
|
||||||
|
final Http2Connection connection = new DefaultHttp2Connection(false);
|
||||||
|
|
||||||
|
HttpToHttp2ConnectionHandler connectionHandler = new HttpToHttp2ConnectionHandlerBuilder()
|
||||||
|
.frameListener(new DelegatingDecompressorFrameListener(connection, new InboundHttp2ToHttpAdapterBuilder(connection).maxContentLength(maxContentLength)
|
||||||
|
.propagateSettings(true)
|
||||||
|
.build()))
|
||||||
|
.frameLogger(logger)
|
||||||
|
.connection(connection)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ApplicationProtocolNegotiationHandler clientAPNHandler = new ApplicationProtocolNegotiationHandler(ApplicationProtocolNames.HTTP_2) {
|
||||||
|
@Override
|
||||||
|
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
|
||||||
|
if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
|
||||||
|
ChannelPipeline p = ctx.pipeline();
|
||||||
|
p.addLast(connectionHandler);
|
||||||
|
p.addLast(settingsHandler, responseHandler);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx.close();
|
||||||
|
throw new IllegalStateException("Protocol: " + protocol + " not supported");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return clientAPNHandler;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FullHttpRequest createGetRequest(String host, int port) {
|
||||||
|
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.valueOf("HTTP/2.0"), HttpMethod.GET, "/", Unpooled.EMPTY_BUFFER);
|
||||||
|
request.headers()
|
||||||
|
.add(HttpHeaderNames.HOST, new String(host + ":" + port));
|
||||||
|
request.headers()
|
||||||
|
.add(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), HttpScheme.HTTPS);
|
||||||
|
request.headers()
|
||||||
|
.add(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP);
|
||||||
|
request.headers()
|
||||||
|
.add(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.DEFLATE);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.baeldung.netty.http2.client;
|
||||||
|
|
||||||
|
import com.baeldung.netty.http2.Http2Util;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import io.netty.channel.ChannelPipeline;
|
||||||
|
import io.netty.channel.socket.SocketChannel;
|
||||||
|
import io.netty.handler.ssl.SslContext;
|
||||||
|
|
||||||
|
public class Http2ClientInitializer extends ChannelInitializer<SocketChannel> {
|
||||||
|
|
||||||
|
private final SslContext sslCtx;
|
||||||
|
private final int maxContentLength;
|
||||||
|
private Http2SettingsHandler settingsHandler;
|
||||||
|
private Http2ClientResponseHandler responseHandler;
|
||||||
|
private String host;
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
public Http2ClientInitializer(SslContext sslCtx, int maxContentLength, String host, int port) {
|
||||||
|
this.sslCtx = sslCtx;
|
||||||
|
this.maxContentLength = maxContentLength;
|
||||||
|
this.host = host;
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initChannel(SocketChannel ch) throws Exception {
|
||||||
|
|
||||||
|
settingsHandler = new Http2SettingsHandler(ch.newPromise());
|
||||||
|
responseHandler = new Http2ClientResponseHandler();
|
||||||
|
|
||||||
|
if (sslCtx != null) {
|
||||||
|
ChannelPipeline pipeline = ch.pipeline();
|
||||||
|
pipeline.addLast(sslCtx.newHandler(ch.alloc(), host, port));
|
||||||
|
pipeline.addLast(Http2Util.getClientAPNHandler(maxContentLength, settingsHandler, responseHandler));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Http2SettingsHandler getSettingsHandler() {
|
||||||
|
return settingsHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Http2ClientResponseHandler getResponseHandler() {
|
||||||
|
return responseHandler;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
package com.baeldung.netty.http2.client;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelFuture;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelPromise;
|
||||||
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
|
import io.netty.handler.codec.http2.HttpConversionUtil;
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
|
public class Http2ClientResponseHandler extends SimpleChannelInboundHandler<FullHttpResponse> {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(Http2ClientResponseHandler.class);
|
||||||
|
private final Map<Integer, MapValues> streamidMap;
|
||||||
|
|
||||||
|
public Http2ClientResponseHandler() {
|
||||||
|
streamidMap = new HashMap<Integer, MapValues>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapValues put(int streamId, ChannelFuture writeFuture, ChannelPromise promise) {
|
||||||
|
return streamidMap.put(streamId, new MapValues(writeFuture, promise));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String awaitResponses(long timeout, TimeUnit unit) {
|
||||||
|
|
||||||
|
Iterator<Entry<Integer, MapValues>> itr = streamidMap.entrySet()
|
||||||
|
.iterator();
|
||||||
|
|
||||||
|
String response = null;
|
||||||
|
|
||||||
|
while (itr.hasNext()) {
|
||||||
|
Entry<Integer, MapValues> entry = itr.next();
|
||||||
|
ChannelFuture writeFuture = entry.getValue()
|
||||||
|
.getWriteFuture();
|
||||||
|
|
||||||
|
if (!writeFuture.awaitUninterruptibly(timeout, unit)) {
|
||||||
|
throw new IllegalStateException("Timed out waiting to write for stream id " + entry.getKey());
|
||||||
|
}
|
||||||
|
if (!writeFuture.isSuccess()) {
|
||||||
|
throw new RuntimeException(writeFuture.cause());
|
||||||
|
}
|
||||||
|
ChannelPromise promise = entry.getValue()
|
||||||
|
.getPromise();
|
||||||
|
|
||||||
|
if (!promise.awaitUninterruptibly(timeout, unit)) {
|
||||||
|
throw new IllegalStateException("Timed out waiting for response on stream id " + entry.getKey());
|
||||||
|
}
|
||||||
|
if (!promise.isSuccess()) {
|
||||||
|
throw new RuntimeException(promise.cause());
|
||||||
|
}
|
||||||
|
logger.info("---Stream id: " + entry.getKey() + " received---");
|
||||||
|
response = entry.getValue().getResponse();
|
||||||
|
|
||||||
|
itr.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void channelRead0(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception {
|
||||||
|
Integer streamId = msg.headers()
|
||||||
|
.getInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text());
|
||||||
|
if (streamId == null) {
|
||||||
|
logger.error("HttpResponseHandler unexpected message received: " + msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MapValues value = streamidMap.get(streamId);
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
logger.error("Message received for unknown stream id " + streamId);
|
||||||
|
ctx.close();
|
||||||
|
} else {
|
||||||
|
ByteBuf content = msg.content();
|
||||||
|
if (content.isReadable()) {
|
||||||
|
int contentLength = content.readableBytes();
|
||||||
|
byte[] arr = new byte[contentLength];
|
||||||
|
content.readBytes(arr);
|
||||||
|
String response = new String(arr, 0, contentLength, CharsetUtil.UTF_8);
|
||||||
|
logger.info("Response from Server: "+ (response));
|
||||||
|
value.setResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
value.getPromise()
|
||||||
|
.setSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MapValues {
|
||||||
|
ChannelFuture writeFuture;
|
||||||
|
ChannelPromise promise;
|
||||||
|
String response;
|
||||||
|
|
||||||
|
public String getResponse() {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResponse(String response) {
|
||||||
|
this.response = response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapValues(ChannelFuture writeFuture2, ChannelPromise promise2) {
|
||||||
|
this.writeFuture = writeFuture2;
|
||||||
|
this.promise = promise2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelFuture getWriteFuture() {
|
||||||
|
return writeFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelPromise getPromise() {
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.baeldung.netty.http2.client;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelPromise;
|
||||||
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
|
import io.netty.handler.codec.http2.Http2Settings;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class Http2SettingsHandler extends SimpleChannelInboundHandler<Http2Settings> {
|
||||||
|
private final ChannelPromise promise;
|
||||||
|
|
||||||
|
public Http2SettingsHandler(ChannelPromise promise) {
|
||||||
|
this.promise = promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void awaitSettings(long timeout, TimeUnit unit) throws Exception {
|
||||||
|
if (!promise.awaitUninterruptibly(timeout, unit)) {
|
||||||
|
throw new IllegalStateException("Timed out waiting for settings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void channelRead0(ChannelHandlerContext ctx, Http2Settings msg) throws Exception {
|
||||||
|
promise.setSuccess();
|
||||||
|
|
||||||
|
ctx.pipeline()
|
||||||
|
.remove(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package com.baeldung.netty.http2.server;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.baeldung.netty.http2.Http2Util;
|
||||||
|
|
||||||
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import io.netty.channel.ChannelOption;
|
||||||
|
import io.netty.channel.EventLoopGroup;
|
||||||
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
|
import io.netty.channel.socket.SocketChannel;
|
||||||
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||||
|
import io.netty.handler.logging.LogLevel;
|
||||||
|
import io.netty.handler.logging.LoggingHandler;
|
||||||
|
import io.netty.handler.ssl.SslContext;
|
||||||
|
|
||||||
|
public final class Http2Server {
|
||||||
|
|
||||||
|
private static final int PORT = 8443;
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(Http2Server.class);
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
SslContext sslCtx = Http2Util.createSSLContext(true);
|
||||||
|
|
||||||
|
EventLoopGroup group = new NioEventLoopGroup();
|
||||||
|
try {
|
||||||
|
ServerBootstrap b = new ServerBootstrap();
|
||||||
|
b.option(ChannelOption.SO_BACKLOG, 1024);
|
||||||
|
b.group(group)
|
||||||
|
.channel(NioServerSocketChannel.class)
|
||||||
|
.handler(new LoggingHandler(LogLevel.INFO))
|
||||||
|
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||||
|
@Override
|
||||||
|
protected void initChannel(SocketChannel ch) throws Exception {
|
||||||
|
if (sslCtx != null) {
|
||||||
|
ch.pipeline()
|
||||||
|
.addLast(sslCtx.newHandler(ch.alloc()), Http2Util.getServerAPNHandler());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Channel ch = b.bind(PORT)
|
||||||
|
.sync()
|
||||||
|
.channel();
|
||||||
|
|
||||||
|
logger.info("HTTP/2 Server is listening on https://127.0.0.1:" + PORT + '/');
|
||||||
|
|
||||||
|
ch.closeFuture()
|
||||||
|
.sync();
|
||||||
|
} finally {
|
||||||
|
group.shutdownGracefully();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.baeldung.netty.http2.server;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.channel.ChannelDuplexHandler;
|
||||||
|
import io.netty.channel.ChannelHandler.Sharable;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
import io.netty.handler.codec.http2.DefaultHttp2DataFrame;
|
||||||
|
import io.netty.handler.codec.http2.DefaultHttp2Headers;
|
||||||
|
import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame;
|
||||||
|
import io.netty.handler.codec.http2.Http2Headers;
|
||||||
|
import io.netty.handler.codec.http2.Http2HeadersFrame;
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
|
@Sharable
|
||||||
|
public class Http2ServerResponseHandler extends ChannelDuplexHandler {
|
||||||
|
|
||||||
|
static final ByteBuf RESPONSE_BYTES = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Hello World", CharsetUtil.UTF_8));
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||||
|
super.exceptionCaught(ctx, cause);
|
||||||
|
cause.printStackTrace();
|
||||||
|
ctx.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
|
if (msg instanceof Http2HeadersFrame) {
|
||||||
|
Http2HeadersFrame msgHeader = (Http2HeadersFrame) msg;
|
||||||
|
if (msgHeader.isEndStream()) {
|
||||||
|
ByteBuf content = ctx.alloc()
|
||||||
|
.buffer();
|
||||||
|
content.writeBytes(RESPONSE_BYTES.duplicate());
|
||||||
|
|
||||||
|
Http2Headers headers = new DefaultHttp2Headers().status(HttpResponseStatus.OK.codeAsText());
|
||||||
|
ctx.write(new DefaultHttp2HeadersFrame(headers).stream(msgHeader.stream()));
|
||||||
|
ctx.write(new DefaultHttp2DataFrame(content, true).stream(msgHeader.stream()));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
super.channelRead(ctx, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
|
||||||
|
ctx.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration>
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||||
|
</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<root level="INFO">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</root>
|
||||||
|
</configuration>
|
|
@ -0,0 +1,91 @@
|
||||||
|
package com.baeldung.netty;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.baeldung.netty.http2.Http2Util;
|
||||||
|
import com.baeldung.netty.http2.client.Http2ClientInitializer;
|
||||||
|
import com.baeldung.netty.http2.client.Http2ClientResponseHandler;
|
||||||
|
import com.baeldung.netty.http2.client.Http2SettingsHandler;
|
||||||
|
|
||||||
|
import io.netty.bootstrap.Bootstrap;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelOption;
|
||||||
|
import io.netty.channel.EventLoopGroup;
|
||||||
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
|
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.ssl.SslContext;
|
||||||
|
|
||||||
|
//Ensure the server class - Http2Server.java is already started before running this test
|
||||||
|
public class Http2ClientLiveTest {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(Http2ClientLiveTest.class);
|
||||||
|
|
||||||
|
private static final String HOST = "127.0.0.1";
|
||||||
|
private static final int PORT = 8443;
|
||||||
|
private SslContext sslCtx;
|
||||||
|
private Channel channel;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() throws Exception {
|
||||||
|
sslCtx = Http2Util.createSSLContext(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenRequestSent_thenHelloWorldReceived() throws Exception {
|
||||||
|
|
||||||
|
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||||
|
Http2ClientInitializer initializer = new Http2ClientInitializer(sslCtx, Integer.MAX_VALUE, HOST, PORT);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Bootstrap b = new Bootstrap();
|
||||||
|
b.group(workerGroup);
|
||||||
|
b.channel(NioSocketChannel.class);
|
||||||
|
b.option(ChannelOption.SO_KEEPALIVE, true);
|
||||||
|
b.remoteAddress(HOST, PORT);
|
||||||
|
b.handler(initializer);
|
||||||
|
|
||||||
|
channel = b.connect()
|
||||||
|
.syncUninterruptibly()
|
||||||
|
.channel();
|
||||||
|
|
||||||
|
logger.info("Connected to [" + HOST + ':' + PORT + ']');
|
||||||
|
|
||||||
|
Http2SettingsHandler http2SettingsHandler = initializer.getSettingsHandler();
|
||||||
|
http2SettingsHandler.awaitSettings(60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
logger.info("Sending request(s)...");
|
||||||
|
|
||||||
|
FullHttpRequest request = Http2Util.createGetRequest(HOST, PORT);
|
||||||
|
|
||||||
|
Http2ClientResponseHandler responseHandler = initializer.getResponseHandler();
|
||||||
|
int streamId = 3;
|
||||||
|
|
||||||
|
responseHandler.put(streamId, channel.write(request), channel.newPromise());
|
||||||
|
channel.flush();
|
||||||
|
String response = responseHandler.awaitResponses(60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
assertEquals("Hello World", response);
|
||||||
|
|
||||||
|
logger.info("Finished HTTP/2 request(s)");
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
workerGroup.shutdownGracefully();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanup() {
|
||||||
|
channel.close()
|
||||||
|
.syncUninterruptibly();
|
||||||
|
}
|
||||||
|
}
|
|
@ -81,7 +81,7 @@
|
||||||
<rest-assured.version>3.3.0</rest-assured.version>
|
<rest-assured.version>3.3.0</rest-assured.version>
|
||||||
<!-- plugins -->
|
<!-- plugins -->
|
||||||
<thin.version>1.0.22.RELEASE</thin.version>
|
<thin.version>1.0.22.RELEASE</thin.version>
|
||||||
<spring-boot.version>2.2.2.RELEASE</spring-boot.version>
|
<spring-boot.version>2.2.6.RELEASE</spring-boot.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -31,8 +31,8 @@
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<spring.version>5.2.2.RELEASE</spring.version>
|
<spring.version>5.2.5.RELEASE</spring.version>
|
||||||
<spring-security.version>5.2.1.RELEASE</spring-security.version>
|
<spring-security.version>5.2.3.RELEASE</spring-security.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
5
pom.xml
5
pom.xml
|
@ -455,6 +455,7 @@
|
||||||
<module>java-collections-conversions-2</module>
|
<module>java-collections-conversions-2</module>
|
||||||
<module>java-collections-maps</module>
|
<module>java-collections-maps</module>
|
||||||
<module>java-collections-maps-2</module>
|
<module>java-collections-maps-2</module>
|
||||||
|
<module>java-collections-maps-3</module>
|
||||||
<!-- <module>java-ee-8-security-api</module> --> <!-- long running -->
|
<!-- <module>java-ee-8-security-api</module> --> <!-- long running -->
|
||||||
|
|
||||||
<module>javafx</module>
|
<module>javafx</module>
|
||||||
|
@ -535,6 +536,7 @@
|
||||||
<module>mybatis</module>
|
<module>mybatis</module>
|
||||||
|
|
||||||
<module>netflix-modules</module>
|
<module>netflix-modules</module>
|
||||||
|
<!-- <module>netty</module> --> <!-- we haven't upgraded to Java 13 -->
|
||||||
<module>ninja</module>
|
<module>ninja</module>
|
||||||
<module>open-liberty</module>
|
<module>open-liberty</module>
|
||||||
|
|
||||||
|
@ -966,6 +968,7 @@
|
||||||
<module>java-collections-conversions-2</module>
|
<module>java-collections-conversions-2</module>
|
||||||
<module>java-collections-maps</module>
|
<module>java-collections-maps</module>
|
||||||
<module>java-collections-maps-2</module>
|
<module>java-collections-maps-2</module>
|
||||||
|
<module>java-collections-maps-3</module>
|
||||||
<!-- <module>java-ee-8-security-api</module> --> <!-- long running -->
|
<!-- <module>java-ee-8-security-api</module> --> <!-- long running -->
|
||||||
|
|
||||||
<module>javafx</module>
|
<module>javafx</module>
|
||||||
|
@ -1045,6 +1048,7 @@
|
||||||
<module>mybatis</module>
|
<module>mybatis</module>
|
||||||
|
|
||||||
<module>netflix-modules</module>
|
<module>netflix-modules</module>
|
||||||
|
<!-- <module>netty</module> --> <!-- we haven't upgraded to Java 13 -->
|
||||||
<module>ninja</module>
|
<module>ninja</module>
|
||||||
<module>open-liberty</module>
|
<module>open-liberty</module>
|
||||||
|
|
||||||
|
@ -1261,6 +1265,7 @@
|
||||||
<module>wildfly</module>
|
<module>wildfly</module>
|
||||||
<module>xml</module>
|
<module>xml</module>
|
||||||
<module>xstream</module>
|
<module>xstream</module>
|
||||||
|
<module>libraries-concurrency</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
</profile>
|
</profile>
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.baeldung.conditionalflow;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class ConditionalFlowApplication implements CommandLineRunner {
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(ConditionalFlowApplication.class);
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(ConditionalFlowApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String... args) throws Exception {
|
||||||
|
logger.info("Running conditional flow application...");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package org.baeldung.conditionalflow;
|
||||||
|
|
||||||
|
import org.springframework.batch.core.JobExecution;
|
||||||
|
import org.springframework.batch.core.StepExecution;
|
||||||
|
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
|
||||||
|
import org.springframework.batch.core.job.flow.JobExecutionDecider;
|
||||||
|
|
||||||
|
public class NumberInfoDecider implements JobExecutionDecider {
|
||||||
|
|
||||||
|
public static final String NOTIFY = "NOTIFY";
|
||||||
|
public static final String QUIET = "QUIET";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that determines notification status of job
|
||||||
|
* @return true if notifications should be sent.
|
||||||
|
*/
|
||||||
|
private boolean shouldNotify() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
|
||||||
|
if (shouldNotify()) {
|
||||||
|
return new FlowExecutionStatus(NOTIFY);
|
||||||
|
} else {
|
||||||
|
return new FlowExecutionStatus(QUIET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package org.baeldung.conditionalflow.config;
|
||||||
|
|
||||||
|
import org.baeldung.conditionalflow.NumberInfoDecider;
|
||||||
|
import org.baeldung.conditionalflow.model.NumberInfo;
|
||||||
|
import org.baeldung.conditionalflow.step.*;
|
||||||
|
import org.springframework.batch.core.Job;
|
||||||
|
import org.springframework.batch.core.Step;
|
||||||
|
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
|
||||||
|
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
|
||||||
|
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
|
||||||
|
import static org.baeldung.conditionalflow.NumberInfoDecider.NOTIFY;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableBatchProcessing
|
||||||
|
public class NumberInfoConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Qualifier("NotificationStep")
|
||||||
|
public Step notificationStep(StepBuilderFactory sbf) {
|
||||||
|
return sbf.get("Notify step")
|
||||||
|
.tasklet(new NotifierTasklet())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Step numberGeneratorStep(StepBuilderFactory sbf, int[] values, String prepend) {
|
||||||
|
return sbf.get("Number generator")
|
||||||
|
.<NumberInfo, Integer> chunk(1)
|
||||||
|
.reader(new NumberInfoGenerator(values))
|
||||||
|
.processor(new NumberInfoClassifier())
|
||||||
|
.writer(new PrependingStdoutWriter<>(prepend))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Step numberGeneratorStepDecider(StepBuilderFactory sbf, int[] values, String prepend) {
|
||||||
|
return sbf.get("Number generator decider")
|
||||||
|
.<NumberInfo, Integer> chunk(1)
|
||||||
|
.reader(new NumberInfoGenerator(values))
|
||||||
|
.processor(new NumberInfoClassifierWithDecider())
|
||||||
|
.writer(new PrependingStdoutWriter<>(prepend))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Qualifier("first_job")
|
||||||
|
public Job numberGeneratorNonNotifierJob(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory, @Qualifier("NotificationStep") Step notificationStep) {
|
||||||
|
int[] nonNotifierData = { -1, -2, -3 };
|
||||||
|
Step step = numberGeneratorStep(stepBuilderFactory, nonNotifierData, "First Dataset Processor");
|
||||||
|
return jobBuilderFactory.get("Number generator - first dataset")
|
||||||
|
.start(step)
|
||||||
|
.on(NOTIFY)
|
||||||
|
.to(notificationStep)
|
||||||
|
.from(step)
|
||||||
|
.on("*")
|
||||||
|
.stop()
|
||||||
|
.end()
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Qualifier("second_job")
|
||||||
|
public Job numberGeneratorNotifierJob(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory, @Qualifier("NotificationStep") Step notificationStep) {
|
||||||
|
int[] billableData = { 11, -2, -3 };
|
||||||
|
Step dataProviderStep = numberGeneratorStep(stepBuilderFactory, billableData, "Second Dataset Processor");
|
||||||
|
return jobBuilderFactory.get("Number generator - second dataset")
|
||||||
|
.start(dataProviderStep)
|
||||||
|
.on(NOTIFY)
|
||||||
|
.to(notificationStep)
|
||||||
|
.end()
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Qualifier("third_job")
|
||||||
|
@Primary
|
||||||
|
public Job numberGeneratorNotifierJobWithDecider(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory, @Qualifier("NotificationStep") Step notificationStep) {
|
||||||
|
int[] billableData = { 11, -2, -3 };
|
||||||
|
Step dataProviderStep = numberGeneratorStepDecider(stepBuilderFactory, billableData, "Third Dataset Processor");
|
||||||
|
return jobBuilderFactory.get("Number generator - third dataset")
|
||||||
|
.start(dataProviderStep)
|
||||||
|
.next(new NumberInfoDecider())
|
||||||
|
.on(NOTIFY)
|
||||||
|
.to(notificationStep)
|
||||||
|
.end()
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package org.baeldung.conditionalflow.model;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class NumberInfo {
|
||||||
|
private int number;
|
||||||
|
|
||||||
|
public NumberInfo(int number) {
|
||||||
|
this.number = number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NumberInfo from(int number) {
|
||||||
|
return new NumberInfo(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPositive() {
|
||||||
|
return number > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEven() {
|
||||||
|
return number % 2 == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumber() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o)
|
||||||
|
return true;
|
||||||
|
if (o == null || getClass() != o.getClass())
|
||||||
|
return false;
|
||||||
|
NumberInfo that = (NumberInfo) o;
|
||||||
|
return number == that.number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "NumberInfo{" + "number=" + number + '}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package org.baeldung.conditionalflow.step;
|
||||||
|
|
||||||
|
import org.springframework.batch.core.StepContribution;
|
||||||
|
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||||
|
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||||
|
import org.springframework.batch.repeat.RepeatStatus;
|
||||||
|
|
||||||
|
public class NotifierTasklet implements Tasklet {
|
||||||
|
@Override
|
||||||
|
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
|
||||||
|
System.err.println("[" + chunkContext.getStepContext()
|
||||||
|
.getJobName() + "] contains interesting data!!");
|
||||||
|
return RepeatStatus.FINISHED;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package org.baeldung.conditionalflow.step;
|
||||||
|
|
||||||
|
import org.baeldung.conditionalflow.model.NumberInfo;
|
||||||
|
import org.springframework.batch.core.ExitStatus;
|
||||||
|
import org.springframework.batch.core.StepExecution;
|
||||||
|
import org.springframework.batch.core.annotation.BeforeStep;
|
||||||
|
import org.springframework.batch.core.listener.ItemListenerSupport;
|
||||||
|
import org.springframework.batch.item.ItemProcessor;
|
||||||
|
|
||||||
|
import static org.baeldung.conditionalflow.NumberInfoDecider.NOTIFY;
|
||||||
|
import static org.baeldung.conditionalflow.NumberInfoDecider.QUIET;
|
||||||
|
|
||||||
|
public class NumberInfoClassifier extends ItemListenerSupport<NumberInfo, Integer>
|
||||||
|
implements ItemProcessor<NumberInfo, Integer> {
|
||||||
|
private StepExecution stepExecution;
|
||||||
|
|
||||||
|
@BeforeStep
|
||||||
|
public void beforeStep(StepExecution stepExecution) {
|
||||||
|
this.stepExecution = stepExecution;
|
||||||
|
this.stepExecution.setExitStatus(new ExitStatus(QUIET));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterProcess(NumberInfo item, Integer result) {
|
||||||
|
super.afterProcess(item, result);
|
||||||
|
if (item.isPositive()) {
|
||||||
|
stepExecution.setExitStatus(new ExitStatus(NOTIFY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer process(NumberInfo numberInfo) throws Exception {
|
||||||
|
return Integer.valueOf(numberInfo.getNumber());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package org.baeldung.conditionalflow.step;
|
||||||
|
|
||||||
|
import org.baeldung.conditionalflow.model.NumberInfo;
|
||||||
|
import org.springframework.batch.core.listener.ItemListenerSupport;
|
||||||
|
import org.springframework.batch.item.ItemProcessor;
|
||||||
|
|
||||||
|
public class NumberInfoClassifierWithDecider extends ItemListenerSupport<NumberInfo, Integer> implements ItemProcessor<NumberInfo, Integer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer process(NumberInfo numberInfo) throws Exception {
|
||||||
|
return Integer.valueOf(numberInfo.getNumber());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package org.baeldung.conditionalflow.step;
|
||||||
|
|
||||||
|
import org.baeldung.conditionalflow.model.NumberInfo;
|
||||||
|
import org.springframework.batch.item.ItemReader;
|
||||||
|
|
||||||
|
public class NumberInfoGenerator implements ItemReader<NumberInfo> {
|
||||||
|
private int[] values;
|
||||||
|
private int counter;
|
||||||
|
|
||||||
|
public NumberInfoGenerator(int[] values) {
|
||||||
|
this.values = values;
|
||||||
|
counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NumberInfo read() {
|
||||||
|
if (counter == values.length) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return new NumberInfo(values[counter++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.baeldung.conditionalflow.step;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.batch.item.ItemWriter;
|
||||||
|
|
||||||
|
public class PrependingStdoutWriter<T> implements ItemWriter<T> {
|
||||||
|
private String prependText;
|
||||||
|
|
||||||
|
public PrependingStdoutWriter(String prependText) {
|
||||||
|
this.prependText = prependText;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(List<? extends T> list) {
|
||||||
|
for (T listItem : list) {
|
||||||
|
System.out.println(prependText + " " + listItem.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package org.baeldung.conditionalflow;
|
||||||
|
|
||||||
|
import org.baeldung.conditionalflow.config.NumberInfoConfig;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.batch.core.ExitStatus;
|
||||||
|
import org.springframework.batch.core.JobExecution;
|
||||||
|
import org.springframework.batch.core.StepExecution;
|
||||||
|
import org.springframework.batch.test.JobLauncherTestUtils;
|
||||||
|
import org.springframework.batch.test.context.SpringBatchTest;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.TestExecutionListeners;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||||
|
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringBatchTest
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@ContextConfiguration(classes = { NumberInfoConfig.class })
|
||||||
|
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class })
|
||||||
|
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
|
||||||
|
public class DeciderJobIntegrationTest {
|
||||||
|
@Autowired
|
||||||
|
private JobLauncherTestUtils jobLauncherTestUtils;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenNumberGeneratorDecider_whenDeciderRuns_thenStatusIsNotify() throws Exception {
|
||||||
|
JobExecution jobExecution = jobLauncherTestUtils.launchJob();
|
||||||
|
Collection<StepExecution> actualStepExecutions = jobExecution.getStepExecutions();
|
||||||
|
ExitStatus actualJobExitStatus = jobExecution.getExitStatus();
|
||||||
|
|
||||||
|
assertEquals("COMPLETED", actualJobExitStatus.getExitCode()
|
||||||
|
.toString());
|
||||||
|
assertEquals(2, actualStepExecutions.size());
|
||||||
|
boolean notifyStepDidRun = false;
|
||||||
|
Iterator<StepExecution> iterator = actualStepExecutions.iterator();
|
||||||
|
while (iterator.hasNext() && !notifyStepDidRun) {
|
||||||
|
if (iterator.next()
|
||||||
|
.getStepName()
|
||||||
|
.equals("Notify step")) {
|
||||||
|
notifyStepDidRun = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(notifyStepDidRun);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package org.baeldung.conditionalflow.model;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
class NumberInfoUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenPositive_whenFrom_isPositive() {
|
||||||
|
assertTrue(NumberInfo.from(1)
|
||||||
|
.isPositive());
|
||||||
|
assertTrue(NumberInfo.from(11)
|
||||||
|
.isPositive());
|
||||||
|
assertFalse(NumberInfo.from(0)
|
||||||
|
.isPositive());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenNegative_whenFrom_isNegative() {
|
||||||
|
assertFalse(NumberInfo.from(-1)
|
||||||
|
.isPositive());
|
||||||
|
assertFalse(NumberInfo.from(-10)
|
||||||
|
.isPositive());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenEven_whenFrom_isEven() {
|
||||||
|
assertTrue(NumberInfo.from(0)
|
||||||
|
.isEven());
|
||||||
|
assertTrue(NumberInfo.from(-2)
|
||||||
|
.isEven());
|
||||||
|
assertTrue(NumberInfo.from(2)
|
||||||
|
.isEven());
|
||||||
|
assertTrue(NumberInfo.from(-22)
|
||||||
|
.isEven());
|
||||||
|
assertTrue(NumberInfo.from(22)
|
||||||
|
.isEven());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenOdd_whenFrom_isOdd() {
|
||||||
|
|
||||||
|
assertFalse(NumberInfo.from(1)
|
||||||
|
.isEven());
|
||||||
|
assertFalse(NumberInfo.from(-1)
|
||||||
|
.isEven());
|
||||||
|
|
||||||
|
assertFalse(NumberInfo.from(13)
|
||||||
|
.isEven());
|
||||||
|
assertFalse(NumberInfo.from(-13)
|
||||||
|
.isEven());
|
||||||
|
assertFalse(NumberInfo.from(31)
|
||||||
|
.isEven());
|
||||||
|
assertFalse(NumberInfo.from(-51)
|
||||||
|
.isEven());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void giveGeneratedInt_whenFrom_isNumberFromGenerator() {
|
||||||
|
for (int i = -100; i < 100; i++) {
|
||||||
|
assertEquals(i, NumberInfo.from(i)
|
||||||
|
.getNumber());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.baeldung.conditionalflow.step;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import org.baeldung.conditionalflow.model.NumberInfo;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class NumberInfoClassifierUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenNumberInfo_whenProcess_thenConvertsToInteger() throws Exception {
|
||||||
|
NumberInfoClassifier nic = new NumberInfoClassifier();
|
||||||
|
assertEquals(Integer.valueOf(4), nic.process(NumberInfo.from(4)));
|
||||||
|
assertEquals(Integer.valueOf(-4), nic.process(NumberInfo.from(-4)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.baeldung.conditionalflow.step;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
|
import org.baeldung.conditionalflow.model.NumberInfo;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class NumberInfoGeneratorUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenArray_whenGenerator_correctOrderAndValue() {
|
||||||
|
int[] numbers = new int[] { 1, -2, 4, -10 };
|
||||||
|
NumberInfoGenerator numberGenerator = new NumberInfoGenerator(numbers);
|
||||||
|
assertEquals(new NumberInfo(numbers[0]), numberGenerator.read());
|
||||||
|
assertEquals(new NumberInfo(numbers[1]), numberGenerator.read());
|
||||||
|
assertEquals(new NumberInfo(numbers[2]), numberGenerator.read());
|
||||||
|
assertEquals(new NumberInfo(numbers[3]), numberGenerator.read());
|
||||||
|
assertNull(numberGenerator.read());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
|
@ -208,7 +208,7 @@
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<!-- The main class to start by executing java -jar -->
|
<!-- The main class to start by executing java -jar -->
|
||||||
<start-class>org.baeldung.boot.Application</start-class>
|
<start-class>com.baeldung.boot.Application</start-class>
|
||||||
<jquery.version>3.1.1</jquery.version>
|
<jquery.version>3.1.1</jquery.version>
|
||||||
<bootstrap.version>3.3.7-1</bootstrap.version>
|
<bootstrap.version>3.3.7-1</bootstrap.version>
|
||||||
<jpa.version>2.2</jpa.version>
|
<jpa.version>2.2</jpa.version>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung.boot;
|
package com.baeldung.boot;
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung.boot.client;
|
package com.baeldung.boot.client;
|
||||||
|
|
||||||
public class Details {
|
public class Details {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung.boot.client;
|
package com.baeldung.boot.client;
|
||||||
|
|
||||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung.websocket.client;
|
package com.baeldung.websocket.client;
|
||||||
|
|
||||||
public class Message {
|
public class Message {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung.websocket.client;
|
package com.baeldung.websocket.client;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung.websocket.client;
|
package com.baeldung.websocket.client;
|
||||||
|
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.baeldung.boot.Application;
|
import com.baeldung.boot.Application;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
|
@ -1,10 +1,12 @@
|
||||||
package org.baeldung.boot.client;
|
package com.baeldung.boot.client;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
|
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
|
||||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
|
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
|
||||||
|
|
||||||
import org.baeldung.boot.Application;
|
import com.baeldung.boot.Application;
|
||||||
|
import com.baeldung.boot.client.Details;
|
||||||
|
import com.baeldung.boot.client.DetailsServiceClient;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
|
@ -1,6 +1,5 @@
|
||||||
package com.baeldung.websocket.client;
|
package com.baeldung.websocket.client;
|
||||||
|
|
||||||
import org.baeldung.websocket.client.MyStompSessionHandler;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.springframework.messaging.simp.stomp.StompHeaders;
|
import org.springframework.messaging.simp.stomp.StompHeaders;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
|
@ -190,7 +190,7 @@
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<!-- The main class to start by executing java -jar -->
|
<!-- The main class to start by executing java -jar -->
|
||||||
<start-class>org.baeldung.boot.Application</start-class>
|
<start-class>com.baeldung.boot.Application</start-class>
|
||||||
<jquery.version>3.1.1</jquery.version>
|
<jquery.version>3.1.1</jquery.version>
|
||||||
<bootstrap.version>3.3.7-1</bootstrap.version>
|
<bootstrap.version>3.3.7-1</bootstrap.version>
|
||||||
<jpa.version>2.2</jpa.version>
|
<jpa.version>2.2</jpa.version>
|
||||||
|
|
|
@ -21,7 +21,7 @@ apply plugin: 'io.spring.dependency-management'
|
||||||
//add tasks thinJar and thinResolve for thin JAR deployments
|
//add tasks thinJar and thinResolve for thin JAR deployments
|
||||||
apply plugin: 'org.springframework.boot.experimental.thin-launcher'
|
apply plugin: 'org.springframework.boot.experimental.thin-launcher'
|
||||||
|
|
||||||
group = 'org.baeldung'
|
group = 'com.baeldung'
|
||||||
version = '0.0.1-SNAPSHOT'
|
version = '0.0.1-SNAPSHOT'
|
||||||
sourceCompatibility = 1.8
|
sourceCompatibility = 1.8
|
||||||
|
|
||||||
|
@ -35,16 +35,16 @@ dependencies {
|
||||||
}
|
}
|
||||||
|
|
||||||
springBoot {
|
springBoot {
|
||||||
mainClassName = 'org.baeldung.DemoApplication'
|
mainClassName = 'com.baeldung.DemoApplication'
|
||||||
}
|
}
|
||||||
|
|
||||||
bootJar {
|
bootJar {
|
||||||
// This is overridden by the mainClassName in springBoot{} and added here for reference purposes.
|
// This is overridden by the mainClassName in springBoot{} and added here for reference purposes.
|
||||||
mainClassName = 'org.baeldung.DemoApplication'
|
mainClassName = 'com.baeldung.DemoApplication'
|
||||||
|
|
||||||
// This block serves the same purpose as the above thus commented out. Added here for reference purposes
|
// This block serves the same purpose as the above thus commented out. Added here for reference purposes
|
||||||
// manifest {
|
// manifest {
|
||||||
// attributes 'Start-Class': 'org.baeldung.DemoApplication'
|
// attributes 'Start-Class': 'com.baeldung.DemoApplication'
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
|
@ -7,7 +7,7 @@ import org.springframework.context.annotation.PropertySource;
|
||||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ComponentScan("org.baeldung.properties.core")
|
@ComponentScan("com.baeldung.properties.core")
|
||||||
@PropertySource("classpath:foo.properties")
|
@PropertySource("classpath:foo.properties")
|
||||||
public class ExternalPropertiesWithJavaConfig {
|
public class ExternalPropertiesWithJavaConfig {
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import org.springframework.context.annotation.ImportResource;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ImportResource("classpath:configForProperties.xml")
|
@ImportResource("classpath:configForProperties.xml")
|
||||||
@ComponentScan("org.baeldung.core")
|
@ComponentScan("com.baeldung.core")
|
||||||
public class ExternalPropertiesWithXmlConfig {
|
public class ExternalPropertiesWithXmlConfig {
|
||||||
|
|
||||||
public ExternalPropertiesWithXmlConfig() {
|
public ExternalPropertiesWithXmlConfig() {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import org.springframework.context.annotation.ImportResource;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ImportResource("classpath:configForPropertiesOne.xml")
|
@ImportResource("classpath:configForPropertiesOne.xml")
|
||||||
@ComponentScan("org.baeldung.core")
|
@ComponentScan("com.baeldung.core")
|
||||||
public class ExternalPropertiesWithXmlConfigOne {
|
public class ExternalPropertiesWithXmlConfigOne {
|
||||||
|
|
||||||
public ExternalPropertiesWithXmlConfigOne() {
|
public ExternalPropertiesWithXmlConfigOne() {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung;
|
package com.baeldung;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.baeldung.boot;
|
package com.baeldung.boot;
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue