Merge branch 'master' of https://github.com/eugenp/tutorials into BAEL_3301_testing_@ConfigurationProperties

This commit is contained in:
m.raheem 2020-02-27 16:38:21 +02:00
commit 057cab32fc
398 changed files with 4074 additions and 10389 deletions

View File

@ -11,5 +11,5 @@ This module contains articles about core Java input and output (IO)
- [List Files in a Directory in Java](https://www.baeldung.com/java-list-directory-files)
- [Java Append Data to a File](https://www.baeldung.com/java-append-to-file)
- [How to Copy a File with Java](https://www.baeldung.com/java-copy-file)
- [Create a Directory in Java](https://www.baeldung.com/java-create-directory)
- [Create a Directory in Java](https://www.baeldung.com/java-create-directory)
- [[<-- Prev]](/core-java-modules/core-java-io)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,148 @@
package com.baeldung.lock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.NonReadableChannelException;
import java.nio.channels.NonWritableChannelException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
public class FileLocks {
private static final Logger LOG = LoggerFactory.getLogger(FileLocks.class);
// Write locks
/**
* Trying to get an exclusive lock on a read-only FileChannel won't work.
*/
static void getExclusiveLockFromInputStream() throws IOException {
Path path = Files.createTempFile("foo", "txt");
try (FileInputStream fis = new FileInputStream(path.toFile());
FileLock lock = fis.getChannel()
.lock()) {
LOG.debug("This won't happen");
} catch (NonWritableChannelException e) {
LOG.error("The channel obtained through a FileInputStream isn't writable. You can't obtain an exclusive lock on it!");
throw e;
}
}
/**
* Gets an exclusive lock from a RandomAccessFile. Works because the file is writable.
* @param from beginning of the locked region
* @param size how many bytes to lock
* @return A lock object representing the newly-acquired lock
* @throws IOException if there is a problem creating the temporary file
*/
static FileLock getExclusiveLockFromRandomAccessFile(long from, long size) throws IOException {
Path path = Files.createTempFile("foo", "txt");
try (RandomAccessFile file = new RandomAccessFile(path.toFile(), "rw");
FileLock lock = file.getChannel()
.lock(from, size, false)) {
if (lock.isValid()) {
LOG.debug("This is a valid exclusive lock");
return lock;
}
return null;
} catch (Exception e) {
LOG.error(e.getMessage());
}
return null;
}
/**
* Acquires an exclusive lock on a file region.
* @param from beginning of the locked region
* @param size how many bytes to lock
* @return A lock object representing the newly-acquired lock
* @throws IOException if there is a problem creating the temporary file
*/
static FileLock getExclusiveLockFromFileChannelOpen(long from, long size) throws IOException {
Path path = Files.createTempFile("foo", "txt");
try (FileChannel channel = FileChannel.open(path, StandardOpenOption.APPEND); FileLock lock = channel.lock(from, size, false)) {
String text = "Hello, world.";
ByteBuffer buffer = ByteBuffer.allocate(text.length() + System.lineSeparator()
.length());
buffer.put((text + System.lineSeparator()).getBytes(StandardCharsets.UTF_8));
buffer.flip();
while (buffer.hasRemaining()) {
channel.write(buffer, channel.size());
}
LOG.debug("This was written to the file");
Files.lines(path)
.forEach(LOG::debug);
return lock;
}
}
// Read locks
/**
* Trying to get a shared lock on a write-only FileChannel won't work.
*/
static void getReadLockFromOutputStream() throws IOException {
Path path = Files.createTempFile("foo", "txt");
try (FileOutputStream fis = new FileOutputStream(path.toFile());
FileLock lock = fis.getChannel()
.lock(0, Long.MAX_VALUE, true)) {
LOG.debug("This won't happen");
} catch (NonReadableChannelException e) {
LOG.error("The channel obtained through a FileOutputStream isn't readable. " + "You can't obtain an shared lock on it!");
throw e;
}
}
/**
* Gets a lock from an <tt>InputStream</tt>.
* @param from beginning of the locked region
* @param size how many bytes to lock
* @return A lock object representing the newly-acquired lock
* @throws IOException if there is a problem creating the temporary file
*/
static FileLock getReadLockFromInputStream(long from, long size) throws IOException {
Path path = Files.createTempFile("foo", "txt");
try (FileInputStream fis = new FileInputStream(path.toFile());
FileLock lock = fis.getChannel()
.lock(from, size, true)) {
if (lock.isValid()) {
LOG.debug("This is a valid shared lock");
return lock;
}
return null;
}
}
/**
* Gets an exclusive lock from a RandomAccessFile. Works because the file is readable.
* @param from beginning of the locked region
* @param size how many bytes to lock
* @return A lock object representing the newly-acquired lock
* @throws IOException if there is a problem creating the temporary file
*/
static FileLock getReadLockFromRandomAccessFile(long from, long size) throws IOException {
Path path = Files.createTempFile("foo", "txt");
try (RandomAccessFile file = new RandomAccessFile(path.toFile(), "r"); // could also be "rw", but "r" is sufficient for reading
FileLock lock = file.getChannel()
.lock(from, size, true)) {
if (lock.isValid()) {
LOG.debug("This is a valid shared lock");
return lock;
}
} catch (Exception e) {
LOG.error(e.getMessage());
}
return null;
}
}

View File

@ -0,0 +1,54 @@
package com.baeldung.lock;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.nio.channels.FileLock;
import java.nio.channels.NonReadableChannelException;
import java.nio.channels.NonWritableChannelException;
import org.junit.jupiter.api.Test;
class FileLocksUnitTest {
@Test
void givenAnInputStream_whenGetWriteLock_thenThrowNonWritableChannelException() {
assertThrows(NonWritableChannelException.class, () -> FileLocks.getExclusiveLockFromInputStream());
}
@Test
void givenARandomAccessFileWithReadWriteAccess_whenGetWriteLock_thenLock() throws IOException {
FileLock lock = FileLocks.getExclusiveLockFromRandomAccessFile(0, 10);
assertNotNull(lock);
assertFalse(lock.isShared());
}
@Test
void givenAPath_whenGetExclusiveLock_thenLock() throws IOException {
FileLock lock = FileLocks.getExclusiveLockFromFileChannelOpen(0, 10);
assertNotNull(lock);
assertFalse(lock.isShared());
}
@Test
void givenAFileOutputStream_whenGetSharedLock_thenThrowNonReadableChannelException() {
assertThrows(NonReadableChannelException.class, FileLocks::getReadLockFromOutputStream);
}
@Test
void givenAnInputStream_whenGetSharedLock_thenLock() throws IOException {
FileLock lock = FileLocks.getReadLockFromInputStream(0, 10);
assertNotNull(lock);
assertTrue(lock.isShared());
}
@Test
void givenARandomAccessFile_whenGetSharedLock_thenLock() throws IOException {
FileLock lock = FileLocks.getReadLockFromRandomAccessFile(0, 10);
assertNotNull(lock);
assertTrue(lock.isShared());
}
}

View File

@ -11,7 +11,7 @@
- [Introduction to Java Serialization](http://www.baeldung.com/java-serialization)
- [Guide to UUID in Java](http://www.baeldung.com/java-uuid)
- [Creating a Java Compiler Plugin](http://www.baeldung.com/java-build-compiler-plugin)
- [Quick Guide to Java Stack](http://www.baeldung.com/java-stack)
- [Quick Guide to the Java Stack](http://www.baeldung.com/java-stack)
- [Compiling Java *.class Files with javac](http://www.baeldung.com/javac)
- [Introduction to Javadoc](http://www.baeldung.com/javadoc)
- [Guide to the Externalizable Interface in Java](http://www.baeldung.com/java-externalizable)

View File

@ -1,4 +1,4 @@
package com.baeldung;
package com.baeldung.timer;
import org.junit.Test;
import org.slf4j.Logger;

View File

@ -5,4 +5,5 @@ This module contains articles about Kotlin core features.
### Relevant articles:
- [Working with Dates in Kotlin](https://www.baeldung.com/kotlin-dates)
- [Kotlin Ternary Conditional Operator](https://www.baeldung.com/kotlin-ternary-operator)
- [Sequences in Kotlin](https://www.baeldung.com/kotlin/sequences)
- [[<-- Prev]](/core-kotlin-modules/core-kotlin)

View File

@ -13,4 +13,7 @@ This module contains articles about Kotlin core features.
- [Implementing a Binary Tree in Kotlin](https://www.baeldung.com/kotlin-binary-tree)
- [JUnit 5 for Kotlin Developers](https://www.baeldung.com/junit-5-kotlin)
- [Converting Kotlin Data Class from JSON using GSON](https://www.baeldung.com/kotlin-json-convert-data-class)
- [Fuel HTTP Library with Kotlin](https://www.baeldung.com/kotlin-fuel)
- [Introduction to Kovenant Library for Kotlin](https://www.baeldung.com/kotlin-kovenant)
- [Dependency Injection for Kotlin with Injekt](https://www.baeldung.com/kotlin-dependency-injection-with-injekt)
- [[More --> ]](/core-kotlin-modules/core-kotlin-2)

View File

@ -5,3 +5,4 @@ This module contains articles about Scala's core features
### Relevant Articles:
- [Introduction to Scala](https://www.baeldung.com/scala-intro)
- [Regular Expressions in Scala](https://www.baeldung.com/scala/regular-expressions)

View File

@ -0,0 +1,82 @@
package com.baeldung.scala
import org.junit.Assert.assertEquals
import org.junit.Test
class HigherOrderFunctionsExamplesUnitTest {
@Test
def whenCallingMapWithAnonymousFunction_thenTransformationIsApplied() = {
val expected = Seq("sir Alex Ferguson", "sir Bobby Charlton", "sir Frank Lampard")
val names = Seq("Alex Ferguson", "Bobby Charlton", "Frank Lampard")
val sirNames = names.map(name => "sir " + name)
assertEquals(expected, sirNames)
}
@Test
def whenCallingMapWithDefined_thenTransformationIsApplied() = {
val expected = Seq("sir Alex Ferguson", "sir Bobby Charlton", "sir Frank Lampard")
val names = Seq("Alex Ferguson", "Bobby Charlton", "Frank Lampard")
def prefixWithSir(name: String) = "sir " + name
val sirNames = names.map(prefixWithSir)
assertEquals(expected, sirNames)
}
@Test
def whenCallingFilter_thenUnecessaryElementsAreRemoved() = {
val expected = Seq("John O'Shea", "John Hartson")
val names = Seq("John O'Shea", "Aiden McGeady", "John Hartson")
val johns = names.filter(name => name.matches("^John .*"))
assertEquals(expected, johns)
}
@Test
def whenCallingReduce_thenProperSumIsCalculated() = {
val expected = 2750
val earnings = Seq(1000, 1300, 450)
val sumEarnings = earnings.reduce((acc, x) => acc + x)
assertEquals(expected, sumEarnings)
}
@Test
def whenCallingFold_thenNumberOfWordsShouldBeCalculated() = {
val expected = 6
val strings = Seq("bunch of words", "just me", "it")
val sumEarnings = strings.foldLeft(0)((acc, x) => acc + x.split(" ").size)
assertEquals(expected, sumEarnings)
}
@Test
def whenCallingOwnHigherOrderFunction_thenProperFunctionIsReturned() = {
def mathOperation(name: String): (Int, Int) => Int = (x: Int, y: Int) => {
name match {
case "addition" => x + y
case "multiplication" => x * y
case "division" => x/y
case "subtraction" => x - y
}
}
def add: (Int, Int) => Int = mathOperation("addition")
def mul: (Int, Int) => Int = mathOperation("multiplication")
def div: (Int, Int) => Int = mathOperation("division")
def sub: (Int, Int) => Int = mathOperation("subtraction")
assertEquals(15, add(10, 5))
assertEquals(50, mul(10, 5))
assertEquals(2, div(10, 5))
assertEquals(5, sub(10, 5))
}
}

View File

@ -1,4 +1,4 @@
package com.baeldung.scala
package com.baeldung.scala.regex
import org.junit.Test
import org.junit.Assert.assertEquals

View File

@ -12,15 +12,32 @@
<version>0.0.1-SNAPSHOT</version>
<relativePath>../parent-java</relativePath>
</parent>
<dependencies>
<dependency>
<dependency>
<groupId>it.unimi.dsi</groupId>
<artifactId>dsiutils</artifactId>
<version>${dsiutils.version}</version>
</dependency>
<dependency>
<groupId>io.vavr</groupId>
<artifactId>vavr</artifactId>
<version>${vavr.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>java-numbers-3</finalName>
<resources>
@ -33,6 +50,9 @@
<properties>
<dsiutils.version>2.6.0</dsiutils.version>
<vavr.version>0.10.2</vavr.version>
<commons.version>3.9</commons.version>
<assertj.version>3.6.1</assertj.version>
</properties>
</project>

View File

@ -0,0 +1,149 @@
package com.baeldung.parsedouble;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Optional;
import org.apache.commons.lang3.math.NumberUtils;
import org.junit.Test;
import com.google.common.base.MoreObjects;
import com.google.common.primitives.Doubles;
import io.vavr.control.Try;
public class StringToDoubleParserUnitTest {
@Test
public void givenNullValue_whenParseStringToDouble_thenDefaultNaNValueIsReturned() {
assertThat(parseStringToDouble(null)).isNaN();
}
@Test
public void givenEmptyStringValue_whenParseStringToDouble_thenDefaultNaNValueIsReturned() {
assertThat(parseStringToDouble("")).isNaN();
}
@Test
public void givenStringValue_whenParseStringToDouble_thenDoubleValueIsReturned() {
assertThat(parseStringToDouble("1")).isEqualTo(1.0d);
}
@Test
public void givenStringValue_whenParseStringToDoubleWithDefault_thenDoubleValueIsReturned() {
assertThat(parseStringToDouble("1", 2.0d)).isEqualTo(1.0d);
}
@Test
public void givenEmptyStringValue_whenParseStringToDoubleWithDefault_thenDefaultValueIsReturned() {
assertThat(parseStringToDouble("", 1.0d)).isEqualTo(1.0d);
}
@Test
public void givenNullValue_whenParseStringToDoubleWithDefault_thenDefaultValueIsReturned() {
assertThat(parseStringToDouble(null, 1.0d)).isEqualTo(1.0d);
}
@Test
public void givenStringValue_whenParseStringToOptionalDouble_thenOptionalValueIsReturned() {
assertThat(parseStringToOptionalDouble("1")).isEqualTo(Optional.of(1.0d));
}
@Test
public void givenNullValue_whenParseStringToOptionalDouble_thenOptionalValueIsEmpty() {
assertThat(parseStringToOptionalDouble(null)).isEqualTo(Optional.empty());
}
@Test
public void givenEmptyStringValue_whenParseStringToOptionalDouble_thenOptionalValueIsEmpty() {
assertThat(parseStringToOptionalDouble("")).isEqualTo(Optional.empty());
}
@Test
public void givenEmptyStringValue_whenParseStringToOptionalDouble_thenDefaulOptionalValueIsReturned() {
assertThat(parseStringToOptionalDouble("").orElse(1.0d)).isEqualTo(1.0d);
}
@Test
public void givenNullValue_whenParseStringToOptionalDouble_thenDefaulOptionalValueIsReturned() {
assertThat(parseStringToOptionalDouble(null).orElse(1.0d)).isEqualTo(1.0d);
}
@Test
public void givenStringValue_whenTryStringToDouble_thenDoubleValueIsReturned() {
assertThat(tryStringToDouble("1", 2.0d)).isEqualTo(1.0d);
}
@Test
public void givenNullValue_whenTryStringToDoubleWithDefault_thenDoubleValueIsReturned() {
assertThat(tryStringToDouble(null, 2.0d)).isEqualTo(2.0d);
}
@Test
public void givenEmptyStringValue_whenTryStringToDoubleWithDefault_thenDoubleValueIsReturned() {
assertThat(tryStringToDouble("", 2.0d)).isEqualTo(2.0d);
}
@Test
public void givenTwoStringValues_whenTryParseFirstNonNull_thenDoubleValueIsReturned() {
assertThat(Doubles.tryParse(MoreObjects.firstNonNull("1.0", "2.0"))).isEqualTo(1.0d);
}
@Test
public void givenNullStringValue_whenTryParseFirstNonNull_thenSecondDoubleValueIsReturned() {
assertThat(Doubles.tryParse(MoreObjects.firstNonNull(null, "2.0"))).isEqualTo(2.0d);
}
@Test
public void givenEmptyStringValue_whenTryParseFirstNonNull_thenNullIsReturned() {
assertThat(Doubles.tryParse(MoreObjects.firstNonNull("", "2.0"))).isEqualTo(null);
}
@Test
public void givenStringValue_whenToDouble_thenDoubleValueIsReturned() {
assertThat(NumberUtils.toDouble("1.0")).isEqualTo(1.0d);
}
@Test
public void givenNullValue_whenToDouble_thenLibraryDefaultDoubleValueIsReturned() {
String nullString = null;
assertThat(NumberUtils.toDouble(nullString)).isEqualTo(0.0d);
}
@Test
public void givenEmptyStringValue_whenToDouble_thenLibraryDefaultDoubleValueIsReturned() {
assertThat(NumberUtils.toDouble("")).isEqualTo(0.0d);
}
@Test
public void givenEmptyStringValue_whenToDoubleWithDefault_thenDoubleValueIsReturned() {
assertThat(NumberUtils.toDouble("", 2.0d)).isEqualTo(2.0d);
}
@Test
public void givenNullValue_whenToDoubleWithDefault_thenDoubleValueIsReturned() {
String nullString = null;
assertThat(NumberUtils.toDouble(nullString, 2.0d)).isEqualTo(2.0d);
}
@Test
public void givenStringValue_whenToDoubleWithDefault_thenDoubleValueIsReturned() {
assertThat(NumberUtils.toDouble("1.0", 2.0d)).isEqualTo(1.0d);
}
private static Optional<Double> parseStringToOptionalDouble(String value) {
return value == null || value.isEmpty() ? Optional.empty() : Optional.of(Double.valueOf(value));
}
private static double parseStringToDouble(String value) {
return value == null || value.isEmpty() ? Double.NaN : Double.parseDouble(value);
}
private static double parseStringToDouble(String value, double defaultValue) {
return value == null || value.isEmpty() ? defaultValue : Double.parseDouble(value);
}
private static double tryStringToDouble(String value, double defaultValue) {
return Try.of(() -> Double.parseDouble(value)).getOrElse(defaultValue);
}
}

View File

@ -73,6 +73,28 @@
<version>${cache2k.version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.squareup.moshi</groupId>
<artifactId>moshi</artifactId>
<version>${moshi.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.moshi</groupId>
<artifactId>moshi-adapters</artifactId>
<version>${moshi.version}</version>
</dependency>
<dependency>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-aspects</artifactId>
<version>${jcabi-aspects.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectjrt.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<repositories>
@ -81,6 +103,36 @@
<url>https://jitpack.io</url>
</repository>
</repositories>
<build>
<finalName>libraries-3</finalName>
<plugins>
<plugin>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-maven-plugin</artifactId>
<version>${jcabi-maven-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>ajc</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectjtools.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectjweaver.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<properties>
<jcommander.version>1.78</jcommander.version>
@ -93,5 +145,11 @@
<cactoos.version>0.43</cactoos.version>
<airline.version>2.7.2</airline.version>
<cache2k.version>1.2.3.Final</cache2k.version>
<moshi.version>1.9.2</moshi.version>
<jcabi-aspects.version>0.22.6</jcabi-aspects.version>
<aspectjrt.version>1.9.2</aspectjrt.version>
<jcabi-maven-plugin.version>0.14.1</jcabi-maven-plugin.version>
<aspectjtools.version>1.9.2</aspectjtools.version>
<aspectjweaver.version>1.9.2</aspectjweaver.version>
</properties>
</project>

View File

@ -4,38 +4,33 @@ import java.util.Objects;
import org.cache2k.Cache;
import org.cache2k.Cache2kBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ProductHelper {
final Logger LOGGER = LoggerFactory.getLogger(ProductHelper.class);
private Cache<String, Integer> cachedDiscounts;
private int cacheMissCount = 0;
public ProductHelper() {
cachedDiscounts = Cache2kBuilder.of(String.class, Integer.class)
.name("discount")
.eternal(true)
.entryCapacity(100)
.build();
initDiscountCache("Sports", 20);
}
public void initDiscountCache(String productType, Integer value) {
cachedDiscounts.put(productType, value);
}
public Integer getDiscount(String productType) {
Integer discount = cachedDiscounts.get(productType);
if (Objects.isNull(discount)) {
LOGGER.info("Discount for {} not found.", productType);
discount = 0;
} else {
LOGGER.info("Discount for {} found.", productType);
cacheMissCount++;
discount = "Sports".equalsIgnoreCase(productType) ? 20 : 10;
cachedDiscounts.put(productType, discount);
}
return discount;
}
public int getCacheMissCount() {
return cacheMissCount;
}
}

View File

@ -1,6 +1,5 @@
package com.baeldung.cache2k;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.cache2k.Cache;
@ -14,28 +13,26 @@ public class ProductHelperUsingLoader {
private Cache<String, Integer> cachedDiscounts;
private int cacheMissCount = 0;
public ProductHelperUsingLoader() {
cachedDiscounts = Cache2kBuilder.of(String.class, Integer.class)
.name("discount-loader")
.eternal(false)
.expireAfterWrite(10, TimeUnit.MILLISECONDS)
.entryCapacity(100)
.loader((key) -> {
LOGGER.info("Calculating discount for {}.", key);
cacheMissCount++;
return "Sports".equalsIgnoreCase(key) ? 20 : 10;
})
.build();
}
public Integer getDiscount(String productType) {
Integer discount = cachedDiscounts.get(productType);
if (Objects.isNull(discount)) {
LOGGER.info("Discount for {} not found.", productType);
discount = 0;
} else {
LOGGER.info("Discount for {} found.", productType);
}
return discount;
return cachedDiscounts.get(productType);
}
public int getCacheMissCount() {
return cacheMissCount;
}
}

View File

@ -1,6 +1,5 @@
package com.baeldung.cache2k;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.cache2k.Cache;
@ -16,14 +15,15 @@ public class ProductHelperWithEventListener {
private Cache<String, Integer> cachedDiscounts;
private int cacheMissCount = 0;
public ProductHelperWithEventListener() {
cachedDiscounts = Cache2kBuilder.of(String.class, Integer.class)
.name("discount-listener")
.eternal(false)
.expireAfterWrite(10, TimeUnit.MILLISECONDS)
.entryCapacity(100)
.loader((key) -> {
LOGGER.info("Calculating discount for {}.", key);
cacheMissCount++;
return "Sports".equalsIgnoreCase(key) ? 20 : 10;
})
.addListener(new CacheEntryCreatedListener<String, Integer>() {
@ -36,14 +36,11 @@ public class ProductHelperWithEventListener {
}
public Integer getDiscount(String productType) {
Integer discount = cachedDiscounts.get(productType);
if (Objects.isNull(discount)) {
LOGGER.info("Discount for {} not found.", productType);
discount = 0;
} else {
LOGGER.info("Discount for {} found.", productType);
}
return discount;
return cachedDiscounts.get(productType);
}
public int getCacheMissCount() {
return cacheMissCount;
}
}

View File

@ -5,39 +5,34 @@ import java.util.concurrent.TimeUnit;
import org.cache2k.Cache;
import org.cache2k.Cache2kBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ProductHelperWithExpiry {
final Logger LOGGER = LoggerFactory.getLogger(ProductHelperWithExpiry.class);
private Cache<String, Integer> cachedDiscounts;
private int cacheMissCount = 0;
public ProductHelperWithExpiry() {
cachedDiscounts = Cache2kBuilder.of(String.class, Integer.class)
.name("discount-expiry")
.eternal(false)
.expireAfterWrite(5, TimeUnit.MILLISECONDS)
.entryCapacity(100)
.build();
initDiscountCache("Sports", 20);
}
public void initDiscountCache(String productType, Integer value) {
cachedDiscounts.put(productType, value);
}
public Integer getDiscount(String productType) {
Integer discount = cachedDiscounts.get(productType);
if (Objects.isNull(discount)) {
LOGGER.info("Discount for {} not found.", productType);
discount = 0;
} else {
LOGGER.info("Discount for {} found.", productType);
cacheMissCount++;
discount = "Sports".equalsIgnoreCase(productType) ? 20 : 10;
cachedDiscounts.put(productType, discount);
}
return discount;
}
public int getCacheMissCount() {
return cacheMissCount;
}
}

View File

@ -0,0 +1,112 @@
package com.baeldung.jcabi;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import com.jcabi.aspects.Async;
import com.jcabi.aspects.Cacheable;
import com.jcabi.aspects.LogExceptions;
import com.jcabi.aspects.Loggable;
import com.jcabi.aspects.Quietly;
import com.jcabi.aspects.RetryOnFailure;
import com.jcabi.aspects.UnitedThrow;
public class JcabiAspectJ {
public static void main(String[] args) {
try {
displayFactorial(10);
getFactorial(10).get();
String result = cacheExchangeRates();
if (result != cacheExchangeRates()) {
System.out.println(result);
}
divideByZero();
} catch(Exception e) {
e.printStackTrace();
}
divideByZeroQuietly();
try {
processFile();
} catch(Exception e) {
e.printStackTrace();
}
}
@Loggable
@Async
public static void displayFactorial(int number) {
long result = factorial(number);
System.out.println(result);
}
@Loggable
@Async
public static Future<Long> getFactorial(int number) {
Future<Long> factorialFuture = CompletableFuture.supplyAsync(() -> factorial(number));
return factorialFuture;
}
/**
* Finds factorial of a number
* @param number
* @return
*/
public static long factorial(int number) {
long result = 1;
for(int i=number;i>0;i--) {
result *= i;
}
return result;
}
@Loggable
@Cacheable(lifetime = 2, unit = TimeUnit.SECONDS)
public static String cacheExchangeRates() {
String result = null;
try {
URL exchangeRateUrl = new URL("https://api.exchangeratesapi.io/latest");
URLConnection con = exchangeRateUrl.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
result = in.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
@LogExceptions
public static void divideByZero() {
int x = 1/0;
}
@RetryOnFailure(attempts = 2, types = {java.lang.NumberFormatException.class})
@Quietly
public static void divideByZeroQuietly() {
int x = 1/0;
}
@UnitedThrow(IllegalStateException.class)
public static void processFile() throws IOException, InterruptedException {
BufferedReader reader = new BufferedReader(new FileReader("baeldung.txt"));
reader.readLine();
Thread thread = new Thread();
thread.wait(2000);
}
}

View File

@ -6,12 +6,13 @@ import org.junit.Test;
public class ProductHelperUnitTest {
ProductHelper productHelper = new ProductHelper();
@Test
public void whenInvokedGetDiscount_thenGetItFromCache() {
public void whenInvokedGetDiscountTwice_thenGetItFromCache() {
ProductHelper productHelper = new ProductHelper();
assertTrue(productHelper.getCacheMissCount() == 0);
assertTrue(productHelper.getDiscount("Sports") == 20);
assertTrue(productHelper.getDiscount("Electronics") == 0);
assertTrue(productHelper.getDiscount("Sports") == 20);
assertTrue(productHelper.getCacheMissCount() == 1);
}
}

View File

@ -6,12 +6,16 @@ import org.junit.Test;
public class ProductHelperUsingLoaderUnitTest {
ProductHelperUsingLoader productHelper = new ProductHelperUsingLoader();
@Test
public void whenInvokedGetDiscount_thenPopulateCache() {
public void whenInvokedGetDiscount_thenPopulateCacheUsingLoader() {
ProductHelperUsingLoader productHelper = new ProductHelperUsingLoader();
assertTrue(productHelper.getCacheMissCount() == 0);
assertTrue(productHelper.getDiscount("Sports") == 20);
assertTrue(productHelper.getCacheMissCount() == 1);
assertTrue(productHelper.getDiscount("Electronics") == 10);
assertTrue(productHelper.getCacheMissCount() == 2);
}
}

View File

@ -6,10 +6,9 @@ import org.junit.Test;
public class ProductHelperWithEventListenerUnitTest {
ProductHelperWithEventListener productHelper = new ProductHelperWithEventListener();
@Test
public void whenEntryAddedInCache_thenEventListenerCalled() {
ProductHelperWithEventListener productHelper = new ProductHelperWithEventListener();
assertTrue(productHelper.getDiscount("Sports") == 20);
}

View File

@ -6,13 +6,17 @@ import org.junit.Test;
public class ProductHelperWithExpiryUnitTest {
ProductHelperWithExpiry productHelper = new ProductHelperWithExpiry();
@Test
public void whenInvokedGetDiscountForExpiredProduct_thenNoDiscount() throws InterruptedException {
public void whenInvokedGetDiscountAfterExpiration_thenDiscountCalculatedAgain() throws InterruptedException {
ProductHelperWithExpiry productHelper = new ProductHelperWithExpiry();
assertTrue(productHelper.getCacheMissCount() == 0);
assertTrue(productHelper.getDiscount("Sports") == 20);
assertTrue(productHelper.getCacheMissCount() == 1);
Thread.sleep(20);
assertTrue(productHelper.getDiscount("Sports") == 0);
assertTrue(productHelper.getDiscount("Sports") == 20);
assertTrue(productHelper.getCacheMissCount() == 2);
}
}

View File

@ -0,0 +1,105 @@
package com.baeldung.moshi;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.time.Instant;
import com.squareup.moshi.FromJson;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.ToJson;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.junit.Test;
public class AlternativeAdapterUnitTest {
@Test
public void whenSerializing_thenAlternativeAdapterUsed() {
Moshi moshi = new Moshi.Builder()
.add(new EpochMillisAdapter())
.build();
JsonAdapter<Post> jsonAdapter = moshi.adapter(Post.class);
String json = jsonAdapter.toJson(new Post("Introduction to Moshi Json", "Baeldung", Instant.now()));
System.out.println(json);
}
@Test
public void whenDeserializing_thenAlternativeAdapterUsed() throws IOException {
Moshi moshi = new Moshi.Builder()
.add(new EpochMillisAdapter())
.build();
JsonAdapter<Post> jsonAdapter = moshi.adapter(Post.class);
String json = "{\"author\":\"Baeldung\",\"posted\":1582095269204,\"title\":\"Introduction to Moshi Json\"}";
Post post = jsonAdapter.fromJson(json);
System.out.println(post);
}
public static class Post {
String title;
String author;
@EpochMillis Instant posted;
public Post() {
}
public Post(String title, String author, Instant posted) {
this.title = title;
this.author = author;
this.posted = posted;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Instant getPosted() {
return posted;
}
public void setPosted(Instant posted) {
this.posted = posted;
}
@Override
public String toString() {
return new ToStringBuilder(this).append("title", title).append("author", author).append("posted", posted)
.toString();
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@JsonQualifier
public @interface EpochMillis {
}
public static class EpochMillisAdapter {
@ToJson
public Long toJson(@EpochMillis Instant input) {
return input.toEpochMilli();
}
@FromJson
@EpochMillis
public Instant fromJson(Long input) {
return Instant.ofEpochMilli(input);
}
}
}

View File

@ -0,0 +1,36 @@
package com.baeldung.moshi;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import org.junit.Test;
public class ArrayUnitTest {
@Test
public void whenSerializingList_thenJsonArrayProduced() {
Moshi moshi = new Moshi.Builder()
.build();
Type type = Types.newParameterizedType(List.class, String.class);
JsonAdapter<List<String>> jsonAdapter = moshi.adapter(type);
String json = jsonAdapter.toJson(Arrays.asList("One", "Two", "Three"));
System.out.println(json);
}
@Test
public void whenDeserializingJsonArray_thenListProduced() throws IOException {
Moshi moshi = new Moshi.Builder()
.build();
Type type = Types.newParameterizedType(List.class, String.class);
JsonAdapter<List<String>> jsonAdapter = moshi.adapter(type);
String json = "[\"One\",\"Two\",\"Three\"]";
List<String> result = jsonAdapter.fromJson(json);
System.out.println(result);
}
}

View File

@ -0,0 +1,94 @@
package com.baeldung.moshi;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import com.squareup.moshi.FromJson;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.ToJson;
import org.junit.Test;
public class ComplexAdapterUnitTest {
@Test
public void whenSerializing_thenCorrectJsonProduced() {
Moshi moshi = new Moshi.Builder()
.add(new JsonDateTimeAdapter())
.build();
JsonAdapter<ZonedDateTime> jsonAdapter = moshi.adapter(ZonedDateTime.class);
String json = jsonAdapter.toJson(ZonedDateTime.now());
System.out.println(json);
}
@Test
public void whenDeserializing_thenCorrectJsonConsumed() throws IOException {
Moshi moshi = new Moshi.Builder()
.add(new JsonDateTimeAdapter())
.build();
JsonAdapter<ZonedDateTime> jsonAdapter = moshi.adapter(ZonedDateTime.class);
String json = "{\"date\":\"2020-02-17\",\"time\":\"07:53:27.064\",\"timezone\":\"Europe/London\"}";
ZonedDateTime now = jsonAdapter.fromJson(json);
System.out.println(now);
}
public static class JsonDateTimeAdapter {
@ToJson
public JsonDateTime toJson(ZonedDateTime input) {
String date = input.toLocalDate().toString();
String time = input.toLocalTime().toString();
String timezone = input.getZone().toString();
return new JsonDateTime(date, time, timezone);
}
@FromJson
public ZonedDateTime fromJson(JsonDateTime input) {
LocalDate date = LocalDate.parse(input.getDate());
LocalTime time = LocalTime.parse(input.getTime());
ZoneId timezone = ZoneId.of(input.getTimezone());
return ZonedDateTime.of(date, time, timezone);
}
}
public static class JsonDateTime {
private String date;
private String time;
private String timezone;
public JsonDateTime() {
}
public JsonDateTime(String date, String time, String timezone) {
this.date = date;
this.time = time;
this.timezone = timezone;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getTimezone() {
return timezone;
}
public void setTimezone(String timezone) {
this.timezone = timezone;
}
}
}

View File

@ -0,0 +1,68 @@
package com.baeldung.moshi;
import java.io.IOException;
import java.time.Instant;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.junit.Test;
public class DefaultUnitTest {
@Test
public void whenDeserializing_thenFieldsGetDefaultValues() throws IOException {
Moshi moshi = new Moshi.Builder()
.build();
JsonAdapter<Post> jsonAdapter = moshi.adapter(Post.class);
String json = "{\"title\":\"My Post\"}";
Post post = jsonAdapter.fromJson(json);
System.out.println(post);
}
public static class Post {
private String title;
private String author;
private String posted;
public Post() {
posted = Instant.now().toString();
}
public Post(String title, String author, String posted) {
this.title = title;
this.author = author;
this.posted = posted;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getPosted() {
return posted;
}
public void setPosted(String posted) {
this.posted = posted;
}
@Override
public String toString() {
return new ToStringBuilder(this).append("title", title).append("author", author).append("posted", posted)
.toString();
}
}
}

View File

@ -0,0 +1,77 @@
package com.baeldung.moshi;
import java.io.IOException;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.junit.Test;
public class PrimitiveUnitTest {
@Test
public void whenSerializing_thenCorrectJsonProduced() {
Moshi moshi = new Moshi.Builder()
.build();
JsonAdapter<Post> jsonAdapter = moshi.adapter(Post.class);
Post post = new Post("My Post", "Baeldung", "This is my post");
String json = jsonAdapter.toJson(post);
System.out.println(json);
}
@Test
public void whenDeserializing_thenCorrectJsonConsumed() throws IOException {
Moshi moshi = new Moshi.Builder()
.build();
JsonAdapter<Post> jsonAdapter = moshi.adapter(Post.class);
String json = "{\"author\":\"Baeldung\",\"text\":\"This is my post\",\"title\":\"My Post\"}";
Post post = jsonAdapter.fromJson(json);
System.out.println(post);
}
public static class Post {
private String title;
private String author;
private String text;
public Post() {
}
public Post(String title, String author, String text) {
this.title = title;
this.author = author;
this.text = text;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
@Override
public String toString() {
return new ToStringBuilder(this).append("title", title).append("author", author).append("text", text)
.toString();
}
}
}

View File

@ -0,0 +1,68 @@
package com.baeldung.moshi;
import java.io.IOException;
import com.squareup.moshi.Json;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.junit.jupiter.api.Test;
public class RenameUnitTest {
@Test
public void whenSerializing_thenFieldsGetRenamed() {
Moshi moshi = new Moshi.Builder()
.build();
JsonAdapter<Post> jsonAdapter = moshi.adapter(Post.class);
Post post = new Post("My Post", "Baeldung");
String json = jsonAdapter.toJson(post);
System.out.println(json);
}
@Test
public void whenSerializing_thenRenamedFieldsGetConsumed() throws IOException {
Moshi moshi = new Moshi.Builder()
.build();
JsonAdapter<Post> jsonAdapter = moshi.adapter(Post.class);
String json = "{\"authored_by\":\"Baeldung\",\"title\":\"My Post\"}";
Post post = jsonAdapter.fromJson(json);
System.out.println(post);
}
public static class Post {
private String title;
@Json(name = "authored_by")
private String author;
public Post() {
}
public Post(String title, String author) {
this.title = title;
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return new ToStringBuilder(this).append("title", title).append("author", author).toString();
}
}
}

View File

@ -0,0 +1,129 @@
package com.baeldung.moshi;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.squareup.moshi.FromJson;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.ToJson;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.junit.Test;
public class SimpleAdapterUnitTest {
@Test
public void whenSerializing_thenAdapterUsed() {
Moshi moshi = new Moshi.Builder()
.add(new AuthorAdapter())
.build();
JsonAdapter<Post> jsonAdapter = moshi.adapter(Post.class);
Post post = new Post("My Post", new Author("Baeldung", "baeldung@example.com"), "This is my post");
String json = jsonAdapter.toJson(post);
System.out.println(json);
}
@Test
public void whenDeserializing_thenAdapterUsed() throws IOException {
Moshi moshi = new Moshi.Builder()
.add(new AuthorAdapter())
.build();
JsonAdapter<Post> jsonAdapter = moshi.adapter(Post.class);
String json = "{\"author\":\"Baeldung <baeldung@example.com>\",\"text\":\"This is my post\",\"title\":\"My Post\"}";
Post post = jsonAdapter.fromJson(json);
System.out.println(post);
}
public static class AuthorAdapter {
private Pattern pattern = Pattern.compile("^(.*) <(.*)>$");
@ToJson
public String toJson(Author author) {
return author.name + " <" + author.email + ">";
}
@FromJson
public Author fromJson(String author) {
Matcher matcher = pattern.matcher(author);
return matcher.find() ? new Author(matcher.group(1), matcher.group(2)) : null;
}
}
public static class Author {
private String name;
private String email;
public Author() {
}
public Author(String name, String email) {
this.name = name;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return new ToStringBuilder(this).append("name", name).append("email", email).toString();
}
}
public static class Post {
private String title;
private Author author;
private String text;
public Post() {
}
public Post(String title, Author author, String text) {
this.title = title;
this.author = author;
this.text = text;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
@Override
public String toString() {
return new ToStringBuilder(this).append("title", title).append("author", author).append("text", text)
.toString();
}
}
}

View File

@ -0,0 +1,66 @@
package com.baeldung.moshi;
import java.io.IOException;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.junit.jupiter.api.Test;
public class TransientUnitTest {
@Test
public void whenSerializing_thenTransientFieldIgnored() {
Moshi moshi = new Moshi.Builder()
.build();
JsonAdapter<Post> jsonAdapter = moshi.adapter(Post.class);
Post post = new Post("My Post", "Baeldung");
String json = jsonAdapter.toJson(post);
System.out.println(json);
}
@Test
public void whenDeserializing_thenTransientFieldIgnored() throws IOException {
Moshi moshi = new Moshi.Builder()
.build();
JsonAdapter<Post> jsonAdapter = moshi.adapter(Post.class);
String json = "{\"authored_by\":\"Baeldung\",\"title\":\"My Post\"}";
Post post = jsonAdapter.fromJson(json);
System.out.println(post);
}
public static class Post {
private String title;
private transient String author;
public Post() {
}
public Post(String title, String author) {
this.title = title;
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return new ToStringBuilder(this).append("title", title).append("author", author).toString();
}
}
}

View File

@ -36,12 +36,12 @@
<profiles>
<profile>
<id>default-tools.jar</id>
<id>default-profile</id>
<activation>
<property>
<name>java.vendor</name>
<value>Oracle Corporation</value>
</property>
<activeByDefault>true</activeByDefault>
<file>
<exists>${java.home}/../lib/tools.jar</exists>
</file>
</activation>
<dependencies>
<dependency>
@ -53,6 +53,24 @@
</dependency>
</dependencies>
</profile>
<profile>
<id>mac-profile</id>
<activation>
<activeByDefault>false</activeByDefault>
<file>
<exists>${java.home}/../Classes/classes.jar</exists>
</file>
</activation>
<dependencies>
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>${java.version}</version>
<scope>system</scope>
<systemPath>${java.home}/../Classes/classes.jar</systemPath>
</dependency>
</dependencies>
</profile>
</profiles>
<properties>

View File

@ -0,0 +1,43 @@
package controllers;
import play.data.Form;
import play.data.FormFactory;
import play.mvc.Controller;
import play.mvc.Result;
import javax.inject.Inject;
// Add the following to conf/routes
/*
GET /$model;format="camel"$ controllers.$model;format="Camel"$Controller.$model;format="camel"$Get
POST /$model;format="camel"$ controllers.$model;format="Camel"$Controller.$model;format="camel"$Post
*/
/**
* $model;format="Camel"$ form controller for Play Java
*/
public class $model;format="Camel"$Controller extends Controller {
private final Form<$model;format="Camel"$Data> $model;format="camel"$Form;
@Inject
public $model;format="Camel"$Controller(FormFactory formFactory) {
this.$model;format="camel"$Form = formFactory.form($model;format="Camel"$Data.class);
}
public Result $model;format="camel"$Get() {
return ok(views.html.$model;format="camel"$.form.render($model;format="camel"$Form));
}
public Result $model;format="camel"$Post() {
Form<$model;format="Camel"$Data> boundForm = $model;format="camel"$Form.bindFromRequest();
if (boundForm.hasErrors()) {
return badRequest(views.html.$model;format="camel"$.form.render(boundForm));
} else {
$model;format="Camel"$Data $model;format="camel"$ = boundForm.get();
flash("success", "$model;format="Camel"$ " + $model;format="camel"$);
return redirect(routes.$model;format="Camel"$Controller.$model;format="camel"$Get());
}
}
}

View File

@ -0,0 +1,37 @@
package controllers;
import play.data.validation.Constraints;
public class $model;format="Camel"$Data {
@Constraints.Required
private String name;
@Constraints.Required
private Integer age;
public $model;format="Camel"$Data() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return String.format("$model;format="Camel"$Data(%s, %s)", name, age);
}
}

View File

@ -0,0 +1,12 @@
@($model;format="camel"$Form: Form[$model;format="Camel"$Data])
<h1>$model;format="camel"$ form</h1>
@flash.getOrDefault("success", "")
@helper.form(action = routes.$model;format="Camel"$Controller.$model;format="camel"$Post()) {
@helper.CSRF.formField
@helper.inputText($model;format="camel"$Form("name"))
@helper.inputText($model;format="camel"$Form("age"))
<input type="submit" value="submit"/>
}

View File

@ -0,0 +1,2 @@
description = Generates a Controller with form handling
model = user

View File

@ -0,0 +1 @@
Temporary file until g8-scaffold will generate "test" directory

View File

@ -0,0 +1,50 @@
package controllers;
import org.junit.Test;
import play.Application;
import play.filters.csrf.*;
import play.inject.guice.GuiceApplicationBuilder;
import play.mvc.*;
import play.test.WithApplication;
import java.util.HashMap;
import static org.junit.Assert.assertEquals;
import static play.mvc.Http.RequestBuilder;
import static play.mvc.Http.Status.OK;
import static play.test.Helpers.*;
import static play.api.test.CSRFTokenHelper.*;
public class $model;format="Camel"$ControllerTest extends WithApplication {
@Override
protected Application provideApplication() {
return new GuiceApplicationBuilder().build();
}
@Test
public void test$model;format="Camel"$Get() {
RequestBuilder request = new RequestBuilder()
.method(GET)
.uri("/$model;format="camel"$");
Result result = route(app, request);
assertEquals(OK, result.status());
}
@Test
public void test$model;format="Camel"$Post() {
HashMap<String, String> formData = new HashMap<>();
formData.put("name", "play");
formData.put("age", "4");
RequestBuilder request = addCSRFToken(new RequestBuilder()
.header(Http.HeaderNames.HOST, "localhost")
.method(POST)
.bodyForm(formData)
.uri("/$model;format="camel"$"));
Result result = route(app, request);
assertEquals(SEE_OTHER, result.status());
}
}

8
play-framework/async-http/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
logs
target
/.idea
/.idea_modules
/.classpath
/.project
/.settings
/RUNNING_PID

View File

@ -0,0 +1,38 @@
package controllers;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;
import java.util.Collections;
import java.util.Map;
import play.mvc.Controller;
import play.mvc.Http;
import play.mvc.Result;
/**
* This controller contains an action to handle HTTP requests to the application's home page.
*/
public class HomeController extends Controller {
/**
* An action that renders an HTML page with a welcome message. The configuration in the
* <code>routes</code> file means that this method will be called when the application receives
* a
* <code>GET</code> request with a path of <code>/</code>.
*/
public Result index(Http.Request request) throws JsonProcessingException {
return ok(printStats(request));
}
private String printStats(Http.Request request) throws JsonProcessingException {
Map<String, String[]> stringMap = request.body()
.asFormUrlEncoded();
Map<String, Object> map = ImmutableMap.of(
"Result", "ok",
"GetParams", request.queryString(),
"PostParams", stringMap == null ? Collections.emptyMap() : stringMap,
"Headers", request.getHeaders().toMap()
);
return new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(map);
}
}

View File

@ -0,0 +1,5 @@
@()
@main("Welcome to Play") {
<h1>Welcome to Play!</h1>
}

View File

@ -0,0 +1,24 @@
@*
* This template is called from the `index` template. This template
* handles the rendering of the page header and body tags. It takes
* two arguments, a `String` for the title of the page and an `Html`
* object to insert into the body of the page.
*@
@(title: String)(content: Html)
<!DOCTYPE html>
<html lang="en">
<head>
@* Here's where we render the page title `String`. *@
<title>@title</title>
<link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/main.css")">
<link rel="shortcut icon" type="image/png" href="@routes.Assets.versioned("images/favicon.png")">
</head>
<body>
@* And here's where we render the `Html` object containing
* the page content. *@
@content
<script src="@routes.Assets.versioned("javascripts/main.js")" type="text/javascript"></script>
</body>
</html>

View File

@ -0,0 +1,12 @@
name := """async"""
organization := "com.example"
version := "1.0-SNAPSHOT"
lazy val root = (project in file(".")).enablePlugins(PlayJava)
scalaVersion := "2.13.1"
// comment out the original line
libraryDependencies += guice
libraryDependencies += javaWs

View File

@ -0,0 +1,11 @@
# This is the main configuration file for the application.
# https://www.playframework.com/documentation/latest/ConfigFile
play.ws.followRedirects=false
play.ws.useragent=MyPlayApplication
play.ws.compressionEnabled=true
# time to wait for the connection to be established
play.ws.timeout.connection=30.seconds
# time to wait for data after the connection is open
play.ws.timeout.idle=30.seconds
# max time available to complete the request
play.ws.timeout.request=300.seconds

View File

@ -0,0 +1,36 @@
<!-- https://www.playframework.com/documentation/latest/SettingsLogger -->
<configuration>
<conversionRule conversionWord="coloredLevel" converterClass="play.api.libs.logback.ColoredLevel" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${application.home:-.}/logs/application.log</file>
<encoder>
<pattern>%date [%level] from %logger in %thread - %message%n%xException</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%coloredLevel %logger{15} - %message%n%xException{10}</pattern>
</encoder>
</appender>
<appender name="ASYNCFILE" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE" />
</appender>
<appender name="ASYNCSTDOUT" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="STDOUT" />
</appender>
<logger name="play" level="INFO" />
<logger name="application" level="DEBUG" />
<logger name="controllers" level="DEBUG" />
<root level="WARN">
<appender-ref ref="ASYNCFILE" />
<appender-ref ref="ASYNCSTDOUT" />
</root>
</configuration>

View File

@ -0,0 +1,10 @@
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
# An example controller showing a sample home page
GET / controllers.HomeController.index(request: Request)
POST / controllers.HomeController.index(request: Request)
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)

View File

@ -0,0 +1 @@
sbt.version=1.3.3

View File

@ -0,0 +1,7 @@
// The Play plugin
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.7.3")
// Defines scaffolding (found under .g8 folder)
// http://www.foundweekends.org/giter8/scaffolding.html
// sbt "g8Scaffold form"
addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.11.0")

Binary file not shown.

After

Width:  |  Height:  |  Size: 687 B

View File

@ -0,0 +1,232 @@
package controllers;
import static java.time.temporal.ChronoUnit.SECONDS;
import static org.junit.Assert.assertEquals;
import static play.mvc.Http.Status.SERVICE_UNAVAILABLE;
import akka.Done;
import akka.actor.ActorSystem;
import akka.stream.ActorMaterializer;
import akka.stream.javadsl.Sink;
import akka.util.ByteString;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.OptionalInt;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
import org.apache.http.HttpStatus;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.Application;
import play.inject.guice.GuiceApplicationBuilder;
import play.libs.concurrent.Futures;
import play.libs.ws.WSClient;
import play.libs.ws.WSResponse;
import play.libs.ws.ahc.AhcCurlRequestLogger;
import play.mvc.Result;
import play.mvc.Results;
import play.test.WithServer;
public class HomeControllerTest extends WithServer {
private final Logger log = LoggerFactory.getLogger(HomeControllerTest.class);
private String url;
private int port;
@Override
protected Application provideApplication() {
return new GuiceApplicationBuilder().build();
}
@Before
public void setup() {
OptionalInt optHttpsPort = testServer.getRunningHttpsPort();
if (optHttpsPort.isPresent()) {
port = optHttpsPort.getAsInt();
url = "https://localhost:" + port;
} else {
port = testServer.getRunningHttpPort()
.getAsInt();
url = "http://localhost:" + port;
}
}
@Test
public void givenASingleGetRequestWhenResponseThenBlockWithCompletableAndLog()
throws Exception {
WSClient ws = play.test.WSTestClient.newClient(port);
WSResponse wsResponse = ws.url(url)
.setRequestFilter(new AhcCurlRequestLogger())
.addHeader("key", "value")
.addQueryParameter("num", "" + 1)
.get()
.toCompletableFuture()
.get();
log.debug("Thread#" + Thread.currentThread()
.getId() + " Request complete: Response code = "
+ wsResponse.getStatus()
+ " | Response: " + wsResponse.getBody() + " | Current Time:"
+ System.currentTimeMillis());
assert (HttpStatus.SC_OK == wsResponse.getStatus());
}
@Test
public void givenASingleGetRequestWhenResponseThenLog() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
WSClient ws = play.test.WSTestClient.newClient(port);
ws.url(url)
.setRequestFilter(new AhcCurlRequestLogger())
.addHeader("key", "value")
.addQueryParameter("num", "" + 1)
.get()
.thenAccept(r -> {
log.debug("Thread#" + Thread.currentThread()
.getId() + " Request complete: Response code = "
+ r.getStatus()
+ " | Response: " + r.getBody() + " | Current Time:" + System.currentTimeMillis());
latch.countDown();
});
log.debug(
"Waiting for requests to be completed. Current Time: " + System.currentTimeMillis());
latch.await(5, TimeUnit.SECONDS );
assertEquals(0, latch.getCount());
log.debug("All requests have been completed. Exiting test.");
}
@Test
public void givenASinglePostRequestWhenResponseThenLog() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
WSClient ws = play.test.WSTestClient.newClient(port);
ws.url(url)
.setContentType("application/x-www-form-urlencoded")
.post("key1=value1&key2=value2")
.thenAccept(r -> {
log.debug("Thread#" + Thread.currentThread()
.getId() + " Request complete: Response code = "
+ r.getStatus()
+ " | Response: " + r.getBody() + " | Current Time:" + System.currentTimeMillis());
latch.countDown();
});
log.debug(
"Waiting for requests to be completed. Current Time: " + System.currentTimeMillis());
latch.await(5, TimeUnit.SECONDS );
assertEquals(0, latch.getCount());
log.debug("All requests have been completed. Exiting test.");
}
@Test
public void givenMultipleRequestsWhenResponseThenLog() throws Exception {
CountDownLatch latch = new CountDownLatch(100);
WSClient ws = play.test.WSTestClient.newClient(port);
IntStream.range(0, 100)
.parallel()
.forEach(num ->
ws.url(url)
.setRequestFilter(new AhcCurlRequestLogger())
.addHeader("key", "value")
.addQueryParameter("num", "" + num)
.get()
.thenAccept(r -> {
log.debug(
"Thread#" + num + " Request complete: Response code = " + r.getStatus()
+ " | Response: " + r.getBody() + " | Current Time:"
+ System.currentTimeMillis());
latch.countDown();
})
);
log.debug(
"Waiting for requests to be completed. Current Time: " + System.currentTimeMillis());
latch.await(5, TimeUnit.SECONDS );
assertEquals(0, latch.getCount());
log.debug("All requests have been completed. Exiting test.");
}
@Test
public void givenLongResponseWhenTimeoutThenHandle() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
WSClient ws = play.test.WSTestClient.newClient(port);
Futures futures = app.injector()
.instanceOf(Futures.class);
CompletionStage<Result> f = futures.timeout(
ws.url(url)
.setRequestTimeout(Duration.of(1, SECONDS))
.get()
.thenApply(result -> {
try {
Thread.sleep(2000L);
return Results.ok();
} catch (InterruptedException e) {
return Results.status(
SERVICE_UNAVAILABLE);
}
}), 1L, TimeUnit.SECONDS
);
CompletionStage<Object> res = f.handleAsync((result, e) -> {
if (e != null) {
log.error("Exception thrown", e);
latch.countDown();
return e.getCause();
} else {
return result;
}
});
res.thenAccept(result -> assertEquals(TimeoutException.class, result));
log.debug(
"Waiting for requests to be completed. Current Time: " + System.currentTimeMillis());
latch.await(5, TimeUnit.SECONDS );
assertEquals(0, latch.getCount());
log.debug("All requests have been completed. Exiting test.");
}
@Test
public void givenMultigigabyteResponseConsumeWithStreams() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
final ActorSystem system = ActorSystem.create();
final ActorMaterializer materializer = ActorMaterializer.create(system);
final Path path = Files.createTempFile("tmp_", ".out");
WSClient ws = play.test.WSTestClient.newClient(port);
log.info("Starting test server on url: " + url);
ws.url(url)
.stream()
.thenAccept(
response -> {
try {
OutputStream outputStream = java.nio.file.Files.newOutputStream(path);
Sink<ByteString, CompletionStage<Done>> outputWriter =
Sink.foreach(bytes -> {
log.info("Reponse: " + bytes.utf8String());
outputStream.write(bytes.toArray());
});
response.getBodyAsSource()
.runWith(outputWriter, materializer);
} catch (IOException e) {
log.error("An error happened while opening the output stream", e);
}
})
.whenComplete((value, error) -> latch.countDown());
log.debug(
"Waiting for requests to be completed. Current Time: " + System.currentTimeMillis());
latch.await(5, TimeUnit.SECONDS );
assertEquals(0, latch.getCount());
log.debug("All requests have been completed. Exiting test.");
}
}

View File

@ -639,7 +639,6 @@
<module>spring-batch</module>
<module>spring-bom</module>
<module>spring-boot-modules</module>
<module>spring-boot-parent</module>
<module>spring-boot-rest</module>
<module>spring-caching</module>
@ -1141,7 +1140,6 @@
<module>spring-batch</module>
<module>spring-bom</module>
<module>spring-boot-modules</module>
<module>spring-boot-parent</module>
<module>spring-boot-rest</module>
<module>spring-caching</module>

View File

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

View File

@ -0,0 +1,21 @@
package com.baeldung.springamqp.exponentialbackoff;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.retry.RejectAndDontRequeueRecoverer;
public class ObservableRejectAndDontRequeueRecoverer extends RejectAndDontRequeueRecoverer {
private Runnable observer;
@Override
public void recover(Message message, Throwable cause) {
if(observer != null) {
observer.run();
}
super.recover(message, cause);
}
void setObserver(Runnable observer){
this.observer = observer;
}
}

View File

@ -0,0 +1,162 @@
package com.baeldung.springamqp.exponentialbackoff;
import org.aopalliance.aop.Advice;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.config.RetryInterceptorBuilder;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.interceptor.RetryOperationsInterceptor;
import com.rabbitmq.client.Channel;
@EnableRabbit
@Configuration
public class RabbitConfiguration {
private static Logger logger = LoggerFactory.getLogger(RabbitConfiguration.class);
@Bean
public ConnectionFactory connectionFactory() {
return new CachingConnectionFactory("localhost");
}
@Bean
public AmqpAdmin amqpAdmin() {
return new RabbitAdmin(connectionFactory());
}
@Bean
public RabbitTemplate rabbitTemplate() {
return new RabbitTemplate(connectionFactory());
}
@Bean
public Queue blockingQueue() {
return QueueBuilder.nonDurable("blocking-queue")
.build();
}
@Bean
public Queue nonBlockingQueue() {
return QueueBuilder.nonDurable("non-blocking-queue")
.build();
}
@Bean
public Queue retryWaitEndedQueue() {
return QueueBuilder.nonDurable("retry-wait-ended-queue")
.build();
}
@Bean
public Queue retryQueue1() {
return QueueBuilder.nonDurable("retry-queue-1")
.deadLetterExchange("")
.deadLetterRoutingKey("retry-wait-ended-queue")
.build();
}
@Bean
public Queue retryQueue2() {
return QueueBuilder.nonDurable("retry-queue-2")
.deadLetterExchange("")
.deadLetterRoutingKey("retry-wait-ended-queue")
.build();
}
@Bean
public Queue retryQueue3() {
return QueueBuilder.nonDurable("retry-queue-3")
.deadLetterExchange("")
.deadLetterRoutingKey("retry-wait-ended-queue")
.build();
}
@Bean
public RetryQueues retryQueues() {
return new RetryQueues(1000, 3.0, 10000, retryQueue1(), retryQueue2(), retryQueue3());
}
@Bean
public ObservableRejectAndDontRequeueRecoverer observableRecoverer() {
return new ObservableRejectAndDontRequeueRecoverer();
}
@Bean
public RetryOperationsInterceptor retryInterceptor() {
return RetryInterceptorBuilder.stateless()
.backOffOptions(1000, 3.0, 10000)
.maxAttempts(5)
.recoverer(observableRecoverer())
.build();
}
@Bean
public RetryQueuesInterceptor retryQueuesInterceptor(RabbitTemplate rabbitTemplate, RetryQueues retryQueues) {
return new RetryQueuesInterceptor(rabbitTemplate, retryQueues);
}
@Bean
public SimpleRabbitListenerContainerFactory defaultContainerFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
return factory;
}
@Bean
public SimpleRabbitListenerContainerFactory retryContainerFactory(ConnectionFactory connectionFactory, RetryOperationsInterceptor retryInterceptor) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
Advice[] adviceChain = { retryInterceptor };
factory.setAdviceChain(adviceChain);
return factory;
}
@Bean
public SimpleRabbitListenerContainerFactory retryQueuesContainerFactory(ConnectionFactory connectionFactory, RetryQueuesInterceptor retryInterceptor) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
Advice[] adviceChain = { retryInterceptor };
factory.setAdviceChain(adviceChain);
return factory;
}
@RabbitListener(queues = "blocking-queue", containerFactory = "retryContainerFactory")
public void consumeBlocking(String payload) throws Exception {
logger.info("Processing message from blocking-queue: {}", payload);
throw new Exception("exception occured!");
}
@RabbitListener(queues = "non-blocking-queue", containerFactory = "retryQueuesContainerFactory", ackMode = "MANUAL")
public void consumeNonBlocking(String payload) throws Exception {
logger.info("Processing message from non-blocking-queue: {}", payload);
throw new Exception("Error occured!");
}
@RabbitListener(queues = "retry-wait-ended-queue", containerFactory = "defaultContainerFactory")
public void consumeRetryWaitEndedMessage(String payload, Message message, Channel channel) throws Exception {
MessageProperties props = message.getMessageProperties();
rabbitTemplate().convertAndSend(props.getHeader("x-original-exchange"), props.getHeader("x-original-routing-key"), message);
}
}

View File

@ -0,0 +1,34 @@
package com.baeldung.springamqp.exponentialbackoff;
import org.springframework.amqp.core.Queue;
public class RetryQueues {
private Queue[] queues;
private long initialInterval;
private double factor;
private long maxWait;
public RetryQueues(long initialInterval, double factor, long maxWait, Queue... queues) {
this.queues = queues;
this.initialInterval = initialInterval;
this.factor = factor;
this.maxWait = maxWait;
}
public boolean retriesExhausted(int retry) {
return retry >= queues.length;
}
public String getQueueName(int retry) {
return queues[retry].getName();
}
public long getTimeToWait(int retry) {
double time = initialInterval * Math.pow(factor, (double) retry);
if (time > maxWait) {
return maxWait;
}
return (long) time;
}
}

View File

@ -0,0 +1,109 @@
package com.baeldung.springamqp.exponentialbackoff;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import com.rabbitmq.client.Channel;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
public class RetryQueuesInterceptor implements MethodInterceptor {
private RabbitTemplate rabbitTemplate;
private RetryQueues retryQueues;
private Runnable observer;
public RetryQueuesInterceptor(RabbitTemplate rabbitTemplate, RetryQueues retryQueues) {
this.rabbitTemplate = rabbitTemplate;
this.retryQueues = retryQueues;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
return tryConsume(invocation, this::ack, (mac, e) -> {
try {
int retryCount = tryGetRetryCountOrFail(mac, e);
sendToNextRetryQueue(mac, retryCount);
} catch (Throwable t) {
if (observer != null) {
observer.run();
}
throw new RuntimeException(t);
}
});
}
void setObserver(Runnable observer) {
this.observer = observer;
}
private Object tryConsume(MethodInvocation invocation, Consumer<MessageAndChannel> successHandler, BiConsumer<MessageAndChannel, Throwable> errorHandler) throws Throwable {
MessageAndChannel mac = new MessageAndChannel((Message) invocation.getArguments()[1], (Channel) invocation.getArguments()[0]);
Object ret = null;
try {
ret = invocation.proceed();
successHandler.accept(mac);
} catch (Throwable e) {
errorHandler.accept(mac, e);
}
return ret;
}
private void ack(MessageAndChannel mac) {
try {
mac.channel.basicAck(mac.message.getMessageProperties()
.getDeliveryTag(), false);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private int tryGetRetryCountOrFail(MessageAndChannel mac, Throwable originalError) throws Throwable {
MessageProperties props = mac.message.getMessageProperties();
String xRetriedCountHeader = (String) props.getHeader("x-retried-count");
final int xRetriedCount = xRetriedCountHeader == null ? 0 : Integer.valueOf(xRetriedCountHeader);
if (retryQueues.retriesExhausted(xRetriedCount)) {
mac.channel.basicReject(props.getDeliveryTag(), false);
throw originalError;
}
return xRetriedCount;
}
private void sendToNextRetryQueue(MessageAndChannel mac, int retryCount) throws Exception {
String retryQueueName = retryQueues.getQueueName(retryCount);
rabbitTemplate.convertAndSend(retryQueueName, mac.message, m -> {
MessageProperties props = m.getMessageProperties();
props.setExpiration(String.valueOf(retryQueues.getTimeToWait(retryCount)));
props.setHeader("x-retried-count", String.valueOf(retryCount + 1));
props.setHeader("x-original-exchange", props.getReceivedExchange());
props.setHeader("x-original-routing-key", props.getReceivedRoutingKey());
return m;
});
mac.channel.basicReject(mac.message.getMessageProperties()
.getDeliveryTag(), false);
}
private class MessageAndChannel {
private Message message;
private Channel channel;
private MessageAndChannel(Message message, Channel channel) {
this.message = message;
this.channel = channel;
}
}
}

View File

@ -0,0 +1,58 @@
package com.baeldung.springamqp.exponentialbackoff;
import java.util.concurrent.CountDownLatch;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* This live test requires:
*
* - A running RabbitMQ instance on localhost (e.g. docker run -p 5672:5672 -p 15672:15672 --name rabbit rabbitmq:3-management)
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { RabbitConfiguration.class })
public class ExponentialBackoffLiveTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private ObservableRejectAndDontRequeueRecoverer observableRecoverer;
@Autowired
private RetryQueuesInterceptor retryQueues;
@Test
public void whenSendToBlockingQueue_thenAllMessagesProcessed() throws Exception {
int nb = 2;
CountDownLatch latch = new CountDownLatch(nb);
observableRecoverer.setObserver(() -> latch.countDown());
for (int i = 1; i <= nb; i++) {
rabbitTemplate.convertAndSend("blocking-queue", "blocking message " + i);
}
latch.await();
}
@Test
public void whenSendToNonBlockingQueue_thenAllMessageProcessed() throws Exception {
int nb = 2;
CountDownLatch latch = new CountDownLatch(nb);
retryQueues.setObserver(() -> latch.countDown());
for (int i = 1; i <= nb; i++) {
rabbitTemplate.convertAndSend("non-blocking-queue", "non-blocking message " + i);
}
latch.await();
}
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include
resource="org/springframework/boot/logging/logback/base.xml" />
<logger name="org.springframework" level="INFO" />
</configuration>

View File

@ -18,6 +18,8 @@ public class App {
final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(SpringConfig.class);
context.register(SpringBatchConfig.class);
context.register(SpringBatchRetryConfig.class);
context.refresh();
// Spring xml config
@ -26,6 +28,8 @@ public class App {
runJob(context, "firstBatchJob");
runJob(context, "skippingBatchJob");
runJob(context, "skipPolicyBatchJob");
runJob(context, "retryBatchJob");
}
private static void runJob(AnnotationConfigApplicationContext context, String batchJobName) {

View File

@ -0,0 +1,117 @@
package org.baeldung.batch;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.baeldung.batch.model.Transaction;
import org.baeldung.batch.service.RecordFieldSetMapper;
import org.baeldung.batch.service.RetryItemProcessor;
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.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.batch.item.xml.StaxEventItemWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.dao.DeadlockLoserDataAccessException;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import java.text.ParseException;
@Configuration
@EnableBatchProcessing
public class SpringBatchRetryConfig {
private static final String[] tokens = { "username", "userid", "transactiondate", "amount" };
private static final int TWO_SECONDS = 2000;
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Value("input/recordRetry.csv")
private Resource inputCsv;
@Value("file:xml/retryOutput.xml")
private Resource outputXml;
public ItemReader<Transaction> itemReader(Resource inputData) throws ParseException {
DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();
tokenizer.setNames(tokens);
DefaultLineMapper<Transaction> lineMapper = new DefaultLineMapper<>();
lineMapper.setLineTokenizer(tokenizer);
lineMapper.setFieldSetMapper(new RecordFieldSetMapper());
FlatFileItemReader<Transaction> reader = new FlatFileItemReader<>();
reader.setResource(inputData);
reader.setLinesToSkip(1);
reader.setLineMapper(lineMapper);
return reader;
}
@Bean
public CloseableHttpClient closeableHttpClient() {
final RequestConfig config = RequestConfig.custom()
.setConnectTimeout(TWO_SECONDS)
.build();
return HttpClientBuilder.create().setDefaultRequestConfig(config).build();
}
@Bean
public ItemProcessor<Transaction, Transaction> retryItemProcessor() {
return new RetryItemProcessor();
}
@Bean
public ItemWriter<Transaction> itemWriter(Marshaller marshaller) {
StaxEventItemWriter<Transaction> itemWriter = new StaxEventItemWriter<>();
itemWriter.setMarshaller(marshaller);
itemWriter.setRootTagName("transactionRecord");
itemWriter.setResource(outputXml);
return itemWriter;
}
@Bean
public Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(Transaction.class);
return marshaller;
}
@Bean
public Step retryStep(@Qualifier("retryItemProcessor") ItemProcessor<Transaction, Transaction> processor,
ItemWriter<Transaction> writer) throws ParseException {
return stepBuilderFactory.get("retryStep")
.<Transaction, Transaction>chunk(10)
.reader(itemReader(inputCsv))
.processor(processor)
.writer(writer)
.faultTolerant()
.retryLimit(3)
.retry(ConnectTimeoutException.class)
.retry(DeadlockLoserDataAccessException.class)
.build();
}
@Bean(name = "retryBatchJob")
public Job retryJob(@Qualifier("retryStep") Step retryStep) {
return jobBuilderFactory
.get("retryBatchJob")
.start(retryStep)
.build();
}
}

View File

@ -9,6 +9,8 @@ import javax.xml.bind.annotation.XmlRootElement;
public class Transaction {
private String username;
private int userId;
private int age;
private String postCode;
private Date transactionDate;
private double amount;
@ -46,9 +48,25 @@ public class Transaction {
this.amount = amount;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPostCode() {
return postCode;
}
public void setPostCode(String postCode) {
this.postCode = postCode;
}
@Override
public String toString() {
return "Transaction [username=" + username + ", userId=" + userId + ", transactionDate=" + transactionDate + ", amount=" + amount + "]";
return "Transaction [username=" + username + ", userId=" + userId + ", age=" + age + ", postCode=" + postCode + ", transactionDate=" + transactionDate + ", amount=" + amount + "]";
}
}

View File

@ -0,0 +1,42 @@
package org.baeldung.batch.service;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.baeldung.batch.model.Transaction;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
public class RetryItemProcessor implements ItemProcessor<Transaction, Transaction> {
private static final Logger LOGGER = LoggerFactory.getLogger(RetryItemProcessor.class);
@Autowired
private CloseableHttpClient closeableHttpClient;
@Override
public Transaction process(Transaction transaction) throws IOException, JSONException {
LOGGER.info("Attempting to process user with id={}", transaction.getUserId());
HttpResponse response = fetchMoreUserDetails(transaction.getUserId());
//parse user's age and postCode from response and update transaction
String result = EntityUtils.toString(response.getEntity());
JSONObject userObject = new JSONObject(result);
transaction.setAge(Integer.parseInt(userObject.getString("age")));
transaction.setPostCode(userObject.getString("postCode"));
return transaction;
}
private HttpResponse fetchMoreUserDetails(int id) throws IOException {
final HttpGet request = new HttpGet("http://www.baeldung.com:81/user/" + id);
return closeableHttpClient.execute(request);
}
}

View File

@ -0,0 +1,3 @@
username, user_id, transaction_date, transaction_amount
sammy, 1234, 31/10/2015, 10000
john, 9999, 3/12/2015, 12321
1 username user_id transaction_date transaction_amount
2 sammy 1234 31/10/2015 10000
3 john 9999 3/12/2015 12321

View File

@ -54,4 +54,19 @@
</batch:tasklet>
</batch:step>
</batch:job>
<batch:job id="retryBatchJob">
<batch:step id="retryStep">
<batch:tasklet>
<batch:chunk reader="itemReader" writer="itemWriter"
processor="retryItemProcessor" commit-interval="10"
retry-limit="3">
<batch:retryable-exception-classes>
<batch:include class="org.apache.http.conn.ConnectTimeoutException"/>
<batch:include class="org.springframework.dao.DeadlockLoserDataAccessException"/>
</batch:retryable-exception-classes>
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
</beans>

View File

@ -0,0 +1,90 @@
package org.baeldung.batch;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.test.AssertFile;
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.boot.test.mock.mockito.MockBean;
import org.springframework.core.io.FileSystemResource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@RunWith(SpringRunner.class)
@SpringBatchTest
@EnableAutoConfiguration
@ContextConfiguration(classes = { SpringBatchRetryConfig.class })
public class SpringBatchRetryIntegrationTest {
private static final String TEST_OUTPUT = "xml/retryOutput.xml";
private static final String EXPECTED_OUTPUT = "src/test/resources/output/batchRetry/retryOutput.xml";
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
@MockBean
private CloseableHttpClient closeableHttpClient;
@Mock
private CloseableHttpResponse httpResponse;
@Test
public void whenEndpointAlwaysFail_thenJobFails() throws Exception {
when(closeableHttpClient.execute(any()))
.thenThrow(new ConnectTimeoutException("Endpoint is down"));
JobExecution jobExecution = jobLauncherTestUtils.launchJob(defaultJobParameters());
JobInstance actualJobInstance = jobExecution.getJobInstance();
ExitStatus actualJobExitStatus = jobExecution.getExitStatus();
assertThat(actualJobInstance.getJobName(), is("retryBatchJob"));
assertThat(actualJobExitStatus.getExitCode(), is("FAILED"));
assertThat(actualJobExitStatus.getExitDescription(), containsString("org.apache.http.conn.ConnectTimeoutException"));
}
@Test
public void whenEndpointFailsTwicePasses3rdTime_thenSuccess() throws Exception {
FileSystemResource expectedResult = new FileSystemResource(EXPECTED_OUTPUT);
FileSystemResource actualResult = new FileSystemResource(TEST_OUTPUT);
//fails for first two calls and passes third time onwards
when(httpResponse.getEntity())
.thenReturn(new StringEntity("{ \"age\":10, \"postCode\":\"430222\" }"));
when(closeableHttpClient.execute(any()))
.thenThrow(new ConnectTimeoutException("Timeout count 1"))
.thenThrow(new ConnectTimeoutException("Timeout count 2"))
.thenReturn(httpResponse);
JobExecution jobExecution = jobLauncherTestUtils.launchJob(defaultJobParameters());
JobInstance actualJobInstance = jobExecution.getJobInstance();
ExitStatus actualJobExitStatus = jobExecution.getExitStatus();
assertThat(actualJobInstance.getJobName(), is("retryBatchJob"));
assertThat(actualJobExitStatus.getExitCode(), is("COMPLETED"));
AssertFile.assertFileEquals(expectedResult, actualResult);
}
private JobParameters defaultJobParameters() {
JobParametersBuilder paramsBuilder = new JobParametersBuilder();
paramsBuilder.addString("jobID", String.valueOf(System.currentTimeMillis()));
return paramsBuilder.toJobParameters();
}
}

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><transactionRecord><transactionRecord><age>10</age><amount>10000.0</amount><postCode>430222</postCode><transactionDate>2015-10-31T00:00:00+05:30</transactionDate><userId>1234</userId><username>sammy</username></transactionRecord><transactionRecord><age>10</age><amount>12321.0</amount><postCode>430222</postCode><transactionDate>2015-12-03T00:00:00+05:30</transactionDate><userId>9999</userId><username>john</username></transactionRecord></transactionRecord>

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><transactionRecord><transactionRecord><age>10</age><amount>10000.0</amount><postCode>430222</postCode><transactionDate>2015-10-31T00:00:00+05:30</transactionDate><userId>1234</userId><username>sammy</username></transactionRecord><transactionRecord><age>10</age><amount>12321.0</amount><postCode>430222</postCode><transactionDate>2015-12-03T00:00:00+05:30</transactionDate><userId>9999</userId><username>john</username></transactionRecord></transactionRecord>

View File

@ -43,6 +43,7 @@
<module>spring-boot-mvc-2</module>
<module>spring-boot-mvc-birt</module>
<module>spring-boot-nashorn</module>
<module>spring-boot-parent</module>
<module>spring-boot-performance</module>
<module>spring-boot-properties</module>
<module>spring-boot-property-exp</module>

View File

@ -16,11 +16,12 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
public class Swagger2Config {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.basePackage("com.baeldung.swagger2boot.controller"))
.paths(PathSelectors.regex("/.*"))
.build()
.apiInfo(apiEndPointsInfo());
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(apiEndPointsInfo());
}
private ApiInfo apiEndPointsInfo() {

View File

@ -0,0 +1,39 @@
package com.baeldung.swagger2boot.controller;
import com.baeldung.swagger2boot.model.Foo;
import com.baeldung.swagger2boot.model.User;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.websocket.server.PathParam;
import static org.apache.commons.lang3.RandomStringUtils.randomNumeric;
@Controller
public class UserController {
public UserController() {
super();
} //@formatter:off
@RequestMapping(method = RequestMethod.POST, value = "/createUser", produces = "application/json; charset=UTF-8")
@ResponseStatus(HttpStatus.CREATED)
@ResponseBody
@ApiOperation(value = "Create user",
notes = "This method creates a new user")
public User createUser(@ApiParam(
name = "firstName",
type = "String",
value = "First Name of the user",
example = "Vatsal",
required = true) @RequestParam String firstName) { //@formatter:on
User user = new User(firstName);
return user;
}
}

View File

@ -0,0 +1,28 @@
package com.baeldung.swagger2boot.model;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel
public class User {
@ApiModelProperty(value = "first name of the user", name = "firstName", dataType = "String", example = "Vatsal")
String firstName;
public User() {
super();
}
public User(final String firstName) {
super();
this.firstName = firstName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}

View File

@ -10,10 +10,9 @@
<description>spring-boot-parent</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-modules</artifactId>
<groupId>com.baeldung.spring-boot-modules</groupId>
<artifactId>spring-boot-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<modules>

View File

@ -1,11 +1,11 @@
package com.baeldung.requestresponsebody;
import com.baeldung.services.ExampleService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
@ -34,4 +34,18 @@ public class ExamplePostController {
log.debug("POST received - serializing LoginForm: " + loginForm.getPassword() + " " + loginForm.getUsername());
return new ResponseTransfer("Thanks For Posting!!!");
}
@PostMapping(value = "/content", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseTransfer postResponseJsonContent(@RequestBody LoginForm loginForm) {
log.debug("POST received - serializing LoginForm: " + loginForm.getPassword() + " " + loginForm.getUsername());
return new ResponseTransfer("JSON Content!");
}
@PostMapping(value = "/content", produces = MediaType.APPLICATION_XML_VALUE)
@ResponseBody
public ResponseTransfer postResponseXmlContent(@RequestBody LoginForm loginForm) {
log.debug("POST received - serializing LoginForm: " + loginForm.getPassword() + " " + loginForm.getUsername());
return new ResponseTransfer("XML Content!");
}
}

View File

@ -1,4 +1,4 @@
package org.baeldung;
package com.baeldung;
import org.junit.Test;
import org.junit.runner.RunWith;

View File

@ -2,7 +2,7 @@
<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>
<groupId>org.baeldung.spring.cloud</groupId>
<groupId>com.baeldung.spring.cloud</groupId>
<artifactId>batch-job</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>batch-job</name>

View File

@ -1,4 +1,4 @@
package org.baeldung.spring.cloud;
package com.baeldung.spring.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

View File

@ -1,39 +1,39 @@
package org.baeldung.spring.cloud;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.StepContribution;
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.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableBatchProcessing
public class JobConfiguration {
private static final Log logger = LogFactory.getLog(JobConfiguration.class);
@Autowired
public JobBuilderFactory jobBuilderFactory;
@Autowired
public StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return jobBuilderFactory.get("job").start(stepBuilderFactory.get("jobStep1").tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
logger.info("Job was run");
return RepeatStatus.FINISHED;
}
}).build()).build();
}
}
package com.baeldung.spring.cloud;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.StepContribution;
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.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableBatchProcessing
public class JobConfiguration {
private static final Log logger = LogFactory.getLog(JobConfiguration.class);
@Autowired
public JobBuilderFactory jobBuilderFactory;
@Autowired
public StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return jobBuilderFactory.get("job").start(stepBuilderFactory.get("jobStep1").tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
logger.info("Job was run");
return RepeatStatus.FINISHED;
}
}).build()).build();
}
}

View File

@ -1,6 +1,6 @@
package org.baeldung;
package com.baeldung;
import org.baeldung.spring.cloud.JobConfiguration;
import com.baeldung.spring.cloud.JobConfiguration;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;

View File

@ -1,4 +1,4 @@
package org.baeldung.spring.cloud;
package com.baeldung.spring.cloud;
import org.junit.Test;
import org.junit.runner.RunWith;

View File

@ -1,4 +1,4 @@
package org.baeldung.spring.cloud;
package com.baeldung.spring.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

View File

@ -1,6 +1,6 @@
package org.baeldung;
package com.baeldung;
import org.baeldung.spring.cloud.DataFlowServerApplication;
import com.baeldung.spring.cloud.DataFlowServerApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;

View File

@ -1,4 +1,4 @@
package org.baeldung.spring.cloud;
package com.baeldung.spring.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

View File

@ -1,6 +1,6 @@
package org.baeldung;
package com.baeldung;
import org.baeldung.spring.cloud.DataFlowShellApplication;
import com.baeldung.spring.cloud.DataFlowShellApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;

View File

@ -1,6 +1,6 @@
package org.baeldung;
package com.baeldung;
import org.baeldung.spring.cloud.LogSinkApplication;
import com.baeldung.spring.cloud.LogSinkApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;

View File

@ -1,6 +1,6 @@
package org.baeldung;
package com.baeldung;
import org.baeldung.spring.cloud.TimeProcessorApplication;
import com.baeldung.spring.cloud.TimeProcessorApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;

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