| /* |
| * 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; |
| |
| import java.io.Externalizable; |
| import java.io.IOException; |
| import java.io.NotSerializableException; |
| import java.io.ObjectInput; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutput; |
| import java.io.ObjectOutputStream; |
| import java.io.Serializable; |
| import java.io.WriteAbortedException; |
| import java.security.Principal; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import java.util.concurrent.locks.Lock; |
| import java.util.concurrent.locks.ReentrantReadWriteLock; |
| |
| import org.apache.catalina.Manager; |
| import org.apache.catalina.SessionListener; |
| import org.apache.catalina.ha.CatalinaCluster; |
| import org.apache.catalina.ha.ClusterManager; |
| import org.apache.catalina.ha.ClusterMessage; |
| import org.apache.catalina.ha.ClusterSession; |
| import org.apache.catalina.session.ManagerBase; |
| import org.apache.catalina.session.StandardSession; |
| import org.apache.catalina.tribes.io.ReplicationStream; |
| import org.apache.catalina.tribes.tipis.ReplicatedMapEntry; |
| import org.apache.juli.logging.Log; |
| import org.apache.juli.logging.LogFactory; |
| import org.apache.tomcat.util.collections.SynchronizedStack; |
| import org.apache.tomcat.util.res.StringManager; |
| |
| /** |
| * Similar to the StandardSession except that this session will keep track of deltas during a request. |
| */ |
| public class DeltaSession extends StandardSession implements Externalizable, ClusterSession, ReplicatedMapEntry { |
| |
| public static final Log log = LogFactory.getLog(DeltaSession.class); |
| |
| /** |
| * The string manager for this package. |
| */ |
| protected static final StringManager sm = StringManager.getManager(DeltaSession.class); |
| |
| // ----------------------------------------------------- Instance Variables |
| |
| /** |
| * 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. |
| */ |
| private transient boolean isPrimarySession = true; |
| |
| /** |
| * The delta request contains all the action info |
| */ |
| private transient DeltaRequest deltaRequest = null; |
| |
| /** |
| * Last time the session was replicated, used for distributed expiring of session |
| */ |
| private transient long lastTimeReplicated = System.currentTimeMillis(); |
| |
| |
| protected final Lock diffLock = new ReentrantReadWriteLock().writeLock(); |
| |
| private long version; |
| |
| // ----------------------------------------------------------- Constructors |
| |
| public DeltaSession() { |
| this(null); |
| } |
| |
| /** |
| * 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); |
| } |
| |
| private DeltaRequest createRequest() { |
| return createRequest(null, false); |
| } |
| |
| /* |
| * DeltaRequest instances are created via this protected method to enable subclasses to over-ride the method to use |
| * custom DeltaRequest implementations. |
| */ |
| protected DeltaRequest createRequest(String sessionId, boolean recordAllActions) { |
| return new DeltaRequest(sessionId, recordAllActions); |
| } |
| |
| // ----------------------------------------------------- ReplicatedMapEntry |
| |
| @Override |
| public boolean isDirty() { |
| return deltaRequest.getSize() > 0; |
| } |
| |
| @Override |
| public boolean isDiffable() { |
| return true; |
| } |
| |
| @Override |
| public byte[] getDiff() throws IOException { |
| SynchronizedStack<DeltaRequest> deltaRequestPool = null; |
| DeltaRequest newDeltaRequest; |
| |
| if (manager instanceof ClusterManagerBase) { |
| deltaRequestPool = ((ClusterManagerBase) manager).getDeltaRequestPool(); |
| newDeltaRequest = deltaRequestPool.pop(); |
| if (newDeltaRequest == null) { |
| newDeltaRequest = createRequest(null, ((ClusterManagerBase) manager).isRecordAllActions()); |
| } |
| } else { |
| newDeltaRequest = createRequest(); |
| } |
| |
| DeltaRequest oldDeltaRequest = replaceDeltaRequest(newDeltaRequest); |
| |
| byte[] result = oldDeltaRequest.serialize(); |
| |
| 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); |
| } |
| |
| return result; |
| } |
| |
| public ClassLoader[] getClassLoaders() { |
| if (manager instanceof ClusterManagerBase) { |
| return ((ClusterManagerBase) manager).getClassLoaders(); |
| } else if (manager instanceof ManagerBase mb) { |
| return ClusterManagerBase.getClassLoaders(mb.getContext()); |
| } |
| return null; |
| } |
| |
| @Override |
| public void applyDiff(byte[] diff, int offset, int length) throws IOException, ClassNotFoundException { |
| Thread currentThread = Thread.currentThread(); |
| ClassLoader contextLoader = currentThread.getContextClassLoader(); |
| lockInternal(); |
| try (ObjectInputStream stream = ((ClusterManager) getManager()).getReplicationStream(diff, offset, length)) { |
| ClassLoader[] loaders = getClassLoaders(); |
| if (loaders != null && loaders.length > 0) { |
| currentThread.setContextClassLoader(loaders[0]); |
| } |
| deltaRequest.readExternal(stream); |
| deltaRequest.execute(this, ((ClusterManager) getManager()).isNotifyListenersOnReplication()); |
| } finally { |
| unlockInternal(); |
| currentThread.setContextClassLoader(contextLoader); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * <p> |
| * This implementation is a NO-OP. The diff is reset in {@link #getDiff()}. |
| */ |
| @Override |
| public void resetDiff() { |
| resetDeltaRequest(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * <p> |
| * This implementation is a NO-OP. Any required locking takes place in the methods that make modifications. |
| */ |
| @Override |
| public void lock() { |
| // NO-OP |
| } |
| |
| /** |
| * {@inheritDoc} |
| * <p> |
| * This implementation is a NO-OP. Any required unlocking takes place in the methods that make modifications. |
| */ |
| @Override |
| public void unlock() { |
| // NO-OP |
| } |
| |
| /** |
| * Lock during serialization. |
| */ |
| private void lockInternal() { |
| diffLock.lock(); |
| } |
| |
| /** |
| * Unlock after serialization. |
| */ |
| private void unlockInternal() { |
| diffLock.unlock(); |
| } |
| |
| @Override |
| public void setOwner(Object owner) { |
| if (owner instanceof ClusterManager cm && getManager() == null) { |
| this.setManager(cm); |
| this.setValid(true); |
| this.setPrimarySession(false); |
| this.access(); |
| this.resetDeltaRequest(); |
| this.endAccess(); |
| } |
| } |
| |
| @Override |
| public boolean isAccessReplicate() { |
| long replDelta = System.currentTimeMillis() - getLastTimeReplicated(); |
| return maxInactiveInterval >= 0 && replDelta > (maxInactiveInterval * 1000L); |
| } |
| |
| @Override |
| public void accessEntry() { |
| this.access(); |
| this.setPrimarySession(false); |
| this.endAccess(); |
| } |
| |
| // ----------------------------------------------------- Session Properties |
| |
| @Override |
| public boolean isPrimarySession() { |
| return isPrimarySession; |
| } |
| |
| @Override |
| public void setPrimarySession(boolean primarySession) { |
| this.isPrimarySession = primarySession; |
| } |
| |
| |
| @Override |
| public void setId(String id, boolean notify) { |
| super.setId(id, notify); |
| lockInternal(); |
| try { |
| deltaRequest.setSessionId(getIdInternal()); |
| } finally { |
| unlockInternal(); |
| } |
| } |
| |
| |
| @Override |
| public void setId(String id) { |
| setId(id, true); |
| } |
| |
| |
| @Override |
| public void setMaxInactiveInterval(int interval) { |
| this.setMaxInactiveInterval(interval, true); |
| } |
| |
| |
| public void setMaxInactiveInterval(int interval, boolean addDeltaRequest) { |
| super.maxInactiveInterval = interval; |
| if (addDeltaRequest) { |
| lockInternal(); |
| try { |
| deltaRequest.setMaxInactiveInterval(interval); |
| } finally { |
| unlockInternal(); |
| } |
| } |
| } |
| |
| @Override |
| public void setNew(boolean isNew) { |
| setNew(isNew, true); |
| } |
| |
| public void setNew(boolean isNew, boolean addDeltaRequest) { |
| super.setNew(isNew); |
| if (addDeltaRequest) { |
| lockInternal(); |
| try { |
| deltaRequest.setNew(isNew); |
| } finally { |
| unlockInternal(); |
| } |
| } |
| } |
| |
| @Override |
| public void setPrincipal(Principal principal) { |
| setPrincipal(principal, true); |
| } |
| |
| public void setPrincipal(Principal principal, boolean addDeltaRequest) { |
| lockInternal(); |
| try { |
| super.setPrincipal(principal); |
| if (addDeltaRequest) { |
| deltaRequest.setPrincipal(principal); |
| } |
| } finally { |
| unlockInternal(); |
| } |
| } |
| |
| @Override |
| public void setAuthType(String authType) { |
| setAuthType(authType, true); |
| } |
| |
| public void setAuthType(String authType, boolean addDeltaRequest) { |
| lockInternal(); |
| try { |
| super.setAuthType(authType); |
| if (addDeltaRequest) { |
| deltaRequest.setAuthType(authType); |
| } |
| } finally { |
| unlockInternal(); |
| } |
| } |
| |
| @Override |
| public boolean isValid() { |
| if (!this.isValid) { |
| return false; |
| } |
| if (this.expiring) { |
| return true; |
| } |
| if (activityCheck && accessCount.get() > 0) { |
| return true; |
| } |
| 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); |
| } |
| } |
| } |
| |
| return this.isValid; |
| } |
| |
| @Override |
| public void endAccess() { |
| super.endAccess(); |
| if (manager instanceof ClusterManagerBase) { |
| ((ClusterManagerBase) manager).registerSessionAtReplicationValve(this); |
| } |
| } |
| |
| // ------------------------------------------------- Session Public Methods |
| |
| @Override |
| public void expire(boolean notify) { |
| expire(notify, true); |
| } |
| |
| public void expire(boolean notify, boolean notifyCluster) { |
| |
| // 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; |
| } |
| |
| if (manager == null) { |
| return; |
| } |
| |
| String expiredId = getIdInternal(); |
| |
| if (notifyCluster && expiredId != null && manager instanceof DeltaManager dmanager) { |
| CatalinaCluster cluster = dmanager.getCluster(); |
| ClusterMessage msg = dmanager.requestCompleted(expiredId, true); |
| if (msg != null) { |
| cluster.send(msg); |
| } |
| } |
| |
| super.expire(notify); |
| |
| if (notifyCluster) { |
| if (log.isDebugEnabled()) { |
| log.debug(sm.getString("deltaSession.notifying", ((ClusterManager) manager).getName(), |
| Boolean.valueOf(isPrimarySession()), expiredId)); |
| } |
| if (manager instanceof DeltaManager) { |
| ((DeltaManager) manager).sessionExpired(expiredId); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void recycle() { |
| lockInternal(); |
| try { |
| super.recycle(); |
| deltaRequest.clear(); |
| } finally { |
| unlockInternal(); |
| } |
| } |
| |
| |
| @Override |
| public String toString() { |
| return "DeltaSession[" + id + ']'; |
| } |
| |
| @Override |
| public void addSessionListener(SessionListener listener) { |
| addSessionListener(listener, true); |
| } |
| |
| public void addSessionListener(SessionListener listener, boolean addDeltaRequest) { |
| lockInternal(); |
| try { |
| super.addSessionListener(listener); |
| if (addDeltaRequest && listener instanceof ReplicatedSessionListener) { |
| deltaRequest.addSessionListener(listener); |
| } |
| } finally { |
| unlockInternal(); |
| } |
| } |
| |
| @Override |
| public void removeSessionListener(SessionListener listener) { |
| removeSessionListener(listener, true); |
| } |
| |
| public void removeSessionListener(SessionListener listener, boolean addDeltaRequest) { |
| lockInternal(); |
| try { |
| super.removeSessionListener(listener); |
| if (addDeltaRequest && listener instanceof ReplicatedSessionListener) { |
| deltaRequest.removeSessionListener(listener); |
| } |
| } finally { |
| unlockInternal(); |
| } |
| } |
| |
| |
| // ------------------------------------------------ Session Package Methods |
| |
| @Override |
| public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { |
| lockInternal(); |
| try { |
| readObjectData(in); |
| } finally { |
| unlockInternal(); |
| } |
| } |
| |
| |
| @Override |
| public void readObjectData(ObjectInputStream stream) throws ClassNotFoundException, IOException { |
| doReadObject((ObjectInput) stream); |
| } |
| |
| public void readObjectData(ObjectInput stream) throws ClassNotFoundException, IOException { |
| doReadObject(stream); |
| } |
| |
| @Override |
| public void writeObjectData(ObjectOutputStream stream) throws IOException { |
| writeObjectData((ObjectOutput) stream); |
| } |
| |
| public void writeObjectData(ObjectOutput stream) throws IOException { |
| doWriteObject(stream); |
| } |
| |
| public void resetDeltaRequest() { |
| lockInternal(); |
| try { |
| deltaRequest.reset(); |
| deltaRequest.setSessionId(getIdInternal()); |
| } finally { |
| unlockInternal(); |
| } |
| } |
| |
| /** |
| * 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(); |
| } |
| } |
| |
| |
| protected void deserializeAndExecuteDeltaRequest(byte[] delta) throws IOException, ClassNotFoundException { |
| if (manager instanceof ClusterManagerBase) { |
| SynchronizedStack<DeltaRequest> deltaRequestPool = ((ClusterManagerBase) manager).getDeltaRequestPool(); |
| |
| DeltaRequest newDeltaRequest = deltaRequestPool.pop(); |
| if (newDeltaRequest == null) { |
| newDeltaRequest = createRequest(null, ((ClusterManagerBase) manager).isRecordAllActions()); |
| } |
| |
| ReplicationStream ois = ((ClusterManagerBase) manager).getReplicationStream(delta); |
| newDeltaRequest.readExternal(ois); |
| ois.close(); |
| |
| DeltaRequest oldDeltaRequest = null; |
| lockInternal(); |
| try { |
| oldDeltaRequest = replaceDeltaRequest(newDeltaRequest); |
| newDeltaRequest.execute(this, ((ClusterManagerBase) manager).isNotifyListenersOnReplication()); |
| setPrimarySession(false); |
| } finally { |
| unlockInternal(); |
| if (oldDeltaRequest != null) { |
| oldDeltaRequest.reset(); |
| deltaRequestPool.push(oldDeltaRequest); |
| } |
| } |
| } |
| } |
| // ------------------------------------------------- HttpSession Properties |
| |
| // ----------------------------------------------HttpSession Public Methods |
| |
| @Override |
| public void removeAttribute(String name, boolean notify) { |
| removeAttribute(name, notify, true); |
| } |
| |
| public void removeAttribute(String name, boolean notify, boolean addDeltaRequest) { |
| // Validate our current state |
| if (!isValid()) { |
| throw new IllegalStateException(sm.getString("standardSession.removeAttribute.ise")); |
| } |
| removeAttributeInternal(name, notify, addDeltaRequest); |
| } |
| |
| @Override |
| public void setAttribute(String name, Object value) { |
| setAttribute(name, value, true, true); |
| } |
| |
| public void setAttribute(String name, Object value, boolean notify, boolean addDeltaRequest) { |
| |
| // 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; |
| } |
| |
| lockInternal(); |
| try { |
| super.setAttribute(name, value, notify); |
| if (addDeltaRequest && !exclude(name, value)) { |
| deltaRequest.setAttribute(name, value); |
| } |
| } finally { |
| unlockInternal(); |
| } |
| } |
| |
| |
| @Override |
| public void removeNote(String name) { |
| removeNote(name, true); |
| } |
| |
| public void removeNote(String name, boolean addDeltaRequest) { |
| lockInternal(); |
| try { |
| super.removeNote(name); |
| if (addDeltaRequest) { |
| deltaRequest.removeNote(name); |
| } |
| } finally { |
| unlockInternal(); |
| } |
| } |
| |
| |
| @Override |
| public void setNote(String name, Object value) { |
| setNote(name, value, true); |
| } |
| |
| public void setNote(String name, Object value, boolean addDeltaRequest) { |
| |
| if (value == null) { |
| removeNote(name, addDeltaRequest); |
| return; |
| } |
| |
| lockInternal(); |
| try { |
| super.setNote(name, value); |
| if (addDeltaRequest) { |
| deltaRequest.setNote(name, value); |
| } |
| } finally { |
| unlockInternal(); |
| } |
| } |
| |
| |
| // -------------------------------------------- HttpSession Private Methods |
| |
| @Override |
| protected void doReadObject(ObjectInputStream stream) throws ClassNotFoundException, IOException { |
| doReadObject((ObjectInput) stream); |
| } |
| |
| private void doReadObject(ObjectInput stream) throws ClassNotFoundException, IOException { |
| |
| // 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(); |
| } |
| |
| @Override |
| public void writeExternal(ObjectOutput out) throws IOException { |
| lockInternal(); |
| try { |
| doWriteObject(out); |
| } finally { |
| unlockInternal(); |
| } |
| } |
| |
| |
| @Override |
| protected void doWriteObject(ObjectOutputStream stream) throws IOException { |
| doWriteObject((ObjectOutput) stream); |
| } |
| |
| private void doWriteObject(ObjectOutput stream) throws IOException { |
| // Write the scalar instance variables (except Manager) |
| 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)); |
| stream.writeObject(Long.valueOf(version)); |
| stream.writeBoolean(getPrincipal() instanceof Serializable); |
| if (getPrincipal() instanceof Serializable) { |
| stream.writeObject(getPrincipal()); |
| } |
| |
| 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 = 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); |
| } |
| } |
| |
| |
| // -------------------------------------------------------- Private Methods |
| |
| protected void removeAttributeInternal(String name, boolean notify, boolean addDeltaRequest) { |
| lockInternal(); |
| try { |
| // Remove this attribute from our collection |
| Object value = attributes.get(name); |
| if (value == null) { |
| return; |
| } |
| |
| super.removeAttributeInternal(name, notify); |
| if (addDeltaRequest && !exclude(name, null)) { |
| deltaRequest.removeAttribute(name); |
| } |
| |
| } finally { |
| unlockInternal(); |
| } |
| } |
| |
| @Override |
| public long getLastTimeReplicated() { |
| return lastTimeReplicated; |
| } |
| |
| @Override |
| public long getVersion() { |
| return version; |
| } |
| |
| @Override |
| public void setLastTimeReplicated(long lastTimeReplicated) { |
| this.lastTimeReplicated = lastTimeReplicated; |
| } |
| |
| @Override |
| public void setVersion(long version) { |
| this.version = version; |
| } |
| |
| protected void setAccessCount(int count) { |
| if (accessCount == null && activityCheck) { |
| accessCount = new AtomicInteger(); |
| } |
| if (accessCount != null) { |
| accessCount.set(count); |
| } |
| } |
| } |