Add a Spring Boot with Spring Session Data Geode sample showing an application using 'session' and 'request' scoped proxy bean.
Resolves Issue #3.
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
apply plugin: 'io.spring.convention.spring-sample-boot'
|
||||
apply plugin: "application"
|
||||
|
||||
apply from: IDE_GRADLE
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
compile project(':spring-session-data-geode')
|
||||
compile "org.springframework.boot:spring-boot-starter-thymeleaf"
|
||||
compile "org.springframework.boot:spring-boot-starter-web"
|
||||
compile "org.webjars:bootstrap"
|
||||
compile "org.webjars:webjars-locator"
|
||||
|
||||
runtime "org.springframework.shell:spring-shell"
|
||||
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test"
|
||||
testCompile seleniumDependencies
|
||||
|
||||
// integrationTestCompile seleniumDependencies
|
||||
|
||||
// integrationTestRuntime "org.springframework.shell:spring-shell"
|
||||
|
||||
}
|
||||
|
||||
run {
|
||||
doFirst {
|
||||
mainClassName = 'sample.server.GemFireServer'
|
||||
}
|
||||
}
|
||||
|
||||
bootJar {
|
||||
mainClassName = 'sample.client.Application'
|
||||
}
|
||||
|
||||
task runGemFireServer() {
|
||||
doLast {
|
||||
ext.port = reservePort()
|
||||
|
||||
println "Starting Apache Geode Server on port [$port] ..."
|
||||
|
||||
def out = new StringBuilder()
|
||||
def err = new StringBuilder()
|
||||
|
||||
String classpath = sourceSets.main.runtimeClasspath.collect { it }.join(File.pathSeparator)
|
||||
|
||||
String[] commandLine = [
|
||||
'java', '-server', '-ea', '-classpath', classpath,
|
||||
//"-Dgemfire.log-file=gemfire-server.log",
|
||||
//"-Dgemfire.log-level=config",
|
||||
"-Dspring.session.data.geode.cache.server.port=$port",
|
||||
'sample.server.GemFireServer'
|
||||
]
|
||||
|
||||
//println commandLine
|
||||
|
||||
ext.process = commandLine.execute()
|
||||
//ext.process = new ProcessBuilder().command(commandLine).redirectErrorStream(true).start();
|
||||
|
||||
ext.process.consumeProcessOutput(out, err)
|
||||
|
||||
//println 'OUT: ' + out
|
||||
//println 'ERR: ' + err
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
integrationTest {
|
||||
dependsOn runGemFireServer
|
||||
doFirst {
|
||||
def port = reservePort()
|
||||
systemProperties['management.port'] = 0
|
||||
systemProperties['server.port'] = port
|
||||
//systemProperties['gemfire.log-file'] = "gemfire-client.log"
|
||||
//systemProperties['gemfire.log-level'] = "config"
|
||||
systemProperties['spring.session.data.geode.cache.server.port'] = runGemFireServer.port
|
||||
}
|
||||
doLast {
|
||||
println 'Stopping Apache Geode Server...'
|
||||
runGemFireServer.process?.destroyForcibly()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def reservePort() {
|
||||
def socket = new ServerSocket(0)
|
||||
def result = socket.localPort
|
||||
socket.close()
|
||||
result
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 2017 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 javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
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.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
import org.springframework.data.gemfire.config.annotation.ClientCacheApplication;
|
||||
import org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer;
|
||||
import org.springframework.data.gemfire.support.ConnectionEndpoint;
|
||||
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import sample.client.model.RequestScopedProxyBean;
|
||||
import sample.client.model.SessionScopedProxyBean;
|
||||
|
||||
/**
|
||||
* A Spring Boot, GemFire cache client, web application that reveals the current state of the HTTP Session.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see javax.servlet.http.HttpSession
|
||||
* @see org.springframework.boot.SpringApplication
|
||||
* @see org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
* @see org.springframework.context.annotation.Bean
|
||||
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
|
||||
* @see org.springframework.stereotype.Controller
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
* @since 1.2.1
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
// tag::class[]
|
||||
@SpringBootApplication // <1>
|
||||
@Controller // <2>
|
||||
public class Application {
|
||||
|
||||
static final String INDEX_TEMPLATE_VIEW_NAME = "index";
|
||||
static final String PING_RESPONSE = "PONG";
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
@ClientCacheApplication(name = "SpringSessionDataGeodeClientWithScopedProxiesBootSample", logLevel = "config",
|
||||
pingInterval = 5000L, readTimeout = 15000, retryAttempts = 1, subscriptionEnabled = true) // <3>
|
||||
@EnableGemFireHttpSession(poolName = "DEFAULT") // <4>
|
||||
static class ClientCacheConfiguration {
|
||||
|
||||
@Bean
|
||||
static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ClientCacheConfigurer clientCacheServerPortConfigurer(
|
||||
@Value("${spring.session.data.geode.cache.server.port:40404}") int port) { // <5>
|
||||
|
||||
return (beanName, clientCacheFactoryBean) ->
|
||||
clientCacheFactoryBean.setServers(Collections.singletonList(
|
||||
new ConnectionEndpoint("localhost", port)));
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SpringWebMvcConfiguration { // <6>
|
||||
|
||||
@Bean
|
||||
public WebMvcConfigurer webMvcConfig() {
|
||||
|
||||
return new WebMvcConfigurer() {
|
||||
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController("/").setViewName(INDEX_TEMPLATE_VIEW_NAME);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private RequestScopedProxyBean requestBean;
|
||||
|
||||
@Autowired
|
||||
private SessionScopedProxyBean sessionBean;
|
||||
|
||||
@ExceptionHandler
|
||||
@ResponseBody
|
||||
public String errorHandler(Throwable error) {
|
||||
StringWriter writer = new StringWriter();
|
||||
error.printStackTrace(new PrintWriter(writer));
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/ping")
|
||||
@ResponseBody
|
||||
public String ping() {
|
||||
return PING_RESPONSE;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/counts")
|
||||
public String requestAndSessionInstanceCount(HttpServletRequest request, HttpSession session, Model model) {
|
||||
|
||||
model.addAttribute("sessionId", session.getId());
|
||||
model.addAttribute("requestCount", this.requestBean.getCount());
|
||||
model.addAttribute("sessionCount", this.sessionBean.getCount());
|
||||
|
||||
return INDEX_TEMPLATE_VIEW_NAME;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2017 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.model;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.annotation.RequestScope;
|
||||
|
||||
/**
|
||||
* The RequestScopedProxyBean class...
|
||||
*
|
||||
* @author John Blum
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Component
|
||||
@RequestScope(proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||
@SuppressWarnings("unused")
|
||||
public class RequestScopedProxyBean {
|
||||
|
||||
private static final AtomicInteger INSTANCE_COUNTER = new AtomicInteger(0);
|
||||
|
||||
private final int count;
|
||||
|
||||
public RequestScopedProxyBean() {
|
||||
this.count = INSTANCE_COUNTER.incrementAndGet();
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("{ @type = '%s', count = %d }", getClass().getName(), getCount());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2017 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.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.annotation.SessionScope;
|
||||
|
||||
/**
|
||||
* The SessionScopedProxyBean class...
|
||||
*
|
||||
* @author John Blum
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Component
|
||||
@SessionScope(proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||
@SuppressWarnings("unused")
|
||||
public class SessionScopedProxyBean implements Serializable {
|
||||
|
||||
private static final AtomicInteger INSTANCE_COUNTER = new AtomicInteger(0);
|
||||
|
||||
private final int count;
|
||||
|
||||
public SessionScopedProxyBean() {
|
||||
this.count = INSTANCE_COUNTER.incrementAndGet();
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("{ @type = '%s', count = %d }", getClass().getName(), getCount());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2017 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.server;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
import org.springframework.data.gemfire.config.annotation.CacheServerApplication;
|
||||
import org.springframework.data.gemfire.config.annotation.CacheServerConfigurer;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableManager;
|
||||
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
|
||||
|
||||
/**
|
||||
* A Spring Boot application bootstrapping a GemFire Cache Server JVM process.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.boot.SpringApplication
|
||||
* @see org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
* @see org.springframework.context.annotation.Bean
|
||||
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
|
||||
* @see org.apache.geode.cache.Cache
|
||||
* @since 1.2.1
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
// tag::class[]
|
||||
@SpringBootApplication
|
||||
@CacheServerApplication(name = "SpringSessionDataGeodeServerWithScopedProxiesBootSample", logLevel = "config")
|
||||
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 10)
|
||||
@EnableManager(start = true)
|
||||
public class GemFireServer {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
new SpringApplicationBuilder(GemFireServer.class)
|
||||
.web(WebApplicationType.NONE)
|
||||
.build()
|
||||
.run(args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
CacheServerConfigurer cacheServerPortConfigurer(
|
||||
@Value("${spring.session.data.geode.cache.server.port:40404}") int port) {
|
||||
|
||||
return (beanName, cacheServerFactoryBean) ->
|
||||
cacheServerFactoryBean.setPort(port);
|
||||
}
|
||||
}
|
||||
// end::class[]
|
||||
@@ -0,0 +1,40 @@
|
||||
<!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>Request and Session Counts</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 both Spring 'request' and 'session' scoped (proxy) beans are utilized
|
||||
in the context of Spring Session when GemFire is used to back the HTTP Session.
|
||||
</p>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th> * </th>
|
||||
<th>Session ID</th>
|
||||
<th>Session Count</th>
|
||||
<th>Request Count</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>count</b></td>
|
||||
<td th:text="${sessionId}">?</td>
|
||||
<td th:text="${sessionCount}">0</td>
|
||||
<td th:text="${requestCount}">0</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user