blob: fc2a6eb2320487401908f422abaa06fd52a93228 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.session;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Deque;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Manager;
import org.apache.catalina.Session;
import org.apache.catalina.mbeans.MBeanUtils;
import org.apache.catalina.util.LifecycleMBeanBase;
import org.apache.catalina.util.SessionIdGenerator;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
/**
* Minimal implementation of the <b>Manager</b> interface that supports
* no session persistence or distributable capabilities. This class may
* be subclassed to create more sophisticated Manager implementations.
*
* @author Craig R. McClanahan
* @version $Id: ManagerBase.java,v 1.1 2011/06/28 21:08:18 rherrmann Exp $
*/
public abstract class ManagerBase extends LifecycleMBeanBase
implements Manager, PropertyChangeListener {
private final Log log = LogFactory.getLog(ManagerBase.class); // must not be static
// ----------------------------------------------------- Instance Variables
/**
* The Container with which this Manager is associated.
*/
protected Container container;
/**
* The distributable flag for Sessions created by this Manager. If this
* flag is set to <code>true</code>, any user attributes added to a
* session controlled by this Manager must be Serializable.
*/
protected boolean distributable;
/**
* The descriptive information string for this implementation.
*/
private static final String info = "ManagerBase/1.0";
/**
* The default maximum inactive interval for Sessions created by
* this Manager.
*/
protected int maxInactiveInterval = 30 * 60;
/**
* The session id length of Sessions created by this Manager.
*/
protected int sessionIdLength = 16;
/**
* The descriptive name of this Manager implementation (for logging).
*/
protected static String name = "ManagerBase";
/**
* The Java class name of the secure random number generator class to be
* used when generating session identifiers. The random number generator
* class must be self-seeding and have a zero-argument constructor. If not
* specified, an instance of {@link java.security.SecureRandom} will be
* generated.
*/
protected String secureRandomClass = null;
/**
* The name of the algorithm to use to create instances of
* {@link java.security.SecureRandom} which are used to generate session IDs.
* If no algorithm is specified, SHA1PRNG is used. To use the platform
* default (which may be SHA1PRNG), specify the empty string. If an invalid
* algorithm and/or provider is specified the SecureRandom instances will be
* created using the defaults. If that fails, the SecureRandom instances
* will be created using platform defaults.
*/
protected String secureRandomAlgorithm = "SHA1PRNG";
/**
* The name of the provider to use to create instances of
* {@link java.security.SecureRandom} which are used to generate session IDs.
* If no algorithm is specified the of SHA1PRNG default is used. If an
* invalid algorithm and/or provider is specified the SecureRandom instances
* will be created using the defaults. If that fails, the SecureRandom
* instances will be created using platform defaults.
*/
protected String secureRandomProvider = null;
protected SessionIdGenerator sessionIdGenerator = null;
/**
* The longest time (in seconds) that an expired session had been alive.
*/
protected volatile int sessionMaxAliveTime;
private final Object sessionMaxAliveTimeUpdateLock = new Object();
protected static final int TIMING_STATS_CACHE_SIZE = 100;
protected Deque<SessionTiming> sessionCreationTiming =
new LinkedList<SessionTiming>();
protected Deque<SessionTiming> sessionExpirationTiming =
new LinkedList<SessionTiming>();
/**
* Number of sessions that have expired.
*/
protected AtomicLong expiredSessions = new AtomicLong(0);
/**
* The set of currently active Sessions for this Manager, keyed by
* session identifier.
*/
protected Map<String, Session> sessions = new ConcurrentHashMap<String, Session>();
// Number of sessions created by this manager
protected long sessionCounter=0;
protected volatile int maxActive=0;
private final Object maxActiveUpdateLock = new Object();
/**
* The maximum number of active Sessions allowed, or -1 for no limit.
*/
protected int maxActiveSessions = -1;
/**
* Number of session creations that failed due to maxActiveSessions.
*/
protected int rejectedSessions = 0;
// number of duplicated session ids - anything >0 means we have problems
protected volatile int duplicates=0;
/**
* Processing time during session expiration.
*/
protected long processingTime = 0;
/**
* Iteration count for background processing.
*/
private int count = 0;
/**
* Frequency of the session expiration, and related manager operations.
* Manager operations will be done once for the specified amount of
* backgrondProcess calls (ie, the lower the amount, the most often the
* checks will occur).
*/
protected int processExpiresFrequency = 6;
/**
* The string manager for this package.
*/
protected static final StringManager sm =
StringManager.getManager(Constants.Package);
/**
* The property change support for this component.
*/
protected PropertyChangeSupport support = new PropertyChangeSupport(this);
// ------------------------------------------------------------- Properties
/**
* Return the Container with which this Manager is associated.
*/
@Override
public Container getContainer() {
return (this.container);
}
/**
* Set the Container with which this Manager is associated.
*
* @param container The newly associated Container
*/
@Override
public void setContainer(Container container) {
// De-register from the old Container (if any)
if ((this.container != null) && (this.container instanceof Context))
((Context) this.container).removePropertyChangeListener(this);
Container oldContainer = this.container;
this.container = container;
support.firePropertyChange("container", oldContainer, this.container);
// Register with the new Container (if any)
if ((this.container != null) && (this.container instanceof Context)) {
setMaxInactiveInterval
( ((Context) this.container).getSessionTimeout()*60 );
((Context) this.container).addPropertyChangeListener(this);
}
}
/** Returns the name of the implementation class.
*/
public String getClassName() {
return this.getClass().getName();
}
/**
* Return the distributable flag for the sessions supported by
* this Manager.
*/
@Override
public boolean getDistributable() {
return (this.distributable);
}
/**
* Set the distributable flag for the sessions supported by this
* Manager. If this flag is set, all user data objects added to
* sessions associated with this manager must implement Serializable.
*
* @param distributable The new distributable flag
*/
@Override
public void setDistributable(boolean distributable) {
boolean oldDistributable = this.distributable;
this.distributable = distributable;
support.firePropertyChange("distributable",
Boolean.valueOf(oldDistributable),
Boolean.valueOf(this.distributable));
}
/**
* Return descriptive information about this Manager implementation and
* the corresponding version number, in the format
* <code>&lt;description&gt;/&lt;version&gt;</code>.
*/
@Override
public String getInfo() {
return (info);
}
/**
* Return the default maximum inactive interval (in seconds)
* for Sessions created by this Manager.
*/
@Override
public int getMaxInactiveInterval() {
return (this.maxInactiveInterval);
}
/**
* Set the default maximum inactive interval (in seconds)
* for Sessions created by this Manager.
*
* @param interval The new default value
*/
@Override
public void setMaxInactiveInterval(int interval) {
int oldMaxInactiveInterval = this.maxInactiveInterval;
this.maxInactiveInterval = interval;
support.firePropertyChange("maxInactiveInterval",
Integer.valueOf(oldMaxInactiveInterval),
Integer.valueOf(this.maxInactiveInterval));
}
/**
* Gets the session id length (in bytes) of Sessions created by
* this Manager.
*
* @return The session id length
*/
@Override
public int getSessionIdLength() {
return (this.sessionIdLength);
}
/**
* Sets the session id length (in bytes) for Sessions created by this
* Manager.
*
* @param idLength The session id length
*/
@Override
public void setSessionIdLength(int idLength) {
int oldSessionIdLength = this.sessionIdLength;
this.sessionIdLength = idLength;
support.firePropertyChange("sessionIdLength",
Integer.valueOf(oldSessionIdLength),
Integer.valueOf(this.sessionIdLength));
}
/**
* Return the descriptive short name of this Manager implementation.
*/
public String getName() {
return (name);
}
/**
* Return the secure random number generator class name.
*/
public String getSecureRandomClass() {
return (this.secureRandomClass);
}
/**
* Set the secure random number generator class name.
*
* @param secureRandomClass The new secure random number generator class
* name
*/
public void setSecureRandomClass(String secureRandomClass) {
String oldSecureRandomClass = this.secureRandomClass;
this.secureRandomClass = secureRandomClass;
support.firePropertyChange("secureRandomClass", oldSecureRandomClass,
this.secureRandomClass);
}
/**
* Return the secure random number generator algorithm name.
*/
public String getSecureRandomAlgorithm() {
return secureRandomAlgorithm;
}
/**
* Set the secure random number generator algorithm name.
*
* @param secureRandomAlgorithm The new secure random number generator
* algorithm name
*/
public void setSecureRandomAlgorithm(String secureRandomAlgorithm) {
this.secureRandomAlgorithm = secureRandomAlgorithm;
}
/**
* Return the secure random number generator provider name.
*/
public String getSecureRandomProvider() {
return secureRandomProvider;
}
/**
* Set the secure random number generator provider name.
*
* @param secureRandomProvider The new secure random number generator
* provider name
*/
public void setSecureRandomProvider(String secureRandomProvider) {
this.secureRandomProvider = secureRandomProvider;
}
/**
* Number of session creations that failed due to maxActiveSessions
*
* @return The count
*/
@Override
public int getRejectedSessions() {
return rejectedSessions;
}
/**
* Gets the number of sessions that have expired.
*
* @return Number of sessions that have expired
*/
@Override
public long getExpiredSessions() {
return expiredSessions.get();
}
/**
* Sets the number of sessions that have expired.
*
* @param expiredSessions Number of sessions that have expired
*/
@Override
public void setExpiredSessions(long expiredSessions) {
this.expiredSessions.set(expiredSessions);
}
public long getProcessingTime() {
return processingTime;
}
public void setProcessingTime(long processingTime) {
this.processingTime = processingTime;
}
/**
* Return the frequency of manager checks.
*/
public int getProcessExpiresFrequency() {
return (this.processExpiresFrequency);
}
/**
* Set the manager checks frequency.
*
* @param processExpiresFrequency the new manager checks frequency
*/
public void setProcessExpiresFrequency(int processExpiresFrequency) {
if (processExpiresFrequency <= 0) {
return;
}
int oldProcessExpiresFrequency = this.processExpiresFrequency;
this.processExpiresFrequency = processExpiresFrequency;
support.firePropertyChange("processExpiresFrequency",
Integer.valueOf(oldProcessExpiresFrequency),
Integer.valueOf(this.processExpiresFrequency));
}
// --------------------------------------------------------- Public Methods
/**
* Implements the Manager interface, direct call to processExpires
*/
@Override
public void backgroundProcess() {
count = (count + 1) % processExpiresFrequency;
if (count == 0)
processExpires();
}
/**
* Invalidate all sessions that have expired.
*/
public void processExpires() {
long timeNow = System.currentTimeMillis();
Session sessions[] = findSessions();
int expireHere = 0 ;
if(log.isDebugEnabled())
log.debug("Start expire sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length);
for (int i = 0; i < sessions.length; i++) {
if (sessions[i]!=null && !sessions[i].isValid()) {
expireHere++;
}
}
long timeEnd = System.currentTimeMillis();
if(log.isDebugEnabled())
log.debug("End expire sessions " + getName() + " processingTime " + (timeEnd - timeNow) + " expired sessions: " + expireHere);
processingTime += ( timeEnd - timeNow );
}
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
setDistributable(((Context) getContainer()).getDistributable());
}
@Override
protected void startInternal() throws LifecycleException {
// Ensure caches for timing stats are the right size by filling with
// nulls.
while (sessionCreationTiming.size() < TIMING_STATS_CACHE_SIZE) {
sessionCreationTiming.add(null);
}
while (sessionExpirationTiming.size() < TIMING_STATS_CACHE_SIZE) {
sessionExpirationTiming.add(null);
}
sessionIdGenerator = new SessionIdGenerator();
sessionIdGenerator.setJvmRoute(getJvmRoute());
sessionIdGenerator.setSecureRandomAlgorithm(getSecureRandomAlgorithm());
sessionIdGenerator.setSecureRandomClass(getSecureRandomClass());
sessionIdGenerator.setSecureRandomProvider(getSecureRandomProvider());
sessionIdGenerator.setSessionIdLength(getSessionIdLength());
// Force initialization of the random number generator
if (log.isDebugEnabled())
log.debug("Force random number initialization starting");
sessionIdGenerator.generateSessionId();
if (log.isDebugEnabled())
log.debug("Force random number initialization completed");
}
@Override
protected void stopInternal() throws LifecycleException {
this.sessionIdGenerator = null;
}
/**
* Add this Session to the set of active Sessions for this Manager.
*
* @param session Session to be added
*/
@Override
public void add(Session session) {
sessions.put(session.getIdInternal(), session);
int size = getActiveSessions();
if( size > maxActive ) {
synchronized(maxActiveUpdateLock) {
if( size > maxActive ) {
maxActive = size;
}
}
}
}
/**
* Add a property change listener to this component.
*
* @param listener The listener to add
*/
@Override
public void addPropertyChangeListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
/**
* Construct and return a new session object, based on the default
* settings specified by this Manager's properties. The session
* id specified will be used as the session id.
* If a new session cannot be created for any reason, return
* <code>null</code>.
*
* @param sessionId The session id which should be used to create the
* new session; if <code>null</code>, a new session id will be
* generated
* @exception IllegalStateException if a new session cannot be
* instantiated for any reason
*/
@Override
public Session createSession(String sessionId) {
if ((maxActiveSessions >= 0) &&
(getActiveSessions() >= maxActiveSessions)) {
rejectedSessions++;
throw new IllegalStateException(
sm.getString("managerBase.createSession.ise"));
}
// Recycle or create a Session instance
Session session = createEmptySession();
// Initialize the properties of the new session and return it
session.setNew(true);
session.setValid(true);
session.setCreationTime(System.currentTimeMillis());
session.setMaxInactiveInterval(this.maxInactiveInterval);
String id = sessionId;
if (id == null) {
id = generateSessionId();
}
session.setId(id);
sessionCounter++;
SessionTiming timing = new SessionTiming(session.getCreationTime(), 0);
synchronized (sessionCreationTiming) {
sessionCreationTiming.add(timing);
sessionCreationTiming.poll();
}
return (session);
}
/**
* Get a session from the recycled ones or create a new empty one.
* The PersistentManager manager does not need to create session data
* because it reads it from the Store.
*/
@Override
public Session createEmptySession() {
return (getNewSession());
}
/**
* Return the active Session, associated with this Manager, with the
* specified session id (if any); otherwise return <code>null</code>.
*
* @param id The session id for the session to be returned
*
* @exception IllegalStateException if a new session cannot be
* instantiated for any reason
* @exception IOException if an input/output error occurs while
* processing this request
*/
@Override
public Session findSession(String id) throws IOException {
if (id == null)
return (null);
return sessions.get(id);
}
/**
* Return the set of active Sessions associated with this Manager.
* If this Manager has no active Sessions, a zero-length array is returned.
*/
@Override
public Session[] findSessions() {
return sessions.values().toArray(new Session[0]);
}
/**
* Remove this Session from the active Sessions for this Manager.
*
* @param session Session to be removed
*/
@Override
public void remove(Session session) {
remove(session, false);
}
/**
* Remove this Session from the active Sessions for this Manager.
*
* @param session Session to be removed
* @param update Should the expiration statistics be updated
*/
@Override
public void remove(Session session, boolean update) {
// If the session has expired - as opposed to just being removed from
// the manager because it is being persisted - update the expired stats
if (update) {
long timeNow = System.currentTimeMillis();
int timeAlive =
(int) (timeNow - session.getCreationTimeInternal())/1000;
updateSessionMaxAliveTime(timeAlive);
expiredSessions.incrementAndGet();
SessionTiming timing = new SessionTiming(timeNow, timeAlive);
synchronized (sessionExpirationTiming) {
sessionExpirationTiming.add(timing);
sessionExpirationTiming.poll();
}
}
if (session.getIdInternal() != null) {
sessions.remove(session.getIdInternal());
}
}
/**
* Remove a property change listener from this component.
*
* @param listener The listener to remove
*/
@Override
public void removePropertyChangeListener(PropertyChangeListener listener) {
support.removePropertyChangeListener(listener);
}
/**
* Change the session ID of the current session to a new randomly generated
* session ID.
*
* @param session The session to change the session ID for
*/
@Override
public void changeSessionId(Session session) {
session.setId(generateSessionId());
}
// ------------------------------------------------------ Protected Methods
/**
* Get new session class to be used in the doLoad() method.
*/
protected StandardSession getNewSession() {
return new StandardSession(this);
}
/**
* Generate and return a new session identifier.
*/
protected String generateSessionId() {
String result = null;
do {
if (result != null) {
duplicates++;
}
result = sessionIdGenerator.generateSessionId();
} while (sessions.containsKey(result));
return result;
}
// ------------------------------------------------------ Protected Methods
/**
* Retrieve the enclosing Engine for this Manager.
*
* @return an Engine object (or null).
*/
public Engine getEngine() {
Engine e = null;
for (Container c = getContainer(); e == null && c != null ; c = c.getParent()) {
if (c instanceof Engine) {
e = (Engine)c;
}
}
return e;
}
/**
* Retrieve the JvmRoute for the enclosing Engine.
* @return the JvmRoute or null.
*/
public String getJvmRoute() {
Engine e = getEngine();
return e == null ? null : e.getJvmRoute();
}
// -------------------------------------------------------- Package Methods
@Override
public void setSessionCounter(long sessionCounter) {
this.sessionCounter = sessionCounter;
}
/**
* Total sessions created by this manager.
*
* @return sessions created
*/
@Override
public long getSessionCounter() {
return sessionCounter;
}
/**
* Number of duplicated session IDs generated by the random source.
* Anything bigger than 0 means problems.
*
* @return The count of duplicates
*/
public int getDuplicates() {
return duplicates;
}
public void setDuplicates(int duplicates) {
this.duplicates = duplicates;
}
/**
* Returns the number of active sessions
*
* @return number of sessions active
*/
@Override
public int getActiveSessions() {
return sessions.size();
}
/**
* Max number of concurrent active sessions
*
* @return The highest number of concurrent active sessions
*/
@Override
public int getMaxActive() {
return maxActive;
}
@Override
public void setMaxActive(int maxActive) {
synchronized (maxActiveUpdateLock) {
this.maxActive = maxActive;
}
}
/**
* Return the maximum number of active Sessions allowed, or -1 for
* no limit.
*/
public int getMaxActiveSessions() {
return (this.maxActiveSessions);
}
/**
* Set the maximum number of active Sessions allowed, or -1 for
* no limit.
*
* @param max The new maximum number of sessions
*/
public void setMaxActiveSessions(int max) {
int oldMaxActiveSessions = this.maxActiveSessions;
this.maxActiveSessions = max;
support.firePropertyChange("maxActiveSessions",
Integer.valueOf(oldMaxActiveSessions),
Integer.valueOf(this.maxActiveSessions));
}
/**
* Gets the longest time (in seconds) that an expired session had been
* alive.
*
* @return Longest time (in seconds) that an expired session had been
* alive.
*/
@Override
public int getSessionMaxAliveTime() {
return sessionMaxAliveTime;
}
/**
* Sets the longest time (in seconds) that an expired session had been
* alive. Typically used for resetting the current value.
*
* @param sessionMaxAliveTime Longest time (in seconds) that an expired
* session had been alive.
*/
@Override
public void setSessionMaxAliveTime(int sessionMaxAliveTime) {
synchronized (sessionMaxAliveTimeUpdateLock) {
this.sessionMaxAliveTime = sessionMaxAliveTime;
}
}
/**
* Updates the sessionMaxAliveTime attribute if the candidate value is
* larger than the current value.
*
* @param sessionAliveTime The candidate value (in seconds) for the new
* sessionMaxAliveTime value.
*/
public void updateSessionMaxAliveTime(int sessionAliveTime) {
if (sessionAliveTime > this.sessionMaxAliveTime) {
synchronized (sessionMaxAliveTimeUpdateLock) {
if (sessionAliveTime > this.sessionMaxAliveTime) {
this.sessionMaxAliveTime = sessionAliveTime;
}
}
}
}
/**
* Gets the average time (in seconds) that expired sessions had been
* alive based on the last 100 sessions to expire. If less than
* 100 sessions have expired then all available data is used.
*
* @return Average time (in seconds) that expired sessions had been
* alive.
*/
@Override
public int getSessionAverageAliveTime() {
// Copy current stats
List<SessionTiming> copy = new ArrayList<SessionTiming>();
synchronized (sessionExpirationTiming) {
copy.addAll(sessionExpirationTiming);
}
// Init
int counter = 0;
int result = 0;
Iterator<SessionTiming> iter = copy.iterator();
// Calculate average
while (iter.hasNext()) {
SessionTiming timing = iter.next();
if (timing != null) {
int timeAlive = timing.getDuration();
counter++;
// Very careful not to overflow - probably not necessary
result =
(result * ((counter - 1)/counter)) + (timeAlive/counter);
}
}
return result;
}
/**
* Gets the current rate of session creation (in session per minute) based
* on the creation time of the previous 100 sessions created. If less than
* 100 sessions have been created then all available data is used.
*
* @return The current rate (in sessions per minute) of session creation
*/
@Override
public int getSessionCreateRate() {
long now = System.currentTimeMillis();
// Copy current stats
List<SessionTiming> copy = new ArrayList<SessionTiming>();
synchronized (sessionCreationTiming) {
copy.addAll(sessionCreationTiming);
}
// Init
long oldest = now;
int counter = 0;
int result = 0;
Iterator<SessionTiming> iter = copy.iterator();
// Calculate rate
while (iter.hasNext()) {
SessionTiming timing = iter.next();
if (timing != null) {
counter++;
if (timing.getTimestamp() < oldest) {
oldest = timing.getTimestamp();
}
}
}
if (counter > 0) {
if (oldest < now) {
result = (1000*60*counter)/(int) (now - oldest);
} else {
result = Integer.MAX_VALUE;
}
}
return result;
}
/**
* Gets the current rate of session expiration (in session per minute) based
* on the expiry time of the previous 100 sessions expired. If less than
* 100 sessions have expired then all available data is used.
*
* @return The current rate (in sessions per minute) of session expiration
*/
@Override
public int getSessionExpireRate() {
long now = System.currentTimeMillis();
// Copy current stats
List<SessionTiming> copy = new ArrayList<SessionTiming>();
synchronized (sessionExpirationTiming) {
copy.addAll(sessionExpirationTiming);
}
// Init
long oldest = now;
int counter = 0;
int result = 0;
Iterator<SessionTiming> iter = copy.iterator();
// Calculate rate
while (iter.hasNext()) {
SessionTiming timing = iter.next();
if (timing != null) {
counter++;
if (timing.getTimestamp() < oldest) {
oldest = timing.getTimestamp();
}
}
}
if (counter > 0) {
if (oldest < now) {
result = (1000*60*counter)/(int) (now - oldest);
} else {
// Better than reporting zero
result = Integer.MAX_VALUE;
}
}
return result;
}
/**
* For debugging: return a list of all session ids currently active
*
*/
public String listSessionIds() {
StringBuilder sb=new StringBuilder();
Iterator<String> keys = sessions.keySet().iterator();
while (keys.hasNext()) {
sb.append(keys.next()).append(" ");
}
return sb.toString();
}
/**
* For debugging: get a session attribute
*
* @param sessionId
* @param key
* @return The attribute value, if found, null otherwise
*/
public String getSessionAttribute( String sessionId, String key ) {
Session s = sessions.get(sessionId);
if( s==null ) {
if(log.isInfoEnabled())
log.info("Session not found " + sessionId);
return null;
}
Object o=s.getSession().getAttribute(key);
if( o==null ) return null;
return o.toString();
}
/**
* Returns information about the session with the given session id.
*
* <p>The session information is organized as a HashMap, mapping
* session attribute names to the String representation of their values.
*
* @param sessionId Session id
*
* @return HashMap mapping session attribute names to the String
* representation of their values, or null if no session with the
* specified id exists, or if the session does not have any attributes
*/
public HashMap<String, String> getSession(String sessionId) {
Session s = sessions.get(sessionId);
if (s == null) {
if (log.isInfoEnabled()) {
log.info("Session not found " + sessionId);
}
return null;
}
Enumeration<String> ee = s.getSession().getAttributeNames();
if (ee == null || !ee.hasMoreElements()) {
return null;
}
HashMap<String, String> map = new HashMap<String, String>();
while (ee.hasMoreElements()) {
String attrName = ee.nextElement();
map.put(attrName, getSessionAttribute(sessionId, attrName));
}
return map;
}
public void expireSession( String sessionId ) {
Session s=sessions.get(sessionId);
if( s==null ) {
if(log.isInfoEnabled())
log.info("Session not found " + sessionId);
return;
}
s.expire();
}
public long getThisAccessedTimestamp( String sessionId ) {
Session s=sessions.get(sessionId);
if(s== null)
return -1 ;
return s.getThisAccessedTime();
}
public String getThisAccessedTime( String sessionId ) {
Session s=sessions.get(sessionId);
if( s==null ) {
if(log.isInfoEnabled())
log.info("Session not found " + sessionId);
return "";
}
return new Date(s.getThisAccessedTime()).toString();
}
public long getLastAccessedTimestamp( String sessionId ) {
Session s=sessions.get(sessionId);
if(s== null)
return -1 ;
return s.getLastAccessedTime();
}
public String getLastAccessedTime( String sessionId ) {
Session s=sessions.get(sessionId);
if( s==null ) {
if(log.isInfoEnabled())
log.info("Session not found " + sessionId);
return "";
}
return new Date(s.getLastAccessedTime()).toString();
}
public String getCreationTime( String sessionId ) {
Session s=sessions.get(sessionId);
if( s==null ) {
if(log.isInfoEnabled())
log.info("Session not found " + sessionId);
return "";
}
return new Date(s.getCreationTime()).toString();
}
public long getCreationTimestamp( String sessionId ) {
Session s=sessions.get(sessionId);
if(s== null)
return -1 ;
return s.getCreationTime();
}
/**
* Return a String rendering of this object.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder(this.getClass().getName());
sb.append('[');
if (container == null) {
sb.append("Container is null");
} else {
sb.append(container.getName());
}
sb.append(']');
return sb.toString();
}
// -------------------- JMX and Registration --------------------
@Override
public String getObjectNameKeyProperties() {
StringBuilder name = new StringBuilder("type=Manager");
if (container instanceof Context) {
name.append(",context=");
String contextName = container.getName();
if (!contextName.startsWith("/")) {
name.append('/');
}
name.append(contextName);
Context context = (Context) container;
name.append(",host=");
name.append(context.getParent().getName());
} else {
// Unlikely / impossible? Handle it to be safe
name.append(",container=");
name.append(container.getName());
}
return name.toString();
}
@Override
public String getDomainInternal() {
return MBeanUtils.getDomain(container);
}
// ----------------------------------------- PropertyChangeListener Methods
/**
* Process property change events from our associated Context.
*
* @param event
* The property change event that has occurred
*/
@Override
public void propertyChange(PropertyChangeEvent event) {
// Validate the source of this event
if (!(event.getSource() instanceof Context))
return;
// Process a relevant property change
if (event.getPropertyName().equals("sessionTimeout")) {
try {
setMaxInactiveInterval(
((Integer) event.getNewValue()).intValue() * 60);
} catch (NumberFormatException e) {
log.error(sm.getString("managerBase.sessionTimeout",
event.getNewValue()));
}
}
}
// ----------------------------------------------------------- Inner classes
protected static final class SessionTiming {
private long timestamp;
private int duration;
public SessionTiming(long timestamp, int duration) {
this.timestamp = timestamp;
this.duration = duration;
}
/**
* Time stamp associated with this piece of timing information in
* milliseconds.
*/
public long getTimestamp() {
return timestamp;
}
/**
* Duration associated with this piece of timing information in seconds.
*/
public int getDuration() {
return duration;
}
}
}