Skip to content

Commit 4b7253f

Browse files
all done
1 parent 59d6564 commit 4b7253f

17 files changed

+461
-203
lines changed

fallback/etc/fallback.urm.png

396 KB
Loading

fallback/etc/fallback.urm.puml

Lines changed: 98 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,173 @@
11
@startuml
2-
package com.iluwatar {
2+
package fallback {
33
class App {
44
- TIMEOUT : int {static}
5+
- MAX_ATTEMPTS : int {static}
6+
- RETRY_DELAY : int {static}
7+
- DEFAULT_API_URL : String {static}
58
- circuitBreaker : CircuitBreaker
69
- executor : ExecutorService
710
- fallbackService : Service
811
- primaryService : Service
12+
- state : ExecutionState
913
- LOGGER : Logger {static}
1014
+ App()
11-
+ App(primaryService : Service, fallbackService : Service)
15+
+ App(primaryService : Service, fallbackService : Service, circuitBreaker : CircuitBreaker)
1216
+ executeWithFallback() : String
1317
- getFallbackData() : String
14-
+ main(args : String[]) {static}
18+
- updateFallbackCache(result : String)
1519
+ shutdown()
20+
+ main(args : String[]) {static}
1621
}
1722

1823
interface Service {
19-
+ getData() : String {abstract}
24+
+ getData() : String
2025
}
2126

2227
interface CircuitBreaker {
23-
+ isOpen() : boolean {abstract}
24-
+ recordFailure() {abstract}
25-
+ recordSuccess() {abstract}
26-
+ reset() {abstract}
28+
+ isOpen() : boolean
29+
+ allowRequest() : boolean
30+
+ recordFailure()
31+
+ recordSuccess()
32+
+ reset()
33+
+ getState() : CircuitState
2734
}
2835

2936
class DefaultCircuitBreaker {
3037
- RESET_TIMEOUT : long {static}
31-
- failureCount : int
38+
- MIN_HALF_OPEN_DURATION : Duration {static}
39+
- state : State
3240
- failureThreshold : int
3341
- lastFailureTime : long
34-
- state : State
42+
- failureTimestamps : Queue<Long>
43+
- windowSize : Duration
44+
- halfOpenStartTime : Instant
3545
+ DefaultCircuitBreaker(failureThreshold : int)
3646
+ isOpen() : boolean
47+
+ allowRequest() : boolean
3748
+ recordFailure()
3849
+ recordSuccess()
3950
+ reset()
40-
- enum State
51+
+ getState() : CircuitState
52+
- transitionToHalfOpen()
53+
- enum State { CLOSED, OPEN, HALF_OPEN }
4154
}
4255

4356
class FallbackService {
44-
- MAX_RETRIES : int {static}
45-
- RETRY_DELAY_MS : int {static}
46-
- TIMEOUT_MS : int {static}
47-
- MAX_REQUESTS_PER_MINUTE : int {static}
48-
- LOGGER : Logger {static}
57+
- {static} MAX_RETRIES : int = 3
58+
- {static} RETRY_DELAY_MS : long = 1000
59+
- {static} TIMEOUT : int = 2
60+
- {static} MIN_SUCCESS_RATE : double = 0.6
61+
- {static} MAX_REQUESTS_PER_MINUTE : int = 60
62+
- {static} LOGGER : Logger
4963
- primaryService : Service
5064
- fallbackService : Service
5165
- circuitBreaker : CircuitBreaker
5266
- executor : ExecutorService
5367
- healthChecker : ScheduledExecutorService
5468
- monitor : ServiceMonitor
5569
- rateLimiter : RateLimiter
56-
- closed : boolean
70+
- state : ServiceState
5771
+ FallbackService(primaryService : Service, fallbackService : Service, circuitBreaker : CircuitBreaker)
5872
+ getData() : String
5973
- executeWithTimeout(task : Callable<String>) : String
6074
- executeFallback() : String
6175
- updateFallbackCache(result : String)
6276
- startHealthChecker()
6377
+ close()
78+
+ getMonitor() : ServiceMonitor
79+
+ getState() : ServiceState
80+
- enum ServiceState { STARTING, RUNNING, DEGRADED, CLOSED }
6481
}
6582

6683
class LocalCacheService {
67-
- cache : Cache
84+
- cache : Cache<String, String>
85+
- refreshExecutor : ScheduledExecutorService
86+
- {static} CACHE_EXPIRY_MS : long = 300000
87+
- {static} CACHE_REFRESH_INTERVAL : Duration = Duration.ofMinutes(5)
88+
- {static} LOGGER : Logger
6889
+ LocalCacheService()
6990
+ getData() : String
7091
+ updateCache(key : String, value : String)
71-
- class Cache
72-
- class CacheEntry
73-
}
92+
+ close() : void
93+
- initializeDefaultCache()
94+
- scheduleMaintenanceTasks()
95+
- cleanupExpiredEntries()
96+
- enum FallbackLevel { PRIMARY, SECONDARY, TERTIARY }
97+
- class Cache<K, V> {
98+
- map : ConcurrentHashMap<K, CacheEntry<V>>
99+
- expiryMs : long
100+
+ Cache(expiryMs : long)
101+
+ get(key : K) : V
102+
+ put(key : K, value : V)
103+
+ cleanup()
104+
- record CacheEntry<V>(value : V, expiryTime : long) { + isExpired() : boolean }
105+
}
106+
74107

75108
class RemoteService {
76-
- HTTP_OK : int {static}
77-
- client : HttpClient
78-
- serviceUrl : String
79-
+ RemoteService(serviceUrl : String)
109+
- apiUrl : String
110+
- httpClient : HttpClient
111+
- {static} TIMEOUT_SECONDS : int = 2
112+
+ RemoteService(apiUrl : String, httpClient : HttpClient)
80113
+ getData() : String
81114
}
82115

83116
class ServiceMonitor {
117+
- successCount : AtomicInteger
118+
- fallbackCount : AtomicInteger
119+
- errorCount : AtomicInteger
120+
- lastSuccessTime : AtomicReference<Instant>
121+
- lastFailureTime : AtomicReference<Instant>
122+
- lastResponseTime : AtomicReference<Duration>
84123
- metrics : Queue<ServiceMetric>
124+
- fallbackWeight : double
125+
- metricWindow : Duration
126+
+ ServiceMonitor()
127+
+ ServiceMonitor(fallbackWeight : double, metricWindow : Duration)
85128
+ recordSuccess(responseTime : Duration)
86-
+ recordError()
87129
+ recordFallback()
88-
+ getMetrics() : List<ServiceMetric>
89-
- class ServiceMetric
90-
- enum MetricType
130+
+ recordError()
131+
+ getSuccessCount() : int
132+
+ getFallbackCount() : int
133+
+ getErrorCount() : int
134+
+ getLastSuccessTime() : Instant
135+
+ getLastFailureTime() : Instant
136+
+ getLastResponseTime() : Duration
137+
+ getSuccessRate() : double
138+
+ reset()
139+
- pruneOldMetrics()
140+
- record ServiceMetric(timestamp : Instant, type : MetricType, responseTime : Duration)
141+
- enum MetricType { SUCCESS, FALLBACK, ERROR }
142+
}
143+
144+
class RateLimiter {
145+
- maxRequests : int
146+
- window : Duration
147+
- requestTimestamps : Queue<Long>
148+
+ RateLimiter(maxRequests : int, window : Duration)
149+
+ tryAcquire() : boolean
91150
}
92151

93152
class ServiceException {
94153
+ ServiceException(message : String)
154+
+ ServiceException(message : String, cause : Throwable)
95155
}
96-
156+
}
97157
' Relationships
98-
App --> "-circuitBreaker" CircuitBreaker
99-
App --> "-primaryService" Service
100-
App --> "-fallbackService" Service
158+
App --> CircuitBreaker
159+
App --> Service : primaryService
160+
App --> Service : fallbackService
101161
DefaultCircuitBreaker ..|> CircuitBreaker
102162
LocalCacheService ..|> Service
103163
RemoteService ..|> Service
104164
FallbackService ..|> Service
105165
FallbackService ..|> AutoCloseable
106-
FallbackService --> "-primaryService" Service
107-
FallbackService --> "-fallbackService" Service
108-
FallbackService --> "-circuitBreaker" CircuitBreaker
109-
FallbackService --> "-monitor" ServiceMonitor
166+
FallbackService --> Service : primaryService
167+
FallbackService --> Service : fallbackService
168+
FallbackService --> CircuitBreaker
169+
FallbackService --> ServiceMonitor
170+
FallbackService --> RateLimiter
110171
ServiceException --|> Exception
111172
}
112173
@enduml

fallback/readme.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Java Design Patterns - Fallback Pattern
2+
3+
## Overview
4+
5+
This project demonstrates the implementation of the Fallback Pattern with Circuit Breaker in Java. The Fallback Pattern is used to handle failures gracefully by providing alternative solutions when the primary service fails. The Circuit Breaker pattern is integrated to prevent cascading failures and to monitor the health of the primary service.
6+
7+
## Features
8+
9+
- **Primary Service**: The main service that attempts to retrieve data.
10+
- **Fallback Service**: A backup service that provides data when the primary service fails.
11+
- **Circuit Breaker**: Monitors the health of the primary service and prevents further calls when the service is deemed unhealthy.
12+
- **Service Monitor**: Tracks the performance metrics of the services.
13+
- **Rate Limiter**: Limits the number of requests to prevent overload.
14+
- **Service Exception**: Custom exceptions to handle service-specific errors.
15+
16+
## Components
17+
18+
- **Service Interface**: Defines the contract for services.
19+
- **RemoteService**: Implementation of the primary service that makes HTTP calls.
20+
- **LocalCacheService**: Implementation of the fallback service that provides cached data.
21+
- **FallbackService**: Manages the primary and fallback services, integrating the circuit breaker and rate limiter.
22+
- **DefaultCircuitBreaker**: Implementation of the circuit breaker with state transitions and failure tracking.
23+
- **ServiceMonitor**: Monitors and records the performance metrics of the services.
24+
- **Service Exception**: Custom exception class to handle errors specific to service operations.
25+
26+
## Usage
27+
28+
To run the application, execute the `App` class. The application will attempt to retrieve data using the primary service and fallback to the cached data if the primary service fails.
29+
30+
## Reflection
31+
32+
This assignment provided a practical application of Object-Oriented Analysis and Design (OOAD) principles. By implementing the Fallback Pattern with Circuit Breaker, I was able to apply concepts such as encapsulation, polymorphism, and separation of concerns. Each component in the system has a clear responsibility, and the use of interfaces allows for flexibility and easy substitution of different implementations.
33+
34+
One of the challenges faced was ensuring thread safety and proper synchronization, especially in the circuit breaker and rate limiter components. To overcome this, I used concurrent data structures and synchronized methods to manage state transitions and request handling. Additionally, integrating the health monitoring and metrics collection required careful consideration of performance and resource management, which was addressed by using scheduled executors and atomic variables.

fallback/src/main/java/com/iluwatar/App.java renamed to fallback/src/main/java/com/iluwatar/fallback/App.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.iluwatar;
1+
package com.iluwatar.fallback;
22

33
import java.net.http.HttpClient;
44
import java.time.Duration;

fallback/src/main/java/com/iluwatar/CircuitBreaker.java renamed to fallback/src/main/java/com/iluwatar/fallback/CircuitBreaker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2323
* THE SOFTWARE.
2424
*/
25-
package com.iluwatar;
25+
package com.iluwatar.fallback;
2626

2727
/**
2828
* Interface defining the contract for a circuit breaker implementation.

fallback/src/main/java/com/iluwatar/DefaultCircuitBreaker.java renamed to fallback/src/main/java/com/iluwatar/fallback/DefaultCircuitBreaker.java

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2323
* THE SOFTWARE.
2424
*/
25-
package com.iluwatar;
25+
package com.iluwatar.fallback;
2626

2727
import java.time.Duration;
2828
import java.time.Instant;
@@ -76,14 +76,30 @@ public DefaultCircuitBreaker(final int failureThreshold) {
7676
*/
7777
@Override
7878
public synchronized boolean allowRequest() {
79-
checkAndTransitionState();
80-
return state != State.OPEN;
79+
if (state == State.CLOSED) {
80+
return true;
81+
}
82+
if (state == State.OPEN) {
83+
if (System.currentTimeMillis() - lastFailureTime > RESET_TIMEOUT) {
84+
transitionToHalfOpen();
85+
return true;
86+
}
87+
return false;
88+
}
89+
// In HALF_OPEN state, allow limited testing
90+
return true;
8191
}
8292

8393
@Override
8494
public synchronized boolean isOpen() {
85-
checkAndTransitionState();
86-
return state == State.OPEN;
95+
if (state == State.OPEN) {
96+
if (System.currentTimeMillis() - lastFailureTime > RESET_TIMEOUT) {
97+
transitionToHalfOpen();
98+
return false;
99+
}
100+
return true;
101+
}
102+
return false;
87103
}
88104

89105
/**
@@ -144,23 +160,16 @@ public void reset() {
144160
*/
145161
@Override
146162
public synchronized CircuitState getState() {
147-
checkAndTransitionState();
163+
if (state == State.OPEN && System.currentTimeMillis() - lastFailureTime > RESET_TIMEOUT) {
164+
transitionToHalfOpen();
165+
}
148166
return switch (state) {
149167
case CLOSED -> CircuitState.CLOSED;
150168
case OPEN -> CircuitState.OPEN;
151169
case HALF_OPEN -> CircuitState.HALF_OPEN;
152170
};
153171
}
154172

155-
/**
156-
* Checks if state transition is needed and performs it if necessary.
157-
*/
158-
private void checkAndTransitionState() {
159-
if (state == State.OPEN && System.currentTimeMillis() - lastFailureTime > RESET_TIMEOUT) {
160-
transitionToHalfOpen();
161-
}
162-
}
163-
164173
/**
165174
* Internal states of the circuit breaker.
166175
* Maps to the public CircuitState enum for external reporting.

fallback/src/main/java/com/iluwatar/FallbackService.java renamed to fallback/src/main/java/com/iluwatar/fallback/FallbackService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2323
* THE SOFTWARE.
2424
*/
25-
package com.iluwatar;
25+
package com.iluwatar.fallback;
2626

2727
import java.time.Duration;
2828
import java.time.Instant;

fallback/src/main/java/com/iluwatar/LocalCacheService.java renamed to fallback/src/main/java/com/iluwatar/fallback/LocalCacheService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2323
* THE SOFTWARE.
2424
*/
25-
package com.iluwatar;
25+
package com.iluwatar.fallback;
2626

2727
import ch.qos.logback.classic.Logger;
2828
import java.time.Duration;

fallback/src/main/java/com/iluwatar/RemoteService.java renamed to fallback/src/main/java/com/iluwatar/fallback/RemoteService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2323
* THE SOFTWARE.
2424
*/
25-
package com.iluwatar;
25+
package com.iluwatar.fallback;
2626

2727
import java.net.URI;
2828
import java.net.http.HttpClient;

fallback/src/main/java/com/iluwatar/Service.java renamed to fallback/src/main/java/com/iluwatar/fallback/Service.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2323
* THE SOFTWARE.
2424
*/
25-
package com.iluwatar;
25+
package com.iluwatar.fallback;
2626

2727
/**
2828
* Service interface that defines a method to retrieve data.

0 commit comments

Comments
 (0)