From 2de17a52f6d688b5b3307b91e01bc607a89b7637 Mon Sep 17 00:00:00 2001
From: Sebastian Laskawiec
Date: Thu, 13 Oct 2016 10:39:07 +0200
Subject: [PATCH] ISPN-7009 Spring-session implementation
---
.../asciidoc/upgrading/upgrading.asciidoc | 14 +-
.../asciidoc/user_guide/integrations.adoc | 77 +++++++++-
integrationtests/spring-boot-it/pom.xml | 145 ++++++++++++++++++
.../session/AbstractSpringSessionTCK.java | 39 +++++
.../session/configuration/SecurityConfig.java | 18 +++
.../boot/session/configuration/WebConfig.java | 28 ++++
.../embedded/EmbeddedConfiguration.java | 22 +++
.../embedded/EmbeddedSpringSessionTest.java | 12 ++
.../session/remote/RemoteConfiguration.java | 30 ++++
.../remote/RemoteSpringSessionTest.java | 45 ++++++
.../boot/session/web/TestRESTController.java | 17 ++
parent/pom.xml | 3 +-
pom.xml | 1 +
spring/spring4/pom.xml | 10 ++
spring/spring4/spring4-common/pom.xml | 22 +++
.../org/infinispan/spring/package-info.java | 5 +
.../spring/provider/CacheDelegate.java | 10 +-
.../spring/provider/SpringCache.java | 9 ++
.../AbstractApplicationPublisherBridge.java | 61 ++++++++
.../AbstractInfinispanSessionRepository.java | 95 ++++++++++++
...finispanApplicationPublishedBridgeTCK.java | 95 ++++++++++++
.../InfinispanSessionRepositoryTCK.java | 135 ++++++++++++++++
.../spring/session/util/EventsWaiter.java | 32 ++++
spring/spring4/spring4-embedded/pom.xml | 17 ++
.../EmbeddedApplicationPublishedBridge.java | 51 ++++++
.../InfinispanEmbeddedSessionRepository.java | 43 ++++++
.../spring/session/PrincipalNameResolver.java | 42 +++++
.../EnableInfinispanEmbeddedHttpSession.java | 61 ++++++++
...ispanEmbeddedHttpSessionConfiguration.java | 54 +++++++
...mbeddedApplicationPublishedBridgeTest.java | 63 ++++++++
...finispanEmbeddedSessionRepositoryTest.java | 89 +++++++++++
spring/spring4/spring4-remote/pom.xml | 24 ++-
.../spring/provider/SpringRemoteCache.java | 94 ------------
.../provider/SpringRemoteCacheManager.java | 5 +-
.../InfinispanRemoteSessionRepository.java | 21 +++
.../RemoteApplicationPublishedBridge.java | 50 ++++++
.../EnableInfinispanRemoteHttpSession.java | 61 ++++++++
...inispanRemoteHttpSessionConfiguration.java | 53 +++++++
...InfinispanRemoteSessionRepositoryTest.java | 95 ++++++++++++
.../RemoteApplicationPublishedBridgeTest.java | 75 +++++++++
40 files changed, 1721 insertions(+), 102 deletions(-)
create mode 100644 integrationtests/spring-boot-it/pom.xml
create mode 100644 integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/AbstractSpringSessionTCK.java
create mode 100644 integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/configuration/SecurityConfig.java
create mode 100644 integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/configuration/WebConfig.java
create mode 100644 integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/embedded/EmbeddedConfiguration.java
create mode 100644 integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/embedded/EmbeddedSpringSessionTest.java
create mode 100644 integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/remote/RemoteConfiguration.java
create mode 100644 integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/remote/RemoteSpringSessionTest.java
create mode 100644 integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/web/TestRESTController.java
create mode 100644 spring/spring4/spring4-common/src/main/java/org/infinispan/spring/session/AbstractApplicationPublisherBridge.java
create mode 100644 spring/spring4/spring4-common/src/main/java/org/infinispan/spring/session/AbstractInfinispanSessionRepository.java
create mode 100644 spring/spring4/spring4-common/src/test/java/org/infinispan/spring/session/InfinispanApplicationPublishedBridgeTCK.java
create mode 100644 spring/spring4/spring4-common/src/test/java/org/infinispan/spring/session/InfinispanSessionRepositoryTCK.java
create mode 100644 spring/spring4/spring4-common/src/test/java/org/infinispan/spring/session/util/EventsWaiter.java
create mode 100644 spring/spring4/spring4-embedded/src/main/java/org/infinispan/spring/session/EmbeddedApplicationPublishedBridge.java
create mode 100644 spring/spring4/spring4-embedded/src/main/java/org/infinispan/spring/session/InfinispanEmbeddedSessionRepository.java
create mode 100644 spring/spring4/spring4-embedded/src/main/java/org/infinispan/spring/session/PrincipalNameResolver.java
create mode 100644 spring/spring4/spring4-embedded/src/main/java/org/infinispan/spring/session/configuration/EnableInfinispanEmbeddedHttpSession.java
create mode 100644 spring/spring4/spring4-embedded/src/main/java/org/infinispan/spring/session/configuration/InfinispanEmbeddedHttpSessionConfiguration.java
create mode 100644 spring/spring4/spring4-embedded/src/test/java/org/infinispan/spring/session/EmbeddedApplicationPublishedBridgeTest.java
create mode 100644 spring/spring4/spring4-embedded/src/test/java/org/infinispan/spring/session/InfinispanEmbeddedSessionRepositoryTest.java
delete mode 100644 spring/spring4/spring4-remote/src/main/java/org/infinispan/spring/provider/SpringRemoteCache.java
create mode 100644 spring/spring4/spring4-remote/src/main/java/org/infinispan/spring/session/InfinispanRemoteSessionRepository.java
create mode 100644 spring/spring4/spring4-remote/src/main/java/org/infinispan/spring/session/RemoteApplicationPublishedBridge.java
create mode 100644 spring/spring4/spring4-remote/src/main/java/org/infinispan/spring/session/configuration/EnableInfinispanRemoteHttpSession.java
create mode 100644 spring/spring4/spring4-remote/src/main/java/org/infinispan/spring/session/configuration/InfinispanRemoteHttpSessionConfiguration.java
create mode 100644 spring/spring4/spring4-remote/src/test/java/org/infinispan/spring/session/InfinispanRemoteSessionRepositoryTest.java
create mode 100644 spring/spring4/spring4-remote/src/test/java/org/infinispan/spring/session/RemoteApplicationPublishedBridgeTest.java
diff --git a/documentation/src/main/asciidoc/upgrading/upgrading.asciidoc b/documentation/src/main/asciidoc/upgrading/upgrading.asciidoc
index a6bf08f75f46..419e6490364c 100644
--- a/documentation/src/main/asciidoc/upgrading/upgrading.asciidoc
+++ b/documentation/src/main/asciidoc/upgrading/upgrading.asciidoc
@@ -39,7 +39,10 @@ The interface `SegmentCompletionListener` has moved from the interface
`org.infinispan.CacheStream` to the new `org.infinispan.BaseCacheStream`.
=== Spring module dependency changes
-All Infinispan dependencies from Spring modules (both embedded and remote) are now in provided scope. One can decide whether use small jars or uber jars but they need to be added to the classpath on client side. Here is an example:
+All Infinispan, Spring and Logger dependencies are now in the `provided` scope. One can decide whether to use small jars or uber jars but they need to be added to the classpath of the application.
+It also gives one freedom in choosing Spring (or Spring Boot) version.
+
+Here is an example:
[source,xml]
----
@@ -51,8 +54,17 @@ All Infinispan dependencies from Spring modules (both embedded and remote) are n
org.infinispaninfinispan-spring4-embedded
+
+ org.springframework
+ spring-context
+
+
+ org.springframework.session
+ spring-session
+
----
+
Additionally there is no Logger implementation specified (since this may vary depending on use case).
=== Total order executor is not removed
diff --git a/documentation/src/main/asciidoc/user_guide/integrations.adoc b/documentation/src/main/asciidoc/user_guide/integrations.adoc
index c0f221b332df..f3fac2d528dd 100644
--- a/documentation/src/main/asciidoc/user_guide/integrations.adoc
+++ b/documentation/src/main/asciidoc/user_guide/integrations.adoc
@@ -1483,6 +1483,13 @@ Now, you will need to add Infinispan and Spring integration module to your class
org.infinispaninfinispan-spring4-embedded
+ ${version.spring}
+
+
+
+ org.springframework
+ spring-context
+ ${version.spring}
----
@@ -1548,8 +1555,76 @@ void deleteBook(Integer bookId) {...}
and you may rest assured that no stray books be left in your application once you decide to remove them.
+==== Externalizing session using Spring Session
+
+link:$$http://docs.spring.io/spring-session/docs/current/reference/html5$$[Spring Session] is a very convenient way to externalize user session into Infinispan cluster.
+
+Spring Session integration allows to use both - embedded and client/server mode. Each mode requires using proper artifacts (`infinispan-spring4-embedded` or `infinispan-spring4-remote`).
+An example is shown below:
+
+[source,xml]
+----
+
+
+ org.infinispan
+ infinispan-embedded
+
+
+ org.infinispan
+ infinispan-spring4-embedded
+ ${version.spring}
+
+
+ org.springframework
+ spring-context
+ ${version.spring}
+
+
+ org.springframework
+ spring-session
+ ${version.spring}
+
+
+ org.springframework
+ spring-web
+ ${version.spring}
+
+
+----
+
+Spring Session integration has been based on Infinispan Spring Cache support so it requires creating a `SpringEmbeddedCacheManagerFactoryBean` or `SpringRemoteCacheManagerFactoryBean`.
+The next step it to use `@EnableInfinispanEmbeddedHttpSession` or `@EnableInfinispanRemoteHttpSession` configuration annotation which turns on Spring Session.
+
+`@EnableInfinispanEmbeddedHttpSession` or `@EnableInfinispanRemoteHttpSession` annotations have 2 optional parameters:
+
+* maxInactiveIntervalInSeconds - which sets session expiration time in seconds. The default is set to `1800`.
+* cacheName - cache name which is used for storing sessions. The default is set to `sessions`.
+
+A complete, annotation based configuration example is shown below:
+
+[source, java]
+----
+@EnableInfinispanEmbeddedHttpSession
+@Configuration
+public class Config {
+
+ @Bean
+ public SpringEmbeddedCacheManagerFactoryBean springCacheManager() {
+ return new SpringEmbeddedCacheManagerFactoryBean();
+ }
+
+ //An optional configuration bean which is responsible for replacing the default cookie
+ //for obtaining configuration.
+ //For more information refer to Spring Session documentation.
+ @Bean
+ public HttpSessionStrategy httpSessionStrategy() {
+ return new HeaderHttpSessionStrategy();
+ }
+}
+----
+
==== Conclusion
-Hopefully you enjoyed our quick tour of Infinispan's support for Spring's cache abstraction and saw how easy it is for all your caching woes to be taken care of by Infinispan. More information may be found in Spring's link:$$http://docs.spring.io/spring-framework/docs/4.1.1.RELEASE/spring-framework-reference/html/cache.html$$[reference documentation]. Also see link:$$http://spring.io/blog/2011/02/23/spring-3-1-m1-cache-abstraction$$[this link] - a very nice posting on the official Spring blog for a somewhat more comprehensive introduction to Spring's cache abstraction.
+Hopefully you enjoyed our quick tour of Infinispan's support for Spring's cache and session abstraction and saw how easy it is for all your caching woes to be taken care of by Infinispan. More information may be found in Spring's link:$$http://docs.spring.io/spring-framework/docs/4.1.1.RELEASE/spring-framework-reference/html/cache.html$$[reference documentation]. Also see link:$$http://spring.io/blog/2011/02/23/spring-3-1-m1-cache-abstraction$$[this link] - a very nice posting on the official Spring blog for a somewhat more comprehensive introduction to Spring's cache abstraction.
=== Infinispan modules for WildFly
diff --git a/integrationtests/spring-boot-it/pom.xml b/integrationtests/spring-boot-it/pom.xml
new file mode 100644
index 000000000000..b65084598397
--- /dev/null
+++ b/integrationtests/spring-boot-it/pom.xml
@@ -0,0 +1,145 @@
+
+
+ 4.0.0
+
+
+ org.infinispan
+ infinispan-parent
+ 9.0.0-SNAPSHOT
+ ../../parent/pom.xml
+
+
+ infinispan-spring-boot-it
+ Integration tests: Spring Boot
+ Integration tests for Infinispan and Spring Boot
+
+
+
+
+ 4.12
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${version.spring-boot}
+ pom
+ import
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+ test
+
+
+ org.springframework.session
+ spring-session
+ test
+
+
+ ${project.groupId}
+ infinispan-spring4-embedded
+ test
+
+
+ ${project.groupId}
+ infinispan-spring4-remote
+ test
+
+
+ ${project.groupId}
+ infinispan-core
+ test
+
+
+ ${project.groupId}
+ infinispan-client-hotrod
+ test
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ ${project.groupId}
+ infinispan-server-hotrod
+ test
+
+
+ junit
+ junit
+ test
+ ${overriden.version.junit}
+
+
+ ${project.groupId}
+ infinispan-server-hotrod
+ ${project.version}
+ test
+
+
+
+
+
+ smoke
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ default-test
+ none
+
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ ${suite.exclude.groups}
+
+ ${log4j.configurationFile}
+ ${project.build.directory}
+
+ false
+
+
+
+ org.apache.maven.surefire
+ surefire-junit4
+ ${version.maven.surefire}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/AbstractSpringSessionTCK.java b/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/AbstractSpringSessionTCK.java
new file mode 100644
index 000000000000..8c5580b8e82d
--- /dev/null
+++ b/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/AbstractSpringSessionTCK.java
@@ -0,0 +1,39 @@
+package org.infinispan.integrationtests.spring.boot.session;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.embedded.LocalServerPort;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.http.HttpHeaders;
+import org.springframework.session.MapSession;
+import org.springframework.session.SessionRepository;
+
+public class AbstractSpringSessionTCK {
+
+ @Autowired
+ private SessionRepository sessionRepository;
+
+ @LocalServerPort
+ private int port;
+
+ @Test
+ public void testCreatingSessionWhenUsingREST() throws Exception {
+ //given
+ TestRestTemplate restTemplate = new TestRestTemplate("user", "password");
+
+ //when
+ HttpHeaders httpHeaders = restTemplate.headForHeaders(getTestURL());
+
+ //then
+ Assert.assertNotNull(sessionRepository.getSession(getSessionId(httpHeaders)));
+ }
+
+ private String getTestURL() {
+ return "http://localhost:" + port + "/test";
+ }
+
+ private String getSessionId(HttpHeaders httpHeaders) {
+ return httpHeaders.getValuesAsList("x-auth-token").get(0);
+ }
+}
diff --git a/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/configuration/SecurityConfig.java b/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/configuration/SecurityConfig.java
new file mode 100644
index 000000000000..5ea8b8bcb1fd
--- /dev/null
+++ b/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/configuration/SecurityConfig.java
@@ -0,0 +1,18 @@
+package org.infinispan.integrationtests.spring.boot.session.configuration;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+/**
+ * Since Spring Session is heavily based on security - we need to define basic user/password.
+ */
+@EnableWebSecurity
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
+
+ @Autowired
+ public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
+ auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
+ }
+}
diff --git a/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/configuration/WebConfig.java b/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/configuration/WebConfig.java
new file mode 100644
index 000000000000..e39bafaf68ed
--- /dev/null
+++ b/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/configuration/WebConfig.java
@@ -0,0 +1,28 @@
+package org.infinispan.integrationtests.spring.boot.session.configuration;
+
+import org.infinispan.integrationtests.spring.boot.session.web.TestRESTController;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.session.web.http.HeaderHttpSessionStrategy;
+import org.springframework.session.web.http.HttpSessionStrategy;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+
+@Configuration
+@EnableWebMvc
+public class WebConfig {
+
+ @Bean
+ public HttpSessionStrategy httpSessionStrategy() {
+ return new HeaderHttpSessionStrategy();
+ }
+
+ @Bean
+ public TestRESTController sessionCreator() {
+ return new TestRESTController();
+ }
+
+ @Bean
+ public SecurityConfig securityConfig() {
+ return new SecurityConfig();
+ }
+}
diff --git a/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/embedded/EmbeddedConfiguration.java b/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/embedded/EmbeddedConfiguration.java
new file mode 100644
index 000000000000..83f428e2af90
--- /dev/null
+++ b/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/embedded/EmbeddedConfiguration.java
@@ -0,0 +1,22 @@
+package org.infinispan.integrationtests.spring.boot.session.embedded;
+
+import org.infinispan.integrationtests.spring.boot.session.configuration.WebConfig;
+import org.infinispan.spring.provider.SpringEmbeddedCacheManagerFactoryBean;
+import org.infinispan.spring.session.configuration.EnableInfinispanEmbeddedHttpSession;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+@Configuration
+@EnableAutoConfiguration
+@EnableInfinispanEmbeddedHttpSession
+@Import(WebConfig.class)
+public class EmbeddedConfiguration {
+
+ @Bean
+ public SpringEmbeddedCacheManagerFactoryBean springCacheManager() {
+ return new SpringEmbeddedCacheManagerFactoryBean();
+ }
+
+}
diff --git a/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/embedded/EmbeddedSpringSessionTest.java b/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/embedded/EmbeddedSpringSessionTest.java
new file mode 100644
index 000000000000..fee5a1d53921
--- /dev/null
+++ b/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/embedded/EmbeddedSpringSessionTest.java
@@ -0,0 +1,12 @@
+package org.infinispan.integrationtests.spring.boot.session.embedded;
+
+import org.infinispan.integrationtests.spring.boot.session.AbstractSpringSessionTCK;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = EmbeddedConfiguration.class, webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
+public class EmbeddedSpringSessionTest extends AbstractSpringSessionTCK {
+
+}
diff --git a/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/remote/RemoteConfiguration.java b/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/remote/RemoteConfiguration.java
new file mode 100644
index 000000000000..3ac413410c63
--- /dev/null
+++ b/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/remote/RemoteConfiguration.java
@@ -0,0 +1,30 @@
+package org.infinispan.integrationtests.spring.boot.session.remote;
+
+import java.net.InetSocketAddress;
+import java.util.Arrays;
+
+import org.infinispan.integrationtests.spring.boot.session.configuration.WebConfig;
+import org.infinispan.spring.provider.SpringRemoteCacheManagerFactoryBean;
+import org.infinispan.spring.session.configuration.EnableInfinispanRemoteHttpSession;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.util.SocketUtils;
+
+@Configuration
+@EnableAutoConfiguration
+@EnableInfinispanRemoteHttpSession
+@Import(WebConfig.class)
+public class RemoteConfiguration {
+
+ public static final int SERVER_PORT = SocketUtils.findAvailableTcpPort();
+
+ @Bean
+ public SpringRemoteCacheManagerFactoryBean springCacheManager() {
+ SpringRemoteCacheManagerFactoryBean factoryBean = new SpringRemoteCacheManagerFactoryBean();
+ factoryBean.setServerList(Arrays.asList(new InetSocketAddress("localhost", SERVER_PORT)));
+ return factoryBean;
+ }
+
+}
diff --git a/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/remote/RemoteSpringSessionTest.java b/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/remote/RemoteSpringSessionTest.java
new file mode 100644
index 000000000000..f01a12566bbc
--- /dev/null
+++ b/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/remote/RemoteSpringSessionTest.java
@@ -0,0 +1,45 @@
+package org.infinispan.integrationtests.spring.boot.session.remote;
+
+import org.infinispan.commons.equivalence.AnyServerEquivalence;
+import org.infinispan.configuration.cache.ConfigurationBuilder;
+import org.infinispan.integrationtests.spring.boot.session.AbstractSpringSessionTCK;
+import org.infinispan.manager.DefaultCacheManager;
+import org.infinispan.manager.EmbeddedCacheManager;
+import org.infinispan.server.hotrod.HotRodServer;
+import org.infinispan.server.hotrod.configuration.HotRodServerConfigurationBuilder;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = RemoteConfiguration.class, webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
+public class RemoteSpringSessionTest extends AbstractSpringSessionTCK {
+
+ private static EmbeddedCacheManager serverCache;
+
+ private static HotRodServer server;
+
+ @BeforeClass
+ public static void beforeclass() {
+ ConfigurationBuilder cacheConfiguration = new ConfigurationBuilder();
+ cacheConfiguration.dataContainer().keyEquivalence(new AnyServerEquivalence());
+
+ serverCache = new DefaultCacheManager();
+ serverCache.defineConfiguration("sessions", cacheConfiguration.build());
+
+ HotRodServerConfigurationBuilder hotRodServerConfigurationBuilder = new HotRodServerConfigurationBuilder();
+ hotRodServerConfigurationBuilder.port(RemoteConfiguration.SERVER_PORT);
+
+ server = new HotRodServer();
+ server.start(hotRodServerConfigurationBuilder.build(), serverCache);
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ server.stop();
+ serverCache.stop();
+ }
+
+}
diff --git a/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/web/TestRESTController.java b/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/web/TestRESTController.java
new file mode 100644
index 000000000000..bb100abbcd82
--- /dev/null
+++ b/integrationtests/spring-boot-it/src/test/java/org/infinispan/integrationtests/spring/boot/session/web/TestRESTController.java
@@ -0,0 +1,17 @@
+package org.infinispan.integrationtests.spring.boot.session.web;
+
+import org.apache.catalina.servlet4preview.http.HttpServletRequest;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@Controller
+public class TestRESTController {
+
+ @RequestMapping("/test")
+ public ResponseEntity testRest(HttpServletRequest request){
+ return new ResponseEntity(HttpStatus.OK);
+ }
+
+}
diff --git a/parent/pom.xml b/parent/pom.xml
index 7035f43c7986..d00a60c69e4c 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -165,7 +165,8 @@
1.1.0.Final1.0.53.1.0.Final
- 3.2.9.RELEASE
+ 1.2.2.RELEASE
+ 1.4.0.RELEASE4.1.0.RELEASE2.12.16.8.8
diff --git a/pom.xml b/pom.xml
index b394c5b4799d..cc6ffe23c1c4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -88,6 +88,7 @@
integrationtests/all-embedded-itintegrationtests/all-embedded-query-itintegrationtests/all-remote-it
+ integrationtests/spring-boot-it
diff --git a/spring/spring4/pom.xml b/spring/spring4/pom.xml
index 0bbf21529e61..4c76c410a806 100644
--- a/spring/spring4/pom.xml
+++ b/spring/spring4/pom.xml
@@ -84,6 +84,16 @@
spring-context-support${version.spring4}
+
+ org.springframework.session
+ spring-session
+ ${version.spring-session}
+
+
+ org.springframework
+ spring-web
+ ${version.spring4}
+
diff --git a/spring/spring4/spring4-common/pom.xml b/spring/spring4/spring4-common/pom.xml
index 6fe12ab94c13..39ca2c41baca 100644
--- a/spring/spring4/spring4-common/pom.xml
+++ b/spring/spring4/spring4-common/pom.xml
@@ -16,12 +16,34 @@
org.springframeworkspring-context
+ provided
+
+
+ org.springframework.session
+ spring-session
+ provided${project.groupId}infinispan-commonsprovided
+
+ ${project.groupId}
+ infinispan-core
+ test-jar
+ test
+
+
+ ${project.groupId}
+ infinispan-core
+ test
+
+
+ org.testng
+ testng
+ test
+
diff --git a/spring/spring4/spring4-common/src/main/java/org/infinispan/spring/package-info.java b/spring/spring4/spring4-common/src/main/java/org/infinispan/spring/package-info.java
index 25085b4e2c71..b9178d1da153 100644
--- a/spring/spring4/spring4-common/src/main/java/org/infinispan/spring/package-info.java
+++ b/spring/spring4/spring4-common/src/main/java/org/infinispan/spring/package-info.java
@@ -13,6 +13,11 @@
* interface for easing usage of JBoss Infinispan within the Spring programming model.
* See package {@link org.infinispan.spring.support org.infinispan.spring.support}.
*
+ *
+ * Provide implementations of Spring's Session
+ * {@link org.springframework.session.SessionRepository SessionRepository} interface for session
+ * management with Spring and Spring Security.
+ *
+ * The configuration requires creating a {@link org.infinispan.spring.provider.SpringCache} (for either remote or
+ * embedded configuration). Here's an example:
+ *
+ * {@literal @Configuration}
+ * {@literal @EnableInfinispanEmbeddednHttpSession}
+ * public class InfinispanConfiguration {
+ *
+ * {@literal @Bean}
+ * public SpringEmbeddedCacheManagerFactoryBean springCache() {
+ * return new SpringEmbeddedCacheManagerFactoryBean();
+ * }
+ * }
+ *
+ *
+ * Configuring advanced features requires putting everything together manually or extending
+ * {@link InfinispanEmbeddedHttpSessionConfiguration}.
+ *
+ * @author Sebastian Łaskawiec
+ * @see EnableSpringHttpSession
+ * @since 9.0
+ */
+@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+@Target({java.lang.annotation.ElementType.TYPE})
+@Documented
+@Import(InfinispanEmbeddedHttpSessionConfiguration.class)
+@Configuration
+public @interface EnableInfinispanEmbeddedHttpSession {
+
+ public static final String DEFAULT_CACHE_NAME = "sessions";
+
+ /**
+ * 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 MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
+
+ /**
+ * Cache name used for storing session data.
+ *
+ * @return the cache name for storing data.
+ */
+ String cacheName() default DEFAULT_CACHE_NAME;
+
+}
diff --git a/spring/spring4/spring4-embedded/src/main/java/org/infinispan/spring/session/configuration/InfinispanEmbeddedHttpSessionConfiguration.java b/spring/spring4/spring4-embedded/src/main/java/org/infinispan/spring/session/configuration/InfinispanEmbeddedHttpSessionConfiguration.java
new file mode 100644
index 000000000000..8c41d1329e98
--- /dev/null
+++ b/spring/spring4/spring4-embedded/src/main/java/org/infinispan/spring/session/configuration/InfinispanEmbeddedHttpSessionConfiguration.java
@@ -0,0 +1,54 @@
+package org.infinispan.spring.session.configuration;
+
+import java.util.Map;
+import java.util.Objects;
+
+import org.infinispan.spring.provider.SpringCache;
+import org.infinispan.spring.provider.SpringEmbeddedCacheManager;
+import org.infinispan.spring.session.InfinispanEmbeddedSessionRepository;
+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.MapSession;
+import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
+
+
+@Configuration
+public class InfinispanEmbeddedHttpSessionConfiguration extends SpringHttpSessionConfiguration implements ImportAware {
+
+ private String cacheName;
+ private int maxInactiveIntervalInSeconds;
+
+ @Bean
+ public InfinispanEmbeddedSessionRepository sessionRepository(SpringEmbeddedCacheManager cacheManager, ApplicationEventPublisher eventPublisher) {
+ Objects.requireNonNull(cacheName, "Cache name can not be null");
+ Objects.requireNonNull(cacheManager, "Cache Manager can not be null");
+ Objects.requireNonNull(eventPublisher, "Event Publisher can not be null");
+
+ SpringCache cacheForSessions = cacheManager.getCache(cacheName);
+
+ InfinispanEmbeddedSessionRepository sessionRepository = new InfinispanEmbeddedSessionRepository(cacheForSessions) {
+ @Override
+ public MapSession createSession() {
+ MapSession session = super.createSession();
+ session.setMaxInactiveIntervalInSeconds(maxInactiveIntervalInSeconds);
+ return session;
+ }
+ };
+ sessionRepository.setApplicationEventPublisher(eventPublisher);
+
+ return sessionRepository;
+ }
+
+ @Override
+ public void setImportMetadata(AnnotationMetadata importMetadata) {
+ Map enableAttrMap = importMetadata
+ .getAnnotationAttributes(EnableInfinispanEmbeddedHttpSession.class.getName());
+ AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(enableAttrMap);
+ cacheName = annotationAttributes.getString("cacheName");
+ maxInactiveIntervalInSeconds = annotationAttributes.getNumber("maxInactiveIntervalInSeconds").intValue();
+ }
+}
diff --git a/spring/spring4/spring4-embedded/src/test/java/org/infinispan/spring/session/EmbeddedApplicationPublishedBridgeTest.java b/spring/spring4/spring4-embedded/src/test/java/org/infinispan/spring/session/EmbeddedApplicationPublishedBridgeTest.java
new file mode 100644
index 000000000000..72f17ceaf3d2
--- /dev/null
+++ b/spring/spring4/spring4-embedded/src/test/java/org/infinispan/spring/session/EmbeddedApplicationPublishedBridgeTest.java
@@ -0,0 +1,63 @@
+package org.infinispan.spring.session;
+
+import org.infinispan.manager.DefaultCacheManager;
+import org.infinispan.manager.EmbeddedCacheManager;
+import org.infinispan.spring.provider.SpringCache;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+@Test(testName = "spring.session.EmbeddedApplicationPublishedBridgeTest", groups = "unit")
+public class EmbeddedApplicationPublishedBridgeTest extends InfinispanApplicationPublishedBridgeTCK {
+
+ private EmbeddedCacheManager embeddedCacheManager;
+
+ @BeforeClass
+ public void beforeClass() {
+ embeddedCacheManager = new DefaultCacheManager();
+ }
+
+ @AfterMethod
+ public void afterMethod() {
+ embeddedCacheManager.getCache().clear();
+ }
+
+ @AfterClass
+ public void afterClass() {
+ embeddedCacheManager.stop();
+ }
+
+ @BeforeMethod
+ public void beforeMethod() throws Exception {
+ super.init();
+ }
+
+ @Override
+ protected SpringCache createSpringCache() {
+ return new SpringCache(embeddedCacheManager.getCache());
+ }
+
+ @Override
+ protected void callEviction() {
+ embeddedCacheManager.getCache().getAdvancedCache().getExpirationManager().processExpiration();
+ }
+
+ @Override
+ protected AbstractInfinispanSessionRepository createRepository(SpringCache springCache) throws Exception {
+ InfinispanEmbeddedSessionRepository sessionRepository = new InfinispanEmbeddedSessionRepository(springCache);
+ sessionRepository.afterPropertiesSet();
+ return sessionRepository;
+ }
+
+ @Override
+ public void testEventBridge() throws Exception {
+ super.testEventBridge();
+ }
+
+ @Override
+ public void testUnregistration() throws Exception {
+ super.testUnregistration();
+ }
+}
diff --git a/spring/spring4/spring4-embedded/src/test/java/org/infinispan/spring/session/InfinispanEmbeddedSessionRepositoryTest.java b/spring/spring4/spring4-embedded/src/test/java/org/infinispan/spring/session/InfinispanEmbeddedSessionRepositoryTest.java
new file mode 100644
index 000000000000..f933aa9283ab
--- /dev/null
+++ b/spring/spring4/spring4-embedded/src/test/java/org/infinispan/spring/session/InfinispanEmbeddedSessionRepositoryTest.java
@@ -0,0 +1,89 @@
+package org.infinispan.spring.session;
+
+import org.infinispan.manager.DefaultCacheManager;
+import org.infinispan.manager.EmbeddedCacheManager;
+import org.infinispan.spring.provider.SpringCache;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+@Test(testName = "spring.session.InfinispanEmbeddedSessionRepositoryTest", groups = "unit")
+public class InfinispanEmbeddedSessionRepositoryTest extends InfinispanSessionRepositoryTCK {
+
+ private EmbeddedCacheManager embeddedCacheManager;
+
+ @BeforeClass
+ public void beforeClass() {
+ embeddedCacheManager = new DefaultCacheManager();
+ }
+
+ @AfterMethod
+ public void afterMethod() {
+ embeddedCacheManager.getCache().clear();
+ }
+
+ @AfterClass
+ public void afterClass() {
+ embeddedCacheManager.stop();
+ }
+
+ @BeforeMethod
+ public void beforeMethod() throws Exception {
+ super.init();
+ }
+
+ @Override
+ protected SpringCache createSpringCache() {
+ return new SpringCache(embeddedCacheManager.getCache());
+ }
+
+ @Override
+ protected AbstractInfinispanSessionRepository createRepository(SpringCache springCache) throws Exception {
+ InfinispanEmbeddedSessionRepository sessionRepository = new InfinispanEmbeddedSessionRepository(springCache);
+ sessionRepository.afterPropertiesSet();
+ return sessionRepository;
+ }
+
+ @Test(expectedExceptions = NullPointerException.class)
+ @Override
+ public void testThrowingExceptionOnNullSpringCache() throws Exception {
+ super.testThrowingExceptionOnNullSpringCache();
+ }
+
+ @Override
+ public void testCreatingSession() throws Exception {
+ super.testCreatingSession();
+ }
+
+ @Override
+ public void testSavingSession() throws Exception {
+ super.testSavingSession();
+ }
+
+ @Override
+ public void testDeletingSession() throws Exception {
+ super.testDeletingSession();
+ }
+
+ @Override
+ public void testEvictingSession() throws Exception {
+ super.testEvictingSession();
+ }
+
+ @Override
+ public void testExtractingPrincipalWithWrongIndexName() throws Exception {
+ super.testExtractingPrincipalWithWrongIndexName();
+ }
+
+ @Override
+ public void testExtractingPrincipal() throws Exception {
+ super.testExtractingPrincipal();
+ }
+
+ @Override
+ public void testUpdatingTTLOnAccessingData() throws Exception {
+ super.testUpdatingTTLOnAccessingData();
+ }
+}
diff --git a/spring/spring4/spring4-remote/pom.xml b/spring/spring4/spring4-remote/pom.xml
index 2773c9811df3..8cee4347172e 100644
--- a/spring/spring4/spring4-remote/pom.xml
+++ b/spring/spring4/spring4-remote/pom.xml
@@ -12,7 +12,7 @@
infinispan-spring4-remotebundle
- Infinispan Spring 4 Integration
+ Infinispan Spring 4 Remote support1
@@ -22,6 +22,12 @@
org.springframeworkspring-context
+ provided
+
+
+ org.springframework.session
+ spring-session
+ provided${project.groupId}
@@ -38,6 +44,12 @@
spring-testtest
+
+ ${project.groupId}
+ infinispan-spring4-common
+ test-jar
+ test
+ ${project.groupId}infinispan-commons-test
@@ -55,6 +67,11 @@
test-jartest
+
+ org.infinispan
+ infinispan-query-dsl
+ test
+ ${project.groupId}infinispan-server-hotrod
@@ -71,6 +88,11 @@
spring-jdbctest
+
+ org.springframework
+ spring-web
+ test
+ org.springframeworkspring-context-support
diff --git a/spring/spring4/spring4-remote/src/main/java/org/infinispan/spring/provider/SpringRemoteCache.java b/spring/spring4/spring4-remote/src/main/java/org/infinispan/spring/provider/SpringRemoteCache.java
deleted file mode 100644
index 8a042f96d310..000000000000
--- a/spring/spring4/spring4-remote/src/main/java/org/infinispan/spring/provider/SpringRemoteCache.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package org.infinispan.spring.provider;
-
-import org.infinispan.client.hotrod.RemoteCache;
-import org.springframework.cache.Cache;
-
-/**
- *
- * A {@link org.springframework.cache.Cache Cache} implementation that delegates to a
- * {@link org.infinispan.client.hotrod.RemoteCache org.infinispan.client.hotrod.RemoteCache} instance supplied at construction
- * time.
- *
- *
- * @author Olaf Bergner
- * @author Marius Bogoevici
- *
- */
-public class SpringRemoteCache implements Cache {
-
- private final CacheDelegate cacheImplementation;
-
- /**
- * @param nativeCache the underlying cache
- */
- public SpringRemoteCache(final RemoteCache