Optimize save operation in HazelcastSessionRepository (#516)
This commit improves saving of sessions to only execute save operation if something has been changed (e.g. session.setAttribute(String, Object) was called). Further, configurable flush mode that specifies when to write to the backing Hazelcast instance is introduced. It can be 'on save' (default) or 'immediate'. Fixes gh-516, fixes gh-641
This commit is contained in:
committed by
Vedran Pavic
parent
b5ea6c752d
commit
6a78101db5
@@ -22,6 +22,7 @@ import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.hazelcast.HazelcastSessionRepository.HazelcastSession;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -41,7 +42,7 @@ public abstract class AbstractHazelcastRepositoryITests {
|
||||
|
||||
@Test
|
||||
public void createAndDestroySession() {
|
||||
MapSession sessionToSave = this.repository.createSession();
|
||||
HazelcastSession sessionToSave = this.repository.createSession();
|
||||
String sessionId = sessionToSave.getId();
|
||||
|
||||
IMap<String, MapSession> hazelcastMap = this.hazelcast.getMap(
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 org.springframework.session.SessionRepository;
|
||||
|
||||
/**
|
||||
* Specifies when to write to the backing Hazelcast instance.
|
||||
*
|
||||
* @author Aleksandar Stojsavljevic
|
||||
* @since 1.3
|
||||
*/
|
||||
public enum HazelcastFlushMode {
|
||||
/**
|
||||
* Only writes to Hazelcast when
|
||||
* {@link SessionRepository#save(org.springframework.session.Session)} is invoked. In
|
||||
* a web environment this is typically done as soon as the HTTP response is committed.
|
||||
*/
|
||||
ON_SAVE,
|
||||
|
||||
/**
|
||||
* Writes to Hazelcast as soon as possible. For example
|
||||
* {@link SessionRepository#createSession()} will write the session to Hazelcast. Another
|
||||
* example is that setting an attribute on the session will also write to Hazelcast
|
||||
* immediately.
|
||||
*/
|
||||
IMMEDIATE
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
@@ -36,8 +37,10 @@ import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.events.AbstractSessionEvent;
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
import org.springframework.session.events.SessionDeletedEvent;
|
||||
@@ -100,10 +103,11 @@ import org.springframework.util.Assert;
|
||||
* @author Vedran Pavic
|
||||
* @author Tommy Ludwig
|
||||
* @author Mark Anderson
|
||||
* @author Aleksandar Stojsavljevic
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public class HazelcastSessionRepository implements
|
||||
FindByIndexNameSessionRepository<MapSession>,
|
||||
FindByIndexNameSessionRepository<HazelcastSessionRepository.HazelcastSession>,
|
||||
EntryAddedListener<String, MapSession>,
|
||||
EntryEvictedListener<String, MapSession>,
|
||||
EntryRemovedListener<String, MapSession> {
|
||||
@@ -117,6 +121,8 @@ public class HazelcastSessionRepository implements
|
||||
|
||||
private final IMap<String, MapSession> sessions;
|
||||
|
||||
private HazelcastFlushMode hazelcastFlushMode = HazelcastFlushMode.ON_SAVE;
|
||||
|
||||
private ApplicationEventPublisher eventPublisher = new ApplicationEventPublisher() {
|
||||
|
||||
public void publishEvent(ApplicationEvent event) {
|
||||
@@ -175,20 +181,33 @@ public class HazelcastSessionRepository implements
|
||||
this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
|
||||
}
|
||||
|
||||
public MapSession createSession() {
|
||||
MapSession result = new MapSession();
|
||||
/**
|
||||
* Sets the Hazelcast flush mode. Default flush mode is {@link HazelcastFlushMode#ON_SAVE}.
|
||||
*
|
||||
* @param hazelcastFlushMode the new Hazelcast flush mode
|
||||
*/
|
||||
public void setHazelcastFlushMode(HazelcastFlushMode hazelcastFlushMode) {
|
||||
Assert.notNull(hazelcastFlushMode, "HazelcastFlushMode cannot be null");
|
||||
this.hazelcastFlushMode = hazelcastFlushMode;
|
||||
}
|
||||
|
||||
public HazelcastSession createSession() {
|
||||
HazelcastSession result = new HazelcastSession();
|
||||
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 void save(HazelcastSession session) {
|
||||
if (session.isChanged()) {
|
||||
this.sessions.put(session.getId(), session.getDelegate(),
|
||||
session.getMaxInactiveIntervalInSeconds(), TimeUnit.SECONDS);
|
||||
session.markUnchanged();
|
||||
}
|
||||
}
|
||||
|
||||
public MapSession getSession(String id) {
|
||||
public HazelcastSession getSession(String id) {
|
||||
MapSession saved = this.sessions.get(id);
|
||||
if (saved == null) {
|
||||
return null;
|
||||
@@ -197,24 +216,24 @@ public class HazelcastSessionRepository implements
|
||||
delete(saved.getId());
|
||||
return null;
|
||||
}
|
||||
return saved;
|
||||
return new HazelcastSession(saved);
|
||||
}
|
||||
|
||||
public void delete(String id) {
|
||||
this.sessions.remove(id);
|
||||
}
|
||||
|
||||
public Map<String, MapSession> findByIndexNameAndIndexValue(
|
||||
public Map<String, HazelcastSession> 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>(
|
||||
Map<String, HazelcastSession> sessionMap = new HashMap<String, HazelcastSession>(
|
||||
sessions.size());
|
||||
for (MapSession session : sessions) {
|
||||
sessionMap.put(session.getId(), session);
|
||||
sessionMap.put(session.getId(), new HazelcastSession(session));
|
||||
}
|
||||
return sessionMap;
|
||||
}
|
||||
@@ -242,4 +261,106 @@ public class HazelcastSessionRepository implements
|
||||
.publishEvent(new SessionDeletedEvent(this, event.getOldValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom implementation of {@link Session} that uses a {@link MapSession} as the
|
||||
* basis for its mapping. It keeps track if changes have been made since last save.
|
||||
*
|
||||
* @author Aleksandar Stojsavljevic
|
||||
* @since 1.3
|
||||
*/
|
||||
final class HazelcastSession implements ExpiringSession {
|
||||
private final MapSession delegate;
|
||||
private boolean changed;
|
||||
|
||||
/**
|
||||
* Creates a new instance ensuring to mark all of the new attributes to be
|
||||
* persisted in the next save operation.
|
||||
*/
|
||||
HazelcastSession() {
|
||||
this(new MapSession());
|
||||
this.changed = true;
|
||||
flushImmediateIfNecessary();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance from the provided {@link MapSession}.
|
||||
*
|
||||
* @param cached the {@link MapSession} that represents the persisted session that was
|
||||
* retrieved. Cannot be null.
|
||||
*/
|
||||
HazelcastSession(MapSession cached) {
|
||||
Assert.notNull(cached, "MapSession cannot be null");
|
||||
this.delegate = cached;
|
||||
}
|
||||
|
||||
public void setLastAccessedTime(long lastAccessedTime) {
|
||||
this.delegate.setLastAccessedTime(lastAccessedTime);
|
||||
this.changed = true;
|
||||
flushImmediateIfNecessary();
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return this.delegate.isExpired();
|
||||
}
|
||||
|
||||
public long getCreationTime() {
|
||||
return this.delegate.getCreationTime();
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.delegate.getId();
|
||||
}
|
||||
|
||||
public long getLastAccessedTime() {
|
||||
return this.delegate.getLastAccessedTime();
|
||||
}
|
||||
|
||||
public void setMaxInactiveIntervalInSeconds(int interval) {
|
||||
this.delegate.setMaxInactiveIntervalInSeconds(interval);
|
||||
this.changed = true;
|
||||
flushImmediateIfNecessary();
|
||||
}
|
||||
|
||||
public int getMaxInactiveIntervalInSeconds() {
|
||||
return this.delegate.getMaxInactiveIntervalInSeconds();
|
||||
}
|
||||
|
||||
public <T> T getAttribute(String attributeName) {
|
||||
return this.delegate.getAttribute(attributeName);
|
||||
}
|
||||
|
||||
public Set<String> getAttributeNames() {
|
||||
return this.delegate.getAttributeNames();
|
||||
}
|
||||
|
||||
public void setAttribute(String attributeName, Object attributeValue) {
|
||||
this.delegate.setAttribute(attributeName, attributeValue);
|
||||
this.changed = true;
|
||||
flushImmediateIfNecessary();
|
||||
}
|
||||
|
||||
public void removeAttribute(String attributeName) {
|
||||
this.delegate.removeAttribute(attributeName);
|
||||
this.changed = true;
|
||||
flushImmediateIfNecessary();
|
||||
}
|
||||
|
||||
boolean isChanged() {
|
||||
return this.changed;
|
||||
}
|
||||
|
||||
void markUnchanged() {
|
||||
this.changed = false;
|
||||
}
|
||||
|
||||
MapSession getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
private void flushImmediateIfNecessary() {
|
||||
if (HazelcastSessionRepository.this.hazelcastFlushMode == HazelcastFlushMode.IMMEDIATE) {
|
||||
HazelcastSessionRepository.this.save(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,9 @@ 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.SessionRepository;
|
||||
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
|
||||
import org.springframework.session.hazelcast.HazelcastFlushMode;
|
||||
|
||||
/**
|
||||
* Add this annotation to a {@code @Configuration} class to expose the
|
||||
@@ -48,6 +50,7 @@ import org.springframework.session.config.annotation.web.http.EnableSpringHttpSe
|
||||
* instead.
|
||||
*
|
||||
* @author Tommy Ludwig
|
||||
* @author Aleksandar Stojsavljevic
|
||||
* @since 1.1
|
||||
* @see EnableSpringHttpSession
|
||||
*/
|
||||
@@ -73,4 +76,21 @@ public @interface EnableHazelcastHttpSession {
|
||||
*/
|
||||
String sessionMapName() default HazelcastHttpSessionConfiguration.DEFAULT_SESSION_MAP_NAME;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets the flush mode for the Hazelcast sessions. The default is ON_SAVE which only
|
||||
* updates the backing Hazelcast when
|
||||
* {@link SessionRepository#save(org.springframework.session.Session)} is invoked. In
|
||||
* a web environment this happens just before the HTTP response is committed.
|
||||
* </p>
|
||||
* <p>
|
||||
* Setting the value to IMMEDIATE will ensure that the any updates to the Session are
|
||||
* immediately written to the Hazelcast instance.
|
||||
* </p>
|
||||
*
|
||||
* @return the {@link HazelcastFlushMode} to use
|
||||
* @since 1.3
|
||||
*/
|
||||
HazelcastFlushMode hazelcastFlushMode() default HazelcastFlushMode.ON_SAVE;
|
||||
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
||||
import org.springframework.session.hazelcast.HazelcastFlushMode;
|
||||
import org.springframework.session.hazelcast.HazelcastSessionRepository;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
|
||||
@@ -52,6 +53,8 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
|
||||
|
||||
private String sessionMapName = DEFAULT_SESSION_MAP_NAME;
|
||||
|
||||
private HazelcastFlushMode hazelcastFlushMode = HazelcastFlushMode.ON_SAVE;
|
||||
|
||||
@Bean
|
||||
public HazelcastSessionRepository sessionRepository(
|
||||
HazelcastInstance hazelcastInstance,
|
||||
@@ -63,6 +66,7 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
|
||||
sessionRepository.setApplicationEventPublisher(eventPublisher);
|
||||
sessionRepository.setDefaultMaxInactiveInterval(
|
||||
this.maxInactiveIntervalInSeconds);
|
||||
sessionRepository.setHazelcastFlushMode(this.hazelcastFlushMode);
|
||||
return sessionRepository;
|
||||
}
|
||||
|
||||
@@ -73,6 +77,7 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
|
||||
setMaxInactiveIntervalInSeconds(
|
||||
(Integer) enableAttrs.getNumber("maxInactiveIntervalInSeconds"));
|
||||
setSessionMapName(enableAttrs.getString("sessionMapName"));
|
||||
setHazelcastFlushMode((HazelcastFlushMode) enableAttrs.getEnum("hazelcastFlushMode"));
|
||||
}
|
||||
|
||||
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
|
||||
@@ -83,4 +88,7 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
|
||||
this.sessionMapName = sessionMapName;
|
||||
}
|
||||
|
||||
public void setHazelcastFlushMode(HazelcastFlushMode hazelcastFlushMode) {
|
||||
this.hazelcastFlushMode = hazelcastFlushMode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,11 +36,13 @@ import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.hazelcast.HazelcastSessionRepository.HazelcastSession;
|
||||
|
||||
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.reset;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
@@ -49,6 +51,7 @@ import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
* Tests for {@link HazelcastSessionRepository}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @author Aleksandar Stojsavljevic
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class HazelcastSessionRepositoryTests {
|
||||
@@ -78,7 +81,7 @@ public class HazelcastSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
public void createSessionDefaultMaxInactiveInterval() throws Exception {
|
||||
MapSession session = this.repository.createSession();
|
||||
HazelcastSession session = this.repository.createSession();
|
||||
|
||||
assertThat(session.getMaxInactiveIntervalInSeconds())
|
||||
.isEqualTo(new MapSession().getMaxInactiveIntervalInSeconds());
|
||||
@@ -90,61 +93,167 @@ public class HazelcastSessionRepositoryTests {
|
||||
int interval = 1;
|
||||
this.repository.setDefaultMaxInactiveInterval(interval);
|
||||
|
||||
MapSession session = this.repository.createSession();
|
||||
HazelcastSession session = this.repository.createSession();
|
||||
|
||||
assertThat(session.getMaxInactiveIntervalInSeconds()).isEqualTo(interval);
|
||||
verifyZeroInteractions(this.sessions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveNew() {
|
||||
MapSession session = this.repository.createSession();
|
||||
public void saveNewFlushModeOnSave() {
|
||||
HazelcastSession session = this.repository.createSession();
|
||||
verifyZeroInteractions(this.sessions);
|
||||
|
||||
this.repository.save(session);
|
||||
|
||||
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session),
|
||||
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session.getDelegate()),
|
||||
isA(Long.class), eq(TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveUpdatedAttributes() {
|
||||
MapSession session = new MapSession();
|
||||
public void saveNewFlushModeImmediate() {
|
||||
this.repository.setHazelcastFlushMode(HazelcastFlushMode.IMMEDIATE);
|
||||
|
||||
HazelcastSession session = this.repository.createSession();
|
||||
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session.getDelegate()),
|
||||
isA(Long.class), eq(TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveUpdatedAttributeFlushModeOnSave() {
|
||||
HazelcastSession session = this.repository.createSession();
|
||||
session.setAttribute("testName", "testValue");
|
||||
verifyZeroInteractions(this.sessions);
|
||||
|
||||
this.repository.save(session);
|
||||
|
||||
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session),
|
||||
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session.getDelegate()),
|
||||
isA(Long.class), eq(TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void saveUpdatedLastAccessedTime() {
|
||||
MapSession session = new MapSession();
|
||||
public void saveUpdatedAttributeFlushModeImmediate() {
|
||||
this.repository.setHazelcastFlushMode(HazelcastFlushMode.IMMEDIATE);
|
||||
|
||||
HazelcastSession session = this.repository.createSession();
|
||||
session.setAttribute("testName", "testValue");
|
||||
verify(this.sessions, times(2)).put(eq(session.getId()), eq(session.getDelegate()),
|
||||
isA(Long.class), eq(TimeUnit.SECONDS));
|
||||
reset(this.sessions);
|
||||
|
||||
this.repository.save(session);
|
||||
verifyZeroInteractions(this.sessions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeAttributeFlushModeOnSave() {
|
||||
HazelcastSession session = this.repository.createSession();
|
||||
session.removeAttribute("testName");
|
||||
verifyZeroInteractions(this.sessions);
|
||||
|
||||
this.repository.save(session);
|
||||
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session.getDelegate()),
|
||||
isA(Long.class), eq(TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void removeAttributeFlushModeImmediate() {
|
||||
this.repository.setHazelcastFlushMode(HazelcastFlushMode.IMMEDIATE);
|
||||
|
||||
HazelcastSession session = this.repository.createSession();
|
||||
session.removeAttribute("testName");
|
||||
verify(this.sessions, times(2)).put(eq(session.getId()), eq(session.getDelegate()),
|
||||
isA(Long.class), eq(TimeUnit.SECONDS));
|
||||
reset(this.sessions);
|
||||
|
||||
this.repository.save(session);
|
||||
verifyZeroInteractions(this.sessions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveUpdatedLastAccessedTimeFlushModeOnSave() {
|
||||
HazelcastSession session = this.repository.createSession();
|
||||
session.setLastAccessedTime(System.currentTimeMillis());
|
||||
verifyZeroInteractions(this.sessions);
|
||||
|
||||
this.repository.save(session);
|
||||
|
||||
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session),
|
||||
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session.getDelegate()),
|
||||
isA(Long.class), eq(TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void saveUnchanged() {
|
||||
MapSession session = new MapSession();
|
||||
public void saveUpdatedLastAccessedTimeFlushModeImmediate() {
|
||||
this.repository.setHazelcastFlushMode(HazelcastFlushMode.IMMEDIATE);
|
||||
|
||||
HazelcastSession session = this.repository.createSession();
|
||||
session.setLastAccessedTime(System.currentTimeMillis());
|
||||
verify(this.sessions, times(2)).put(eq(session.getId()), eq(session.getDelegate()),
|
||||
isA(Long.class), eq(TimeUnit.SECONDS));
|
||||
reset(this.sessions);
|
||||
|
||||
this.repository.save(session);
|
||||
verifyZeroInteractions(this.sessions);
|
||||
}
|
||||
|
||||
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session),
|
||||
@Test
|
||||
public void saveUpdatedMaxInactiveIntervalInSecondsFlushModeOnSave() {
|
||||
HazelcastSession session = this.repository.createSession();
|
||||
session.setMaxInactiveIntervalInSeconds(1);
|
||||
verifyZeroInteractions(this.sessions);
|
||||
|
||||
this.repository.save(session);
|
||||
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session.getDelegate()),
|
||||
isA(Long.class), eq(TimeUnit.SECONDS));
|
||||
// TODO - once save optimization is implemented, should be replaced with:
|
||||
//verifyZeroInteractions(this.sessions);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void saveUpdatedMaxInactiveIntervalInSecondsFlushModeImmediate() {
|
||||
this.repository.setHazelcastFlushMode(HazelcastFlushMode.IMMEDIATE);
|
||||
|
||||
HazelcastSession session = this.repository.createSession();
|
||||
session.setMaxInactiveIntervalInSeconds(1);
|
||||
verify(this.sessions, times(2)).put(eq(session.getId()), eq(session.getDelegate()),
|
||||
isA(Long.class), eq(TimeUnit.SECONDS));
|
||||
reset(this.sessions);
|
||||
|
||||
this.repository.save(session);
|
||||
verifyZeroInteractions(this.sessions);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void saveUnchangedFlushModeOnSave() {
|
||||
HazelcastSession session = this.repository.createSession();
|
||||
this.repository.save(session);
|
||||
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session.getDelegate()),
|
||||
isA(Long.class), eq(TimeUnit.SECONDS));
|
||||
reset(this.sessions);
|
||||
|
||||
this.repository.save(session);
|
||||
verifyZeroInteractions(this.sessions);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void saveUnchangedFlushModeImmediate() {
|
||||
this.repository.setHazelcastFlushMode(HazelcastFlushMode.IMMEDIATE);
|
||||
|
||||
HazelcastSession session = this.repository.createSession();
|
||||
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session.getDelegate()),
|
||||
isA(Long.class), eq(TimeUnit.SECONDS));
|
||||
reset(this.sessions);
|
||||
|
||||
this.repository.save(session);
|
||||
verifyZeroInteractions(this.sessions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionNotFound() {
|
||||
String sessionId = "testSessionId";
|
||||
|
||||
MapSession session = this.repository.getSession(sessionId);
|
||||
HazelcastSession session = this.repository.getSession(sessionId);
|
||||
|
||||
assertThat(session).isNull();
|
||||
verify(this.sessions, times(1)).get(eq(sessionId));
|
||||
@@ -157,7 +266,7 @@ public class HazelcastSessionRepositoryTests {
|
||||
(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS * 1000 + 1000));
|
||||
given(this.sessions.get(eq(expired.getId()))).willReturn(expired);
|
||||
|
||||
MapSession session = this.repository.getSession(expired.getId());
|
||||
HazelcastSession session = this.repository.getSession(expired.getId());
|
||||
|
||||
assertThat(session).isNull();
|
||||
verify(this.sessions, times(1)).get(eq(expired.getId()));
|
||||
@@ -170,7 +279,7 @@ public class HazelcastSessionRepositoryTests {
|
||||
saved.setAttribute("savedName", "savedValue");
|
||||
given(this.sessions.get(eq(saved.getId()))).willReturn(saved);
|
||||
|
||||
MapSession session = this.repository.getSession(saved.getId());
|
||||
HazelcastSession session = this.repository.getSession(saved.getId());
|
||||
|
||||
assertThat(session.getId()).isEqualTo(saved.getId());
|
||||
assertThat(session.getAttribute("savedName")).isEqualTo("savedValue");
|
||||
@@ -190,7 +299,7 @@ public class HazelcastSessionRepositoryTests {
|
||||
public void findByIndexNameAndIndexValueUnknownIndexName() {
|
||||
String indexValue = "testIndexValue";
|
||||
|
||||
Map<String, MapSession> sessions = this.repository.findByIndexNameAndIndexValue(
|
||||
Map<String, HazelcastSession> sessions = this.repository.findByIndexNameAndIndexValue(
|
||||
"testIndexName", indexValue);
|
||||
|
||||
assertThat(sessions).isEmpty();
|
||||
@@ -201,7 +310,7 @@ public class HazelcastSessionRepositoryTests {
|
||||
public void findByIndexNameAndIndexValuePrincipalIndexNameNotFound() {
|
||||
String principal = "username";
|
||||
|
||||
Map<String, MapSession> sessions = this.repository.findByIndexNameAndIndexValue(
|
||||
Map<String, HazelcastSession> sessions = this.repository.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principal);
|
||||
|
||||
assertThat(sessions).isEmpty();
|
||||
@@ -222,7 +331,7 @@ public class HazelcastSessionRepositoryTests {
|
||||
saved.add(saved2);
|
||||
given(this.sessions.values(isA(EqualPredicate.class))).willReturn(saved);
|
||||
|
||||
Map<String, MapSession> sessions = this.repository.findByIndexNameAndIndexValue(
|
||||
Map<String, HazelcastSession> sessions = this.repository.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principal);
|
||||
|
||||
assertThat(sessions).hasSize(2);
|
||||
|
||||
@@ -31,6 +31,7 @@ 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.HazelcastFlushMode;
|
||||
import org.springframework.session.hazelcast.HazelcastSessionRepository;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
@@ -42,6 +43,7 @@ import static org.mockito.Matchers.isA;
|
||||
* Tests for {@link HazelcastHttpSessionConfiguration}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @author Aleksandar Stojsavljevic
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class HazelcastHttpSessionConfigurationTests {
|
||||
@@ -50,6 +52,8 @@ public class HazelcastHttpSessionConfigurationTests {
|
||||
|
||||
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 600;
|
||||
|
||||
private static final HazelcastFlushMode HAZELCAST_FLUSH_MODE = HazelcastFlushMode.IMMEDIATE;
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@@ -139,6 +143,29 @@ public class HazelcastHttpSessionConfigurationTests {
|
||||
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customFlushImmediately() {
|
||||
registerAndRefresh(CustomFlushImmediatelyConfiguration.class);
|
||||
|
||||
HazelcastSessionRepository repository = this.context
|
||||
.getBean(HazelcastSessionRepository.class);
|
||||
assertThat(repository).isNotNull();
|
||||
assertThat(ReflectionTestUtils.getField(repository, "hazelcastFlushMode")).isEqualTo(
|
||||
HazelcastFlushMode.IMMEDIATE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setCustomFlushImmediately() {
|
||||
registerAndRefresh(BaseConfiguration.class,
|
||||
CustomFlushImmediatelySetConfiguration.class);
|
||||
|
||||
HazelcastSessionRepository repository = this.context
|
||||
.getBean(HazelcastSessionRepository.class);
|
||||
assertThat(repository).isNotNull();
|
||||
assertThat(ReflectionTestUtils.getField(repository, "hazelcastFlushMode")).isEqualTo(
|
||||
HazelcastFlushMode.IMMEDIATE);
|
||||
}
|
||||
|
||||
private void registerAndRefresh(Class<?>... annotatedClasses) {
|
||||
this.context.register(annotatedClasses);
|
||||
this.context.refresh();
|
||||
@@ -194,4 +221,20 @@ public class HazelcastHttpSessionConfigurationTests {
|
||||
extends BaseConfiguration {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomFlushImmediatelySetConfiguration
|
||||
extends HazelcastHttpSessionConfiguration {
|
||||
|
||||
CustomFlushImmediatelySetConfiguration() {
|
||||
setHazelcastFlushMode(HAZELCAST_FLUSH_MODE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableHazelcastHttpSession(hazelcastFlushMode = HazelcastFlushMode.IMMEDIATE)
|
||||
static class CustomFlushImmediatelyConfiguration
|
||||
extends BaseConfiguration {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user