Support External Hazelcast instance

Previously the Hazelcast support only worked with
embedded Hazelcast instances.

This commit ensures that Hazelcast support works
with external Hazelcast instances.

Fixes gh-339
This commit is contained in:
Mark Anderson
2016-02-01 19:10:09 +00:00
committed by Rob Winch
parent 2b5386ad98
commit f2443f5e21
7 changed files with 107 additions and 132 deletions

View File

@@ -59,8 +59,7 @@ import com.hazelcast.core.HazelcastInstance;
@WebAppConfiguration
public class EnableHazelcastHttpSessionEventsTests<S extends ExpiringSession> {
private final static String MAX_INACTIVE_INTERVAL_IN_SECONDS_STR = "1";
private final static int MAX_INACTIVE_INTERVAL_IN_SECONDS = Integer.valueOf(MAX_INACTIVE_INTERVAL_IN_SECONDS_STR);
private final static int MAX_INACTIVE_INTERVAL_IN_SECONDS = 1;
@Autowired
private SessionRepository<S> repository;
@@ -143,9 +142,29 @@ public class EnableHazelcastHttpSessionEventsTests<S extends ExpiringSession> {
assertThat(repository.getSession(sessionToSave.getId())).isNull();
}
@Test
public void saveUpdatesTimeToLiveTest() throws InterruptedException {
S sessionToSave = repository.createSession();
repository.save(sessionToSave);
synchronized (lock) {
lock.wait((sessionToSave.getMaxInactiveIntervalInSeconds() * 1000) - 500);
}
// Get and save the session like SessionRepositoryFilter would.
S sessionToUpdate = repository.getSession(sessionToSave.getId());
repository.save(sessionToUpdate);
synchronized (lock) {
lock.wait((sessionToUpdate.getMaxInactiveIntervalInSeconds() * 1000) - 100);
}
assertThat(repository.getSession(sessionToUpdate.getId())).isNotNull();
}
@Configuration
@EnableHazelcastHttpSession(maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS_STR)
@EnableHazelcastHttpSession(maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class HazelcastSessionConfig {
@Bean

View File

@@ -43,59 +43,6 @@ import com.hazelcast.core.HazelcastInstance;
*/
public class HazelcastHttpSessionConfigurationXmlTests<S extends ExpiringSession> {
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public static class CustomXmlInactiveIntervalTest<S extends ExpiringSession> {
@Autowired
private SessionRepository<S> repository;
@Autowired
private HazelcastInstance hazelcast;
@Test
public void saveSessionTest() throws InterruptedException {
S sessionToSave = repository.createSession();
assertThat(sessionToSave.getMaxInactiveIntervalInSeconds())
.isEqualTo(150);
repository.save(sessionToSave);
S session = repository.getSession(sessionToSave.getId());
assertThat(session.getId()).isEqualTo(sessionToSave.getId());
assertThat(session.getMaxInactiveIntervalInSeconds())
.isEqualTo(150);
}
@Test
public void checkUnderlyingMapSettingsTest() {
assertThat(
hazelcast.getConfig()
.getMapConfig("spring:session:sessions")
.getMaxIdleSeconds())
.isEqualTo(150);
}
@Configuration
@EnableHazelcastHttpSession(maxInactiveIntervalInSeconds = "")
static class HazelcastSessionXmlConfigCustomIdle {
@Bean
public HazelcastInstance embeddedHazelcast() {
Config hazelcastConfig = new ClasspathXmlConfig(
"org/springframework/session/hazelcast/config/annotation/web/http/hazelcast-custom-idle-time.xml");
NetworkConfig netConfig = new NetworkConfig();
netConfig.setPort(SocketUtils.findAvailableTcpPort());
hazelcastConfig.setNetworkConfig(netConfig);
return Hazelcast.newHazelcastInstance(hazelcastConfig);
}
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
@@ -104,9 +51,6 @@ public class HazelcastHttpSessionConfigurationXmlTests<S extends ExpiringSession
@Autowired
private SessionRepository<S> repository;
@Autowired
private HazelcastInstance hazelcast;
@Test
public void saveSessionTest() throws InterruptedException {
@@ -120,15 +64,6 @@ public class HazelcastHttpSessionConfigurationXmlTests<S extends ExpiringSession
assertThat(session.getMaxInactiveIntervalInSeconds()).isEqualTo(1800);
}
@Test
public void checkUnderlyingMapSettingsTest() {
assertThat(
hazelcast.getConfig()
.getMapConfig("my-sessions")
.getMaxIdleSeconds())
.isEqualTo(1800);
}
@Configuration
@EnableHazelcastHttpSession(sessionMapName = "my-sessions")
static class HazelcastSessionXmlConfigCustomMapName {
@@ -153,9 +88,6 @@ public class HazelcastHttpSessionConfigurationXmlTests<S extends ExpiringSession
@Autowired
private SessionRepository<S> repository;
@Autowired
private HazelcastInstance hazelcast;
@Test
public void saveSessionTest() throws InterruptedException {
@@ -169,17 +101,8 @@ public class HazelcastHttpSessionConfigurationXmlTests<S extends ExpiringSession
assertThat(session.getMaxInactiveIntervalInSeconds()).isEqualTo(1200);
}
@Test
public void checkUnderlyingMapSettingsTest() {
assertThat(
hazelcast.getConfig()
.getMapConfig("test-sessions")
.getMaxIdleSeconds())
.isEqualTo(1200);
}
@Configuration
@EnableHazelcastHttpSession(sessionMapName = "test-sessions", maxInactiveIntervalInSeconds = "1200")
@EnableHazelcastHttpSession(sessionMapName = "test-sessions", maxInactiveIntervalInSeconds = 1200)
static class HazelcastSessionXmlConfigCustomMapNameAndIdle {
@Bean

View File

@@ -59,13 +59,10 @@ public @interface EnableHazelcastHttpSession {
/**
* This is the session timeout in seconds. By default, it is set to 1800 seconds (30 minutes).
* This should be a non-negative integer.
* <p>If you wish to use external configuration (outside of this annotation) to set this value, you can
* set this to "" (an empty String), which will prevent this configuration from overriding
* the external configuration for this value.</p>
*
* @return the seconds a session can be inactive before expiring
*/
String maxInactiveIntervalInSeconds() default "1800";
int maxInactiveIntervalInSeconds() default 1800;
/**
* This is the name of the Map that will be used in Hazelcast to store the session data.

View File

@@ -15,7 +15,10 @@
*/
package org.springframework.session.hazelcast.config.annotation.web.http;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.annotation.PreDestroy;
@@ -32,7 +35,6 @@ import org.springframework.session.config.annotation.web.http.SpringHttpSessionC
import org.springframework.session.hazelcast.SessionEntryListener;
import org.springframework.session.web.http.SessionRepositoryFilter;
import com.hazelcast.config.MapConfig;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap;
@@ -48,10 +50,6 @@ import com.hazelcast.core.IMap;
@Configuration
public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfiguration implements ImportAware {
/** This is the magic value to use if you do not want this configuration
* overriding the maxIdleSeconds value for the Map backing the session data. */
private static final String DO_NOT_CONFIGURE_INACTIVE_INTERVAL_STRING = "";
private Integer maxInactiveIntervalInSeconds = 1800;
private String sessionMapName = "spring:session:sessions";
@@ -62,11 +60,10 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
@Bean
public SessionRepository<ExpiringSession> sessionRepository(HazelcastInstance hazelcastInstance, SessionEntryListener sessionListener) {
configureSessionMap(hazelcastInstance);
this.sessionsMap = hazelcastInstance.getMap(sessionMapName);
this.sessionListenerUid = this.sessionsMap.addEntryListener(sessionListener, true);
MapSessionRepository sessionRepository = new MapSessionRepository(this.sessionsMap);
MapSessionRepository sessionRepository = new MapSessionRepository(new ExpiringSessionMap(this.sessionsMap));
sessionRepository.setDefaultMaxInactiveInterval(maxInactiveIntervalInSeconds);
return sessionRepository;
@@ -82,22 +79,6 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
return new SessionEntryListener(eventPublisher);
}
/**
* Make a {@link MapConfig} for the given sessionMapName if one does not exist.
* Set Hazelcast's maxIdleSeconds to maxInactiveIntervalInSeconds if set (not "").
* Otherwise get the externally configured maxIdleSeconds for the distributed sessions map.
*
* @param hazelcastInstance the {@link HazelcastInstance} to configure
*/
private void configureSessionMap(HazelcastInstance hazelcastInstance) {
MapConfig sessionMapConfig = hazelcastInstance.getConfig().getMapConfig(sessionMapName);
if (this.maxInactiveIntervalInSeconds != null) {
sessionMapConfig.setMaxIdleSeconds(this.maxInactiveIntervalInSeconds);
} else {
this.maxInactiveIntervalInSeconds = sessionMapConfig.getMaxIdleSeconds();
}
}
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> enableAttrMap = importMetadata.getAnnotationAttributes(EnableHazelcastHttpSession.class.getName());
AnnotationAttributes enableAttrs = AnnotationAttributes.fromMap(enableAttrMap);
@@ -106,19 +87,7 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
}
private void transferAnnotationAttributes(AnnotationAttributes enableAttrs) {
String maxInactiveIntervalString = enableAttrs.getString("maxInactiveIntervalInSeconds");
if (DO_NOT_CONFIGURE_INACTIVE_INTERVAL_STRING.equals(maxInactiveIntervalString)) {
this.maxInactiveIntervalInSeconds = null;
} else {
try {
this.maxInactiveIntervalInSeconds = Integer.parseInt(maxInactiveIntervalString);
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException(
"@EnableHazelcastHttpSession's maxInactiveIntervalInSeconds expects an int format String but was '"
+ maxInactiveIntervalString + "' instead.", nfe);
}
}
setMaxInactiveIntervalInSeconds((Integer) enableAttrs.getNumber("maxInactiveIntervalInSeconds"));
setSessionMapName(enableAttrs.getString("sessionMapName"));
}
@@ -129,4 +98,70 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
public void setSessionMapName(String sessionMapName) {
this.sessionMapName = sessionMapName;
}
static class ExpiringSessionMap implements Map<String, ExpiringSession> {
private IMap<String,ExpiringSession> delegate;
ExpiringSessionMap(IMap<String,ExpiringSession> delegate) {
this.delegate = delegate;
}
public ExpiringSession put(String key, ExpiringSession value) {
if(value == null) {
return delegate.put(key, value);
}
return delegate.put(key, value, value.getMaxInactiveIntervalInSeconds(), TimeUnit.SECONDS);
}
public int size() {
return delegate.size();
}
public boolean isEmpty() {
return delegate.isEmpty();
}
public boolean containsKey(Object key) {
return delegate.containsKey(key);
}
public boolean containsValue(Object value) {
return delegate.containsValue(value);
}
public ExpiringSession get(Object key) {
return delegate.get(key);
}
public ExpiringSession remove(Object key) {
return delegate.remove(key);
}
public void putAll(Map<? extends String, ? extends ExpiringSession> m) {
delegate.putAll(m);
}
public void clear() {
delegate.clear();
}
public Set<String> keySet() {
return delegate.keySet();
}
public Collection<ExpiringSession> values() {
return delegate.values();
}
public Set<java.util.Map.Entry<String, ExpiringSession>> entrySet() {
return delegate.entrySet();
}
public boolean equals(Object o) {
return delegate.equals(o);
}
public int hashCode() {
return delegate.hashCode();
}
}
}