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 final static 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(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(Long increment) throws CircuitBreakingException { 116 if (threshold == 0) { 117 open(); 118 } 119 120 long used = this.used.addAndGet(increment); 121 if (used > threshold) { 122 open(); 123 } 124 125 return checkState(); 126 } 127 128}