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: */
017:
018: package org.apache.catalina.valves;
019:
020: import java.io.IOException;
021: import java.util.concurrent.Semaphore;
022:
023: import javax.servlet.ServletException;
024:
025: import org.apache.catalina.Lifecycle;
026: import org.apache.catalina.LifecycleException;
027: import org.apache.catalina.LifecycleListener;
028: import org.apache.catalina.connector.Request;
029: import org.apache.catalina.connector.Response;
030: import org.apache.catalina.util.LifecycleSupport;
031: import org.apache.catalina.util.StringManager;
032:
033: /**
034: * <p>Implementation of a Valve that limits concurrency.</p>
035: *
036: * <p>This Valve may be attached to any Container, depending on the granularity
037: * of the concurrency control you wish to perform.</p>
038: *
039: * @author Remy Maucherat
040: * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
041: */
042:
043: public class SemaphoreValve extends ValveBase implements Lifecycle {
044:
045: // ----------------------------------------------------- Instance Variables
046:
047: /**
048: * The descriptive information related to this implementation.
049: */
050: private static final String info = "org.apache.catalina.valves.SemaphoreValve/1.0";
051:
052: /**
053: * The string manager for this package.
054: */
055: private StringManager sm = StringManager
056: .getManager(Constants.Package);
057:
058: /**
059: * Semaphore.
060: */
061: protected Semaphore semaphore = null;
062:
063: /**
064: * The lifecycle event support for this component.
065: */
066: protected LifecycleSupport lifecycle = new LifecycleSupport(this );
067:
068: /**
069: * Has this component been started yet?
070: */
071: private boolean started = false;
072:
073: // ------------------------------------------------------------- Properties
074:
075: /**
076: * Concurrency level of the semaphore.
077: */
078: protected int concurrency = 10;
079:
080: public int getConcurrency() {
081: return concurrency;
082: }
083:
084: public void setConcurrency(int concurrency) {
085: this .concurrency = concurrency;
086: }
087:
088: /**
089: * Fairness of the semaphore.
090: */
091: protected boolean fairness = false;
092:
093: public boolean getFairness() {
094: return fairness;
095: }
096:
097: public void setFairness(boolean fairness) {
098: this .fairness = fairness;
099: }
100:
101: /**
102: * Block until a permit is available.
103: */
104: protected boolean block = true;
105:
106: public boolean getBlock() {
107: return block;
108: }
109:
110: public void setBlock(boolean block) {
111: this .block = block;
112: }
113:
114: /**
115: * Block interruptibly until a permit is available.
116: */
117: protected boolean interruptible = false;
118:
119: public boolean getInterruptible() {
120: return interruptible;
121: }
122:
123: public void setInterruptible(boolean interruptible) {
124: this .interruptible = interruptible;
125: }
126:
127: // ------------------------------------------------------ Lifecycle Methods
128:
129: /**
130: * Add a lifecycle event listener to this component.
131: *
132: * @param listener The listener to add
133: */
134: public void addLifecycleListener(LifecycleListener listener) {
135:
136: lifecycle.addLifecycleListener(listener);
137:
138: }
139:
140: /**
141: * Get the lifecycle listeners associated with this lifecycle. If this
142: * Lifecycle has no listeners registered, a zero-length array is returned.
143: */
144: public LifecycleListener[] findLifecycleListeners() {
145:
146: return lifecycle.findLifecycleListeners();
147:
148: }
149:
150: /**
151: * Remove a lifecycle event listener from this component.
152: *
153: * @param listener The listener to add
154: */
155: public void removeLifecycleListener(LifecycleListener listener) {
156:
157: lifecycle.removeLifecycleListener(listener);
158:
159: }
160:
161: /**
162: * Prepare for the beginning of active use of the public methods of this
163: * component. This method should be called after <code>configure()</code>,
164: * and before any of the public methods of the component are utilized.
165: *
166: * @exception LifecycleException if this component detects a fatal error
167: * that prevents this component from being used
168: */
169: public void start() throws LifecycleException {
170:
171: // Validate and update our current component state
172: if (started)
173: throw new LifecycleException(sm
174: .getString("semaphoreValve.alreadyStarted"));
175: lifecycle.fireLifecycleEvent(START_EVENT, null);
176: started = true;
177:
178: semaphore = new Semaphore(concurrency, fairness);
179:
180: }
181:
182: /**
183: * Gracefully terminate the active use of the public methods of this
184: * component. This method should be the last one called on a given
185: * instance of this component.
186: *
187: * @exception LifecycleException if this component detects a fatal error
188: * that needs to be reported
189: */
190: public void stop() throws LifecycleException {
191:
192: // Validate and update our current component state
193: if (!started)
194: throw new LifecycleException(sm
195: .getString("semaphoreValve.notStarted"));
196: lifecycle.fireLifecycleEvent(STOP_EVENT, null);
197: started = false;
198:
199: semaphore = null;
200:
201: }
202:
203: // --------------------------------------------------------- Public Methods
204:
205: /**
206: * Return descriptive information about this Valve implementation.
207: */
208: public String getInfo() {
209: return (info);
210: }
211:
212: /**
213: * Do concurrency control on the request using the semaphore.
214: *
215: * @param request The servlet request to be processed
216: * @param response The servlet response to be created
217: *
218: * @exception IOException if an input/output error occurs
219: * @exception ServletException if a servlet error occurs
220: */
221: public void invoke(Request request, Response response)
222: throws IOException, ServletException {
223:
224: if (controlConcurrency(request, response)) {
225: boolean shouldRelease = true;
226: try {
227: if (block) {
228: if (interruptible) {
229: try {
230: semaphore.acquire();
231: } catch (InterruptedException e) {
232: shouldRelease = false;
233: permitDenied(request, response);
234: return;
235: }
236: } else {
237: semaphore.acquireUninterruptibly();
238: }
239: } else {
240: if (!semaphore.tryAcquire()) {
241: shouldRelease = false;
242: permitDenied(request, response);
243: return;
244: }
245: }
246: getNext().invoke(request, response);
247: } finally {
248: if (shouldRelease) {
249: semaphore.release();
250: }
251: }
252: } else {
253: getNext().invoke(request, response);
254: }
255:
256: }
257:
258: /**
259: * Subclass friendly method to add conditions.
260: */
261: public boolean controlConcurrency(Request request, Response response) {
262: return true;
263: }
264:
265: /**
266: * Subclass friendly method to add error handling when a permit isn't granted.
267: */
268: public void permitDenied(Request request, Response response)
269: throws IOException, ServletException {
270: }
271:
272: }
|