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