diff --git a/spring-session/src/integration-test/java/org/springframework/session/hazelcast/AbstractHazelcastRepositoryITests.java b/spring-session/src/integration-test/java/org/springframework/session/hazelcast/AbstractHazelcastRepositoryITests.java index d5fba1f..9cd1fea 100644 --- a/spring-session/src/integration-test/java/org/springframework/session/hazelcast/AbstractHazelcastRepositoryITests.java +++ b/spring-session/src/integration-test/java/org/springframework/session/hazelcast/AbstractHazelcastRepositoryITests.java @@ -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 hazelcastMap = this.hazelcast.getMap( diff --git a/spring-session/src/main/java/org/springframework/session/hazelcast/HazelcastFlushMode.java b/spring-session/src/main/java/org/springframework/session/hazelcast/HazelcastFlushMode.java new file mode 100644 index 0000000..90fbcbe --- /dev/null +++ b/spring-session/src/main/java/org/springframework/session/hazelcast/HazelcastFlushMode.java @@ -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 +} diff --git a/spring-session/src/main/java/org/springframework/session/hazelcast/HazelcastSessionRepository.java b/spring-session/src/main/java/org/springframework/session/hazelcast/HazelcastSessionRepository.java index 7f82066..a267365 100644 --- a/spring-session/src/main/java/org/springframework/session/hazelcast/HazelcastSessionRepository.java +++ b/spring-session/src/main/java/org/springframework/session/hazelcast/HazelcastSessionRepository.java @@ -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, + FindByIndexNameSessionRepository, EntryAddedListener, EntryEvictedListener, EntryRemovedListener { @@ -117,6 +121,8 @@ public class HazelcastSessionRepository implements private final IMap 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 findByIndexNameAndIndexValue( + public Map findByIndexNameAndIndexValue( String indexName, String indexValue) { if (!PRINCIPAL_NAME_INDEX_NAME.equals(indexName)) { return Collections.emptyMap(); } Collection sessions = this.sessions.values( Predicates.equal(PRINCIPAL_NAME_ATTRIBUTE, indexValue)); - Map sessionMap = new HashMap( + Map sessionMap = new HashMap( 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 getAttribute(String attributeName) { + return this.delegate.getAttribute(attributeName); + } + + public Set 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); + } + } + } } diff --git a/spring-session/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/EnableHazelcastHttpSession.java b/spring-session/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/EnableHazelcastHttpSession.java index 702b815..a7b6993 100644 --- a/spring-session/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/EnableHazelcastHttpSession.java +++ b/spring-session/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/EnableHazelcastHttpSession.java @@ -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; + /** + *

+ * 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. + *

+ *

+ * Setting the value to IMMEDIATE will ensure that the any updates to the Session are + * immediately written to the Hazelcast instance. + *

+ * + * @return the {@link HazelcastFlushMode} to use + * @since 1.3 + */ + HazelcastFlushMode hazelcastFlushMode() default HazelcastFlushMode.ON_SAVE; + } diff --git a/spring-session/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/HazelcastHttpSessionConfiguration.java b/spring-session/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/HazelcastHttpSessionConfiguration.java index b008204..6d06596 100644 --- a/spring-session/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/HazelcastHttpSessionConfiguration.java +++ b/spring-session/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/HazelcastHttpSessionConfiguration.java @@ -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; + } } diff --git a/spring-session/src/test/java/org/springframework/session/hazelcast/HazelcastSessionRepositoryTests.java b/spring-session/src/test/java/org/springframework/session/hazelcast/HazelcastSessionRepositoryTests.java index fa69ee9..5fdc63f 100644 --- a/spring-session/src/test/java/org/springframework/session/hazelcast/HazelcastSessionRepositoryTests.java +++ b/spring-session/src/test/java/org/springframework/session/hazelcast/HazelcastSessionRepositoryTests.java @@ -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 sessions = this.repository.findByIndexNameAndIndexValue( + Map sessions = this.repository.findByIndexNameAndIndexValue( "testIndexName", indexValue); assertThat(sessions).isEmpty(); @@ -201,7 +310,7 @@ public class HazelcastSessionRepositoryTests { public void findByIndexNameAndIndexValuePrincipalIndexNameNotFound() { String principal = "username"; - Map sessions = this.repository.findByIndexNameAndIndexValue( + Map 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 sessions = this.repository.findByIndexNameAndIndexValue( + Map sessions = this.repository.findByIndexNameAndIndexValue( FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principal); assertThat(sessions).hasSize(2); diff --git a/spring-session/src/test/java/org/springframework/session/hazelcast/config/annotation/web/http/HazelcastHttpSessionConfigurationTests.java b/spring-session/src/test/java/org/springframework/session/hazelcast/config/annotation/web/http/HazelcastHttpSessionConfigurationTests.java index e907ea5..7f898fb 100644 --- a/spring-session/src/test/java/org/springframework/session/hazelcast/config/annotation/web/http/HazelcastHttpSessionConfigurationTests.java +++ b/spring-session/src/test/java/org/springframework/session/hazelcast/config/annotation/web/http/HazelcastHttpSessionConfigurationTests.java @@ -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 { + } + }