View Javadoc
1   package com.guinetik.rr.http;
2   
3   /**
4    * Exception thrown when a request is rejected due to an open circuit breaker.
5    *
6    * <p>This exception indicates that the downstream service is considered unhealthy and
7    * requests are being fast-failed to prevent cascading failures. It provides timing
8    * information to help callers decide when to retry.
9    *
10   * <h2>Handling Circuit Open</h2>
11   * <pre class="language-java"><code>
12   * try {
13   *     User user = client.get("/users/1", User.class);
14   * } catch (CircuitBreakerOpenException e) {
15   *     long waitTime = e.getEstimatedMillisUntilReset();
16   *
17   *     if (waitTime &gt; 0) {
18   *         System.out.println("Service unavailable, retry in " + waitTime + "ms");
19   *         // Schedule retry after waitTime
20   *     } else {
21   *         // Circuit should be half-open soon, retry immediately
22   *         System.out.println("Circuit may reset soon, retrying...");
23   *     }
24   * }
25   * </code></pre>
26   *
27   * <h2>Using with Fluent API</h2>
28   * <pre class="language-java"><code>
29   * Result&lt;User, ApiError&gt; result = client.fluent().get("/users/1", User.class);
30   *
31   * result.match(
32   *     user -&gt; System.out.println("Success"),
33   *     error -&gt; {
34   *         if (error.isCircuitOpen()) {
35   *             System.out.println("Circuit breaker is open");
36   *         }
37   *     }
38   * );
39   * </code></pre>
40   *
41   * @author guinetik &lt;guinetik@gmail.com&gt;
42   * @see CircuitBreakerClient
43   * @see RocketRestException
44   * @since 1.0.0
45   */
46  public class CircuitBreakerOpenException extends RocketRestException {
47      
48      private static final long serialVersionUID = 1L;
49      private final long millisSinceLastFailure;
50      private final long resetTimeoutMs;
51      
52      /**
53       * Creates a new CircuitBreakerOpenException with the specified message.
54       *
55       * @param message The error message
56       */
57      public CircuitBreakerOpenException(String message) {
58          this(message, 0, 0);
59      }
60      
61      /**
62       * Creates a new CircuitBreakerOpenException with the specified message 
63       * and timing information about when the circuit might reset.
64       *
65       * @param message The error message
66       * @param millisSinceLastFailure Milliseconds since the last failure
67       * @param resetTimeoutMs Milliseconds until the circuit will attempt to reset
68       */
69      public CircuitBreakerOpenException(String message, long millisSinceLastFailure, long resetTimeoutMs) {
70          super(message);
71          this.millisSinceLastFailure = millisSinceLastFailure;
72          this.resetTimeoutMs = resetTimeoutMs;
73      }
74      
75      /**
76       * Creates a new CircuitBreakerOpenException with the specified message, cause, 
77       * and timing information about when the circuit might reset.
78       *
79       * @param message The error message
80       * @param cause The cause of this exception
81       * @param millisSinceLastFailure Milliseconds since the last failure
82       * @param resetTimeoutMs Milliseconds until the circuit will attempt to reset
83       */
84      public CircuitBreakerOpenException(String message, Throwable cause, 
85                                        long millisSinceLastFailure, long resetTimeoutMs) {
86          super(message, cause);
87          this.millisSinceLastFailure = millisSinceLastFailure;
88          this.resetTimeoutMs = resetTimeoutMs;
89      }
90      
91      /**
92       * Gets the milliseconds since the last failure that caused the circuit to open.
93       *
94       * @return milliseconds since the last failure, or 0 if not available
95       */
96      public long getMillisSinceLastFailure() {
97          return millisSinceLastFailure;
98      }
99      
100     /**
101      * Gets the configured timeout after which the circuit will try to reset.
102      *
103      * @return reset timeout in milliseconds, or 0 if not available
104      */
105     public long getResetTimeoutMs() {
106         return resetTimeoutMs;
107     }
108     
109     /**
110      * Gets an estimated time in milliseconds until the circuit might reset.
111      * A negative value indicates the circuit should have already attempted to reset.
112      *
113      * @return estimated milliseconds until reset, or 0 if timing data is unavailable
114      */
115     public long getEstimatedMillisUntilReset() {
116         if (resetTimeoutMs > 0) {
117             return resetTimeoutMs - millisSinceLastFailure;
118         }
119         return 0;
120     }
121     
122     /**
123      * Gets the underlying cause of the circuit opening.
124      * This may be null if the circuit was already open when the request was made.
125      *
126      * @return the cause exception that led to the circuit opening, or null if not available
127      */
128     @Override
129     public Throwable getCause() {
130         return super.getCause();
131     }
132 }