mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-05-30 06:21:15 +00:00
deps: Refactor dependencies (#3224)
* remove spring dep move junit, logging, mockito under dep mgmt * upgrade anti-corruption-layer deps * async method invocation * balking, bloc * bridge to bytecode * caching * callback - cqrs * component - health check * hexagonal - metadata mapping * rest of the patterns * remove checkstyle, take spotless into use
This commit is contained in:
@@ -38,71 +38,86 @@ import com.iluwatar.commander.shippingservice.ShippingDatabase;
|
||||
import com.iluwatar.commander.shippingservice.ShippingService;
|
||||
|
||||
/**
|
||||
* The {@code AppAllCases} class tests various scenarios for the microservices involved
|
||||
* in the order placement process. This class consolidates previously separated cases
|
||||
* into a single class to manage different success and failure scenarios for each service.
|
||||
* The {@code AppAllCases} class tests various scenarios for the microservices involved in the order
|
||||
* placement process. This class consolidates previously separated cases into a single class to
|
||||
* manage different success and failure scenarios for each service.
|
||||
*
|
||||
* <p>The application consists of abstract classes {@link Database} and {@link Service} which are
|
||||
* extended by all the databases and services. Each service has a corresponding database to be
|
||||
* updated and receives requests from an external user through the {@link Commander} class. There
|
||||
* are 5 microservices:
|
||||
*
|
||||
* <p>The application consists of abstract classes {@link Database} and {@link Service}
|
||||
* which are extended by all the databases and services. Each service has a corresponding
|
||||
* database to be updated and receives requests from an external user through the
|
||||
* {@link Commander} class. There are 5 microservices:
|
||||
* <ul>
|
||||
* <li>{@link ShippingService}</li>
|
||||
* <li>{@link PaymentService}</li>
|
||||
* <li>{@link MessagingService}</li>
|
||||
* <li>{@link EmployeeHandle}</li>
|
||||
* <li>{@link QueueDatabase}</li>
|
||||
* <li>{@link ShippingService}
|
||||
* <li>{@link PaymentService}
|
||||
* <li>{@link MessagingService}
|
||||
* <li>{@link EmployeeHandle}
|
||||
* <li>{@link QueueDatabase}
|
||||
* </ul>
|
||||
*
|
||||
* <p>Retries are managed using the {@link Retry} class, ensuring idempotence by performing
|
||||
* checks before making requests to services and updating the {@link Order} class fields
|
||||
* upon request success or definitive failure.
|
||||
* <p>Retries are managed using the {@link Retry} class, ensuring idempotence by performing checks
|
||||
* before making requests to services and updating the {@link Order} class fields upon request
|
||||
* success or definitive failure.
|
||||
*
|
||||
* <p>This class tests the following scenarios:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Employee database availability and unavailability</li>
|
||||
* <li>Payment service success and failures</li>
|
||||
* <li>Messaging service database availability and unavailability</li>
|
||||
* <li>Queue database availability and unavailability</li>
|
||||
* <li>Shipping service success and failures</li>
|
||||
* <li>Employee database availability and unavailability
|
||||
* <li>Payment service success and failures
|
||||
* <li>Messaging service database availability and unavailability
|
||||
* <li>Queue database availability and unavailability
|
||||
* <li>Shipping service success and failures
|
||||
* </ul>
|
||||
*
|
||||
* <p>Each scenario is encapsulated in a corresponding method that sets up the service
|
||||
* conditions and tests the order placement process.
|
||||
* <p>Each scenario is encapsulated in a corresponding method that sets up the service conditions
|
||||
* and tests the order placement process.
|
||||
*
|
||||
* <p>The main method executes all success and failure cases to verify the application's
|
||||
* behavior under different conditions.
|
||||
* <p>The main method executes all success and failure cases to verify the application's behavior
|
||||
* under different conditions.
|
||||
*
|
||||
* <p><strong>Usage:</strong>
|
||||
* <pre>
|
||||
* {@code
|
||||
*
|
||||
* <pre>{@code
|
||||
* public static void main(String[] args) {
|
||||
* AppAllCases app = new AppAllCases();
|
||||
* app.testAllScenarios();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*/
|
||||
|
||||
public class AppAllCases {
|
||||
private static final RetryParams retryParams = RetryParams.DEFAULT;
|
||||
private static final TimeLimits timeLimits = TimeLimits.DEFAULT;
|
||||
|
||||
// Employee Database Fail Case
|
||||
void employeeDatabaseUnavailableCase() {
|
||||
var ps = new PaymentService(new PaymentDatabase(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ps =
|
||||
new PaymentService(
|
||||
new PaymentDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ss = new ShippingService(new ShippingDatabase());
|
||||
var ms = new MessagingService(new MessagingDatabase());
|
||||
var eh = new EmployeeHandle(new EmployeeDatabase(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var qdb = new QueueDatabase(new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException());
|
||||
var eh =
|
||||
new EmployeeHandle(
|
||||
new EmployeeDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var qdb =
|
||||
new QueueDatabase(
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var c = new Commander(eh, ps, ss, ms, qdb, retryParams, timeLimits);
|
||||
var user = new User("Jim", "ABCD");
|
||||
var order = new Order(user, "book", 10f);
|
||||
@@ -114,8 +129,11 @@ public class AppAllCases {
|
||||
var ps = new PaymentService(new PaymentDatabase());
|
||||
var ss = new ShippingService(new ShippingDatabase(), new ItemUnavailableException());
|
||||
var ms = new MessagingService(new MessagingDatabase());
|
||||
var eh = new EmployeeHandle(new EmployeeDatabase(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var eh =
|
||||
new EmployeeHandle(
|
||||
new EmployeeDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var qdb = new QueueDatabase();
|
||||
var c = new Commander(eh, ps, ss, ms, qdb, retryParams, timeLimits);
|
||||
var user = new User("Jim", "ABCD");
|
||||
@@ -127,10 +145,15 @@ public class AppAllCases {
|
||||
void messagingDatabaseUnavailableCasePaymentSuccess() {
|
||||
var ps = new PaymentService(new PaymentDatabase());
|
||||
var ss = new ShippingService(new ShippingDatabase());
|
||||
var ms = new MessagingService(new MessagingDatabase(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ms =
|
||||
new MessagingService(
|
||||
new MessagingDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var eh = new EmployeeHandle(new EmployeeDatabase());
|
||||
var qdb = new QueueDatabase();
|
||||
var c = new Commander(eh, ps, ss, ms, qdb, retryParams, timeLimits);
|
||||
@@ -140,19 +163,34 @@ public class AppAllCases {
|
||||
}
|
||||
|
||||
void messagingDatabaseUnavailableCasePaymentError() {
|
||||
var ps = new PaymentService(new PaymentDatabase(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ps =
|
||||
new PaymentService(
|
||||
new PaymentDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ss = new ShippingService(new ShippingDatabase());
|
||||
var ms = new MessagingService(new MessagingDatabase(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException());
|
||||
var ms =
|
||||
new MessagingService(
|
||||
new MessagingDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var eh = new EmployeeHandle(new EmployeeDatabase());
|
||||
var qdb = new QueueDatabase();
|
||||
var c = new Commander(eh, ps, ss, ms, qdb, retryParams, timeLimits);
|
||||
@@ -161,21 +199,35 @@ public class AppAllCases {
|
||||
c.placeOrder(order);
|
||||
}
|
||||
|
||||
|
||||
void messagingDatabaseUnavailableCasePaymentFailure() {
|
||||
var ps = new PaymentService(new PaymentDatabase(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ps =
|
||||
new PaymentService(
|
||||
new PaymentDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ss = new ShippingService(new ShippingDatabase());
|
||||
var ms = new MessagingService(new MessagingDatabase(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ms =
|
||||
new MessagingService(
|
||||
new MessagingDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var eh = new EmployeeHandle(new EmployeeDatabase());
|
||||
var qdb = new QueueDatabase(new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException());
|
||||
var qdb =
|
||||
new QueueDatabase(
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var c = new Commander(eh, ps, ss, ms, qdb, retryParams, timeLimits);
|
||||
var user = new User("Jim", "ABCD");
|
||||
var order = new Order(user, "book", 10f);
|
||||
@@ -184,8 +236,11 @@ public class AppAllCases {
|
||||
|
||||
// Messaging Database Success Case
|
||||
void messagingSuccessCase() {
|
||||
var ps = new PaymentService(new PaymentDatabase(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ps =
|
||||
new PaymentService(
|
||||
new PaymentDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ss = new ShippingService(new ShippingDatabase());
|
||||
var ms = new MessagingService(new MessagingDatabase(), new DatabaseUnavailableException());
|
||||
var eh = new EmployeeHandle(new EmployeeDatabase());
|
||||
@@ -198,8 +253,11 @@ public class AppAllCases {
|
||||
|
||||
// Payment Database Fail Cases
|
||||
void paymentNotPossibleCase() {
|
||||
var ps = new PaymentService(new PaymentDatabase(), new DatabaseUnavailableException(),
|
||||
new PaymentDetailsErrorException());
|
||||
var ps =
|
||||
new PaymentService(
|
||||
new PaymentDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new PaymentDetailsErrorException());
|
||||
var ss = new ShippingService(new ShippingDatabase());
|
||||
var ms = new MessagingService(new MessagingDatabase(), new DatabaseUnavailableException());
|
||||
var eh = new EmployeeHandle(new EmployeeDatabase());
|
||||
@@ -211,10 +269,15 @@ public class AppAllCases {
|
||||
}
|
||||
|
||||
void paymentDatabaseUnavailableCase() {
|
||||
var ps = new PaymentService(new PaymentDatabase(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ps =
|
||||
new PaymentService(
|
||||
new PaymentDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ss = new ShippingService(new ShippingDatabase());
|
||||
var ms = new MessagingService(new MessagingDatabase());
|
||||
var eh = new EmployeeHandle(new EmployeeDatabase());
|
||||
@@ -227,8 +290,11 @@ public class AppAllCases {
|
||||
|
||||
// Payment Database Success Case
|
||||
void paymentSuccessCase() {
|
||||
var ps = new PaymentService(new PaymentDatabase(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ps =
|
||||
new PaymentService(
|
||||
new PaymentDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ss = new ShippingService(new ShippingDatabase());
|
||||
var ms = new MessagingService(new MessagingDatabase(), new DatabaseUnavailableException());
|
||||
var eh = new EmployeeHandle(new EmployeeDatabase());
|
||||
@@ -241,16 +307,26 @@ public class AppAllCases {
|
||||
|
||||
// Queue Database Fail Cases
|
||||
void queuePaymentTaskDatabaseUnavailableCase() {
|
||||
var ps = new PaymentService(new PaymentDatabase(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ps =
|
||||
new PaymentService(
|
||||
new PaymentDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ss = new ShippingService(new ShippingDatabase());
|
||||
var ms = new MessagingService(new MessagingDatabase());
|
||||
var eh = new EmployeeHandle(new EmployeeDatabase());
|
||||
var qdb = new QueueDatabase(new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException());
|
||||
var qdb =
|
||||
new QueueDatabase(
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var c = new Commander(eh, ps, ss, ms, qdb, retryParams, timeLimits);
|
||||
var user = new User("Jim", "ABCD");
|
||||
var order = new Order(user, "book", 10f);
|
||||
@@ -260,14 +336,24 @@ public class AppAllCases {
|
||||
void queueMessageTaskDatabaseUnavailableCase() {
|
||||
var ps = new PaymentService(new PaymentDatabase());
|
||||
var ss = new ShippingService(new ShippingDatabase());
|
||||
var ms = new MessagingService(new MessagingDatabase(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ms =
|
||||
new MessagingService(
|
||||
new MessagingDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var eh = new EmployeeHandle(new EmployeeDatabase());
|
||||
var qdb = new QueueDatabase(new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException());
|
||||
var qdb =
|
||||
new QueueDatabase(
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var c = new Commander(eh, ps, ss, ms, qdb, retryParams, timeLimits);
|
||||
var user = new User("Jim", "ABCD");
|
||||
var order = new Order(user, "book", 10f);
|
||||
@@ -278,20 +364,35 @@ public class AppAllCases {
|
||||
var ps = new PaymentService(new PaymentDatabase());
|
||||
var ss = new ShippingService(new ShippingDatabase(), new ItemUnavailableException());
|
||||
var ms = new MessagingService(new MessagingDatabase());
|
||||
var eh = new EmployeeHandle(new EmployeeDatabase(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var eh =
|
||||
new EmployeeHandle(
|
||||
new EmployeeDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var qdb =
|
||||
new QueueDatabase(new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException());
|
||||
new QueueDatabase(
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var c = new Commander(eh, ps, ss, ms, qdb, retryParams, timeLimits);
|
||||
var user = new User("Jim", "ABCD");
|
||||
var order = new Order(user, "book", 10f);
|
||||
@@ -303,8 +404,11 @@ public class AppAllCases {
|
||||
var ps = new PaymentService(new PaymentDatabase());
|
||||
var ss = new ShippingService(new ShippingDatabase(), new ItemUnavailableException());
|
||||
var ms = new MessagingService(new MessagingDatabase());
|
||||
var eh = new EmployeeHandle(new EmployeeDatabase(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var eh =
|
||||
new EmployeeHandle(
|
||||
new EmployeeDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var qdb = new QueueDatabase();
|
||||
var c = new Commander(eh, ps, ss, ms, qdb, retryParams, timeLimits);
|
||||
var user = new User("Jim", "ABCD");
|
||||
@@ -327,10 +431,16 @@ public class AppAllCases {
|
||||
|
||||
void shippingDatabaseUnavailableCase() {
|
||||
var ps = new PaymentService(new PaymentDatabase());
|
||||
var ss = new ShippingService(new ShippingDatabase(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(), new DatabaseUnavailableException());
|
||||
var ss =
|
||||
new ShippingService(
|
||||
new ShippingDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var ms = new MessagingService(new MessagingDatabase());
|
||||
var eh = new EmployeeHandle(new EmployeeDatabase());
|
||||
var qdb = new QueueDatabase();
|
||||
@@ -357,8 +467,11 @@ public class AppAllCases {
|
||||
var ps = new PaymentService(new PaymentDatabase());
|
||||
var ss = new ShippingService(new ShippingDatabase(), new ItemUnavailableException());
|
||||
var ms = new MessagingService(new MessagingDatabase(), new DatabaseUnavailableException());
|
||||
var eh = new EmployeeHandle(new EmployeeDatabase(), new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var eh =
|
||||
new EmployeeHandle(
|
||||
new EmployeeDatabase(),
|
||||
new DatabaseUnavailableException(),
|
||||
new DatabaseUnavailableException());
|
||||
var qdb = new QueueDatabase();
|
||||
var c = new Commander(eh, ps, ss, ms, qdb, retryParams, timeLimits);
|
||||
var user = new User("Jim", "ABCD");
|
||||
@@ -368,6 +481,7 @@ public class AppAllCases {
|
||||
|
||||
/**
|
||||
* Program entry point.
|
||||
*
|
||||
* @param args command line arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
@@ -383,7 +497,7 @@ public class AppAllCases {
|
||||
app.messagingDatabaseUnavailableCasePaymentFailure();
|
||||
app.messagingSuccessCase();
|
||||
|
||||
//Payment Database cases
|
||||
// Payment Database cases
|
||||
app.paymentNotPossibleCase();
|
||||
app.paymentDatabaseUnavailableCase();
|
||||
app.paymentSuccessCase();
|
||||
@@ -400,4 +514,4 @@ public class AppAllCases {
|
||||
app.shippingItemNotPossibleCase();
|
||||
app.shippingSuccessCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,36 +42,34 @@ import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Commander pattern is used to handle all issues that can come up while making a
|
||||
* distributed transaction. The idea is to have a commander, which coordinates the execution of all
|
||||
* instructions and ensures proper completion using retries and taking care of idempotence. By
|
||||
* queueing instructions while they haven't been done, we can ensure a state of 'eventual
|
||||
* consistency'.</p>
|
||||
* <p>In our example, we have an e-commerce application. When the user places an order,
|
||||
* the shipping service is intimated first. If the service does not respond for some reason, the
|
||||
* order is not placed. If response is received, the commander then calls for the payment service to
|
||||
* be intimated. If this fails, the shipping still takes place (order converted to COD) and the item
|
||||
* is queued. If the queue is also found to be unavailable, the payment is noted to be not done and
|
||||
* Commander pattern is used to handle all issues that can come up while making a distributed
|
||||
* transaction. The idea is to have a commander, which coordinates the execution of all instructions
|
||||
* and ensures proper completion using retries and taking care of idempotence. By queueing
|
||||
* instructions while they haven't been done, we can ensure a state of 'eventual consistency'.
|
||||
*
|
||||
* <p>In our example, we have an e-commerce application. When the user places an order, the shipping
|
||||
* service is intimated first. If the service does not respond for some reason, the order is not
|
||||
* placed. If response is received, the commander then calls for the payment service to be
|
||||
* intimated. If this fails, the shipping still takes place (order converted to COD) and the item is
|
||||
* queued. If the queue is also found to be unavailable, the payment is noted to be not done and
|
||||
* this is added to an employee database. Three types of messages are sent to the user - one, if
|
||||
* payment succeeds; two, if payment fails definitively; and three, if payment fails in the first
|
||||
* attempt. If the message is not sent, this is also queued and is added to employee db. We also
|
||||
* have a time limit for each instruction to be completed, after which, the instruction is not
|
||||
* executed, thereby ensuring that resources are not held for too long. In the rare occasion in
|
||||
* which everything fails, an individual would have to step in to figure out how to solve the
|
||||
* issue.</p>
|
||||
* <p>We have abstract classes {@link Database} and {@link Service} which are extended
|
||||
* by all the databases and services. Each service has a database to be updated, and receives
|
||||
* request from an outside user (the {@link Commander} class here). There are 5 microservices -
|
||||
* {@link ShippingService}, {@link PaymentService}, {@link MessagingService}, {@link EmployeeHandle}
|
||||
* and a {@link QueueDatabase}. We use retries to execute any instruction using {@link Retry} class,
|
||||
* and idempotence is ensured by going through some checks before making requests to services and
|
||||
* making change in {@link Order} class fields if request succeeds or definitively fails. There is
|
||||
* a single class {@link AppAllCases} that looks at the different scenarios that may be encountered
|
||||
* during the placing of an order, including both success and failure cases for each service.</p>
|
||||
* which everything fails, an individual would have to step in to figure out how to solve the issue.
|
||||
*
|
||||
* <p>We have abstract classes {@link Database} and {@link Service} which are extended by all the
|
||||
* databases and services. Each service has a database to be updated, and receives request from an
|
||||
* outside user (the {@link Commander} class here). There are 5 microservices - {@link
|
||||
* ShippingService}, {@link PaymentService}, {@link MessagingService}, {@link EmployeeHandle} and a
|
||||
* {@link QueueDatabase}. We use retries to execute any instruction using {@link Retry} class, and
|
||||
* idempotence is ensured by going through some checks before making requests to services and making
|
||||
* change in {@link Order} class fields if request succeeds or definitively fails. There is a single
|
||||
* class {@link AppAllCases} that looks at the different scenarios that may be encountered during
|
||||
* the placing of an order, including both success and failure cases for each service.
|
||||
*/
|
||||
|
||||
public class Commander {
|
||||
|
||||
private final QueueDatabase queue;
|
||||
@@ -79,7 +77,8 @@ public class Commander {
|
||||
private final PaymentService paymentService;
|
||||
private final ShippingService shippingService;
|
||||
private final MessagingService messagingService;
|
||||
private int queueItems = 0; //keeping track here only so don't need access to queue db to get this
|
||||
private int queueItems =
|
||||
0; // keeping track here only so don't need access to queue db to get this
|
||||
private final int numOfRetries;
|
||||
private final long retryDuration;
|
||||
private final long queueTime;
|
||||
@@ -90,19 +89,24 @@ public class Commander {
|
||||
private boolean finalSiteMsgShown;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Commander.class);
|
||||
//we could also have another db where it stores all orders
|
||||
// we could also have another db where it stores all orders
|
||||
|
||||
private static final String ORDER_ID = "Order {}";
|
||||
private static final String REQUEST_ID = " request Id: {}";
|
||||
private static final String ERROR_CONNECTING_MSG_SVC =
|
||||
": Error in connecting to messaging service ";
|
||||
private static final String TRY_CONNECTING_MSG_SVC =
|
||||
": Trying to connect to messaging service..";
|
||||
": Error in connecting to messaging service ";
|
||||
private static final String TRY_CONNECTING_MSG_SVC = ": Trying to connect to messaging service..";
|
||||
|
||||
private static final String DEFAULT_EXCEPTION_MESSAGE = "An exception occurred";
|
||||
|
||||
Commander(EmployeeHandle empDb, PaymentService paymentService, ShippingService shippingService,
|
||||
MessagingService messagingService, QueueDatabase qdb, RetryParams retryParams, TimeLimits timeLimits) {
|
||||
Commander(
|
||||
EmployeeHandle empDb,
|
||||
PaymentService paymentService,
|
||||
ShippingService shippingService,
|
||||
MessagingService messagingService,
|
||||
QueueDatabase qdb,
|
||||
RetryParams retryParams,
|
||||
TimeLimits timeLimits) {
|
||||
this.paymentService = paymentService;
|
||||
this.shippingService = shippingService;
|
||||
this.messagingService = messagingService;
|
||||
@@ -124,47 +128,68 @@ public class Commander {
|
||||
|
||||
private void sendShippingRequest(Order order) {
|
||||
var list = shippingService.exceptionsList;
|
||||
Retry.Operation op = l -> {
|
||||
if (!l.isEmpty()) {
|
||||
if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
|
||||
LOG.debug(ORDER_ID + ": Error in connecting to shipping service, "
|
||||
+ "trying again..", order.id);
|
||||
} else {
|
||||
LOG.debug(ORDER_ID + ": Error in creating shipping request..", order.id);
|
||||
}
|
||||
throw l.remove(0);
|
||||
}
|
||||
String transactionId = shippingService.receiveRequest(order.item, order.user.address);
|
||||
//could save this transaction id in a db too
|
||||
LOG.info(ORDER_ID + ": Shipping placed successfully, transaction id: {}",
|
||||
order.id, transactionId);
|
||||
LOG.info("Order has been placed and will be shipped to you. Please wait while we make your"
|
||||
+ " payment... ");
|
||||
sendPaymentRequest(order);
|
||||
};
|
||||
Retry.HandleErrorIssue<Order> handleError = (o, err) -> {
|
||||
if (ShippingNotPossibleException.class.isAssignableFrom(err.getClass())) {
|
||||
LOG.info("Shipping is currently not possible to your address. We are working on the problem"
|
||||
+ " and will get back to you asap.");
|
||||
finalSiteMsgShown = true;
|
||||
LOG.info(ORDER_ID + ": Shipping not possible to address, trying to add problem "
|
||||
+ "to employee db..", order.id);
|
||||
employeeHandleIssue(o);
|
||||
} else if (ItemUnavailableException.class.isAssignableFrom(err.getClass())) {
|
||||
LOG.info("This item is currently unavailable. We will inform you as soon as the item "
|
||||
+ "becomes available again.");
|
||||
finalSiteMsgShown = true;
|
||||
LOG.info(ORDER_ID + ": Item {}" + " unavailable, trying to add "
|
||||
+ "problem to employee handle..", order.id, order.item);
|
||||
employeeHandleIssue(o);
|
||||
} else {
|
||||
LOG.info("Sorry, there was a problem in creating your order. Please try later.");
|
||||
LOG.error(ORDER_ID + ": Shipping service unavailable, order not placed..", order.id);
|
||||
finalSiteMsgShown = true;
|
||||
}
|
||||
};
|
||||
var r = new Retry<>(op, handleError, numOfRetries, retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
Retry.Operation op =
|
||||
l -> {
|
||||
if (!l.isEmpty()) {
|
||||
if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
|
||||
LOG.debug(
|
||||
ORDER_ID + ": Error in connecting to shipping service, " + "trying again..",
|
||||
order.id);
|
||||
} else {
|
||||
LOG.debug(ORDER_ID + ": Error in creating shipping request..", order.id);
|
||||
}
|
||||
throw l.remove(0);
|
||||
}
|
||||
String transactionId = shippingService.receiveRequest(order.item, order.user.address);
|
||||
// could save this transaction id in a db too
|
||||
LOG.info(
|
||||
ORDER_ID + ": Shipping placed successfully, transaction id: {}",
|
||||
order.id,
|
||||
transactionId);
|
||||
LOG.info(
|
||||
"Order has been placed and will be shipped to you. Please wait while we make your"
|
||||
+ " payment... ");
|
||||
sendPaymentRequest(order);
|
||||
};
|
||||
Retry.HandleErrorIssue<Order> handleError =
|
||||
(o, err) -> {
|
||||
if (ShippingNotPossibleException.class.isAssignableFrom(err.getClass())) {
|
||||
LOG.info(
|
||||
"Shipping is currently not possible to your address. We are working on the problem"
|
||||
+ " and will get back to you asap.");
|
||||
finalSiteMsgShown = true;
|
||||
LOG.info(
|
||||
ORDER_ID
|
||||
+ ": Shipping not possible to address, trying to add problem "
|
||||
+ "to employee db..",
|
||||
order.id);
|
||||
employeeHandleIssue(o);
|
||||
} else if (ItemUnavailableException.class.isAssignableFrom(err.getClass())) {
|
||||
LOG.info(
|
||||
"This item is currently unavailable. We will inform you as soon as the item "
|
||||
+ "becomes available again.");
|
||||
finalSiteMsgShown = true;
|
||||
LOG.info(
|
||||
ORDER_ID
|
||||
+ ": Item {}"
|
||||
+ " unavailable, trying to add "
|
||||
+ "problem to employee handle..",
|
||||
order.id,
|
||||
order.item);
|
||||
employeeHandleIssue(o);
|
||||
} else {
|
||||
LOG.info("Sorry, there was a problem in creating your order. Please try later.");
|
||||
LOG.error(ORDER_ID + ": Shipping service unavailable, order not placed..", order.id);
|
||||
finalSiteMsgShown = true;
|
||||
}
|
||||
};
|
||||
var r =
|
||||
new Retry<>(
|
||||
op,
|
||||
handleError,
|
||||
numOfRetries,
|
||||
retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
r.perform(list, order);
|
||||
}
|
||||
|
||||
@@ -174,23 +199,30 @@ public class Commander {
|
||||
order.paid = PaymentStatus.NOT_DONE;
|
||||
sendPaymentFailureMessage(order);
|
||||
LOG.error(ORDER_ID + ": Payment time for order over, failed and returning..", order.id);
|
||||
} //if succeeded or failed, would have been dequeued, no attempt to make payment
|
||||
} // if succeeded or failed, would have been dequeued, no attempt to make payment
|
||||
return;
|
||||
}
|
||||
var list = paymentService.exceptionsList;
|
||||
var t = new Thread(() -> {
|
||||
Retry.Operation op = getRetryOperation(order);
|
||||
var t =
|
||||
new Thread(
|
||||
() -> {
|
||||
Retry.Operation op = getRetryOperation(order);
|
||||
|
||||
Retry.HandleErrorIssue<Order> handleError = getRetryHandleErrorIssue(order);
|
||||
Retry.HandleErrorIssue<Order> handleError = getRetryHandleErrorIssue(order);
|
||||
|
||||
var r = new Retry<>(op, handleError, numOfRetries, retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
try {
|
||||
r.perform(list, order);
|
||||
} catch (Exception e1) {
|
||||
LOG.error(DEFAULT_EXCEPTION_MESSAGE, e1);
|
||||
}
|
||||
});
|
||||
var r =
|
||||
new Retry<>(
|
||||
op,
|
||||
handleError,
|
||||
numOfRetries,
|
||||
retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
try {
|
||||
r.perform(list, order);
|
||||
} catch (Exception e1) {
|
||||
LOG.error(DEFAULT_EXCEPTION_MESSAGE, e1);
|
||||
}
|
||||
});
|
||||
|
||||
t.start();
|
||||
}
|
||||
@@ -203,8 +235,8 @@ public class Commander {
|
||||
if (o.messageSent.equals(MessageSent.NONE_SENT)) {
|
||||
handlePaymentError(order.id, o);
|
||||
}
|
||||
if (o.paid.equals(PaymentStatus.TRYING) && System
|
||||
.currentTimeMillis() - o.createdTime < paymentTime) {
|
||||
if (o.paid.equals(PaymentStatus.TRYING)
|
||||
&& System.currentTimeMillis() - o.createdTime < paymentTime) {
|
||||
var qt = new QueueTask(o, TaskType.PAYMENT, -1);
|
||||
updateQueue(qt);
|
||||
}
|
||||
@@ -214,8 +246,9 @@ public class Commander {
|
||||
|
||||
private void handlePaymentError(String orderId, Order o) {
|
||||
if (!finalSiteMsgShown) {
|
||||
LOG.info("There was an error in payment. We are on it, and will get back to you "
|
||||
+ "asap. Don't worry, your order has been placed and will be shipped.");
|
||||
LOG.info(
|
||||
"There was an error in payment. We are on it, and will get back to you "
|
||||
+ "asap. Don't worry, your order has been placed and will be shipped.");
|
||||
finalSiteMsgShown = true;
|
||||
}
|
||||
LOG.warn(ORDER_ID + ": Payment error, going to queue..", orderId);
|
||||
@@ -224,9 +257,10 @@ public class Commander {
|
||||
|
||||
private void handlePaymentDetailsError(String orderId, Order o) {
|
||||
if (!finalSiteMsgShown) {
|
||||
LOG.info("There was an error in payment. Your account/card details "
|
||||
+ "may have been incorrect. "
|
||||
+ "Meanwhile, your order has been converted to COD and will be shipped.");
|
||||
LOG.info(
|
||||
"There was an error in payment. Your account/card details "
|
||||
+ "may have been incorrect. "
|
||||
+ "Meanwhile, your order has been converted to COD and will be shipped.");
|
||||
finalSiteMsgShown = true;
|
||||
}
|
||||
LOG.error(ORDER_ID + ": Payment details incorrect, failed..", orderId);
|
||||
@@ -238,8 +272,8 @@ public class Commander {
|
||||
return l -> {
|
||||
if (!l.isEmpty()) {
|
||||
if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
|
||||
LOG.debug(ORDER_ID + ": Error in connecting to payment service,"
|
||||
+ " trying again..", order.id);
|
||||
LOG.debug(
|
||||
ORDER_ID + ": Error in connecting to payment service," + " trying again..", order.id);
|
||||
} else {
|
||||
LOG.debug(ORDER_ID + ": Error in creating payment request..", order.id);
|
||||
}
|
||||
@@ -248,8 +282,7 @@ public class Commander {
|
||||
if (order.paid.equals(PaymentStatus.TRYING)) {
|
||||
var transactionId = paymentService.receiveRequest(order.price);
|
||||
order.paid = PaymentStatus.DONE;
|
||||
LOG.info(ORDER_ID + ": Payment successful, transaction Id: {}",
|
||||
order.id, transactionId);
|
||||
LOG.info(ORDER_ID + ": Payment successful, transaction Id: {}", order.id, transactionId);
|
||||
if (!finalSiteMsgShown) {
|
||||
LOG.info("Payment made successfully, thank you for shopping with us!!");
|
||||
finalSiteMsgShown = true;
|
||||
@@ -266,92 +299,122 @@ public class Commander {
|
||||
LOG.trace(ORDER_ID + ": Queue time for order over, failed..", qt.order.id);
|
||||
return;
|
||||
} else if (qt.taskType.equals(TaskType.PAYMENT) && !qt.order.paid.equals(PaymentStatus.TRYING)
|
||||
|| qt.taskType.equals(TaskType.MESSAGING) && (qt.messageType == 1
|
||||
&& !qt.order.messageSent.equals(MessageSent.NONE_SENT)
|
||||
|| qt.order.messageSent.equals(MessageSent.PAYMENT_FAIL)
|
||||
|| qt.order.messageSent.equals(MessageSent.PAYMENT_SUCCESSFUL))
|
||||
|| qt.taskType.equals(TaskType.MESSAGING)
|
||||
&& (qt.messageType == 1 && !qt.order.messageSent.equals(MessageSent.NONE_SENT)
|
||||
|| qt.order.messageSent.equals(MessageSent.PAYMENT_FAIL)
|
||||
|| qt.order.messageSent.equals(MessageSent.PAYMENT_SUCCESSFUL))
|
||||
|| qt.taskType.equals(TaskType.EMPLOYEE_DB) && qt.order.addedToEmployeeHandle) {
|
||||
LOG.trace(ORDER_ID + ": Not queueing task since task already done..", qt.order.id);
|
||||
return;
|
||||
}
|
||||
var list = queue.exceptionsList;
|
||||
Thread t = new Thread(() -> {
|
||||
Retry.Operation op = list1 -> {
|
||||
if (!list1.isEmpty()) {
|
||||
LOG.warn(ORDER_ID + ": Error in connecting to queue db, trying again..", qt.order.id);
|
||||
throw list1.remove(0);
|
||||
}
|
||||
queue.add(qt);
|
||||
queueItems++;
|
||||
LOG.info(ORDER_ID + ": {}" + " task enqueued..", qt.order.id, qt.getType());
|
||||
tryDoingTasksInQueue();
|
||||
};
|
||||
Retry.HandleErrorIssue<QueueTask> handleError = (qt1, err) -> {
|
||||
if (qt1.taskType.equals(TaskType.PAYMENT)) {
|
||||
qt1.order.paid = PaymentStatus.NOT_DONE;
|
||||
sendPaymentFailureMessage(qt1.order);
|
||||
LOG.error(ORDER_ID + ": Unable to enqueue payment task,"
|
||||
+ " payment failed..", qt1.order.id);
|
||||
}
|
||||
LOG.error(ORDER_ID + ": Unable to enqueue task of type {}"
|
||||
+ ", trying to add to employee handle..", qt1.order.id, qt1.getType());
|
||||
employeeHandleIssue(qt1.order);
|
||||
};
|
||||
var r = new Retry<>(op, handleError, numOfRetries, retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
try {
|
||||
r.perform(list, qt);
|
||||
} catch (Exception e1) {
|
||||
LOG.error(DEFAULT_EXCEPTION_MESSAGE, e1);
|
||||
}
|
||||
});
|
||||
Thread t =
|
||||
new Thread(
|
||||
() -> {
|
||||
Retry.Operation op =
|
||||
list1 -> {
|
||||
if (!list1.isEmpty()) {
|
||||
LOG.warn(
|
||||
ORDER_ID + ": Error in connecting to queue db, trying again..",
|
||||
qt.order.id);
|
||||
throw list1.remove(0);
|
||||
}
|
||||
queue.add(qt);
|
||||
queueItems++;
|
||||
LOG.info(ORDER_ID + ": {}" + " task enqueued..", qt.order.id, qt.getType());
|
||||
tryDoingTasksInQueue();
|
||||
};
|
||||
Retry.HandleErrorIssue<QueueTask> handleError =
|
||||
(qt1, err) -> {
|
||||
if (qt1.taskType.equals(TaskType.PAYMENT)) {
|
||||
qt1.order.paid = PaymentStatus.NOT_DONE;
|
||||
sendPaymentFailureMessage(qt1.order);
|
||||
LOG.error(
|
||||
ORDER_ID + ": Unable to enqueue payment task," + " payment failed..",
|
||||
qt1.order.id);
|
||||
}
|
||||
LOG.error(
|
||||
ORDER_ID
|
||||
+ ": Unable to enqueue task of type {}"
|
||||
+ ", trying to add to employee handle..",
|
||||
qt1.order.id,
|
||||
qt1.getType());
|
||||
employeeHandleIssue(qt1.order);
|
||||
};
|
||||
var r =
|
||||
new Retry<>(
|
||||
op,
|
||||
handleError,
|
||||
numOfRetries,
|
||||
retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
try {
|
||||
r.perform(list, qt);
|
||||
} catch (Exception e1) {
|
||||
LOG.error(DEFAULT_EXCEPTION_MESSAGE, e1);
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
|
||||
private void tryDoingTasksInQueue() { //commander controls operations done to queue
|
||||
private void tryDoingTasksInQueue() { // commander controls operations done to queue
|
||||
var list = queue.exceptionsList;
|
||||
var t2 = new Thread(() -> {
|
||||
Retry.Operation op = list1 -> {
|
||||
if (!list1.isEmpty()) {
|
||||
LOG.warn("Error in accessing queue db to do tasks, trying again..");
|
||||
throw list1.remove(0);
|
||||
}
|
||||
doTasksInQueue();
|
||||
};
|
||||
Retry.HandleErrorIssue<QueueTask> handleError = (o, err) -> {
|
||||
};
|
||||
var r = new Retry<>(op, handleError, numOfRetries, retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
try {
|
||||
r.perform(list, null);
|
||||
} catch (Exception e1) {
|
||||
LOG.error(DEFAULT_EXCEPTION_MESSAGE, e1);
|
||||
}
|
||||
});
|
||||
var t2 =
|
||||
new Thread(
|
||||
() -> {
|
||||
Retry.Operation op =
|
||||
list1 -> {
|
||||
if (!list1.isEmpty()) {
|
||||
LOG.warn("Error in accessing queue db to do tasks, trying again..");
|
||||
throw list1.remove(0);
|
||||
}
|
||||
doTasksInQueue();
|
||||
};
|
||||
Retry.HandleErrorIssue<QueueTask> handleError = (o, err) -> {};
|
||||
var r =
|
||||
new Retry<>(
|
||||
op,
|
||||
handleError,
|
||||
numOfRetries,
|
||||
retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
try {
|
||||
r.perform(list, null);
|
||||
} catch (Exception e1) {
|
||||
LOG.error(DEFAULT_EXCEPTION_MESSAGE, e1);
|
||||
}
|
||||
});
|
||||
t2.start();
|
||||
}
|
||||
|
||||
private void tryDequeue() {
|
||||
var list = queue.exceptionsList;
|
||||
var t3 = new Thread(() -> {
|
||||
Retry.Operation op = list1 -> {
|
||||
if (!list1.isEmpty()) {
|
||||
LOG.warn("Error in accessing queue db to dequeue task, trying again..");
|
||||
throw list1.remove(0);
|
||||
}
|
||||
queue.dequeue();
|
||||
queueItems--;
|
||||
};
|
||||
Retry.HandleErrorIssue<QueueTask> handleError = (o, err) -> {
|
||||
};
|
||||
var r = new Retry<>(op, handleError, numOfRetries, retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
try {
|
||||
r.perform(list, null);
|
||||
} catch (Exception e1) {
|
||||
LOG.error(DEFAULT_EXCEPTION_MESSAGE, e1);
|
||||
}
|
||||
});
|
||||
var t3 =
|
||||
new Thread(
|
||||
() -> {
|
||||
Retry.Operation op =
|
||||
list1 -> {
|
||||
if (!list1.isEmpty()) {
|
||||
LOG.warn("Error in accessing queue db to dequeue task, trying again..");
|
||||
throw list1.remove(0);
|
||||
}
|
||||
queue.dequeue();
|
||||
queueItems--;
|
||||
};
|
||||
Retry.HandleErrorIssue<QueueTask> handleError = (o, err) -> {};
|
||||
var r =
|
||||
new Retry<>(
|
||||
op,
|
||||
handleError,
|
||||
numOfRetries,
|
||||
retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
try {
|
||||
r.perform(list, null);
|
||||
} catch (Exception e1) {
|
||||
LOG.error(DEFAULT_EXCEPTION_MESSAGE, e1);
|
||||
}
|
||||
});
|
||||
t3.start();
|
||||
}
|
||||
|
||||
@@ -361,28 +424,39 @@ public class Commander {
|
||||
return;
|
||||
}
|
||||
var list = messagingService.exceptionsList;
|
||||
Thread t = new Thread(() -> {
|
||||
Retry.Operation op = handleSuccessMessageRetryOperation(order);
|
||||
Retry.HandleErrorIssue<Order> handleError = (o, err) -> handleSuccessMessageErrorIssue(order, o);
|
||||
var r = new Retry<>(op, handleError, numOfRetries, retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
try {
|
||||
r.perform(list, order);
|
||||
} catch (Exception e1) {
|
||||
LOG.error(DEFAULT_EXCEPTION_MESSAGE, e1);
|
||||
}
|
||||
});
|
||||
Thread t =
|
||||
new Thread(
|
||||
() -> {
|
||||
Retry.Operation op = handleSuccessMessageRetryOperation(order);
|
||||
Retry.HandleErrorIssue<Order> handleError =
|
||||
(o, err) -> handleSuccessMessageErrorIssue(order, o);
|
||||
var r =
|
||||
new Retry<>(
|
||||
op,
|
||||
handleError,
|
||||
numOfRetries,
|
||||
retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
try {
|
||||
r.perform(list, order);
|
||||
} catch (Exception e1) {
|
||||
LOG.error(DEFAULT_EXCEPTION_MESSAGE, e1);
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
|
||||
private void handleSuccessMessageErrorIssue(Order order, Order o) {
|
||||
if ((o.messageSent.equals(MessageSent.NONE_SENT) || o.messageSent
|
||||
.equals(MessageSent.PAYMENT_TRYING))
|
||||
if ((o.messageSent.equals(MessageSent.NONE_SENT)
|
||||
|| o.messageSent.equals(MessageSent.PAYMENT_TRYING))
|
||||
&& System.currentTimeMillis() - o.createdTime < messageTime) {
|
||||
var qt = new QueueTask(order, TaskType.MESSAGING, 2);
|
||||
updateQueue(qt);
|
||||
LOG.info(ORDER_ID + ": Error in sending Payment Success message, trying to"
|
||||
+ " queue task and add to employee handle..", order.id);
|
||||
LOG.info(
|
||||
ORDER_ID
|
||||
+ ": Error in sending Payment Success message, trying to"
|
||||
+ " queue task and add to employee handle..",
|
||||
order.id);
|
||||
employeeHandleIssue(order);
|
||||
}
|
||||
}
|
||||
@@ -391,11 +465,12 @@ public class Commander {
|
||||
return l -> {
|
||||
if (!l.isEmpty()) {
|
||||
if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
|
||||
LOG.debug(ORDER_ID + ERROR_CONNECTING_MSG_SVC
|
||||
+ "(Payment Success msg), trying again..", order.id);
|
||||
LOG.debug(
|
||||
ORDER_ID + ERROR_CONNECTING_MSG_SVC + "(Payment Success msg), trying again..",
|
||||
order.id);
|
||||
} else {
|
||||
LOG.debug(ORDER_ID + ": Error in creating Payment Success"
|
||||
+ " messaging request..", order.id);
|
||||
LOG.debug(
|
||||
ORDER_ID + ": Error in creating Payment Success" + " messaging request..", order.id);
|
||||
}
|
||||
throw l.remove(0);
|
||||
}
|
||||
@@ -403,8 +478,7 @@ public class Commander {
|
||||
&& !order.messageSent.equals(MessageSent.PAYMENT_SUCCESSFUL)) {
|
||||
var requestId = messagingService.receiveRequest(2);
|
||||
order.messageSent = MessageSent.PAYMENT_SUCCESSFUL;
|
||||
LOG.info(ORDER_ID + ": Payment Success message sent,"
|
||||
+ REQUEST_ID, order.id, requestId);
|
||||
LOG.info(ORDER_ID + ": Payment Success message sent," + REQUEST_ID, order.id, requestId);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -415,38 +489,53 @@ public class Commander {
|
||||
return;
|
||||
}
|
||||
var list = messagingService.exceptionsList;
|
||||
var t = new Thread(() -> {
|
||||
Retry.Operation op = l -> handlePaymentFailureRetryOperation(order, l);
|
||||
Retry.HandleErrorIssue<Order> handleError = (o, err) -> handlePaymentErrorIssue(order, o);
|
||||
var r = new Retry<>(op, handleError, numOfRetries, retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
try {
|
||||
r.perform(list, order);
|
||||
} catch (Exception e1) {
|
||||
LOG.error(DEFAULT_EXCEPTION_MESSAGE, e1);
|
||||
}
|
||||
});
|
||||
var t =
|
||||
new Thread(
|
||||
() -> {
|
||||
Retry.Operation op = l -> handlePaymentFailureRetryOperation(order, l);
|
||||
Retry.HandleErrorIssue<Order> handleError =
|
||||
(o, err) -> handlePaymentErrorIssue(order, o);
|
||||
var r =
|
||||
new Retry<>(
|
||||
op,
|
||||
handleError,
|
||||
numOfRetries,
|
||||
retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
try {
|
||||
r.perform(list, order);
|
||||
} catch (Exception e1) {
|
||||
LOG.error(DEFAULT_EXCEPTION_MESSAGE, e1);
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
|
||||
private void handlePaymentErrorIssue(Order order, Order o) {
|
||||
if ((o.messageSent.equals(MessageSent.NONE_SENT) || o.messageSent
|
||||
.equals(MessageSent.PAYMENT_TRYING))
|
||||
if ((o.messageSent.equals(MessageSent.NONE_SENT)
|
||||
|| o.messageSent.equals(MessageSent.PAYMENT_TRYING))
|
||||
&& System.currentTimeMillis() - o.createdTime < messageTime) {
|
||||
var qt = new QueueTask(order, TaskType.MESSAGING, 0);
|
||||
updateQueue(qt);
|
||||
LOG.warn(ORDER_ID + ": Error in sending Payment Failure message, "
|
||||
+ "trying to queue task and add to employee handle..", order.id);
|
||||
LOG.warn(
|
||||
ORDER_ID
|
||||
+ ": Error in sending Payment Failure message, "
|
||||
+ "trying to queue task and add to employee handle..",
|
||||
order.id);
|
||||
employeeHandleIssue(o);
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePaymentFailureRetryOperation(Order order, List<Exception> l) throws IndexOutOfBoundsException, DatabaseUnavailableException {
|
||||
private void handlePaymentFailureRetryOperation(Order order, List<Exception> l)
|
||||
throws IndexOutOfBoundsException, DatabaseUnavailableException {
|
||||
if (!l.isEmpty()) {
|
||||
if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
|
||||
LOG.debug(ORDER_ID + ERROR_CONNECTING_MSG_SVC + "(Payment Failure msg), trying again..", order.id);
|
||||
LOG.debug(
|
||||
ORDER_ID + ERROR_CONNECTING_MSG_SVC + "(Payment Failure msg), trying again..",
|
||||
order.id);
|
||||
} else {
|
||||
LOG.debug(ORDER_ID + ": Error in creating Payment Failure" + " message request..", order.id);
|
||||
LOG.debug(
|
||||
ORDER_ID + ": Error in creating Payment Failure" + " message request..", order.id);
|
||||
}
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
@@ -454,8 +543,10 @@ public class Commander {
|
||||
&& !order.messageSent.equals(MessageSent.PAYMENT_SUCCESSFUL)) {
|
||||
var requestId = messagingService.receiveRequest(0);
|
||||
order.messageSent = MessageSent.PAYMENT_FAIL;
|
||||
LOG.info(ORDER_ID + ": Payment Failure message sent successfully,"
|
||||
+ REQUEST_ID, order.id, requestId);
|
||||
LOG.info(
|
||||
ORDER_ID + ": Payment Failure message sent successfully," + REQUEST_ID,
|
||||
order.id,
|
||||
requestId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,28 +556,37 @@ public class Commander {
|
||||
return;
|
||||
}
|
||||
var list = messagingService.exceptionsList;
|
||||
var t = new Thread(() -> {
|
||||
Retry.Operation op = l -> handlePaymentPossibleErrorMsgRetryOperation(order, l);
|
||||
Retry.HandleErrorIssue<Order> handleError = (o, err) -> handlePaymentPossibleErrorMsgErrorIssue(order, o);
|
||||
var r = new Retry<>(op, handleError, numOfRetries, retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
try {
|
||||
r.perform(list, order);
|
||||
} catch (Exception e1) {
|
||||
LOG.error(DEFAULT_EXCEPTION_MESSAGE, e1);
|
||||
}
|
||||
});
|
||||
var t =
|
||||
new Thread(
|
||||
() -> {
|
||||
Retry.Operation op = l -> handlePaymentPossibleErrorMsgRetryOperation(order, l);
|
||||
Retry.HandleErrorIssue<Order> handleError =
|
||||
(o, err) -> handlePaymentPossibleErrorMsgErrorIssue(order, o);
|
||||
var r =
|
||||
new Retry<>(
|
||||
op,
|
||||
handleError,
|
||||
numOfRetries,
|
||||
retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
try {
|
||||
r.perform(list, order);
|
||||
} catch (Exception e1) {
|
||||
LOG.error(DEFAULT_EXCEPTION_MESSAGE, e1);
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
|
||||
private void handlePaymentPossibleErrorMsgErrorIssue(Order order, Order o) {
|
||||
if (o.messageSent.equals(MessageSent.NONE_SENT) && order.paid
|
||||
.equals(PaymentStatus.TRYING)
|
||||
if (o.messageSent.equals(MessageSent.NONE_SENT)
|
||||
&& order.paid.equals(PaymentStatus.TRYING)
|
||||
&& System.currentTimeMillis() - o.createdTime < messageTime) {
|
||||
var qt = new QueueTask(order, TaskType.MESSAGING, 1);
|
||||
updateQueue(qt);
|
||||
LOG.warn("Order {}: Error in sending Payment Error message, trying to queue task and add to employee handle..",
|
||||
order.id);
|
||||
LOG.warn(
|
||||
"Order {}: Error in sending Payment Error message, trying to queue task and add to employee handle..",
|
||||
order.id);
|
||||
employeeHandleIssue(o);
|
||||
}
|
||||
}
|
||||
@@ -495,20 +595,22 @@ public class Commander {
|
||||
throws IndexOutOfBoundsException, DatabaseUnavailableException {
|
||||
if (!l.isEmpty()) {
|
||||
if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
|
||||
LOG.debug(ORDER_ID + ERROR_CONNECTING_MSG_SVC
|
||||
+ "(Payment Error msg), trying again..", order.id);
|
||||
LOG.debug(
|
||||
ORDER_ID + ERROR_CONNECTING_MSG_SVC + "(Payment Error msg), trying again..", order.id);
|
||||
} else {
|
||||
LOG.debug(ORDER_ID + ": Error in creating Payment Error"
|
||||
+ " messaging request..", order.id);
|
||||
LOG.debug(
|
||||
ORDER_ID + ": Error in creating Payment Error" + " messaging request..", order.id);
|
||||
}
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
if (order.paid.equals(PaymentStatus.TRYING) && order.messageSent
|
||||
.equals(MessageSent.NONE_SENT)) {
|
||||
if (order.paid.equals(PaymentStatus.TRYING)
|
||||
&& order.messageSent.equals(MessageSent.NONE_SENT)) {
|
||||
var requestId = messagingService.receiveRequest(1);
|
||||
order.messageSent = MessageSent.PAYMENT_TRYING;
|
||||
LOG.info(ORDER_ID + ": Payment Error message sent successfully,"
|
||||
+ REQUEST_ID, order.id, requestId);
|
||||
LOG.info(
|
||||
ORDER_ID + ": Payment Error message sent successfully," + REQUEST_ID,
|
||||
order.id,
|
||||
requestId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,51 +620,70 @@ public class Commander {
|
||||
return;
|
||||
}
|
||||
var list = employeeDb.exceptionsList;
|
||||
var t = new Thread(() -> {
|
||||
Retry.Operation op = l -> {
|
||||
if (!l.isEmpty()) {
|
||||
LOG.warn(ORDER_ID + ": Error in connecting to employee handle,"
|
||||
+ " trying again..", order.id);
|
||||
throw l.remove(0);
|
||||
}
|
||||
if (!order.addedToEmployeeHandle) {
|
||||
employeeDb.receiveRequest(order);
|
||||
order.addedToEmployeeHandle = true;
|
||||
LOG.info(ORDER_ID + ": Added order to employee database", order.id);
|
||||
}
|
||||
};
|
||||
Retry.HandleErrorIssue<Order> handleError = (o, err) -> {
|
||||
if (!o.addedToEmployeeHandle && System
|
||||
.currentTimeMillis() - order.createdTime < employeeTime) {
|
||||
var qt = new QueueTask(order, TaskType.EMPLOYEE_DB, -1);
|
||||
updateQueue(qt);
|
||||
LOG.warn(ORDER_ID + ": Error in adding to employee db,"
|
||||
+ " trying to queue task..", order.id);
|
||||
}
|
||||
};
|
||||
var r = new Retry<>(op, handleError, numOfRetries, retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
try {
|
||||
r.perform(list, order);
|
||||
} catch (Exception e1) {
|
||||
LOG.error(DEFAULT_EXCEPTION_MESSAGE, e1);
|
||||
}
|
||||
});
|
||||
var t =
|
||||
new Thread(
|
||||
() -> {
|
||||
Retry.Operation op =
|
||||
l -> {
|
||||
if (!l.isEmpty()) {
|
||||
LOG.warn(
|
||||
ORDER_ID
|
||||
+ ": Error in connecting to employee handle,"
|
||||
+ " trying again..",
|
||||
order.id);
|
||||
throw l.remove(0);
|
||||
}
|
||||
if (!order.addedToEmployeeHandle) {
|
||||
employeeDb.receiveRequest(order);
|
||||
order.addedToEmployeeHandle = true;
|
||||
LOG.info(ORDER_ID + ": Added order to employee database", order.id);
|
||||
}
|
||||
};
|
||||
Retry.HandleErrorIssue<Order> handleError =
|
||||
(o, err) -> {
|
||||
if (!o.addedToEmployeeHandle
|
||||
&& System.currentTimeMillis() - order.createdTime < employeeTime) {
|
||||
var qt = new QueueTask(order, TaskType.EMPLOYEE_DB, -1);
|
||||
updateQueue(qt);
|
||||
LOG.warn(
|
||||
ORDER_ID
|
||||
+ ": Error in adding to employee db,"
|
||||
+ " trying to queue task..",
|
||||
order.id);
|
||||
}
|
||||
};
|
||||
var r =
|
||||
new Retry<>(
|
||||
op,
|
||||
handleError,
|
||||
numOfRetries,
|
||||
retryDuration,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
try {
|
||||
r.perform(list, order);
|
||||
} catch (Exception e1) {
|
||||
LOG.error(DEFAULT_EXCEPTION_MESSAGE, e1);
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
|
||||
private void doTasksInQueue() throws IsEmptyException, InterruptedException {
|
||||
if (queueItems != 0) {
|
||||
var qt = queue.peek(); //this should probably be cloned here
|
||||
//this is why we have retry for doTasksInQueue
|
||||
var qt = queue.peek(); // this should probably be cloned here
|
||||
// this is why we have retry for doTasksInQueue
|
||||
LOG.trace(ORDER_ID + ": Started doing task of type {}", qt.order.id, qt.getType());
|
||||
if (qt.isFirstAttempt()) {
|
||||
qt.setFirstAttemptTime(System.currentTimeMillis());
|
||||
}
|
||||
if (System.currentTimeMillis() - qt.getFirstAttemptTime() >= queueTaskTime) {
|
||||
tryDequeue();
|
||||
LOG.trace(ORDER_ID + ": This queue task of type {}"
|
||||
+ " does not need to be done anymore (timeout), dequeue..", qt.order.id, qt.getType());
|
||||
LOG.trace(
|
||||
ORDER_ID
|
||||
+ ": This queue task of type {}"
|
||||
+ " does not need to be done anymore (timeout), dequeue..",
|
||||
qt.order.id,
|
||||
qt.getType());
|
||||
} else {
|
||||
switch (qt.taskType) {
|
||||
case PAYMENT -> doPaymentTask(qt);
|
||||
@@ -583,8 +704,7 @@ public class Commander {
|
||||
private void doEmployeeDbTask(QueueTask qt) {
|
||||
if (qt.order.addedToEmployeeHandle) {
|
||||
tryDequeue();
|
||||
LOG.trace(ORDER_ID + ": This employee handle task already done,"
|
||||
+ " dequeue..", qt.order.id);
|
||||
LOG.trace(ORDER_ID + ": This employee handle task already done," + " dequeue..", qt.order.id);
|
||||
} else {
|
||||
employeeHandleIssue(qt.order);
|
||||
LOG.debug(ORDER_ID + ": Trying to connect to employee handle..", qt.order.id);
|
||||
@@ -596,11 +716,12 @@ public class Commander {
|
||||
|| qt.order.messageSent.equals(MessageSent.PAYMENT_SUCCESSFUL)) {
|
||||
tryDequeue();
|
||||
LOG.trace(ORDER_ID + ": This messaging task already done, dequeue..", qt.order.id);
|
||||
} else if (qt.messageType == 1 && (!qt.order.messageSent.equals(MessageSent.NONE_SENT)
|
||||
|| !qt.order.paid.equals(PaymentStatus.TRYING))) {
|
||||
} else if (qt.messageType == 1
|
||||
&& (!qt.order.messageSent.equals(MessageSent.NONE_SENT)
|
||||
|| !qt.order.paid.equals(PaymentStatus.TRYING))) {
|
||||
tryDequeue();
|
||||
LOG.trace(ORDER_ID + ": This messaging task does not need to be done,"
|
||||
+ " dequeue..", qt.order.id);
|
||||
LOG.trace(
|
||||
ORDER_ID + ": This messaging task does not need to be done," + " dequeue..", qt.order.id);
|
||||
} else if (qt.messageType == 0) {
|
||||
sendPaymentFailureMessage(qt.order);
|
||||
LOG.debug(ORDER_ID + TRY_CONNECTING_MSG_SVC, qt.order.id);
|
||||
@@ -622,5 +743,4 @@ public class Commander {
|
||||
LOG.debug(ORDER_ID + ": Trying to connect to payment service..", qt.order.id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import com.iluwatar.commander.exceptions.DatabaseUnavailableException;
|
||||
*
|
||||
* @param <T> T is the type of object being held by database.
|
||||
*/
|
||||
|
||||
public abstract class Database<T> {
|
||||
public abstract T add(T obj) throws DatabaseUnavailableException;
|
||||
|
||||
|
||||
@@ -28,11 +28,8 @@ import java.security.SecureRandom;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Order class holds details of the order.
|
||||
*/
|
||||
|
||||
public class Order { //can store all transactions ids also
|
||||
/** Order class holds details of the order. */
|
||||
public class Order { // can store all transactions ids also
|
||||
|
||||
enum PaymentStatus {
|
||||
NOT_DONE,
|
||||
@@ -56,8 +53,8 @@ public class Order { //can store all transactions ids also
|
||||
private static final String ALL_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
private static final Map<String, Boolean> USED_IDS = new HashMap<>();
|
||||
PaymentStatus paid;
|
||||
MessageSent messageSent; //to avoid sending error msg on page and text more than once
|
||||
boolean addedToEmployeeHandle; //to avoid creating more to enqueue
|
||||
MessageSent messageSent; // to avoid sending error msg on page and text more than once
|
||||
boolean addedToEmployeeHandle; // to avoid creating more to enqueue
|
||||
|
||||
Order(User user, String item, float price) {
|
||||
this.createdTime = System.currentTimeMillis();
|
||||
@@ -85,5 +82,4 @@ public class Order { //can store all transactions ids also
|
||||
}
|
||||
return random.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,13 +36,9 @@ import java.util.function.Predicate;
|
||||
*
|
||||
* @param <T> is the type of object passed into HandleErrorIssue as a parameter.
|
||||
*/
|
||||
|
||||
public class Retry<T> {
|
||||
|
||||
/**
|
||||
* Operation Interface will define method to be implemented.
|
||||
*/
|
||||
|
||||
/** Operation Interface will define method to be implemented. */
|
||||
public interface Operation {
|
||||
void operation(List<Exception> list) throws Exception;
|
||||
}
|
||||
@@ -52,7 +48,6 @@ public class Retry<T> {
|
||||
*
|
||||
* @param <T> is the type of object to be passed into the method as parameter.
|
||||
*/
|
||||
|
||||
public interface HandleErrorIssue<T> {
|
||||
void handleIssue(T obj, Exception e);
|
||||
}
|
||||
@@ -67,8 +62,12 @@ public class Retry<T> {
|
||||
private final Predicate<Exception> test;
|
||||
private final List<Exception> errors;
|
||||
|
||||
Retry(Operation op, HandleErrorIssue<T> handleError, int maxAttempts,
|
||||
long maxDelay, Predicate<Exception>... ignoreTests) {
|
||||
Retry(
|
||||
Operation op,
|
||||
HandleErrorIssue<T> handleError,
|
||||
int maxAttempts,
|
||||
long maxDelay,
|
||||
Predicate<Exception>... ignoreTests) {
|
||||
this.op = op;
|
||||
this.handleError = handleError;
|
||||
this.maxAttempts = maxAttempts;
|
||||
@@ -82,9 +81,8 @@ public class Retry<T> {
|
||||
* Performing the operation with retries.
|
||||
*
|
||||
* @param list is the exception list
|
||||
* @param obj is the parameter to be passed into handleIsuue method
|
||||
* @param obj is the parameter to be passed into handleIsuue method
|
||||
*/
|
||||
|
||||
public void perform(List<Exception> list, T obj) {
|
||||
do {
|
||||
try {
|
||||
@@ -94,7 +92,7 @@ public class Retry<T> {
|
||||
this.errors.add(e);
|
||||
if (this.attempts.incrementAndGet() >= this.maxAttempts || !this.test.test(e)) {
|
||||
this.handleError.handleIssue(obj, e);
|
||||
return; //return here... don't go further
|
||||
return; // return here... don't go further
|
||||
}
|
||||
try {
|
||||
long testDelay =
|
||||
@@ -102,10 +100,9 @@ public class Retry<T> {
|
||||
long delay = Math.min(testDelay, this.maxDelay);
|
||||
Thread.sleep(delay);
|
||||
} catch (InterruptedException f) {
|
||||
//ignore
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,9 +26,10 @@ package com.iluwatar.commander;
|
||||
|
||||
/**
|
||||
* Record to hold the parameters related to retries.
|
||||
*
|
||||
* @param numOfRetries number of retries
|
||||
* @param retryDuration retry duration
|
||||
*/
|
||||
public record RetryParams(int numOfRetries, long retryDuration) {
|
||||
public static final RetryParams DEFAULT = new RetryParams(3, 30000L);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ import java.util.List;
|
||||
* for the transactions/requests, which are then sent back. These could be stored by the {@link
|
||||
* Commander} class in a separate database for reference (though we are not doing that here).
|
||||
*/
|
||||
|
||||
public abstract class Service {
|
||||
|
||||
protected final Database database;
|
||||
|
||||
@@ -25,16 +25,17 @@
|
||||
package com.iluwatar.commander;
|
||||
|
||||
/**
|
||||
* Record to hold parameters related to time limit
|
||||
* for various tasks.
|
||||
* Record to hold parameters related to time limit for various tasks.
|
||||
*
|
||||
* @param queueTime time limit for queue
|
||||
* @param queueTaskTime time limit for queuing task
|
||||
* @param paymentTime time limit for payment error message
|
||||
* @param messageTime time limit for message time order
|
||||
* @param employeeTime time limit for employee handle time
|
||||
*/
|
||||
public record TimeLimits(long queueTime, long queueTaskTime, long paymentTime,
|
||||
long messageTime, long employeeTime) {
|
||||
public record TimeLimits(
|
||||
long queueTime, long queueTaskTime, long paymentTime, long messageTime, long employeeTime) {
|
||||
|
||||
public static final TimeLimits DEFAULT = new TimeLimits(240000L, 60000L, 120000L, 150000L, 240000L);
|
||||
}
|
||||
public static final TimeLimits DEFAULT =
|
||||
new TimeLimits(240000L, 60000L, 120000L, 150000L, 240000L);
|
||||
}
|
||||
|
||||
@@ -26,9 +26,7 @@ package com.iluwatar.commander;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* User class contains details of user who places order.
|
||||
*/
|
||||
/** User class contains details of user who places order. */
|
||||
@AllArgsConstructor
|
||||
public class User {
|
||||
String name;
|
||||
|
||||
@@ -30,10 +30,7 @@ import com.iluwatar.commander.exceptions.DatabaseUnavailableException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The Employee Database is where orders which have encountered some issue(s) are added.
|
||||
*/
|
||||
|
||||
/** The Employee Database is where orders which have encountered some issue(s) are added. */
|
||||
public class EmployeeDatabase extends Database<Order> {
|
||||
private final Map<String, Order> data = new HashMap<>();
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ import com.iluwatar.commander.exceptions.DatabaseUnavailableException;
|
||||
* The EmployeeHandle class is the middle-man between {@link com.iluwatar.commander.Commander} and
|
||||
* {@link EmployeeDatabase}.
|
||||
*/
|
||||
|
||||
public class EmployeeHandle extends Service {
|
||||
|
||||
public EmployeeHandle(EmployeeDatabase db, Exception... exc) {
|
||||
@@ -47,9 +46,8 @@ public class EmployeeHandle extends Service {
|
||||
var o = (Order) parameters[0];
|
||||
if (database.get(o.id) == null) {
|
||||
database.add(o);
|
||||
return o.id; //true rcvd - change addedToEmployeeHandle to true else don't do anything
|
||||
return o.id; // true rcvd - change addedToEmployeeHandle to true else don't do anything
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-1
@@ -28,7 +28,6 @@ package com.iluwatar.commander.exceptions;
|
||||
* DatabaseUnavailableException is thrown when database is unavailable and nothing can be added or
|
||||
* retrieved.
|
||||
*/
|
||||
|
||||
public class DatabaseUnavailableException extends Exception {
|
||||
private static final long serialVersionUID = 2459603L;
|
||||
}
|
||||
|
||||
@@ -24,10 +24,7 @@
|
||||
*/
|
||||
package com.iluwatar.commander.exceptions;
|
||||
|
||||
/**
|
||||
* IsEmptyException is thrown when it is attempted to dequeue from an empty queue.
|
||||
*/
|
||||
|
||||
/** IsEmptyException is thrown when it is attempted to dequeue from an empty queue. */
|
||||
public class IsEmptyException extends Exception {
|
||||
private static final long serialVersionUID = 123546L;
|
||||
}
|
||||
|
||||
+1
-4
@@ -24,10 +24,7 @@
|
||||
*/
|
||||
package com.iluwatar.commander.exceptions;
|
||||
|
||||
/**
|
||||
* ItemUnavailableException is thrown when item is not available for shipping.
|
||||
*/
|
||||
|
||||
/** ItemUnavailableException is thrown when item is not available for shipping. */
|
||||
public class ItemUnavailableException extends Exception {
|
||||
private static final long serialVersionUID = 575940L;
|
||||
}
|
||||
|
||||
-1
@@ -28,7 +28,6 @@ package com.iluwatar.commander.exceptions;
|
||||
* PaymentDetailsErrorException is thrown when the details entered are incorrect or payment cannot
|
||||
* be made with the details given.
|
||||
*/
|
||||
|
||||
public class PaymentDetailsErrorException extends Exception {
|
||||
private static final long serialVersionUID = 867203L;
|
||||
}
|
||||
|
||||
-1
@@ -28,7 +28,6 @@ package com.iluwatar.commander.exceptions;
|
||||
* ShippingNotPossibleException is thrown when the address entered cannot be shipped to by service
|
||||
* currently for some reason.
|
||||
*/
|
||||
|
||||
public class ShippingNotPossibleException extends Exception {
|
||||
private static final long serialVersionUID = 342055L;
|
||||
}
|
||||
|
||||
+1
-5
@@ -29,10 +29,7 @@ import com.iluwatar.commander.messagingservice.MessagingService.MessageRequest;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The MessagingDatabase is where the MessageRequest is added.
|
||||
*/
|
||||
|
||||
/** The MessagingDatabase is where the MessageRequest is added. */
|
||||
public class MessagingDatabase extends Database<MessageRequest> {
|
||||
private final Map<String, MessageRequest> data = new Hashtable<>();
|
||||
|
||||
@@ -45,5 +42,4 @@ public class MessagingDatabase extends Database<MessageRequest> {
|
||||
public MessageRequest get(String requestId) {
|
||||
return data.get(requestId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-8
@@ -26,7 +26,6 @@ package com.iluwatar.commander.messagingservice;
|
||||
|
||||
import com.iluwatar.commander.Service;
|
||||
import com.iluwatar.commander.exceptions.DatabaseUnavailableException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
@@ -34,7 +33,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||
* In case an error is encountered in payment and this service is found to be unavailable, the order
|
||||
* is added to the {@link com.iluwatar.commander.employeehandle.EmployeeDatabase}.
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
public class MessagingService extends Service {
|
||||
|
||||
@@ -50,9 +48,7 @@ public class MessagingService extends Service {
|
||||
super(db, exc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public method which will receive request from {@link com.iluwatar.commander.Commander}.
|
||||
*/
|
||||
/** Public method which will receive request from {@link com.iluwatar.commander.Commander}. */
|
||||
public String receiveRequest(Object... parameters) throws DatabaseUnavailableException {
|
||||
var messageToSend = (int) parameters[0];
|
||||
var id = generateId();
|
||||
@@ -61,7 +57,7 @@ public class MessagingService extends Service {
|
||||
msg = MessageToSend.PAYMENT_FAIL;
|
||||
} else if (messageToSend == 1) {
|
||||
msg = MessageToSend.PAYMENT_TRYING;
|
||||
} else { //messageToSend == 2
|
||||
} else { // messageToSend == 2
|
||||
msg = MessageToSend.PAYMENT_SUCCESSFUL;
|
||||
}
|
||||
var req = new MessageRequest(id, msg);
|
||||
@@ -70,8 +66,8 @@ public class MessagingService extends Service {
|
||||
|
||||
protected String updateDb(Object... parameters) throws DatabaseUnavailableException {
|
||||
var req = (MessageRequest) parameters[0];
|
||||
if (this.database.get(req.reqId) == null) { //idempotence, in case db fails here
|
||||
database.add(req); //if successful:
|
||||
if (this.database.get(req.reqId) == null) { // idempotence, in case db fails here
|
||||
database.add(req); // if successful:
|
||||
LOGGER.info(sendMessage(req.msg));
|
||||
return req.reqId;
|
||||
}
|
||||
|
||||
@@ -29,12 +29,10 @@ import com.iluwatar.commander.paymentservice.PaymentService.PaymentRequest;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* PaymentDatabase is where the PaymentRequest is added, along with details.
|
||||
*/
|
||||
/** PaymentDatabase is where the PaymentRequest is added, along with details. */
|
||||
public class PaymentDatabase extends Database<PaymentRequest> {
|
||||
|
||||
//0-fail, 1-error, 2-success
|
||||
// 0-fail, 1-error, 2-success
|
||||
private final Map<String, PaymentRequest> data = new Hashtable<>();
|
||||
|
||||
@Override
|
||||
@@ -46,5 +44,4 @@ public class PaymentDatabase extends Database<PaymentRequest> {
|
||||
public PaymentRequest get(String requestId) {
|
||||
return data.get(requestId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import lombok.RequiredArgsConstructor;
|
||||
* The PaymentService class receives request from the {@link com.iluwatar.commander.Commander} and
|
||||
* adds to the {@link PaymentDatabase}.
|
||||
*/
|
||||
|
||||
public class PaymentService extends Service {
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@@ -46,12 +45,9 @@ public class PaymentService extends Service {
|
||||
super(db, exc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public method which will receive request from {@link com.iluwatar.commander.Commander}.
|
||||
*/
|
||||
|
||||
/** Public method which will receive request from {@link com.iluwatar.commander.Commander}. */
|
||||
public String receiveRequest(Object... parameters) throws DatabaseUnavailableException {
|
||||
//it could also be sending an userid, payment details here or something, not added here
|
||||
// it could also be sending an userid, payment details here or something, not added here
|
||||
var id = generateId();
|
||||
var req = new PaymentRequest(id, (float) parameters[0]);
|
||||
return updateDb(req);
|
||||
|
||||
@@ -29,10 +29,7 @@ import com.iluwatar.commander.exceptions.IsEmptyException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* QueueDatabase id where the instructions to be implemented are queued.
|
||||
*/
|
||||
|
||||
/** QueueDatabase id where the instructions to be implemented are queued. */
|
||||
public class QueueDatabase extends Database<QueueTask> {
|
||||
|
||||
private final Queue<QueueTask> data;
|
||||
@@ -47,16 +44,15 @@ public class QueueDatabase extends Database<QueueTask> {
|
||||
public QueueTask add(QueueTask t) {
|
||||
data.enqueue(t);
|
||||
return t;
|
||||
//even if same thing queued twice, it is taken care of in other dbs
|
||||
// even if same thing queued twice, it is taken care of in other dbs
|
||||
}
|
||||
|
||||
/**
|
||||
* peek method returns object at front without removing it from queue.
|
||||
*
|
||||
* @return object at front of queue
|
||||
* @throws IsEmptyException if queue is empty
|
||||
* @throws IsEmptyException if queue is empty
|
||||
*/
|
||||
|
||||
public QueueTask peek() throws IsEmptyException {
|
||||
return this.data.peek();
|
||||
}
|
||||
@@ -65,9 +61,8 @@ public class QueueDatabase extends Database<QueueTask> {
|
||||
* dequeue method removes the object at front and returns it.
|
||||
*
|
||||
* @return object at front of queue
|
||||
* @throws IsEmptyException if queue is empty
|
||||
* @throws IsEmptyException if queue is empty
|
||||
*/
|
||||
|
||||
public QueueTask dequeue() throws IsEmptyException {
|
||||
return this.data.dequeue();
|
||||
}
|
||||
@@ -76,5 +71,4 @@ public class QueueDatabase extends Database<QueueTask> {
|
||||
public QueueTask get(String taskId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,15 +29,11 @@ import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* QueueTask object is the object enqueued in queue.
|
||||
*/
|
||||
/** QueueTask object is the object enqueued in queue. */
|
||||
@RequiredArgsConstructor
|
||||
public class QueueTask {
|
||||
|
||||
/**
|
||||
* TaskType is the type of task to be done.
|
||||
*/
|
||||
/** TaskType is the type of task to be done. */
|
||||
public enum TaskType {
|
||||
MESSAGING,
|
||||
PAYMENT,
|
||||
@@ -46,13 +42,11 @@ public class QueueTask {
|
||||
|
||||
public final Order order;
|
||||
public final TaskType taskType;
|
||||
public final int messageType; //0-fail, 1-error, 2-success
|
||||
|
||||
public final int messageType; // 0-fail, 1-error, 2-success
|
||||
|
||||
/*we could have varargs Object instead to pass in any parameter instead of just message type
|
||||
but keeping it simple here*/
|
||||
@Getter
|
||||
@Setter
|
||||
private long firstAttemptTime = -1L; //when first time attempt made to do task
|
||||
@Getter @Setter private long firstAttemptTime = -1L; // when first time attempt made to do task
|
||||
|
||||
/**
|
||||
* getType method.
|
||||
@@ -76,4 +70,4 @@ public class QueueTask {
|
||||
public boolean isFirstAttempt() {
|
||||
return this.firstAttemptTime == -1L;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-5
@@ -29,10 +29,7 @@ import com.iluwatar.commander.shippingservice.ShippingService.ShippingRequest;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* ShippingDatabase is where the ShippingRequest objects are added.
|
||||
*/
|
||||
|
||||
/** ShippingDatabase is where the ShippingRequest objects are added. */
|
||||
public class ShippingDatabase extends Database<ShippingRequest> {
|
||||
|
||||
private final Map<String, ShippingRequest> data = new Hashtable<>();
|
||||
@@ -45,5 +42,4 @@ public class ShippingDatabase extends Database<ShippingRequest> {
|
||||
public ShippingRequest get(String trasnactionId) {
|
||||
return data.get(trasnactionId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import lombok.AllArgsConstructor;
|
||||
* ShippingService class receives request from {@link com.iluwatar.commander.Commander} class and
|
||||
* adds it to the {@link ShippingDatabase}.
|
||||
*/
|
||||
|
||||
public class ShippingService extends Service {
|
||||
|
||||
@AllArgsConstructor
|
||||
@@ -46,10 +45,7 @@ public class ShippingService extends Service {
|
||||
super(db, exc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public method which will receive request from {@link com.iluwatar.commander.Commander}.
|
||||
*/
|
||||
|
||||
/** Public method which will receive request from {@link com.iluwatar.commander.Commander}. */
|
||||
public String receiveRequest(Object... parameters) throws DatabaseUnavailableException {
|
||||
var id = generateId();
|
||||
var item = (String) parameters[0];
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -40,33 +40,47 @@ class RetryTest {
|
||||
|
||||
@Test
|
||||
void performTest() {
|
||||
Retry.Operation op = (l) -> {
|
||||
if (!l.isEmpty()) {
|
||||
throw l.remove(0);
|
||||
}
|
||||
};
|
||||
Retry.HandleErrorIssue<Order> handleError = (o, e) -> {
|
||||
};
|
||||
var r1 = new Retry<>(op, handleError, 3, 30000,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
var r2 = new Retry<>(op, handleError, 3, 30000,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
Retry.Operation op =
|
||||
(l) -> {
|
||||
if (!l.isEmpty()) {
|
||||
throw l.remove(0);
|
||||
}
|
||||
};
|
||||
Retry.HandleErrorIssue<Order> handleError = (o, e) -> {};
|
||||
var r1 =
|
||||
new Retry<>(
|
||||
op,
|
||||
handleError,
|
||||
3,
|
||||
30000,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
var r2 =
|
||||
new Retry<>(
|
||||
op,
|
||||
handleError,
|
||||
3,
|
||||
30000,
|
||||
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
|
||||
var user = new User("Jim", "ABCD");
|
||||
var order = new Order(user, "book", 10f);
|
||||
var arr1 = new ArrayList<>(List.of(new ItemUnavailableException(), new DatabaseUnavailableException()));
|
||||
var arr1 =
|
||||
new ArrayList<>(
|
||||
List.of(new ItemUnavailableException(), new DatabaseUnavailableException()));
|
||||
try {
|
||||
r1.perform(arr1, order);
|
||||
} catch (Exception e1) {
|
||||
LOG.error("An exception occurred", e1);
|
||||
}
|
||||
var arr2 = new ArrayList<>(List.of(new DatabaseUnavailableException(), new ItemUnavailableException()));
|
||||
var arr2 =
|
||||
new ArrayList<>(
|
||||
List.of(new DatabaseUnavailableException(), new ItemUnavailableException()));
|
||||
try {
|
||||
r2.perform(arr2, order);
|
||||
} catch (Exception e1) {
|
||||
LOG.error("An exception occurred", e1);
|
||||
}
|
||||
//r1 stops at ItemUnavailableException, r2 retries because it encounters DatabaseUnavailableException
|
||||
// r1 stops at ItemUnavailableException, r2 retries because it encounters
|
||||
// DatabaseUnavailableException
|
||||
assertTrue(arr1.size() == 1 && arr2.isEmpty());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user