Skip to content

Commit

Permalink
ISPN-7009 Spring-session implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastian Laskawiec authored and tristantarrant committed Oct 21, 2016
1 parent d0a8c0b commit 2de17a5
Show file tree
Hide file tree
Showing 40 changed files with 1,721 additions and 102 deletions.
14 changes: 13 additions & 1 deletion documentation/src/main/asciidoc/upgrading/upgrading.asciidoc
Expand Up @@ -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]
----
<dependencies>
Expand All @@ -51,8 +54,17 @@ All Infinispan dependencies from Spring modules (both embedded and remote) are n
<groupId>org.infinispan</groupId>
<artifactId>infinispan-spring4-embedded</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
</dependency>
</dependencies>
----

Additionally there is no Logger implementation specified (since this may vary depending on use case).

=== Total order executor is not removed
Expand Down
77 changes: 76 additions & 1 deletion documentation/src/main/asciidoc/user_guide/integrations.adoc
Expand Up @@ -1483,6 +1483,13 @@ Now, you will need to add Infinispan and Spring integration module to your class
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-spring4-embedded</artifactId>
<version>${version.spring}</version>
</dependency>
<!-- depending on a use case, one should use Spring Context or Spring Boot jars -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${version.spring}</version>
</dependency>
</dependencies>
----
Expand Down Expand Up @@ -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]
----
<dependencies>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-embedded</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-spring4-embedded</artifactId>
<version>${version.spring}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${version.spring}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-session</artifactId>
<version>${version.spring}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${version.spring}</version>
</dependency>
</dependencies>
----

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

Expand Down
145 changes: 145 additions & 0 deletions integrationtests/spring-boot-it/pom.xml
@@ -0,0 +1,145 @@
<?xml version='1.0' encoding='UTF-8'?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-parent</artifactId>
<version>9.0.0-SNAPSHOT</version>
<relativePath>../../parent/pom.xml</relativePath>
</parent>

<artifactId>infinispan-spring-boot-it</artifactId>
<name>Integration tests: Spring Boot</name>
<description>Integration tests for Infinispan and Spring Boot</description>

<properties>
<!-- Most or our tests require JUnit 4.11 for Aruquillian, however Spring needs 4.12 -->
<!-- FIXME: https://issues.jboss.org/browse/ISPN-7034 -->
<overriden.version.junit>4.12</overriden.version.junit>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${version.spring-boot}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- Those are normal dependencies that are recommended for Spring Boot users -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>infinispan-spring4-embedded</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>infinispan-spring4-remote</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>infinispan-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>infinispan-client-hotrod</artifactId>
<scope>test</scope>
</dependency>

<!-- Helper, test dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>infinispan-server-hotrod</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<version>${overriden.version.junit}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>infinispan-server-hotrod</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<profiles>
<profile>
<id>smoke</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>default-test</id>
<phase>none</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration combine.self="override">
<excludedGroups>${suite.exclude.groups}</excludedGroups>
<systemPropertyVariables>
<log4j.configurationFile>${log4j.configurationFile}</log4j.configurationFile>
<build.directory>${project.build.directory}</build.directory>
</systemPropertyVariables>
<trimStackTrace>false</trimStackTrace>
</configuration>
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-junit4</artifactId>
<version>${version.maven.surefire}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
@@ -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<MapSession> 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);
}
}
@@ -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");
}
}
@@ -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();
}
}
@@ -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();
}

}
@@ -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 {

}

0 comments on commit 2de17a5

Please sign in to comment.