Skip to content

Commit d9a567c

Browse files
AnaghaSasikumariluwatar
authored andcommitted
Commander pattern iluwatar#505 (iluwatar#857)
* Commander pattern * Fix checkstyle errors * Update Commander.java * Update README.md * Update PaymentService.java * Update Commander.java * Update README.md
1 parent a113de6 commit d9a567c

32 files changed

+2620
-0
lines changed

commander/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
layout: pattern
3+
title: Commander
4+
folder: commander
5+
permalink: /patterns/commander/
6+
categories:
7+
tags:
8+
- Java
9+
- Difficulty-Intermediate
10+
---
11+
12+
## Intent
13+
14+
> Used to handle all problems that can be encountered when doing distributed transactions.
15+
16+
## Applicability
17+
This pattern can be used when we need to make commits into 2 (or more) databases to complete transaction, which cannot be done atomically and can thereby create problems.
18+
19+
## Explanation
20+
Handling distributed transactions can be tricky, but if we choose to not handle it carefully, there could be unwanted consequences. Say, we have an e-commerce website which has a Payment microservice and a Shipping microservice. If the shipping is available currently but payment service is not up, or vice versa, how would we deal with it after having already received the order from the user?
21+
We need a mechanism in place which can handle these kinds of situations. We have to direct the order to either one of the services (in this example, shipping) and then add the order into the database of the other service (in this example, payment), since two databses cannot be updated atomically. If currently unable to do it, there should be a queue where this request can be queued, and there has to be a mechanism which allows for a failure in the queueing as well. All this needs to be done by constant retries while ensuring idempotence (even if the request is made several times, the change should only be applied once) by a commander class, to reach a state of eventual consistency.
22+
23+
## Credits
24+
* [https://www.grahamlea.com/2016/08/distributed-transactions-microservices-icebergs/]

commander/pom.xml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2+
<modelVersion>4.0.0</modelVersion>
3+
<parent>
4+
<groupId>com.iluwatar</groupId>
5+
<artifactId>java-design-patterns</artifactId>
6+
<version>1.21.0-SNAPSHOT</version>
7+
</parent>
8+
<artifactId>commander</artifactId>
9+
<dependencies>
10+
<dependency>
11+
<groupId>org.junit.jupiter</groupId>
12+
<artifactId>junit-jupiter-api</artifactId>
13+
<scope>test</scope>
14+
</dependency>
15+
<dependency>
16+
<groupId>org.junit.jupiter</groupId>
17+
<artifactId>junit-jupiter-engine</artifactId>
18+
<scope>test</scope>
19+
</dependency>
20+
<dependency>
21+
<groupId>log4j</groupId>
22+
<artifactId>log4j</artifactId>
23+
<version>1.2.17</version>
24+
</dependency>
25+
</dependencies>
26+
</project>

commander/properties/log4j.properties

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#Define root logger options
2+
log4j.rootLogger=TRACE, file, console
3+
4+
#Define console appender
5+
log4j.appender.console=org.apache.log4j.ConsoleAppender
6+
logrj.appender.console.Target=System.out
7+
log4j.appender.console.layout=org.apache.log4j.PatternLayout
8+
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd} %d{HH:mm:ss} %5p[%t] %m%n
9+
10+
#Define rolling file appender
11+
log4j.appender.file=org.apache.log4j.RollingFileAppender
12+
log4j.appender.file.File=/log/logFile.log
13+
log4j.appender.file.Append=true
14+
log4j.appender.file.ImmediateFlush=true
15+
log4j.appender.file.MaxFileSize=10MB
16+
log4j.appender.file.MaxBackupIndex=5
17+
log4j.appender.file.layout=org.apache.log4j.PatternLayout
18+
log4j.appender.file.layout.ConversionPattern=%d %d{HH:mm:ss} %5p[%t] %m%n
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* The MIT License
3+
* Copyright (c) 2014-2016 Ilkka Sepp�l�
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
24+
package com.iluwatar.commander;
25+
26+
import com.iluwatar.commander.employeehandle.EmployeeDatabase;
27+
import com.iluwatar.commander.employeehandle.EmployeeHandle;
28+
import com.iluwatar.commander.exceptions.DatabaseUnavailableException;
29+
import com.iluwatar.commander.exceptions.ItemUnavailableException;
30+
import com.iluwatar.commander.messagingservice.MessagingDatabase;
31+
import com.iluwatar.commander.messagingservice.MessagingService;
32+
import com.iluwatar.commander.paymentservice.PaymentDatabase;
33+
import com.iluwatar.commander.paymentservice.PaymentService;
34+
import com.iluwatar.commander.shippingservice.ShippingDatabase;
35+
import com.iluwatar.commander.shippingservice.ShippingService;
36+
import com.iluwatar.commander.queue.QueueDatabase;
37+
38+
/**
39+
* AppEmployeeDbFailCases class looks at possible cases when Employee handle service is
40+
* available/unavailable.
41+
*/
42+
43+
public class AppEmployeeDbFailCases {
44+
final int numOfRetries = 3;
45+
final long retryDuration = 30000;
46+
final long queueTime = 240000; //4 mins
47+
final long queueTaskTime = 60000; //1 min
48+
final long paymentTime = 120000; //2 mins
49+
final long messageTime = 150000; //2.5 mins
50+
final long employeeTime = 240000; //4 mins
51+
52+
void employeeDatabaseUnavailableCase() throws Exception {
53+
PaymentService ps = new PaymentService(new PaymentDatabase(), new DatabaseUnavailableException(),
54+
new DatabaseUnavailableException(), new DatabaseUnavailableException(), new DatabaseUnavailableException(),
55+
new DatabaseUnavailableException(), new DatabaseUnavailableException());
56+
ShippingService ss = new ShippingService(new ShippingDatabase());
57+
MessagingService ms = new MessagingService(new MessagingDatabase());
58+
EmployeeHandle eh = new EmployeeHandle(new EmployeeDatabase(), new DatabaseUnavailableException(),
59+
new DatabaseUnavailableException(), new DatabaseUnavailableException(), new DatabaseUnavailableException(),
60+
new DatabaseUnavailableException(), new DatabaseUnavailableException());
61+
QueueDatabase qdb = new QueueDatabase(new DatabaseUnavailableException(), new DatabaseUnavailableException(),
62+
new DatabaseUnavailableException(), new DatabaseUnavailableException(), new DatabaseUnavailableException(),
63+
new DatabaseUnavailableException());
64+
Commander c = new Commander(eh,ps,ss,ms,qdb,numOfRetries,retryDuration,
65+
queueTime,queueTaskTime,paymentTime,messageTime,employeeTime);
66+
User user = new User("Jim", "ABCD");
67+
Order order = new Order(user, "book", 10f);
68+
c.placeOrder(order);
69+
}
70+
71+
void employeeDbSuccessCase() throws Exception {
72+
PaymentService ps = new PaymentService(new PaymentDatabase());
73+
ShippingService ss = new ShippingService(new ShippingDatabase(), new ItemUnavailableException());
74+
MessagingService ms = new MessagingService(new MessagingDatabase());
75+
EmployeeHandle eh = new EmployeeHandle(new EmployeeDatabase(), new DatabaseUnavailableException(),
76+
new DatabaseUnavailableException());
77+
QueueDatabase qdb = new QueueDatabase();
78+
Commander c = new Commander(eh,ps,ss,ms,qdb,numOfRetries,retryDuration,
79+
queueTime,queueTaskTime,paymentTime,messageTime,employeeTime);
80+
User user = new User("Jim", "ABCD");
81+
Order order = new Order(user, "book", 10f);
82+
c.placeOrder(order);
83+
}
84+
85+
/**
86+
* Program entry point.
87+
*
88+
* @param args command line args
89+
*/
90+
91+
public static void main(String[] args) throws Exception {
92+
AppEmployeeDbFailCases aefc = new AppEmployeeDbFailCases();
93+
//aefc.employeeDatabaseUnavailableCase();
94+
aefc.employeeDbSuccessCase();
95+
}
96+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/**
2+
* The MIT License
3+
* Copyright (c) 2014-2016 Ilkka Sepp�l�
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
24+
package com.iluwatar.commander;
25+
26+
import com.iluwatar.commander.employeehandle.EmployeeDatabase;
27+
import com.iluwatar.commander.employeehandle.EmployeeHandle;
28+
import com.iluwatar.commander.exceptions.DatabaseUnavailableException;
29+
import com.iluwatar.commander.messagingservice.MessagingDatabase;
30+
import com.iluwatar.commander.messagingservice.MessagingService;
31+
import com.iluwatar.commander.paymentservice.PaymentDatabase;
32+
import com.iluwatar.commander.paymentservice.PaymentService;
33+
import com.iluwatar.commander.shippingservice.ShippingDatabase;
34+
import com.iluwatar.commander.shippingservice.ShippingService;
35+
import com.iluwatar.commander.queue.QueueDatabase;
36+
37+
/**
38+
* AppMessagingFailCases class looks at possible cases when Messaging service is
39+
* available/unavailable.
40+
*/
41+
42+
public class AppMessagingFailCases {
43+
final int numOfRetries = 3;
44+
final long retryDuration = 30000;
45+
final long queueTime = 240000; //4 mins
46+
final long queueTaskTime = 60000; //1 min
47+
final long paymentTime = 120000; //2 mins
48+
final long messageTime = 150000; //2.5 mins
49+
final long employeeTime = 240000; //4 mins
50+
51+
void messagingDatabaseUnavailableCasePaymentSuccess() throws Exception {
52+
//rest is successful
53+
PaymentService ps = new PaymentService(new PaymentDatabase());
54+
ShippingService ss = new ShippingService(new ShippingDatabase());
55+
MessagingService ms = new MessagingService(new MessagingDatabase(), new DatabaseUnavailableException(),
56+
new DatabaseUnavailableException(), new DatabaseUnavailableException(), new DatabaseUnavailableException(),
57+
new DatabaseUnavailableException(), new DatabaseUnavailableException());
58+
EmployeeHandle eh = new EmployeeHandle(new EmployeeDatabase());
59+
QueueDatabase qdb = new QueueDatabase();
60+
Commander c = new Commander(eh,ps,ss,ms,qdb,numOfRetries,retryDuration,
61+
queueTime,queueTaskTime,paymentTime,messageTime,employeeTime);
62+
User user = new User("Jim", "ABCD");
63+
Order order = new Order(user, "book", 10f);
64+
c.placeOrder(order);
65+
}
66+
67+
void messagingDatabaseUnavailableCasePaymentError() throws Exception {
68+
//rest is successful
69+
PaymentService ps = new PaymentService(new PaymentDatabase(), new DatabaseUnavailableException(),
70+
new DatabaseUnavailableException(), new DatabaseUnavailableException(), new DatabaseUnavailableException(),
71+
new DatabaseUnavailableException(), new DatabaseUnavailableException());
72+
ShippingService ss = new ShippingService(new ShippingDatabase());
73+
MessagingService ms = new MessagingService(new MessagingDatabase(), new DatabaseUnavailableException(),
74+
new DatabaseUnavailableException(), new DatabaseUnavailableException(), new DatabaseUnavailableException(),
75+
new DatabaseUnavailableException(), new DatabaseUnavailableException(), new DatabaseUnavailableException(),
76+
new DatabaseUnavailableException(), new DatabaseUnavailableException(), new DatabaseUnavailableException(),
77+
new DatabaseUnavailableException(), new DatabaseUnavailableException(), new DatabaseUnavailableException(),
78+
new DatabaseUnavailableException(), new DatabaseUnavailableException(), new DatabaseUnavailableException());
79+
EmployeeHandle eh = new EmployeeHandle(new EmployeeDatabase());
80+
QueueDatabase qdb = new QueueDatabase();
81+
Commander c = new Commander(eh,ps,ss,ms,qdb,numOfRetries,retryDuration,
82+
queueTime,queueTaskTime,paymentTime,messageTime,employeeTime);
83+
User user = new User("Jim", "ABCD");
84+
Order order = new Order(user, "book", 10f);
85+
c.placeOrder(order);
86+
}
87+
88+
void messagingDatabaseUnavailableCasePaymentFailure() throws Exception {
89+
//rest is successful
90+
PaymentService ps = new PaymentService(new PaymentDatabase(), new DatabaseUnavailableException(),
91+
new DatabaseUnavailableException(), new DatabaseUnavailableException(), new DatabaseUnavailableException(),
92+
new DatabaseUnavailableException(), new DatabaseUnavailableException());
93+
ShippingService ss = new ShippingService(new ShippingDatabase());
94+
MessagingService ms = new MessagingService(new MessagingDatabase(), new DatabaseUnavailableException(),
95+
new DatabaseUnavailableException(), new DatabaseUnavailableException(), new DatabaseUnavailableException(),
96+
new DatabaseUnavailableException(), new DatabaseUnavailableException());
97+
EmployeeHandle eh = new EmployeeHandle(new EmployeeDatabase());
98+
QueueDatabase qdb = new QueueDatabase(new DatabaseUnavailableException(), new DatabaseUnavailableException(),
99+
new DatabaseUnavailableException(), new DatabaseUnavailableException(), new DatabaseUnavailableException(),
100+
new DatabaseUnavailableException());
101+
Commander c = new Commander(eh,ps,ss,ms,qdb,numOfRetries,retryDuration,queueTime,queueTaskTime,
102+
paymentTime,messageTime,employeeTime);
103+
User user = new User("Jim", "ABCD");
104+
Order order = new Order(user, "book", 10f);
105+
c.placeOrder(order);
106+
}
107+
108+
void messagingSuccessCase() throws Exception {
109+
//done here
110+
PaymentService ps = new PaymentService(new PaymentDatabase(), new DatabaseUnavailableException(),
111+
new DatabaseUnavailableException(), new DatabaseUnavailableException(), new DatabaseUnavailableException(),
112+
new DatabaseUnavailableException(), new DatabaseUnavailableException());
113+
ShippingService ss = new ShippingService(new ShippingDatabase());
114+
MessagingService ms = new MessagingService(new MessagingDatabase(), new DatabaseUnavailableException(),
115+
new DatabaseUnavailableException());
116+
EmployeeHandle eh = new EmployeeHandle(new EmployeeDatabase());
117+
QueueDatabase qdb = new QueueDatabase();
118+
Commander c = new Commander(eh,ps,ss,ms,qdb,numOfRetries,retryDuration,
119+
queueTime,queueTaskTime,paymentTime,messageTime,employeeTime);
120+
User user = new User("Jim", "ABCD");
121+
Order order = new Order(user, "book", 10f);
122+
c.placeOrder(order);
123+
}
124+
125+
/**
126+
* Program entry point.
127+
*
128+
* @param args command line args
129+
*/
130+
131+
public static void main(String[] args) throws Exception {
132+
AppMessagingFailCases amfc = new AppMessagingFailCases();
133+
//amfc.messagingDatabaseUnavailableCasePaymentSuccess();
134+
//amfc.messagingDatabaseUnavailableCasePaymentError();
135+
//amfc.messagingDatabaseUnavailableCasePaymentFailure();
136+
amfc.messagingSuccessCase();
137+
}
138+
}

0 commit comments

Comments
 (0)