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}