geode-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dschnei...@apache.org
Subject [04/20] incubator-geode git commit: GEODE-14: Imported modules from geode-1.0.0-SNAPSHOT-2.src.tar
Date Mon, 06 Jul 2015 21:46:25 GMT
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/DeltaSessionManager.java
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/DeltaSessionManager.java b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/DeltaSessionManager.java
new file mode 100644
index 0000000..c5befb5
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/DeltaSessionManager.java
@@ -0,0 +1,1000 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.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.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 com.gemstone.gemfire.cache.Region;
+import com.gemstone.gemfire.modules.util.ContextMapper;
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Loader;
+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.catalina.util.LifecycleSupport;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+import com.gemstone.gemfire.cache.query.Query;
+import com.gemstone.gemfire.cache.query.QueryService;
+import com.gemstone.gemfire.cache.query.SelectResults;
+import com.gemstone.gemfire.cache.CacheFactory;
+import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
+import com.gemstone.gemfire.modules.session.catalina.internal.DeltaSessionStatistics;
+import com.gemstone.gemfire.modules.util.RegionConfiguration;
+import com.gemstone.gemfire.modules.util.RegionHelper;
+
+abstract public class DeltaSessionManager extends ManagerBase implements Lifecycle,
+    PropertyChangeListener, SessionManager {
+
+  /**
+   * The <code>LifecycleSupport</code> for this component.
+   */
+  protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+  
+  /**
+   * The number of rejected sessions.
+   */
+  private AtomicInteger rejectedSessions;
+
+  /**
+   * The maximum number of active Sessions allowed, or -1 for no limit.
+   */
+  protected 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;
+  
+  protected Valve jvmRouteBinderValve;
+
+  protected Valve commitSessionValve;
+
+  protected SessionCache sessionCache;
+  
+  protected static final String DEFAULT_REGION_NAME = RegionHelper.NAME + "_sessions";
+  
+  protected static final boolean DEFAULT_ENABLE_GATEWAY_DELTA_REPLICATION = false;
+  
+  protected static final boolean DEFAULT_ENABLE_GATEWAY_REPLICATION = false;
+  
+  protected static final boolean DEFAULT_ENABLE_DEBUG_LISTENER = false;
+
+  protected static final boolean DEFAULT_ENABLE_COMMIT_VALVE = true;
+
+  protected static final boolean DEFAULT_ENABLE_COMMIT_VALVE_FAILFAST = false;
+
+  protected 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.
+   */
+  protected Log LOGGER;
+
+  protected String regionName = DEFAULT_REGION_NAME;
+  
+  protected String regionAttributesId; // the default is different for client-server and peer-to-peer
+  
+  protected Boolean enableLocalCache; // the default is different for client-server and peer-to-peer
+
+  protected boolean enableCommitValve = DEFAULT_ENABLE_COMMIT_VALVE;
+
+  protected boolean enableCommitValveFailfast = DEFAULT_ENABLE_COMMIT_VALVE_FAILFAST;
+
+  protected boolean enableGatewayDeltaReplication = DEFAULT_ENABLE_GATEWAY_DELTA_REPLICATION;
+  
+  protected boolean enableGatewayReplication = DEFAULT_ENABLE_GATEWAY_REPLICATION;
+
+  protected boolean enableDebugListener = DEFAULT_ENABLE_DEBUG_LISTENER;
+
+  protected 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() {
+    // Create the set to store sessions to be touched after get attribute requests
+    this.sessionsToTouch =  Collections.newSetFromMap(
+        new ConcurrentHashMap<String, Boolean>());
+  }
+  
+  @Override
+  public String getRegionName() {
+    return this.regionName;
+  }
+  
+  public void setRegionName(String regionName) {
+    this.regionName = regionName;
+  }
+
+  @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;
+  }
+    
+  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;
+  }
+  
+  public void setEnableLocalCache(boolean enableLocalCache) {
+    this.enableLocalCache = enableLocalCache;
+  }
+
+  public int getMaxActiveSessions() {
+    return this.maxActiveSessions;
+  }
+
+  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
+  }
+
+  public void setEnableGatewayDeltaReplication(boolean enableGatewayDeltaReplication) {
+    this.enableGatewayDeltaReplication = enableGatewayDeltaReplication;
+  }
+  
+  @Override
+  public boolean getEnableGatewayReplication() {
+    return this.enableGatewayReplication;
+  }
+
+  public void setEnableGatewayReplication(boolean enableGatewayReplication) {
+    this.enableGatewayReplication = enableGatewayReplication;
+  }
+
+  @Override
+  public boolean getEnableDebugListener() {
+    return this.enableDebugListener;
+  }
+
+  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;
+  }
+  
+  public void setEnableCommitValveFailfast(boolean enable) {
+    this.enableCommitValveFailfast = enable;
+  }
+
+  @Override
+  public boolean isBackingCacheAvailable() {
+    return sessionCache.isBackingCacheAvailable();
+  }
+
+  public void setPreferDeserializedForm(boolean enable) {
+    this.preferDeserializedForm = enable;
+  }
+
+  @Override
+  public boolean getPreferDeserializedForm() {
+    return this.preferDeserializedForm;
+  }
+
+  @Override
+  public String getStatisticsName() {
+    return getContainer().getName().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();
+  }
+  
+  public boolean isPeerToPeer() {
+    return getSessionCache().isPeerToPeer();
+  }
+  
+  public boolean isClientServer() {
+    return getSessionCache().isClientServer();
+  }
+
+  /**
+   * This method was taken from StandardManager to set the default
+   * maxInactiveInterval based on the container (to 30 minutes).
+   * 
+   * 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)) {
+      ((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);
+      ((Context) 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());
+    }
+    DeltaSession session = (DeltaSession) 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() &&
+        ! getContainer().getName().equals(session.getContextName())) {
+      getLogger().info(this + ": Session " + id +
+          " rejected as container name and context do not match: " +
+          getContainer().getName() + " != " + 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) {
+        DeltaSession ds = (DeltaSession) session;
+        ds.setOwner(this);
+        ds.activate();
+      }
+    }
+    return session;
+  }
+
+  protected void initializeSessionCache() {
+    // Retrieve the cache
+    GemFireCacheImpl cache = (GemFireCacheImpl) CacheFactory.getAnyInstance();
+    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
+    this.sessionCache.initialize();
+  }
+
+  @Override
+  protected StandardSession getNewSession() {
+    return new DeltaSession(this);
+  }
+
+  @Override
+  public void remove(Session session) {
+    remove(session, false);
+  }
+
+  public void remove(Session session, boolean update) {
+    //super.remove(session);
+    // Remove the session from the region if necessary.
+    // It will have already been removed if it expired implicitly.
+    DeltaSession ds = (DeltaSession) 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();
+  }
+
+  public void setRejectedSessions(int rejectedSessions) {
+    this.rejectedSessions.set(rejectedSessions);
+  }
+
+  private void incrementRejectedSessions() {
+    this.rejectedSessions.incrementAndGet();
+  }
+
+  /** 
+   * 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.
+   */
+  protected void addSessionToTouch(String sessionId) {
+    this.sessionsToTouch.add(sessionId);
+  }
+
+  protected Set<String> getSessionsToTouch() {
+    return this.sessionsToTouch;
+  }
+
+  protected 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 = null;
+        sessionIds = new HashSet<String>(getSessionsToTouch());
+        getSessionsToTouch().clear();
+
+        // Touch the sessions we currently have
+        if (sessionIds != null && (!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(getContainer().getName(), this);
+  }
+
+  @Override
+  public void unload() throws IOException {
+    doUnload();
+    ContextMapper.removeContext(getContainer().getName());
+  }
+
+  protected void registerJvmRouteBinderValve() {
+    if (getLogger().isDebugEnabled()) {
+      getLogger().debug(this + ": Registering JVM route binder valve");
+    }
+    jvmRouteBinderValve = new JvmRouteBinderValve();
+    getContainer().getPipeline().addValve(jvmRouteBinderValve);
+  }
+
+  protected void unregisterJvmRouteBinderValve() {
+    if (getLogger().isDebugEnabled()) {
+      getLogger().debug(this + ": Unregistering JVM route binder valve");
+    }
+    if (jvmRouteBinderValve != null) {
+      getContainer().getPipeline().removeValve(jvmRouteBinderValve);
+    }
+  }
+
+  protected void registerCommitSessionValve() {
+    if (getLogger().isDebugEnabled()) {
+      getLogger().debug(this + ": Registering CommitSessionValve");
+    }
+    commitSessionValve = new CommitSessionValve();
+    getContainer().getPipeline().addValve(commitSessionValve);
+  }
+  
+  protected void unregisterCommitSessionValve() {
+    if (getLogger().isDebugEnabled()) {
+      getLogger().debug(this + ": Unregistering CommitSessionValve");
+    }
+    if (commitSessionValve != null) {
+      getContainer().getPipeline().removeValve(commitSessionValve);
+    }
+  }
+  
+  // ------------------------------ Lifecycle Methods
+  
+  /**
+   * Add a lifecycle event listener to this component.
+   *
+   * @param listener The listener to add
+   */
+  @Override
+  public void addLifecycleListener(LifecycleListener listener) {
+    this.lifecycle.addLifecycleListener(listener);
+  }
+
+  /**
+   * Get the lifecycle listeners associated with this lifecycle. If this
+   * Lifecycle has no listeners registered, a zero-length array is returned.
+   */
+  @Override
+  public LifecycleListener[] findLifecycleListeners() {
+    return this.lifecycle.findLifecycleListeners();
+  }
+
+  /**
+   * Remove a lifecycle event listener from this component.
+   *
+   * @param listener The listener to remove
+   */
+  @Override
+  public void removeLifecycleListener(LifecycleListener listener) {
+    this.lifecycle.removeLifecycleListener(listener);
+  }
+  
+  /**
+   * Process property change events from our associated Context.
+   * 
+   * 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.
+   * 
+   * The precedence order for setting the session timeout value is:
+   * 
+   * <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;
+    }
+    Context context = (Context) event.getSource();
+
+    // Process a relevant property change
+    if (event.getPropertyName().equals("sessionTimeout")) {
+      try {
+      	int interval = ((Integer) event.getNewValue()).intValue();
+      	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()).intValue();;
+      	}
+				// 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.
+   *
+   * @exception IOException if an input/output error occurs
+   */
+  protected void doUnload() throws IOException {
+    QueryService querySvc = sessionCache.getCache().getQueryService();
+    Context context;
+    if (getContainer() instanceof Context) {
+      context = (Context) getContainer();
+    } else {
+      getLogger().error("Unable to unload sessions - container is of type " +
+          getContainer().getClass().getName() + " instead of StandardContext");
+      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() + "'");
+    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 = new FileOutputStream(store.getAbsolutePath());
+      bos = new BufferedOutputStream(fos);
+      oos = new ObjectOutputStream(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<StandardSession> list = new ArrayList<StandardSession>();
+    Iterator<String> elements = results.iterator();
+    while (elements.hasNext()) {
+      String id = elements.next();
+      DeltaSession session = (DeltaSession) 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 {
+      oos.writeObject(new Integer(list.size()));
+      for (StandardSession session : list) {
+        session.passivate();
+        session.writeObjectData(oos);
+      }
+    } 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 (StandardSession session : list) {
+        if (getLogger().isDebugEnabled()) {
+          getLogger().debug("Locally destroying session " + session.getId());
+        }
+        getSessionCache().getOperatingRegion().localDestroy(session.getId());
+      }
+    }
+
+//    // Expire all the sessions we just wrote
+//    if (getLogger().isDebugEnabled()) {
+//      getLogger().debug("Expiring " + list.size() + " persisted sessions");
+//    }
+//    Iterator<StandardSession> expires = list.iterator();
+//    while (expires.hasNext()) {
+//      StandardSession session = expires.next();
+//      try {
+//        session.expire(false);
+//      } catch (Throwable t) {
+////        ExceptionUtils.handleThrowable(t);
+//      } finally {
+//        session.recycle();
+//      }
+//    }
+
+    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.
+   *
+   * @exception ClassNotFoundException if a serialized class cannot be
+   *  found during the reload
+   * @exception IOException if an input/output error occurs
+   */
+  protected void doLoad() throws ClassNotFoundException, IOException {
+    Context context;
+    if (getContainer() instanceof Context) {
+      context = (Context) getContainer();
+    } else {
+      getLogger().error("Unable to unload sessions - container is of type " +
+          getContainer().getClass().getName() + " instead of StandardContext");
+      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 = null;
+    Loader loader = null;
+    ClassLoader classLoader = null;
+    try {
+      fis = new FileInputStream(store.getAbsolutePath());
+      bis = new BufferedInputStream(fis);
+      if (container != null) {
+        loader = container.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 = new ObjectInputStream(bis);
+      }
+    } catch (FileNotFoundException e) {
+      if (getLogger().isDebugEnabled()) {
+        getLogger().debug("No persisted data file found");
+      }
+      return;
+    } catch (IOException e) {
+      getLogger().error("Exception loading sessions", e);
+      if (fis != null) {
+        try {
+          fis.close();
+        } catch (IOException f) {
+          // Ignore
+        }
+      }
+      if (bis != null) {
+        try {
+          bis.close();
+        } catch (IOException f) {
+          // Ignore
+        }
+      }
+      throw e;
+    }
+
+    // Load the previously unloaded active sessions
+    try {
+      Integer count = (Integer) ois.readObject();
+      int n = count.intValue();
+      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();
+        DeltaSession existingSession = (DeltaSession) 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 e) {
+      getLogger().error(e);
+      try {
+        ois.close();
+      } catch (IOException f) {
+        // Ignore
+      }
+      throw e;
+    } catch (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()) {
+        store.delete();
+      }
+    }
+  }
+
+  /**
+   * Return a File object representing the pathname to our
+   * persistence file, if any.
+   */
+  private File sessionStore(String ctxPath) {
+    String storeDir = System.getProperty("catalina.base");
+    if (storeDir == null || storeDir.isEmpty()) {
+      storeDir = System.getProperty("java.io.tmpdir");
+    } else {
+      storeDir += System.getProperty("file.separator") + "temp";
+    }
+    File file = new File(storeDir,
+        ctxPath.replaceAll("/", "_") + ".sessions.ser");
+
+    return (file);
+  }
+
+  @Override
+  public String toString() {
+    return new StringBuilder()
+      .append(getClass().getSimpleName())
+      .append("[")
+      .append("container=")
+      .append(getContainer())
+      .append("; regionName=")
+      .append(this.regionName)
+      .append("; regionAttributesId=")
+      .append(this.regionAttributesId)
+      .append("]")
+      .toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/JvmRouteBinderValve.java
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/JvmRouteBinderValve.java b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/JvmRouteBinderValve.java
new file mode 100644
index 0000000..42e25cc
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/JvmRouteBinderValve.java
@@ -0,0 +1,104 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.modules.session.catalina;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.apache.catalina.Manager;
+import org.apache.catalina.Session;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.valves.ValveBase;
+
+public class JvmRouteBinderValve extends ValveBase {
+  
+  protected static final String info = "com.gemstone.gemfire.modules.session.JvmRouteBinderValve/1.0";
+
+  @Override
+  public void invoke(Request request, Response response) throws IOException,
+      ServletException {
+    
+    // Get the Manager
+    Manager manager = request.getContext().getManager();
+
+    // If it is an AbstractManager, handle possible failover
+    if (manager instanceof DeltaSessionManager) {
+      DeltaSessionManager absMgr = (DeltaSessionManager) manager;
+      String localJvmRoute = absMgr.getJvmRoute();
+        if (localJvmRoute != null) {
+          handlePossibleFailover(request, absMgr,localJvmRoute);
+        }
+    }
+
+    // Invoke the next Valve
+    getNext().invoke(request, response);
+  }
+  
+  private void handlePossibleFailover(Request request, DeltaSessionManager manager, String localJvmRoute) {
+    String sessionId = request.getRequestedSessionId();
+    if (sessionId != null) {
+      // Get request JVM route
+      String requestJvmRoute = null;
+      int index = sessionId.indexOf(".");
+      if (index > 0) {
+        requestJvmRoute = sessionId.substring(index + 1, sessionId.length());
+      }
+
+      // If the requested JVM route doesn't equal the session's JVM route, handle failover
+      if (requestJvmRoute != null && !requestJvmRoute.equals(localJvmRoute)) {
+        if (manager.getLogger().isDebugEnabled()) {
+          StringBuilder builder = new StringBuilder();
+          builder
+            .append(this)
+            .append(": Handling failover of session ")
+            .append(sessionId)
+            .append(" from ")
+            .append(requestJvmRoute)
+            .append(" to ")
+            .append(localJvmRoute);
+          manager.getLogger().debug(builder.toString());
+        }
+        // Get the original session
+        Session session = null;
+        try {
+          session = manager.findSession(sessionId);
+        } catch (IOException e) {
+          StringBuilder builder = new StringBuilder();
+          builder
+            .append(this)
+            .append(": Caught exception attempting to find session ")
+            .append(sessionId)
+            .append(" in ")
+            .append(manager);
+          manager.getLogger().warn(builder.toString(), e);
+        }
+        
+        if (session == null) {
+          StringBuilder builder = new StringBuilder();
+          builder
+            .append(this)
+            .append(": Did not find session ")
+            .append(sessionId)
+            .append(" to failover in ")
+            .append(manager);
+          manager.getLogger().warn(builder.toString());
+        } else {
+          // Change its session id. This removes the previous session and creates the new one.
+          String baseSessionId = sessionId.substring(0, index);
+          String newSessionId = baseSessionId + "." + localJvmRoute;
+          session.setId(newSessionId);
+  
+          // Change the request's session id
+          request.changeSessionId(newSessionId);
+        }
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/LocalStrings.properties
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/LocalStrings.properties b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/LocalStrings.properties
new file mode 100644
index 0000000..5abddf7
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/LocalStrings.properties
@@ -0,0 +1 @@
+deltaSession.commit.ise=commit: Session {0} already invalidated

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/PeerToPeerCacheLifecycleListener.java
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/PeerToPeerCacheLifecycleListener.java b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/PeerToPeerCacheLifecycleListener.java
new file mode 100644
index 0000000..30da3bc
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/PeerToPeerCacheLifecycleListener.java
@@ -0,0 +1,14 @@
+package com.gemstone.gemfire.modules.session.catalina;
+
+import com.gemstone.gemfire.modules.session.bootstrap.PeerToPeerCache;
+
+/**
+ * This is a thin wrapper around a peer-to-peer cache.
+ */
+public class PeerToPeerCacheLifecycleListener 
+    extends AbstractCacheLifecycleListener {
+
+  public PeerToPeerCacheLifecycleListener() {
+    cache = PeerToPeerCache.getInstance();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/PeerToPeerSessionCache.java
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/PeerToPeerSessionCache.java b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/PeerToPeerSessionCache.java
new file mode 100644
index 0000000..df9c152
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/PeerToPeerSessionCache.java
@@ -0,0 +1,209 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.modules.session.catalina;
+
+import java.util.Set;
+
+
+import com.gemstone.gemfire.cache.Cache;
+import com.gemstone.gemfire.cache.GemFireCache;
+import com.gemstone.gemfire.cache.Region;
+import com.gemstone.gemfire.cache.RegionFactory;
+import com.gemstone.gemfire.cache.RegionShortcut;
+import com.gemstone.gemfire.cache.execute.Execution;
+import com.gemstone.gemfire.cache.execute.FunctionService;
+import com.gemstone.gemfire.cache.execute.ResultCollector;
+import com.gemstone.gemfire.modules.session.catalina.callback.LocalSessionCacheLoader;
+import com.gemstone.gemfire.modules.session.catalina.callback.LocalSessionCacheWriter;
+import com.gemstone.gemfire.modules.session.catalina.callback.SessionExpirationCacheListener;
+import com.gemstone.gemfire.modules.util.RegionConfiguration;
+import com.gemstone.gemfire.modules.util.RegionHelper;
+import com.gemstone.gemfire.modules.util.SessionCustomExpiry;
+import com.gemstone.gemfire.modules.util.TouchPartitionedRegionEntriesFunction;
+import com.gemstone.gemfire.modules.util.TouchReplicatedRegionEntriesFunction;
+import javax.servlet.http.HttpSession;
+
+public class PeerToPeerSessionCache extends AbstractSessionCache {
+
+  private Cache cache;
+  
+  protected static final String DEFAULT_REGION_ATTRIBUTES_ID = RegionShortcut.REPLICATE.toString();
+
+  protected static final boolean DEFAULT_ENABLE_LOCAL_CACHE = false;
+
+  public PeerToPeerSessionCache(SessionManager sessionManager, Cache cache) {
+    super(sessionManager);
+    this.cache = cache;
+  }
+  
+  @Override
+  public void initialize() {
+    // Register Functions
+    registerFunctions();
+    
+    // Create or retrieve the region
+    createOrRetrieveRegion();
+
+    // If local cache is enabled, create the local region fronting the session region
+    // and set it as the operating region; otherwise, use the session region directly
+    // as the operating region.
+    this.operatingRegion = getSessionManager().getEnableLocalCache() 
+      ? createOrRetrieveLocalRegion()
+      : this.sessionRegion;
+    
+    // Create or retrieve the statistics
+    createStatistics();
+  }
+  
+  @Override
+  public String getDefaultRegionAttributesId() {
+    return DEFAULT_REGION_ATTRIBUTES_ID;
+  }
+  
+  @Override
+  public boolean getDefaultEnableLocalCache() {
+    return DEFAULT_ENABLE_LOCAL_CACHE;
+  }
+
+  @Override
+  public void touchSessions(Set<String> sessionIds) {
+    // Get the region attributes id to determine the region type. This is
+    // problematic since the region attributes id doesn't really define the
+    // region type. This should look at the actual session region.
+    String regionAttributesID = getSessionManager().getRegionAttributesId().toLowerCase();
+    
+    // Invoke the appropriate function depending on the type of region
+    ResultCollector collector = null;
+    if (regionAttributesID.startsWith("partition")) {
+      // Execute the partitioned touch function on the primary server(s)
+      Execution execution = FunctionService.onRegion(getSessionRegion()).withFilter(sessionIds);
+      collector = execution.execute(TouchPartitionedRegionEntriesFunction.ID, true, false, true);
+    } else {
+      // Execute the member touch function on all the server(s)
+      Execution execution = FunctionService.onMembers(
+          getCache().getDistributedSystem()).withArgs(
+          new Object[] { this.sessionRegion.getFullPath(), sessionIds });
+      collector = execution.execute(TouchReplicatedRegionEntriesFunction.ID, true, false, false);
+    }
+    
+    // Get the result
+    try {
+      collector.getResult();
+    } catch (Exception e) {
+      // If an exception occurs in the function, log it.
+      getSessionManager().getLogger().warn("Caught unexpected exception:", e);
+    }
+  }
+  
+  @Override
+  public boolean isPeerToPeer() {
+    return true;
+  }
+  
+  @Override
+  public boolean isClientServer() {
+    return false;
+  }
+  
+  @Override
+  public Set<String> keySet() {
+  	return getSessionRegion().keySet();
+  }
+
+  @Override
+  public int size() {
+  	return getSessionRegion().size();
+  }
+
+  @Override
+  public GemFireCache getCache() {
+    return this.cache;
+  }
+
+  /**
+   * For peer-to-peer the backing cache *is* what's embedded in tomcat so it's always available
+   * @return
+   */
+  @Override
+  public boolean isBackingCacheAvailable() {
+    return true;
+  }
+
+  private void registerFunctions() {
+    // Register the touch partitioned region entries function if it is not already registered
+    if (!FunctionService.isRegistered(TouchPartitionedRegionEntriesFunction.ID)) {
+      FunctionService.registerFunction(new TouchPartitionedRegionEntriesFunction());
+    }
+    
+    // Register the touch replicated region entries function if it is not already registered
+    if (!FunctionService.isRegistered(TouchReplicatedRegionEntriesFunction.ID)) {
+      FunctionService.registerFunction(new TouchReplicatedRegionEntriesFunction());
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  protected void createOrRetrieveRegion() {
+    // Create the RegionConfiguration
+    RegionConfiguration configuration = createRegionConfiguration();
+    configuration.setSessionExpirationCacheListener(true);
+
+    // Attempt to retrieve the region
+    // If it already exists, validate it
+    // If it doesn't already exist, create it
+    Region region = this.cache.getRegion(getSessionManager().getRegionName());
+    if (region == null) {
+      // Create the region
+      region = RegionHelper.createRegion((Cache) getCache(), configuration);
+      if (getSessionManager().getLogger().isDebugEnabled()) {
+        getSessionManager().getLogger().debug("Created new session region: " + region);
+      }
+    } else {
+      // Validate the existing region
+      if (getSessionManager().getLogger().isDebugEnabled()) {
+        getSessionManager().getLogger().debug("Retrieved existing session region: " + region);
+      }
+      RegionHelper.validateRegion((Cache) getCache(), configuration, region);
+    }
+
+    // Set the session region
+    this.sessionRegion = region;
+  }
+  
+  private Region<String, HttpSession> createOrRetrieveLocalRegion() {
+    // Attempt to retrieve the fronting region
+    String frontingRegionName = this.sessionRegion.getName() + "_local";
+    Region<String, HttpSession> frontingRegion = this.cache.getRegion(frontingRegionName);
+    if (frontingRegion == null) {
+      // Create the region factory
+      RegionFactory<String,HttpSession> factory = this.cache.createRegionFactory(RegionShortcut.LOCAL_HEAP_LRU);
+      
+      // Add the cache loader and writer
+      factory.setCacheLoader(new LocalSessionCacheLoader(this.sessionRegion));
+      factory.setCacheWriter(new LocalSessionCacheWriter(this.sessionRegion));
+      
+      // Set the expiration time, action and listener if necessary
+      int maxInactiveInterval = getSessionManager().getMaxInactiveInterval();
+      if (maxInactiveInterval != RegionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL) {
+        factory.setStatisticsEnabled(true);
+        factory.setCustomEntryIdleTimeout(new SessionCustomExpiry());
+        factory.addCacheListener(new SessionExpirationCacheListener());
+      }
+      
+      // Create the region
+      frontingRegion = factory.create(frontingRegionName);
+      if (getSessionManager().getLogger().isDebugEnabled()) {
+        getSessionManager().getLogger().debug("Created new local session region: " + frontingRegion);
+      }
+    } else {
+      if (getSessionManager().getLogger().isDebugEnabled()) {
+        getSessionManager().getLogger().debug("Retrieved existing local session region: " + frontingRegion);
+      }
+    }
+    return frontingRegion;
+  }
+}  

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/SessionCache.java
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/SessionCache.java b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/SessionCache.java
new file mode 100644
index 0000000..a14ee8b
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/SessionCache.java
@@ -0,0 +1,55 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.modules.session.catalina;
+
+import java.util.Set;
+
+import com.gemstone.gemfire.cache.GemFireCache;
+import com.gemstone.gemfire.cache.Region;
+import com.gemstone.gemfire.modules.session.catalina.internal.DeltaSessionStatistics;
+import javax.servlet.http.HttpSession;
+import org.apache.catalina.Session;
+
+public interface SessionCache {
+  
+  public void initialize();
+  
+  public String getDefaultRegionAttributesId();
+
+  public boolean getDefaultEnableLocalCache();
+  
+  public String getSessionRegionName();
+  
+  public String getOperatingRegionName();
+
+  public void putSession(Session session);
+  
+  public HttpSession getSession(String sessionId);
+  
+  public void destroySession(String sessionId);
+  
+  public void touchSessions(Set<String> sessionIds);
+  
+  public DeltaSessionStatistics getStatistics();
+  
+  public GemFireCache getCache();
+  
+  public Region<String,HttpSession> getSessionRegion();
+  
+  public Region<String,HttpSession> getOperatingRegion();
+  
+  public boolean isPeerToPeer();
+  
+  public boolean isClientServer();
+  
+  public Set<String> keySet();
+  
+  public int size();
+
+  public boolean isBackingCacheAvailable();
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/SessionManager.java
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/SessionManager.java b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/SessionManager.java
new file mode 100644
index 0000000..1df8aab
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/SessionManager.java
@@ -0,0 +1,39 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.modules.session.catalina;
+
+import org.apache.juli.logging.Log;
+
+public interface SessionManager {
+
+  public String getRegionName();
+  
+  public String getRegionAttributesId();
+  
+  public int getMaxInactiveInterval();
+  
+  public boolean getEnableGatewayReplication();
+  
+  public boolean getEnableGatewayDeltaReplication();
+  
+  public boolean getEnableDebugListener();
+
+  public boolean getEnableLocalCache();
+
+  public boolean isCommitValveEnabled();
+
+  public boolean isCommitValveFailfastEnabled();
+
+  public boolean isBackingCacheAvailable();
+
+  public boolean getPreferDeserializedForm();
+
+  public String getStatisticsName();
+  
+  public Log getLogger();
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/Tomcat6DeltaSessionManager.java
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/Tomcat6DeltaSessionManager.java b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/Tomcat6DeltaSessionManager.java
new file mode 100644
index 0000000..17b03ec
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/Tomcat6DeltaSessionManager.java
@@ -0,0 +1,93 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.modules.session.catalina;
+
+import org.apache.catalina.LifecycleException;
+
+public class Tomcat6DeltaSessionManager extends DeltaSessionManager {
+
+  /**
+   * Prepare for the beginning of active use of the public methods of this
+   * component.  This method should be called after <code>configure()</code>,
+   * and before any of the public methods of the component are utilized.
+   *
+   * @exception LifecycleException if this component detects a fatal error
+   *  that prevents this component from being used
+   */ 
+  @Override
+  public void start() throws LifecycleException {
+    if (getLogger().isDebugEnabled()) {
+      getLogger().debug(this + ": Starting");
+    }
+    if (this.started.get()) {
+      return;
+    }
+    this.lifecycle.fireLifecycleEvent(START_EVENT, null);
+    try {
+      init();
+    } catch (Throwable t) {
+      getLogger().error(t.getMessage(), t);
+    }
+    
+    // Register our various valves
+    registerJvmRouteBinderValve();
+
+    if (isCommitValveEnabled()) {
+      registerCommitSessionValve();
+    }
+    
+    // Initialize the appropriate session cache interface
+    initializeSessionCache();
+
+    // Create the timer and schedule tasks
+    scheduleTimerTasks();
+
+    this.started.set(true);
+  }
+
+  /**
+   * Gracefully terminate the active use of the public methods of this
+   * component.  This method should be the last one called on a given
+   * instance of this component.
+   *
+   * @exception LifecycleException if this component detects a fatal error
+   *  that needs to be reported
+   */
+  @Override
+  public void stop() throws LifecycleException {
+    if (getLogger().isDebugEnabled()) {
+      getLogger().debug(this + ": Stopping");
+    }
+    this.started.set(false);
+    this.lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+
+    // StandardManager expires all Sessions here.
+    // All Sessions are not known by this Manager.
+    
+    // Require a new random number generator if we are restarted
+    this.random = null;
+
+    // Remove from RMI registry
+    if (this.initialized) {
+      destroy();
+    }
+    
+    // Clear any sessions to be touched
+    getSessionsToTouch().clear();
+    
+    // Cancel the timer
+    cancelTimer();
+    
+    // Unregister the JVM route valve
+    unregisterJvmRouteBinderValve();
+
+    if (isCommitValveEnabled()) {
+      unregisterCommitSessionValve();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/callback/LocalSessionCacheLoader.java
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/callback/LocalSessionCacheLoader.java b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/callback/LocalSessionCacheLoader.java
new file mode 100644
index 0000000..ff1ab0b
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/callback/LocalSessionCacheLoader.java
@@ -0,0 +1,35 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.modules.session.catalina.callback;
+
+import java.util.Properties;
+
+import com.gemstone.gemfire.cache.CacheLoader;
+import com.gemstone.gemfire.cache.CacheLoaderException;
+import com.gemstone.gemfire.cache.Declarable;
+import com.gemstone.gemfire.cache.LoaderHelper;
+import com.gemstone.gemfire.cache.Region;
+import javax.servlet.http.HttpSession;
+
+public class LocalSessionCacheLoader implements CacheLoader<String, HttpSession>,
+    Declarable {
+
+  private final Region<String,HttpSession> backingRegion;
+  
+  public LocalSessionCacheLoader(Region<String,HttpSession> backingRegion) {
+    this.backingRegion = backingRegion;
+  }
+  
+  public HttpSession load(LoaderHelper<String,HttpSession> helper) throws CacheLoaderException {
+    return this.backingRegion.get(helper.getKey());
+  }
+
+  public void close() {}
+
+  public void init(Properties p) {}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/callback/LocalSessionCacheWriter.java
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/callback/LocalSessionCacheWriter.java b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/callback/LocalSessionCacheWriter.java
new file mode 100644
index 0000000..458c8e8
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/callback/LocalSessionCacheWriter.java
@@ -0,0 +1,51 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.modules.session.catalina.callback;
+
+import java.util.Properties;
+
+import org.apache.catalina.Session;
+
+import com.gemstone.gemfire.cache.CacheWriterException;
+import com.gemstone.gemfire.cache.Declarable;
+import com.gemstone.gemfire.cache.EntryEvent;
+import com.gemstone.gemfire.cache.EntryNotFoundException;
+import com.gemstone.gemfire.cache.Region;
+import com.gemstone.gemfire.cache.util.CacheWriterAdapter;
+import javax.servlet.http.HttpSession;
+
+public class LocalSessionCacheWriter extends
+    CacheWriterAdapter<String, HttpSession> implements Declarable {
+
+  private final Region<String,HttpSession> backingRegion;
+  
+  public LocalSessionCacheWriter(Region<String,HttpSession> backingRegion) {
+    this.backingRegion = backingRegion;
+  }
+
+  public void beforeCreate(EntryEvent<String,HttpSession> event) throws CacheWriterException {
+    this.backingRegion.put(event.getKey(), event.getNewValue(), event.getCallbackArgument());
+  }
+
+  public void beforeUpdate(EntryEvent<String,HttpSession> event) throws CacheWriterException {
+    this.backingRegion.put(event.getKey(), event.getNewValue(), event.getCallbackArgument());
+  }
+
+  public void beforeDestroy(EntryEvent<String,HttpSession> event) throws CacheWriterException {
+    try {
+      this.backingRegion.destroy(event.getKey(), event.getCallbackArgument());
+    } catch (EntryNotFoundException e) {
+      // I think it is safe to ignore this exception. The entry could have
+      // expired already in the backing region.
+    }
+  }
+
+  public void close() {}
+
+  public void init(Properties p) {}
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/callback/SessionExpirationCacheListener.java
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/callback/SessionExpirationCacheListener.java b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/callback/SessionExpirationCacheListener.java
new file mode 100644
index 0000000..d5f9bf5
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/callback/SessionExpirationCacheListener.java
@@ -0,0 +1,70 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.modules.session.catalina.callback;
+
+import com.gemstone.gemfire.cache.Declarable;
+import com.gemstone.gemfire.cache.EntryEvent;
+import com.gemstone.gemfire.cache.Operation;
+
+import com.gemstone.gemfire.cache.util.CacheListenerAdapter;
+import com.gemstone.gemfire.modules.session.catalina.DeltaSession;
+import com.gemstone.gemfire.modules.session.catalina.DeltaSessionManager;
+import com.gemstone.gemfire.modules.util.ContextMapper;
+
+import java.util.Properties;
+
+import javax.servlet.http.HttpSession;
+
+public class SessionExpirationCacheListener extends CacheListenerAdapter<String,HttpSession> implements Declarable {
+
+  public void afterDestroy(EntryEvent<String,HttpSession> event) {
+    // A Session expired. If it was destroyed by GemFire expiration, process it.
+    // If it was destroyed via Session.invalidate, ignore it since it has
+    // already been processed.
+    DeltaSession session = null;
+    if (event.getOperation() == Operation.EXPIRE_DESTROY) {
+      session = (DeltaSession) event.getOldValue();
+    } else {
+      /*
+       * This comes into play when we're dealing with an empty client proxy. We
+       * need the actual destroyed object to come back from the server so that
+       * any associated listeners can fire correctly. Having the destroyed
+       * object come back as the callback arg depends on setting the property
+       * gemfire.EXPIRE_SENDS_ENTRY_AS_CALLBACK.
+       */
+      Object callback = event.getCallbackArgument();
+      if (callback != null && callback instanceof DeltaSession) {
+        session = (DeltaSession) callback;
+        DeltaSessionManager m = ContextMapper.getContext(
+            session.getContextName());
+        if (m != null) {
+          session.setOwner(m);
+        }
+      }
+    }
+    if (session != null) {
+      session.processExpired();
+    }
+  }
+
+  public void init(Properties p) {}
+
+  public boolean equals(Object obj) {
+    // This method is only implemented so that RegionAttributesCreation.sameAs
+    // works properly.
+    if (this == obj) {
+      return true;
+    }
+
+    if (obj == null || !(obj instanceof SessionExpirationCacheListener)) {
+      return false;
+    }
+
+    return true;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionAttributeEvent.java
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionAttributeEvent.java b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionAttributeEvent.java
new file mode 100644
index 0000000..70f5376
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionAttributeEvent.java
@@ -0,0 +1,9 @@
+package com.gemstone.gemfire.modules.session.catalina.internal;
+
+import com.gemstone.gemfire.DataSerializable;
+import com.gemstone.gemfire.modules.session.catalina.DeltaSession;
+
+public interface DeltaSessionAttributeEvent extends DataSerializable {
+
+  public void apply(DeltaSession session);
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionAttributeEventBatch.java
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionAttributeEventBatch.java b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionAttributeEventBatch.java
new file mode 100644
index 0000000..b0d8681
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionAttributeEventBatch.java
@@ -0,0 +1,85 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.modules.session.catalina.internal;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.gemstone.gemfire.DataSerializer;
+import com.gemstone.gemfire.cache.Cache;
+import com.gemstone.gemfire.cache.Region;
+import com.gemstone.gemfire.modules.gatewaydelta.AbstractGatewayDeltaEvent;
+import com.gemstone.gemfire.modules.session.catalina.DeltaSession;
+
+@SuppressWarnings("serial")
+public class DeltaSessionAttributeEventBatch extends AbstractGatewayDeltaEvent {
+
+  private List<DeltaSessionAttributeEvent> eventQueue;
+  
+  public DeltaSessionAttributeEventBatch() {
+  }
+
+  public DeltaSessionAttributeEventBatch(String regionName, String sessionId, List<DeltaSessionAttributeEvent> eventQueue) {
+    super(regionName, sessionId);
+    this.eventQueue = eventQueue;
+  }
+  
+  public List<DeltaSessionAttributeEvent> getEventQueue() {
+    return this.eventQueue;
+  }
+
+  public void apply(Cache cache) {
+    Region<String,DeltaSession> region = getRegion(cache);
+    DeltaSession session = region.get(this.key);
+    if (session == null) {
+      StringBuilder builder = new StringBuilder();
+      builder
+        .append("Session ")
+        .append(this.key)
+        .append(" was not found while attempting to apply ")
+        .append(this);
+      cache.getLogger().warning(builder.toString());
+    } else {
+      session.applyAttributeEvents(region, this.eventQueue);
+      if (cache.getLogger().fineEnabled()) {
+        StringBuilder builder = new StringBuilder();
+          builder
+            .append("Applied ")
+            .append(this);
+        cache.getLogger().fine(builder.toString());
+      }
+    }
+  }
+
+  public void fromData(DataInput in) throws IOException, ClassNotFoundException {
+    super.fromData(in);
+    this.eventQueue = DataSerializer.readArrayList(in);
+  }
+
+  public void toData(DataOutput out) throws IOException {
+    super.toData(out);
+    DataSerializer.writeArrayList((ArrayList) this.eventQueue, out);
+  }
+
+  public String toString() {
+    return new StringBuilder()
+      .append("DeltaSessionAttributeEventBatch[")
+      .append("regionName=")
+      .append(this.regionName)
+      .append("; sessionId=")
+      .append(this.key)
+      .append("; numberOfEvents=")
+      .append(this.eventQueue.size())
+      .append("]")
+      .toString();
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionDestroyAttributeEvent.java
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionDestroyAttributeEvent.java b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionDestroyAttributeEvent.java
new file mode 100644
index 0000000..a8ee4fa
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionDestroyAttributeEvent.java
@@ -0,0 +1,65 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.modules.session.catalina.internal;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import com.gemstone.gemfire.DataSerializable;
+import com.gemstone.gemfire.DataSerializer;
+import com.gemstone.gemfire.Instantiator;
+import com.gemstone.gemfire.modules.session.catalina.DeltaSession;
+
+
+@SuppressWarnings("serial")
+public class DeltaSessionDestroyAttributeEvent implements DeltaSessionAttributeEvent {
+
+  private String attributeName;
+  
+  public DeltaSessionDestroyAttributeEvent() {
+  }
+
+  public DeltaSessionDestroyAttributeEvent(String attributeName) {
+    this.attributeName = attributeName;
+  }
+  
+  public String getAttributeName() {
+    return this.attributeName;
+  }
+
+  public void apply(DeltaSession session) {
+    session.localDestroyAttribute(this.attributeName);
+  }
+
+  public void fromData(DataInput in) throws IOException, ClassNotFoundException {
+    this.attributeName = DataSerializer.readString(in);
+  }
+
+  public void toData(DataOutput out) throws IOException {
+    DataSerializer.writeString(this.attributeName, out);
+  }
+  
+  public static void registerInstantiator(int id) {
+    Instantiator.register(new Instantiator(DeltaSessionDestroyAttributeEvent.class, id) {
+      public DataSerializable newInstance() {
+        return new DeltaSessionDestroyAttributeEvent();
+      }
+    });
+  }
+  
+  public String toString() {
+    return new StringBuilder()
+      .append("DeltaSessionDestroyAttributeEvent[")
+      .append("attributeName=")
+      .append(this.attributeName)
+      .append("]")
+      .toString();
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionStatistics.java
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionStatistics.java b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionStatistics.java
new file mode 100644
index 0000000..43c8df5
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionStatistics.java
@@ -0,0 +1,81 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.modules.session.catalina.internal;
+
+import com.gemstone.gemfire.StatisticDescriptor;
+import com.gemstone.gemfire.Statistics;
+import com.gemstone.gemfire.StatisticsFactory;
+import com.gemstone.gemfire.StatisticsType;
+import com.gemstone.gemfire.StatisticsTypeFactory;
+import com.gemstone.gemfire.internal.StatisticsTypeFactoryImpl;
+
+public class DeltaSessionStatistics {
+
+  public static final String typeName = "SessionStatistics";
+
+  private static final StatisticsType type;
+
+  private static final String SESSIONS_CREATED = "sessionsCreated";
+  private static final String SESSIONS_INVALIDATED= "sessionsInvalidated";
+  private static final String SESSIONS_EXPIRED= "sessionsExpired";
+
+  private static final int sessionsCreatedId;
+  private static final int sessionsInvalidatedId;
+  private static final int sessionsExpiredId;
+
+  static {
+    // Initialize type
+    StatisticsTypeFactory f = StatisticsTypeFactoryImpl.singleton();
+    type = f.createType(typeName, typeName,
+      new StatisticDescriptor[] {
+        f.createIntCounter(SESSIONS_CREATED, "The number of sessions created", "operations"),
+        f.createIntCounter(SESSIONS_INVALIDATED, "The number of sessions invalidated by invoking invalidate", "operations"),
+        f.createIntCounter(SESSIONS_EXPIRED, "The number of sessions invalidated by timeout", "operations"),
+      }
+    );
+
+    // Initialize id fields
+    sessionsCreatedId = type.nameToId(SESSIONS_CREATED);
+    sessionsInvalidatedId = type.nameToId(SESSIONS_INVALIDATED);
+    sessionsExpiredId = type.nameToId(SESSIONS_EXPIRED);
+  }
+
+  private final Statistics stats;
+
+  public DeltaSessionStatistics(StatisticsFactory factory, String applicationName) {
+    this.stats = factory.createAtomicStatistics(type, typeName + "_" + applicationName);
+  }
+
+  public void close() {
+    this.stats.close();
+  }
+
+  public int getSessionsCreated() {
+    return this.stats.getInt(sessionsCreatedId);
+  }
+
+  public void incSessionsCreated() {
+    this.stats.incInt(sessionsCreatedId, 1);
+  }
+
+  public int getSessionsInvalidated() {
+    return this.stats.getInt(sessionsInvalidatedId);
+  }
+
+  public void incSessionsInvalidated() {
+    this.stats.incInt(sessionsInvalidatedId, 1);
+  }
+
+  public int getSessionsExpired() {
+    return this.stats.getInt(sessionsExpiredId);
+  }
+
+  public void incSessionsExpired() {
+    this.stats.incInt(sessionsExpiredId, 1);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionUpdateAttributeEvent.java
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionUpdateAttributeEvent.java b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionUpdateAttributeEvent.java
new file mode 100644
index 0000000..93fdac4
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/session/catalina/internal/DeltaSessionUpdateAttributeEvent.java
@@ -0,0 +1,75 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.modules.session.catalina.internal;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import com.gemstone.gemfire.DataSerializable;
+import com.gemstone.gemfire.DataSerializer;
+import com.gemstone.gemfire.Instantiator;
+import com.gemstone.gemfire.modules.session.catalina.DeltaSession;
+
+@SuppressWarnings("serial")
+public class DeltaSessionUpdateAttributeEvent implements DeltaSessionAttributeEvent {
+
+  private String attributeName;
+
+  private Object attributeValue;
+  
+  public DeltaSessionUpdateAttributeEvent() {
+  }
+
+  public DeltaSessionUpdateAttributeEvent(String attributeName, Object attributeValue) {
+    this.attributeName = attributeName;
+    this.attributeValue = attributeValue;
+  }
+  
+  public String getAttributeName() {
+    return this.attributeName;
+  }
+
+  public Object getAttributeValue() {
+    return this.attributeValue;
+  }
+
+  public void apply(DeltaSession session) {
+    session.localUpdateAttribute(this.attributeName, this.attributeValue);
+  }
+
+  public void fromData(DataInput in) throws IOException, ClassNotFoundException {
+    this.attributeName = DataSerializer.readString(in);
+    this.attributeValue = DataSerializer.readObject(in);
+  }
+
+  public void toData(DataOutput out) throws IOException {
+    DataSerializer.writeString(this.attributeName, out);
+    DataSerializer.writeObject(this.attributeValue, out);
+  }
+  
+  public static void registerInstantiator(int id) {
+    Instantiator.register(new Instantiator(DeltaSessionUpdateAttributeEvent.class, id) {
+      public DataSerializable newInstance() {
+        return new DeltaSessionUpdateAttributeEvent();
+      }
+    });
+  }
+  
+  public String toString() {
+    return new StringBuilder()
+      .append("DeltaSessionUpdateAttributeEvent[")
+      .append("attributeName=")
+      .append(this.attributeName)
+      .append("; attributeValue=")
+      .append(this.attributeValue)
+      .append("]")
+      .toString();
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/util/Banner.java
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/util/Banner.java b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/util/Banner.java
new file mode 100644
index 0000000..9e3332d
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/util/Banner.java
@@ -0,0 +1,49 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.modules.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Properties;
+
+import com.gemstone.gemfire.internal.GemFireVersion;
+
+public class Banner {
+
+  private static String VERSION = "unknown";
+
+  private static Properties props = new Properties();
+
+  static {
+    InputStream is = Banner.class.getResourceAsStream("/modules-version.properties");
+    try {
+      props.load(is);
+      VERSION = props.getProperty("version");
+    } catch (IOException e) {
+    }
+  }
+	
+  public static String getString() {
+    StringWriter sw = new StringWriter();
+    PrintWriter pw = new PrintWriter(sw);
+    print(pw);
+    pw.close();
+    return sw.toString();
+  }
+  
+  private static void print(PrintWriter pw) {
+    pw.println("GemFire Modules");
+    pw.print("Modules version: ");
+    pw.println(VERSION);
+    GemFireVersion.print(pw);
+  }
+  
+  private Banner() {}
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4b56f5e4/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/util/BootstrappingFunction.java
----------------------------------------------------------------------
diff --git a/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/util/BootstrappingFunction.java b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/util/BootstrappingFunction.java
new file mode 100644
index 0000000..d749d2e
--- /dev/null
+++ b/modules/gemfire-modules/src/main/java/com/gemstone/gemfire/modules/util/BootstrappingFunction.java
@@ -0,0 +1,180 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.modules.util;
+
+import com.gemstone.gemfire.cache.Cache;
+import com.gemstone.gemfire.cache.CacheFactory;
+import com.gemstone.gemfire.cache.execute.Execution;
+import com.gemstone.gemfire.cache.execute.Function;
+import com.gemstone.gemfire.cache.execute.FunctionContext;
+import com.gemstone.gemfire.cache.execute.FunctionService;
+import com.gemstone.gemfire.cache.execute.ResultCollector;
+import com.gemstone.gemfire.distributed.internal.DM;
+import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem;
+import com.gemstone.gemfire.distributed.internal.MembershipListener;
+import com.gemstone.gemfire.distributed.internal.membership.InternalDistributedMember;
+
+import java.util.List;
+import java.util.Set;
+
+public class BootstrappingFunction implements Function, MembershipListener {
+
+  private static final long serialVersionUID = 1856043174458190605L;
+    
+  public static final String ID = "bootstrapping-function";
+  
+  private static final int TIME_TO_WAIT_FOR_CACHE = Integer.getInteger("gemfiremodules.timeToWaitForCache", 30000);
+  
+  @Override
+  public void execute(FunctionContext context) {
+    // Verify that the cache exists before continuing.
+    // When this function is executed by a remote membership listener, it is
+    // being invoked before the cache is started.
+    Cache cache = verifyCacheExists();
+    
+    // Register as membership listener
+    registerAsMembershipListener(cache);
+
+    // Register functions
+    registerFunctions();
+    
+    // Return status
+    context.getResultSender().lastResult(Boolean.TRUE);
+  }
+  
+  private Cache verifyCacheExists() {
+    int timeToWait = 0;
+    Cache cache = null;
+    while (timeToWait < TIME_TO_WAIT_FOR_CACHE) {
+      try {
+        cache = CacheFactory.getAnyInstance();
+        break;
+      } catch (Exception ignore) {
+        //keep trying and hope for the best
+      }
+      try {
+        Thread.sleep(250);
+      } catch (InterruptedException ie) {
+        Thread.currentThread().interrupt();
+        break;
+      }
+      timeToWait += 250;
+    }
+    
+    if (cache == null) {
+      cache = new CacheFactory().create();
+    }
+    
+    return cache;
+  }
+
+  private void registerAsMembershipListener(Cache cache) {
+    DM dm = ((InternalDistributedSystem) cache.getDistributedSystem()).getDistributionManager();
+    dm.addMembershipListener(this);
+  }
+
+  private void registerFunctions() {
+    // Synchronize so that these functions aren't registered twice. The
+    // constructor for the CreateRegionFunction creates a meta region.
+    synchronized (ID) {
+      // Register the create region function if it is not already registered
+      if (!FunctionService.isRegistered(CreateRegionFunction.ID)) {
+        FunctionService.registerFunction(new CreateRegionFunction());
+      }
+      
+      // Register the touch partitioned region entries function if it is not already registered
+      if (!FunctionService.isRegistered(TouchPartitionedRegionEntriesFunction.ID)) {
+        FunctionService.registerFunction(new TouchPartitionedRegionEntriesFunction());
+      }
+      
+      // Register the touch replicated region entries function if it is not already registered
+      if (!FunctionService.isRegistered(TouchReplicatedRegionEntriesFunction.ID)) {
+        FunctionService.registerFunction(new TouchReplicatedRegionEntriesFunction());
+      }
+      
+      // Register the region size function if it is not already registered
+      if (!FunctionService.isRegistered(RegionSizeFunction.ID)) {
+      	FunctionService.registerFunction(new RegionSizeFunction());
+      }
+    }
+  }
+
+  private void bootstrapMember(InternalDistributedMember member) {
+    // Create and execute the function
+    Cache cache = CacheFactory.getAnyInstance();
+    Execution execution = FunctionService.onMember(cache.getDistributedSystem(), member);
+    ResultCollector collector = execution.execute(this);
+
+    // Get the result. Nothing is being done with it.
+    try {
+      collector.getResult();
+    } catch (Exception e) {
+      // If an exception occurs in the function, log it.
+      cache.getLogger().warning("Caught unexpected exception:", e);
+    }
+  }
+
+  @Override
+  public String getId() {
+    return ID;
+  }
+
+  @Override
+  public boolean hasResult() {
+    return true;
+  }
+
+  @Override
+  public boolean isHA() {
+    return false;
+  }
+
+  @Override
+  public boolean optimizeForWrite() {
+    return false;
+  }
+  
+  public int hashCode() {
+    // This method is only implemented so that multiple instances of this class
+    // don't get added as membership listeners.
+    return ID.hashCode();
+  }
+  
+  public boolean equals(Object obj) {
+    // This method is only implemented so that multiple instances of this class
+    // don't get added as membership listeners.
+    if (this == obj) {
+      return true;
+    }
+
+    if (obj == null || !(obj instanceof BootstrappingFunction)) {
+      return false;
+    }
+    
+    return true;
+  }
+
+  @Override
+  public void memberDeparted(InternalDistributedMember id, boolean crashed) {
+  }
+
+  @Override
+  public void memberJoined(InternalDistributedMember id) {
+    bootstrapMember(id);
+  }
+
+  @Override
+  public void memberSuspect(InternalDistributedMember id,
+      InternalDistributedMember whoSuspected) {
+  }
+
+  @Override
+  public void quorumLost(Set<InternalDistributedMember> internalDistributedMembers,
+      List<InternalDistributedMember> internalDistributedMembers2) {
+  }
+}



Mime
View raw message