    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.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    @Override
    053    public boolean isOpen() {
    054        return isOpen(state.get());
    055    }
    056
    057    /**
    058     * {@inheritDoc}
    059     */
    060    @Override
    061    public boolean isClosed() {
    062        return !isOpen();
    063    }
    064
    065    /**
    066     * {@inheritDoc}
    067     */
    068    @Override
    069    public abstract boolean checkState();
    070
    071    /**
    072     * {@inheritDoc}
    073     */
    074    @Override
    075    public abstract boolean incrementAndCheckState(T increment);
    076
    077    /**
    078     * {@inheritDoc}
    079     */
    080    @Override
    081    public void close() {
    082        changeState(State.CLOSED);
    083    }
    084
    085    /**
    086     * {@inheritDoc}
    087     */
    088    @Override
    089    public void open() {
    090        changeState(State.OPEN);
    091    }
    092
    093    /**
    094     * Converts the given state value to a boolean <em>open</em> property.
    095     *
    096     * @param state the state to be converted
    097     * @return the boolean open flag
    098     */
    099    protected static boolean isOpen(final State state) {
    100        return state == State.OPEN;
    101    }
    102
    103    /**
    104     * Changes the internal state of this circuit breaker. If there is actually a change
    105     * of the state value, all registered change listeners are notified.
    106     *
    107     * @param newState the new state to be set
    108     */
    109    protected void changeState(final State newState) {
    110        if (state.compareAndSet(newState.oppositeState(), newState)) {
    111            changeSupport.firePropertyChange(PROPERTY_NAME, !isOpen(newState), isOpen(newState));
    112        }
    113    }
    114
    115    /**
    116     * Adds a change listener to this circuit breaker. This listener is notified whenever
    117     * the state of this circuit breaker changes. If the listener is
    118     * <strong>null</strong>, it is silently ignored.
    119     *
    120     * @param listener the listener to be added
    121     */
    122    public void addChangeListener(final PropertyChangeListener listener) {
    123        changeSupport.addPropertyChangeListener(listener);
    124    }
    125
    126    /**
    127     * Removes the specified change listener from this circuit breaker.
    128     *
    129     * @param listener the listener to be removed
    130     */
    131    public void removeChangeListener(final PropertyChangeListener listener) {
    132        changeSupport.removePropertyChangeListener(listener);
    133    }
    134
    135    /**
    136     * An internal enumeration representing the different states of a circuit
    137     * breaker. This class also contains some logic for performing state
    138     * transitions. This is done to avoid complex if-conditions in the code of
    139     * {@code CircuitBreaker}.
    140     */
    141    protected enum State {
    142        CLOSED {
    143            /**
    144             * {@inheritDoc}
    145             */
    146            @Override
    147            public State oppositeState() {
    148                return OPEN;
    149            }
    150        },
    151
    152        OPEN {
    153            /**
    154             * {@inheritDoc}
    155             */
    156            @Override
    157            public State oppositeState() {
    158                return CLOSED;
    159            }
    160        };
    161
    162        /**
    163         * Returns the opposite state to the represented state. This is useful
    164         * for flipping the current state.
    165         *
    166         * @return the opposite state
    167         */
    168        public abstract State oppositeState();
    169    }
    170
    171}