| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| |
| package org.apache.catalina.valves; |
| |
| |
| import java.io.IOException; |
| import java.util.concurrent.Semaphore; |
| |
| import javax.servlet.ServletException; |
| |
| import org.apache.catalina.Lifecycle; |
| import org.apache.catalina.LifecycleException; |
| import org.apache.catalina.LifecycleListener; |
| import org.apache.catalina.connector.Request; |
| import org.apache.catalina.connector.Response; |
| import org.apache.catalina.util.LifecycleSupport; |
| import org.apache.catalina.util.StringManager; |
| |
| |
| /** |
| * <p>Implementation of a Valve that limits concurrency.</p> |
| * |
| * <p>This Valve may be attached to any Container, depending on the granularity |
| * of the concurrency control you wish to perform.</p> |
| * |
| * @author Remy Maucherat |
| * @version $Revision$ $Date$ |
| */ |
| |
| public class SemaphoreValve |
| extends ValveBase |
| implements Lifecycle { |
| |
| |
| // ----------------------------------------------------- Instance Variables |
| |
| |
| /** |
| * The descriptive information related to this implementation. |
| */ |
| private static final String info = |
| "org.apache.catalina.valves.SemaphoreValve/1.0"; |
| |
| |
| /** |
| * The string manager for this package. |
| */ |
| private StringManager sm = |
| StringManager.getManager(Constants.Package); |
| |
| |
| /** |
| * Semaphore. |
| */ |
| protected Semaphore semaphore = null; |
| |
| |
| /** |
| * The lifecycle event support for this component. |
| */ |
| protected LifecycleSupport lifecycle = new LifecycleSupport(this); |
| |
| |
| /** |
| * Has this component been started yet? |
| */ |
| private boolean started = false; |
| |
| |
| // ------------------------------------------------------------- Properties |
| |
| |
| /** |
| * Concurrency level of the semaphore. |
| */ |
| protected int concurrency = 10; |
| public int getConcurrency() { return concurrency; } |
| public void setConcurrency(int concurrency) { this.concurrency = concurrency; } |
| |
| |
| /** |
| * Fairness of the semaphore. |
| */ |
| protected boolean fairness = false; |
| public boolean getFairness() { return fairness; } |
| public void setFairness(boolean fairness) { this.fairness = fairness; } |
| |
| |
| /** |
| * Block until a permit is available. |
| */ |
| protected boolean block = true; |
| public boolean getBlock() { return block; } |
| public void setBlock(boolean block) { this.block = block; } |
| |
| |
| /** |
| * Block interruptibly until a permit is available. |
| */ |
| protected boolean interruptible = false; |
| public boolean getInterruptible() { return interruptible; } |
| public void setInterruptible(boolean interruptible) { this.interruptible = interruptible; } |
| |
| |
| // ------------------------------------------------------ Lifecycle Methods |
| |
| |
| /** |
| * Add a lifecycle event listener to this component. |
| * |
| * @param listener The listener to add |
| */ |
| public void addLifecycleListener(LifecycleListener listener) { |
| |
| lifecycle.addLifecycleListener(listener); |
| |
| } |
| |
| |
| /** |
| * Get the lifecycle listeners associated with this lifecycle. If this |
| * Lifecycle has no listeners registered, a zero-length array is returned. |
| */ |
| public LifecycleListener[] findLifecycleListeners() { |
| |
| return lifecycle.findLifecycleListeners(); |
| |
| } |
| |
| |
| /** |
| * Remove a lifecycle event listener from this component. |
| * |
| * @param listener The listener to add |
| */ |
| public void removeLifecycleListener(LifecycleListener listener) { |
| |
| lifecycle.removeLifecycleListener(listener); |
| |
| } |
| |
| |
| /** |
| * Prepare for the beginning of active use of the public methods of this |
| * component. This method should be called after <code>configure()</code>, |
| * and before any of the public methods of the component are utilized. |
| * |
| * @exception LifecycleException if this component detects a fatal error |
| * that prevents this component from being used |
| */ |
| public void start() throws LifecycleException { |
| |
| // Validate and update our current component state |
| if (started) |
| throw new LifecycleException |
| (sm.getString("semaphoreValve.alreadyStarted")); |
| lifecycle.fireLifecycleEvent(START_EVENT, null); |
| started = true; |
| |
| semaphore = new Semaphore(concurrency, fairness); |
| |
| } |
| |
| |
| /** |
| * Gracefully terminate the active use of the public methods of this |
| * component. This method should be the last one called on a given |
| * instance of this component. |
| * |
| * @exception LifecycleException if this component detects a fatal error |
| * that needs to be reported |
| */ |
| public void stop() throws LifecycleException { |
| |
| // Validate and update our current component state |
| if (!started) |
| throw new LifecycleException |
| (sm.getString("semaphoreValve.notStarted")); |
| lifecycle.fireLifecycleEvent(STOP_EVENT, null); |
| started = false; |
| |
| semaphore = null; |
| |
| } |
| |
| |
| // --------------------------------------------------------- Public Methods |
| |
| |
| /** |
| * Return descriptive information about this Valve implementation. |
| */ |
| public String getInfo() { |
| return (info); |
| } |
| |
| |
| /** |
| * Do concurrency control on the request using the semaphore. |
| * |
| * @param request The servlet request to be processed |
| * @param response The servlet response to be created |
| * |
| * @exception IOException if an input/output error occurs |
| * @exception ServletException if a servlet error occurs |
| */ |
| public void invoke(Request request, Response response) |
| throws IOException, ServletException { |
| |
| if (controlConcurrency(request, response)) { |
| boolean shouldRelease = true; |
| try { |
| if (block) { |
| if (interruptible) { |
| try { |
| semaphore.acquire(); |
| } catch (InterruptedException e) { |
| shouldRelease = false; |
| permitDenied(request, response); |
| return; |
| } |
| } else { |
| semaphore.acquireUninterruptibly(); |
| } |
| } else { |
| if (!semaphore.tryAcquire()) { |
| shouldRelease = false; |
| permitDenied(request, response); |
| return; |
| } |
| } |
| getNext().invoke(request, response); |
| } finally { |
| if (shouldRelease) { |
| semaphore.release(); |
| } |
| } |
| } else { |
| getNext().invoke(request, response); |
| } |
| |
| } |
| |
| |
| /** |
| * Subclass friendly method to add conditions. |
| */ |
| public boolean controlConcurrency(Request request, Response response) { |
| return true; |
| } |
| |
| |
| /** |
| * Subclass friendly method to add error handling when a permit isn't granted. |
| */ |
| public void permitDenied(Request request, Response response) |
| throws IOException, ServletException { |
| } |
| |
| |
| } |