You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by kl...@apache.org on 2016/02/08 22:58:50 UTC
[23/50] [abbrv] incubator-geode git commit: GEODE-14: Integration of
GemFire Session Replication and Hibernate modules
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/ClientServerSessionCache.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/ClientServerSessionCache.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/ClientServerSessionCache.java
new file mode 100644
index 0000000..a23888c
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/ClientServerSessionCache.java
@@ -0,0 +1,186 @@
+/*
+* 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 com.gemstone.gemfire.modules.session.internal.common;
+
+import com.gemstone.gemfire.cache.GemFireCache;
+import com.gemstone.gemfire.cache.Region;
+import com.gemstone.gemfire.cache.RegionShortcut;
+import com.gemstone.gemfire.cache.client.ClientCache;
+import com.gemstone.gemfire.cache.client.ClientRegionFactory;
+import com.gemstone.gemfire.cache.client.ClientRegionShortcut;
+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.util.BootstrappingFunction;
+import com.gemstone.gemfire.modules.util.CreateRegionFunction;
+import com.gemstone.gemfire.modules.util.RegionConfiguration;
+import com.gemstone.gemfire.modules.util.RegionStatus;
+
+import java.util.List;
+import java.util.Map;
+import javax.servlet.http.HttpSession;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class which defines a client/server cache.
+ */
+public class ClientServerSessionCache extends AbstractSessionCache {
+
+ private static final Logger LOG =
+ LoggerFactory.getLogger(PeerToPeerSessionCache.class.getName());
+
+ private ClientCache cache;
+
+ protected static final String DEFAULT_REGION_ATTRIBUTES_ID =
+ RegionShortcut.PARTITION_REDUNDANT.toString();
+
+ protected static final Boolean DEFAULT_ENABLE_LOCAL_CACHE = true;
+
+ /**
+ * Constructor
+ *
+ * @param cache
+ * @param properties
+ */
+ public ClientServerSessionCache(ClientCache cache,
+ Map<CacheProperty, Object> properties) {
+ super();
+ this.cache = cache;
+
+ /**
+ * Set some default properties for this cache if they haven't already
+ * been set
+ */
+ this.properties.put(CacheProperty.REGION_ATTRIBUTES_ID,
+ DEFAULT_REGION_ATTRIBUTES_ID);
+ this.properties.put(CacheProperty.ENABLE_LOCAL_CACHE,
+ DEFAULT_ENABLE_LOCAL_CACHE);
+ this.properties.putAll(properties);
+ }
+
+ @Override
+ public void initialize() {
+ // Bootstrap the servers
+ bootstrapServers();
+
+ // Create or retrieve the region
+ createOrRetrieveRegion();
+
+ // Set the session region directly as the operating region since there is no difference
+ // between the local cache region and the session region.
+ operatingRegion = sessionRegion;
+
+ // Create or retrieve the statistics
+ createStatistics();
+ }
+
+ @Override
+ public GemFireCache getCache() {
+ return cache;
+ }
+
+ @Override
+ public boolean isClientServer() {
+ return true;
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////
+ // Private methods
+
+ private void bootstrapServers() {
+ Execution execution = FunctionService.onServers(this.cache);
+ ResultCollector collector = execution.execute(new BootstrappingFunction());
+ // Get the result. Nothing is being done with it.
+ try {
+ collector.getResult();
+ } catch (Exception e) {
+ // If an exception occurs in the function, log it.
+ LOG.warn("Caught unexpected exception:", e);
+ }
+ }
+
+ private void createOrRetrieveRegion() {
+ // Retrieve the local session region
+ this.sessionRegion =
+ this.cache.getRegion(
+ (String) properties.get(CacheProperty.REGION_NAME));
+
+ // If necessary, create the regions on the server and client
+ if (this.sessionRegion == null) {
+ // Create the PR on the servers
+ createSessionRegionOnServers();
+
+ // Create the region on the client
+ this.sessionRegion = createLocalSessionRegion();
+ LOG.debug("Created session region: " + this.sessionRegion);
+ } else {
+ LOG.debug("Retrieved session region: " + this.sessionRegion);
+ }
+ }
+
+ private void createSessionRegionOnServers() {
+ // Create the RegionConfiguration
+ RegionConfiguration configuration = createRegionConfiguration();
+
+ // Send it to the server tier
+ Execution execution = FunctionService.onServer(this.cache).withArgs(
+ configuration);
+ ResultCollector collector = execution.execute(CreateRegionFunction.ID);
+
+ // Verify the region was successfully created on the servers
+ List<RegionStatus> results = (List<RegionStatus>) collector.getResult();
+ for (RegionStatus status : results) {
+ if (status == RegionStatus.INVALID) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(
+ "An exception occurred on the server while attempting to create or validate region named ");
+ builder.append(properties.get(CacheProperty.REGION_NAME));
+ builder.append(". See the server log for additional details.");
+ throw new IllegalStateException(builder.toString());
+ }
+ }
+ }
+
+ private Region<String, HttpSession> createLocalSessionRegion() {
+ ClientRegionFactory<String, HttpSession> factory = null;
+ boolean enableLocalCache =
+ (Boolean) properties.get(CacheProperty.ENABLE_LOCAL_CACHE);
+
+ String regionName = (String) properties.get(CacheProperty.REGION_NAME);
+ if (enableLocalCache) {
+ // Create the region factory with caching and heap LRU enabled
+ factory = ((ClientCache) this.cache).
+ createClientRegionFactory(
+ ClientRegionShortcut.CACHING_PROXY_HEAP_LRU);
+ LOG.info("Created new local client session region: {}", regionName);
+ } else {
+ // Create the region factory without caching enabled
+ factory = ((ClientCache) this.cache).createClientRegionFactory(
+ ClientRegionShortcut.PROXY);
+ LOG.info(
+ "Created new local client (uncached) session region: {} without any session expiry",
+ regionName);
+ }
+
+ // Create the region
+ return factory.create(regionName);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/PeerToPeerSessionCache.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/PeerToPeerSessionCache.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/PeerToPeerSessionCache.java
new file mode 100644
index 0000000..878adaa
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/PeerToPeerSessionCache.java
@@ -0,0 +1,184 @@
+/*
+* 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 com.gemstone.gemfire.modules.session.internal.common;
+
+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.FunctionService;
+import com.gemstone.gemfire.modules.session.catalina.callback.LocalSessionCacheLoader;
+import com.gemstone.gemfire.modules.session.catalina.callback.LocalSessionCacheWriter;
+import com.gemstone.gemfire.modules.util.RegionConfiguration;
+import com.gemstone.gemfire.modules.util.RegionHelper;
+import com.gemstone.gemfire.modules.util.TouchPartitionedRegionEntriesFunction;
+import com.gemstone.gemfire.modules.util.TouchReplicatedRegionEntriesFunction;
+
+import java.util.Map;
+import javax.servlet.http.HttpSession;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class which defines a peer-to-peer cache
+ */
+public class PeerToPeerSessionCache extends AbstractSessionCache {
+
+ private static final Logger LOG =
+ LoggerFactory.getLogger(PeerToPeerSessionCache.class.getName());
+
+ private Cache cache;
+
+ private static final String DEFAULT_REGION_ATTRIBUTES_ID =
+ RegionShortcut.REPLICATE.toString();
+
+ private static final Boolean DEFAULT_ENABLE_LOCAL_CACHE = false;
+
+ /**
+ * Constructor
+ *
+ * @param cache
+ * @param properties
+ */
+ public PeerToPeerSessionCache(Cache cache,
+ Map<CacheProperty, Object> properties) {
+ super();
+ this.cache = cache;
+
+ /**
+ * Set some default properties for this cache if they haven't already
+ * been set
+ */
+ this.properties.put(CacheProperty.REGION_ATTRIBUTES_ID,
+ DEFAULT_REGION_ATTRIBUTES_ID);
+ this.properties.put(CacheProperty.ENABLE_LOCAL_CACHE,
+ DEFAULT_ENABLE_LOCAL_CACHE);
+ this.properties.putAll(properties);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @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.
+ */
+ boolean enableLocalCache =
+ (Boolean) properties.get(CacheProperty.ENABLE_LOCAL_CACHE);
+ operatingRegion = enableLocalCache
+ ? createOrRetrieveLocalRegion()
+ : this.sessionRegion;
+
+ // Create or retrieve the statistics
+ createStatistics();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public GemFireCache getCache() {
+ return cache;
+ }
+
+ @Override
+ public boolean isClientServer() {
+ return false;
+ }
+
+ 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());
+ }
+ }
+
+ private void createOrRetrieveRegion() {
+ // Create the RegionConfiguration
+ RegionConfiguration configuration = createRegionConfiguration();
+
+ // Attempt to retrieve the region
+ // If it already exists, validate it
+ // If it doesn't already exist, create it
+ Region region = this.cache.getRegion(
+ (String) properties.get(CacheProperty.REGION_NAME));
+ if (region == null) {
+ // Create the region
+ region = RegionHelper.createRegion(cache, configuration);
+ LOG.info("Created new session region: {}", region);
+ } else {
+ // Validate the existing region
+ LOG.info("Retrieved existing session region: {}", region);
+ RegionHelper.validateRegion(cache, configuration, region);
+ }
+
+ // Set the session region
+ this.sessionRegion = region;
+ }
+
+ /**
+ * Create a local region fronting the main region.
+ *
+ * @return
+ */
+ 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));
+
+ // Create the region
+ frontingRegion = factory.create(frontingRegionName);
+ LOG.info("Created new local session region: {}", frontingRegion);
+ } else {
+ LOG.info("Retrieved existing local session region: {}",
+ frontingRegion);
+ }
+
+ return frontingRegion;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/SessionCache.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/SessionCache.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/SessionCache.java
new file mode 100644
index 0000000..7562dff
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/SessionCache.java
@@ -0,0 +1,68 @@
+/*
+* 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 com.gemstone.gemfire.modules.session.internal.common;
+
+import com.gemstone.gemfire.cache.GemFireCache;
+import com.gemstone.gemfire.cache.Region;
+
+import javax.servlet.http.HttpSession;
+
+/**
+ * Interface to basic cache operations.
+ */
+public interface SessionCache {
+
+ /**
+ * Initialize the cache and create the appropriate region.
+ */
+ public void initialize();
+
+ /**
+ * Stop the cache.
+ */
+ public void stop();
+
+ /**
+ * Retrieve the cache reference.
+ *
+ * @return a {@code GemFireCache} reference
+ */
+ public GemFireCache getCache();
+
+ /**
+ * Get the {@code Region} being used by client code to put attributes.
+ *
+ * @return a {@code Region<String, HttpSession>} reference
+ */
+ public Region<String, HttpSession> getOperatingRegion();
+
+ /**
+ * Get the backing {@code Region} being used. This may not be the same as the
+ * region being used by client code to put attributes.
+ *
+ * @return a {@code Region<String, HttpSession>} reference
+ */
+ public Region<String, HttpSession> getSessionRegion();
+
+ /**
+ * Is this cache client-server? The only other alternative is peer-to-peer.
+ *
+ * @return true if this cache is client-server.
+ */
+ public boolean isClientServer();
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/SessionExpirationCacheListener.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/SessionExpirationCacheListener.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/SessionExpirationCacheListener.java
new file mode 100644
index 0000000..648e711
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/SessionExpirationCacheListener.java
@@ -0,0 +1,53 @@
+/*
+* 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 com.gemstone.gemfire.modules.session.internal.common;
+
+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 java.util.Properties;
+import javax.servlet.http.HttpSession;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SessionExpirationCacheListener extends
+ CacheListenerAdapter<String, HttpSession> implements Declarable {
+
+ private static final Logger LOG =
+ LoggerFactory.getLogger(SessionExpirationCacheListener.class.getName());
+
+ @Override
+ 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.
+ */
+ if (event.getOperation() == Operation.EXPIRE_DESTROY) {
+ HttpSession session = (HttpSession) event.getOldValue();
+ session.invalidate();
+ }
+ }
+
+ @Override
+ public void init(Properties p) {
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/Constants.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/Constants.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/Constants.java
new file mode 100644
index 0000000..4ce8733
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/Constants.java
@@ -0,0 +1,30 @@
+/*
+ * 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 com.gemstone.gemfire.modules.session.internal.filter;
+
+/**
+ * Various constant values used through the app
+ */
+public class Constants {
+
+ public static String GEMFIRE_SESSION_REQUEST = "_gemfire_session_request_";
+
+ public static String SESSION_STATISTICS_MBEAN_NAME =
+ "com.gemstone:type=SessionStatistics,name=sessionStatistics";
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/DummySessionManager.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/DummySessionManager.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/DummySessionManager.java
new file mode 100644
index 0000000..9628912
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/DummySessionManager.java
@@ -0,0 +1,132 @@
+/*
+* 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 com.gemstone.gemfire.modules.session.internal.filter;
+
+import com.gemstone.gemfire.modules.session.internal.filter.attributes.AbstractSessionAttributes;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import javax.servlet.http.HttpSession;
+
+/**
+ * Class which fakes an in-memory basic session manager for testing purposes.
+ */
+public class DummySessionManager implements SessionManager {
+
+ /**
+ * Map of sessions
+ */
+ private final Map<String, HttpSession> sessions =
+ new HashMap<String, HttpSession>();
+
+ private class Attributes extends AbstractSessionAttributes {
+
+ @Override
+ public Object putAttribute(String attr, Object value) {
+ return attributes.put(attr, value);
+ }
+
+ @Override
+ public Object removeAttribute(String attr) {
+ return attributes.remove(attr);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void start(Object config, ClassLoader loader) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void stop() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public HttpSession getSession(String id) {
+ return sessions.get(id);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public HttpSession wrapSession(HttpSession nativeSession) {
+ String id = generateId();
+ AbstractSessionAttributes attributes = new Attributes();
+ GemfireHttpSession session = new GemfireHttpSession(id, nativeSession);
+ session.setManager(this);
+ session.setAttributes(attributes);
+ sessions.put(id, session);
+
+ return session;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public HttpSession getWrappingSession(String nativeId) {
+ return sessions.get(nativeId);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void putSession(HttpSession session) {
+ // shouldn't ever get called
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void destroySession(String id) {
+ sessions.remove(id);
+ }
+
+ @Override
+ public String destroyNativeSession(String id) {
+ return null;
+ }
+
+ public String getSessionCookieName() {
+ return "JSESSIONID";
+ }
+
+ public String getJvmId() {
+ return "jvm-id";
+ }
+
+ /**
+ * Generate an ID string
+ */
+ private String generateId() {
+ return UUID.randomUUID().toString().toUpperCase() + "-GF";
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/GemfireHttpSession.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/GemfireHttpSession.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/GemfireHttpSession.java
new file mode 100644
index 0000000..695a03b
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/GemfireHttpSession.java
@@ -0,0 +1,526 @@
+/*
+* 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 com.gemstone.gemfire.modules.session.internal.filter;
+
+import com.gemstone.gemfire.DataSerializable;
+import com.gemstone.gemfire.DataSerializer;
+import com.gemstone.gemfire.Delta;
+import com.gemstone.gemfire.Instantiator;
+import com.gemstone.gemfire.InvalidDeltaException;
+import com.gemstone.gemfire.modules.session.internal.filter.attributes.AbstractSessionAttributes;
+import com.gemstone.gemfire.modules.session.internal.filter.attributes.SessionAttributes;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionContext;
+
+import com.gemstone.gemfire.modules.util.ClassLoaderObjectInputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class which implements a Gemfire persisted {@code HttpSession}
+ */
+public class GemfireHttpSession implements
+ HttpSession, DataSerializable, Delta {
+
+ private static transient final Logger LOG =
+ LoggerFactory.getLogger(GemfireHttpSession.class.getName());
+
+ /**
+ * Serial id
+ */
+ private static final long serialVersionUID = 238915238964017823L;
+
+ /**
+ * Id for the session
+ */
+ private String id;
+
+ /**
+ * Attributes really hold the essence of persistence.
+ */
+ private SessionAttributes attributes;
+
+ private transient SessionManager manager;
+
+ private HttpSession nativeSession = null;
+
+ /**
+ * A session becomes invalid if it is explicitly invalidated or if it
+ * expires.
+ */
+ private boolean isValid = true;
+
+ private boolean isNew = true;
+
+ private boolean isDirty = false;
+
+ /**
+ * This is set during serialization and then reset by the SessionManager when
+ * it is retrieved from the attributes.
+ */
+ private AtomicBoolean serialized = new AtomicBoolean(false);
+
+ /**
+ * Register ourselves for de-serialization
+ */
+ static {
+ Instantiator.register(new Instantiator(GemfireHttpSession.class, 27315) {
+ @Override
+ public DataSerializable newInstance() {
+ return new GemfireHttpSession();
+ }
+ });
+ }
+
+ /**
+ * Constructor used for de-serialization
+ */
+ private GemfireHttpSession() {
+ }
+
+ /**
+ * Constructor
+ */
+ public GemfireHttpSession(String id, HttpSession nativeSession) {
+ this();
+ this.id = id;
+ this.nativeSession = nativeSession;
+ if (nativeSession != null) {
+ attributes.setMaxInactiveInterval(nativeSession.getMaxInactiveInterval());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object getAttribute(String name) {
+ if (!isValid) {
+ throw new IllegalStateException("Session is already invalidated");
+ }
+ Object obj = attributes.getAttribute(name);
+
+ if (obj != null) {
+ Object tmpObj = null;
+ ClassLoader loader = ((GemfireSessionManager) manager).getReferenceClassLoader();
+
+ if (obj.getClass().getClassLoader() != loader) {
+ LOG.debug(
+ "Attribute '{}' needs to be reconstructed with a new classloader",
+ name);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(obj);
+ oos.close();
+
+ ObjectInputStream ois = new ClassLoaderObjectInputStream(
+ new ByteArrayInputStream(baos.toByteArray()),
+ loader);
+ tmpObj = ois.readObject();
+ } catch (IOException e) {
+ LOG.error("Exception while recreating attribute '" + name +
+ "'", e);
+ } catch (ClassNotFoundException e) {
+ LOG.error("Exception while recreating attribute '" + name +
+ "'", e);
+ }
+ if (tmpObj != null) {
+ setAttribute(name, tmpObj);
+ obj = tmpObj;
+ }
+ }
+ }
+
+ return obj;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Enumeration getAttributeNames() {
+ if (!isValid) {
+ throw new IllegalStateException("Session is already invalidated");
+ }
+ return Collections.enumeration(attributes.getAttributeNames());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public long getCreationTime() {
+ if (nativeSession != null) {
+ return nativeSession.getCreationTime();
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public long getLastAccessedTime() {
+ if (!isValid) {
+ throw new IllegalStateException("Session is already invalidated");
+ }
+ return attributes.getLastAccessedTime();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ServletContext getServletContext() {
+ if (nativeSession != null) {
+ return nativeSession.getServletContext();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public HttpSessionContext getSessionContext() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object getValue(String name) {
+ return getAttribute(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String[] getValueNames() {
+ return attributes.getAttributeNames().toArray(new String[0]);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void invalidate() {
+ nativeSession.invalidate();
+ manager.destroySession(id);
+ isValid = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isNew() {
+ if (!isValid) {
+ throw new IllegalStateException("Session is already invalidated");
+ }
+ return isNew;
+ }
+
+ public void setIsNew(boolean isNew) {
+ this.isNew = isNew;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setMaxInactiveInterval(int interval) {
+ if (nativeSession != null) {
+ nativeSession.setMaxInactiveInterval(interval);
+ }
+ attributes.setMaxInactiveInterval(interval);
+ isDirty = true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getMaxInactiveInterval() {
+ if (nativeSession != null) {
+ return nativeSession.getMaxInactiveInterval();
+ } else {
+ return attributes.getMaxIntactiveInterval();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void putValue(String name, Object value) {
+ setAttribute(name, value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void removeAttribute(final String name) {
+ LOG.debug("Session {} removing attribute {}", getId(), name);
+ nativeSession.removeAttribute(name);
+ attributes.removeAttribute(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void removeValue(String name) {
+ removeAttribute(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setAttribute(final String name, final Object value) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Session {} setting attribute {} = '{}'",
+ new Object[]{id, name, value});
+ }
+
+ isDirty = true;
+ nativeSession.setAttribute(name, value);
+ if (value == null) {
+ removeAttribute(name);
+ } else {
+ attributes.putAttribute(name, value);
+ }
+ }
+
+ /**
+ * Gemfire serialization {@inheritDoc}
+ */
+ @Override
+ public void toData(DataOutput out) throws IOException {
+ DataSerializer.writeString(id, out);
+ DataSerializer.writeObject(attributes, out);
+ }
+
+ /**
+ * Gemfire de-serialization {@inheritDoc}
+ */
+ @Override
+ public void fromData(DataInput in) throws IOException,
+ ClassNotFoundException {
+ id = DataSerializer.readString(in);
+ attributes = DataSerializer.readObject(in);
+ if (getNativeSession() != null) {
+ for (String s : attributes.getAttributeNames()) {
+ getNativeSession().setAttribute(s, attributes.getAttribute(s));
+ }
+ }
+
+ // Explicit sets
+ serialized.set(true);
+ attributes.setSession(this);
+ }
+
+ /**
+ * These three methods handle delta propagation and are deferred to the
+ * attribute object.
+ */
+ @Override
+ public boolean hasDelta() {
+ return isDirty;
+ }
+
+ @Override
+ public void toDelta(DataOutput out) throws IOException {
+ if (attributes instanceof Delta) {
+ ((Delta) attributes).toDelta(out);
+ } else {
+ toData(out);
+ }
+ }
+
+ @Override
+ public void fromDelta(DataInput in) throws IOException,
+ InvalidDeltaException {
+ if (attributes instanceof Delta) {
+ ((Delta) attributes).fromDelta(in);
+ } else {
+ try {
+ fromData(in);
+ } catch (ClassNotFoundException cex) {
+ throw new IOException("Unable to forward fromDelta() call "
+ + "to fromData()", cex);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("[id=").append(id)
+ .append(", isNew=").append(isNew)
+ .append(", isValid=").append(isValid)
+ .append(", hasDelta=").append(hasDelta())
+ .append(", lastAccessedTime=").append(attributes.getLastAccessedTime())
+ .append(", jvmOwnerId=").append(attributes.getJvmOwnerId());
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /**
+ * Flush the session object to the region
+ */
+ public void putInRegion() {
+
+ manager.putSession(this);
+ isDirty = false;
+ }
+
+ /**
+ * Determine whether the session is still valid or whether it has expired.
+ *
+ * @return true or false
+ */
+ public boolean isValid() {
+ if (!isValid) {
+ return false;
+ }
+ if (getMaxInactiveInterval() >= 0) {
+ long now = System.currentTimeMillis();
+ if (now - attributes.getLastAccessedTime() >= getMaxInactiveInterval() * 1000) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Is this session dirty and should it be written to cache
+ */
+ public boolean isDirty() {
+ return isDirty;
+ }
+
+ public void setManager(SessionManager manager) {
+ this.manager = manager;
+ }
+
+ /**
+ * For testing allow retrieval of the wrapped, native session.
+ */
+ public HttpSession getNativeSession() {
+ return nativeSession;
+ }
+
+
+ public void setNativeSession(HttpSession session) {
+ this.nativeSession = session;
+ }
+
+ /**
+ * Handle the process of failing over the session to a new native session
+ * object.
+ *
+ * @param session
+ */
+ public void failoverSession(HttpSession session) {
+ LOG.debug("Failing over session {} to {}", getId(), session.getId());
+ setNativeSession(session);
+ for (String name : attributes.getAttributeNames()) {
+ LOG.debug("Copying '{}' => {}", name, attributes.getAttribute(name));
+ session.setAttribute(name, attributes.getAttribute(name));
+ }
+ session.setMaxInactiveInterval(attributes.getMaxIntactiveInterval());
+ manager.putSession(this);
+ }
+
+
+ /**
+ * Update the last accessed time
+ */
+ public void updateAccessTime() {
+ attributes.setLastAccessedTime(System.currentTimeMillis());
+ }
+
+ /**
+ * The {@code SessionManager} injects this when creating a new session.
+ *
+ * @param attributes
+ */
+ public void setAttributes(AbstractSessionAttributes attributes) {
+ this.attributes = attributes;
+ }
+
+ /**
+ * This is called on deserialization. You can only call it once to get a
+ * meaningful value as it resets the serialized state. In other words, this
+ * call is not idempotent.
+ *
+ * @return whether this object has just been serialized
+ */
+ public boolean justSerialized() {
+ return serialized.getAndSet(false);
+ }
+
+ /**
+ * Called when the session is about to go out of scope. If the session has
+ * been defined to use async queued attributes then they will be written out
+ * at this point.
+ */
+ public void commit() {
+ attributes.setJvmOwnerId(manager.getJvmId());
+ attributes.flush();
+ }
+
+ public String getJvmOwnerId() {
+ if (attributes != null) {
+ return attributes.getJvmOwnerId();
+ }
+
+ return null;
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/GemfireSessionException.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/GemfireSessionException.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/GemfireSessionException.java
new file mode 100644
index 0000000..3ce81be
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/GemfireSessionException.java
@@ -0,0 +1,41 @@
+/*
+* 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 com.gemstone.gemfire.modules.session.internal.filter;
+
+/**
+ * Exception class for Gemfire Session Cache specific exceptions.
+ */
+public class GemfireSessionException extends Exception {
+
+ public GemfireSessionException() {
+ super();
+ }
+
+ public GemfireSessionException(String message) {
+ super(message);
+ }
+
+ public GemfireSessionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public GemfireSessionException(Throwable cause) {
+ super(cause);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/GemfireSessionManager.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/GemfireSessionManager.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/GemfireSessionManager.java
new file mode 100644
index 0000000..a3d3c10
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/GemfireSessionManager.java
@@ -0,0 +1,511 @@
+/*
+* 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 com.gemstone.gemfire.modules.session.internal.filter;
+
+import com.gemstone.gemfire.cache.CacheClosedException;
+import com.gemstone.gemfire.cache.CacheFactory;
+import com.gemstone.gemfire.cache.EntryNotFoundException;
+import com.gemstone.gemfire.cache.control.ResourceManager;
+import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
+import com.gemstone.gemfire.modules.session.bootstrap.AbstractCache;
+import com.gemstone.gemfire.modules.session.bootstrap.ClientServerCache;
+import com.gemstone.gemfire.modules.session.bootstrap.LifecycleTypeAdapter;
+import com.gemstone.gemfire.modules.session.bootstrap.PeerToPeerCache;
+import com.gemstone.gemfire.modules.session.internal.common.CacheProperty;
+import com.gemstone.gemfire.modules.session.internal.common.ClientServerSessionCache;
+import com.gemstone.gemfire.modules.session.internal.common.PeerToPeerSessionCache;
+import com.gemstone.gemfire.modules.session.internal.common.SessionCache;
+import com.gemstone.gemfire.modules.session.internal.filter.attributes.AbstractSessionAttributes;
+import com.gemstone.gemfire.modules.session.internal.filter.attributes.DeltaQueuedSessionAttributes;
+import com.gemstone.gemfire.modules.session.internal.filter.attributes.DeltaSessionAttributes;
+import com.gemstone.gemfire.modules.session.internal.filter.attributes.ImmediateSessionAttributes;
+import com.gemstone.gemfire.modules.session.internal.filter.util.TypeAwareMap;
+import com.gemstone.gemfire.modules.session.internal.jmx.SessionStatistics;
+import com.gemstone.gemfire.modules.util.RegionHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.naming.InitialContext;
+import javax.servlet.FilterConfig;
+import javax.servlet.http.HttpSession;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * This class implements the session management using a Gemfire distributedCache
+ * as a persistent store for the session objects
+ */
+public class GemfireSessionManager implements SessionManager {
+
+ private final Logger LOG;
+
+ /**
+ * Prefix of init param string used to set gemfire properties
+ */
+ private static final String GEMFIRE_PROPERTY = "gemfire.property.";
+
+ /**
+ * Prefix of init param string used to set gemfire distributedCache setting
+ */
+ private static final String GEMFIRE_CACHE = "gemfire.cache.";
+
+ private static final String INIT_PARAM_CACHE_TYPE = "cache-type";
+ private static final String CACHE_TYPE_CLIENT_SERVER = "client-server";
+ private static final String CACHE_TYPE_PEER_TO_PEER = "peer-to-peer";
+ private static final String INIT_PARAM_SESSION_COOKIE_NAME = "session-cookie-name";
+ private static final String INIT_PARAM_JVM_ID = "jvm-id";
+ private static final String DEFAULT_JVM_ID = "default";
+
+ private SessionCache sessionCache = null;
+
+ /**
+ * Reference to the distributed system
+ */
+ private AbstractCache distributedCache = null;
+
+ /**
+ * Boolean indicating whether the manager is shutting down
+ */
+ private boolean isStopping = false;
+
+ /**
+ * Boolean indicating whether this manager is defined in the same context (war
+ * / classloader) as the filter.
+ */
+ private boolean isolated = false;
+
+ /**
+ * Map of wrapping GemFire session id to native session id
+ */
+ private Map<String, String> nativeSessionMap =
+ new HashMap<String, String>();
+
+ /**
+ * MBean for statistics
+ */
+ private SessionStatistics mbean;
+
+ /**
+ * This CL is used to compare against the class loader of attributes getting
+ * pulled out of the cache. This variable should be set to the CL of the
+ * filter running everything.
+ */
+ private ClassLoader referenceClassLoader;
+
+ private String sessionCookieName = "JSESSIONID";
+
+ /**
+ * Give this JVM a unique identifier.
+ */
+ private String jvmId = "default";
+
+ /**
+ * Set up properties with default values
+ */
+ private TypeAwareMap<CacheProperty, Object> properties =
+ new TypeAwareMap<CacheProperty, Object>(CacheProperty.class) {{
+ put(CacheProperty.REGION_NAME, RegionHelper.NAME + "_sessions");
+ put(CacheProperty.ENABLE_GATEWAY_DELTA_REPLICATION, Boolean.FALSE);
+ put(CacheProperty.ENABLE_GATEWAY_REPLICATION, Boolean.FALSE);
+ put(CacheProperty.ENABLE_DEBUG_LISTENER, Boolean.FALSE);
+ put(CacheProperty.STATISTICS_NAME, "gemfire_statistics");
+ put(CacheProperty.SESSION_DELTA_POLICY, "delta_queued");
+ put(CacheProperty.REPLICATION_TRIGGER, "set");
+ /**
+ * For REGION_ATTRIBUTES_ID and ENABLE_LOCAL_CACHE the default
+ * is different for ClientServerCache and PeerToPeerCache
+ * so those values are set in the relevant constructors when
+ * these properties are passed in to them.
+ */
+ }};
+
+ public GemfireSessionManager() {
+ LOG = LoggerFactory.getLogger(GemfireSessionManager.class.getName());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void start(Object conf, ClassLoader loader) {
+ this.referenceClassLoader = loader;
+ FilterConfig config = (FilterConfig) conf;
+
+ startDistributedSystem(config);
+ initializeSessionCache(config);
+
+ // Register MBean
+ registerMBean();
+
+ if (distributedCache.getClass().getClassLoader() == loader) {
+ isolated = true;
+ }
+
+ String sessionCookieName = config.getInitParameter(
+ INIT_PARAM_SESSION_COOKIE_NAME);
+ if (sessionCookieName != null && !sessionCookieName.isEmpty()) {
+ this.sessionCookieName = sessionCookieName;
+ LOG.info("Session cookie name set to: {}", this.sessionCookieName);
+ }
+
+ jvmId = config.getInitParameter(INIT_PARAM_JVM_ID);
+ if (jvmId == null || jvmId.isEmpty()) {
+ jvmId = DEFAULT_JVM_ID;
+ }
+
+ LOG.info("Started GemfireSessionManager (isolated={}, jvmId={})",
+ isolated, jvmId);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void stop() {
+ isStopping = true;
+
+ if (isolated) {
+ if (distributedCache != null) {
+ LOG.info("Closing distributed cache - assuming isolated cache");
+ distributedCache.close();
+ }
+ } else {
+ LOG.info("Not closing distributed cache - assuming common cache");
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public HttpSession getSession(String id) {
+ GemfireHttpSession session = (GemfireHttpSession) sessionCache.getOperatingRegion().get(
+ id);
+
+ if (session != null) {
+ if (session.justSerialized()) {
+ session.setManager(this);
+ LOG.debug("Recovered serialized session {} (jvmId={})", id,
+ session.getJvmOwnerId());
+ }
+ LOG.debug("Retrieved session id {}", id);
+ } else {
+ LOG.debug("Session id {} not found", id);
+ }
+ return session;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public HttpSession wrapSession(HttpSession nativeSession) {
+ String id = generateId();
+ GemfireHttpSession session =
+ new GemfireHttpSession(id, nativeSession);
+
+ /**
+ * Set up the attribute container depending on how things are configured
+ */
+ AbstractSessionAttributes attributes;
+ if ("delta_queued".equals(
+ properties.get(CacheProperty.SESSION_DELTA_POLICY))) {
+ attributes = new DeltaQueuedSessionAttributes();
+ ((DeltaQueuedSessionAttributes) attributes).setReplicationTrigger(
+ (String) properties.get(CacheProperty.REPLICATION_TRIGGER));
+ } else if ("delta_immediate".equals(
+ properties.get(CacheProperty.SESSION_DELTA_POLICY))) {
+ attributes = new DeltaSessionAttributes();
+ } else if ("immediate".equals(
+ properties.get(CacheProperty.SESSION_DELTA_POLICY))) {
+ attributes = new ImmediateSessionAttributes();
+ } else {
+ attributes = new DeltaSessionAttributes();
+ LOG.warn(
+ "No session delta policy specified - using default of 'delta_immediate'");
+ }
+
+ attributes.setSession(session);
+ attributes.setJvmOwnerId(jvmId);
+
+ session.setManager(this);
+ session.setAttributes(attributes);
+
+ LOG.debug("Creating new session {}", id);
+ sessionCache.getOperatingRegion().put(id, session);
+
+ mbean.incActiveSessions();
+
+ return session;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public HttpSession getWrappingSession(String nativeId) {
+ HttpSession session = null;
+ String gemfireId = getGemfireSessionIdFromNativeId(nativeId);
+
+ if (gemfireId != null) {
+ session = getSession(gemfireId);
+ }
+ return session;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void destroySession(String id) {
+ if (!isStopping) {
+ try {
+ GemfireHttpSession session = (GemfireHttpSession) sessionCache.getOperatingRegion().get(
+ id);
+ if (session != null && session.getJvmOwnerId().equals(jvmId)) {
+ LOG.debug("Destroying session {}", id);
+ sessionCache.getOperatingRegion().destroy(id);
+ mbean.decActiveSessions();
+ }
+ } catch (EntryNotFoundException nex) {
+ }
+ } else {
+ if (sessionCache.isClientServer()) {
+ LOG.debug("Destroying session {}", id);
+ try {
+ sessionCache.getOperatingRegion().localDestroy(id);
+ } catch (EntryNotFoundException nex) {
+ // Ignored
+ } catch (CacheClosedException ccex) {
+ // Ignored
+ }
+ } else {
+ GemfireHttpSession session = (GemfireHttpSession) sessionCache.getOperatingRegion().get(
+ id);
+ if (session != null) {
+ session.setNativeSession(null);
+ }
+ }
+ }
+
+ synchronized (nativeSessionMap) {
+ String nativeId = nativeSessionMap.remove(id);
+ LOG.debug("destroySession called for {} wrapping {}", id, nativeId);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void putSession(HttpSession session) {
+ sessionCache.getOperatingRegion().put(session.getId(), session);
+ mbean.incRegionUpdates();
+ nativeSessionMap.put(session.getId(),
+ ((GemfireHttpSession) session).getNativeSession().getId());
+ }
+
+ @Override
+ public String destroyNativeSession(String nativeId) {
+ String gemfireSessionId = getGemfireSessionIdFromNativeId(nativeId);
+ if (gemfireSessionId != null) {
+ destroySession(gemfireSessionId);
+ }
+ return gemfireSessionId;
+ }
+
+ public ClassLoader getReferenceClassLoader() {
+ return referenceClassLoader;
+ }
+
+ /**
+ * This method is called when a native session gets destroyed. It will check
+ * if the GemFire session is actually still valid/not expired and will then
+ * attach a new, native session.
+ *
+ * @param nativeId the id of the native session
+ * @return the id of the newly attached native session or null if the GemFire
+ * session was already invalid
+ */
+ public String refreshSession(String nativeId) {
+ String gemfireId = getGemfireSessionIdFromNativeId(nativeId);
+ if (gemfireId == null) {
+ return null;
+ }
+
+ GemfireHttpSession session = (GemfireHttpSession) sessionCache.getOperatingRegion().get(
+ gemfireId);
+ if (session.isValid()) {
+
+ }
+
+ return null;
+ }
+
+ public String getSessionCookieName() {
+ return sessionCookieName;
+ }
+
+ public String getJvmId() {
+ return jvmId;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // Private methods
+
+ private String getGemfireSessionIdFromNativeId(String nativeId) {
+ if (nativeId == null) {
+ return null;
+ }
+
+ for (Map.Entry<String, String> e : nativeSessionMap.entrySet()) {
+ if (nativeId.equals(e.getValue())) {
+ return e.getKey();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Start the underlying distributed system
+ *
+ * @param config
+ */
+ private void startDistributedSystem(FilterConfig config) {
+ // Get the distributedCache type
+ final String cacheType = config.getInitParameter(INIT_PARAM_CACHE_TYPE);
+ if (CACHE_TYPE_CLIENT_SERVER.equals(cacheType)) {
+ distributedCache = ClientServerCache.getInstance();
+ } else if (CACHE_TYPE_PEER_TO_PEER.equals(cacheType)) {
+ distributedCache = PeerToPeerCache.getInstance();
+ } else {
+ LOG.error("No 'cache-type' initialization param set. "
+ + "Cache will not be started");
+ return;
+ }
+
+ if (!distributedCache.isStarted()) {
+ /**
+ * Process all the init params and see if any apply to the
+ * distributed system.
+ */
+ for (Enumeration<String> e = config.getInitParameterNames(); e.hasMoreElements(); ) {
+ String param = e.nextElement();
+ if (!param.startsWith(GEMFIRE_PROPERTY)) {
+ continue;
+ }
+
+ String gemfireProperty = param.substring(GEMFIRE_PROPERTY.length());
+ LOG.info("Setting gemfire property: {} = {}",
+ gemfireProperty, config.getInitParameter(param));
+ distributedCache.setProperty(gemfireProperty,
+ config.getInitParameter(param));
+ }
+
+ distributedCache.lifecycleEvent(LifecycleTypeAdapter.START);
+ }
+ }
+
+ /**
+ * Initialize the distributedCache
+ */
+ private void initializeSessionCache(FilterConfig config) {
+ // Retrieve the distributedCache
+ 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.");
+ }
+
+ /**
+ * Process all the init params and see if any apply to the distributedCache
+ */
+ ResourceManager rm = cache.getResourceManager();
+ for (Enumeration<String> e = config.getInitParameterNames(); e.hasMoreElements(); ) {
+ String param = e.nextElement();
+
+ // Uggh - don't like this non-generic stuff
+ if (param.equalsIgnoreCase("criticalHeapPercentage")) {
+ float val = Float.parseFloat(config.getInitParameter(param));
+ rm.setCriticalHeapPercentage(val);
+ }
+
+ if (param.equalsIgnoreCase("evictionHeapPercentage")) {
+ float val = Float.parseFloat(config.getInitParameter(param));
+ rm.setEvictionHeapPercentage(val);
+ }
+
+
+ if (!param.startsWith(GEMFIRE_CACHE)) {
+ continue;
+ }
+
+ String gemfireWebParam = param.substring(GEMFIRE_CACHE.length());
+ LOG.info("Setting cache parameter: {} = {}",
+ gemfireWebParam, config.getInitParameter(param));
+ properties.put(CacheProperty.valueOf(gemfireWebParam.toUpperCase()),
+ config.getInitParameter(param));
+ }
+
+ // Create the appropriate session distributedCache
+ sessionCache = cache.isClient()
+ ? new ClientServerSessionCache(cache, properties)
+ : new PeerToPeerSessionCache(cache, properties);
+
+ // Initialize the session distributedCache
+ sessionCache.initialize();
+ }
+
+ /**
+ * Register a bean for statistic gathering purposes
+ */
+ private void registerMBean() {
+ mbean = new SessionStatistics();
+
+ try {
+ InitialContext ctx = new InitialContext();
+ MBeanServer mbs = MBeanServer.class.cast(
+ ctx.lookup("java:comp/env/jmx/runtime"));
+ ObjectName oname = new ObjectName(
+ Constants.SESSION_STATISTICS_MBEAN_NAME);
+
+ mbs.registerMBean(mbean, oname);
+ } catch (Exception ex) {
+ LOG.warn("Unable to register statistics MBean. Error: {}",
+ ex.getMessage());
+ }
+ }
+
+
+ /**
+ * Generate an ID string
+ */
+ private String generateId() {
+ return UUID.randomUUID().toString().toUpperCase() + "-GF";
+ }
+
+ AbstractCache getCache() {
+ return distributedCache;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/ListenerEventType.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/ListenerEventType.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/ListenerEventType.java
new file mode 100644
index 0000000..b040dda
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/ListenerEventType.java
@@ -0,0 +1,75 @@
+/*
+* 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 com.gemstone.gemfire.modules.session.internal.filter;
+
+/**
+ * Enumeration of all possible event types which can be listened for.
+ */
+public enum ListenerEventType {
+
+ /**
+ * HttpSessionAttributeListener
+ */
+ SESSION_ATTRIBUTE_ADDED,
+ SESSION_ATTRIBUTE_REMOVED,
+ SESSION_ATTRIBUTE_REPLACED,
+
+ /**
+ * HttpSessionBindingListener
+ */
+ SESSION_VALUE_BOUND,
+ SESSION_VALUE_UNBOUND,
+
+ /**
+ * HttpSessionListener
+ */
+ SESSION_CREATED,
+ SESSION_DESTROYED,
+
+ /**
+ * HttpSessionActivationListener
+ */
+ SESSION_WILL_ACTIVATE,
+ SESSION_DID_PASSIVATE,
+
+ /**
+ * ServletContextListener
+ */
+ SERVLET_CONTEXT_INITIALIZED,
+ SERVLET_CONTEXT_DESTROYED,
+
+ /**
+ * ServletContextAttributeListener
+ */
+ SERVLET_CONTEXT_ATTRIBUTE_ADDED,
+ SERVLET_CONTEXT_ATTRIBUTE_REMOVED,
+ SERVLET_CONTEXT_ATTRIBUTE_REPLACED,
+
+ /**
+ * ServletRequestListener
+ */
+ SERVLET_REQUEST_DESTROYED,
+ SERVLET_REQUEST_INITIALIZED,
+
+ /**
+ * ServletRequestAttributeListener
+ */
+ SERVLET_REQUEST_ATTRIBUTE_ADDED,
+ SERVLET_REQUEST_ATTRIBUTE_REMOVED,
+ SERVLET_REQUEST_ATTRIBUTE_REPLACED;
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/SessionManager.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/SessionManager.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/SessionManager.java
new file mode 100644
index 0000000..9d8996c
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/SessionManager.java
@@ -0,0 +1,110 @@
+/*
+* 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 com.gemstone.gemfire.modules.session.internal.filter;
+
+import javax.servlet.http.HttpSession;
+
+/**
+ * Interface to session management. This class would be responsible for creating
+ * new sessions.
+ */
+public interface SessionManager {
+
+ /**
+ * Start the manager possibly using the config passed in.
+ *
+ * @param config Config object specific to individual implementations.
+ * @param loader This is a hack. When the manager is started it wants to be
+ * able to determine if the cache, which it would create, and
+ * the filter which starts everything, are defined by the same
+ * classloader. This is so that during shutdown, the manager can
+ * decide whether or not to also stop the cache. This option
+ * allows the filter's classloader to be passed in.
+ */
+ public void start(Object config, ClassLoader loader);
+
+ /**
+ * Stop the session manager and free up any resources.
+ */
+ public void stop();
+
+ /**
+ * Write the session to the region
+ *
+ * @param session the session to write
+ */
+ public void putSession(HttpSession session);
+
+ /**
+ * Return a session if it exists or null otherwise
+ *
+ * @param id The session id to attempt to retrieve
+ * @return a HttpSession object if a session was found otherwise null.
+ */
+ public HttpSession getSession(String id);
+
+ /**
+ * Create a new session, wrapping a container session.
+ *
+ * @param nativeSession
+ * @return the HttpSession object
+ */
+ public HttpSession wrapSession(HttpSession nativeSession);
+
+ /**
+ * Get the wrapped (GemFire) session from a native session id. This method
+ * would typically be used from within session/http event listeners which
+ * receive the original session id.
+ *
+ * @param nativeId
+ * @return the wrapped GemFire session which maps the native session
+ */
+ public HttpSession getWrappingSession(String nativeId);
+
+ /**
+ * Destroy the session associated with the given id.
+ *
+ * @param id The id of the session to destroy.
+ */
+ public void destroySession(String id);
+
+ /**
+ * Destroy the session associated with a given native session
+ *
+ * @param id the id of the native session
+ * @return the corresponding Gemfire session which wrapped the native session
+ * and was destroyed.
+ */
+ public String destroyNativeSession(String id);
+
+ /**
+ * Returns the cookie name used to hold the session id. By default this is
+ * JSESSIONID.
+ *
+ * @return the name of the cookie which contains the session id
+ */
+ public String getSessionCookieName();
+
+ /**
+ * Get the JVM Id - this is a unique string used internally to identify who
+ * last touched a session.
+ *
+ * @return the jvm id
+ */
+ public String getJvmId();
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/AbstractDeltaSessionAttributes.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/AbstractDeltaSessionAttributes.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/AbstractDeltaSessionAttributes.java
new file mode 100644
index 0000000..f46495d
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/AbstractDeltaSessionAttributes.java
@@ -0,0 +1,107 @@
+/*
+* 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 com.gemstone.gemfire.modules.session.internal.filter.attributes;
+
+import com.gemstone.gemfire.DataSerializer;
+import com.gemstone.gemfire.Delta;
+import com.gemstone.gemfire.InvalidDeltaException;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This abstract class contains the structures and methods to handle delta
+ * updates to attributes.
+ */
+public abstract class AbstractDeltaSessionAttributes
+ extends AbstractSessionAttributes implements Delta {
+
+ private static final Logger LOG =
+ LoggerFactory.getLogger(AbstractDeltaSessionAttributes.class.getName());
+
+ /**
+ * This map holds the updates to attributes
+ */
+ protected transient Map<String, DeltaEvent> deltas =
+ Collections.synchronizedMap(new HashMap<String, DeltaEvent>());
+
+ @Override
+ public boolean hasDelta() {
+ return true;
+ }
+
+ @Override
+ public void toDelta(DataOutput out) throws IOException {
+ out.writeInt(maxInactiveInterval);
+ out.writeLong(lastAccessedTime);
+
+ synchronized (deltas) {
+ DataSerializer.writeInteger(deltas.size(), out);
+ for (Map.Entry<String, DeltaEvent> e : deltas.entrySet()) {
+ DataSerializer.writeString(e.getKey(), out);
+ DataSerializer.writeObject(e.getValue(), out);
+ }
+ deltas.clear();
+ }
+
+ out.writeUTF(jvmOwnerId);
+ }
+
+ @Override
+ public void fromDelta(DataInput in)
+ throws IOException, InvalidDeltaException {
+ maxInactiveInterval = in.readInt();
+ lastAccessedTime = in.readLong();
+ Map<String, DeltaEvent> localDeltas = new HashMap<String, DeltaEvent>();
+ try {
+ int size = DataSerializer.readInteger(in);
+ for (int i = 0; i < size; i++) {
+ String key = DataSerializer.readString(in);
+ DeltaEvent evt = DataSerializer.readObject(in);
+ localDeltas.put(key, evt);
+ }
+ } catch (ClassNotFoundException ex) {
+ LOG.error("Unable to de-serialize delta events", ex);
+ return;
+ }
+
+ LOG.debug("Processing {} delta events for {}",
+ localDeltas.size(), session);
+ for (DeltaEvent e : localDeltas.values()) {
+ if (e.isUpdate()) {
+ attributes.put(e.getName(), e.getValue());
+ if (session.getNativeSession() != null) {
+ session.getNativeSession().setAttribute(e.getName(), e.getValue());
+ }
+ } else {
+ attributes.remove(e.getName());
+ if (session.getNativeSession() != null) {
+ session.getNativeSession().setAttribute(e.getName(), null);
+ }
+ }
+ }
+ jvmOwnerId = in.readUTF();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/AbstractSessionAttributes.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/AbstractSessionAttributes.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/AbstractSessionAttributes.java
new file mode 100644
index 0000000..c4af041
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/AbstractSessionAttributes.java
@@ -0,0 +1,188 @@
+/*
+* 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 com.gemstone.gemfire.modules.session.internal.filter.attributes;
+
+import com.gemstone.gemfire.DataSerializer;
+import com.gemstone.gemfire.internal.util.BlobHelper;
+import com.gemstone.gemfire.modules.session.internal.filter.GemfireHttpSession;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract implementation for attributes. Should be sub-classed to provide
+ * differing implementations for synchronous or delta propagation. The backing
+ * store used is defined by the session manager.
+ */
+public abstract class AbstractSessionAttributes implements SessionAttributes {
+
+ private static final Logger LOG =
+ LoggerFactory.getLogger(AbstractSessionAttributes.class.getName());
+
+ /**
+ * Internal attribute store.
+ */
+ protected Map<String, Object> attributes =
+ Collections.synchronizedMap(new HashMap<String, Object>());
+
+ /**
+ * The session to which these attributes belong
+ */
+ protected transient GemfireHttpSession session;
+
+ /**
+ * The last accessed time
+ */
+ protected long lastAccessedTime;
+
+ /**
+ * The maximum inactive interval. Default is 1800 seconds.
+ */
+ protected int maxInactiveInterval = 60 * 30;
+
+ /**
+ * The JVM Id who last committed these attributes
+ */
+ protected String jvmOwnerId;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setSession(GemfireHttpSession session) {
+ this.session = session;
+ }
+
+ /**
+ * {@inheritDoc} The actual de-serialization of any domain objects is deferred
+ * until the point at which they are actually retrieved by the application
+ * layer.
+ */
+ @Override
+ public Object getAttribute(String name) {
+ Object value = attributes.get(name);
+
+ // If the value is a byte[] (meaning it came from the server),
+ // deserialize it and re-add it to attributes map before returning it.
+ if (value instanceof byte[]) {
+ try {
+ value = BlobHelper.deserializeBlob((byte[]) value);
+ attributes.put(name, value);
+ } catch (Exception iox) {
+ LOG.error("Attribute '" + name +
+ " contains a byte[] that cannot be deserialized due "
+ + "to the following exception", iox);
+ }
+ }
+
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<String> getAttributeNames() {
+ return attributes.keySet();
+ }
+
+ /**
+ * {@inheritDoc} +
+ */
+ @Override
+ public void setMaxInactiveInterval(int interval) {
+ maxInactiveInterval = interval;
+ }
+
+ @Override
+ public int getMaxIntactiveInterval() {
+ return maxInactiveInterval;
+ }
+
+ @Override
+ public void setLastAccessedTime(long time) {
+ lastAccessedTime = time;
+ }
+
+ @Override
+ public long getLastAccessedTime() {
+ return lastAccessedTime;
+ }
+
+ /**
+ * {@inheritDoc} This method calls back into the session to flush the whole
+ * session including its attributes.
+ */
+ @Override
+ public void flush() {
+ session.putInRegion();
+ }
+
+ /**
+ * Use DeltaEvents to propagate the actual attribute data - DeltaEvents turn
+ * the values into byte arrays which means that the actual domain classes are
+ * not required on the server.
+ */
+ @Override
+ public void toData(DataOutput out) throws IOException {
+ out.writeInt(maxInactiveInterval);
+ out.writeLong(lastAccessedTime);
+
+ synchronized (attributes) {
+ out.writeInt(attributes.size());
+ for (Map.Entry<String, Object> entry : attributes.entrySet()) {
+ DeltaEvent delta = new DeltaEvent(true, entry.getKey(),
+ entry.getValue());
+ DataSerializer.writeObject(delta, out);
+ }
+ }
+
+ out.writeUTF(jvmOwnerId);
+ }
+
+ @Override
+ public void fromData(
+ DataInput in) throws IOException, ClassNotFoundException {
+ maxInactiveInterval = in.readInt();
+ lastAccessedTime = in.readLong();
+ int size = in.readInt();
+ while (size-- > 0) {
+ DeltaEvent event = DataSerializer.readObject(in);
+ attributes.put(event.getName(), event.getValue());
+ }
+ jvmOwnerId = in.readUTF();
+ }
+
+ @Override
+ public void setJvmOwnerId(String jvmId) {
+ this.jvmOwnerId = jvmId;
+ }
+
+ @Override
+ public String getJvmOwnerId() {
+ return jvmOwnerId;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/DeltaEvent.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/DeltaEvent.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/DeltaEvent.java
new file mode 100644
index 0000000..4c248dd
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/DeltaEvent.java
@@ -0,0 +1,119 @@
+/*
+* 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 com.gemstone.gemfire.modules.session.internal.filter.attributes;
+
+import com.gemstone.gemfire.DataSerializable;
+import com.gemstone.gemfire.DataSerializer;
+import com.gemstone.gemfire.internal.util.BlobHelper;
+import com.gemstone.gemfire.modules.session.internal.filter.GemfireHttpSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+/**
+ * Capture the update to a particular name
+ */
+public class DeltaEvent implements DataSerializable {
+
+ private static final Logger LOG =
+ LoggerFactory.getLogger(DeltaEvent.class.getName());
+ /**
+ * The event is either an update (true) or a remove (false)
+ */
+ private boolean update;
+
+ private String name;
+
+ private Object value = null;
+
+ private GemfireHttpSession session = null;
+
+ /**
+ * Constructor for de-serialization only
+ */
+ public DeltaEvent() {
+ }
+
+ /**
+ * Constructor which creates a 'deferred' event. This is used when the value
+ * should only be applied when the object is serialized.
+ *
+ * @param session the session from which the value ultimately will be
+ * retrieved
+ * @param attribute the name of the attribute
+ */
+ public DeltaEvent(GemfireHttpSession session, String attribute) {
+ this.session = session;
+ this.name = attribute;
+ this.update = true;
+ }
+
+ public DeltaEvent(boolean update, String attribute, Object value) {
+ this.update = update;
+ this.name = attribute;
+ this.value = value;
+ blobifyValue();
+ }
+
+ private void blobifyValue() {
+ if (value instanceof byte[]) {
+ LOG.warn("Session attribute is already a byte[] - problems may "
+ + "occur transmitting this delta.");
+ }
+ try {
+ value = BlobHelper.serializeToBlob(value);
+ } catch (IOException iox) {
+ LOG.error("Attribute '" + name + "' value: " + value
+ + " cannot be serialized due to the following exception", iox);
+ }
+ }
+
+ public boolean isUpdate() {
+ return update;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
+ public void toData(DataOutput out) throws IOException {
+ if (session != null) {
+ value = session.getNativeSession().getAttribute(name);
+ blobifyValue();
+ }
+ out.writeBoolean(update);
+ DataSerializer.writeString(name, out);
+ DataSerializer.writeObject(value, out);
+ }
+
+ @Override
+ public void fromData(
+ DataInput in) throws IOException, ClassNotFoundException {
+ update = in.readBoolean();
+ name = DataSerializer.readString(in);
+ value = DataSerializer.readObject(in);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/DeltaQueuedSessionAttributes.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/DeltaQueuedSessionAttributes.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/DeltaQueuedSessionAttributes.java
new file mode 100644
index 0000000..cb4f673
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/DeltaQueuedSessionAttributes.java
@@ -0,0 +1,94 @@
+/*
+* 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 com.gemstone.gemfire.modules.session.internal.filter.attributes;
+
+import com.gemstone.gemfire.DataSerializable;
+import com.gemstone.gemfire.Instantiator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class implements delayed attribute delta propagation. Updates to
+ * attributes are only propagated once the session goes out of scope - i.e. as
+ * the request is done being processed.
+ */
+public class DeltaQueuedSessionAttributes extends AbstractDeltaSessionAttributes {
+
+ private static final Logger LOG =
+ LoggerFactory.getLogger(DeltaQueuedSessionAttributes.class.getName());
+
+ private Trigger trigger = Trigger.SET;
+
+ private enum Trigger {
+ SET,
+ SET_AND_GET;
+ }
+
+ /**
+ * Register ourselves for de-serialization
+ */
+ static {
+ Instantiator.register(
+ new Instantiator(DeltaQueuedSessionAttributes.class, 3479) {
+ @Override
+ public DataSerializable newInstance() {
+ return new DeltaQueuedSessionAttributes();
+ }
+ });
+ }
+
+ /**
+ * Default constructor
+ */
+ public DeltaQueuedSessionAttributes() {
+ }
+
+ public void setReplicationTrigger(String trigger) {
+ this.trigger = Trigger.valueOf(trigger.toUpperCase());
+ }
+
+ @Override
+ public Object getAttribute(String attr) {
+ if (trigger == Trigger.SET_AND_GET) {
+ deltas.put(attr, new DeltaEvent(session, attr));
+ }
+ return super.getAttribute(attr);
+ }
+
+ /**
+ * {@inheritDoc} Put an attribute, setting the dirty flag. The changes are
+ * flushed at the end of filter processing.
+ */
+ @Override
+ public Object putAttribute(String attr, Object value) {
+ Object obj = attributes.put(attr, value);
+ deltas.put(attr, new DeltaEvent(true, attr, value));
+ return obj;
+ }
+
+ /**
+ * {@inheritDoc} Remove an attribute, setting the dirty flag. The changes are
+ * flushed at the end of filter processing.
+ */
+ @Override
+ public Object removeAttribute(String attr) {
+ Object obj = attributes.remove(attr);
+ deltas.put(attr, new DeltaEvent(false, attr, null));
+ return obj;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/DeltaSessionAttributes.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/DeltaSessionAttributes.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/DeltaSessionAttributes.java
new file mode 100644
index 0000000..8cc9866
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/filter/attributes/DeltaSessionAttributes.java
@@ -0,0 +1,75 @@
+/*
+* 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 com.gemstone.gemfire.modules.session.internal.filter.attributes;
+
+import com.gemstone.gemfire.DataSerializable;
+import com.gemstone.gemfire.Instantiator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class implements synchronous attribute delta propagation. Updates to
+ * attributes are immediately propagated.
+ */
+public class DeltaSessionAttributes extends AbstractDeltaSessionAttributes {
+
+ private static final Logger LOG =
+ LoggerFactory.getLogger(DeltaSessionAttributes.class.getName());
+
+ /**
+ * Register ourselves for de-serialization
+ */
+ static {
+ Instantiator.register(new Instantiator(DeltaSessionAttributes.class, 347) {
+ @Override
+ public DataSerializable newInstance() {
+ return new DeltaSessionAttributes();
+ }
+ });
+ }
+
+ /**
+ * Default constructor
+ */
+ public DeltaSessionAttributes() {
+ }
+
+ /**
+ * {@inheritDoc} Put an attribute, setting the dirty flag and immediately
+ * flushing the delta queue.
+ */
+ @Override
+ public Object putAttribute(String attr, Object value) {
+ Object obj = attributes.put(attr, value);
+ deltas.put(attr, new DeltaEvent(true, attr, value));
+ flush();
+ return obj;
+ }
+
+ /**
+ * {@inheritDoc} Remove an attribute, setting the dirty flag and immediately
+ * flushing the delta queue.
+ */
+ @Override
+ public Object removeAttribute(String attr) {
+ Object obj = attributes.remove(attr);
+ deltas.put(attr, new DeltaEvent(false, attr, null));
+ flush();
+ return obj;
+ }
+}