/* * Copyright 2009 James Abley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.eternus.ratelimit.circuitbreaker; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; /** * Default implementation of {@link CircuitBreaker}. * * @author jabley * */ public class CircuitBreakerImpl implements CircuitBreaker { /** * The positive number of failed attempts allowed before this {@link CircuitBreaker} will trip. */ private final int threshold; /** * The timeout in milliseconds after which the open circuit breaker will attempt to reset. */ private final int timeout; /** * Count of the number of times this {@link CircuitBreaker} has been tripped. */ private final AtomicLong tripCount; /** * The non-null current {@link CircuitBreakerState}. */ private final AtomicReference<CircuitBreakerState> state; /** * The non-null list of {@link CircuitBreakerListener}s that wish to be notified of state changes. */ private final List<CircuitBreakerListener> listeners; /** * Creates a new {@link CircuitBreakerImpl} with the specified threshold and timeout. * * @param threshold * a positive number of failures allowed before this {@link CircuitBreaker} will trip * @param timeout * the time in milliseconds needed for this tripped {@link CircuitBreaker} to attempt a reset */ public CircuitBreakerImpl(int threshold, int timeout) { this.threshold = threshold; this.timeout = timeout; this.tripCount = new AtomicLong(); this.listeners = new ArrayList<CircuitBreakerListener>(); this.state = new AtomicReference<CircuitBreakerState>(new ClosedState(threshold)); } /** * {@inheritDoc} */ public void addListener(CircuitBreakerListener listener) { this.listeners.add(listener); } /** * {@inheritDoc} */ public void after() { getState().after(this); } /** * {@inheritDoc} */ public void attemptReset() { setState(new HalfOpenState()); notifyListeners(Notifications.ATTEMPT_RESET); } /** * {@inheritDoc} */ public void before() throws CircuitBreakerException { getState().before(this); } /** * {@inheritDoc} */ public void handleFailure() { getState().handleFailure(this); } /** * {@inheritDoc} */ public String getCurrentState() { return getState().toString(); } /** * {@inheritDoc} */ public int getThreshold() { return this.threshold; } /** * {@inheritDoc} */ public long getTimeToResetInMillis() { return getState().getTimeToReset(); } /** * {@inheritDoc} */ public long getTripCount() { return this.tripCount.get(); } /** * {@inheritDoc} */ public void tripBreaker() { tripCount.incrementAndGet(); setState(new OpenState(this.timeout)); notifyListeners(Notifications.TRIPPED); } /** * {@inheritDoc} */ public void reset() { setState(new ClosedState(threshold)); notifyListeners(Notifications.RESET); } /** * Returns the non-null current state. * * @return a non-null {@link CircuitBreakerState} */ private CircuitBreakerState getState() { return this.state.get(); } /** * Notify {@link CircuitBreakerListener}s with the appropriate {@link Notifications} function. * * @param notifications * a non-null {@link Notifications} */ private void notifyListeners(Notifications notifications) { for (CircuitBreakerListener listener : this.listeners) { try { notifications.notifyListener(listener); } catch (RuntimeException e) { /* ignore and carry on processing the others */ } } } /** * Sets the non-null new state. * * @param newState * the non-null new {@link CircuitBreakerState} */ private void setState(CircuitBreakerState newState) { this.state.set(newState); } /** * Simple interface defining a Functor for notifying listeners. * * @author jabley * */ interface NotifyListener { /** * Method called to notify {@link CircuitBreakerListener}s of a state change in this {@link CircuitBreaker}. * * @param listener * a non-null {@link CircuitBreakerListener} */ void notifyListener(CircuitBreakerListener listener); } /** * Enumeration defining the possible notifications that we can pass to {@link CircuitBreakerListener}s. * * @author jabley * */ private static enum Notifications implements NotifyListener { /** * {@link NotifyListener} implementation for when {@link CircuitBreaker#attemptReset()} has been called. */ ATTEMPT_RESET() { /** * {@inheritDoc} */ public void notifyListener(CircuitBreakerListener listener) { listener.attemptReset(); } }, /** * {@link NotifyListener} implementation for when {@link CircuitBreaker#reset()} has been called. */ RESET() { /** * {@inheritDoc} */ public void notifyListener(CircuitBreakerListener listener) { listener.reset(); } }, /** * {@link NotifyListener} implementation for when {@link CircuitBreaker#tripBreaker()} has been called. */ TRIPPED() { /** * {@inheritDoc} */ public void notifyListener(CircuitBreakerListener listener) { listener.tripped(); } }; /** * {@inheritDoc} */ public abstract void notifyListener(CircuitBreakerListener listener); } }