001: /*
002: * Copyright 1999,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.catalina.session;
018:
019: import java.beans.PropertyChangeListener;
020: import java.beans.PropertyChangeSupport;
021: import java.io.DataInputStream;
022: import java.io.File;
023: import java.io.FileInputStream;
024: import java.io.IOException;
025: import java.security.AccessController;
026: import java.security.MessageDigest;
027: import java.security.NoSuchAlgorithmException;
028: import java.security.PrivilegedAction;
029: import java.util.Date;
030: import java.util.HashMap;
031: import java.util.Iterator;
032: import java.util.Random;
033:
034: import javax.management.MBeanRegistration;
035: import javax.management.MBeanServer;
036: import javax.management.ObjectName;
037:
038: import org.apache.catalina.Container;
039: import org.apache.catalina.DefaultContext;
040: import org.apache.catalina.Engine;
041: import org.apache.catalina.Manager;
042: import org.apache.catalina.Session;
043: import org.apache.catalina.core.StandardContext;
044: import org.apache.catalina.core.StandardHost;
045: import org.apache.catalina.util.StringManager;
046: import org.apache.commons.logging.Log;
047: import org.apache.commons.logging.LogFactory;
048: import org.apache.commons.modeler.Registry;
049:
050: /**
051: * Minimal implementation of the <b>Manager</b> interface that supports
052: * no session persistence or distributable capabilities. This class may
053: * be subclassed to create more sophisticated Manager implementations.
054: *
055: * @author Craig R. McClanahan
056: * @version $Revision: 1.27 $ $Date: 2004/05/26 16:13:59 $
057: */
058:
059: public abstract class ManagerBase implements Manager, MBeanRegistration {
060: protected Log log = LogFactory.getLog(ManagerBase.class);
061:
062: // ----------------------------------------------------- Instance Variables
063:
064: protected DataInputStream randomIS = null;
065: protected String devRandomSource = "/dev/urandom";
066:
067: /**
068: * The default message digest algorithm to use if we cannot use
069: * the requested one.
070: */
071: protected static final String DEFAULT_ALGORITHM = "MD5";
072:
073: /**
074: * The message digest algorithm to be used when generating session
075: * identifiers. This must be an algorithm supported by the
076: * <code>java.security.MessageDigest</code> class on your platform.
077: */
078: protected String algorithm = DEFAULT_ALGORITHM;
079:
080: /**
081: * The Container with which this Manager is associated.
082: */
083: protected Container container;
084:
085: /**
086: * The debugging detail level for this component.
087: */
088: protected int debug = 0;
089:
090: /**
091: * The DefaultContext with which this Manager is associated.
092: */
093: protected DefaultContext defaultContext = null;
094:
095: /**
096: * Return the MessageDigest implementation to be used when
097: * creating session identifiers.
098: */
099: protected MessageDigest digest = null;
100:
101: /**
102: * The distributable flag for Sessions created by this Manager. If this
103: * flag is set to <code>true</code>, any user attributes added to a
104: * session controlled by this Manager must be Serializable.
105: */
106: protected boolean distributable;
107:
108: /**
109: * A String initialization parameter used to increase the entropy of
110: * the initialization of our random number generator.
111: */
112: protected String entropy = null;
113:
114: /**
115: * The descriptive information string for this implementation.
116: */
117: private static final String info = "ManagerBase/1.0";
118:
119: /**
120: * The default maximum inactive interval for Sessions created by
121: * this Manager.
122: */
123: protected int maxInactiveInterval = 60;
124:
125: /**
126: * The session id length of Sessions created by this Manager.
127: */
128: protected int sessionIdLength = 16;
129:
130: /**
131: * The descriptive name of this Manager implementation (for logging).
132: */
133: protected static String name = "ManagerBase";
134:
135: /**
136: * A random number generator to use when generating session identifiers.
137: */
138: protected Random random = null;
139:
140: /**
141: * The Java class name of the random number generator class to be used
142: * when generating session identifiers.
143: */
144: protected String randomClass = "java.security.SecureRandom";
145:
146: /**
147: * The set of currently active Sessions for this Manager, keyed by
148: * session identifier.
149: */
150: protected HashMap sessions = new HashMap();
151:
152: // Number of sessions created by this manager
153: protected int sessionCounter = 0;
154:
155: protected int maxActive = 0;
156:
157: // number of duplicated session ids - anything >0 means we have problems
158: protected int duplicates = 0;
159:
160: protected boolean initialized = false;
161:
162: /**
163: * The string manager for this package.
164: */
165: protected static StringManager sm = StringManager
166: .getManager(Constants.Package);
167:
168: /**
169: * The property change support for this component.
170: */
171: protected PropertyChangeSupport support = new PropertyChangeSupport(
172: this );
173:
174: // ------------------------------------------------------------- Security classes
175: private class PrivilegedSetRandomFile implements PrivilegedAction {
176:
177: public Object run() {
178: try {
179: File f = new File(devRandomSource);
180: if (!f.exists())
181: return null;
182: randomIS = new DataInputStream(new FileInputStream(f));
183: randomIS.readLong();
184: if (log.isDebugEnabled())
185: log.debug("Opening " + devRandomSource);
186: return randomIS;
187: } catch (IOException ex) {
188: return null;
189: }
190: }
191: }
192:
193: // ------------------------------------------------------------- Properties
194:
195: /**
196: * Return the message digest algorithm for this Manager.
197: */
198: public String getAlgorithm() {
199:
200: return (this .algorithm);
201:
202: }
203:
204: /**
205: * Set the message digest algorithm for this Manager.
206: *
207: * @param algorithm The new message digest algorithm
208: */
209: public void setAlgorithm(String algorithm) {
210:
211: String oldAlgorithm = this .algorithm;
212: this .algorithm = algorithm;
213: support.firePropertyChange("algorithm", oldAlgorithm,
214: this .algorithm);
215:
216: }
217:
218: /**
219: * Return the Container with which this Manager is associated.
220: */
221: public Container getContainer() {
222:
223: return (this .container);
224:
225: }
226:
227: /**
228: * Set the Container with which this Manager is associated.
229: *
230: * @param container The newly associated Container
231: */
232: public void setContainer(Container container) {
233:
234: Container oldContainer = this .container;
235: this .container = container;
236: support.firePropertyChange("container", oldContainer,
237: this .container);
238: // TODO: find a good scheme for the log names
239: //log=LogFactory.getLog("tomcat.manager." + container.getName());
240: }
241:
242: /**
243: * Return the DefaultContext with which this Manager is associated.
244: */
245: public DefaultContext getDefaultContext() {
246:
247: return (this .defaultContext);
248:
249: }
250:
251: /**
252: * Set the DefaultContext with which this Manager is associated.
253: *
254: * @param defaultContext The newly associated DefaultContext
255: */
256: public void setDefaultContext(DefaultContext defaultContext) {
257:
258: DefaultContext oldDefaultContext = this .defaultContext;
259: this .defaultContext = defaultContext;
260: support.firePropertyChange("defaultContext", oldDefaultContext,
261: this .defaultContext);
262:
263: }
264:
265: /**
266: * Return the debugging detail level for this component.
267: */
268: public int getDebug() {
269:
270: return (this .debug);
271:
272: }
273:
274: /**
275: * Set the debugging detail level for this component.
276: *
277: * @param debug The new debugging detail level
278: */
279: public void setDebug(int debug) {
280:
281: this .debug = debug;
282:
283: }
284:
285: /** Returns the name of the implementation class.
286: */
287: public String getClassName() {
288: return this .getClass().getName();
289: }
290:
291: /**
292: * Return the MessageDigest object to be used for calculating
293: * session identifiers. If none has been created yet, initialize
294: * one the first time this method is called.
295: */
296: public synchronized MessageDigest getDigest() {
297:
298: if (this .digest == null) {
299: long t1 = System.currentTimeMillis();
300: if (log.isDebugEnabled())
301: log.debug(sm
302: .getString("managerBase.getting", algorithm));
303: try {
304: this .digest = MessageDigest.getInstance(algorithm);
305: } catch (NoSuchAlgorithmException e) {
306: log.error(
307: sm.getString("managerBase.digest", algorithm),
308: e);
309: try {
310: this .digest = MessageDigest
311: .getInstance(DEFAULT_ALGORITHM);
312: } catch (NoSuchAlgorithmException f) {
313: log.error(sm.getString("managerBase.digest",
314: DEFAULT_ALGORITHM), e);
315: this .digest = null;
316: }
317: }
318: if (log.isDebugEnabled())
319: log.debug(sm.getString("managerBase.gotten"));
320: long t2 = System.currentTimeMillis();
321: if (log.isDebugEnabled())
322: log.debug("getDigest() " + (t2 - t1));
323: }
324:
325: return (this .digest);
326:
327: }
328:
329: /**
330: * Return the distributable flag for the sessions supported by
331: * this Manager.
332: */
333: public boolean getDistributable() {
334:
335: return (this .distributable);
336:
337: }
338:
339: /**
340: * Set the distributable flag for the sessions supported by this
341: * Manager. If this flag is set, all user data objects added to
342: * sessions associated with this manager must implement Serializable.
343: *
344: * @param distributable The new distributable flag
345: */
346: public void setDistributable(boolean distributable) {
347:
348: boolean oldDistributable = this .distributable;
349: this .distributable = distributable;
350: support.firePropertyChange("distributable", new Boolean(
351: oldDistributable), new Boolean(this .distributable));
352:
353: }
354:
355: /**
356: * Return the entropy increaser value, or compute a semi-useful value
357: * if this String has not yet been set.
358: */
359: public String getEntropy() {
360:
361: // Calculate a semi-useful value if this has not been set
362: if (this .entropy == null)
363: setEntropy(this .toString());
364:
365: return (this .entropy);
366:
367: }
368:
369: /**
370: * Set the entropy increaser value.
371: *
372: * @param entropy The new entropy increaser value
373: */
374: public void setEntropy(String entropy) {
375:
376: String oldEntropy = entropy;
377: this .entropy = entropy;
378: support.firePropertyChange("entropy", oldEntropy, this .entropy);
379:
380: }
381:
382: /**
383: * Return descriptive information about this Manager implementation and
384: * the corresponding version number, in the format
385: * <code><description>/<version></code>.
386: */
387: public String getInfo() {
388:
389: return (info);
390:
391: }
392:
393: /**
394: * Return the default maximum inactive interval (in seconds)
395: * for Sessions created by this Manager.
396: */
397: public int getMaxInactiveInterval() {
398:
399: return (this .maxInactiveInterval);
400:
401: }
402:
403: /**
404: * Set the default maximum inactive interval (in seconds)
405: * for Sessions created by this Manager.
406: *
407: * @param interval The new default value
408: */
409: public void setMaxInactiveInterval(int interval) {
410:
411: int oldMaxInactiveInterval = this .maxInactiveInterval;
412: this .maxInactiveInterval = interval;
413: support.firePropertyChange("maxInactiveInterval", new Integer(
414: oldMaxInactiveInterval), new Integer(
415: this .maxInactiveInterval));
416:
417: }
418:
419: /**
420: * Gets the session id length (in bytes) of Sessions created by
421: * this Manager.
422: *
423: * @return The session id length
424: */
425: public int getSessionIdLength() {
426:
427: return (this .sessionIdLength);
428:
429: }
430:
431: /**
432: * Sets the session id length (in bytes) for Sessions created by this
433: * Manager.
434: *
435: * @param idLength The session id length
436: */
437: public void setSessionIdLength(int idLength) {
438:
439: int oldSessionIdLength = this .sessionIdLength;
440: this .sessionIdLength = idLength;
441: support.firePropertyChange("sessionIdLength", new Integer(
442: oldSessionIdLength), new Integer(this .sessionIdLength));
443:
444: }
445:
446: /**
447: * Return the descriptive short name of this Manager implementation.
448: */
449: public String getName() {
450:
451: return (name);
452:
453: }
454:
455: /**
456: * Use /dev/random-type special device. This is new code, but may reduce
457: * the big delay in generating the random.
458: *
459: * You must specify a path to a random generator file. Use /dev/urandom
460: * for linux ( or similar ) systems. Use /dev/random for maximum security
461: * ( it may block if not enough "random" exist ). You can also use
462: * a pipe that generates random.
463: *
464: * The code will check if the file exists, and default to java Random
465: * if not found. There is a significant performance difference, very
466: * visible on the first call to getSession ( like in the first JSP )
467: * - so use it if available.
468: */
469: public void setRandomFile(String s) {
470: // as a hack, you can use a static file - and genarate the same
471: // session ids ( good for strange debugging )
472: if (System.getSecurityManager() != null) {
473: randomIS = (DataInputStream) AccessController
474: .doPrivileged(new PrivilegedSetRandomFile());
475: } else {
476: try {
477: devRandomSource = s;
478: File f = new File(devRandomSource);
479: if (!f.exists())
480: return;
481: randomIS = new DataInputStream(new FileInputStream(f));
482: randomIS.readLong();
483: if (log.isDebugEnabled())
484: log.debug("Opening " + devRandomSource);
485: } catch (IOException ex) {
486: randomIS = null;
487: }
488: }
489: }
490:
491: public String getRandomFile() {
492: return devRandomSource;
493: }
494:
495: /**
496: * Return the random number generator instance we should use for
497: * generating session identifiers. If there is no such generator
498: * currently defined, construct and seed a new one.
499: */
500: public synchronized Random getRandom() {
501: if (this .random == null) {
502: synchronized (this ) {
503: if (this .random == null) {
504: // Calculate the new random number generator seed
505: long seed = System.currentTimeMillis();
506: long t1 = seed;
507: char entropy[] = getEntropy().toCharArray();
508: for (int i = 0; i < entropy.length; i++) {
509: long update = ((byte) entropy[i]) << ((i % 8) * 8);
510: seed ^= update;
511: }
512: try {
513: // Construct and seed a new random number generator
514: Class clazz = Class.forName(randomClass);
515: this .random = (Random) clazz.newInstance();
516: this .random.setSeed(seed);
517: } catch (Exception e) {
518: // Fall back to the simple case
519: log.error(sm.getString("managerBase.random",
520: randomClass), e);
521: this .random = new java.util.Random();
522: this .random.setSeed(seed);
523: }
524: long t2 = System.currentTimeMillis();
525: if ((t2 - t1) > 100)
526: log.debug(sm.getString("managerBase.seeding",
527: randomClass)
528: + " " + (t2 - t1));
529: }
530: }
531: }
532:
533: return (this .random);
534:
535: }
536:
537: /**
538: * Return the random number generator class name.
539: */
540: public String getRandomClass() {
541:
542: return (this .randomClass);
543:
544: }
545:
546: /**
547: * Set the random number generator class name.
548: *
549: * @param randomClass The new random number generator class name
550: */
551: public void setRandomClass(String randomClass) {
552:
553: String oldRandomClass = this .randomClass;
554: this .randomClass = randomClass;
555: support.firePropertyChange("randomClass", oldRandomClass,
556: this .randomClass);
557:
558: }
559:
560: // --------------------------------------------------------- Public Methods
561:
562: /**
563: * Implements the Manager interface, direct call to processExpires
564: */
565: public void backgroundProcess() {
566: }
567:
568: public void destroy() {
569: if (oname != null)
570: Registry.getRegistry(null, null).unregisterComponent(oname);
571: initialized = false;
572: oname = null;
573: }
574:
575: public void init() {
576: if (initialized)
577: return;
578: initialized = true;
579:
580: if (oname == null) {
581: try {
582: StandardContext ctx = (StandardContext) this
583: .getContainer();
584: Engine eng = (Engine) ctx.getParent().getParent();
585: domain = ctx.getEngineName();
586: StandardHost hst = (StandardHost) ctx.getParent();
587: String path = ctx.getPath();
588: if (path.equals("")) {
589: path = "/";
590: }
591: oname = new ObjectName(domain + ":type=Manager,path="
592: + path + ",host=" + hst.getName());
593: Registry.getRegistry(null, null).registerComponent(
594: this , oname, null);
595: } catch (Exception e) {
596: log.error("Error registering ", e);
597: }
598: }
599: log.debug("Registering " + oname);
600:
601: }
602:
603: /**
604: * Add this Session to the set of active Sessions for this Manager.
605: *
606: * @param session Session to be added
607: */
608: public void add(Session session) {
609:
610: synchronized (sessions) {
611: sessions.put(session.getId(), session);
612: if (sessions.size() > maxActive) {
613: maxActive = sessions.size();
614: }
615: }
616: }
617:
618: /**
619: * Add a property change listener to this component.
620: *
621: * @param listener The listener to add
622: */
623: public void addPropertyChangeListener(
624: PropertyChangeListener listener) {
625:
626: support.addPropertyChangeListener(listener);
627:
628: }
629:
630: /**
631: * Construct and return a new session object, based on the default
632: * settings specified by this Manager's properties. The session
633: * id will be assigned by this method, and available via the getId()
634: * method of the returned session. If a new session cannot be created
635: * for any reason, return <code>null</code>.
636: *
637: * @exception IllegalStateException if a new session cannot be
638: * instantiated for any reason
639: */
640: public Session createSession() {
641:
642: // Recycle or create a Session instance
643: Session session = createEmptySession();
644:
645: // Initialize the properties of the new session and return it
646: session.setNew(true);
647: session.setValid(true);
648: session.setCreationTime(System.currentTimeMillis());
649: session.setMaxInactiveInterval(this .maxInactiveInterval);
650: String sessionId = generateSessionId();
651:
652: String jvmRoute = getJvmRoute();
653: // @todo Move appending of jvmRoute generateSessionId()???
654: if (jvmRoute != null) {
655: sessionId += '.' + jvmRoute;
656: }
657: synchronized (sessions) {
658: while (sessions.get(sessionId) != null) { // Guarantee uniqueness
659: duplicates++;
660: sessionId = generateSessionId();
661: // @todo Move appending of jvmRoute generateSessionId()???
662: if (jvmRoute != null) {
663: sessionId += '.' + jvmRoute;
664: }
665: }
666: }
667:
668: session.setId(sessionId);
669: sessionCounter++;
670:
671: return (session);
672:
673: }
674:
675: /**
676: * Get a session from the recycled ones or create a new empty one.
677: * The PersistentManager manager does not need to create session data
678: * because it reads it from the Store.
679: */
680: public Session createEmptySession() {
681: return (getNewSession());
682: }
683:
684: /**
685: * Return the active Session, associated with this Manager, with the
686: * specified session id (if any); otherwise return <code>null</code>.
687: *
688: * @param id The session id for the session to be returned
689: *
690: * @exception IllegalStateException if a new session cannot be
691: * instantiated for any reason
692: * @exception IOException if an input/output error occurs while
693: * processing this request
694: */
695: public Session findSession(String id) throws IOException {
696:
697: if (id == null)
698: return (null);
699: synchronized (sessions) {
700: Session session = (Session) sessions.get(id);
701: return (session);
702: }
703:
704: }
705:
706: /**
707: * Return the set of active Sessions associated with this Manager.
708: * If this Manager has no active Sessions, a zero-length array is returned.
709: */
710: public Session[] findSessions() {
711:
712: Session results[] = null;
713: synchronized (sessions) {
714: results = new Session[sessions.size()];
715: results = (Session[]) sessions.values().toArray(results);
716: }
717: return (results);
718:
719: }
720:
721: /**
722: * Remove this Session from the active Sessions for this Manager.
723: *
724: * @param session Session to be removed
725: */
726: public void remove(Session session) {
727:
728: synchronized (sessions) {
729: sessions.remove(session.getId());
730: }
731:
732: }
733:
734: /**
735: * Remove a property change listener from this component.
736: *
737: * @param listener The listener to remove
738: */
739: public void removePropertyChangeListener(
740: PropertyChangeListener listener) {
741:
742: support.removePropertyChangeListener(listener);
743:
744: }
745:
746: // ------------------------------------------------------ Protected Methods
747:
748: /**
749: * Get new session class to be used in the doLoad() method.
750: */
751: protected StandardSession getNewSession() {
752: return new StandardSession(this );
753: }
754:
755: protected void getRandomBytes(byte bytes[]) {
756: // Generate a byte array containing a session identifier
757: if (devRandomSource != null && randomIS == null) {
758: setRandomFile(devRandomSource);
759: }
760: if (randomIS != null) {
761: try {
762: int len = randomIS.read(bytes);
763: if (len == bytes.length) {
764: return;
765: }
766: log.debug("Got " + len + " " + bytes.length);
767: } catch (Exception ex) {
768: }
769: devRandomSource = null;
770: randomIS = null;
771: }
772: Random random = getRandom();
773: getRandom().nextBytes(bytes);
774: }
775:
776: /**
777: * Generate and return a new session identifier.
778: */
779: protected synchronized String generateSessionId() {
780:
781: byte random[] = new byte[16];
782:
783: // Render the result as a String of hexadecimal digits
784: StringBuffer result = new StringBuffer();
785: int resultLenBytes = 0;
786: while (resultLenBytes < this .sessionIdLength) {
787: getRandomBytes(random);
788: random = getDigest().digest(random);
789: for (int j = 0; j < random.length
790: && resultLenBytes < this .sessionIdLength; j++) {
791: byte b1 = (byte) ((random[j] & 0xf0) >> 4);
792: byte b2 = (byte) (random[j] & 0x0f);
793: if (b1 < 10)
794: result.append((char) ('0' + b1));
795: else
796: result.append((char) ('A' + (b1 - 10)));
797: if (b2 < 10)
798: result.append((char) ('0' + b2));
799: else
800: result.append((char) ('A' + (b2 - 10)));
801: resultLenBytes++;
802: }
803: }
804: return (result.toString());
805:
806: }
807:
808: // ------------------------------------------------------ Protected Methods
809:
810: /**
811: * Retrieve the enclosing Engine for this Manager.
812: *
813: * @return an Engine object (or null).
814: */
815: public Engine getEngine() {
816: Engine e = null;
817: for (Container c = getContainer(); e == null && c != null; c = c
818: .getParent()) {
819: if (c != null && c instanceof Engine) {
820: e = (Engine) c;
821: }
822: }
823: return e;
824: }
825:
826: /**
827: * Retrieve the JvmRoute for the enclosing Engine.
828: * @return the JvmRoute or null.
829: */
830: public String getJvmRoute() {
831: Engine e = getEngine();
832: return e == null ? null : e.getJvmRoute();
833: }
834:
835: // -------------------------------------------------------- Package Methods
836:
837: /**
838: * Log a message on the Logger associated with our Container (if any).
839: *
840: * @param message Message to be logged
841: * @deprecated
842: */
843: protected void log(String message) {
844: log.info(message);
845: }
846:
847: /**
848: * Log a message on the Logger associated with our Container (if any).
849: *
850: * @param message Message to be logged
851: * @param throwable Associated exception
852: * @deprecated
853: */
854: protected void log(String message, Throwable throwable) {
855: log.info(message, throwable);
856: }
857:
858: public void setSessionCounter(int sessionCounter) {
859: this .sessionCounter = sessionCounter;
860: }
861:
862: /**
863: * Total sessions created by this manager.
864: *
865: * @return sessions created
866: */
867: public int getSessionCounter() {
868: return sessionCounter;
869: }
870:
871: /**
872: * Number of duplicated session IDs generated by the random source.
873: * Anything bigger than 0 means problems.
874: *
875: * @return The count of duplicates
876: */
877: public int getDuplicates() {
878: return duplicates;
879: }
880:
881: public void setDuplicates(int duplicates) {
882: this .duplicates = duplicates;
883: }
884:
885: /**
886: * Returns the number of active sessions
887: *
888: * @return number of sessions active
889: */
890: public int getActiveSessions() {
891: return sessions.size();
892: }
893:
894: /**
895: * Max number of concurrent active sessions
896: *
897: * @return The highest number of concurrent active sessions
898: */
899: public int getMaxActive() {
900: return maxActive;
901: }
902:
903: public void setMaxActive(int maxActive) {
904: this .maxActive = maxActive;
905: }
906:
907: /**
908: * For debugging: return a list of all session ids currently active
909: *
910: */
911: public String listSessionIds() {
912: StringBuffer sb = new StringBuffer();
913: Iterator keys = sessions.keySet().iterator();
914: while (keys.hasNext()) {
915: sb.append(keys.next()).append(" ");
916: }
917: return sb.toString();
918: }
919:
920: /**
921: * For debugging: get a session attribute
922: *
923: * @param sessionId
924: * @param key
925: * @return The attribute value, if found, null otherwise
926: */
927: public String getSessionAttribute(String sessionId, String key) {
928: Session s = (Session) sessions.get(sessionId);
929: if (s == null) {
930: log.info("Session not found " + sessionId);
931: return null;
932: }
933: Object o = s.getSession().getAttribute(key);
934: if (o == null)
935: return null;
936: return o.toString();
937: }
938:
939: public void expireSession(String sessionId) {
940: Session s = (Session) sessions.get(sessionId);
941: if (s == null) {
942: log.info("Session not found " + sessionId);
943: return;
944: }
945: s.expire();
946: }
947:
948: public String getLastAccessedTime(String sessionId) {
949: Session s = (Session) sessions.get(sessionId);
950: if (s == null) {
951: log.info("Session not found " + sessionId);
952: return "";
953: }
954: return new Date(s.getLastAccessedTime()).toString();
955: }
956:
957: // -------------------- JMX and Registration --------------------
958: protected String domain;
959: protected ObjectName oname;
960: protected MBeanServer mserver;
961:
962: public ObjectName getObjectName() {
963: return oname;
964: }
965:
966: public String getDomain() {
967: return domain;
968: }
969:
970: public ObjectName preRegister(MBeanServer server, ObjectName name)
971: throws Exception {
972: oname = name;
973: mserver = server;
974: domain = name.getDomain();
975: return name;
976: }
977:
978: public void postRegister(Boolean registrationDone) {
979: }
980:
981: public void preDeregister() throws Exception {
982: }
983:
984: public void postDeregister() {
985: }
986:
987: }
|