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