/* * 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.ha.session;
/** * Similar to the StandardSession except that this session will keep track of deltas during a request.
*/ publicclass DeltaSession extends StandardSession implements Externalizable, ClusterSession, ReplicatedMapEntry {
/** * only the primary session will expire, or be able to expire due to inactivity. This is set to false as soon as I * receive this session over the wire in a session message. That means that someone else has made a request on * another server.
*/ privatetransientboolean isPrimarySession = true;
/** * The delta request contains all the action info
*/ privatetransient DeltaRequest deltaRequest = null;
/** * Last time the session was replicated, used for distributed expiring of session
*/ privatetransientlong lastTimeReplicated = System.currentTimeMillis();
protectedfinal Lock diffLock = new ReentrantReadWriteLock().writeLock();
/** * Construct a new Session associated with the specified Manager. * * @param manager The manager with which this Session is associated
*/ public DeltaSession(Manager manager) { super(manager); boolean recordAllActions =
manager instanceof ClusterManagerBase && ((ClusterManagerBase) manager).isRecordAllActions();
deltaRequest = createRequest(getIdInternal(), recordAllActions);
}
/* * DeltaRequest instances are created via this protected method to enable sub-classes to over-ride the method to use * custom DeltaRequest implementations.
*/ protected DeltaRequest createRequest(String sessionId, boolean recordAllActions) { returnnew DeltaRequest(sessionId, recordAllActions);
}
/** * Has the object changed since last replication and is not in a locked state * * @return boolean
*/
@Override publicboolean isDirty() { return deltaRequest.getSize() > 0;
}
/** * If this returns true, the map will extract the diff using getDiff() Otherwise it will serialize the entire * object. * * @return boolean
*/
@Override publicboolean isDiffable() { returntrue;
}
/** * Returns a diff and sets the dirty map to false * * @return a serialized view of the difference * * @throws IOException IO error serializing
*/
@Override publicbyte[] getDiff() throws IOException {
SynchronizedStack<DeltaRequest> deltaRequestPool = null;
DeltaRequest newDeltaRequest = null;
if (deltaRequestPool != null) { // Only need to reset the old request if it is going to be pooled. // Otherwise let GC do its thing.
oldDeltaRequest.reset();
deltaRequestPool.push(oldDeltaRequest);
}
/** * {@inheritDoc} * <p> * This implementation is a NO-OP. The diff is reset in {@link #getDiff()}.
*/
@Override publicvoid resetDiff() {
resetDeltaRequest();
}
/** * {@inheritDoc} * <p> * This implementation is a NO-OP. Any required locking takes place in the methods that make modifications.
*/
@Override publicvoid lock() { // NO-OP
}
/** * {@inheritDoc} * <p> * This implementation is a NO-OP. Any required unlocking takes place in the methods that make modifications.
*/
@Override publicvoid unlock() { // NO-OP
}
/** * If this returns true, to replicate that an object has been accessed * * @return boolean
*/
@Override publicboolean isAccessReplicate() { long replDelta = System.currentTimeMillis() - getLastTimeReplicated(); if (maxInactiveInterval >= 0 && replDelta > (maxInactiveInterval * 1000L)) { returntrue;
} returnfalse;
}
/** * Access to an existing object.
*/
@Override publicvoid accessEntry() { this.access(); this.setPrimarySession(false); this.endAccess();
}
/** * returns true if this session is the primary session, if that is the case, the manager can expire it upon timeout.
*/
@Override publicboolean isPrimarySession() { return isPrimarySession;
}
/** * Sets whether this is the primary session or not. * * @param primarySession Flag value
*/
@Override publicvoid setPrimarySession(boolean primarySession) { this.isPrimarySession = primarySession;
}
/** * Set the session identifier for this session. * * @param id The new session identifier
*/
@Override publicvoid setId(String id) {
setId(id, true);
}
/** * Set the <code>isNew</code> flag for this session. * * @param isNew The new value for the <code>isNew</code> flag
*/
@Override publicvoid setNew(boolean isNew) {
setNew(isNew, true);
}
/** * Set the authenticated Principal that is associated with this Session. This provides an <code>Authenticator</code> * with a means to cache a previously authenticated Principal, and avoid potentially expensive * <code>Realm.authenticate()</code> calls on every request. * * @param principal The new Principal, or <code>null</code> if none
*/
@Override publicvoid setPrincipal(Principal principal) {
setPrincipal(principal, true);
}
/** * Set the authentication type used to authenticate our cached Principal, if any. * * @param authType The new cached authentication type
*/
@Override publicvoid setAuthType(String authType) {
setAuthType(authType, true);
}
/** * Return the <code>isValid</code> flag for this session.
*/
@Override publicboolean isValid() { if (!this.isValid) { returnfalse;
} if (this.expiring) { returntrue;
} if (activityCheck && accessCount.get() > 0) { returntrue;
} if (maxInactiveInterval > 0) { int timeIdle = (int) (getIdleTimeInternal() / 1000L); if (isPrimarySession()) { if (timeIdle >= maxInactiveInterval) {
expire(true);
}
} else { if (timeIdle >= (2 * maxInactiveInterval)) { // if the session has been idle twice as long as allowed, // the primary session has probably crashed, and no other // requests are coming in. that is why we do this. otherwise // we would have a memory leak
expire(true, false);
}
}
}
returnthis.isValid;
}
/** * End the access and register to ReplicationValve (crossContext support)
*/
@Override publicvoid endAccess() { super.endAccess(); if (manager instanceof ClusterManagerBase) {
((ClusterManagerBase) manager).registerSessionAtReplicationValve(this);
}
}
// ------------------------------------------------- Session Public Methods
/** * 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?
*/
@Override publicvoid expire(boolean notify) {
expire(notify, true);
}
// Check to see if session has already been invalidated. // Do not check expiring at this point as expire should not return until // isValid is false if (!isValid) { return;
}
synchronized (this) { // Check again, now we are inside the sync so this code only runs once // Double check locking - isValid needs to be volatile if (!isValid) { return;
}
/** * Read a serialized version of the contents of this session object from the specified object input stream, without * requiring that the StandardSession itself have been serialized. * * @param stream The object input stream to read from * * @exception ClassNotFoundException if an unknown class is specified * @exception IOException if an input/output error occurs
*/
@Override publicvoid readObjectData(ObjectInputStream stream) throws ClassNotFoundException, IOException {
doReadObject((ObjectInput) stream);
}
/** * Write a serialized version of the contents of this session object to the specified object output stream, without * requiring that the StandardSession itself have been serialized. * * @param stream The object output stream to write to * * @exception IOException if an input/output error occurs
*/
@Override publicvoid writeObjectData(ObjectOutputStream stream) throws IOException {
writeObjectData((ObjectOutput) stream);
}
/** * Get the request. * @return the request * @deprecated Unused. This method will be removed in Tomcat 12.
*/
@Deprecated public DeltaRequest getDeltaRequest() { return deltaRequest;
}
/** * Replace the existing deltaRequest with the provided replacement. * * @param deltaRequest The new deltaRequest. Expected to be either a newly created object or an instance that has * been reset. * * @return The old deltaRequest
*/
DeltaRequest replaceDeltaRequest(DeltaRequest deltaRequest) {
lockInternal(); try {
DeltaRequest oldDeltaRequest = this.deltaRequest; this.deltaRequest = deltaRequest; this.deltaRequest.setSessionId(getIdInternal()); return oldDeltaRequest;
} finally {
unlockInternal();
}
}
// ----------------------------------------------HttpSession Public Methods
/** * 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</code>, the container * calls <code>valueUnbound()</code> 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? * * @exception IllegalStateException if this method is called on an invalidated session
*/
@Override publicvoid removeAttribute(String name, boolean notify) {
removeAttribute(name, notify, true);
}
publicvoid removeAttribute(String name, boolean notify, boolean addDeltaRequest) { // Validate our current state if (!isValid()) { thrownew IllegalStateException(sm.getString("standardSession.removeAttribute.ise"));
}
removeAttributeInternal(name, notify, addDeltaRequest);
}
/** * 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</code>, the container * calls <code>valueBound()</code> 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
*/
@Override publicvoid setAttribute(String name, Object value) {
setAttribute(name, value, true, true);
}
/** * Read a serialized version of this session object from the specified object input stream. * <p> * <b>IMPLEMENTATION NOTE </b>: 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
*/
@Override protectedvoid doReadObject(ObjectInputStream stream) throws ClassNotFoundException, IOException {
doReadObject((ObjectInput) stream);
}
// Deserialize the scalar instance variables (except Manager)
authType = null; // Transient only
creationTime = ((Long) stream.readObject()).longValue();
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();
version = ((Long) stream.readObject()).longValue(); boolean hasPrincipal = stream.readBoolean();
principal = null; if (hasPrincipal) {
principal = (Principal) stream.readObject();
}
id = (String) stream.readObject(); if (log.isDebugEnabled()) {
log.debug(sm.getString("deltaSession.readSession", id));
}
Object nextObject = stream.readObject();
// Compatibility with versions that do not persist the authentication // notes if (!(nextObject instanceof Integer)) { // Not an Integer so the next two objects will be // 'expected session ID' and 'saved request' if (nextObject != null) {
notes.put(org.apache.catalina.authenticator.Constants.SESSION_ID_NOTE, nextObject);
}
nextObject = stream.readObject(); if (nextObject != null) {
notes.put(org.apache.catalina.authenticator.Constants.FORM_REQUEST_NOTE, nextObject);
}
// Next object will be the number of attributes
nextObject = stream.readObject();
}
// Deserialize the attribute count and attribute values if (attributes == null) {
attributes = new ConcurrentHashMap<>();
} int n = ((Integer) nextObject).intValue(); boolean isValidSave = isValid;
isValid = true; for (int i = 0; i < n; i++) {
String name = (String) stream.readObject(); final Object value; try {
value = stream.readObject();
} catch (WriteAbortedException wae) { if (wae.getCause() instanceof NotSerializableException) { // Skip non serializable attributes continue;
} throw wae;
} // Handle the case where the filter configuration was changed while // the web application was stopped. if (exclude(name, value)) { continue;
} // ConcurrentHashMap does not allow null keys or values if (null != value) {
attributes.put(name, value);
}
}
isValid = isValidSave;
// Session listeners
n = ((Integer) stream.readObject()).intValue(); if (listeners == null || n > 0) {
listeners = new ArrayList<>();
} for (int i = 0; i < n; i++) {
SessionListener listener = (SessionListener) stream.readObject();
listeners.add(listener);
}
if (notes == null) {
notes = new ConcurrentHashMap<>();
}
activate();
}
/** * Write a serialized version of this session object to the specified object output stream. * <p> * <b>IMPLEMENTATION NOTE </b>: The owning Manager will not be stored in the serialized representation of this * Session. After calling <code>readObject()</code>, you must set the associated Manager explicitly. * <p> * <b>IMPLEMENTATION NOTE </b>: 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</code> property of the associated Manager is set to <code>true</code>. * * @param stream The output stream to write to * * @exception IOException if an input/output error occurs
*/
@Override protectedvoid doWriteObject(ObjectOutputStream stream) throws IOException {
doWriteObject((ObjectOutput) stream);
}
stream.writeObject(id); if (log.isDebugEnabled()) {
log.debug(sm.getString("deltaSession.writeSession", id));
}
// Write the notes associated with authentication. Without these, // authentication can fail without sticky sessions or if there is a // fail-over during authentication.
stream.writeObject(notes.get(org.apache.catalina.authenticator.Constants.SESSION_ID_NOTE));
stream.writeObject(notes.get(org.apache.catalina.authenticator.Constants.FORM_REQUEST_NOTE));
// Accumulate the names of serializable and non-serializable attributes
String keys[] = keys();
List<String> saveNames = new ArrayList<>();
List<Object> saveValues = new ArrayList<>(); for (String key : keys) {
Object value = null;
value = attributes.get(key); if (value != null && !exclude(key, value) && isAttributeDistributable(key, value)) {
saveNames.add(key);
saveValues.add(value);
}
}
// Serialize the attribute count and the Serializable attributes int n = saveNames.size();
stream.writeObject(Integer.valueOf(n)); for (int i = 0; i < n; i++) {
stream.writeObject(saveNames.get(i)); try {
stream.writeObject(saveValues.get(i));
} catch (NotSerializableException e) {
log.error(sm.getString("standardSession.notSerializable", saveNames.get(i), id), e);
}
}
// Serializable listeners
ArrayList<SessionListener> saveListeners = new ArrayList<>(); for (SessionListener listener : listeners) { if (listener instanceof ReplicatedSessionListener) {
saveListeners.add(listener);
}
}
stream.writeObject(Integer.valueOf(saveListeners.size())); for (SessionListener listener : saveListeners) {
stream.writeObject(listener);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.