001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.util;
018:
019: import java.io.IOException;
020: import java.io.ObjectInputStream;
021: import java.io.Serializable;
022:
023: import org.apache.commons.logging.Log;
024: import org.apache.commons.logging.LogFactory;
025:
026: /**
027: * Support class for throttling concurrent access to a specific resource.
028: *
029: * <p>Designed for use as a base class, with the subclass invoking
030: * the {@link #beforeAccess()} and {@link #afterAccess()} methods at
031: * appropriate points of its workflow. Note that <code>afterAccess</code>
032: * should usually be called in a finally block!
033: *
034: * <p>The default concurrency limit of this support class is -1
035: * ("unbounded concurrency"). Subclasses may override this default;
036: * check the javadoc of the concrete class that you're using.
037: *
038: * @author Juergen Hoeller
039: * @since 1.2.5
040: * @see #setConcurrencyLimit
041: * @see #beforeAccess()
042: * @see #afterAccess()
043: * @see org.springframework.aop.interceptor.ConcurrencyThrottleInterceptor
044: * @see java.io.Serializable
045: */
046: public abstract class ConcurrencyThrottleSupport implements
047: Serializable {
048:
049: /**
050: * Permit any number of concurrent invocations: that is, don't throttle concurrency.
051: */
052: public static final int UNBOUNDED_CONCURRENCY = -1;
053:
054: /**
055: * Switch concurrency 'off': that is, don't allow any concurrent invocations.
056: */
057: public static final int NO_CONCURRENCY = 0;
058:
059: /** Transient to optimize serialization */
060: protected transient Log logger = LogFactory.getLog(getClass());
061:
062: private transient Object monitor = new Object();
063:
064: private int concurrencyLimit = UNBOUNDED_CONCURRENCY;
065:
066: private int concurrencyCount = 0;
067:
068: /**
069: * Set the maximum number of concurrent access attempts allowed.
070: * -1 indicates unbounded concurrency.
071: * <p>In principle, this limit can be changed at runtime,
072: * although it is generally designed as a config time setting.
073: * <p>NOTE: Do not switch between -1 and any concrete limit at runtime,
074: * as this will lead to inconsistent concurrency counts: A limit
075: * of -1 effectively turns off concurrency counting completely.
076: */
077: public void setConcurrencyLimit(int concurrencyLimit) {
078: this .concurrencyLimit = concurrencyLimit;
079: }
080:
081: /**
082: * Return the maximum number of concurrent access attempts allowed.
083: */
084: public int getConcurrencyLimit() {
085: return this .concurrencyLimit;
086: }
087:
088: /**
089: * Return whether this throttle is currently active.
090: * @return <code>true</code> if the concurrency limit for this instance is active
091: * @see #getConcurrencyLimit()
092: */
093: public boolean isThrottleActive() {
094: return (this .concurrencyLimit > 0);
095: }
096:
097: /**
098: * To be invoked before the main execution logic of concrete subclasses.
099: * <p>This implementation applies the concurrency throttle.
100: * @see #afterAccess()
101: */
102: protected void beforeAccess() {
103: if (this .concurrencyLimit == NO_CONCURRENCY) {
104: throw new IllegalStateException(
105: "Currently no invocations allowed - concurrency limit set to NO_CONCURRENCY");
106: }
107: if (this .concurrencyLimit > 0) {
108: boolean debug = logger.isDebugEnabled();
109: synchronized (this .monitor) {
110: while (this .concurrencyCount >= this .concurrencyLimit) {
111: if (debug) {
112: logger
113: .debug("Concurrency count "
114: + this .concurrencyCount
115: + " has reached limit "
116: + this .concurrencyLimit
117: + " - blocking");
118: }
119: try {
120: this .monitor.wait();
121: } catch (InterruptedException ex) {
122: // Re-interrupt current thread, to allow other threads to react.
123: Thread.currentThread().interrupt();
124: }
125: }
126: if (debug) {
127: logger
128: .debug("Entering throttle at concurrency count "
129: + this .concurrencyCount);
130: }
131: this .concurrencyCount++;
132: }
133: }
134: }
135:
136: /**
137: * To be invoked after the main execution logic of concrete subclasses.
138: * @see #beforeAccess()
139: */
140: protected void afterAccess() {
141: if (this .concurrencyLimit >= 0) {
142: synchronized (this .monitor) {
143: this .concurrencyCount--;
144: if (logger.isDebugEnabled()) {
145: logger
146: .debug("Returning from throttle at concurrency count "
147: + this .concurrencyCount);
148: }
149: this .monitor.notify();
150: }
151: }
152: }
153:
154: //---------------------------------------------------------------------
155: // Serialization support
156: //---------------------------------------------------------------------
157:
158: private void readObject(ObjectInputStream ois) throws IOException,
159: ClassNotFoundException {
160: // Rely on default serialization, just initialize state after deserialization.
161: ois.defaultReadObject();
162:
163: // Initialize transient fields.
164: this .logger = LogFactory.getLog(getClass());
165: this .monitor = new Object();
166: }
167:
168: }
|