001: /*
002: * StoreBase.java
003: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/StoreBase.java,v 1.5 2002/06/09 02:19:43 remm Exp $
004: * $Revision: 1.5 $
005: * $Date: 2002/06/09 02:19:43 $
006: *
007: * ====================================================================
008: *
009: * The Apache Software License, Version 1.1
010: *
011: * Copyright (c) 1999 The Apache Software Foundation. All rights
012: * reserved.
013: *
014: * Redistribution and use in source and binary forms, with or without
015: * modification, are permitted provided that the following conditions
016: * are met:
017: *
018: * 1. Redistributions of source code must retain the above copyright
019: * notice, this list of conditions and the following disclaimer.
020: *
021: * 2. Redistributions in binary form must reproduce the above copyright
022: * notice, this list of conditions and the following disclaimer in
023: * the documentation and/or other materials provided with the
024: * distribution.
025: *
026: * 3. The end-user documentation included with the redistribution, if
027: * any, must include the following acknowlegement:
028: * "This product includes software developed by the
029: * Apache Software Foundation (http://www.apache.org/)."
030: * Alternately, this acknowlegement may appear in the software itself,
031: * if and wherever such third-party acknowlegements normally appear.
032: *
033: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
034: * Foundation" must not be used to endorse or promote products derived
035: * from this software without prior written permission. For written
036: * permission, please contact apache@apache.org.
037: *
038: * 5. Products derived from this software may not be called "Apache"
039: * nor may "Apache" appear in their names without prior written
040: * permission of the Apache Group.
041: *
042: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
043: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
044: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
045: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
046: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
047: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
048: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
049: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
050: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
051: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
052: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
053: * SUCH DAMAGE.
054: * ====================================================================
055: *
056: * This software consists of voluntary contributions made by many
057: * individuals on behalf of the Apache Software Foundation. For more
058: * information on the Apache Software Foundation, please see
059: * <http://www.apache.org/>.
060: *
061: * [Additional notices, if required by prior licensing conditions]
062: *
063: */
064:
065: package org.apache.catalina.session;
066:
067: import java.beans.PropertyChangeListener;
068: import java.beans.PropertyChangeSupport;
069: import java.io.IOException;
070: import org.apache.catalina.Container;
071: import org.apache.catalina.Lifecycle;
072: import org.apache.catalina.LifecycleEvent;
073: import org.apache.catalina.LifecycleException;
074: import org.apache.catalina.LifecycleListener;
075: import org.apache.catalina.Logger;
076: import org.apache.catalina.Manager;
077: import org.apache.catalina.Store;
078: import org.apache.catalina.util.LifecycleSupport;
079: import org.apache.catalina.util.StringManager;
080:
081: /**
082: * Abstract implementation of the Store interface to
083: * support most of the functionality required by a Store.
084: *
085: * @author Bip Thelin
086: * @version $Revision: 1.5 $, $Date: 2002/06/09 02:19:43 $
087: */
088:
089: public abstract class StoreBase implements Lifecycle, Runnable, Store {
090:
091: // ----------------------------------------------------- Instance Variables
092:
093: /**
094: * The descriptive information about this implementation.
095: */
096: protected static String info = "StoreBase/1.0";
097:
098: /**
099: * The interval (in seconds) between checks for expired sessions.
100: */
101: protected int checkInterval = 60;
102:
103: /**
104: * Name to register for the background thread.
105: */
106: protected String threadName = "StoreBase";
107:
108: /**
109: * Name to register for this Store, used for logging.
110: */
111: protected static String storeName = "StoreBase";
112:
113: /**
114: * The background thread.
115: */
116: protected Thread thread = null;
117:
118: /**
119: * The background thread completion semaphore.
120: */
121: protected boolean threadDone = false;
122:
123: /**
124: * The debugging detail level for this component.
125: */
126: protected int debug = 0;
127:
128: /**
129: * Has this component been started yet?
130: */
131: protected boolean started = false;
132:
133: /**
134: * The lifecycle event support for this component.
135: */
136: protected LifecycleSupport lifecycle = new LifecycleSupport(this );
137:
138: /**
139: * The property change support for this component.
140: */
141: protected PropertyChangeSupport support = new PropertyChangeSupport(
142: this );
143:
144: /**
145: * The string manager for this package.
146: */
147: protected StringManager sm = StringManager
148: .getManager(Constants.Package);
149:
150: /**
151: * The Manager with which this JDBCStore is associated.
152: */
153: protected Manager manager;
154:
155: // ------------------------------------------------------------- Properties
156:
157: /**
158: * Return the info for this Store.
159: */
160: public String getInfo() {
161: return (info);
162: }
163:
164: /**
165: * Return the thread name for this Store.
166: */
167: public String getThreadName() {
168: return (threadName);
169: }
170:
171: /**
172: * Return the name for this Store, used for logging.
173: */
174: public String getStoreName() {
175: return (storeName);
176: }
177:
178: /**
179: * Set the debugging detail level for this Store.
180: *
181: * @param debug The new debugging detail level
182: */
183: public void setDebug(int debug) {
184: this .debug = debug;
185: }
186:
187: /**
188: * Return the debugging detail level for this Store.
189: */
190: public int getDebug() {
191: return (this .debug);
192: }
193:
194: /**
195: * Set the check interval (in seconds) for this Store.
196: *
197: * @param checkInterval The new check interval
198: */
199: public void setCheckInterval(int checkInterval) {
200: int oldCheckInterval = this .checkInterval;
201: this .checkInterval = checkInterval;
202: support.firePropertyChange("checkInterval", new Integer(
203: oldCheckInterval), new Integer(this .checkInterval));
204: }
205:
206: /**
207: * Return the check interval (in seconds) for this Store.
208: */
209: public int getCheckInterval() {
210: return (this .checkInterval);
211: }
212:
213: /**
214: * Set the Manager with which this Store is associated.
215: *
216: * @param manager The newly associated Manager
217: */
218: public void setManager(Manager manager) {
219: Manager oldManager = this .manager;
220: this .manager = manager;
221: support.firePropertyChange("manager", oldManager, this .manager);
222: }
223:
224: /**
225: * Return the Manager with which the Store is associated.
226: */
227: public Manager getManager() {
228: return (this .manager);
229: }
230:
231: // --------------------------------------------------------- Public Methods
232:
233: /**
234: * Add a lifecycle event listener to this component.
235: *
236: * @param listener The listener to add
237: */
238: public void addLifecycleListener(LifecycleListener listener) {
239: lifecycle.addLifecycleListener(listener);
240: }
241:
242: /**
243: * Get the lifecycle listeners associated with this lifecycle. If this
244: * Lifecycle has no listeners registered, a zero-length array is returned.
245: */
246: public LifecycleListener[] findLifecycleListeners() {
247:
248: return lifecycle.findLifecycleListeners();
249:
250: }
251:
252: /**
253: * Remove a lifecycle event listener from this component.
254: *
255: * @param listener The listener to add
256: */
257: public void removeLifecycleListener(LifecycleListener listener) {
258: lifecycle.removeLifecycleListener(listener);
259: }
260:
261: /**
262: * Add a property change listener to this component.
263: *
264: * @param listener a value of type 'PropertyChangeListener'
265: */
266: public void addPropertyChangeListener(
267: PropertyChangeListener listener) {
268: support.addPropertyChangeListener(listener);
269: }
270:
271: /**
272: * Remove a property change listener from this component.
273: *
274: * @param listener The listener to remove
275: */
276: public void removePropertyChangeListener(
277: PropertyChangeListener listener) {
278: support.removePropertyChangeListener(listener);
279: }
280:
281: // --------------------------------------------------------- Protected Methods
282:
283: /**
284: * Called by our background reaper thread to check if Sessions
285: * saved in our store are subject of being expired. If so expire
286: * the Session and remove it from the Store.
287: *
288: */
289: protected void processExpires() {
290: long timeNow = System.currentTimeMillis();
291: String[] keys = null;
292:
293: if (!started)
294: return;
295:
296: try {
297: keys = keys();
298: } catch (IOException e) {
299: log(e.toString());
300: e.printStackTrace();
301: return;
302: }
303:
304: for (int i = 0; i < keys.length; i++) {
305: try {
306: StandardSession session = (StandardSession) load(keys[i]);
307: if (!session.isValid())
308: continue;
309: int maxInactiveInterval = session
310: .getMaxInactiveInterval();
311: if (maxInactiveInterval < 0)
312: continue;
313: int timeIdle = // Truncate, do not round up
314: (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
315: if (timeIdle >= maxInactiveInterval) {
316: session.expire();
317: remove(session.getId());
318: }
319: } catch (IOException e) {
320: log(e.toString());
321: e.printStackTrace();
322: } catch (ClassNotFoundException e) {
323: log(e.toString());
324: e.printStackTrace();
325: }
326: }
327: }
328:
329: /**
330: * Log a message on the Logger associated with our Container (if any).
331: *
332: * @param message Message to be logged
333: */
334: protected void log(String message) {
335: Logger logger = null;
336: Container container = manager.getContainer();
337:
338: if (container != null)
339: logger = container.getLogger();
340:
341: if (logger != null) {
342: logger.log(getStoreName() + "[" + container.getName()
343: + "]: " + message);
344: } else {
345: String containerName = null;
346: if (container != null)
347: containerName = container.getName();
348: System.out.println(getStoreName() + "[" + containerName
349: + "]: " + message);
350: }
351: }
352:
353: // --------------------------------------------------------- Thread Methods
354:
355: /**
356: * The background thread that checks for session timeouts and shutdown.
357: */
358: public void run() {
359: // Loop until the termination semaphore is set
360: while (!threadDone) {
361: threadSleep();
362: processExpires();
363: }
364: }
365:
366: /**
367: * Prepare for the beginning of active use of the public methods of this
368: * component. This method should be called after <code>configure()</code>,
369: * and before any of the public methods of the component are utilized.
370: *
371: * @exception LifecycleException if this component detects a fatal error
372: * that prevents this component from being used
373: */
374: public void start() throws LifecycleException {
375: // Validate and update our current component state
376: if (started)
377: throw new LifecycleException(sm.getString(getStoreName()
378: + ".alreadyStarted"));
379: lifecycle.fireLifecycleEvent(START_EVENT, null);
380: started = true;
381:
382: // Start the background reaper thread
383: threadStart();
384: }
385:
386: /**
387: * Gracefully terminate the active use of the public methods of this
388: * component. This method should be the last one called on a given
389: * instance of this component.
390: *
391: * @exception LifecycleException if this component detects a fatal error
392: * that needs to be reported
393: */
394: public void stop() throws LifecycleException {
395: // Validate and update our current component state
396: if (!started)
397: throw new LifecycleException(sm.getString(getStoreName()
398: + ".notStarted"));
399: lifecycle.fireLifecycleEvent(STOP_EVENT, null);
400: started = false;
401:
402: // Stop the background reaper thread
403: threadStop();
404: }
405:
406: /**
407: * Start the background thread that will periodically check for
408: * session timeouts.
409: */
410: protected void threadStart() {
411: if (thread != null)
412: return;
413:
414: threadDone = false;
415: thread = new Thread(this , getThreadName());
416: thread.setDaemon(true);
417: thread.start();
418: }
419:
420: /**
421: * Sleep for the duration specified by the <code>checkInterval</code>
422: * property.
423: */
424: protected void threadSleep() {
425: try {
426: Thread.sleep(checkInterval * 1000L);
427: } catch (InterruptedException e) {
428: ;
429: }
430: }
431:
432: /**
433: * Stop the background thread that is periodically checking for
434: * session timeouts.
435: */
436: protected void threadStop() {
437: if (thread == null)
438: return;
439:
440: threadDone = true;
441: thread.interrupt();
442: try {
443: thread.join();
444: } catch (InterruptedException e) {
445: ;
446: }
447:
448: thread = null;
449: }
450: }
|