diff --git a/docs/build.gradle b/docs/build.gradle index 06804e9c..11ff5379 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -33,6 +33,7 @@ dependencies { "org.springframework.data:spring-data-gemfire:$springDataGemFireVersion", "org.springframework:spring-websocket:${springVersion}", "org.springframework:spring-messaging:${springVersion}", + "org.springframework:spring-jdbc:${springVersion}", "org.springframework.security:spring-security-web:${springSecurityVersion}", "org.springframework.security:spring-security-test:${springSecurityVersion}", 'junit:junit:4.11', @@ -60,6 +61,7 @@ asciidoctor { 'docs-test-dir' : rootProject.projectDir.path + '/docs/src/test/java/', 'docs-test-resources-dir' : rootProject.projectDir.path + '/docs/src/test/resources/', 'samples-dir' : rootProject.projectDir.path + '/samples/', + 'session-main-resources-dir' : rootProject.projectDir.path + '/spring-session/src/main/resources/', 'source-highlighter' : 'coderay', 'imagesdir':'./images', diff --git a/docs/src/docs/asciidoc/guides/httpsession-jdbc-xml.adoc b/docs/src/docs/asciidoc/guides/httpsession-jdbc-xml.adoc new file mode 100644 index 00000000..a5154f11 --- /dev/null +++ b/docs/src/docs/asciidoc/guides/httpsession-jdbc-xml.adoc @@ -0,0 +1,159 @@ += Spring Session - HttpSession (Quick Start) +Rob Winch, Vedran Pavić +:toc: + +This guide describes how to use Spring Session to transparently leverage a relational to back a web application's `HttpSession` with XML based configuration. + +NOTE: The completed guide can be found in the <>. + +== Updating Dependencies +Before you use Spring Session, you must ensure to update your dependencies. +If you are using Maven, ensure to add the following dependencies: + +.pom.xml +[source,xml] +[subs="verbatim,attributes"] +---- + + + + + org.springframework.session + spring-session-jdbc + {spring-session-version} + pom + + + org.springframework + spring-web + {spring-version} + + +---- + +ifeval::["{version-snapshot}" == "true"] +Since we are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository. +Ensure you have the following in your pom.xml: + +.pom.xml +[source,xml] +---- + + + + + + spring-snapshot + https://repo.spring.io/libs-snapshot + + +---- +endif::[] + +ifeval::["{version-milestone}" == "true"] +Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository. +Ensure you have the following in your pom.xml: + +.pom.xml +[source,xml] +---- + + spring-milestone + https://repo.spring.io/libs-milestone + +---- +endif::[] + +// tag::config[] + +[[httpsession-jdbc-xml-spring-configuration]] +== Spring XML Configuration + +After adding the required dependencies, we can create our Spring configuration. +The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session. +Add the following Spring Configuration: + +.src/main/webapp/WEB-INF/spring/session.xml +[source,xml,indent=0] +---- +include::{samples-dir}httpsession-jdbc-xml/src/main/webapp/WEB-INF/spring/session.xml[tags=beans] +---- + +<1> We use the combination of `` and `JdbcHttpSessionConfiguration` because Spring Session does not yet provide XML Namespace support (see https://github.com/spring-projects/spring-session/issues/104[gh-104]). +This creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter. +The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session. +In this instance Spring Session is backed by a relational database. +<2> We create a `dataSource` that connects Spring Session to an embedded instance of H2 database. +We configure the H2 database to create database tables using the SQL script which is included in Spring Session. + +== XML Servlet Container Initialization + +Our <> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`. +The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session. + +In order for our `Filter` to do its magic, we need to instruct Spring to load our `session.xml` configuration. +We do this with the following configuration: + + +.src/main/webapp/WEB-INF/web.xml +[source,xml,indent=0] +---- +include::{samples-dir}httpsession-xml/src/main/webapp/WEB-INF/web.xml[tags=context-param] +include::{samples-dir}httpsession-xml/src/main/webapp/WEB-INF/web.xml[tags=listeners] +---- + +The http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-create[ContextLoaderListener] reads the contextConfigLocation and picks up our session.xml configuration. + +Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request. +The following snippet performs this last step for us: + +.src/main/webapp/WEB-INF/web.xml +[source,xml,indent=0] +---- +include::{samples-dir}httpsession-xml/src/main/webapp/WEB-INF/web.xml[tags=springSessionRepositoryFilter] +---- + +The http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html[DelegatingFilterProxy] will look up a Bean by the name of `springSessionRepositoryFilter` and cast it to a `Filter`. +For every request that `DelegatingFilterProxy` is invoked, the `springSessionRepositoryFilter` will be invoked. + +// end::config[] + +[[httpsession-jdbc-xml-sample]] +== httpsession-jdbc-xml Sample Application + +=== Running the httpsession-jdbc-xml Sample Application + +You can run the sample by obtaining the {download-url}[source code] and invoking the following command: + +---- +$ ./gradlew :samples:httpsession-jdbc-xml:tomcatRun +---- + +You should now be able to access the application at http://localhost:8080/ + +=== Exploring the httpsession-jdbc-xml Sample Application + +Try using the application. Fill out the form with the following information: + +* **Attribute Name:** _username_ +* **Attribute Value:** _rob_ + +Now click the **Set Attribute** button. You should now see the values displayed in the table. + +=== How does it work? + +We interact with the standard `HttpSession` in the `SessionServlet` shown below: + +.src/main/java/sample/SessionServlet.java +[source,java] +---- +include::{samples-dir}httpsession-jdbc-xml/src/main/java/sample/SessionServlet.java[tags=class] +---- + +Instead of using Tomcat's `HttpSession`, we are actually persisting the values in H2 database. +Spring Session creates a cookie named SESSION in your browser that contains the id of your session. +Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]). + +If you like, you can easily remove the session using H2 web console available at: http://localhost:8080/console (use `jdbc:h2:mem:testdb` for JDBC URL) + +Now visit the application at http://localhost:8080/ and observe that the attribute we added is no longer displayed. diff --git a/docs/src/docs/asciidoc/guides/httpsession-jdbc.adoc b/docs/src/docs/asciidoc/guides/httpsession-jdbc.adoc new file mode 100644 index 00000000..c591f601 --- /dev/null +++ b/docs/src/docs/asciidoc/guides/httpsession-jdbc.adoc @@ -0,0 +1,149 @@ += Spring Session - HttpSession (Quick Start) +Rob Winch, Vedran Pavić +:toc: + +This guide describes how to use Spring Session to transparently leverage a relational database to back a web application's `HttpSession` with Java Configuration. + +NOTE: The completed guide can be found in the <>. + +== Updating Dependencies +Before you use Spring Session, you must ensure to update your dependencies. +If you are using Maven, ensure to add the following dependencies: + +.pom.xml +[source,xml] +[subs="verbatim,attributes"] +---- + + + + + org.springframework.session + spring-session-jdbc + {spring-session-version} + pom + + + org.springframework + spring-web + {spring-version} + + +---- + +ifeval::["{version-snapshot}" == "true"] +Since we are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository. +Ensure you have the following in your pom.xml: + +.pom.xml +[source,xml] +---- + + + + + + spring-snapshot + https://repo.spring.io/libs-snapshot + + +---- +endif::[] + +ifeval::["{version-milestone}" == "true"] +Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository. +Ensure you have the following in your pom.xml: + +.pom.xml +[source,xml] +---- + + spring-milestone + https://repo.spring.io/libs-milestone + +---- +endif::[] + +// tag::config[] + +[[httpsession-jdbc-spring-configuration]] +== Spring Java Configuration + +After adding the required dependencies, we can create our Spring configuration. +The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session. +Add the following Spring Configuration: + +[source,java] +---- +include::{samples-dir}httpsession-jdbc/src/main/java/sample/Config.java[tags=class] +---- + +<1> The `@EnableJdbcHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter. +The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session. +In this instance Spring Session is backed by a relational database. +<2> We create a `dataSource` that connects Spring Session to an embedded instance of H2 database. +We configure the H2 database to create database tables using the SQL script which is included in Spring Session. + +== Java Servlet Container Initialization + +Our <> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`. +The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session. + +In order for our `Filter` to do its magic, Spring needs to load our `Config` class. +Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request. +Fortunately, Spring Session provides a utility class named `AbstractHttpSessionApplicationInitializer` both of these steps extremely easy. +You can find an example below: + +.src/main/java/sample/Initializer.java +[source,java] +---- +include::{samples-dir}httpsession/src/main/java/sample/Initializer.java[tags=class] +---- + +NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`. + +<1> The first step is to extend `AbstractHttpSessionApplicationInitializer`. +This ensures that the Spring Bean by the name `springSessionRepositoryFilter` is registered with our Servlet Container for every request. +<2> `AbstractHttpSessionApplicationInitializer` also provides a mechanism to easily ensure Spring loads our `Config`. + +// end::config[] + +[[httpsession-jdbc-sample]] +== httpsession-jdbc Sample Application + +=== Running the httpsession-jdbc Sample Application + +You can run the sample by obtaining the {download-url}[source code] and invoking the following command: + +---- +$ ./gradlew :samples:httpsession-jdbc:tomcatRun +---- + +You should now be able to access the application at http://localhost:8080/ + +=== Exploring the httpsession-jdbc Sample Application + +Try using the application. Fill out the form with the following information: + +* **Attribute Name:** _username_ +* **Attribute Value:** _rob_ + +Now click the **Set Attribute** button. You should now see the values displayed in the table. + +=== How does it work? + +We interact with the standard `HttpSession` in the `SessionServlet` shown below: + +.src/main/java/sample/SessionServlet.java +[source,java] +---- +include::{samples-dir}httpsession-jdbc/src/main/java/sample/SessionServlet.java[tags=class] +---- + +Instead of using Tomcat's `HttpSession`, we are actually persisting the values in H2 database. +Spring Session creates a cookie named SESSION in your browser that contains the id of your session. +Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]). + +If you like, you can easily remove the session using H2 web console available at: http://localhost:8080/console (use `jdbc:h2:mem:testdb` for JDBC URL) + +Now visit the application at http://localhost:8080/ and observe that the attribute we added is no longer displayed. diff --git a/docs/src/docs/asciidoc/index.adoc b/docs/src/docs/asciidoc/index.adoc index 3ab33d2c..bfe41a44 100644 --- a/docs/src/docs/asciidoc/index.adoc +++ b/docs/src/docs/asciidoc/index.adoc @@ -1,5 +1,5 @@ = Spring Session -Rob Winch +Rob Winch, Vedran Pavić :doctype: book :indexdoc-tests: {docs-test-dir}docs/IndexDocTests.java :websocketdoc-test-dir: {docs-test-dir}docs/websocket/ @@ -113,6 +113,14 @@ If you are looking to get started with Spring Session, the best place to start i | Demonstrates how to use Spring Session and Hazelcast with an existing Spring Security application. | link:guides/hazelcast-spring.html[Hazelcast Spring Guide] +| {gh-samples-url}httpsession-jdbc[HttpSession JDBC] +| Demonstrates how to use Spring Session to replace the `HttpSession` with a relational database store. +| link:guides/httpsession-jdbc.html[HttpSession JDBC Guide] + +| {gh-samples-url}httpsession-jdbc-xml[HttpSession JDBC XML] +| Demonstrates how to use Spring Session to replace the `HttpSession` with a relational database store using XML based configuration. +| link:guides/httpsession-jdbc-xml.html[HttpSession JDBC XML Guide] + |=== [[httpsession]] @@ -263,6 +271,35 @@ Guide when integrating with your own application. include::guides/httpsession-gemfire-p2p-xml.adoc[tags=config,leveloffset=+3] +[[httpsession-jdbc]] +=== HttpSession with JDBC + +Using Spring Session with `HttpSession` is enabled by adding a Servlet Filter before anything that uses the `HttpSession`. +You can choose from enabling this using either: + +* <> +* <> + +[[httpsession-jdbc-jc]] +==== JDBC Java Based Configuration + +This section describes how to use a relational database to back `HttpSession` using Java based configuration. + +NOTE: The <> provides a working sample on how to integrate Spring Session and `HttpSession` using Java configuration. +You can read the basic steps for integration below, but you are encouraged to follow along with the detailed HttpSession JDBC Guide when integrating with your own application. + +include::guides/httpsession-jdbc.adoc[tags=config,leveloffset=+3] + +[[httpsession-jdbc-xml]] +==== JDBC XML Based Configuration + +This section describes how to use a relational database to back `HttpSession` using XML based configuration. + +NOTE: The <> provides a working sample on how to integrate Spring Session and `HttpSession` using XML configuration. +You can read the basic steps for integration below, but you are encouraged to follow along with the detailed HttpSession JDBC XML Guide when integrating with your own application. + +include::guides/httpsession-jdbc-xml.adoc[tags=config,leveloffset=+3] + [[httpsession-mongo]] === HttpSession with Mongo @@ -906,6 +943,67 @@ To run it use the following: ./gradlew :samples:hazelcast-spring:tomcatRun +[[api-jdbcoperationssessionrepository]] +=== JdbcOperationsSessionRepository + +`JdbcOperationsSessionRepository` is a `SessionRepository` implementation that uses Spring's `JdbcOperations` to store sessions in a relational database. +In a web environment, this is typically used in combination with `SessionRepositoryFilter`. +Please note that this implementation does not support publishing of session events. + +[[api-jdbcoperationssessionrepository-new]] +==== Instantiating a JdbcOperationsSessionRepository + +A typical example of how to create a new instance can be seen below: + +[source,java,indent=0] +---- +include::{indexdoc-tests}[tags=new-jdbcoperationssessionrepository] +---- + +For additional information on how to create and configure a `JdbcTemplate`, refer to the Spring Framework Reference Documentation. + +[[api-jdbcoperationssessionrepository-config]] +==== EnableJdbcHttpSession + +In a web environment, the simplest way to create a new `JdbcOperationsSessionRepository` is to use `@EnableJdbcHttpSession`. +Complete example usage can be found in the <> +You can use the following attributes to customize the configuration: + +* **tableName** - the name of database table used by Spring Session to store sessions +* **maxInactiveIntervalInSeconds** - the amount of time before the session will expire in seconds + +===== Custom LobHandler + +You can customize the BLOB handling by creating a Bean named `springSessionLobHandler` that implements `LobHandler`. + +===== Custom ConversionService + +You can customize the default serialization and deserialization of the session by providing a `ConversionService` instance. +When working in a typical Spring environment, the default `ConversionService` Bean (named `conversionService`) will be automatically picked up and used for serialization and deserialization. +However, you can override the default `ConversionService` by providing a Bean named `springSessionConversionService`. + +[[api-jdbcoperationssessionrepository-storage]] +==== Storage Details + +By default, this implementation uses `SPRING_SESSION` table to store sessions. Note that the table name can be easily customized as already described. + +Due to the differences between the various database vendors, especially when it comes to storing binary data, make sure to use SQL script specific to your database. +Scripts for most major database vendors are packaged as `org/springframework/session/jdbc/schema-\*.sql`, where `*` is the target database type. + +For example, with PostgreSQL database you would use the following schema script: + +[source,sql,indent=0] +---- +include::{session-main-resources-dir}org/springframework/session/jdbc/schema-postgresql.sql[] +---- + +And with MySQL database: + +[source,sql,indent=0] +---- +include::{session-main-resources-dir}org/springframework/session/jdbc/schema-mysql.sql[] +---- + [[community]] == Spring Session Community diff --git a/docs/src/test/java/docs/IndexDocTests.java b/docs/src/test/java/docs/IndexDocTests.java index 859a8217..09e376ae 100644 --- a/docs/src/test/java/docs/IndexDocTests.java +++ b/docs/src/test/java/docs/IndexDocTests.java @@ -19,12 +19,14 @@ package docs; import org.junit.Test; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.mock.web.MockServletContext; import org.springframework.session.ExpiringSession; import org.springframework.session.MapSessionRepository; import org.springframework.session.Session; import org.springframework.session.SessionRepository; import org.springframework.session.data.redis.RedisOperationsSessionRepository; +import org.springframework.session.jdbc.JdbcOperationsSessionRepository; import org.springframework.session.web.http.SessionRepositoryFilter; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; @@ -32,6 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** * @author Rob Winch + * @author Vedran Pavic */ public class IndexDocTests { static final String ATTR_USER = "user"; @@ -113,6 +116,19 @@ public class IndexDocTests { // end::new-mapsessionrepository[] } + @Test + @SuppressWarnings("unused") + public void newJdbcOperationsSessionRepository() { + // tag::new-jdbcoperationssessionrepository[] + JdbcTemplate jdbcTemplate = new JdbcTemplate(); + + // ... configure JdbcTemplate ... + + SessionRepository repository = + new JdbcOperationsSessionRepository(jdbcTemplate); + // end::new-jdbcoperationssessionrepository[] + } + @Test public void runSpringHttpSessionConfig() { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); diff --git a/samples/httpsession-jdbc-xml/src/main/webapp/WEB-INF/spring/session.xml b/samples/httpsession-jdbc-xml/src/main/webapp/WEB-INF/spring/session.xml index b9488e9c..35f6d431 100644 --- a/samples/httpsession-jdbc-xml/src/main/webapp/WEB-INF/spring/session.xml +++ b/samples/httpsession-jdbc-xml/src/main/webapp/WEB-INF/spring/session.xml @@ -14,7 +14,7 @@ - + diff --git a/samples/httpsession-jdbc-xml/src/main/webapp/WEB-INF/web.xml b/samples/httpsession-jdbc-xml/src/main/webapp/WEB-INF/web.xml index f6ccafd8..6ebf56ce 100644 --- a/samples/httpsession-jdbc-xml/src/main/webapp/WEB-INF/web.xml +++ b/samples/httpsession-jdbc-xml/src/main/webapp/WEB-INF/web.xml @@ -49,6 +49,16 @@ /session + + h2console + org.h2.server.web.WebServlet + + + + h2console + /console/* + + index.jsp diff --git a/samples/httpsession-jdbc/src/main/java/sample/H2ConsoleInitializer.java b/samples/httpsession-jdbc/src/main/java/sample/H2ConsoleInitializer.java new file mode 100644 index 00000000..ffdfaf67 --- /dev/null +++ b/samples/httpsession-jdbc/src/main/java/sample/H2ConsoleInitializer.java @@ -0,0 +1,32 @@ +/* + * 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 sample; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; + +import org.h2.server.web.WebServlet; + +import org.springframework.web.WebApplicationInitializer; + +public class H2ConsoleInitializer implements WebApplicationInitializer { + + @Override + public void onStartup(ServletContext servletContext) throws ServletException { + servletContext.addServlet("h2console", new WebServlet()).addMapping("/console/*"); + } + +}