op = new Retry<>(
+ new FindCustomer(
+ "1235",
+ new CustomerNotFoundException("not found"),
+ new CustomerNotFoundException("still not found"),
+ new CustomerNotFoundException("don't give up yet!")
+ ),
+ 5,
+ 100,
+ e -> CustomerNotFoundException.class.isAssignableFrom(e.getClass())
+);
+```
+
+Executing `op` *once* would automatically trigger at most 5 retry attempts,
+with a 100 millisecond delay between attempts, ignoring any
+`CustomerNotFoundException` thrown while trying. In this particular scenario,
+due to the configuration for `FindCustomer`, there will be 1 initial attempt
+and 3 additional retries before finally returning the desired result `12345`.
+
+If our `FindCustomer` operation were instead to throw a fatal
+`DatabaseNotFoundException`, which we were instructed not to ignore, but
+more importantly we did *not* instruct our `Retry` to ignore, then the operation
+would have failed immediately upon receiving the error, not matter how many
+attempts were left.
+
+
+
+[1] Please note that *Hystrix* is a complete implementation of the *Circuit
+Breaker* pattern, of which the *Retry* pattern can be considered a subset of.
+
+## Applicability
+Whenever an application needs to communicate with an external resource,
+particularly in a cloud environment, and if the business requirements allow it.
+
+## Presentations
+You can view Microsoft's article [here](https://docs.microsoft.com/en-us/azure/architecture/patterns/retry).
+
+## Consequences
+**Pros:**
+
+* Resiliency
+* Provides hard data on external failures
+
+**Cons:**
+
+* Complexity
+* Operations maintenance
+
+## Related Patterns
+* [Circuit Breaker](https://martinfowler.com/bliki/CircuitBreaker.html)
diff --git a/retry/etc/retry.png b/retry/etc/retry.png
new file mode 100644
index 000000000..3ef6d3800
Binary files /dev/null and b/retry/etc/retry.png differ
diff --git a/retry/pom.xml b/retry/pom.xml
new file mode 100644
index 000000000..5271f43de
--- /dev/null
+++ b/retry/pom.xml
@@ -0,0 +1,45 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.18.0-SNAPSHOT
+
+ retry
+ jar
+
+
+ junit
+ junit
+ test
+
+
+ org.hamcrest
+ hamcrest-core
+ test
+
+
+
\ No newline at end of file
diff --git a/retry/src/main/java/com/iluwatar/retry/App.java b/retry/src/main/java/com/iluwatar/retry/App.java
new file mode 100644
index 000000000..376a64fbf
--- /dev/null
+++ b/retry/src/main/java/com/iluwatar/retry/App.java
@@ -0,0 +1,105 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014-2016 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.iluwatar.retry;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Retry pattern enables applications to handle potentially recoverable failures from
+ * the environment if the business requirements and nature of the failures allow it. By retrying
+ * failed operations on external dependencies, the application may maintain stability and minimize
+ * negative impact on the user experience.
+ *
+ * In our example, we have the {@link BusinessOperation} interface as an abstraction over
+ * all operations that our application performs involving remote systems. The calling code should
+ * remain decoupled from implementations.
+ *
+ * {@link FindCustomer} is a business operation that looks up a customer's record and returns
+ * its ID. Imagine its job is performed by looking up the customer in our local database and
+ * returning its ID. We can pass {@link CustomerNotFoundException} as one of its
+ * {@link FindCustomer#FindCustomer(java.lang.String, com.iluwatar.retry.BusinessException...)
+ * constructor parameters} in order to simulate not finding the customer.
+ *
+ * Imagine that, lately, this operation has experienced intermittent failures due to some weird
+ * corruption and/or locking in the data. After retrying a few times the customer is found. The
+ * database is still, however, expected to always be available. While a definitive solution is
+ * found to the problem, our engineers advise us to retry the operation a set number
+ * of times with a set delay between retries, although not too many retries otherwise the end user
+ * will be left waiting for a long time, while delays that are too short will not allow the database
+ * to recover from the load.
+ *
+ * To keep the calling code as decoupled as possible from this workaround, we have implemented the
+ * retry mechanism as a {@link BusinessOperation} named {@link Retry}.
+ *
+ * @author George Aristy (george.aristy@gmail.com)
+ * @see Retry pattern (Microsoft Azure Docs)
+ */
+public final class App {
+ private static final Logger LOG = LoggerFactory.getLogger(App.class);
+ private static BusinessOperation op;
+
+ /**
+ * Entry point.
+ *
+ * @param args not used
+ * @throws Exception not expected
+ */
+ public static void main(String[] args) throws Exception {
+ noErrors();
+ errorNoRetry();
+ errorWithRetry();
+ }
+
+ private static void noErrors() throws Exception {
+ op = new FindCustomer("123");
+ op.perform();
+ LOG.info("Sometimes the operation executes with no errors.");
+ }
+
+ private static void errorNoRetry() throws Exception {
+ op = new FindCustomer("123", new CustomerNotFoundException("not found"));
+ try {
+ op.perform();
+ } catch (CustomerNotFoundException e) {
+ LOG.info("Yet the operation will throw an error every once in a while.");
+ }
+ }
+
+ private static void errorWithRetry() throws Exception {
+ final Retry retry = new Retry<>(
+ new FindCustomer("123", new CustomerNotFoundException("not found")),
+ 3, //3 attempts
+ 100, //100 ms delay between attempts
+ e -> CustomerNotFoundException.class.isAssignableFrom(e.getClass())
+ );
+ op = retry;
+ final String customerId = op.perform();
+ LOG.info(String.format(
+ "However, retrying the operation while ignoring a recoverable error will eventually yield "
+ + "the result %s after a number of attempts %s", customerId, retry.attempts()
+ ));
+ }
+}
diff --git a/retry/src/main/java/com/iluwatar/retry/BusinessException.java b/retry/src/main/java/com/iluwatar/retry/BusinessException.java
new file mode 100644
index 000000000..eefbf2813
--- /dev/null
+++ b/retry/src/main/java/com/iluwatar/retry/BusinessException.java
@@ -0,0 +1,46 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014-2016 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.iluwatar.retry;
+
+/**
+ * The top-most type in our exception hierarchy that signifies that an unexpected error
+ * condition occurred. Its use is reserved as a "catch-all" for cases where no other subtype
+ * captures the specificity of the error condition in question. Calling code is not expected to
+ * be able to handle this error and should be reported to the maintainers immediately.
+ *
+ * @author George Aristy (george.aristy@gmail.com)
+ */
+public class BusinessException extends Exception {
+ private static final long serialVersionUID = 6235833142062144336L;
+
+ /**
+ * Ctor
+ *
+ * @param message the error message
+ */
+ public BusinessException(String message) {
+ super(message);
+ }
+}
diff --git a/retry/src/main/java/com/iluwatar/retry/BusinessOperation.java b/retry/src/main/java/com/iluwatar/retry/BusinessOperation.java
new file mode 100644
index 000000000..aefb589c7
--- /dev/null
+++ b/retry/src/main/java/com/iluwatar/retry/BusinessOperation.java
@@ -0,0 +1,44 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014-2016 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.iluwatar.retry;
+
+/**
+ * Performs some business operation.
+ *
+ * @author George Aristy (george.aristy@gmail.com)
+ * @param the return type
+ */
+@FunctionalInterface
+public interface BusinessOperation {
+ /**
+ * Performs some business operation, returning a value {@code T} if successful, otherwise throwing
+ * an exception if an error occurs.
+ *
+ * @return the return value
+ * @throws BusinessException if the operation fails. Implementations are allowed to throw more
+ * specific subtypes depending on the error conditions
+ */
+ T perform() throws BusinessException;
+}
diff --git a/retry/src/main/java/com/iluwatar/retry/CustomerNotFoundException.java b/retry/src/main/java/com/iluwatar/retry/CustomerNotFoundException.java
new file mode 100644
index 000000000..596d8584d
--- /dev/null
+++ b/retry/src/main/java/com/iluwatar/retry/CustomerNotFoundException.java
@@ -0,0 +1,46 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014-2016 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.iluwatar.retry;
+
+/**
+ * Indicates that the customer was not found.
+ *
+ * The severity of this error is bounded by its context: was the search for the customer triggered
+ * by an input from some end user, or were the search parameters pulled from your database?
+ *
+ * @author George Aristy (george.aristy@gmail.com)
+ */
+public final class CustomerNotFoundException extends BusinessException {
+ private static final long serialVersionUID = -6972888602621778664L;
+
+ /**
+ * Ctor.
+ *
+ * @param message the error message
+ */
+ public CustomerNotFoundException(String message) {
+ super(message);
+ }
+}
diff --git a/retry/src/main/java/com/iluwatar/retry/DatabaseNotAvailableException.java b/retry/src/main/java/com/iluwatar/retry/DatabaseNotAvailableException.java
new file mode 100644
index 000000000..2a93e992d
--- /dev/null
+++ b/retry/src/main/java/com/iluwatar/retry/DatabaseNotAvailableException.java
@@ -0,0 +1,43 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014-2016 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.iluwatar.retry;
+
+/**
+ * Catastrophic error indicating that we have lost connection to our database.
+ *
+ * @author George Aristy (george.aristy@gmail.com)
+ */
+public final class DatabaseNotAvailableException extends BusinessException {
+ private static final long serialVersionUID = -3750769625095997799L;
+
+ /**
+ * Ctor.
+ *
+ * @param message the error message
+ */
+ public DatabaseNotAvailableException(String message) {
+ super(message);
+ }
+}
diff --git a/retry/src/main/java/com/iluwatar/retry/FindCustomer.java b/retry/src/main/java/com/iluwatar/retry/FindCustomer.java
new file mode 100644
index 000000000..421f450e5
--- /dev/null
+++ b/retry/src/main/java/com/iluwatar/retry/FindCustomer.java
@@ -0,0 +1,63 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014-2016 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.iluwatar.retry;
+
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Deque;
+
+/**
+ * Finds a customer, returning its ID from our records.
+ *
+ * This is an imaginary operation that, for some imagined input, returns the ID for a customer.
+ * However, this is a "flaky" operation that is supposed to fail intermittently, but for the
+ * purposes of this example it fails in a programmed way depending on the constructor parameters.
+ *
+ * @author George Aristy (george.aristy@gmail.com)
+ */
+public final class FindCustomer implements BusinessOperation {
+ private final String customerId;
+ private final Deque errors;
+
+ /**
+ * Ctor.
+ *
+ * @param customerId the final result of the remote operation
+ * @param errors the errors to throw before returning {@code customerId}
+ */
+ public FindCustomer(String customerId, BusinessException... errors) {
+ this.customerId = customerId;
+ this.errors = new ArrayDeque<>(Arrays.asList(errors));
+ }
+
+ @Override
+ public String perform() throws BusinessException {
+ if (!this.errors.isEmpty()) {
+ throw this.errors.pop();
+ }
+
+ return this.customerId;
+ }
+}
diff --git a/retry/src/main/java/com/iluwatar/retry/Retry.java b/retry/src/main/java/com/iluwatar/retry/Retry.java
new file mode 100644
index 000000000..5ed60f98b
--- /dev/null
+++ b/retry/src/main/java/com/iluwatar/retry/Retry.java
@@ -0,0 +1,110 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014-2016 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.iluwatar.retry;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Predicate;
+
+/**
+ * Decorates {@link BusinessOperation business operation} with "retry" capabilities.
+ *
+ * @author George Aristy (george.aristy@gmail.com)
+ * @param the remote op's return type
+ */
+public final class Retry implements BusinessOperation {
+ private final BusinessOperation op;
+ private final int maxAttempts;
+ private final long delay;
+ private final AtomicInteger attempts;
+ private final Predicate test;
+ private final List errors;
+
+ /**
+ * Ctor.
+ *
+ * @param op the {@link BusinessOperation} to retry
+ * @param maxAttempts number of times to retry
+ * @param delay delay (in milliseconds) between attempts
+ * @param ignoreTests tests to check whether the remote exception can be ignored. No exceptions
+ * will be ignored if no tests are given
+ */
+ @SafeVarargs
+ public Retry(
+ BusinessOperation op,
+ int maxAttempts,
+ long delay,
+ Predicate... ignoreTests
+ ) {
+ this.op = op;
+ this.maxAttempts = maxAttempts;
+ this.delay = delay;
+ this.attempts = new AtomicInteger();
+ this.test = Arrays.stream(ignoreTests).reduce(Predicate::or).orElse(e -> false);
+ this.errors = new ArrayList<>();
+ }
+
+ /**
+ * The errors encountered while retrying, in the encounter order.
+ *
+ * @return the errors encountered while retrying
+ */
+ public List errors() {
+ return Collections.unmodifiableList(this.errors);
+ }
+
+ /**
+ * The number of retries performed.
+ *
+ * @return the number of retries performed
+ */
+ public int attempts() {
+ return this.attempts.intValue();
+ }
+
+ @Override
+ public T perform() throws BusinessException {
+ do {
+ try {
+ return this.op.perform();
+ } catch (BusinessException e) {
+ this.errors.add(e);
+
+ if (this.attempts.incrementAndGet() >= this.maxAttempts || !this.test.test(e)) {
+ throw e;
+ }
+
+ try {
+ Thread.sleep(this.delay);
+ } catch (InterruptedException f) {
+ //ignore
+ }
+ }
+ } while (true);
+ }
+}
diff --git a/retry/src/test/java/com/iluwatar/retry/FindCustomerTest.java b/retry/src/test/java/com/iluwatar/retry/FindCustomerTest.java
new file mode 100644
index 000000000..634443fef
--- /dev/null
+++ b/retry/src/test/java/com/iluwatar/retry/FindCustomerTest.java
@@ -0,0 +1,86 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014-2016 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.iluwatar.retry;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link FindCustomer}.
+ *
+ * @author George Aristy (george.aristy@gmail.com)
+ */
+public class FindCustomerTest {
+ /**
+ * Returns the given result with no exceptions.
+ */
+ @Test
+ public void noExceptions() throws Exception {
+ assertThat(
+ new FindCustomer("123").perform(),
+ is("123")
+ );
+ }
+
+ /**
+ * Throws the given exception.
+ *
+ * @throws Exception the expected exception
+ */
+ @Test(expected = BusinessException.class)
+ public void oneException() throws Exception {
+ new FindCustomer("123", new BusinessException("test")).perform();
+ }
+
+ /**
+ * Should first throw the given exceptions, then return the given result.
+ *
+ * @throws Exception not an expected exception
+ */
+ @Test
+ public void resultAfterExceptions() throws Exception {
+ final BusinessOperation op = new FindCustomer(
+ "123",
+ new CustomerNotFoundException("not found"),
+ new DatabaseNotAvailableException("not available")
+ );
+ try {
+ op.perform();
+ } catch (CustomerNotFoundException e) {
+ //ignore
+ }
+ try {
+ op.perform();
+ } catch (DatabaseNotAvailableException e) {
+ //ignore
+ }
+
+ assertThat(
+ op.perform(),
+ is("123")
+ );
+ }
+}
diff --git a/retry/src/test/java/com/iluwatar/retry/RetryTest.java b/retry/src/test/java/com/iluwatar/retry/RetryTest.java
new file mode 100644
index 000000000..c66c41354
--- /dev/null
+++ b/retry/src/test/java/com/iluwatar/retry/RetryTest.java
@@ -0,0 +1,110 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014-2016 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.iluwatar.retry;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.is;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ * Unit tests for {@link Retry}.
+ *
+ * @author George Aristy (george.aristy@gmail.com)
+ */
+public class RetryTest {
+ /**
+ * Should contain all errors thrown.
+ */
+ @Test
+ public void errors() throws Exception {
+ final BusinessException e = new BusinessException("unhandled");
+ final Retry retry = new Retry<>(
+ () -> { throw e; },
+ 2,
+ 0
+ );
+ try {
+ retry.perform();
+ } catch (BusinessException ex) {
+ //ignore
+ }
+
+ assertThat(
+ retry.errors(),
+ hasItem(e)
+ );
+ }
+
+ /**
+ * No exceptions will be ignored, hence final number of attempts should be 1 even if we're asking
+ * it to attempt twice.
+ */
+ @Test
+ public void attempts() {
+ final BusinessException e = new BusinessException("unhandled");
+ final Retry retry = new Retry<>(
+ () -> { throw e; },
+ 2,
+ 0
+ );
+ try {
+ retry.perform();
+ } catch (BusinessException ex) {
+ //ignore
+ }
+
+ assertThat(
+ retry.attempts(),
+ is(1)
+ );
+ }
+
+ /**
+ * Final number of attempts should be equal to the number of attempts asked because we are
+ * asking it to ignore the exception that will be thrown.
+ */
+ @Test
+ public void ignore() throws Exception {
+ final BusinessException e = new CustomerNotFoundException("customer not found");
+ final Retry retry = new Retry<>(
+ () -> { throw e; },
+ 2,
+ 0,
+ ex -> CustomerNotFoundException.class.isAssignableFrom(ex.getClass())
+ );
+ try {
+ retry.perform();
+ } catch (BusinessException ex) {
+ //ignore
+ }
+
+ assertThat(
+ retry.attempts(),
+ is(2)
+ );
+ }
+
+}
\ No newline at end of file