Glassfish example source code file (StandardSession.java)
This example Glassfish source code file (StandardSession.java) is included in the DevDaily.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2004 The Apache Software Foundation
*
* Licensed 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 com.sun.enterprise.spi.io.BaseIndirectlySerializable;
import org.apache.catalina.*;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.security.SecurityUtil;
import org.apache.catalina.util.Enumerator;
import org.apache.catalina.util.StringManager;
import javax.servlet.ServletContext;
import javax.servlet.http.*;
import java.io.*;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
//end HERCULES:add
/**
* Standard implementation of the <b>Session interface. This object is
* serializable, so that it can be stored in persistent storage or transferred
* to a different JVM for distributable session support.
* <p>
* <b>IMPLEMENTATION NOTE: An instance of this class represents both the
* internal (Session) and application level (HttpSession) view of the session.
* However, because the class itself is not declared public, Java logic outside
* of the <code>org.apache.catalina.session package cannot cast an
* HttpSession view of this instance back to a Session view.
* <p>
* <b>IMPLEMENTATION NOTE: If you add fields to this class, you must
* make sure that you carry them over in the read/writeObject methods so
* that this class is properly serialized.
*
* @author Craig R. McClanahan
* @author Sean Legassick
* @author <a href="mailto:jon@latchkey.com">Jon S. Stevens
* @version $Revision: 1.33 $ $Date: 2007/03/12 21:41:52 $
*/
public class StandardSession
implements HttpSession, Session, Serializable {
// ----------------------------------------------------------- Constructors
/**
* Construct a new Session associated with the specified Manager.
*
* @param manager The manager with which this Session is associated
*/
public StandardSession(Manager manager) {
super();
setManager(manager);
if (manager instanceof ManagerBase) {
this.debug = ((ManagerBase) manager).getDebug();
}
}
// ----------------------------------------------------- Class Variables
private static final java.util.logging.Logger log =
java.util.logging.Logger.getLogger(
StandardSession.class.getName());
/**
* Type array.
*/
protected static final String EMPTY_ARRAY[] = new String[0];
/**
* The dummy attribute value serialized when a NotSerializableException is
* encountered in <code>writeObject().
*/
protected static final String NOT_SERIALIZED =
"___NOT_SERIALIZABLE_EXCEPTION___";
//HERCULES:add
/**
* The string used in the name for setAttribute and removeAttribute
* to signify on-demand sync
*/
protected static final String SYNC_STRING = "com.sun.sync";
//end HERCULES:add
/**
* The method signature for the <code>fireContainerEvent method.
*/
protected static final Class<?> containerEventTypes[] =
{ String.class, Object.class };
/**
* Descriptive information describing this Session implementation.
*/
protected static final String info = "StandardSession/1.0";
/**
* Set of attribute names which are not allowed to be persisted.
*/
private static final String[] excludedAttributes = {
Globals.SUBJECT_ATTR
};
/**
* The string manager for this package.
*/
protected static final StringManager sm =
StringManager.getManager(Constants.Package);
/**
* The HTTP session context associated with this session.
*/
protected static volatile HttpSessionContext sessionContext = null;
/**
* Used for serialized format versioning.
* 1 = first version where this is being tracked.
*
* NOTE: You must increment this version whenever any changes are made
* to the serialized representation of this class between releases
*/
private static final Short SERIALIZED_FORM_VERSION = Short.valueOf("1");
// ----------------------------------------------------- Instance Variables
/**
* The session identifier of the parent SipApplicationSession, if any
*/
private String sipAppSessionId = null;
/**
* The BEKEY of this session, or <tt>null.
*
* <p>The BEKEY is used by the Converged Loadbalancer (CLB) in DCR mode
* for loadbalancing purposes, and supplied to the web container in the
* form of a request header.
*
* <p>See https://sailfin.dev.java.net/issues/show_bug.cgi?id=1647
* for additional details
*/
private String beKey;
/**
* The collection of user data attributes associated with this Session.
*/
protected Map<String, Object> attributes = new ConcurrentHashMap();
/**
* The authentication type used to authenticate our cached Principal,
* if any. NOTE: This value is not included in the serialized
* version of this object.
*/
protected transient String authType = null;
/**
* The <code>java.lang.Method for the
* <code>fireContainerEvent() method of the
* <code>org.apache.catalina.core.StandardContext method,
* if our Context implementation is of this class. This value is
* computed dynamically the first time it is needed, or after
* a session reload (since it is declared transient).
*/
protected transient Method containerEventMethod = null;
/**
* The time this session was created, in milliseconds since midnight,
* January 1, 1970 GMT.
*/
protected long creationTime = 0L;
/**
* The debugging detail level for this component. NOTE: This value
* is not included in the serialized version of this object.
*/
protected transient int debug = 0;
/**
* We are currently processing a session expiration, so bypass
* certain IllegalStateException tests. NOTE: This value is not
* included in the serialized version of this object.
*/
protected transient boolean expiring = false;
/**
* The facade associated with this session. NOTE: This value is not
* included in the serialized version of this object.
*/
protected transient StandardSessionFacade facade = null;
/**
* The session identifier of this Session.
*/
protected String id = null;
/**
* The last accessed time for this Session.
*/
protected long lastAccessedTime = creationTime;
/**
* The session event listeners for this Session.
*/
protected transient ArrayList<SessionListener> listeners =
new ArrayList<SessionListener>();
/**
* The Manager with which this Session is associated.
*/
protected transient Manager manager = null;
/**
* The context with which this Session is associated.
*/
protected transient StandardContext context = null;
/**
* The maximum time interval, in seconds, between client requests before
* the servlet container may invalidate this session. A negative time
* indicates that the session should never time out.
*/
protected int maxInactiveInterval = -1;
/**
* Flag indicating whether this session is new or not.
*/
protected boolean isNew = false;
/**
* Flag indicating whether this session is valid or not.
*/
protected boolean isValid = false;
/**
* Internal notes associated with this session by Catalina components
* and event listeners. <b>IMPLEMENTATION NOTE: This object is
* <em>not saved and restored across session serializations!
*/
protected transient Map<String, Object> notes = new Hashtable();
/**
* The authenticated Principal associated with this session, if any.
// START SJSWS 6371339
// * <b>IMPLEMENTATION NOTE: This object is not saved and
// * restored across session serializations!
// END SJSWS 6371339
*/
protected transient Principal principal = null;
/**
* The current accessed time for this session.
*/
protected long thisAccessedTime = creationTime;
/**
* The session version, incremented and used by in-memory-replicating
* session managers
*/
protected AtomicLong version = new AtomicLong(-1);
/**
* single sign on id. It is null if there is no SSO.
*/
protected String ssoId = null;
// ----------------------------------------------------- Session Properties
/**
* Return the authentication type used to authenticate our cached
* Principal, if any.
*/
public String getAuthType() {
return (this.authType);
}
/**
* Set the authentication type used to authenticate our cached
* Principal, if any.
*
* @param authType The new cached authentication type
*/
public void setAuthType(String authType) {
String oldAuthType = this.authType;
this.authType = authType;
}
/**
* Set the creation time for this session. This method is called by the
* Manager when an existing Session instance is reused.
*
* @param time The new creation time
*/
public void setCreationTime(long time) {
this.creationTime = time;
this.lastAccessedTime = time;
this.thisAccessedTime = time;
}
/**
* Return the session identifier for this session.
*/
public String getId() {
return getIdInternal();
}
/**
* Return the session identifier for this session.
*/
public String getIdInternal() {
return (this.id);
}
/**
* Set the session identifier for this session.
*
* @param id The new session identifier
*/
public void setId(String id) {
if ((this.id != null) && (manager != null))
manager.remove(this);
this.id = id;
if (manager != null)
manager.add(this);
tellNew();
}
/**
* Sets the BEKEY for this session
*
* <p>The BEKEY is used by the Converged Loadbalancer (CLB) in DCR mode
* for loadbalancing purposes, and supplied to the web container in the
* form of a request header.
*
* @param beKey the BEKEY for this session, or <tt>null if not
* present
*/
public void setBeKey(String beKey) {
this.beKey = beKey;
}
/**
* Gets the BEKEY of this session
*
* @return the BEKEY of this session, or <tt>null if not present
*/
public String getBeKey() {
return beKey;
}
/**
* Sets the id of the SipApplicationSession that is the parent of this
* StandardSession.
*
* @param id SipApplicationSession id
*/
public void setSipApplicationSessionId(String id) {
sipAppSessionId = id;
}
/**
* Gets the id of the SipApplicationSession that is the parent of this
* StandardSession.
*
* @return The SipApplicationSession id, or null if this
* StandardSession does not have any SipApplicationSession parent
*/
public String getSipApplicationSessionId() {
return sipAppSessionId;
}
/**
* Inform the listeners about the new session.
*
*/
public void tellNew() {
// Notify interested session event listeners
fireSessionEvent(Session.SESSION_CREATED_EVENT, null);
HttpSessionEvent event = new HttpSessionEvent(getSession());
// Notify interested application event listeners
for (HttpSessionListener listener : context.getSessionListeners()) {
try {
fireContainerEvent(context, "beforeSessionCreated",
listener);
listener.sessionCreated(event);
fireContainerEvent(context, "afterSessionCreated",
listener);
} catch (Throwable t) {
try {
fireContainerEvent(context, "afterSessionCreated",
listener);
} catch (Exception e) {
// Ignore
}
log(sm.getString("standardSession.sessionEvent"), t);
}
}
}
/**
* Return descriptive information about this Session implementation and
* the corresponding version number, in the format
* <code><description>/<version>.
*/
public String getInfo() {
return (this.info);
}
/**
* Return the last time the client sent a request associated with this
* session, as the number of milliseconds since midnight, January 1, 1970
* GMT. Actions that your application takes, such as getting or setting
* a value associated with the session, do not affect the access time.
*/
public long getLastAccessedTime() {
if ( !isValid() ) {
throw new IllegalStateException
(sm.getString("standardSession.getLastAccessedTime.ise"));
}
return (this.lastAccessedTime);
}
// START SJSAS 6470831
/**
* Same as getLastAccessedTime(), except that there is no call to
* isValid(), which may expire the session and cause any subsequent
* session access to throw an IllegalStateException.
*/
public long getLastAccessedTimeInternal() {
return this.lastAccessedTime;
}
// END SJSAS 6470831
/**
* Set the last time the client sent a request associated with this
* session, as the number of milliseconds since midnight, January 1, 1970
* GMT. Actions that your application takes, such as getting or setting
* a value associated with the session, do not affect the access time.
* HERCULES: added method
*/
public void setLastAccessedTime(long lastAcessedTime) {
this.lastAccessedTime = lastAcessedTime;
}
/**
* Return the Manager within which this Session is valid.
*/
public Manager getManager() {
return (this.manager);
}
/**
* Set the Manager within which this Session is valid.
*
* @param manager The new Manager
*/
public void setManager(Manager manager) {
this.manager = manager;
context = (StandardContext) manager.getContainer();
}
/**
* Return the maximum time interval, in seconds, between client requests
* before the servlet container will invalidate the session. A negative
* time indicates that the session should never time out.
*/
public int getMaxInactiveInterval() {
return (this.maxInactiveInterval);
}
/**
* Set the maximum time interval, in seconds, between client requests
* before the servlet container will invalidate the session. A negative
* time indicates that the session should never time out.
*
* @param interval The new maximum interval
*/
public void setMaxInactiveInterval(int interval) {
this.maxInactiveInterval = interval;
if (isValid && interval == 0) {
expire();
}
}
/**
* Set the <code>isNew flag for this session.
*
* @param isNew The new value for the <code>isNew flag
*/
public void setNew(boolean isNew) {
this.isNew = isNew;
}
/**
* Return the authenticated Principal that is associated with this Session.
* This provides an <code>Authenticator with a means to cache a
* previously authenticated Principal, and avoid potentially expensive
* <code>Realm.authenticate() calls on every request. If there
* is no current associated Principal, return <code>null.
*/
public Principal getPrincipal() {
return (this.principal);
}
/**
* Set the authenticated Principal that is associated with this Session.
* This provides an <code>Authenticator with a means to cache a
* previously authenticated Principal, and avoid potentially expensive
* <code>Realm.authenticate() calls on every request.
*
* @param principal The new Principal, or <code>null if none
*/
public void setPrincipal(Principal principal) {
Principal oldPrincipal = this.principal;
this.principal = principal;
}
/**
* Return the <code>HttpSession for which this object
* is the facade.
*/
public HttpSession getSession() {
if (facade == null){
if (SecurityUtil.isPackageProtectionEnabled()){
final StandardSession fsession = this;
facade = AccessController.doPrivileged(
new PrivilegedAction<StandardSessionFacade>(){
public StandardSessionFacade run(){
return new StandardSessionFacade(fsession);
}
});
} else {
facade = new StandardSessionFacade(this);
}
}
return (facade);
}
/**
* Return the <code>isValid flag for this session.
*/
public boolean isValid() {
if (this.expiring){
return true;
}
if (!this.isValid ) {
return false;
}
if (isForegroundLocked()) {
return true;
}
/* SJSAS 6329289
if (maxInactiveInterval >= 0) {
long timeNow = System.currentTimeMillis();
int timeIdle = (int) ((timeNow - thisAccessedTime) / 1000L);
if (timeIdle >= maxInactiveInterval) {
expire(true);
}
}
*/
// START SJSAS 6329289
if (hasExpired()) {
expire(true);
}
// END SJSAS 6329289
return (this.isValid);
}
// START CR 6363689
public boolean getIsValid() {
return this.isValid;
}
// END CR 6363689
/**
* Set the <code>isValid flag for this session.
*
* @param isValid The new value for the <code>isValid flag
*/
public void setValid(boolean isValid) {
this.isValid = isValid;
//SJSAS 6406580 START
if (!isValid && (getManager() instanceof PersistentManagerBase)) {
((PersistentManagerBase) getManager()).addToInvalidatedSessions(this.id);
}
//SJSAS 6406580 END
}
// ------------------------------------------------- Session Public Methods
/**
* Update the accessed time information for this session. This method
* should be called by the context when a request comes in for a particular
* session, even if the application does not reference it.
*/
public void access() {
this.lastAccessedTime = this.thisAccessedTime;
this.thisAccessedTime = System.currentTimeMillis();
evaluateIfValid();
}
/**
* End the access.
*/
public void endAccess() {
isNew = false;
}
/**
* Add a session event listener to this component.
*/
public void addSessionListener(SessionListener listener) {
synchronized (listeners) {
listeners.add(listener);
}
}
/**
* Perform the internal processing required to invalidate this session,
* without triggering an exception if the session has already expired.
*/
public void expire() {
expire(true);
}
/**
* Perform the internal processing required to invalidate this session,
* without triggering an exception if the session has already expired.
*
* @param notify Should we notify listeners about the demise of
* this session?
*/
public void expire(boolean notify) {
expire(notify, true);
}
/**
* Perform the internal processing required to invalidate this session,
* without triggering an exception if the session has already expired.
*
* @param notify Should we notify listeners about the demise of
* this session?
* @param persistentRemove should we call store to remove the session
* if available
*/
public void expire(boolean notify, boolean persistentRemove) {
// Mark this session as "being expired" if needed
if (expiring)
return;
synchronized (this) {
if (manager == null)
return;
expiring = true;
// Notify interested application event listeners
// FIXME - Assumes we call listeners in reverse order
List<HttpSessionListener> listeners = context.getSessionListeners();
if (notify && !listeners.isEmpty()) {
HttpSessionEvent event = new HttpSessionEvent(getSession());
int len = listeners.size();
for (int i = 0; i < len; i++) {
// Invoke in reverse order of declaration
HttpSessionListener listener = listeners.get((len - 1) - i);
try {
fireContainerEvent(context,
"beforeSessionDestroyed",
listener);
listener.sessionDestroyed(event);
fireContainerEvent(context,
"afterSessionDestroyed",
listener);
} catch (Throwable t) {
try {
fireContainerEvent(context,
"afterSessionDestroyed",
listener);
} catch (Exception e) {
// Ignore
}
// FIXME - should we do anything besides log these?
log(sm.getString("standardSession.sessionEvent"), t);
}
}
}
setValid(false);
/*
* Compute how long this session has been alive, and update
* session manager's related properties accordingly
*/
long timeNow = System.currentTimeMillis();
int timeAlive = (int) ((timeNow - creationTime)/1000);
synchronized (manager) {
if (timeAlive > manager.getSessionMaxAliveTimeSeconds()) {
manager.setSessionMaxAliveTimeSeconds(timeAlive);
}
int numExpired = manager.getExpiredSessions();
numExpired++;
manager.setExpiredSessions(numExpired);
int average = manager.getSessionAverageAliveTimeSeconds();
average = ((average * (numExpired-1)) + timeAlive)/numExpired;
manager.setSessionAverageAliveTimeSeconds(average);
}
// Remove this session from our manager's active sessions
if(persistentRemove) {
manager.remove(this);
} else {
if(manager instanceof PersistentManagerBase) {
((PersistentManagerBase)manager).remove(this, false);
}
}
/*
* Mark session as expired *before* removing its attributes, so
* that its HttpSessionBindingListener objects will get an
* IllegalStateException when accessing the session attributes
* from within their valueUnbound() method
*/
expiring = false;
// Unbind any objects associated with this session
String keys[] = keys();
for (int i = 0; i < keys.length; i++)
removeAttribute(keys[i], notify, false);
// Notify interested session event listeners
if (notify) {
context.sessionExpiredEvent(this);
fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null);
}
}
}
/**
* Perform the internal processing required to passivate
* this session.
*/
public void passivate() {
context.sessionPassivatedStartEvent(this);
try {
// Notify ActivationListeners
HttpSessionEvent event = null;
String keys[] = keys();
for (int i = 0; i < keys.length; i++) {
Object attribute = getAttributeInternal(keys[i]);
if (attribute instanceof HttpSessionActivationListener) {
if (event == null)
event = new HttpSessionEvent(getSession());
// FIXME: Should we catch throwables?
((HttpSessionActivationListener)attribute).sessionWillPassivate(event);
}
}
} finally {
context.sessionPassivatedEndEvent(this);
}
}
/**
* Perform internal processing required to activate this
* session.
*/
public void activate() {
context.sessionActivatedStartEvent(this);
try {
// Notify ActivationListeners
HttpSessionEvent event = null;
String keys[] = keys();
for (int i = 0; i < keys.length; i++) {
Object attribute = getAttributeInternal(keys[i]);
if (attribute instanceof HttpSessionActivationListener) {
if (event == null)
event = new HttpSessionEvent(getSession());
// FIXME: Should we catch throwables?
((HttpSessionActivationListener)attribute).sessionDidActivate(event);
}
}
} finally {
context.sessionActivatedEndEvent(this);
}
}
/**
* Return the object bound with the specified name to the internal notes
* for this session, or <code>null if no such binding exists.
*
* @param name Name of the note to be returned
*/
public Object getNote(String name) {
return (notes.get(name));
}
/**
* Return an Iterator containing the String names of all notes bindings
* that exist for this session.
*/
public Iterator<String> getNoteNames() {
return (notes.keySet().iterator());
}
/**
* Release all object references, and initialize instance variables, in
* preparation for reuse of this object.
*/
public void recycle() {
// Reset the instance variables associated with this Session
attributes.clear();
setAuthType(null);
creationTime = 0L;
expiring = false;
id = null;
lastAccessedTime = 0L;
maxInactiveInterval = -1;
notes.clear();
setPrincipal(null);
isNew = false;
isValid = false;
listeners.clear();
manager = null;
}
/**
* Remove any object bound to the specified name in the internal notes
* for this session.
*
* @param name Name of the note to be removed
*/
public void removeNote(String name) {
notes.remove(name);
}
/**
* Remove a session event listener from this component.
*/
public void removeSessionListener(SessionListener listener) {
synchronized (listeners) {
listeners.remove(listener);
}
}
/**
* Bind an object to a specified name in the internal notes associated
* with this session, replacing any existing binding for this name.
*
* @param name Name to which the object should be bound
* @param value Object to be bound to the specified name
*/
public void setNote(String name, Object value) {
notes.put(name, value);
}
// START SJSAS 6329289
/**
* Checks whether this Session has expired.
*
* @return true if this Session has expired, false otherwise
*/
public boolean hasExpired() {
if (maxInactiveInterval >= 0
&& (System.currentTimeMillis() - thisAccessedTime >=
maxInactiveInterval * 1000)) {
return true;
} else {
return false;
}
}
// END SJSAS 6329289
/**
* Increments the version number
*/
public long incrementVersion() {
return version.incrementAndGet();
}
/**
* Gets the version number
*/
public long getVersion() {
return version.get();
}
/**
* Sets the version number
*/
public void setVersion(long value) {
version.set(value);
}
/**
* Return the single sign on id.
* It is null if there is no SSO.
*/
public String getSsoId() {
return ssoId;
}
/**
* Set the single sign on id.
*/
public void setSsoId(String ssoId) {
this.ssoId = ssoId;
}
/**
* Return a string representation of this object.
*/
public String toString() {
// STARTS S1AS
/*
StringBuilder sb = new StringBuilder();
sb.append("StandardSession[");
sb.append(id);
sb.append("]");
return (sb.toString());
*/
// END S1AS
// START S1AS
StringBuilder sb = null;
if(!this.isValid) {
sb = new StringBuilder();
} else {
sb = new StringBuilder(1000);
}
sb.append("StandardSession[");
sb.append(id);
sb.append("]");
if (this.isValid) {
Enumeration<String> attrNamesEnum = getAttributeNames();
while(attrNamesEnum.hasMoreElements()) {
String nextAttrName = attrNamesEnum.nextElement();
Object nextAttrValue = getAttribute(nextAttrName);
sb.append("\n");
sb.append("attrName = " + nextAttrName);
sb.append(" : attrValue = " + nextAttrValue);
}
}
return sb.toString();
// END S1AS
}
// ------------------------------------------------ Session Package Methods
/**
* Creates a StandardSession instance from the given ObjectInputStream,
* and returns it.
*
* If ObjectInputStream does not contain a serialized StandardSession
* (or one of its subclasses), this method will create an empty session
* and populate it with the serialized data (this is for backwards
* compatibility).
*
* @param ois The ObjectInputStream from which to read the serialized
* session data
* @param manager The session manager from which to create an empty
* session if needed
*
* @return The restored session
*
* @exception ClassNotFoundException If the class for an object being
* restored cannot be found.
* @exception IOException if I/O errors occur
*/
static StandardSession deserialize(ObjectInputStream ois,
Manager manager)
throws ClassNotFoundException, IOException {
StandardSession result = null;
Object obj = ois.readObject();
if (obj instanceof StandardSession) {
// New format following standard serialization
result = (StandardSession) obj;
} else {
// Old format, obj is an instance of Long and contains the
// session's creation time
result = (StandardSession) manager.createEmptySession();
result.setCreationTime(((Long) obj).longValue());
result.readRemainingObject(ois);
}
return result;
}
// ------------------------------------------------- HttpSession Properties
/**
* Return the time when this session was created, in milliseconds since
* midnight, January 1, 1970 GMT.
*
* @exception IllegalStateException if this method is called on an
* invalidated session
*/
public long getCreationTime() {
if (!isValid())
throw new IllegalStateException
(sm.getString("standardSession.getCreationTime.ise"));
return (this.creationTime);
}
/**
* Return the ServletContext to which this session belongs.
*/
public ServletContext getServletContext() {
if (manager == null)
return (null);
if (context == null)
return (null);
else
return (context.getServletContext());
}
/**
* Return the session context with which this session is associated.
*
* @deprecated As of Version 2.1, this method is deprecated and has no
* replacement. It will be removed in a future version of the
* Java Servlet API.
*/
public HttpSessionContext getSessionContext() {
if (sessionContext == null)
sessionContext = new StandardSessionContext();
return (sessionContext);
}
// ----------------------------------------------HttpSession Public Methods
/**
* Return the object bound with the specified name in this session, or
* <code>null if no object is bound with that name.
*
* @param name Name of the attribute to be returned
*
* @exception IllegalStateException if this method is called on an
* invalidated session
*/
public Object getAttribute(String name) {
if (!isValid())
throw new IllegalStateException
(sm.getString("standardSession.getAttribute.ise"));
if (name == null) return null;
return (attributes.get(name));
}
@Override
public Map<String, Object> getAttributes() {
return attributes;
}
/**
* Return an <code>Enumeration of String objects
* containing the names of the objects bound to this session.
*
* @exception IllegalStateException if this method is called on an
* invalidated session
*/
public Enumeration<String> getAttributeNames() {
if (!isValid())
throw new IllegalStateException
(sm.getString("standardSession.getAttributeNames.ise"));
synchronized (attributes) {
return (new Enumerator<String>(attributes.keySet(), true));
}
}
/**
* Return the object bound with the specified name in this session, or
* <code>null if no object is bound with that name.
*
* @param name Name of the value to be returned
*
* @exception IllegalStateException if this method is called on an
* invalidated session
*
* @deprecated As of Version 2.2, this method is replaced by
* <code>getAttribute()
*/
public Object getValue(String name) {
return (getAttribute(name));
}
/**
* Return the set of names of objects bound to this session. If there
* are no such objects, a zero-length array is returned.
*
* @exception IllegalStateException if this method is called on an
* invalidated session
*
* @deprecated As of Version 2.2, this method is replaced by
* <code>getAttributeNames()
*/
public String[] getValueNames() {
if (!isValid())
throw new IllegalStateException
(sm.getString("standardSession.getValueNames.ise"));
return (keys());
}
// ------------------------session locking --HERCULES:add-------------------
/**
* get this session locked for foreground
* if the session is found to be presently background
* locked; retry logic in a time-decay polling loop
* waits for background lock to clear
* after 6 attempts (12.6 seconds) it unlocks the
* session and acquires the foreground lock
*/
protected boolean getSessionLockForForeground() {
boolean result = false;
StandardSession sess = (StandardSession) this;
//now lock the session
//System.out.println("IN LOCK_SESSION_FOR_FOREGROUND: sess =" + sess);
long pollTime = 200L;
int tryNumber = 0;
int numTries = 7;
boolean keepTrying = true;
boolean lockResult = false;
//System.out.println("locking session: sess =" + sess);
//try to lock up to numTries (i.e. 7) times
//poll and wait starting with 200 ms
while(keepTrying) {
lockResult = sess.lockForeground();
if(lockResult) {
keepTrying = false;
result = true;
break;
}
tryNumber++;
if(tryNumber < (numTries - 1) ) {
pollTime = pollTime * 2L;
} else {
//unlock the background so we can take over
//FIXME: need to log warning for this situation
sess.unlockBackground();
}
}
//System.out.println("finished locking session: sess =" + sess);
//System.out.println("LOCK = " + sess.getSessionLock());
return result;
}
/**
* return whether this session is currently foreground locked
*/
public boolean isForegroundLocked() {
//in this case we are not using locks
//so just return false
if(_sessionLock == null)
return false;
synchronized(this) {
return _sessionLock.isForegroundLocked();
}
}
/**
* lock the session for foreground
* returns true if successful; false if unsuccessful
*/
public boolean lockBackground() {
//in this case we are not using locks
//so just return true
if(_sessionLock == null)
return true;
synchronized(this) {
return _sessionLock.lockBackground();
}
}
/**
* lock the session for background
* returns true if successful; false if unsuccessful
*/
public boolean lockForeground() {
//in this case we are not using locks
//so just return true
if(_sessionLock == null)
return true;
synchronized(this) {
return _sessionLock.lockForeground();
}
}
/**
* unlock the session completely
* irregardless of whether it was foreground or background locked
*/
public void unlockForegroundCompletely() {
//in this case we are not using locks
//so just return true
if(_sessionLock == null)
return;
synchronized(this) {
_sessionLock.unlockForegroundCompletely();
}
}
/**
* unlock the session from foreground
*/
public void unlockForeground() {
//in this case we are not using locks
//so just return true
if(_sessionLock == null)
return;
synchronized(this) {
_sessionLock.unlockForeground();
}
}
/**
* unlock the session from background
*/
public void unlockBackground() {
//in this case we are not using locks
//so just return true
if(_sessionLock == null)
return;
synchronized(this) {
_sessionLock.unlockBackground();
}
}
/**
* return the Session lock
*/
public SessionLock getSessionLock() {
return _sessionLock;
}
/**
* set the Session lock
* @param sessionLock
*/
public void setSessionLock(SessionLock sessionLock) {
_sessionLock = sessionLock;
}
/**
* @return true if this session has been locked by any
* out-of-band (i.e., non-http) request, false otherwise
*/
public synchronized boolean hasNonHttpLockOccurred() {
//in this case we are not using locks
//so just return false
if(_sessionLock == null)
return false;
return _sessionLock.hasNonHttpLockOccurred();
}
protected transient SessionLock _sessionLock = new SessionLock();
// ------------------------end session locking ---HERCULES:add--------
/**
* Invalidates this session and unbinds any objects bound to it.
*
* @exception IllegalStateException if this method is called on
* an invalidated session
* HERCULES:modified method
*/
public void invalidate() {
if (!isValid)
throw new IllegalStateException
(sm.getString("standardSession.invalidate.ise"));
//make sure foreground locked first
if(!this.isForegroundLocked()) {
this.getSessionLockForForeground();
}
// Cause this session to expire
try {
expire();
} finally {
this.unlockForeground();
}
}
/**
* Return <code>true if the client does not yet know about the
* session, or if the client chooses not to join the session. For
* example, if the server used only cookie-based sessions, and the client
* has disabled the use of cookies, then a session would be new on each
* request.
*
* @exception IllegalStateException if this method is called on an
* invalidated session
*/
public boolean isNew() {
if (!isValid())
throw new IllegalStateException
(sm.getString("standardSession.isNew.ise"));
return (this.isNew);
}
/**
* Bind an object to this session, using the specified name. If an object
* of the same name is already bound to this session, the object is
* replaced.
* <p>
* After this method executes, and if the object implements
* <code>HttpSessionBindingListener, the container calls
* <code>valueBound() on the object.
*
* @param name Name to which the object is bound, cannot be null
* @param value Object to be bound, cannot be null
*
* @exception IllegalStateException if this method is called on an
* invalidated session
*
* @deprecated As of Version 2.2, this method is replaced by
* <code>setAttribute()
*/
public void putValue(String name, Object value) {
setAttribute(name, value);
}
/**
* Remove the object bound with the specified name from this session. If
* the session does not have an object bound with this name, this method
* does nothing.
* <p>
* After this method executes, and if the object implements
* <code>HttpSessionBindingListener, the container calls
* <code>valueUnbound() on the object.
*
* @param name Name of the object to remove from this session.
*
* @exception IllegalStateException if this method is called on an
* invalidated session
*/
public void removeAttribute(String name) {
removeAttribute(name, true, true);
}
/**
* Remove the object bound with the specified name from this session. If
* the session does not have an object bound with this name, this method
* does nothing.
* <p>
* After this method executes, and if the object implements
* <code>HttpSessionBindingListener, the container calls
* <code>valueUnbound() on the object.
*
* @param name Name of the object to remove from this session.
* @param notify Should we notify interested listeners that this
* attribute is being removed?
* @param checkValid Indicates whether IllegalStateException must be
* thrown if session has already been invalidated
*
* @exception IllegalStateException if this method is called on an
* invalidated session
*/
public void removeAttribute(String name, boolean notify,
boolean checkValid) {
if (name == null) return;
// Validate our current state
if (!isValid() && checkValid)
throw new IllegalStateException
(sm.getString("standardSession.removeAttribute.ise"));
// Remove this attribute from our collection
Object value = attributes.remove(name);
// Do we need to do valueUnbound() and attributeRemoved() notification?
if (!notify || (value == null)) {
return;
}
// Call the valueUnbound() method if necessary
HttpSessionBindingEvent event = null;
if (value instanceof HttpSessionBindingListener) {
event = new HttpSessionBindingEvent(getSession(), name, value);
try {
context.fireContainerEvent(
ContainerEvent.BEFORE_SESSION_VALUE_UNBOUND, null);
((HttpSessionBindingListener) value).valueUnbound(event);
context.fireContainerEvent(
ContainerEvent.AFTER_SESSION_VALUE_UNBOUND, null);
} catch (Throwable t) {
// Log exception
context.fireContainerEvent(
ContainerEvent.AFTER_SESSION_VALUE_UNBOUND, null);
}
}
// Notify special event listeners on removeAttribute
//HERCULES:add
// fire container event
context.fireContainerEvent("sessionRemoveAttributeCalled", event);
// fire sync container event if name equals SYNC_STRING
if (SYNC_STRING.equals(name)) {
context.fireContainerEvent("sessionSync", (new HttpSessionBindingEvent(getSession(), name)));
}
//END HERCULES:add
// Notify interested application event listeners
List<EventListener> listeners = context.getApplicationEventListeners();
if (listeners.isEmpty()) {
return;
}
Iterator<EventListener> iter = listeners.iterator();
while (iter.hasNext()) {
EventListener eventListener = iter.next();
if (!(eventListener instanceof HttpSessionAttributeListener)) {
continue;
}
HttpSessionAttributeListener listener =
(HttpSessionAttributeListener) eventListener;
try {
fireContainerEvent(context,
"beforeSessionAttributeRemoved",
listener);
if (event == null) {
event = new HttpSessionBindingEvent(getSession(), name, value);
}
listener.attributeRemoved(event);
fireContainerEvent(context,
"afterSessionAttributeRemoved",
listener);
} catch (Throwable t) {
try {
fireContainerEvent(context,
"afterSessionAttributeRemoved",
listener);
} catch (Exception e) {
// Ignore
}
log(sm.getString("standardSession.attributeEvent"), t);
}
}
}
/**
* Remove the object bound with the specified name from this session. If
* the session does not have an object bound with this name, this method
* does nothing.
* <p>
* After this method executes, and if the object implements
* <code>HttpSessionBindingListener, the container calls
* <code>valueUnbound() on the object.
*
* @param name Name of the object to remove from this session.
*
* @exception IllegalStateException if this method is called on an
* invalidated session
*
* @deprecated As of Version 2.2, this method is replaced by
* <code>removeAttribute()
*/
public void removeValue(String name) {
removeAttribute(name);
}
/**
* Bind an object to this session, using the specified name. If an object
* of the same name is already bound to this session, the object is
* replaced.
* <p>
* After this method executes, and if the object implements
* <code>HttpSessionBindingListener, the container calls
* <code>valueBound() on the object.
*
* @param name Name to which the object is bound, cannot be null
* @param value Object to be bound, cannot be null
*
* @exception IllegalArgumentException if an attempt is made to add a
* non-serializable object in an environment marked distributable.
* @exception IllegalStateException if this method is called on an
* invalidated session
*/
public void setAttribute(String name, Object value) {
// Name cannot be null
if (name == null)
throw new IllegalArgumentException
(sm.getString("standardSession.setAttribute.namenull"));
// Null value is the same as removeAttribute()
if (value == null) {
removeAttribute(name);
return;
}
// Validate our current state
if (!isValid()) {
throw new IllegalStateException
(sm.getString("standardSession.setAttribute.ise"));
}
if (manager != null) {
manager.checkSessionAttribute(name, value);
}
// Construct an event with the new value
HttpSessionBindingEvent event = null;
// Call the valueBound() method if necessary
if (value instanceof HttpSessionBindingListener) {
event = new HttpSessionBindingEvent(getSession(), name, value);
try {
((HttpSessionBindingListener) value).valueBound(event);
} catch (Throwable t){
log(sm.getString("standardSession.bindingEvent"), t);
}
}
// Replace or add this attribute
Object unbound = attributes.put(name, value);
// Call the valueUnbound() method if necessary
if ((unbound != null) &&
(unbound instanceof HttpSessionBindingListener)) {
try {
context.fireContainerEvent(
ContainerEvent.BEFORE_SESSION_VALUE_UNBOUND, null);
((HttpSessionBindingListener) unbound).valueUnbound
(new HttpSessionBindingEvent(getSession(), name));
context.fireContainerEvent(
ContainerEvent.AFTER_SESSION_VALUE_UNBOUND, null);
} catch (Throwable t) {
context.fireContainerEvent(
ContainerEvent.AFTER_SESSION_VALUE_UNBOUND, null);
log(sm.getString("standardSession.bindingEvent"), t);
}
}
//HERCULES:add
// fire sync container event if name equals SYNC_STRING
if (SYNC_STRING.equals(name)) {
context.fireContainerEvent("sessionSync",
new HttpSessionBindingEvent(getSession(), name));
}
//end HERCULES:add
// Notify interested application event listeners
List<EventListener> listeners = context.getApplicationEventListeners();
if (listeners.isEmpty()) {
return;
}
Iterator<EventListener> iter = listeners.iterator();
while (iter.hasNext()) {
EventListener eventListener = iter.next();
if (!(eventListener instanceof HttpSessionAttributeListener)) {
continue;
}
HttpSessionAttributeListener listener =
(HttpSessionAttributeListener) eventListener;
try {
if (unbound != null) {
fireContainerEvent(context,
"beforeSessionAttributeReplaced",
listener);
if (event == null) {
event = new HttpSessionBindingEvent
(getSession(), name, unbound);
}
listener.attributeReplaced(event);
fireContainerEvent(context,
"afterSessionAttributeReplaced",
listener);
} else {
fireContainerEvent(context,
"beforeSessionAttributeAdded",
listener);
if (event == null) {
event = new HttpSessionBindingEvent(
getSession(), name, value);
}
listener.attributeAdded(event);
fireContainerEvent(context,
"afterSessionAttributeAdded",
listener);
}
} catch (Throwable t) {
try {
if (unbound != null) {
fireContainerEvent(context,
"afterSessionAttributeReplaced",
listener);
} else {
fireContainerEvent(context,
"afterSessionAttributeAdded",
listener);
}
} catch (Exception e) {
// Ignore
}
log(sm.getString("standardSession.attributeEvent"), t);
}
}
}
// ------------------------------------------ HttpSession Protected Methods
/**
* Read a serialized version of this session object from the specified
* object input stream.
* <p>
* <b>IMPLEMENTATION NOTE: The reference to the owning Manager
* is not restored by this method, and must be set explicitly.
*
* @param stream The input stream to read from
*
* @exception ClassNotFoundException if an unknown class is specified
* @exception IOException if an input/output error occurs
*/
private void readObject(ObjectInputStream stream)
throws ClassNotFoundException, IOException {
if (listeners == null) {
listeners = new ArrayList<SessionListener>();
}
if (notes == null) {
notes = new Hashtable<String, Object>();
}
// Deserialize the scalar instance variables (except Manager)
authType = null; // Transient only
/*
* The stream starts with a Long, which indicates the session's
* creation time. This Long may optionally be preceded by a Short,
* which indicates the session's serializedFormVersion.
*/
Object obj = stream.readObject();
short readSerializedFormVersion = 0;
if (obj instanceof Short) {
readSerializedFormVersion = ((Short) obj).shortValue();
creationTime = ((Long) stream.readObject()).longValue();
} else {
creationTime = ((Long) obj).longValue();
}
readRemainingObject(stream);
/*
* Any additional fields that are to be included in the serialized
* representation of this class MUST be written to the end of the
* stream (in writeObject), and must be read back in HERE, i.e.,
* AFTER readRemainingObject (which is shared by the code that reads
* in sessions that were serialized using an earlier, proprietary
* format) has returned.
*/
sipAppSessionId = (String) stream.readObject();
switch (readSerializedFormVersion) {
case 0:
// Do nothing
break;
case 1:
beKey = (String) stream.readObject();
break;
default:
throw new IOException("Unable to deserialize into "
+ getClass().getName()
+ " due to unknown serializedFormVersion of "
+ readSerializedFormVersion);
}
}
/**
* Reads the serialized session data from the given ObjectInputStream,
* with the assumption that the session's creation time, which appears
* first in the serialized data, has already been consumed.
*
* @param stream The ObjectInputStream from which to read the serialized
* session data
*
* @exception ClassNotFoundException If the class for an object being
* restored cannot be found.
* @exception IOException if I/O errors occur
*/
private void readRemainingObject(ObjectInputStream stream)
throws ClassNotFoundException, IOException {
version = new AtomicLong();
lastAccessedTime = ((Long) stream.readObject()).longValue();
maxInactiveInterval = ((Integer) stream.readObject()).intValue();
isNew = ((Boolean) stream.readObject()).booleanValue();
isValid = ((Boolean) stream.readObject()).booleanValue();
thisAccessedTime = ((Long) stream.readObject()).longValue();
/* SJSWS 6371339
principal = null; // Transient only
// setId((String) stream.readObject());
id = (String) stream.readObject();
*/
// START SJSWS 6371339
// Read the next object, if it is of type Principal, then
// store it in the principal variable
Object obj = stream.readObject();
if (obj instanceof Principal) {
principal = (Principal)obj;
id = (String) stream.readObject();
}
else {
principal = null;
id = (String) obj;
}
// END SJSWS 6371339
if (debug >= 2)
log("readObject() loading session " + id);
// START PWC 6444754
obj = stream.readObject();
int n = 0;
if (obj instanceof String) {
authType = (String) obj;
n = ((Integer) stream.readObject()).intValue();
} else {
n = ((Integer) obj).intValue();
}
// END PWC 6444754
// Deserialize the attribute count and attribute values
if (attributes == null)
attributes = new ConcurrentHashMap<String, Object>();
/* PWC 6444754
int n = ((Integer) stream.readObject()).intValue();
*/
boolean isValidSave = isValid;
isValid = true;
for (int i = 0; i < n; i++) {
String name = (String) stream.readObject();
Object value = stream.readObject();
if ((value instanceof String) && (value.equals(NOT_SERIALIZED)))
continue;
if (debug >= 2)
log(" loading attribute '" + name +
"' with value '" + value + "'");
attributes.put(name, value);
}
isValid = isValidSave;
}
/**
* Write a serialized version of this session object to the specified
* object output stream.
* <p>
* <b>IMPLEMENTATION NOTE: The owning Manager will not be stored
* in the serialized representation of this Session. After calling
* <code>readObject(), you must set the associated Manager
* explicitly.
* <p>
* <b>IMPLEMENTATION NOTE: Any attribute that is not Serializable
* will be unbound from the session, with appropriate actions if it
* implements HttpSessionBindingListener. If you do not want any such
* attributes, be sure the <code>distributable property of the
* associated Manager is set to <code>true.
*
* @param stream The output stream to write to
*
* @exception IOException if an input/output error occurs
*/
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.writeObject(SERIALIZED_FORM_VERSION);
stream.writeObject(Long.valueOf(creationTime));
stream.writeObject(Long.valueOf(lastAccessedTime));
stream.writeObject(Integer.valueOf(maxInactiveInterval));
stream.writeObject(Boolean.valueOf(isNew));
stream.writeObject(Boolean.valueOf(isValid));
stream.writeObject(Long.valueOf(thisAccessedTime));
// START SJSWS 6371339
// If the principal is serializable, write it out
// START PWC 6444754
boolean serialPrincipal = false;
// END PWC 6444754
if (principal instanceof java.io.Serializable) {
// START PWC 6444754
serialPrincipal = true;
// END PWC 6444754
stream.writeObject(principal);
}
// END SJSWS 6371339
stream.writeObject(id);
if (debug >= 2)
log("writeObject() storing session " + id);
// START PWC 6444754
if (serialPrincipal && authType != null) {
stream.writeObject(authType);
}
// END PWC 6444754
// Accumulate the names of serializable and non-serializable attributes
String keys[] = keys();
ArrayList<String> saveNames = new ArrayList();
ArrayList<Object> saveValues = new ArrayList