Add sample demonstrating the use of Spring Session with Gfsh started Apache Geode servers configured with DataSerialization.
Resolve gh-11.
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 157 KiB |
@@ -0,0 +1,39 @@
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
apply plugin: 'io.spring.convention.spring-sample-boot'
|
||||
|
||||
dependencies {
|
||||
|
||||
compile project(':spring-session-data-geode')
|
||||
|
||||
compile("org.springframework.boot:spring-boot-starter-thymeleaf") {
|
||||
exclude group: "org.springframework.boot", module: "spring-boot-starter-logging"
|
||||
}
|
||||
|
||||
compile("org.springframework.boot:spring-boot-starter-web") {
|
||||
exclude group: "org.springframework.boot", module: "spring-boot-starter-logging"
|
||||
}
|
||||
|
||||
compile "org.springframework.data:spring-data-geode-test"
|
||||
compile "org.webjars:bootstrap"
|
||||
compile "org.webjars:webjars-locator"
|
||||
|
||||
runtime "org.springframework.shell:spring-shell"
|
||||
|
||||
testCompile("org.springframework.boot:spring-boot-starter-test") {
|
||||
exclude group: "org.springframework.boot", module: "spring-boot-starter-logging"
|
||||
}
|
||||
}
|
||||
|
||||
bootJar {
|
||||
mainClassName = 'sample.client.Application'
|
||||
}
|
||||
|
||||
processResources {
|
||||
filter ReplaceTokens, tokens: [
|
||||
'spring.version' : project.property("springVersion"),
|
||||
'spring-data.version' : project.property("springDataGeodeVersion"),
|
||||
'spring-session.version' : project.property("springSessionVersion"),
|
||||
'spring-session-data-geode.version' : project.property("version")
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2018 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.client;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.gemfire.config.annotation.ClientCacheApplication;
|
||||
import org.springframework.data.gemfire.util.CollectionUtils;
|
||||
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
// tag::class[]
|
||||
@SpringBootApplication // <1>
|
||||
@ClientCacheApplication(subscriptionEnabled = true) // <2>
|
||||
@EnableGemFireHttpSession(regionName = "Sessions", poolName = "DEFAULT") // <3>
|
||||
@Controller // <4>
|
||||
public class Application {
|
||||
|
||||
private static final String INDEX_TEMPLATE_VIEW_NAME = "index";
|
||||
private static final String PING_RESPONSE = "PONG";
|
||||
private static final String REQUEST_COUNT_ATTRIBUTE_NAME = "requestCount";
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SpringWebMvcConfiguration { // <5>
|
||||
|
||||
@Bean
|
||||
public WebMvcConfigurer webMvcConfig() {
|
||||
|
||||
return new WebMvcConfigurer() {
|
||||
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController("/").setViewName(INDEX_TEMPLATE_VIEW_NAME);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ExceptionHandler // <6>
|
||||
public String errorHandler(Throwable error) {
|
||||
StringWriter writer = new StringWriter();
|
||||
error.printStackTrace(new PrintWriter(writer));
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
@GetMapping("/ping")
|
||||
@ResponseBody
|
||||
public String ping() {
|
||||
return PING_RESPONSE;
|
||||
}
|
||||
|
||||
@PostMapping("/session")
|
||||
public String session(HttpSession session, ModelMap modelMap,
|
||||
@RequestParam(name = "attributeName", required = false) String name,
|
||||
@RequestParam(name = "attributeValue", required = false) String value) { // <7>
|
||||
|
||||
modelMap.addAttribute("sessionAttributes",
|
||||
attributes(setAttribute(updateRequestCount(session), name, value)));
|
||||
|
||||
return INDEX_TEMPLATE_VIEW_NAME;
|
||||
}
|
||||
|
||||
// end::class[]
|
||||
|
||||
HttpSession updateRequestCount(HttpSession session) {
|
||||
|
||||
synchronized (session) {
|
||||
Integer currentRequestCount = (Integer) session.getAttribute(REQUEST_COUNT_ATTRIBUTE_NAME);
|
||||
session.setAttribute(REQUEST_COUNT_ATTRIBUTE_NAME, nullSafeIncrement(currentRequestCount));
|
||||
return session;
|
||||
}
|
||||
}
|
||||
|
||||
Integer nullSafeIncrement(Integer value) {
|
||||
return nullSafeIntValue(value) + 1;
|
||||
}
|
||||
|
||||
int nullSafeIntValue(Number value) {
|
||||
return Optional.ofNullable(value).map(Number::intValue).orElse(0);
|
||||
}
|
||||
|
||||
HttpSession setAttribute(HttpSession session, String attributeName, String attributeValue) {
|
||||
|
||||
if (isSet(attributeName, attributeValue)) {
|
||||
session.setAttribute(attributeName, attributeValue);
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
boolean isSet(String... values) {
|
||||
|
||||
boolean set = true;
|
||||
|
||||
for (String value : values) {
|
||||
set &= StringUtils.hasText(value);
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
Map<String, String> attributes(HttpSession session) {
|
||||
|
||||
Map<String, String> sessionAttributes = new HashMap<>();
|
||||
|
||||
StreamSupport.stream(toIterable(session.getAttributeNames()).spliterator(), false)
|
||||
.forEach(attributeName -> sessionAttributes.put(attributeName,
|
||||
String.valueOf(session.getAttribute(attributeName))));
|
||||
|
||||
return sessionAttributes;
|
||||
}
|
||||
|
||||
<T> Iterable<T> toIterable(Enumeration<T> enumeration) {
|
||||
|
||||
return () -> Optional.ofNullable(enumeration)
|
||||
.map(CollectionUtils::toIterator)
|
||||
.orElseGet(Collections::emptyIterator);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#tag::shell-script[]
|
||||
#!$GEODE_HOME/bin/gfsh
|
||||
|
||||
set variable --name=USER_HOME --value=${SYS_USER_HOME}
|
||||
set variable --name=REPO_HOME --value=${USER_HOME}/.m2/repository
|
||||
set variable --name=SPRING_VERSION --value=@spring.version@
|
||||
set variable --name=SPRING_DATA_VERSION --value=@spring-data.version@
|
||||
set variable --name=SPRING_SESSION_VERSION --value=@spring-session.version@
|
||||
set variable --name=SPRING_SESSION_DATA_GEODE_VERSION --value=@spring-session-data-geode.version@
|
||||
set variable --name=MEMBER_TIMEOUT --value=5000
|
||||
set variable --name=CACHE_XML_FILE --value=${USER_HOME}/spring-session-data-geode/samples/boot/gemfire-with-gfsh-servers/src/main/resources/initializer-cache.xml
|
||||
|
||||
#set variable --name=SERVER_CLASSPATH --value=${REPO_HOME}/org/springframework/spring-core/${SPRING_VERSION}/spring-core-${SPRING_VERSION}.jar\
|
||||
#:${REPO_HOME}/org/springframework/spring-aop/${SPRING_VERSION}/spring-aop-${SPRING_VERSION}.jar\
|
||||
#:${REPO_HOME}/org/springframework/spring-beans/${SPRING_VERSION}/spring-beans-${SPRING_VERSION}.jar\
|
||||
#:${REPO_HOME}/org/springframework/spring-context/${SPRING_VERSION}/spring-context-${SPRING_VERSION}.jar\
|
||||
#:${REPO_HOME}/org/springframework/spring-context-support/${SPRING_VERSION}/spring-context-support-${SPRING_VERSION}.jar\
|
||||
#:${REPO_HOME}/org/springframework/spring-expression/${SPRING_VERSION}/spring-expression-${SPRING_VERSION}.jar\
|
||||
#:${REPO_HOME}/org/springframework/spring-jcl/${SPRING_VERSION}/spring-jcl-${SPRING_VERSION}.jar\
|
||||
#:${REPO_HOME}/org/springframework/spring-tx/${SPRING_VERSION}/spring-tx-${SPRING_VERSION}.jar\
|
||||
#:${REPO_HOME}/org/springframework/data/spring-data-commons/${SPRING_DATA_VERSION}/spring-data-commons-${SPRING_DATA_VERSION}.jar\
|
||||
#:${REPO_HOME}/org/springframework/data/spring-data-geode/${SPRING_DATA_VERSION}/spring-data-geode-${SPRING_DATA_VERSION}.jar\
|
||||
#:${REPO_HOME}/org/springframework/session/spring-session-core/${SPRING_SESSION_VERSION}/spring-session-core-${SPRING_SESSION_VERSION}.jar\
|
||||
#:${REPO_HOME}/org/springframework/session/spring-session-data-geode/${SPRING_SESSION_DATA_GEODE_VERSION}/spring-session-data-geode-${SPRING_SESSION_DATA_GEODE_VERSION}.jar\
|
||||
#:${REPO_HOME}/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar
|
||||
|
||||
set variable --name=SERVER_CLASSPATH --value=${REPO_HOME}/org/springframework/spring-core/${SPRING_VERSION}/spring-core-${SPRING_VERSION}.jar:${REPO_HOME}/org/springframework/spring-aop/${SPRING_VERSION}/spring-aop-${SPRING_VERSION}.jar:${REPO_HOME}/org/springframework/spring-beans/${SPRING_VERSION}/spring-beans-${SPRING_VERSION}.jar:${REPO_HOME}/org/springframework/spring-context/${SPRING_VERSION}/spring-context-${SPRING_VERSION}.jar:${REPO_HOME}/org/springframework/spring-context-support/${SPRING_VERSION}/spring-context-support-${SPRING_VERSION}.jar:${REPO_HOME}/org/springframework/spring-expression/${SPRING_VERSION}/spring-expression-${SPRING_VERSION}.jar:${REPO_HOME}/org/springframework/spring-jcl/${SPRING_VERSION}/spring-jcl-${SPRING_VERSION}.jar:${REPO_HOME}/org/springframework/spring-tx/${SPRING_VERSION}/spring-tx-${SPRING_VERSION}.jar:${REPO_HOME}/org/springframework/data/spring-data-commons/${SPRING_DATA_VERSION}/spring-data-commons-${SPRING_DATA_VERSION}.jar:${REPO_HOME}/org/springframework/data/spring-data-geode/${SPRING_DATA_VERSION}/spring-data-geode-${SPRING_DATA_VERSION}.jar:${REPO_HOME}/org/springframework/session/spring-session-core/${SPRING_SESSION_VERSION}/spring-session-core-${SPRING_SESSION_VERSION}.jar:${REPO_HOME}/org/springframework/session/spring-session-data-geode/${SPRING_SESSION_DATA_GEODE_VERSION}/spring-session-data-geode-${SPRING_SESSION_DATA_GEODE_VERSION}.jar:${REPO_HOME}/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar
|
||||
|
||||
start locator --name=Locator1 --log-level=config
|
||||
#start locator --name=Locator1 --log-level=config --J=-Dgemfire.member-timeout=${MEMBER_TIMEOUT}
|
||||
|
||||
start server --name=Server1 --log-level=config --cache-xml-file=${CACHE_XML_FILE} --classpath=${SERVER_CLASSPATH}
|
||||
#start server --name=Server1 --log-level=config --cache-xml-file=${CACHE_XML_FILE} --classpath=${SERVER_CLASSPATH} --J=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 --J=-Dgemfire.member-timeout=${MEMBER_TIMEOUT}
|
||||
|
||||
start server --name=Server2 --server-port=0 --log-level=config --cache-xml-file=${CACHE_XML_FILE} --classpath=${SERVER_CLASSPATH}
|
||||
#start server --name=Server2 --server-port=0 --log-level=config --cache-xml-file=${CACHE_XML_FILE} --classpath=${SERVER_CLASSPATH} --J=-Dgemfire.member-timeout=${MEMBER_TIMEOUT}
|
||||
|
||||
create region --name=Sessions --type=PARTITION --skip-if-exists --enable-statistics=true --entry-idle-time-expiration=15 --entry-idle-time-expiration-action=INVALIDATE
|
||||
|
||||
#end::shell-script[]
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
gfsh -e "run --file=${SYS_USER_HOME}/spring-session-data-geode/samples/boot/gemfire-with-gfsh-servers/src/main/resources/geode/bin/start-cluster.gfsh"
|
||||
@@ -0,0 +1,5 @@
|
||||
#!$GEODE_HOME/bin/gfsh
|
||||
|
||||
stop server --name=Server2
|
||||
stop server --name=Server1
|
||||
stop locator --name=Locator1
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
gfsh -e "run --file=${SYS_USER_HOME}/spring-session-data-geode/samples/boot/gemfire-with-gfsh-servers/src/main/resources/geode/bin/stop-cluster.gfsh"
|
||||
@@ -0,0 +1,5 @@
|
||||
# tag::queries[]
|
||||
SELECT session.id FROM /Sessions session
|
||||
SELECT session.getClass().getName() FROM /Sessions session
|
||||
SELECT attributes.key, attributes.value FROM /Sessions session, session.attributes attributes
|
||||
# end::queries[]
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- tag::cache-xml[] -->
|
||||
<cache xmlns="http://geode.apache.org/schema/cache"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://geode.apache.org/schema/cache http://geode.apache.org/schema/cache/cache-1.0.xsd"
|
||||
version="1.0">
|
||||
|
||||
<initializer>
|
||||
<class-name>
|
||||
org.springframework.session.data.gemfire.serialization.data.support.DataSerializableSessionSerializerInitializer
|
||||
</class-name>
|
||||
</initializer>
|
||||
|
||||
</cache>
|
||||
<!-- end::cache-xml[] -->
|
||||
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-3.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<title>Session Attributes</title>
|
||||
<link rel="stylesheet" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" href="/webjars/bootstrap/css/bootstrap.min.css"/>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Description</h1>
|
||||
<p>
|
||||
This application demonstrates how to use a GemFire instance to back your session. Notice that there
|
||||
is no JSESSIONID cookie. We are also able to customize the way of identifying what the requested
|
||||
session id is.
|
||||
</p>
|
||||
|
||||
<h1>Try it</h1>
|
||||
|
||||
<form class="form-inline" role="form" action="./session" method="post">
|
||||
<label for="attributeName">Attribute Name</label>
|
||||
<input id="attributeName" type="text" name="attributeName"/>
|
||||
<label for="attributeValue">Attribute Value</label>
|
||||
<input id="attributeValue" type="text" name="attributeValue"/>
|
||||
<input type="submit" value="Set Attribute"/>
|
||||
</form>
|
||||
|
||||
<hr/>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Attribute Name</th>
|
||||
<th>Attribute Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="attribute: ${sessionAttributes}">
|
||||
<td th:text="${attribute.key}">name</td>
|
||||
<td th:text="${attribute.value}">value</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user