001: /*
002: License $Id: JoSessionContext.java,v 1.7 2004/04/16 01:30:04 hendriks73 Exp $
003:
004: Copyright (c) 2001-2005 tagtraum industries.
005:
006: LGPL
007: ====
008:
009: jo! is free software; you can redistribute it and/or
010: modify it under the terms of the GNU Lesser General Public
011: License as published by the Free Software Foundation; either
012: version 2.1 of the License, or (at your option) any later version.
013:
014: jo! is distributed in the hope that it will be useful,
015: but WITHOUT ANY WARRANTY; without even the implied warranty of
016: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: Lesser General Public License for more details.
018:
019: You should have received a copy of the GNU Lesser General Public
020: License along with this library; if not, write to the Free Software
021: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022:
023: For LGPL see <http://www.fsf.org/copyleft/lesser.txt>
024:
025:
026: Sun license
027: ===========
028:
029: This release contains software by Sun Microsystems. Therefore
030: the following conditions have to be met, too. They apply to the
031: files
032:
033: - lib/mail.jar
034: - lib/activation.jar
035: - lib/jsse.jar
036: - lib/jcert.jar
037: - lib/jaxp.jar
038: - lib/crimson.jar
039: - lib/servlet.jar
040: - lib/jnet.jar
041: - lib/jaas.jar
042: - lib/jaasmod.jar
043:
044: contained in this release.
045:
046: a. Licensee may not modify the Java Platform
047: Interface (JPI, identified as classes contained within the javax
048: package or any subpackages of the javax package), by creating additional
049: classes within the JPI or otherwise causing the addition to or modification
050: of the classes in the JPI. In the event that Licensee creates any
051: Java-related API and distribute such API to others for applet or
052: application development, you must promptly publish broadly, an accurate
053: specification for such API for free use by all developers of Java-based
054: software.
055:
056: b. Software is confidential copyrighted information of Sun and
057: title to all copies is retained by Sun and/or its licensors. Licensee
058: shall not modify, decompile, disassemble, decrypt, extract, or otherwise
059: reverse engineer Software. Software may not be leased, assigned, or
060: sublicensed, in whole or in part. Software is not designed or intended
061: for use in on-line control of aircraft, air traffic, aircraft navigation
062: or aircraft communications; or in the design, construction, operation or
063: maintenance of any nuclear facility. Licensee warrants that it will not
064: use or redistribute the Software for such purposes.
065:
066: c. Software is provided "AS IS," without a warranty
067: of any kind. ALL EXPRESS OR IMPLIED REPRESENTATIONS AND WARRANTIES,
068: INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
069: PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
070:
071: d. This License is effective until terminated. Licensee may
072: terminate this License at any time by destroying all copies of Software.
073: This License will terminate immediately without notice from Sun if Licensee
074: fails to comply with any provision of this License. Upon such termination,
075: Licensee must destroy all copies of Software.
076:
077: e. Software, including technical data, is subject to U.S.
078: export control laws, including the U.S. Export Administration Act and its
079: associated regulations, and may be subject to export or import regulations
080: in other countries. Licensee agrees to comply strictly with all such
081: regulations and acknowledges that it has the responsibility to obtain
082: licenses to export, re-export, or import Software. Software may not be
083: downloaded, or otherwise exported or re-exported (i) into, or to a national
084: or resident of, Cuba, Iraq, Iran, North Korea, Libya, Sudan, Syria or any
085: country to which the U.S. has embargoed goods; or (ii) to anyone on the
086: U.S. Treasury Department's list of Specially Designated Nations or the U.S.
087: Commerce Department's Table of Denial Orders.
088:
089:
090: Feedback
091: ========
092:
093: We encourage your feedback and suggestions and want to use your feedback to
094: improve the Software. Send all such feedback to:
095: <feedback@tagtraum.com>
096:
097: For more information on tagtraum industries and jo!
098: please see <http://www.tagtraum.com/>.
099:
100:
101: */
102: package com.tagtraum.jo;
103:
104: import com.tagtraum.framework.log.C_Log;
105: import com.tagtraum.framework.log.Log;
106: import com.tagtraum.framework.recycler.Recycler;
107: import com.tagtraum.framework.util.FactoryException;
108: import com.tagtraum.framework.util.StringHelper;
109:
110: import javax.servlet.http.HttpSession;
111: import javax.servlet.http.HttpSessionActivationListener;
112: import javax.servlet.http.HttpSessionEvent;
113: import java.io.*;
114: import java.util.Enumeration;
115: import java.util.Hashtable;
116: import java.util.Random;
117:
118: /**
119: * SessionContext that is accessible for the engine, but not for the
120: * servlet.
121: *
122: * @author <a href="mailto:hs@tagtraum.com">Hendrik Schreiber</a>
123: * @version 1.1beta1 $Id: JoSessionContext.java,v 1.7 2004/04/16 01:30:04 hendriks73 Exp $
124: */
125: public class JoSessionContext extends Thread implements
126: I_JoSessionContext, C_Jo {
127:
128: /**
129: * Source-Version.
130: */
131: public static String vcid = "$Id: JoSessionContext.java,v 1.7 2004/04/16 01:30:04 hendriks73 Exp $";
132:
133: /**
134: * Datastructure for holding the sessions. This could
135: * be replaced with a persistence engine or database connection.
136: */
137: private Hashtable sessions;
138:
139: /**
140: * InvalidationTime in s.
141: */
142: private int defaultMaxInactiveInterval;
143:
144: /**
145: * InvalidationIntervall in ms.
146: */
147: private long invalidationIntervall;
148:
149: /**
150: * Indicates whether the context is checking the sessions.
151: */
152: private boolean running;
153:
154: /**
155: * Host.
156: */
157: private I_JoHost host;
158:
159: /**
160: * Factory.
161: */
162: private Recycler factory;
163:
164: /**
165: * Peer.
166: */
167: private I_JoServletContextPeer peer;
168:
169: /**
170: * IDExtension.
171: */
172: private int idExtension;
173:
174: /**
175: * Filename of the session file.
176: */
177: private File sessionFile;
178:
179: /**
180: * Random part of the session id.
181: */
182: private Random random;
183:
184: public JoSessionContext() {
185: running = true;
186: sessions = new Hashtable();
187: random = new Random();
188: }
189:
190: /**
191: * Initializes this context.
192: */
193: public void init(I_JoServletContextPeer peer) {
194: this .peer = peer;
195: this .host = peer.getHost();
196: this .factory = Recycler.getNamedRecycler(host.getName());
197: sessions.clear();
198: invalidationIntervall = C_DefaultInvalidationIntervall;
199: // this looks nasty (rik)
200: defaultMaxInactiveInterval = peer.getSessionTimeout() * 60; // min = sec !!!
201: if (peer.isRestoreSessions())
202: loadSerializableSessions();
203: start();
204: }
205:
206: /**
207: * Checks the validity of all sessions.
208: */
209: public void run() {
210: try {
211: synchronized (this ) {
212: while (running) {
213: checkSessions();
214: wait(invalidationIntervall);
215: }
216: }
217: } catch (InterruptedException ie) {
218: if (Log.getLog(host.getName()).isLog(C_Log.VERBOSE))
219: Log.getLog(host.getName()).log(
220: "SessionContext " + getName() + " for '"
221: + peer.getName()
222: + "' has been interrupted.");
223: }
224: }
225:
226: /**
227: * Returns a unique ID.
228: *
229: * @return new SessionID
230: */
231: public String getNewId() {
232: if (idExtension < Long.MAX_VALUE) {
233: idExtension++;
234: } else {
235: idExtension = 0;
236: }
237: // use base 36 to shorten string
238: return Long.toHexString(System.currentTimeMillis())
239: .substring(2)
240: + Long.toHexString(idExtension)
241: + Integer.toHexString(random.nextInt(10000));
242: }
243:
244: /**
245: * Destroys this context. Stores serializable sessions and invalidates
246: * all sessions.
247: */
248: public synchronized void destroy() {
249: if (peer.isRestoreSessions())
250: storeSerializableSessions();
251: invalidateAllSessions();
252: running = false;
253: notify();
254: if (Log.getLog(host.getName()).isLog(C_Log.METHOD))
255: Log.getLog(host.getName()).log(
256: "SessionContext " + getName() + " for '"
257: + peer.getName() + "' has been stopped.",
258: C_Log.METHOD);
259: }
260:
261: /**
262: * Computes the session file.
263: */
264: private File getSessionFile() {
265: if (sessionFile == null) {
266: File sessionFileDir = new File((host.getService())
267: .getJoHome(), "/sessions/"
268: + StringHelper.toJavaIdentifier(peer.getHost()
269: .getName()) + "/"
270: + StringHelper.toJavaIdentifier(peer.getName()));
271: // make sure the dir exists.
272: sessionFileDir.mkdirs();
273: sessionFile = new File(sessionFileDir, "sessions.ser");
274: }
275: return sessionFile;
276: }
277:
278: /**
279: * Stores serializable sessions.
280: */
281: public void storeSerializableSessions() {
282: fireSessionWillPassivate();
283: ObjectOutputStream out = null;
284: try {
285: out = new ObjectOutputStream(new FileOutputStream(
286: getSessionFile()));
287: out.writeObject(sessions);
288: } catch (IOException ioe) {
289: Log.getLog(host.getName()).log(ioe);
290: } catch (RuntimeException rte) {
291: Log.getLog(host.getName()).log(rte);
292: } finally {
293: try {
294: if (out != null) {
295: out.close();
296: }
297: } catch (IOException ioe) {
298: Log.getLog(host.getName()).log(ioe);
299: }
300: }
301: }
302:
303: private void fireSessionWillPassivate() {
304: for (Enumeration sessionObjects = sessions.elements(); sessionObjects
305: .hasMoreElements();) {
306: HttpSession session = (HttpSession) sessionObjects
307: .nextElement();
308: fireSessionWillPassivate(session);
309: }
310: }
311:
312: private static void fireSessionWillPassivate(HttpSession session) {
313: HttpSessionEvent sessionEvent = new HttpSessionEvent(session);
314: for (Enumeration names = session.getAttributeNames(); names
315: .hasMoreElements();) {
316: Object value = session.getAttribute((String) names
317: .nextElement());
318: if (value instanceof HttpSessionActivationListener) {
319: ((HttpSessionActivationListener) value)
320: .sessionWillPassivate(sessionEvent);
321: }
322: }
323: }
324:
325: private void fireSessionDidActivate() {
326: for (Enumeration sessionObjects = sessions.elements(); sessionObjects
327: .hasMoreElements();) {
328: HttpSession session = (HttpSession) sessionObjects
329: .nextElement();
330: fireSessionDidActivate(session);
331: }
332: }
333:
334: private static void fireSessionDidActivate(HttpSession session) {
335: HttpSessionEvent sessionEvent = new HttpSessionEvent(session);
336: for (Enumeration names = session.getAttributeNames(); names
337: .hasMoreElements();) {
338: Object value = session.getAttribute((String) names
339: .nextElement());
340: if (value instanceof HttpSessionActivationListener) {
341: ((HttpSessionActivationListener) value)
342: .sessionDidActivate(sessionEvent);
343: }
344: }
345: }
346:
347: /**
348: * Loads serializable sessions.
349: */
350: public void loadSerializableSessions() {
351: ObjectInputStream in = null;
352: try {
353: in = new ObjectInputStream(new FileInputStream(
354: getSessionFile()));
355: sessions = (Hashtable) in.readObject();
356: Enumeration e = sessions.elements();
357: while (e.hasMoreElements()) {
358: I_JoSession theSession = (I_JoSession) e.nextElement();
359: theSession.setSessionContext(this );
360: }
361: fireSessionDidActivate();
362: } catch (FileNotFoundException fnfe) {
363: Log.getLog(host.getName()).log(
364: "No serialized sessions found for webapp '"
365: + peer.getName() + "'.");
366: } catch (Exception e) {
367: Log.getLog(host.getName()).log(e);
368: } finally {
369: try {
370: if (in != null) {
371: in.close();
372: }
373: } catch (IOException ioe) {
374: Log.getLog(host.getName()).log(ioe);
375: }
376: }
377: }
378:
379: /**
380: * Checks validity of all session.
381: */
382: public void checkSessions() {
383: if (Log.getLog(host.getName()).isLog(C_Log.FORTYTWO)) {
384: logAvailableMemoryVerbosely();
385: }
386: long now = System.currentTimeMillis();
387: Enumeration e = sessions.elements();
388: HttpSession session;
389: while (e.hasMoreElements()) {
390: session = (HttpSession) e.nextElement();
391: long max = session.getLastAccessedTime();
392: if (session.getLastAccessedTime() < session
393: .getCreationTime()) {
394: max = session.getCreationTime();
395: }
396: if ((now - max) > session.getMaxInactiveInterval() * 1000) {
397: session.invalidate();
398: }
399: }
400: }
401:
402: /**
403: * Returns the session with the given ID.
404: *
405: * @param sessionId
406: * @return the Session or <code>null</code>, if none is found
407: */
408: public HttpSession getSession(String sessionId) {
409: I_JoSession session = (I_JoSession) sessions.get(sessionId);
410: if (session != null) {
411: session.markAccessed();
412: }
413: return session;
414: }
415:
416: /**
417: * Creates a new session as long as the maximum number of
418: * sessions is not exceeded.
419: *
420: * @return Session or <code>null</code>
421: */
422: public I_JoSession newSession() throws FactoryException {
423: int theMaxSessions = peer.getMaxSessions();
424: if (sessions.size() > theMaxSessions && theMaxSessions >= 0) {
425: return null;
426: }
427: I_JoSession theSession = (I_JoSession) factory
428: .get(C_FactoryKey_Session);
429: theSession.setSessionContext(this );
430: return theSession;
431: }
432:
433: /**
434: * Adds a session to this context.
435: *
436: * @param aSession a session
437: */
438: public void addSession(HttpSession aSession) {
439: sessions.put(aSession.getId(), aSession);
440: if (Log.getLog(host.getName()).isLog(C_Log.METHOD)) {
441: Log.getLog(host.getName()).log(
442: "Added a session. Now " + sessions.size()
443: + " are active.", C_Log.METHOD);
444: }
445: peer.getListenerSupport().fireSessionCreated(aSession);
446: }
447:
448: /**
449: * Removes a Session.
450: *
451: * @param anID a session ID
452: */
453: public void removeSession(String anID) {
454: HttpSession session = (HttpSession) sessions.remove(anID);
455: // TODO: Do we really fire this *after* the session was invalidated?
456: if (session != null)
457: peer.getListenerSupport().fireSessionDestroyed(session);
458: if (Log.getLog(host.getName()).isLog(C_Log.METHOD)) {
459: Log.getLog(host.getName()).log(
460: "Removed a session. Now " + sessions.size()
461: + " are active.", C_Log.METHOD);
462: }
463: }
464:
465: /**
466: * Invalidates all sessions.
467: */
468: public void invalidateAllSessions() {
469: Enumeration e = sessions.elements();
470: while (e.hasMoreElements()) {
471: ((I_JoSession) e.nextElement()).invalidate();
472: }
473: }
474:
475: /**
476: * Returns the MaxInactiveInterval all sessions are initialized with.
477: *
478: * @return MaxInactiveInterval in seconds
479: */
480: public int getDefaultMaxInactiveInterval() {
481: return defaultMaxInactiveInterval;
482: }
483:
484: /**
485: * Sets the MaxInactiveInterval all sessions are initialized with.
486: *
487: * @param interval MaxInactiveInterval in seconds
488: */
489: public void setDefaultMaxInactiveInterval(int interval) {
490: defaultMaxInactiveInterval = interval;
491: }
492:
493: /**
494: * Returns the peer.
495: */
496: public I_JoServletContextPeer getServletContextPeer() {
497: return peer;
498: }
499:
500: /**
501: * Prints out some status information about the engine.
502: */
503: private void logAvailableMemoryVerbosely() {
504: Log.getLog(host.getName())
505: .log("Checking sessions: " + sessions.size(),
506: C_Log.FORTYTWO);
507: int threads = host.getService().getHandlerCount();
508: long free = Runtime.getRuntime().freeMemory();
509: long total = Runtime.getRuntime().totalMemory();
510: int ratio = (int) ((free * 100) / total);
511: long used = total - free;
512: long threadMemoryRatio = 0;
513:
514: if (threads != 0) {
515: threadMemoryRatio = used / (long) threads;
516: }
517:
518: Log.getLog(host.getName()).log(
519: "Used Memory: " + used + ", Free Memory: " + free
520: + ", Total Memory: " + total + ", Ratio: "
521: + ratio, C_Log.FORTYTWO);
522: Log.getLog(host.getName()).log(
523: "Threads: " + threads + ", Memory/Thread: "
524: + threadMemoryRatio, C_Log.FORTYTWO);
525: }
526:
527: }
|