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.util.concurrent.atomic.AtomicLong;
020
021/**
022 * <p>
023 * A simple implementation of the <a
024 * href="http://martinfowler.com/bliki/CircuitBreaker.html">Circuit Breaker</a> pattern
025 * that opens if the requested increment amount is greater than a given threshold.
026 * </p>
027 *
028 * <p>
029 * It contains an internal counter that starts in zero, and each call increments the counter by a given amount.
030 * If the threshold is zero, the circuit breaker will be in a permanent <em>open</em> state.
031 * </p>
032 *
033 * <p>
034 * An example of use case could be a memory circuit breaker.
035 * </p>
036 *
037 * <pre>
038 * long threshold = 10L;
039 * ThresholdCircuitBreaker breaker = new ThresholdCircuitBreaker(10L);
040 * ...
041 * public void handleRequest(Request request) {
042 *     long memoryUsed = estimateMemoryUsage(request);
043 *     if (breaker.incrementAndCheckState(memoryUsed)) {
044 *         // actually handle this request
045 *     } else {
046 *         // do something else, e.g. send an error code
047 *     }
048 * }
049 * </pre>
050 *
051 * <p>#Thread safe#</p>
052 * @since 3.5
053 */
054public class ThresholdCircuitBreaker extends AbstractCircuitBreaker<Long> {
055    /**
056     * The initial value of the internal counter.
057     */
058    private static final long INITIAL_COUNT = 0L;
059
060    /**
061     * The threshold.
062     */
063    private final long threshold;
064
065    /**
066     * Controls the amount used.
067     */
068    private final AtomicLong used;
069
070    /**
071     * <p>Creates a new instance of {@code ThresholdCircuitBreaker} and initializes the threshold.</p>
072     *
073     * @param threshold the threshold.
074     */
075    public ThresholdCircuitBreaker(final long threshold) {
076        super();
077        this.used = new AtomicLong(INITIAL_COUNT);
078        this.threshold = threshold;
079    }
080
081    /**
082     * Gets the threshold.
083     *
084     * @return the threshold
085     */
086    public long getThreshold() {
087        return threshold;
088    }
089
090    /**
091     * {@inheritDoc}
092     */
093    @Override
094    public boolean checkState() throws CircuitBreakingException {
095        return isOpen();
096    }
097
098    /**
099     * {@inheritDoc}
100     *
101     * <p>Resets the internal counter back to its initial value (zero).</p>
102     */
103    @Override
104    public void close() {
105        super.close();
106        this.used.set(INITIAL_COUNT);
107    }
108
109    /**
110     * {@inheritDoc}
111     *
112     * <p>If the threshold is zero, the circuit breaker will be in a permanent <em>open</em> state.</p>
113     */
114    @Override
115    public boolean incrementAndCheckState(final Long increment) throws CircuitBreakingException {
116        if (threshold == 0) {
117            open();
118        }
119
120        final long used = this.used.addAndGet(increment);
121        if (used > threshold) {
122            open();
123        }
124
125        return checkState();
126    }
127
128}