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}