blob: 09dacdeace9f41cc9b988a6ebacdceae6731dac7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.modules.session.catalina;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.Loader;
import org.apache.catalina.Pipeline;
import org.apache.catalina.Session;
import org.apache.catalina.Valve;
import org.apache.catalina.session.ManagerBase;
import org.apache.catalina.session.StandardSession;
import org.apache.catalina.util.CustomObjectInputStream;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.query.Query;
import org.apache.geode.cache.query.QueryService;
import org.apache.geode.cache.query.SelectResults;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.apache.geode.modules.session.catalina.internal.DeltaSessionStatistics;
import org.apache.geode.modules.util.ContextMapper;
import org.apache.geode.modules.util.RegionConfiguration;
import org.apache.geode.modules.util.RegionHelper;
public abstract class DeltaSessionManager extends ManagerBase
implements Lifecycle, PropertyChangeListener, SessionManager {
static final String catalinaBaseSystemProperty = "catalina.base";
static final String javaTempDirSystemProperty = "java.io.tmpdir";
static final String fileSeparatorSystemProperty = "file.separator";
/**
* The number of rejected sessions.
*/
private AtomicInteger rejectedSessions;
/**
* The maximum number of active Sessions allowed, or -1 for no limit.
*/
private int maxActiveSessions = -1;
/**
* Has this <code>Manager</code> been started?
*/
protected AtomicBoolean started = new AtomicBoolean(false);
/**
* The name of this <code>Manager</code>
*/
protected String name;
private Valve jvmRouteBinderValve;
private Valve commitSessionValve;
private SessionCache sessionCache;
private static final String DEFAULT_REGION_NAME = RegionHelper.NAME + "_sessions";
private static final boolean DEFAULT_ENABLE_GATEWAY_REPLICATION = false;
private static final boolean DEFAULT_ENABLE_DEBUG_LISTENER = false;
private static final boolean DEFAULT_ENABLE_COMMIT_VALVE = true;
private static final boolean DEFAULT_ENABLE_COMMIT_VALVE_FAILFAST = false;
private static final boolean DEFAULT_PREFER_DESERIALIZED_FORM = true;
/*
* This *MUST* only be assigned during start/startInternal otherwise it will be associated with
* the incorrect context class loader.
*/
private Log LOGGER;
protected String regionName = DEFAULT_REGION_NAME;
private String regionAttributesId; // the default is different for client-server and
// peer-to-peer
private Boolean enableLocalCache; // the default is different for client-server and peer-to-peer
private boolean enableCommitValve = DEFAULT_ENABLE_COMMIT_VALVE;
private boolean enableCommitValveFailfast = DEFAULT_ENABLE_COMMIT_VALVE_FAILFAST;
private boolean enableGatewayReplication = DEFAULT_ENABLE_GATEWAY_REPLICATION;
private boolean enableDebugListener = DEFAULT_ENABLE_DEBUG_LISTENER;
private boolean preferDeserializedForm = DEFAULT_PREFER_DESERIALIZED_FORM;
private Timer timer;
private final Set<String> sessionsToTouch;
private static final long TIMER_TASK_PERIOD =
Long.getLong("gemfiremodules.sessionTimerTaskPeriod", 10000);
private static final long TIMER_TASK_DELAY =
Long.getLong("gemfiremodules.sessionTimerTaskDelay", 10000);
public DeltaSessionManager() {
this.rejectedSessions = new AtomicInteger(0);
// Create the set to store sessions to be touched after get attribute requests
this.sessionsToTouch = Collections.newSetFromMap(new ConcurrentHashMap<>());
}
@Override
public String getRegionName() {
return this.regionName;
}
public void setRegionName(String regionName) {
this.regionName = regionName;
}
@Override
public void setMaxInactiveInterval(final int interval) {
super.setMaxInactiveInterval(interval);
}
@Override
public String getRegionAttributesId() {
// This property will be null if it hasn't been set in the context.xml file.
// Since its default is dependent on the session cache, get the default from
// the session cache.
if (this.regionAttributesId == null) {
this.regionAttributesId = getSessionCache().getDefaultRegionAttributesId();
}
return this.regionAttributesId;
}
@SuppressWarnings("unused")
public void setRegionAttributesId(String regionType) {
this.regionAttributesId = regionType;
}
@Override
public boolean getEnableLocalCache() {
// This property will be null if it hasn't been set in the context.xml file.
// Since its default is dependent on the session cache, get the default from
// the session cache.
if (this.enableLocalCache == null) {
this.enableLocalCache = getSessionCache().getDefaultEnableLocalCache();
}
return this.enableLocalCache;
}
@SuppressWarnings("unused")
public void setEnableLocalCache(boolean enableLocalCache) {
this.enableLocalCache = enableLocalCache;
}
@SuppressWarnings("unused")
public int getMaxActiveSessions() {
return this.maxActiveSessions;
}
@SuppressWarnings("unused")
public void setMaxActiveSessions(int maxActiveSessions) {
int oldMaxActiveSessions = this.maxActiveSessions;
this.maxActiveSessions = maxActiveSessions;
support.firePropertyChange("maxActiveSessions", new Integer(oldMaxActiveSessions),
new Integer(this.maxActiveSessions));
}
@Override
public boolean getEnableGatewayDeltaReplication() {
// return this.enableGatewayDeltaReplication;
return false; // disabled
}
@SuppressWarnings("unused")
public void setEnableGatewayDeltaReplication(boolean enableGatewayDeltaReplication) {
// this.enableGatewayDeltaReplication = enableGatewayDeltaReplication;
// Disabled. Keeping the method for backward compatibility.
}
@Override
public boolean getEnableGatewayReplication() {
return this.enableGatewayReplication;
}
@SuppressWarnings("unused")
public void setEnableGatewayReplication(boolean enableGatewayReplication) {
this.enableGatewayReplication = enableGatewayReplication;
}
@Override
public boolean getEnableDebugListener() {
return this.enableDebugListener;
}
@SuppressWarnings("unused")
public void setEnableDebugListener(boolean enableDebugListener) {
this.enableDebugListener = enableDebugListener;
}
@Override
public boolean isCommitValveEnabled() {
return this.enableCommitValve;
}
public void setEnableCommitValve(boolean enable) {
this.enableCommitValve = enable;
}
@Override
public boolean isCommitValveFailfastEnabled() {
return this.enableCommitValveFailfast;
}
@SuppressWarnings("unused")
public void setEnableCommitValveFailfast(boolean enable) {
this.enableCommitValveFailfast = enable;
}
@Override
public boolean isBackingCacheAvailable() {
return sessionCache.isBackingCacheAvailable();
}
@SuppressWarnings("unused")
public void setPreferDeserializedForm(boolean enable) {
this.preferDeserializedForm = enable;
}
@Override
public boolean getPreferDeserializedForm() {
return this.preferDeserializedForm;
}
@Override
public String getStatisticsName() {
return getContextName().replace("/", "");
}
@Override
public Log getLogger() {
if (LOGGER == null) {
LOGGER = LogFactory.getLog(DeltaSessionManager.class);
}
return LOGGER;
}
public SessionCache getSessionCache() {
return this.sessionCache;
}
public DeltaSessionStatistics getStatistics() {
return getSessionCache().getStatistics();
}
boolean isPeerToPeer() {
return getSessionCache().isPeerToPeer();
}
@SuppressWarnings("unused")
public boolean isClientServer() {
return getSessionCache().isClientServer();
}
/**
* This method was taken from StandardManager to set the default maxInactiveInterval based on the
* container (to 30 minutes).
* <p>
* Set the Container with which this Manager has been associated. If it is a Context (the usual
* case), listen for changes to the session timeout property.
*
* @param container The associated Container
*/
@Override
public void setContainer(Container container) {
// De-register from the old Container (if any)
if ((this.container != null) && (this.container instanceof Context)) {
this.container.removePropertyChangeListener(this);
}
// Default processing provided by our superclass
super.setContainer(container);
// Register with the new Container (if any)
if ((this.container != null) && (this.container instanceof Context)) {
// Overwrite the max inactive interval with the context's session timeout.
setMaxInactiveInterval(((Context) this.container).getSessionTimeout() * 60);
this.container.addPropertyChangeListener(this);
}
}
@Override
public Session findSession(String id) throws IOException {
if (id == null) {
return null;
}
if (getLogger().isDebugEnabled()) {
getLogger().debug(
this + ": Finding session " + id + " in " + getSessionCache().getOperatingRegionName());
}
DeltaSessionInterface session = (DeltaSessionInterface) getSessionCache().getSession(id);
/*
* Check that the context name for this session is the same as this manager's. This comes into
* play when multiple versions of a webapp are deployed and active at the same time; the context
* name will contain an embedded version number; something like /test###2.
*/
if (session != null && !session.getContextName().isEmpty()
&& !getContextName().equals(session.getContextName())) {
getLogger()
.info(this + ": Session " + id + " rejected as container name and context do not match: "
+ getContextName() + " != " + session.getContextName());
session = null;
}
if (session == null) {
if (getLogger().isDebugEnabled()) {
getLogger().debug(this + ": Did not find session " + id + " in "
+ getSessionCache().getOperatingRegionName());
}
} else {
if (getLogger().isDebugEnabled()) {
getLogger().debug(this + ": Found session " + id + " in "
+ getSessionCache().getOperatingRegionName() + ": " + session);
}
// The session was previously stored. Set new to false.
session.setNew(false);
// Check the manager.
// If the manager is null, the session was replicated and this is a
// failover situation. Reset the manager and activate the session.
if (session.getManager() == null) {
session.setOwner(this);
session.activate();
}
}
return session;
}
protected void initializeSessionCache() {
// Retrieve the cache
GemFireCacheImpl cache = (GemFireCacheImpl) getAnyCacheInstance();
if (cache == null) {
throw new IllegalStateException(
"No cache exists. Please configure either a PeerToPeerCacheLifecycleListener or ClientServerCacheLifecycleListener in the server.xml file.");
}
// Create the appropriate session cache
this.sessionCache = cache.isClient() ? new ClientServerSessionCache(this, cache)
: new PeerToPeerSessionCache(this, cache);
// Initialize the session cache
initSessionCache();
}
void initSessionCache() {
this.sessionCache.initialize();
}
Cache getAnyCacheInstance() {
return CacheFactory.getAnyInstance();
}
@Override
protected StandardSession getNewSession() {
return new DeltaSession(this);
}
@Override
public void remove(Session session) {
remove(session, false);
}
public void remove(Session session, @SuppressWarnings("unused") boolean update) {
// super.remove(session);
// Remove the session from the region if necessary.
// It will have already been removed if it expired implicitly.
DeltaSessionInterface ds = (DeltaSessionInterface) session;
if (ds.getExpired()) {
if (getLogger().isDebugEnabled()) {
getLogger().debug(this + ": Expired session " + session.getId() + " from "
+ getSessionCache().getOperatingRegionName());
}
} else {
if (getLogger().isDebugEnabled()) {
getLogger().debug(this + ": Destroying session " + session.getId() + " from "
+ getSessionCache().getOperatingRegionName());
}
getSessionCache().destroySession(session.getId());
if (getLogger().isDebugEnabled()) {
getLogger().debug(this + ": Destroyed session " + session.getId() + " from "
+ getSessionCache().getOperatingRegionName());
}
}
}
@Override
public void add(Session session) {
// super.add(session);
if (getLogger().isDebugEnabled()) {
getLogger().debug(this + ": Storing session " + session.getId() + " into "
+ getSessionCache().getOperatingRegionName());
}
getSessionCache().putSession(session);
if (getLogger().isDebugEnabled()) {
getLogger().debug(this + ": Stored session " + session.getId() + " into "
+ getSessionCache().getOperatingRegionName());
}
getSessionCache().getStatistics().incSessionsCreated();
}
@Override
public int getRejectedSessions() {
return this.rejectedSessions.get();
}
@Override
public void setRejectedSessions(int rejectedSessions) {
this.rejectedSessions.set(rejectedSessions);
}
/**
* Returns the number of active sessions
*
* @return number of sessions active
*/
@Override
public int getActiveSessions() {
return getSessionCache().size();
}
/**
* For debugging: return a list of all session ids currently active
*/
@Override
public String listSessionIds() {
StringBuilder builder = new StringBuilder();
Iterator<String> sessionIds = getSessionCache().keySet().iterator();
while (sessionIds.hasNext()) {
builder.append(sessionIds.next());
if (sessionIds.hasNext()) {
builder.append(" ");
}
}
return builder.toString();
}
/*
* If local caching is enabled, add the session to the set of sessions to be touched. A timer task
* will be periodically invoked to get the session in the session region to update its last
* accessed time. This prevents the session from expiring in the case where the application is
* only getting attributes from the session and never putting attributes into the session. If
* local caching is disabled. the session's last accessed time would already have been updated
* properly in the sessions region.
*
* Note: Due to issues in GemFire expiry, sessions are always asynchronously touched using a
* function regardless whether or not local caching is enabled. This prevents premature
* expiration.
*/
void addSessionToTouch(String sessionId) {
this.sessionsToTouch.add(sessionId);
}
protected Set<String> getSessionsToTouch() {
return this.sessionsToTouch;
}
boolean removeTouchedSession(String sessionId) {
return this.sessionsToTouch.remove(sessionId);
}
protected void scheduleTimerTasks() {
// Create the timer
this.timer = new Timer("Timer for " + toString(), true);
// Schedule the task to handle sessions to be touched
scheduleTouchSessionsTask();
// Schedule the task to maintain the maxActive sessions
scheduleDetermineMaxActiveSessionsTask();
}
private void scheduleTouchSessionsTask() {
TimerTask task = new TimerTask() {
@Override
public void run() {
// Get the sessionIds to touch and clear the set inside synchronization
Set<String> sessionIds;
sessionIds = new HashSet<>(getSessionsToTouch());
getSessionsToTouch().clear();
// Touch the sessions we currently have
if (!sessionIds.isEmpty()) {
getSessionCache().touchSessions(sessionIds);
if (getLogger().isDebugEnabled()) {
getLogger().debug(DeltaSessionManager.this + ": Touched sessions: " + sessionIds);
}
}
}
};
this.timer.schedule(task, TIMER_TASK_DELAY, TIMER_TASK_PERIOD);
}
protected void cancelTimer() {
if (timer != null) {
this.timer.cancel();
}
}
private void scheduleDetermineMaxActiveSessionsTask() {
TimerTask task = new TimerTask() {
@Override
public void run() {
int currentActiveSessions = getSessionCache().size();
if (currentActiveSessions > getMaxActive()) {
setMaxActive(currentActiveSessions);
if (getLogger().isDebugEnabled()) {
getLogger().debug(
DeltaSessionManager.this + ": Set max active sessions: " + currentActiveSessions);
}
}
}
};
this.timer.schedule(task, TIMER_TASK_DELAY, TIMER_TASK_PERIOD);
}
@Override
public void load() throws ClassNotFoundException, IOException {
doLoad();
ContextMapper.addContext(getContextName(), this);
}
@Override
public void unload() throws IOException {
doUnload();
ContextMapper.removeContext(getContextName());
}
protected void registerJvmRouteBinderValve() {
if (getLogger().isDebugEnabled()) {
getLogger().debug(this + ": Registering JVM route binder valve");
}
jvmRouteBinderValve = new JvmRouteBinderValve();
getPipeline().addValve(jvmRouteBinderValve);
}
Pipeline getPipeline() {
return getContainer().getPipeline();
}
protected void unregisterJvmRouteBinderValve() {
if (getLogger().isDebugEnabled()) {
getLogger().debug(this + ": Unregistering JVM route binder valve");
}
if (jvmRouteBinderValve != null) {
getPipeline().removeValve(jvmRouteBinderValve);
}
}
protected void registerCommitSessionValve() {
if (getLogger().isDebugEnabled()) {
getLogger().debug(this + ": Registering CommitSessionValve");
}
commitSessionValve = new CommitSessionValve();
getPipeline().addValve(commitSessionValve);
}
protected void unregisterCommitSessionValve() {
if (getLogger().isDebugEnabled()) {
getLogger().debug(this + ": Unregistering CommitSessionValve");
}
if (commitSessionValve != null) {
getPipeline().removeValve(commitSessionValve);
}
}
// ------------------------------ Lifecycle Methods
/**
* Process property change events from our associated Context.
* <p>
* Part of this method implementation was taken from StandardManager. The sessionTimeout can be
* changed in the web.xml which is processed after the context.xml. The context (and the default
* session timeout) would already have been set in this Manager. This is the way to get the new
* session timeout value specified in the web.xml.
* <p>
* The precedence order for setting the session timeout value is:
* <p>
* <ol>
* <li>the max inactive interval is set based on the Manager defined in the context.xml
* <li>the max inactive interval is then overwritten by the value of the Context's session timeout
* when setContainer is called
* <li>the max inactive interval is then overwritten by the value of the session-timeout specified
* in the web.xml (if any)
* </ol>
*
* @param event The property change event that has occurred
*/
@Override
public void propertyChange(PropertyChangeEvent event) {
// Validate the source of this event
if (!(event.getSource() instanceof Context)) {
return;
}
// Process a relevant property change
if (event.getPropertyName().equals("sessionTimeout")) {
try {
int interval = (Integer) event.getNewValue();
if (interval < RegionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL) {
getLogger().warn("The configured session timeout of " + interval
+ " minutes is invalid. Using the original value of " + event.getOldValue()
+ " minutes.");
interval = (Integer) event.getOldValue();
}
// StandardContext.setSessionTimeout passes -1 if the configured timeout
// is 0; otherwise it passes the value set in web.xml. If the interval
// parameter equals the default, set the max inactive interval to the
// default (no expiration); otherwise set it in seconds.
setMaxInactiveInterval(interval == RegionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL
? RegionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL : interval * 60);
} catch (NumberFormatException e) {
getLogger()
.error(sm.getString("standardManager.sessionTimeout", event.getNewValue().toString()));
}
}
}
/**
* Save any currently active sessions in the appropriate persistence mechanism, if any. If
* persistence is not supported, this method returns without doing anything.
*
* @throws IOException if an input/output error occurs
*/
private void doUnload() throws IOException {
QueryService querySvc = getSessionCache().getCache().getQueryService();
Context context = getTheContext();
if (context == null) {
return;
}
String regionName;
if (getRegionName().startsWith("/")) {
regionName = getRegionName();
} else {
regionName = "/" + getRegionName();
}
Query query = querySvc.newQuery("select s.id from " + regionName
+ " as s where s.contextName = '" + context.getPath() + "'");
if (getLogger().isDebugEnabled()) {
getLogger().debug("Query: " + query.getQueryString());
}
SelectResults results;
try {
results = (SelectResults) query.execute();
} catch (Exception ex) {
getLogger().error("Unable to perform query during doUnload", ex);
return;
}
if (results.isEmpty()) {
getLogger().debug("No sessions to unload for context " + context.getPath());
return; // nothing to do
}
// Open an output stream to the specified pathname, if any
File store = sessionStore(context.getPath());
if (store == null) {
return;
}
if (getLogger().isDebugEnabled()) {
getLogger().debug("Unloading sessions to " + store.getAbsolutePath());
}
FileOutputStream fos = null;
BufferedOutputStream bos = null;
ObjectOutputStream oos = null;
boolean error = false;
try {
fos = getFileOutputStream(store);
bos = getBufferedOutputStream(fos);
oos = getObjectOutputStream(bos);
} catch (IOException e) {
error = true;
getLogger().error("Exception unloading sessions", e);
throw e;
} finally {
if (error) {
if (oos != null) {
try {
oos.close();
} catch (IOException ioe) {
// Ignore
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException ioe) {
// Ignore
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException ioe) {
// Ignore
}
}
}
}
ArrayList<DeltaSessionInterface> list = new ArrayList<>();
@SuppressWarnings("unchecked")
Iterator<String> elements = (Iterator<String>) results.iterator();
while (elements.hasNext()) {
String id = elements.next();
DeltaSessionInterface session = (DeltaSessionInterface) findSession(id);
if (session != null) {
list.add(session);
}
}
// Write the number of active sessions, followed by the details
if (getLogger().isDebugEnabled())
getLogger().debug("Unloading " + list.size() + " sessions");
try {
writeToObjectOutputStream(oos, list);
for (DeltaSessionInterface session : list) {
if (session instanceof StandardSession) {
StandardSession standardSession = (StandardSession) session;
standardSession.passivate();
standardSession.writeObjectData(oos);
} else {
// All DeltaSessionInterfaces as of Geode 1.0 should be based on StandardSession
throw new IOException("Session should be of type StandardSession");
}
}
} catch (IOException e) {
getLogger().error("Exception unloading sessions", e);
try {
oos.close();
} catch (IOException f) {
// Ignore
}
throw e;
}
// Flush and close the output stream
try {
oos.flush();
} finally {
try {
oos.close();
} catch (IOException f) {
// Ignore
}
}
// Locally destroy the sessions we just wrote
if (getSessionCache().isClientServer()) {
for (DeltaSessionInterface session : list) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Locally destroying session " + session.getId());
}
getSessionCache().getOperatingRegion().localDestroy(session.getId());
}
}
if (getLogger().isDebugEnabled()) {
getLogger().debug("Unloading complete");
}
}
/**
* Load any currently active sessions that were previously unloaded to the appropriate persistence
* mechanism, if any. If persistence is not supported, this method returns without doing anything.
*
* @throws ClassNotFoundException if a serialized class cannot be found during the reload
* @throws IOException if an input/output error occurs
*/
private void doLoad() throws ClassNotFoundException, IOException {
Context context = getTheContext();
if (context == null) {
return;
}
// Open an input stream to the specified pathname, if any
File store = sessionStore(context.getPath());
if (store == null) {
getLogger().debug("No session store file found");
return;
}
if (getLogger().isDebugEnabled()) {
getLogger().debug("Loading sessions from " + store.getAbsolutePath());
}
FileInputStream fis = null;
BufferedInputStream bis = null;
ObjectInputStream ois;
Loader loader = null;
ClassLoader classLoader = null;
try {
fis = getFileInputStream(store);
bis = getBufferedInputStream(fis);
if (getTheContext() != null) {
loader = getTheContext().getLoader();
}
if (loader != null) {
classLoader = loader.getClassLoader();
}
if (classLoader != null) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Creating custom object input stream for class loader");
}
ois = new CustomObjectInputStream(bis, classLoader);
} else {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Creating standard object input stream");
}
ois = getObjectInputStream(bis);
}
} catch (FileNotFoundException e) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("No persisted data file found");
}
return;
} catch (IOException e) {
getLogger().error("Exception loading sessions", e);
try {
fis.close();
} catch (IOException f) {
// Ignore
}
try {
bis.close();
} catch (IOException f) {
// Ignore
}
throw e;
}
// Load the previously unloaded active sessions
try {
int n = getSessionCountFromObjectInputStream(ois);
if (getLogger().isDebugEnabled()) {
getLogger().debug("Loading " + n + " persisted sessions");
}
for (int i = 0; i < n; i++) {
StandardSession session = getNewSession();
session.readObjectData(ois);
session.setManager(this);
Region region = getSessionCache().getOperatingRegion();
DeltaSessionInterface existingSession = (DeltaSessionInterface) region.get(session.getId());
// Check whether the existing session is newer
if (existingSession != null
&& existingSession.getLastAccessedTime() > session.getLastAccessedTime()) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Loaded session " + session.getId() + " is older than cached copy");
}
continue;
}
// Check whether the new session has already expired
if (!session.isValid()) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Loaded session " + session.getId() + " is invalid");
}
continue;
}
getLogger().debug("Loading session " + session.getId());
session.activate();
add(session);
}
} catch (ClassNotFoundException | IOException e) {
getLogger().error(e);
try {
ois.close();
} catch (IOException f) {
// Ignore
}
throw e;
} finally {
// Close the input stream
try {
ois.close();
} catch (IOException f) {
// ignored
}
// Delete the persistent storage file
if (store.exists()) {
if (!store.delete()) {
getLogger().warn("Couldn't delete persistent storage file " + store.getAbsolutePath());
}
}
}
}
/**
* Return a File object representing the pathname to our persistence file, if any.
*/
private File sessionStore(String ctxPath) {
String storeDir = getSystemPropertyValue(catalinaBaseSystemProperty);
if (storeDir == null || storeDir.isEmpty()) {
storeDir = getSystemPropertyValue(javaTempDirSystemProperty);
} else {
storeDir += getSystemPropertyValue(fileSeparatorSystemProperty) + "temp";
}
return getFileAtPath(storeDir, ctxPath);
}
String getSystemPropertyValue(String propertyKey) {
return System.getProperty(propertyKey);
}
File getFileAtPath(String storeDir, String ctxPath) {
return (new File(storeDir, ctxPath.replaceAll("/", "_") + ".sessions.ser"));
}
FileInputStream getFileInputStream(File file) throws FileNotFoundException {
return new FileInputStream(file.getAbsolutePath());
}
BufferedInputStream getBufferedInputStream(FileInputStream fis) {
return new BufferedInputStream(fis);
}
ObjectInputStream getObjectInputStream(BufferedInputStream bis) throws IOException {
return new ObjectInputStream(bis);
}
FileOutputStream getFileOutputStream(File file) throws FileNotFoundException {
return new FileOutputStream(file.getAbsolutePath());
}
BufferedOutputStream getBufferedOutputStream(FileOutputStream fos) {
return new BufferedOutputStream(fos);
}
ObjectOutputStream getObjectOutputStream(BufferedOutputStream bos) throws IOException {
return new ObjectOutputStream(bos);
}
void writeToObjectOutputStream(ObjectOutputStream oos, List listToWrite) throws IOException {
oos.writeObject(listToWrite.size());
}
int getSessionCountFromObjectInputStream(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
return (Integer) ois.readObject();
}
@Override
public String toString() {
return getClass().getSimpleName() + "[" + "container="
+ getTheContext() + "; regionName=" + this.regionName
+ "; regionAttributesId=" + this.regionAttributesId + "]";
}
String getContextName() {
return getTheContext().getName();
}
public Context getTheContext() {
if (getContainer() instanceof Context) {
return (Context) getContainer();
} else {
getLogger().error("Unable to unload sessions - container is of type "
+ getContainer().getClass().getName() + " instead of StandardContext");
return null;
}
}
}