KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > barracuda > core > event > DefaultEventPool


1 /*
2  * Copyright (C) 2003 Christian Cryder [christianc@granitepeaks.com]
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * $Id: DefaultEventPool.java,v 1.14 2004/02/01 05:16:28 christianc Exp $
19  */

20 package org.enhydra.barracuda.core.event;
21
22 import java.util.*;
23 import java.lang.reflect.*;
24
25 import org.apache.log4j.*;
26
27 /**
28  * This class acts as a pool for Events. Should significantly improve
29  * performance by allowing us to reuse event objects.
30  */

31 public class DefaultEventPool implements EventPool {
32
33     //public constants
34
protected static final Logger logger = Logger.getLogger(DefaultEventPool.class.getName());
35
36     //public vars
37
public static int DEFAULT_POOL_SIZE = 50; //csc_060903_1
38
public static long DEFAULT_TIMEOUT = 60000; //csc_060903_1
39
public static long DEFAULT_RETRY_INTERVAL = 50; //csc_060903_1
40
public static int DEFAULT_MAX_RETRIES = 3; //csc_060903_1
41
public static long DEFAULT_CLEANUP_INTERVAL = 600000; //csc_060903_1
42

43     //private vars
44
protected Map eventMap = null;
45     protected int poolSize = -1;
46     protected long timeout = -1;
47     protected long retryInterval = -1;
48     protected int maxRetries = -1;
49     protected long cleanupInterval = -1;
50     protected long lastUpdate = -1;
51     protected long lastCleanup = -1;
52     protected Object JavaDoc sync = new Object JavaDoc();
53     private Thread JavaDoc thread = null;
54     private boolean stayAlive = true;
55     
56     /**
57      * Default constructor. Defaults to pool size of 50 with
58      * a timeout of 60 seconds. Retry interval of 50 millis,
59      * with max retries of 3. Cleanup thread runs every 10
60      * minutes.
61      */

62     public DefaultEventPool() {
63 // this(50,15000, 50, 3, 90000); //short version for testing purposes
64
//csc_060903_1 this(50,60000, 50, 3, 600000);
65
this(DEFAULT_POOL_SIZE, DEFAULT_TIMEOUT, DEFAULT_RETRY_INTERVAL, DEFAULT_MAX_RETRIES, DEFAULT_CLEANUP_INTERVAL); //csc_060903_1
66
}
67     
68     /**
69      * Public constructor.
70      *
71      * @param ipoolSize - how many event instances to keep for each
72      * class of event)
73      * @param itimeout - how long may an event be checked out before
74      * it may be reclaimed by the pool
75      * @param iretryInterval - how long between retries if there are no
76      * events currently available
77      * @param imaxRetries - maximum number of retries
78      * @param icleanupInterval - how often should the cleanup thread run (millisecs).
79      */

80     public DefaultEventPool(int ipoolSize, long itimeout, long iretryInterval, int imaxRetries, long icleanupInterval) {
81         //set up working vars
82
eventMap = new HashMap();
83 // eventMap = new TreeMap();
84
poolSize = ipoolSize;
85         timeout = itimeout;
86         retryInterval = iretryInterval;
87         maxRetries = imaxRetries;
88         cleanupInterval = icleanupInterval;
89         if (logger.isInfoEnabled()) logger.info("Instantiating EventPool:"+this+" Pool size:"+poolSize+" Timeout:"+timeout+" Retry Interval:"+retryInterval+" Max Retries:"+maxRetries+" Cleanup Interval:"+cleanupInterval);
90         
91         //start the cleanup thread
92
thread = new Thread JavaDoc(new EventListCleanerUpper());
93         thread.setName("Barracuda event pool cleanup thread");
94         thread.start();
95     }
96
97     /**
98      * check out an event from the EventPool.
99      *
100      * @param event the class of event we are interested in checking out
101      * @return the checked out event
102      * @throws NoAvailableEventsException if there are no available events in the queue
103      * @throws InvalidClassException if the event class is not valid
104      */

105     public BaseEvent checkoutEvent(Class JavaDoc event) throws NoAvailableEventsException, InvalidClassException {
106         if (logger.isInfoEnabled()) logger.info("Checking out event "+event.getName());
107         if (!((BaseEvent.class).isAssignableFrom(event))) throw new InvalidClassException ("Class "+event.getName()+" is not a BaseEvent");
108
109         //we catch NoAvailbleEventsExceptions at this level so as
110
//to allow for an unlock on the sync object (thus allowing other
111
//threads to check in events while we sleep).
112
int cntr = 1;
113         BaseEvent be = null;
114         while (be==null) try {
115             synchronized (sync) {
116                 EventList el = (EventList) eventMap.get(event);
117                 if (el==null) {
118                     el = new EventList();
119                     eventMap.put(event, el);
120                 }
121                 be = el.lock(event);
122             }
123         } catch (NoAvailableEventsException e) {
124             //if we have to wait for more than 3 iterations (150 millisecs) return
125
try {
126                 if (++cntr>maxRetries) {
127                      logger.warn("ALERT: EventPool timeout. You might want to consider upping your pool size to avoid this condition");
128                      throw e;
129                 }
130                 if (logger.isDebugEnabled()) logger.debug("Waiting "+cntr+" for next available event "+event.getName());
131                 Thread.yield();
132                 Thread.sleep(retryInterval);
133             } catch (InterruptedException JavaDoc ie) {}
134         }
135         
136         return be;
137     }
138     
139     /**
140      * check the event back in, allowing someone
141      * else to have access to it.
142      *
143      * @param event the event we're releasing our lock on
144      */

145     public void releaseEvent(BaseEvent event) {
146         if (logger.isInfoEnabled()) logger.info("Releasing event "+event);
147         synchronized (sync) {
148             EventList el = (EventList) eventMap.get(event.getClass());
149             if (el==null) return;
150             el.release(event);
151         }
152     }
153
154     /**
155      * Cleanup any locked events which weren't released
156      * (they should all be). You should not ever really need
157      * to run this method. It will get invoked automatically
158      * when the cleaner-upper runs
159      */

160     public void cleanupLockedEvents() {
161         lastCleanup = System.currentTimeMillis();
162         if (logger.isInfoEnabled()) logger.info("Cleaning up locked events @"+lastCleanup);
163         synchronized (sync) {
164             Iterator it = eventMap.values().iterator();
165             while (it.hasNext()) {
166                 EventList el = (EventList) it.next();
167                 el.cleanup();
168             }
169         }
170     }
171     
172     //lb_032801_start - Patch submitted by Larry Brasfield
173
//[larry.brasfield@theplatform.com] so JVM can exit once
174
//the servlet's destroy() method is called. This method
175
//allows us to force the event pool cleanup thread to stop.
176
//You can find related changes by searching for lb_032801
177
/**
178      * Shutdown the event pool
179      */

180     public void shutdown() {
181         stayAlive = false;
182         if (thread!=null) {
183             thread.interrupt();
184             // Let it loose so it can be gc'ed.
185
thread = null;
186         }
187     }
188     //lb_032801_end
189

190
191     /**
192      * This inner class is used to store individual lists
193      * of event instances
194      */

195     class EventList {
196         String JavaDoc name = null;
197         List freeList = new ArrayList(poolSize);
198         List lockedList = new ArrayList(poolSize);
199
200         /**
201          * lock an event within the local event list
202          *
203          * @param event the class of event to get a lock on
204          * @return a locked instance of the event
205          */

206         public BaseEvent lock(Class JavaDoc event) throws InvalidClassException, NoAvailableEventsException {
207             //set the name
208
if (name==null) name = event.getName();
209             if (logger.isDebugEnabled()) logger.debug("Attempting to lock event "+name);
210             
211             //wait until there's a free event. If the wait exceeds
212
//the timeout period, somethign is wrong so throw an exception
213
//csc_112103_1 - unused fields
214
//csc_112103_1 long stime = System.currentTimeMillis();
215
//csc_112103_1 int cntr = 0;
216
if (freeList.size()<1 && lockedList.size()>=poolSize) throw new NoAvailableEventsException("No available events:"+event);
217             
218             //try and get the next available event
219
BaseEvent be = null;
220             if (freeList.size()>0) {
221                 if (logger.isDebugEnabled()) logger.debug("Looking up next event "+name);
222                 be = (BaseEvent) freeList.get(0);
223                 freeList.remove(0);
224             }
225             
226             //instantiate it if need be
227
if (be==null) {
228                 if (logger.isDebugEnabled()) logger.debug("Instantiating event "+name);
229                 try {
230                     //Note: the event pool assumes all event objects must have
231
//a noargs constructor
232
if (logger.isDebugEnabled()) logger.debug("Instantiating Event");
233                     be = (BaseEvent) event.newInstance();
234                 } catch (Exception JavaDoc e) {
235                     throw new InvalidClassException ("Error instantiating event:"+event, e);
236                 }
237             }
238             
239             //now move the event to the locked list, set the timestamp,
240
//and return the event
241
if (logger.isDebugEnabled()) logger.debug("Locking event "+be+" in EventList: "+name);
242             be.touch();
243             lockedList.add(be);
244             lastUpdate = be.getTimestamp();
245             return be;
246         }
247         
248         /**
249          * release an event within the local event list
250          *
251          * @param be the locked event to be released
252          */

253         public void release(BaseEvent be) {
254             if (logger.isDebugEnabled()) logger.debug("Releasing event "+be+" in EventList: "+name);
255             lockedList.remove(be);
256             be.reset();
257             freeList.add(be);
258         }
259         
260         /**
261          * Cleanup the locked list
262          */

263         public void cleanup() {
264             if (logger.isDebugEnabled()) logger.debug("Cleaning up EventList: "+name);
265             boolean gotSome = false;
266             Iterator it = lockedList.iterator();
267             long curTime = System.currentTimeMillis();
268             while (it.hasNext()) {
269                 BaseEvent be = (BaseEvent) it.next();
270                 if (be.getTimestamp()-curTime>timeout) {
271                     if (logger.isDebugEnabled()) logger.debug("Forcing release for event:"+be);
272                     release(be);
273                     gotSome = true;
274                 }
275             }
276             if (logger.isDebugEnabled() && !gotSome) logger.debug("All was clean...no events needed to be released");
277         }
278         
279         protected void finalize() {
280             if (logger.isInfoEnabled()) logger.info("Finalizing event pool...");
281             stayAlive = false;
282
283             //lb_032801_start - Patch submitted by Larry Brasfield
284
//[larry.brasfield@theplatform.com] so JVM can exit once
285
//the servlet's destroy() method is called. You can find
286
//related changes by searching for lb_032801
287
// if (thread!=null) thread.interrupt();
288
if (thread!=null) {
289                 thread.interrupt();
290                 // Let it loose to break circular reference.
291
thread = null;
292             }
293             //lb_032801_end
294
}
295     }
296     
297
298     /**
299      * This inner class cleans up events which for some reason
300      * weren't released.
301      */

302     class EventListCleanerUpper implements Runnable JavaDoc {
303         
304         public void run() {
305             if (logger.isInfoEnabled()) logger.info("Starting EventListCleanerUpper (ELCU)...");
306         
307             //loop infinitely
308
while (stayAlive) {
309                 try {
310                     //sleep for a while
311
if (logger.isDebugEnabled()) logger.debug("ELCU...Going to sleep");
312                     Thread.yield();
313                     Thread.sleep(cleanupInterval);
314                     
315                     //see if anything has even changed since we last checked
316
if (logger.isDebugEnabled()) logger.debug("ELCU...Checking to see if cleanup necessary");
317                     if (lastCleanup>=lastUpdate) continue;
318                     
319                     //run the check
320
if (logger.isDebugEnabled()) logger.debug("ELCU...Running cleanup");
321                     cleanupLockedEvents();
322                 } catch (InterruptedException JavaDoc e) {}
323             }
324             if (logger.isInfoEnabled()) logger.info("Shutting down ELCU...Goodbye.");
325         }
326     }
327     
328     
329 }
330
Popular Tags