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