Improve Hazelcast configuration

This commit improves Hazelcast configuration by introducing `@SpringSessionHazelcastInstance` qualifier for explicitly declaring a `HazelcastInstance` to be used by Spring Session. This is in particular useful in scenarios with multiple `HazelcastInstance` beans present in the application context.

 Closes gh-912
This commit is contained in:
Vedran Pavic
2017-10-02 12:52:52 +02:00
parent 7e26897ec2
commit 34f29cf36c
3 changed files with 241 additions and 33 deletions

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2014-2017 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;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.hazelcast.core.HazelcastInstance;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.session.hazelcast.HazelcastSessionRepository;
/**
* Qualifier annotation for a {@link HazelcastInstance} to be injected in
* {@link HazelcastSessionRepository}.
*
* @author Vedran Pavic
* @since 2.0.0
*/
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier
public @interface SpringSessionHazelcastInstance {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2017 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.
@@ -21,6 +21,7 @@ import java.util.Map;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -31,6 +32,7 @@ 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.hazelcast.config.annotation.SpringSessionHazelcastInstance;
import org.springframework.session.web.http.SessionRepositoryFilter;
/**
@@ -57,15 +59,21 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
@Bean
public HazelcastSessionRepository sessionRepository(
HazelcastInstance hazelcastInstance,
@SpringSessionHazelcastInstance ObjectProvider<HazelcastInstance> springSessionHazelcastInstance,
ObjectProvider<HazelcastInstance> hazelcastInstance,
ApplicationEventPublisher eventPublisher) {
IMap<String, MapSession> sessions = hazelcastInstance.getMap(
this.sessionMapName);
HazelcastInstance hazelcastInstanceToUse = springSessionHazelcastInstance
.getIfAvailable();
if (hazelcastInstanceToUse == null) {
hazelcastInstanceToUse = hazelcastInstance.getObject();
}
IMap<String, MapSession> sessions = hazelcastInstanceToUse
.getMap(this.sessionMapName);
HazelcastSessionRepository sessionRepository = new HazelcastSessionRepository(
sessions);
sessionRepository.setApplicationEventPublisher(eventPublisher);
sessionRepository.setDefaultMaxInactiveInterval(
this.maxInactiveIntervalInSeconds);
sessionRepository
.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
sessionRepository.setHazelcastFlushMode(this.hazelcastFlushMode);
return sessionRepository;
}

View File

@@ -19,25 +19,24 @@ 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.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.UnsatisfiedDependencyException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.session.hazelcast.HazelcastFlushMode;
import org.springframework.session.hazelcast.HazelcastSessionRepository;
import org.springframework.session.hazelcast.config.annotation.SpringSessionHazelcastInstance;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link HazelcastHttpSessionConfiguration}.
@@ -45,7 +44,6 @@ import static org.mockito.BDDMockito.given;
* @author Vedran Pavic
* @author Aleksandar Stojsavljevic
*/
@RunWith(MockitoJUnitRunner.class)
public class HazelcastHttpSessionConfigurationTests {
private static final String MAP_NAME = "spring:test:sessions";
@@ -57,19 +55,8 @@ public class HazelcastHttpSessionConfigurationTests {
@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) {
@@ -79,18 +66,17 @@ public class HazelcastHttpSessionConfigurationTests {
@Test
public void noHazelcastInstanceConfiguration() {
this.thrown.expect(UnsatisfiedDependencyException.class);
this.thrown.expect(BeanCreationException.class);
this.thrown.expectMessage("HazelcastInstance");
registerAndRefresh(EmptyConfiguration.class);
registerAndRefresh(NoHazelcastInstanceConfiguration.class);
}
@Test
public void defaultConfiguration() {
registerAndRefresh(DefaultConfiguration.class);
assertThat(this.context.getBean(HazelcastSessionRepository.class))
.isNotNull();
assertThat(this.context.getBean(HazelcastSessionRepository.class)).isNotNull();
}
@Test
@@ -128,8 +114,8 @@ public class HazelcastHttpSessionConfigurationTests {
HazelcastSessionRepository repository = this.context
.getBean(HazelcastSessionRepository.class);
assertThat(repository).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "defaultMaxInactiveInterval")).isEqualTo(
MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(ReflectionTestUtils.getField(repository, "defaultMaxInactiveInterval"))
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
}
@Test
@@ -166,6 +152,70 @@ public class HazelcastHttpSessionConfigurationTests {
.isEqualTo(HazelcastFlushMode.IMMEDIATE);
}
@Test
public void qualifiedHazelcastInstanceConfiguration() {
registerAndRefresh(QualifiedHazelcastInstanceConfiguration.class);
HazelcastSessionRepository repository = this.context
.getBean(HazelcastSessionRepository.class);
HazelcastInstance hazelcastInstance = this.context
.getBean("qualifiedHazelcastInstance", HazelcastInstance.class);
assertThat(repository).isNotNull();
assertThat(hazelcastInstance).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "sessions")).isEqualTo(
QualifiedHazelcastInstanceConfiguration.qualifiedHazelcastInstanceSessions);
}
@Test
public void primaryHazelcastInstanceConfiguration() {
registerAndRefresh(PrimaryHazelcastInstanceConfiguration.class);
HazelcastSessionRepository repository = this.context
.getBean(HazelcastSessionRepository.class);
HazelcastInstance hazelcastInstance = this.context
.getBean("primaryHazelcastInstance", HazelcastInstance.class);
assertThat(repository).isNotNull();
assertThat(hazelcastInstance).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "sessions")).isEqualTo(
PrimaryHazelcastInstanceConfiguration.primaryHazelcastInstanceSessions);
}
@Test
public void qualifiedAndPrimaryHazelcastInstanceConfiguration() {
registerAndRefresh(QualifiedAndPrimaryHazelcastInstanceConfiguration.class);
HazelcastSessionRepository repository = this.context
.getBean(HazelcastSessionRepository.class);
HazelcastInstance hazelcastInstance = this.context
.getBean("qualifiedHazelcastInstance", HazelcastInstance.class);
assertThat(repository).isNotNull();
assertThat(hazelcastInstance).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "sessions")).isEqualTo(
QualifiedAndPrimaryHazelcastInstanceConfiguration.qualifiedHazelcastInstanceSessions);
}
@Test
public void namedDataSourceConfiguration() {
registerAndRefresh(NamedHazelcastInstanceConfiguration.class);
HazelcastSessionRepository repository = this.context
.getBean(HazelcastSessionRepository.class);
HazelcastInstance hazelcastInstance = this.context.getBean("hazelcastInstance",
HazelcastInstance.class);
assertThat(repository).isNotNull();
assertThat(hazelcastInstance).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "sessions")).isEqualTo(
NamedHazelcastInstanceConfiguration.hazelcastInstanceSessions);
}
@Test
public void multipleDataSourceConfiguration() {
this.thrown.expect(BeanCreationException.class);
this.thrown.expectMessage("sessionRepository");
registerAndRefresh(MultipleHazelcastInstanceConfiguration.class);
}
private void registerAndRefresh(Class<?>... annotatedClasses) {
this.context.register(annotatedClasses);
this.context.refresh();
@@ -173,13 +223,19 @@ public class HazelcastHttpSessionConfigurationTests {
@Configuration
@EnableHazelcastHttpSession
static class EmptyConfiguration {
static class NoHazelcastInstanceConfiguration {
}
static class BaseConfiguration {
@SuppressWarnings("unchecked")
static IMap<Object, Object> defaultHazelcastInstanceSessions = mock(IMap.class);
@Bean
public HazelcastInstance hazelcastInstance() {
public HazelcastInstance defaultHazelcastInstance() {
HazelcastInstance hazelcastInstance = mock(HazelcastInstance.class);
given(hazelcastInstance.getMap(anyString()))
.willReturn(defaultHazelcastInstanceSessions);
return hazelcastInstance;
}
@@ -233,8 +289,108 @@ public class HazelcastHttpSessionConfigurationTests {
@Configuration
@EnableHazelcastHttpSession(hazelcastFlushMode = HazelcastFlushMode.IMMEDIATE)
static class CustomFlushImmediatelyConfiguration
static class CustomFlushImmediatelyConfiguration extends BaseConfiguration {
}
@Configuration
@EnableHazelcastHttpSession
static class QualifiedHazelcastInstanceConfiguration extends BaseConfiguration {
@SuppressWarnings("unchecked")
static IMap<Object, Object> qualifiedHazelcastInstanceSessions = mock(IMap.class);
@Bean
@SpringSessionHazelcastInstance
public HazelcastInstance qualifiedHazelcastInstance() {
HazelcastInstance hazelcastInstance = mock(HazelcastInstance.class);
given(hazelcastInstance.getMap(anyString()))
.willReturn(qualifiedHazelcastInstanceSessions);
return hazelcastInstance;
}
}
@Configuration
@EnableHazelcastHttpSession
static class PrimaryHazelcastInstanceConfiguration extends BaseConfiguration {
@SuppressWarnings("unchecked")
static IMap<Object, Object> primaryHazelcastInstanceSessions = mock(IMap.class);
@Bean
@Primary
public HazelcastInstance primaryHazelcastInstance() {
HazelcastInstance hazelcastInstance = mock(HazelcastInstance.class);
given(hazelcastInstance.getMap(anyString()))
.willReturn(primaryHazelcastInstanceSessions);
return hazelcastInstance;
}
}
@Configuration
@EnableHazelcastHttpSession
static class QualifiedAndPrimaryHazelcastInstanceConfiguration
extends BaseConfiguration {
@SuppressWarnings("unchecked")
static IMap<Object, Object> qualifiedHazelcastInstanceSessions = mock(IMap.class);
@SuppressWarnings("unchecked")
static IMap<Object, Object> primaryHazelcastInstanceSessions = mock(IMap.class);
@Bean
@SpringSessionHazelcastInstance
public HazelcastInstance qualifiedHazelcastInstance() {
HazelcastInstance hazelcastInstance = mock(HazelcastInstance.class);
given(hazelcastInstance.getMap(anyString()))
.willReturn(qualifiedHazelcastInstanceSessions);
return hazelcastInstance;
}
@Bean
@Primary
public HazelcastInstance primaryHazelcastInstance() {
HazelcastInstance hazelcastInstance = mock(HazelcastInstance.class);
given(hazelcastInstance.getMap(anyString()))
.willReturn(primaryHazelcastInstanceSessions);
return hazelcastInstance;
}
}
@Configuration
@EnableHazelcastHttpSession
static class NamedHazelcastInstanceConfiguration extends BaseConfiguration {
@SuppressWarnings("unchecked")
static IMap<Object, Object> hazelcastInstanceSessions = mock(IMap.class);
@Bean
public HazelcastInstance hazelcastInstance() {
HazelcastInstance hazelcastInstance = mock(HazelcastInstance.class);
given(hazelcastInstance.getMap(anyString()))
.willReturn(hazelcastInstanceSessions);
return hazelcastInstance;
}
}
@Configuration
@EnableHazelcastHttpSession
static class MultipleHazelcastInstanceConfiguration extends BaseConfiguration {
@SuppressWarnings("unchecked")
static IMap<Object, Object> secondaryHazelcastInstanceSessions = mock(IMap.class);
@Bean
public HazelcastInstance secondaryHazelcastInstance() {
HazelcastInstance hazelcastInstance = mock(HazelcastInstance.class);
given(hazelcastInstance.getMap(anyString()))
.willReturn(secondaryHazelcastInstanceSessions);
return hazelcastInstance;
}
}
}