Skip to content

Commit 3fba381

Browse files
dev is done, tests are not.
1 parent f9945c9 commit 3fba381

16 files changed

+2313
-436
lines changed

fallback/etc/fallback.urm.puml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
@startuml
2+
package com.iluwatar {
3+
class App {
4+
- MAX_ATTEMPTS : int {static}
5+
- RETRY_DELAY : int {static}
6+
- TIMEOUT : int {static}
7+
- circuitBreaker : CircuitBreaker
8+
- executor : ExecutorService
9+
- fallbackService : Service
10+
- primaryService : Service
11+
+ App()
12+
+ App(primaryService : Service, fallbackService : Service)
13+
+ executeWithFallback() : String
14+
- getFallbackData() : String
15+
+ main(args : String[]) {static}
16+
+ shutdown()
17+
}
18+
interface CircuitBreaker {
19+
+ isOpen() : boolean {abstract}
20+
+ recordFailure() {abstract}
21+
+ recordSuccess() {abstract}
22+
+ reset() {abstract}
23+
}
24+
class DefaultCircuitBreaker {
25+
- RESET_TIMEOUT : long {static}
26+
- failureCount : int
27+
- failureThreshold : int
28+
- lastFailureTime : long
29+
+ DefaultCircuitBreaker(failureThreshold : int)
30+
+ isOpen() : boolean
31+
+ recordFailure()
32+
+ recordSuccess()
33+
+ reset()
34+
}
35+
class LocalCacheService {
36+
- cache : ConcurrentHashMap<String, String>
37+
+ LocalCacheService()
38+
+ getData() : String
39+
+ updateCache(key : String, value : String)
40+
}
41+
class RemoteService {
42+
- HTTP_OK : int {static}
43+
- client : HttpClient
44+
- serviceUrl : String
45+
+ RemoteService(serviceUrl : String)
46+
+ getData() : String
47+
}
48+
interface Service {
49+
+ getData() : String {abstract}
50+
}
51+
}
52+
App --> "-circuitBreaker" CircuitBreaker
53+
App --> "-primaryService" Service
54+
DefaultCircuitBreaker ..|> CircuitBreaker
55+
LocalCacheService ..|> Service
56+
RemoteService ..|> Service
57+
@enduml

fallback/pom.xml

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
5+
6+
The MIT License
7+
Copyright © 2014-2022 Ilkka Seppälä
8+
9+
Permission is hereby granted, free of charge, to any person obtaining a copy
10+
of this software and associated documentation files (the "Software"), to deal
11+
in the Software without restriction, including without limitation the rights
12+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
copies of the Software, and to permit persons to whom the Software is
14+
furnished to do so, subject to the following conditions:
15+
16+
The above copyright notice and this permission notice shall be included in
17+
all copies or substantial portions of the Software.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
THE SOFTWARE.
26+
27+
-->
28+
<project xmlns="http://maven.apache.org/POM/4.0.0"
29+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
30+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
31+
<modelVersion>4.0.0</modelVersion>
32+
<parent>
33+
<groupId>com.iluwatar</groupId>
34+
<artifactId>java-design-patterns</artifactId>
35+
<version>1.26.0-SNAPSHOT</version>
36+
</parent>
37+
38+
<artifactId>fallback</artifactId>
39+
40+
<properties>
41+
<maven.compiler.source>17</maven.compiler.source>
42+
<maven.compiler.target>17</maven.compiler.target>
43+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
44+
</properties>
45+
<dependencies>
46+
<dependency>
47+
<groupId>org.junit.jupiter</groupId>
48+
<artifactId>junit-jupiter-engine</artifactId>
49+
<version>5.8.2</version>
50+
<scope>test</scope>
51+
</dependency>
52+
<dependency>
53+
<groupId>org.junit.jupiter</groupId>
54+
<artifactId>junit-jupiter-api</artifactId>
55+
<version>5.8.2</version>
56+
<scope>test</scope>
57+
</dependency>
58+
<dependency>
59+
<groupId>org.junit.jupiter</groupId>
60+
<artifactId>junit-jupiter</artifactId>
61+
<version>5.8.2</version>
62+
<scope>test</scope>
63+
</dependency>
64+
<dependency>
65+
<groupId>org.mockito</groupId>
66+
<artifactId>mockito-core</artifactId>
67+
<version>4.5.1</version>
68+
<scope>test</scope>
69+
</dependency>
70+
</dependencies>
71+
</project>
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
3+
*
4+
* The MIT License
5+
* Copyright © 2014-2022 Ilkka Seppälä
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
package com.iluwatar;
26+
27+
import java.net.http.HttpClient;
28+
import java.time.Duration; // Add this import
29+
import java.util.concurrent.ExecutorService;
30+
import java.util.concurrent.Executors;
31+
import java.util.concurrent.Future;
32+
import java.util.concurrent.TimeUnit;
33+
import java.util.logging.Logger;
34+
35+
/**
36+
* App class that demonstrates the use of Circuit Breaker pattern with fallback mechanism.
37+
*/
38+
public class App {
39+
private static final Logger LOGGER = Logger.getLogger(App.class.getName());
40+
private static final int TIMEOUT = 2;
41+
private static final int MAX_ATTEMPTS = 3;
42+
private static final int RETRY_DELAY = 1000;
43+
private static final String DEFAULT_API_URL = "https://jsonplaceholder.typicode.com/todos";
44+
45+
private final CircuitBreaker circuitBreaker;
46+
private final ExecutorService executor;
47+
private final Service primaryService;
48+
private final Service fallbackService;
49+
50+
/**
51+
* Constructs an App with default primary and fallback services.
52+
*/
53+
public App() {
54+
HttpClient httpClient;
55+
try {
56+
httpClient = HttpClient.newBuilder()
57+
.connectTimeout(Duration.ofSeconds(TIMEOUT))
58+
.build();
59+
} catch (Exception e) {
60+
LOGGER.severe("Failed to create HTTP client: " + e.getMessage());
61+
httpClient = HttpClient.newHttpClient(); // Fallback to default client
62+
}
63+
64+
this.primaryService = new RemoteService(DEFAULT_API_URL, httpClient);
65+
this.fallbackService = new LocalCacheService();
66+
this.circuitBreaker = new DefaultCircuitBreaker(MAX_ATTEMPTS);
67+
this.executor = Executors.newSingleThreadExecutor();
68+
}
69+
70+
/**
71+
* Constructs an App with the specified primary and fallback services and a circuit breaker.
72+
*
73+
* @param primaryService the primary service to use
74+
* @param fallbackService the fallback service to use
75+
* @param circuitBreaker the circuit breaker to use
76+
*/
77+
public App(final Service primaryService, final Service fallbackService, final CircuitBreaker circuitBreaker) {
78+
this.circuitBreaker = circuitBreaker;
79+
this.executor = Executors.newSingleThreadExecutor();
80+
this.primaryService = primaryService;
81+
this.fallbackService = fallbackService;
82+
}
83+
84+
/**
85+
* Main method to run the application.
86+
*
87+
* @param args command line arguments
88+
*/
89+
public static void main(final String[] args) {
90+
App app = new App();
91+
for (int i = 0; i < 5; i++) {
92+
try {
93+
String result = app.executeWithFallback();
94+
System.out.println("Attempt " + (i + 1) + ": Result = " + result);
95+
} catch (Exception e) {
96+
System.err.println("Attempt " + (i + 1) + " failed: " + e.getMessage());
97+
}
98+
try {
99+
Thread.sleep(RETRY_DELAY);
100+
} catch (InterruptedException e) {
101+
Thread.currentThread().interrupt();
102+
System.err.println("Thread was interrupted: " + e.getMessage());
103+
}
104+
}
105+
app.shutdown();
106+
}
107+
108+
/**
109+
* Executes the primary service with a fallback mechanism.
110+
*
111+
* @return the result from the primary or fallback service
112+
*/
113+
public String executeWithFallback() {
114+
if (circuitBreaker.isOpen()) {
115+
LOGGER.info("Circuit breaker is open, using cached data");
116+
return getFallbackData();
117+
}
118+
119+
try {
120+
Future<String> future = executor.submit(primaryService::getData);
121+
String result = future.get(TIMEOUT, TimeUnit.SECONDS);
122+
circuitBreaker.recordSuccess();
123+
if (fallbackService instanceof LocalCacheService) {
124+
((LocalCacheService) fallbackService).updateCache("default", result);
125+
}
126+
return result;
127+
} catch (Exception e) {
128+
LOGGER.warning("Primary service failed, using fallback. Exception: " + e.getMessage());
129+
circuitBreaker.recordFailure();
130+
return getFallbackData();
131+
}
132+
}
133+
134+
/**
135+
* Retrieves data from the fallback service.
136+
*
137+
* @return the data from the fallback service
138+
*/
139+
private String getFallbackData() {
140+
try {
141+
return fallbackService.getData();
142+
} catch (Exception e) {
143+
LOGGER.warning("Fallback service failed. Exception: " + e.getMessage());
144+
return "System is currently unavailable";
145+
}
146+
}
147+
148+
/**
149+
* Shuts down the executor service.
150+
*/
151+
public void shutdown() {
152+
executor.shutdown();
153+
}
154+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
3+
*
4+
* The MIT License
5+
* Copyright © 2014-2022 Ilkka Seppälä
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
package com.iluwatar;
26+
27+
/**
28+
* CircuitBreaker interface that defines methods to manage the state of a circuit breaker.
29+
*/
30+
public interface CircuitBreaker {
31+
32+
/**
33+
* Checks if the circuit breaker is open.
34+
*
35+
* @return true if the circuit breaker is open, false otherwise
36+
*/
37+
boolean isOpen();
38+
39+
/**
40+
* Records a successful operation.
41+
*/
42+
void recordSuccess();
43+
44+
/**
45+
* Records a failed operation.
46+
*/
47+
void recordFailure();
48+
49+
/**
50+
* Resets the circuit breaker.
51+
*/
52+
void reset();
53+
}

0 commit comments

Comments
 (0)