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+ }
0 commit comments