Add support for Hazelcast 4

Closes gh-1584
This commit is contained in:
Eleftheria Stein
2020-09-14 17:50:03 +02:00
parent c0c672b9f8
commit 2b6489c2bd
18 changed files with 2050 additions and 5 deletions

View File

@@ -33,7 +33,6 @@ import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
import org.springframework.session.hazelcast.HazelcastFlushMode;
import org.springframework.session.hazelcast.HazelcastIndexedSessionRepository;
import org.springframework.session.web.http.SessionRepositoryFilter;
/**
@@ -68,7 +67,7 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(HazelcastHttpSessionConfiguration.class)
@Import(EnableHazelcastHttpSessionSelector.class)
@Configuration(proxyBeanMethods = false)
public @interface EnableHazelcastHttpSession {
@@ -81,11 +80,10 @@ public @interface EnableHazelcastHttpSession {
/**
* This is the name of the Map that will be used in Hazelcast to store the session
* data. Default is
* {@link HazelcastIndexedSessionRepository#DEFAULT_SESSION_MAP_NAME}.
* data. Default is "spring:session:sessions".
* @return the name of the Map to store the sessions in Hazelcast
*/
String sessionMapName() default HazelcastIndexedSessionRepository.DEFAULT_SESSION_MAP_NAME;
String sessionMapName() default "spring:session:sessions";
/**
* Flush mode for the Hazelcast sessions. The default is {@code ON_SAVE} which only

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2014-2020 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
*
* https://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 java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
/**
* Dynamically determines which Hazelcast configuration class to include.
*
* @author Eleftheria Stein
* @since 2.4.0
*/
class EnableHazelcastHttpSessionSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
ClassLoader classLoader = getClass().getClassLoader();
boolean hazelcast4OnClasspath = ClassUtils.isPresent("com.hazelcast.map.IMap", classLoader);
List<String> classNames = new ArrayList<>(1);
if (hazelcast4OnClasspath) {
classNames.add(Hazelcast4HttpSessionConfiguration.class.getName());
}
else {
classNames.add(HazelcastHttpSessionConfiguration.class.getName());
}
return classNames.toArray(new String[0]);
}
}

View File

@@ -0,0 +1,161 @@
/*
* Copyright 2014-2020 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
*
* https://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 java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.hazelcast.core.HazelcastInstance;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
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.FlushMode;
import org.springframework.session.IndexResolver;
import org.springframework.session.MapSession;
import org.springframework.session.SaveMode;
import org.springframework.session.Session;
import org.springframework.session.config.SessionRepositoryCustomizer;
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
import org.springframework.session.hazelcast.Hazelcast4IndexedSessionRepository;
import org.springframework.session.hazelcast.HazelcastFlushMode;
import org.springframework.session.hazelcast.config.annotation.SpringSessionHazelcastInstance;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.util.StringUtils;
/**
* Exposes the {@link SessionRepositoryFilter} as a bean named
* {@code springSessionRepositoryFilter}. In order to use this a single
* {@link HazelcastInstance} configured for Hazelcast 4 must be exposed as a Bean.
*
* @author Eleftheria Stein
* @since 2.4.0
* @see EnableHazelcastHttpSession
*/
@Configuration(proxyBeanMethods = false)
public class Hazelcast4HttpSessionConfiguration extends SpringHttpSessionConfiguration implements ImportAware {
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
private String sessionMapName = Hazelcast4IndexedSessionRepository.DEFAULT_SESSION_MAP_NAME;
private FlushMode flushMode = FlushMode.ON_SAVE;
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
private HazelcastInstance hazelcastInstance;
private ApplicationEventPublisher applicationEventPublisher;
private IndexResolver<Session> indexResolver;
private List<SessionRepositoryCustomizer<Hazelcast4IndexedSessionRepository>> sessionRepositoryCustomizers;
@Bean
public Hazelcast4IndexedSessionRepository sessionRepository() {
Hazelcast4IndexedSessionRepository sessionRepository = new Hazelcast4IndexedSessionRepository(
this.hazelcastInstance);
sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher);
if (this.indexResolver != null) {
sessionRepository.setIndexResolver(this.indexResolver);
}
if (StringUtils.hasText(this.sessionMapName)) {
sessionRepository.setSessionMapName(this.sessionMapName);
}
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
sessionRepository.setFlushMode(this.flushMode);
sessionRepository.setSaveMode(this.saveMode);
this.sessionRepositoryCustomizers
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
return sessionRepository;
}
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
}
public void setSessionMapName(String sessionMapName) {
this.sessionMapName = sessionMapName;
}
@Deprecated
public void setHazelcastFlushMode(HazelcastFlushMode hazelcastFlushMode) {
setFlushMode(hazelcastFlushMode.getFlushMode());
}
public void setFlushMode(FlushMode flushMode) {
this.flushMode = flushMode;
}
public void setSaveMode(SaveMode saveMode) {
this.saveMode = saveMode;
}
@Autowired
public void setHazelcastInstance(
@SpringSessionHazelcastInstance ObjectProvider<HazelcastInstance> springSessionHazelcastInstance,
ObjectProvider<HazelcastInstance> hazelcastInstance) {
HazelcastInstance hazelcastInstanceToUse = springSessionHazelcastInstance.getIfAvailable();
if (hazelcastInstanceToUse == null) {
hazelcastInstanceToUse = hazelcastInstance.getObject();
}
this.hazelcastInstance = hazelcastInstanceToUse;
}
@Autowired
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
@Autowired(required = false)
public void setIndexResolver(IndexResolver<Session> indexResolver) {
this.indexResolver = indexResolver;
}
@Autowired(required = false)
public void setSessionRepositoryCustomizer(
ObjectProvider<SessionRepositoryCustomizer<Hazelcast4IndexedSessionRepository>> sessionRepositoryCustomizers) {
this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList());
}
@Override
@SuppressWarnings("deprecation")
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> attributeMap = importMetadata
.getAnnotationAttributes(EnableHazelcastHttpSession.class.getName());
AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap);
this.maxInactiveIntervalInSeconds = attributes.getNumber("maxInactiveIntervalInSeconds");
String sessionMapNameValue = attributes.getString("sessionMapName");
if (StringUtils.hasText(sessionMapNameValue)) {
this.sessionMapName = sessionMapNameValue;
}
FlushMode flushMode = attributes.getEnum("flushMode");
HazelcastFlushMode hazelcastFlushMode = attributes.getEnum("hazelcastFlushMode");
if (flushMode == FlushMode.ON_SAVE && hazelcastFlushMode != HazelcastFlushMode.ON_SAVE) {
flushMode = hazelcastFlushMode.getFlushMode();
}
this.flushMode = flushMode;
this.saveMode = attributes.getEnum("saveMode");
}
}