001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.lang3.concurrent; 018 019import java.beans.PropertyChangeListener; 020import java.beans.PropertyChangeSupport; 021import java.util.concurrent.atomic.AtomicReference; 022 023/** 024 * Base class for circuit breakers. 025 * 026 * @param <T> the type of the value monitored by this circuit breaker 027 * @since 3.5 028 */ 029public abstract class AbstractCircuitBreaker<T> implements CircuitBreaker<T> { 030 /** 031 * The name of the <em>open</em> property as it is passed to registered 032 * change listeners. 033 */ 034 public static final String PROPERTY_NAME = "open"; 035 036 /** The current state of this circuit breaker. */ 037 protected final AtomicReference<State> state = new AtomicReference<State>(State.CLOSED); 038 039 /** An object for managing change listeners registered at this instance. */ 040 private final PropertyChangeSupport changeSupport; 041 042 /** 043 * Creates an {@code AbstractCircuitBreaker}. It also creates an internal {@code PropertyChangeSupport}. 044 */ 045 public AbstractCircuitBreaker() { 046 changeSupport = new PropertyChangeSupport(this); 047 } 048 049 /** 050 * {@inheritDoc} 051 */ 052 public boolean isOpen() { 053 return isOpen(state.get()); 054 } 055 056 /** 057 * {@inheritDoc} 058 */ 059 public boolean isClosed() { 060 return !isOpen(); 061 } 062 063 /** 064 * {@inheritDoc} 065 */ 066 public abstract boolean checkState(); 067 068 /** 069 * {@inheritDoc} 070 */ 071 public abstract boolean incrementAndCheckState(T increment); 072 073 /** 074 * {@inheritDoc} 075 */ 076 public void close() { 077 changeState(State.CLOSED); 078 } 079 080 /** 081 * {@inheritDoc} 082 */ 083 public void open() { 084 changeState(State.OPEN); 085 } 086 087 /** 088 * Converts the given state value to a boolean <em>open</em> property. 089 * 090 * @param state the state to be converted 091 * @return the boolean open flag 092 */ 093 protected static boolean isOpen(State state) { 094 return state == State.OPEN; 095 } 096 097 /** 098 * Changes the internal state of this circuit breaker. If there is actually a change 099 * of the state value, all registered change listeners are notified. 100 * 101 * @param newState the new state to be set 102 */ 103 protected void changeState(State newState) { 104 if (state.compareAndSet(newState.oppositeState(), newState)) { 105 changeSupport.firePropertyChange(PROPERTY_NAME, !isOpen(newState), isOpen(newState)); 106 } 107 } 108 109 /** 110 * Adds a change listener to this circuit breaker. This listener is notified whenever 111 * the state of this circuit breaker changes. If the listener is 112 * <strong>null</strong>, it is silently ignored. 113 * 114 * @param listener the listener to be added 115 */ 116 public void addChangeListener(PropertyChangeListener listener) { 117 changeSupport.addPropertyChangeListener(listener); 118 } 119 120 /** 121 * Removes the specified change listener from this circuit breaker. 122 * 123 * @param listener the listener to be removed 124 */ 125 public void removeChangeListener(PropertyChangeListener listener) { 126 changeSupport.removePropertyChangeListener(listener); 127 } 128 129 /** 130 * An internal enumeration representing the different states of a circuit 131 * breaker. This class also contains some logic for performing state 132 * transitions. This is done to avoid complex if-conditions in the code of 133 * {@code CircuitBreaker}. 134 */ 135 protected static enum State { 136 CLOSED { 137 /** 138 * {@inheritDoc} 139 */ 140 @Override 141 public State oppositeState() { 142 return OPEN; 143 } 144 }, 145 146 OPEN { 147 /** 148 * {@inheritDoc} 149 */ 150 @Override 151 public State oppositeState() { 152 return CLOSED; 153 } 154 }; 155 156 /** 157 * Returns the opposite state to the represented state. This is useful 158 * for flipping the current state. 159 * 160 * @return the opposite state 161 */ 162 public abstract State oppositeState(); 163 } 164 165}