Add HazelcastSessionRepository
This commit improves existing Hazelcast support, which is based on MapSessionRepository, with dedicated HazelcastSessionRepository that implements the FindByIndexNameSessionRepository contract. Also a new hazelcast-spring-session module was added to provide dependency management for Hazelcast support. Fixes gh-544
This commit is contained in:
@@ -21,8 +21,7 @@ import com.hazelcast.core.IMap;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.MapSession;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -32,20 +31,21 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Tommy Ludwig
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
public abstract class AbstractHazelcastRepositoryITests<S extends ExpiringSession> {
|
||||
public abstract class AbstractHazelcastRepositoryITests {
|
||||
|
||||
@Autowired
|
||||
private HazelcastInstance hazelcast;
|
||||
|
||||
@Autowired
|
||||
private SessionRepository<S> repository;
|
||||
private HazelcastSessionRepository repository;
|
||||
|
||||
@Test
|
||||
public void createAndDestroySession() {
|
||||
S sessionToSave = this.repository.createSession();
|
||||
MapSession sessionToSave = this.repository.createSession();
|
||||
String sessionId = sessionToSave.getId();
|
||||
|
||||
IMap<String, S> hazelcastMap = this.hazelcast.getMap("spring:session:sessions");
|
||||
IMap<String, MapSession> hazelcastMap = this.hazelcast.getMap(
|
||||
"spring:session:sessions");
|
||||
|
||||
assertThat(hazelcastMap.size()).isEqualTo(0);
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.hazelcast.config.annotation.web.http.EnableHazelcastHttpSession;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@@ -43,8 +42,7 @@ import org.springframework.util.SocketUtils;
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class HazelcastClientRepositoryITests<S extends ExpiringSession>
|
||||
extends AbstractHazelcastRepositoryITests<S> {
|
||||
public class HazelcastClientRepositoryITests extends AbstractHazelcastRepositoryITests {
|
||||
|
||||
private static final int PORT = SocketUtils.findAvailableTcpPort();
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package org.springframework.session.hazelcast;
|
||||
|
||||
import com.hazelcast.config.Config;
|
||||
import com.hazelcast.config.MapAttributeConfig;
|
||||
import com.hazelcast.config.MapIndexConfig;
|
||||
import com.hazelcast.core.Hazelcast;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
|
||||
@@ -38,8 +40,20 @@ public final class HazelcastITestUtils {
|
||||
* @return the Hazelcast instance
|
||||
*/
|
||||
public static HazelcastInstance embeddedHazelcastServer(int port) {
|
||||
MapAttributeConfig attributeConfig = new MapAttributeConfig()
|
||||
.setName(HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
|
||||
.setExtractor(PrincipalNameExtractor.class.getName());
|
||||
|
||||
Config config = new Config();
|
||||
config.getNetworkConfig().setPort(port);
|
||||
|
||||
config.getNetworkConfig()
|
||||
.setPort(port);
|
||||
|
||||
config.getMapConfig("spring:session:sessions")
|
||||
.addMapAttributeConfig(attributeConfig)
|
||||
.addMapIndexConfig(new MapIndexConfig(
|
||||
HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
|
||||
|
||||
return Hazelcast.newHazelcastInstance(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.hazelcast.config.annotation.web.http.EnableHazelcastHttpSession;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@@ -37,8 +36,7 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class HazelcastServerRepositoryITests<S extends ExpiringSession>
|
||||
extends AbstractHazelcastRepositoryITests<S> {
|
||||
public class HazelcastServerRepositoryITests extends AbstractHazelcastRepositoryITests {
|
||||
|
||||
@EnableHazelcastHttpSession
|
||||
@Configuration
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.hazelcast;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import com.hazelcast.core.EntryEvent;
|
||||
import com.hazelcast.core.IMap;
|
||||
import com.hazelcast.map.listener.EntryAddedListener;
|
||||
import com.hazelcast.map.listener.EntryEvictedListener;
|
||||
import com.hazelcast.map.listener.EntryRemovedListener;
|
||||
import com.hazelcast.query.Predicates;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.events.AbstractSessionEvent;
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
import org.springframework.session.events.SessionDeletedEvent;
|
||||
import org.springframework.session.events.SessionExpiredEvent;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link org.springframework.session.SessionRepository} implementation that stores
|
||||
* sessions in Hazelcast's distributed {@link IMap}.
|
||||
*
|
||||
* <p>
|
||||
* An example of how to create a new instance can be seen below:
|
||||
*
|
||||
* <pre class="code">
|
||||
* Config config = new Config();
|
||||
*
|
||||
* // ... configure Hazelcast ...
|
||||
*
|
||||
* HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance(config);
|
||||
*
|
||||
* IMap{@code <String, MapSession>} sessions = hazelcastInstance
|
||||
* .getMap("spring:session:sessions");
|
||||
*
|
||||
* HazelcastSessionRepository sessionRepository =
|
||||
* new HazelcastSessionRepository(sessions);
|
||||
* </pre>
|
||||
*
|
||||
* In order to support finding sessions by principal name using
|
||||
* {@link #findByIndexNameAndIndexValue(String, String)} method, custom configuration of
|
||||
* {@code IMap} supplied to this implementation is required.
|
||||
*
|
||||
* The following snippet demonstrates how to define required configuration using
|
||||
* programmatic Hazelcast Configuration:
|
||||
*
|
||||
* <pre class="code">
|
||||
* MapAttributeConfig attributeConfig = new MapAttributeConfig()
|
||||
* .setName(HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
|
||||
* .setExtractor(PrincipalNameExtractor.class.getName());
|
||||
*
|
||||
* Config config = new Config();
|
||||
*
|
||||
* config.getMapConfig("spring:session:sessions")
|
||||
* .addMapAttributeConfig(attributeConfig)
|
||||
* .addMapIndexConfig(new MapIndexConfig(
|
||||
* HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
|
||||
*
|
||||
* Hazelcast.newHazelcastInstance(config);
|
||||
* </pre>
|
||||
*
|
||||
* This implementation listens for events on the Hazelcast-backed SessionRepository and
|
||||
* translates those events into the corresponding Spring Session events. Publish the
|
||||
* Spring Session events with the given {@link ApplicationEventPublisher}.
|
||||
*
|
||||
* <ul>
|
||||
* <li>entryAdded - {@link SessionCreatedEvent}</li>
|
||||
* <li>entryEvicted - {@link SessionExpiredEvent}</li>
|
||||
* <li>entryRemoved - {@link SessionDeletedEvent}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @author Tommy Ludwig
|
||||
* @author Mark Anderson
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public class HazelcastSessionRepository implements
|
||||
FindByIndexNameSessionRepository<MapSession>,
|
||||
EntryAddedListener<String, MapSession>,
|
||||
EntryEvictedListener<String, MapSession>,
|
||||
EntryRemovedListener<String, MapSession> {
|
||||
|
||||
/**
|
||||
* The principal name custom attribute name.
|
||||
*/
|
||||
public static final String PRINCIPAL_NAME_ATTRIBUTE = "principalName";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(HazelcastSessionRepository.class);
|
||||
|
||||
private final IMap<String, MapSession> sessions;
|
||||
|
||||
private ApplicationEventPublisher eventPublisher = new ApplicationEventPublisher() {
|
||||
|
||||
public void publishEvent(ApplicationEvent event) {
|
||||
}
|
||||
|
||||
public void publishEvent(Object event) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* If non-null, this value is used to override
|
||||
* {@link MapSession#setMaxInactiveIntervalInSeconds(int)}.
|
||||
*/
|
||||
private Integer defaultMaxInactiveInterval;
|
||||
|
||||
private String sessionListenerId;
|
||||
|
||||
public HazelcastSessionRepository(IMap<String, MapSession> sessions) {
|
||||
Assert.notNull(sessions, "Sessions IMap must not be null");
|
||||
this.sessions = sessions;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
this.sessionListenerId = this.sessions.addEntryListener(this, true);
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
private void close() {
|
||||
this.sessions.removeEntryListener(this.sessionListenerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ApplicationEventPublisher} that is used to publish
|
||||
* {@link AbstractSessionEvent session events}. The default is to not publish session
|
||||
* events.
|
||||
*
|
||||
* @param applicationEventPublisher the {@link ApplicationEventPublisher} that is used
|
||||
* to publish session events. Cannot be null.
|
||||
*/
|
||||
public void setApplicationEventPublisher(
|
||||
ApplicationEventPublisher applicationEventPublisher) {
|
||||
Assert.notNull(applicationEventPublisher,
|
||||
"ApplicationEventPublisher cannot be null");
|
||||
this.eventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum inactive interval in seconds between requests before newly created
|
||||
* sessions will be invalidated. A negative time indicates that the session will never
|
||||
* timeout. The default is 1800 (30 minutes).
|
||||
* @param defaultMaxInactiveInterval the maximum inactive interval in seconds
|
||||
*/
|
||||
public void setDefaultMaxInactiveInterval(Integer defaultMaxInactiveInterval) {
|
||||
this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
|
||||
}
|
||||
|
||||
public MapSession createSession() {
|
||||
MapSession result = new MapSession();
|
||||
if (this.defaultMaxInactiveInterval != null) {
|
||||
result.setMaxInactiveIntervalInSeconds(this.defaultMaxInactiveInterval);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void save(MapSession session) {
|
||||
this.sessions.put(session.getId(), session,
|
||||
session.getMaxInactiveIntervalInSeconds(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public MapSession getSession(String id) {
|
||||
MapSession saved = this.sessions.get(id);
|
||||
if (saved == null) {
|
||||
return null;
|
||||
}
|
||||
if (saved.isExpired()) {
|
||||
delete(saved.getId());
|
||||
return null;
|
||||
}
|
||||
return saved;
|
||||
}
|
||||
|
||||
public void delete(String id) {
|
||||
this.sessions.remove(id);
|
||||
}
|
||||
|
||||
public Map<String, MapSession> findByIndexNameAndIndexValue(
|
||||
String indexName, String indexValue) {
|
||||
if (!PRINCIPAL_NAME_INDEX_NAME.equals(indexName)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Collection<MapSession> sessions = this.sessions.values(
|
||||
Predicates.equal(PRINCIPAL_NAME_ATTRIBUTE, indexValue));
|
||||
Map<String, MapSession> sessionMap = new HashMap<String, MapSession>(
|
||||
sessions.size());
|
||||
for (MapSession session : sessions) {
|
||||
sessionMap.put(session.getId(), session);
|
||||
}
|
||||
return sessionMap;
|
||||
}
|
||||
|
||||
public void entryAdded(EntryEvent<String, MapSession> event) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Session created with id: " + event.getValue().getId());
|
||||
}
|
||||
this.eventPublisher.publishEvent(new SessionCreatedEvent(this, event.getValue()));
|
||||
}
|
||||
|
||||
public void entryEvicted(EntryEvent<String, MapSession> event) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Session expired with id: " + event.getOldValue().getId());
|
||||
}
|
||||
this.eventPublisher
|
||||
.publishEvent(new SessionExpiredEvent(this, event.getOldValue()));
|
||||
}
|
||||
|
||||
public void entryRemoved(EntryEvent<String, MapSession> event) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Session deleted with id: " + event.getOldValue().getId());
|
||||
}
|
||||
this.eventPublisher
|
||||
.publishEvent(new SessionDeletedEvent(this, event.getOldValue()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.hazelcast;
|
||||
|
||||
import com.hazelcast.query.extractor.ValueCollector;
|
||||
import com.hazelcast.query.extractor.ValueExtractor;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.Session;
|
||||
|
||||
/**
|
||||
* Hazelcast {@link ValueExtractor} responsible for extracting principal name from the
|
||||
* {@link MapSession}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public class PrincipalNameExtractor extends ValueExtractor<MapSession, String> {
|
||||
|
||||
private static final PrincipalNameResolver PRINCIPAL_NAME_RESOLVER =
|
||||
new PrincipalNameResolver();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void extract(MapSession target, String argument,
|
||||
ValueCollector collector) {
|
||||
String principalName = PRINCIPAL_NAME_RESOLVER.resolvePrincipal(target);
|
||||
if (principalName != null) {
|
||||
collector.addObject(principalName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the Spring Security principal name.
|
||||
*/
|
||||
static class PrincipalNameResolver {
|
||||
|
||||
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
|
||||
|
||||
private SpelExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
public String resolvePrincipal(Session session) {
|
||||
String principalName = session.getAttribute(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME);
|
||||
if (principalName != null) {
|
||||
return principalName;
|
||||
}
|
||||
Object authentication = session.getAttribute(SPRING_SECURITY_CONTEXT);
|
||||
if (authentication != null) {
|
||||
Expression expression = this.parser
|
||||
.parseExpression("authentication?.name");
|
||||
return expression.getValue(authentication, String.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -43,7 +43,9 @@ import org.springframework.util.Assert;
|
||||
* @author Tommy Ludwig
|
||||
* @author Mark Anderson
|
||||
* @since 1.1
|
||||
* @deprecated Use {@link HazelcastSessionRepository} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class SessionEntryListener implements EntryAddedListener<String, ExpiringSession>,
|
||||
EntryEvictedListener<String, ExpiringSession>,
|
||||
EntryRemovedListener<String, ExpiringSession> {
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
|
||||
|
||||
/**
|
||||
@@ -56,19 +57,20 @@ import org.springframework.session.config.annotation.web.http.EnableSpringHttpSe
|
||||
@Import(HazelcastHttpSessionConfiguration.class)
|
||||
@Configuration
|
||||
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.
|
||||
*
|
||||
* @return the seconds a session can be inactive before expiring
|
||||
*/
|
||||
int maxInactiveIntervalInSeconds() default 1800;
|
||||
int maxInactiveIntervalInSeconds() default MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||
|
||||
/**
|
||||
* This is the name of the Map that will be used in Hazelcast to store the session
|
||||
* data. Default is "spring:session:sessions".
|
||||
* data. Default is {@link HazelcastHttpSessionConfiguration#DEFAULT_SESSION_MAP_NAME}.
|
||||
* @return the name of the Map to store the sessions in Hazelcast
|
||||
*/
|
||||
String sessionMapName() default "spring:session:sessions";
|
||||
String sessionMapName() default HazelcastHttpSessionConfiguration.DEFAULT_SESSION_MAP_NAME;
|
||||
|
||||
}
|
||||
|
||||
@@ -16,12 +16,7 @@
|
||||
|
||||
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;
|
||||
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.hazelcast.core.IMap;
|
||||
@@ -32,11 +27,9 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.MapSessionRepository;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
||||
import org.springframework.session.hazelcast.SessionEntryListener;
|
||||
import org.springframework.session.hazelcast.HazelcastSessionRepository;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
|
||||
/**
|
||||
@@ -45,6 +38,7 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
* {@link HazelcastInstance} must be exposed as a Bean.
|
||||
*
|
||||
* @author Tommy Ludwig
|
||||
* @author Vedran Pavic
|
||||
* @since 1.1
|
||||
* @see EnableHazelcastHttpSession
|
||||
*/
|
||||
@@ -52,49 +46,30 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||
implements ImportAware {
|
||||
|
||||
private Integer maxInactiveIntervalInSeconds = 1800;
|
||||
static final String DEFAULT_SESSION_MAP_NAME = "spring:session:sessions";
|
||||
|
||||
private String sessionMapName = "spring:session:sessions";
|
||||
private Integer maxInactiveIntervalInSeconds;
|
||||
|
||||
private String sessionListenerUid;
|
||||
|
||||
private IMap<String, ExpiringSession> sessionsMap;
|
||||
private String sessionMapName = DEFAULT_SESSION_MAP_NAME;
|
||||
|
||||
@Bean
|
||||
public SessionRepository<ExpiringSession> sessionRepository(
|
||||
HazelcastInstance hazelcastInstance, SessionEntryListener sessionListener) {
|
||||
this.sessionsMap = hazelcastInstance.getMap(this.sessionMapName);
|
||||
this.sessionListenerUid = this.sessionsMap.addEntryListener(sessionListener,
|
||||
true);
|
||||
|
||||
MapSessionRepository sessionRepository = new MapSessionRepository(
|
||||
new ExpiringSessionMap(this.sessionsMap));
|
||||
sessionRepository
|
||||
.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
|
||||
|
||||
return sessionRepository;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
private void removeSessionListener() {
|
||||
this.sessionsMap.removeEntryListener(this.sessionListenerUid);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SessionEntryListener sessionListener(
|
||||
public HazelcastSessionRepository sessionRepository(
|
||||
HazelcastInstance hazelcastInstance,
|
||||
ApplicationEventPublisher eventPublisher) {
|
||||
return new SessionEntryListener(eventPublisher);
|
||||
IMap<String, MapSession> sessions = hazelcastInstance.getMap(
|
||||
this.sessionMapName);
|
||||
HazelcastSessionRepository sessionRepository = new HazelcastSessionRepository(
|
||||
sessions);
|
||||
sessionRepository.setApplicationEventPublisher(eventPublisher);
|
||||
sessionRepository.setDefaultMaxInactiveInterval(
|
||||
this.maxInactiveIntervalInSeconds);
|
||||
return sessionRepository;
|
||||
}
|
||||
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
Map<String, Object> enableAttrMap = importMetadata
|
||||
.getAnnotationAttributes(EnableHazelcastHttpSession.class.getName());
|
||||
AnnotationAttributes enableAttrs = AnnotationAttributes.fromMap(enableAttrMap);
|
||||
|
||||
transferAnnotationAttributes(enableAttrs);
|
||||
}
|
||||
|
||||
private void transferAnnotationAttributes(AnnotationAttributes enableAttrs) {
|
||||
setMaxInactiveIntervalInSeconds(
|
||||
(Integer) enableAttrs.getNumber("maxInactiveIntervalInSeconds"));
|
||||
setSessionMapName(enableAttrs.getString("sessionMapName"));
|
||||
@@ -108,74 +83,4 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
|
||||
this.sessionMapName = sessionMapName;
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper for Hazelcast's {@link IMap} which is used to store the sessions.
|
||||
*/
|
||||
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 this.delegate.put(key, value);
|
||||
}
|
||||
return this.delegate.put(key, value, value.getMaxInactiveIntervalInSeconds(),
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.delegate.size();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.delegate.isEmpty();
|
||||
}
|
||||
|
||||
public boolean containsKey(Object key) {
|
||||
return this.delegate.containsKey(key);
|
||||
}
|
||||
|
||||
public boolean containsValue(Object value) {
|
||||
return this.delegate.containsValue(value);
|
||||
}
|
||||
|
||||
public ExpiringSession get(Object key) {
|
||||
return this.delegate.get(key);
|
||||
}
|
||||
|
||||
public ExpiringSession remove(Object key) {
|
||||
return this.delegate.remove(key);
|
||||
}
|
||||
|
||||
public void putAll(Map<? extends String, ? extends ExpiringSession> m) {
|
||||
this.delegate.putAll(m);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.delegate.clear();
|
||||
}
|
||||
|
||||
public Set<String> keySet() {
|
||||
return this.delegate.keySet();
|
||||
}
|
||||
|
||||
public Collection<ExpiringSession> values() {
|
||||
return this.delegate.values();
|
||||
}
|
||||
|
||||
public Set<java.util.Map.Entry<String, ExpiringSession>> entrySet() {
|
||||
return this.delegate.entrySet();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return this.delegate.equals(o);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return this.delegate.hashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.hazelcast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.hazelcast.core.IMap;
|
||||
import com.hazelcast.query.impl.predicates.EqualPredicate;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.MapSession;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Matchers.isA;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link HazelcastSessionRepository}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class HazelcastSessionRepositoryTests {
|
||||
|
||||
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Mock
|
||||
private IMap<String, MapSession> sessions;
|
||||
|
||||
private HazelcastSessionRepository repository;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.repository = new HazelcastSessionRepository(this.sessions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorNullHazelcastInstance() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Sessions IMap must not be null");
|
||||
|
||||
new HazelcastSessionRepository(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionDefaultMaxInactiveInterval() throws Exception {
|
||||
MapSession session = this.repository.createSession();
|
||||
|
||||
assertThat(session.getMaxInactiveIntervalInSeconds())
|
||||
.isEqualTo(new MapSession().getMaxInactiveIntervalInSeconds());
|
||||
verifyZeroInteractions(this.sessions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionCustomMaxInactiveInterval() throws Exception {
|
||||
int interval = 1;
|
||||
this.repository.setDefaultMaxInactiveInterval(interval);
|
||||
|
||||
MapSession session = this.repository.createSession();
|
||||
|
||||
assertThat(session.getMaxInactiveIntervalInSeconds()).isEqualTo(interval);
|
||||
verifyZeroInteractions(this.sessions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveNew() {
|
||||
MapSession session = this.repository.createSession();
|
||||
|
||||
this.repository.save(session);
|
||||
|
||||
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session),
|
||||
isA(Long.class), eq(TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveUpdatedAttributes() {
|
||||
MapSession session = new MapSession();
|
||||
session.setAttribute("testName", "testValue");
|
||||
|
||||
this.repository.save(session);
|
||||
|
||||
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session),
|
||||
isA(Long.class), eq(TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveUpdatedLastAccessedTime() {
|
||||
MapSession session = new MapSession();
|
||||
session.setLastAccessedTime(System.currentTimeMillis());
|
||||
|
||||
this.repository.save(session);
|
||||
|
||||
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session),
|
||||
isA(Long.class), eq(TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveUnchanged() {
|
||||
MapSession session = new MapSession();
|
||||
|
||||
this.repository.save(session);
|
||||
|
||||
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session),
|
||||
isA(Long.class), eq(TimeUnit.SECONDS));
|
||||
// TODO - once save optimization is implemented, should be replaced with:
|
||||
//verifyZeroInteractions(this.sessions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionNotFound() {
|
||||
String sessionId = "testSessionId";
|
||||
|
||||
MapSession session = this.repository.getSession(sessionId);
|
||||
|
||||
assertThat(session).isNull();
|
||||
verify(this.sessions, times(1)).get(eq(sessionId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionExpired() {
|
||||
MapSession expired = new MapSession();
|
||||
expired.setLastAccessedTime(System.currentTimeMillis() -
|
||||
(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS * 1000 + 1000));
|
||||
given(this.sessions.get(eq(expired.getId()))).willReturn(expired);
|
||||
|
||||
MapSession session = this.repository.getSession(expired.getId());
|
||||
|
||||
assertThat(session).isNull();
|
||||
verify(this.sessions, times(1)).get(eq(expired.getId()));
|
||||
verify(this.sessions, times(1)).remove(eq(expired.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionFound() {
|
||||
MapSession saved = new MapSession();
|
||||
saved.setAttribute("savedName", "savedValue");
|
||||
given(this.sessions.get(eq(saved.getId()))).willReturn(saved);
|
||||
|
||||
MapSession session = this.repository.getSession(saved.getId());
|
||||
|
||||
assertThat(session.getId()).isEqualTo(saved.getId());
|
||||
assertThat(session.getAttribute("savedName")).isEqualTo("savedValue");
|
||||
verify(this.sessions, times(1)).get(eq(saved.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delete() {
|
||||
String sessionId = "testSessionId";
|
||||
|
||||
this.repository.delete(sessionId);
|
||||
|
||||
verify(this.sessions, times(1)).remove(eq(sessionId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByIndexNameAndIndexValueUnknownIndexName() {
|
||||
String indexValue = "testIndexValue";
|
||||
|
||||
Map<String, MapSession> sessions = this.repository.findByIndexNameAndIndexValue(
|
||||
"testIndexName", indexValue);
|
||||
|
||||
assertThat(sessions).isEmpty();
|
||||
verifyZeroInteractions(this.sessions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByIndexNameAndIndexValuePrincipalIndexNameNotFound() {
|
||||
String principal = "username";
|
||||
|
||||
Map<String, MapSession> sessions = this.repository.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principal);
|
||||
|
||||
assertThat(sessions).isEmpty();
|
||||
verify(this.sessions, times(1)).values(isA(EqualPredicate.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByIndexNameAndIndexValuePrincipalIndexNameFound() {
|
||||
String principal = "username";
|
||||
Authentication authentication = new UsernamePasswordAuthenticationToken(principal,
|
||||
"notused", AuthorityUtils.createAuthorityList("ROLE_USER"));
|
||||
List<MapSession> saved = new ArrayList<MapSession>(2);
|
||||
MapSession saved1 = new MapSession();
|
||||
saved1.setAttribute(SPRING_SECURITY_CONTEXT, authentication);
|
||||
saved.add(saved1);
|
||||
MapSession saved2 = new MapSession();
|
||||
saved2.setAttribute(SPRING_SECURITY_CONTEXT, authentication);
|
||||
saved.add(saved2);
|
||||
given(this.sessions.values(isA(EqualPredicate.class))).willReturn(saved);
|
||||
|
||||
Map<String, MapSession> sessions = this.repository.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principal);
|
||||
|
||||
assertThat(sessions).hasSize(2);
|
||||
verify(this.sessions, times(1)).values(isA(EqualPredicate.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.hazelcast.config.annotation.web.http;
|
||||
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.hazelcast.core.IMap;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.session.hazelcast.HazelcastSessionRepository;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Matchers.isA;
|
||||
|
||||
/**
|
||||
* Tests for {@link HazelcastHttpSessionConfiguration}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class HazelcastHttpSessionConfigurationTests {
|
||||
|
||||
private static final String MAP_NAME = "spring:test:sessions";
|
||||
|
||||
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 600;
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Mock
|
||||
private static HazelcastInstance hazelcastInstance;
|
||||
|
||||
@Mock
|
||||
private IMap<Object, Object> sessions;
|
||||
|
||||
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
given(hazelcastInstance.getMap(isA(String.class))).willReturn(this.sessions);
|
||||
}
|
||||
|
||||
@After
|
||||
public void closeContext() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noHazelcastInstanceConfiguration() {
|
||||
this.thrown.expect(UnsatisfiedDependencyException.class);
|
||||
this.thrown.expectMessage("HazelcastInstance");
|
||||
|
||||
registerAndRefresh(EmptyConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultConfiguration() {
|
||||
registerAndRefresh(DefaultConfiguration.class);
|
||||
|
||||
assertThat(this.context.getBean(HazelcastSessionRepository.class))
|
||||
.isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTableName() {
|
||||
registerAndRefresh(CustomSessionMapNameConfiguration.class);
|
||||
|
||||
HazelcastSessionRepository repository = this.context
|
||||
.getBean(HazelcastSessionRepository.class);
|
||||
HazelcastHttpSessionConfiguration configuration = this.context
|
||||
.getBean(HazelcastHttpSessionConfiguration.class);
|
||||
assertThat(repository).isNotNull();
|
||||
assertThat(ReflectionTestUtils.getField(configuration, "sessionMapName"))
|
||||
.isEqualTo(MAP_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setCustomSessionMapName() {
|
||||
registerAndRefresh(BaseConfiguration.class,
|
||||
CustomSessionMapNameSetConfiguration.class);
|
||||
|
||||
HazelcastSessionRepository repository = this.context
|
||||
.getBean(HazelcastSessionRepository.class);
|
||||
HazelcastHttpSessionConfiguration configuration = this.context
|
||||
.getBean(HazelcastHttpSessionConfiguration.class);
|
||||
assertThat(repository).isNotNull();
|
||||
assertThat(ReflectionTestUtils.getField(configuration, "sessionMapName"))
|
||||
.isEqualTo(MAP_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setCustomMaxInactiveIntervalInSeconds() {
|
||||
registerAndRefresh(BaseConfiguration.class,
|
||||
CustomMaxInactiveIntervalInSecondsSetConfiguration.class);
|
||||
|
||||
HazelcastSessionRepository repository = this.context
|
||||
.getBean(HazelcastSessionRepository.class);
|
||||
assertThat(repository).isNotNull();
|
||||
assertThat(ReflectionTestUtils.getField(repository, "defaultMaxInactiveInterval")).isEqualTo(
|
||||
MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customMaxInactiveIntervalInSeconds() {
|
||||
registerAndRefresh(CustomMaxInactiveIntervalInSecondsConfiguration.class);
|
||||
|
||||
HazelcastSessionRepository repository = this.context
|
||||
.getBean(HazelcastSessionRepository.class);
|
||||
assertThat(repository).isNotNull();
|
||||
assertThat(ReflectionTestUtils.getField(repository, "defaultMaxInactiveInterval"))
|
||||
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
}
|
||||
|
||||
private void registerAndRefresh(Class<?>... annotatedClasses) {
|
||||
this.context.register(annotatedClasses);
|
||||
this.context.refresh();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableHazelcastHttpSession
|
||||
static class EmptyConfiguration {
|
||||
}
|
||||
|
||||
static class BaseConfiguration {
|
||||
|
||||
@Bean
|
||||
public HazelcastInstance hazelcastInstance() {
|
||||
return hazelcastInstance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableHazelcastHttpSession
|
||||
static class DefaultConfiguration extends BaseConfiguration {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableHazelcastHttpSession(sessionMapName = MAP_NAME)
|
||||
static class CustomSessionMapNameConfiguration extends BaseConfiguration {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomSessionMapNameSetConfiguration
|
||||
extends HazelcastHttpSessionConfiguration {
|
||||
|
||||
CustomSessionMapNameSetConfiguration() {
|
||||
setSessionMapName(MAP_NAME);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomMaxInactiveIntervalInSecondsSetConfiguration
|
||||
extends HazelcastHttpSessionConfiguration {
|
||||
|
||||
CustomMaxInactiveIntervalInSecondsSetConfiguration() {
|
||||
setMaxInactiveIntervalInSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableHazelcastHttpSession(maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
|
||||
static class CustomMaxInactiveIntervalInSecondsConfiguration
|
||||
extends BaseConfiguration {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<hazelcast
|
||||
xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.5.xsd"
|
||||
xmlns="http://www.hazelcast.com/schema/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<hazelcast xmlns="http://www.hazelcast.com/schema/config"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.6.xsd">
|
||||
|
||||
<group>
|
||||
<name>spring-session-it-test-idle-time-map-name</name>
|
||||
<password>test-pass</password>
|
||||
</group>
|
||||
|
||||
<network>
|
||||
<port auto-increment="true" port-count="100">5701</port>
|
||||
<outbound-ports>
|
||||
@@ -24,14 +26,20 @@
|
||||
</aws>
|
||||
</join>
|
||||
</network>
|
||||
|
||||
<map name="test-sessions">
|
||||
<in-memory-format>BINARY</in-memory-format>
|
||||
<backup-count>1</backup-count>
|
||||
<async-backup-count>0</async-backup-count>
|
||||
<time-to-live-seconds>0</time-to-live-seconds>
|
||||
<max-idle-seconds>300</max-idle-seconds>
|
||||
<merge-policy>com.hazelcast.map.merge.PutIfAbsentMapMergePolicy
|
||||
</merge-policy>
|
||||
<merge-policy>com.hazelcast.map.merge.PutIfAbsentMapMergePolicy</merge-policy>
|
||||
<attributes>
|
||||
<attribute extractor="org.springframework.session.hazelcast.PrincipalNameExtractor">principalName</attribute>
|
||||
</attributes>
|
||||
<indexes>
|
||||
<index ordered="false">principalName</index>
|
||||
</indexes>
|
||||
</map>
|
||||
|
||||
</hazelcast>
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<hazelcast
|
||||
xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.5.xsd"
|
||||
xmlns="http://www.hazelcast.com/schema/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<group>
|
||||
<name>spring-session-it-test-idle-time</name>
|
||||
<password>test-pass</password>
|
||||
</group>
|
||||
<network>
|
||||
<port auto-increment="true" port-count="100">5701</port>
|
||||
<outbound-ports>
|
||||
<ports>0</ports>
|
||||
</outbound-ports>
|
||||
<join>
|
||||
<multicast enabled="false">
|
||||
</multicast>
|
||||
<tcp-ip enabled="true">
|
||||
<interface>127.0.0.1</interface>
|
||||
<member-list>
|
||||
<member>127.0.0.1</member>
|
||||
</member-list>
|
||||
</tcp-ip>
|
||||
<aws enabled="false">
|
||||
</aws>
|
||||
</join>
|
||||
</network>
|
||||
<map name="spring:session:sessions">
|
||||
<in-memory-format>BINARY</in-memory-format>
|
||||
<backup-count>1</backup-count>
|
||||
<async-backup-count>0</async-backup-count>
|
||||
<time-to-live-seconds>0</time-to-live-seconds>
|
||||
<max-idle-seconds>150</max-idle-seconds>
|
||||
<merge-policy>com.hazelcast.map.merge.PutIfAbsentMapMergePolicy
|
||||
</merge-policy>
|
||||
</map>
|
||||
|
||||
</hazelcast>
|
||||
@@ -1,11 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<hazelcast
|
||||
xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.5.xsd"
|
||||
xmlns="http://www.hazelcast.com/schema/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<hazelcast xmlns="http://www.hazelcast.com/schema/config"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.6.xsd">
|
||||
|
||||
<group>
|
||||
<name>spring-session-it-test-map-name</name>
|
||||
<password>test-pass</password>
|
||||
</group>
|
||||
|
||||
<network>
|
||||
<port auto-increment="true" port-count="100">5701</port>
|
||||
<outbound-ports>
|
||||
@@ -24,14 +26,20 @@
|
||||
</aws>
|
||||
</join>
|
||||
</network>
|
||||
|
||||
<map name="my-sessions">
|
||||
<in-memory-format>BINARY</in-memory-format>
|
||||
<backup-count>1</backup-count>
|
||||
<async-backup-count>0</async-backup-count>
|
||||
<time-to-live-seconds>0</time-to-live-seconds>
|
||||
<max-idle-seconds>0</max-idle-seconds>
|
||||
<merge-policy>com.hazelcast.map.merge.PutIfAbsentMapMergePolicy
|
||||
</merge-policy>
|
||||
<merge-policy>com.hazelcast.map.merge.PutIfAbsentMapMergePolicy</merge-policy>
|
||||
<attributes>
|
||||
<attribute extractor="org.springframework.session.hazelcast.PrincipalNameExtractor">principalName</attribute>
|
||||
</attributes>
|
||||
<indexes>
|
||||
<index ordered="false">principalName</index>
|
||||
</indexes>
|
||||
</map>
|
||||
|
||||
</hazelcast>
|
||||
|
||||
Reference in New Issue
Block a user