001: /*
002: * Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: */
007: package winstone;
008:
009: import java.io.IOException;
010: import java.net.Socket;
011: import java.util.ArrayList;
012: import java.util.Collection;
013: import java.util.Iterator;
014: import java.util.List;
015: import java.util.Map;
016:
017: /**
018: * Holds the object pooling code for Winstone. Presently this is only responses
019: * and requests, but may increase.
020: *
021: * @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
022: * @version $Id: ObjectPool.java,v 1.9 2006/11/18 14:56:59 rickknowles Exp $
023: */
024: public class ObjectPool implements Runnable {
025: private static final long FLUSH_PERIOD = 60000L;
026:
027: private int STARTUP_REQUEST_HANDLERS_IN_POOL = 5;
028: private int MAX_IDLE_REQUEST_HANDLERS_IN_POOL = 50;
029: private int MAX_REQUEST_HANDLERS_IN_POOL = 1000;
030: private long RETRY_PERIOD = 1000;
031: private int START_REQUESTS_IN_POOL = 10;
032: private int MAX_REQUESTS_IN_POOL = 1000;
033: private int START_RESPONSES_IN_POOL = 10;
034: private int MAX_RESPONSES_IN_POOL = 1000;
035: private List unusedRequestHandlerThreads;
036: private List usedRequestHandlerThreads;
037: private List usedRequestPool;
038: private List unusedRequestPool;
039: private List usedResponsePool;
040: private List unusedResponsePool;
041: private Object requestHandlerSemaphore = new Boolean(true);
042: private Object requestPoolSemaphore = new Boolean(true);
043: private Object responsePoolSemaphore = new Boolean(true);
044: private int threadIndex = 0;
045: private boolean simulateModUniqueId;
046: private boolean saveSessions;
047:
048: private Thread thread;
049:
050: /**
051: * Constructs an instance of the object pool, including handlers, requests
052: * and responses
053: */
054: public ObjectPool(Map args) throws IOException {
055: this .simulateModUniqueId = WebAppConfiguration.booleanArg(args,
056: "simulateModUniqueId", false);
057: this .saveSessions = WebAppConfiguration.useSavedSessions(args);
058:
059: // Build the initial pool of handler threads
060: this .unusedRequestHandlerThreads = new ArrayList();
061: this .usedRequestHandlerThreads = new ArrayList();
062:
063: // Build the request/response pools
064: this .usedRequestPool = new ArrayList();
065: this .usedResponsePool = new ArrayList();
066: this .unusedRequestPool = new ArrayList();
067: this .unusedResponsePool = new ArrayList();
068:
069: // Get handler pool options
070: if (args.get("handlerCountStartup") != null) {
071: STARTUP_REQUEST_HANDLERS_IN_POOL = Integer
072: .parseInt((String) args.get("handlerCountStartup"));
073: }
074: if (args.get("handlerCountMax") != null) {
075: MAX_IDLE_REQUEST_HANDLERS_IN_POOL = Integer
076: .parseInt((String) args.get("handlerCountMax"));
077: }
078: if (args.get("handlerCountMaxIdle") != null) {
079: MAX_IDLE_REQUEST_HANDLERS_IN_POOL = Integer
080: .parseInt((String) args.get("handlerCountMaxIdle"));
081: }
082:
083: // Start the base set of handler threads
084: for (int n = 0; n < STARTUP_REQUEST_HANDLERS_IN_POOL; n++) {
085: this .unusedRequestHandlerThreads
086: .add(new RequestHandlerThread(this ,
087: this .threadIndex++,
088: this .simulateModUniqueId, this .saveSessions));
089: }
090:
091: // Initialise the request/response pools
092: for (int n = 0; n < START_REQUESTS_IN_POOL; n++) {
093: this .unusedRequestPool.add(new WinstoneRequest());
094: }
095: for (int n = 0; n < START_RESPONSES_IN_POOL; n++) {
096: this .unusedResponsePool.add(new WinstoneResponse());
097: }
098:
099: this .thread = new Thread(this , "WinstoneObjectPoolMgmt");
100: this .thread.setDaemon(true);
101: this .thread.start();
102: }
103:
104: public void run() {
105: boolean interrupted = false;
106: while (!interrupted) {
107: try {
108: Thread.sleep(FLUSH_PERIOD);
109: removeUnusedRequestHandlers();
110: } catch (InterruptedException err) {
111: interrupted = true;
112: }
113: }
114: this .thread = null;
115: }
116:
117: private void removeUnusedRequestHandlers() {
118: // Check max idle requestHandler count
119: synchronized (this .requestHandlerSemaphore) {
120: // If we have too many idle request handlers
121: while (this .unusedRequestHandlerThreads.size() > MAX_IDLE_REQUEST_HANDLERS_IN_POOL) {
122: RequestHandlerThread rh = (RequestHandlerThread) this .unusedRequestHandlerThreads
123: .get(0);
124: rh.destroy();
125: this .unusedRequestHandlerThreads.remove(rh);
126: }
127: }
128: }
129:
130: public void destroy() {
131: synchronized (this .requestHandlerSemaphore) {
132: Collection usedHandlers = new ArrayList(
133: this .usedRequestHandlerThreads);
134: for (Iterator i = usedHandlers.iterator(); i.hasNext();)
135: releaseRequestHandler((RequestHandlerThread) i.next());
136: Collection unusedHandlers = new ArrayList(
137: this .unusedRequestHandlerThreads);
138: for (Iterator i = unusedHandlers.iterator(); i.hasNext();)
139: ((RequestHandlerThread) i.next()).destroy();
140: this .unusedRequestHandlerThreads.clear();
141: }
142: if (this .thread != null) {
143: this .thread.interrupt();
144: }
145: }
146:
147: /**
148: * Once the socket request comes in, this method is called. It reserves a
149: * request handler, then delegates the socket to that class. When it
150: * finishes, the handler is released back into the pool.
151: */
152: public void handleRequest(Socket socket, Listener listener)
153: throws IOException, InterruptedException {
154: RequestHandlerThread rh = null;
155: synchronized (this .requestHandlerSemaphore) {
156: // If we have any spare, get it from the pool
157: int unused = this .unusedRequestHandlerThreads.size();
158: if (unused > 0) {
159: rh = (RequestHandlerThread) this .unusedRequestHandlerThreads
160: .remove(unused - 1);
161: this .usedRequestHandlerThreads.add(rh);
162: Logger
163: .log(
164: Logger.FULL_DEBUG,
165: Launcher.RESOURCES,
166: "ObjectPool.UsingRHPoolThread",
167: new String[] {
168: ""
169: + this .usedRequestHandlerThreads
170: .size(),
171: ""
172: + this .unusedRequestHandlerThreads
173: .size() });
174: }
175:
176: // If we are out (and not over our limit), allocate a new one
177: else if (this .usedRequestHandlerThreads.size() < MAX_REQUEST_HANDLERS_IN_POOL) {
178: rh = new RequestHandlerThread(this , this .threadIndex++,
179: this .simulateModUniqueId, this .saveSessions);
180: this .usedRequestHandlerThreads.add(rh);
181: Logger
182: .log(
183: Logger.FULL_DEBUG,
184: Launcher.RESOURCES,
185: "ObjectPool.NewRHPoolThread",
186: new String[] {
187: ""
188: + this .usedRequestHandlerThreads
189: .size(),
190: ""
191: + this .unusedRequestHandlerThreads
192: .size() });
193: }
194:
195: // otherwise throw fail message - we've blown our limit
196: else {
197: // Possibly insert a second chance here ? Delay and one retry ?
198: // Remember to release the lock first
199: Logger.log(Logger.WARNING, Launcher.RESOURCES,
200: "ObjectPool.NoRHPoolThreadsRetry");
201: // socket.close();
202: // throw new UnavailableException("NoHandlersAvailable");
203: }
204: }
205:
206: if (rh != null)
207: rh.commenceRequestHandling(socket, listener);
208: else {
209: // Sleep for a set period and try again from the pool
210: Thread.sleep(RETRY_PERIOD);
211:
212: synchronized (this .requestHandlerSemaphore) {
213: if (this .usedRequestHandlerThreads.size() < MAX_REQUEST_HANDLERS_IN_POOL) {
214: rh = new RequestHandlerThread(this ,
215: this .threadIndex++,
216: this .simulateModUniqueId, this .saveSessions);
217: this .usedRequestHandlerThreads.add(rh);
218: Logger
219: .log(
220: Logger.FULL_DEBUG,
221: Launcher.RESOURCES,
222: "ObjectPool.NewRHPoolThread",
223: new String[] {
224: ""
225: + this .usedRequestHandlerThreads
226: .size(),
227: ""
228: + this .unusedRequestHandlerThreads
229: .size() });
230: }
231: }
232: if (rh != null)
233: rh.commenceRequestHandling(socket, listener);
234: else {
235: Logger.log(Logger.WARNING, Launcher.RESOURCES,
236: "ObjectPool.NoRHPoolThreads");
237: socket.close();
238: }
239: }
240: }
241:
242: /**
243: * Release the handler back into the pool
244: */
245: public void releaseRequestHandler(RequestHandlerThread rh) {
246: synchronized (this .requestHandlerSemaphore) {
247: this .usedRequestHandlerThreads.remove(rh);
248: this .unusedRequestHandlerThreads.add(rh);
249: Logger.log(Logger.FULL_DEBUG, Launcher.RESOURCES,
250: "ObjectPool.ReleasingRHPoolThread", new String[] {
251: "" + this .usedRequestHandlerThreads.size(),
252: ""
253: + this .unusedRequestHandlerThreads
254: .size() });
255: }
256: }
257:
258: /**
259: * An attempt at pooling request objects for reuse.
260: */
261: public WinstoneRequest getRequestFromPool() throws IOException {
262: WinstoneRequest req = null;
263: synchronized (this .requestPoolSemaphore) {
264: // If we have any spare, get it from the pool
265: int unused = this .unusedRequestPool.size();
266: if (unused > 0) {
267: req = (WinstoneRequest) this .unusedRequestPool
268: .remove(unused - 1);
269: this .usedRequestPool.add(req);
270: Logger.log(Logger.FULL_DEBUG, Launcher.RESOURCES,
271: "ObjectPool.UsingRequestFromPool", ""
272: + this .unusedRequestPool.size());
273: }
274: // If we are out, allocate a new one
275: else if (this .usedRequestPool.size() < MAX_REQUESTS_IN_POOL) {
276: req = new WinstoneRequest();
277: this .usedRequestPool.add(req);
278: Logger.log(Logger.FULL_DEBUG, Launcher.RESOURCES,
279: "ObjectPool.NewRequestForPool", ""
280: + this .usedRequestPool.size());
281: } else
282: throw new WinstoneException(
283: Launcher.RESOURCES
284: .getString("ObjectPool.PoolRequestLimitExceeded"));
285: }
286: return req;
287: }
288:
289: public void releaseRequestToPool(WinstoneRequest req) {
290: req.cleanUp();
291: synchronized (this .requestPoolSemaphore) {
292: this .usedRequestPool.remove(req);
293: this .unusedRequestPool.add(req);
294: Logger.log(Logger.FULL_DEBUG, Launcher.RESOURCES,
295: "ObjectPool.RequestReleased", ""
296: + this .unusedRequestPool.size());
297: }
298: }
299:
300: /**
301: * An attempt at pooling request objects for reuse.
302: */
303: public WinstoneResponse getResponseFromPool() throws IOException {
304: WinstoneResponse rsp = null;
305: synchronized (this .responsePoolSemaphore) {
306: // If we have any spare, get it from the pool
307: int unused = this .unusedResponsePool.size();
308: if (unused > 0) {
309: rsp = (WinstoneResponse) this .unusedResponsePool
310: .remove(unused - 1);
311: this .usedResponsePool.add(rsp);
312: Logger.log(Logger.FULL_DEBUG, Launcher.RESOURCES,
313: "ObjectPool.UsingResponseFromPool", ""
314: + this .unusedResponsePool.size());
315: }
316: // If we are out, allocate a new one
317: else if (this .usedResponsePool.size() < MAX_RESPONSES_IN_POOL) {
318: rsp = new WinstoneResponse();
319: this .usedResponsePool.add(rsp);
320: Logger.log(Logger.FULL_DEBUG, Launcher.RESOURCES,
321: "ObjectPool.NewResponseForPool", ""
322: + this .usedResponsePool.size());
323: } else
324: throw new WinstoneException(
325: Launcher.RESOURCES
326: .getString("ObjectPool.PoolResponseLimitExceeded"));
327: }
328: return rsp;
329: }
330:
331: public void releaseResponseToPool(WinstoneResponse rsp) {
332: rsp.cleanUp();
333: synchronized (this .responsePoolSemaphore) {
334: this .usedResponsePool.remove(rsp);
335: this .unusedResponsePool.add(rsp);
336: Logger.log(Logger.FULL_DEBUG, Launcher.RESOURCES,
337: "ObjectPool.ResponseReleased", ""
338: + this.unusedResponsePool.size());
339: }
340: }
341:
342: }
|