Migrate WebFlux sample to Boot

Closes gh-923
This commit is contained in:
Vedran Pavic
2017-11-10 10:05:48 +01:00
parent f2ba773ec2
commit 4c9fbd5b6b
18 changed files with 76 additions and 327 deletions

View File

@@ -4,7 +4,6 @@
<suppressions>
<suppress files=".+Application\.java" checks="HideUtilityClassConstructor"/>
<suppress files=".+Configuration\.java" checks="HideUtilityClassConstructor"/>
<suppress files=".+SSEFluxWebConfig\.java" checks="RegexpHeader"/>
<suppress files="[\\/]src[\\/]test[\\/]java[\\/]" checks="Javadoc"/>
<suppress files="[\\/]src[\\/]integration-test[\\/]java[\\/]" checks="Javadoc"/>

View File

@@ -20,36 +20,18 @@ dependencyManagement {
entry 'testcontainers'
}
dependency 'ch.qos.logback:logback-classic:1.2.3'
dependency 'com.h2database:h2:1.4.196'
dependency 'com.maxmind.geoip2:geoip2:2.3.1'
dependency 'commons-codec:commons-codec:1.11'
dependency 'edu.umd.cs.mtc:multithreadedtc:1.01'
dependency 'io.lettuce:lettuce-core:5.0.0.RELEASE'
dependency 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.1'
dependency 'javax.servlet.jsp:javax.servlet.jsp-api:2.3.2-b02'
dependency 'javax.servlet:javax.servlet-api:3.1.0'
dependency 'junit:junit:4.12'
dependency 'mysql:mysql-connector-java:5.1.44'
dependency 'org.apache.derby:derby:10.14.1.0'
dependency 'org.apache.httpcomponents:httpclient:4.5.3'
dependency 'org.apache.taglibs:taglibs-standard-jstlel:1.2.5'
dependency 'org.assertj:assertj-core:3.8.0'
dependency 'org.hsqldb:hsqldb:2.4.0'
dependency 'org.mariadb.jdbc:mariadb-java-client:2.1.2'
dependency 'org.mockito:mockito-core:2.11.0'
dependency 'org.postgresql:postgresql:42.1.4'
dependency 'org.seleniumhq.selenium:htmlunit-driver:2.27'
dependency 'org.slf4j:jcl-over-slf4j:1.7.25'
dependency 'org.slf4j:log4j-over-slf4j:1.7.25'
dependency 'org.thymeleaf.extras:thymeleaf-extras-java8time:3.0.1.RELEASE'
dependency 'org.thymeleaf:thymeleaf-spring5:3.0.8.RELEASE'
dependency 'org.webjars:bootstrap:2.3.2'
dependency 'org.webjars:html5shiv:3.7.3'
dependency 'org.webjars:jquery:1.12.4'
dependency 'org.webjars:knockout:2.3.0'
dependency 'org.webjars:sockjs-client:0.3.4'
dependency 'org.webjars:stomp-websocket:2.3.0'
dependency 'org.webjars:webjars-taglib:0.3'
}
}

View File

@@ -0,0 +1,15 @@
apply plugin: 'io.spring.convention.spring-sample-boot'
dependencies {
compile project(':spring-session-data-redis')
compile "org.springframework.boot:spring-boot-starter-webflux"
compile "org.springframework.boot:spring-boot-starter-thymeleaf"
compile "org.springframework.boot:spring-boot-starter-data-redis"
compile "org.springframework.boot:spring-boot-devtools"
compile 'org.webjars:bootstrap'
testCompile "org.springframework.boot:spring-boot-starter-test"
integrationTestCompile seleniumDependencies
integrationTestCompile "org.testcontainers:testcontainers"
}

View File

@@ -16,21 +16,26 @@
package sample;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.testcontainers.containers.GenericContainer;
import sample.pages.HomePage;
import sample.pages.HomePage.Attribute;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
@@ -40,11 +45,18 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Rob Winch
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = HelloWebfluxApplication.class)
@TestPropertySource(properties = { "spring.profiles.active=embedded-redis", "server.port=0" })
@SpringBootTest(classes = HelloWebFluxApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
@ContextConfiguration(initializers = AttributeTests.Initializer.class)
public class AttributeTests {
@Value("#{@nettyContext.address().getPort()}")
int port;
private static final String DOCKER_IMAGE = "redis:4.0.2";
@ClassRule
public static GenericContainer redisContainer = new GenericContainer(DOCKER_IMAGE)
.withExposedPorts(6379);
@LocalServerPort
private int port;
private WebDriver driver;
@@ -87,4 +99,18 @@ public class AttributeTests {
assertThat(row.getAttributeValue()).isEqualTo("b");
}
static class Initializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(
ConfigurableApplicationContext configurableApplicationContext) {
TestPropertyValues
.of("spring.redis.host=" + redisContainer.getContainerIpAddress(),
"spring.redis.port=" + redisContainer.getFirstMappedPort())
.applyTo(configurableApplicationContext.getEnvironment());
}
}
}

View File

@@ -16,19 +16,17 @@
package sample;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@Import(EmbeddedRedisConfig.class)
// tag::class[]
@EnableRedisWebSession
public class HelloWebfluxSessionConfig {
/**
* @author Rob Winch
*/
@SpringBootApplication
public class HelloWebFluxApplication {
@Bean
public LettuceConnectionFactory lettuceConnectionFactory() {
return new LettuceConnectionFactory();
public static void main(String[] args) {
SpringApplication.run(HelloWebFluxApplication.class, args);
}
}
// end::class[]

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,18 @@
dependencyManagement {
dependencies {
dependency 'ch.qos.logback:logback-classic:1.2.3'
dependency 'com.maxmind.geoip2:geoip2:2.3.1'
dependency 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.1'
dependency 'javax.servlet.jsp:javax.servlet.jsp-api:2.3.2-b02'
dependency 'org.apache.taglibs:taglibs-standard-jstlel:1.2.5'
dependency 'org.slf4j:jcl-over-slf4j:1.7.25'
dependency 'org.slf4j:log4j-over-slf4j:1.7.25'
dependency 'org.webjars:bootstrap:2.3.2'
dependency 'org.webjars:html5shiv:3.7.3'
dependency 'org.webjars:jquery:1.12.4'
dependency 'org.webjars:knockout:2.3.0'
dependency 'org.webjars:sockjs-client:0.3.4'
dependency 'org.webjars:stomp-websocket:2.3.0'
dependency 'org.webjars:webjars-taglib:0.3'
}
}

View File

@@ -1,23 +0,0 @@
apply plugin: 'io.spring.convention.spring-sample'
dependencies {
compile project(':spring-session-data-redis')
compile 'io.lettuce:lettuce-core'
compile 'io.netty:netty-buffer'
compile 'io.projectreactor.ipc:reactor-netty'
compile 'org.springframework:spring-context'
compile 'org.springframework:spring-web'
compile 'org.springframework:spring-webflux'
compile 'org.thymeleaf.extras:thymeleaf-extras-java8time'
compile 'org.thymeleaf:thymeleaf-spring5'
compile 'org.webjars:bootstrap'
compile 'org.webjars:webjars-taglib'
compile jstlDependencies
compile slf4jDependencies
compile 'org.testcontainers:testcontainers'
testCompile 'junit:junit'
testCompile 'org.assertj:assertj-core'
integrationTestCompile seleniumDependencies
}

View File

@@ -1,59 +0,0 @@
/*
* Copyright 2014-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;
import java.io.IOException;
import org.testcontainers.containers.GenericContainer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
@Configuration
@Profile("embedded-redis")
public class EmbeddedRedisConfig {
private static final String REDIS_DOCKER_IMAGE = "redis:4.0.2";
@Bean(initMethod = "start")
public GenericContainer redisContainer() {
return new GenericContainer(REDIS_DOCKER_IMAGE) {
@Override
public void close() {
super.close();
try {
this.dockerClient.close();
}
catch (IOException ignored) {
}
}
}.withExposedPorts(6379);
}
@Bean
@Primary
public LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(redisContainer().getContainerIpAddress(),
redisContainer().getFirstMappedPort());
}
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright 2014-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;
import reactor.ipc.netty.NettyContext;
import reactor.ipc.netty.http.server.HttpServer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
/**
* @author Rob Winch
* @since 5.0
*/
@Configuration
@EnableWebFlux
@ComponentScan
public class HelloWebfluxApplication {
@Value("${server.port:8080}")
private int port = 8080;
public static void main(String[] args) throws Exception {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
HelloWebfluxApplication.class)) {
context.getBean(NettyContext.class).onClose().block();
}
}
@Bean
public NettyContext nettyContext(ApplicationContext context) {
HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context).build();
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer httpServer = HttpServer.create("localhost", this.port);
return httpServer.newHandler(adapter).block();
}
}

View File

@@ -1,91 +0,0 @@
/*
* =============================================================================
*
* Copyright (c) 2011-2014, The THYMELEAF team (http://www.thymeleaf.org)
*
* 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 org.thymeleaf.spring5.ISpringWebFluxTemplateEngine;
import org.thymeleaf.spring5.SpringWebFluxTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.reactive.ThymeleafReactiveViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SSEFluxWebConfig {
// TODO * Once there is a Spring Boot starter for thymeleaf-spring5, there would be no
// need to have
// TODO that @EnableConfigurationProperties annotation or use it for declaring the
// beans down in the
// TODO "thymeleaf" section below.
private ApplicationContext applicationContext;
public SSEFluxWebConfig(final ApplicationContext applicationContext) {
super();
this.applicationContext = applicationContext;
}
/*
* -------------------------------------- THYMELEAF CONFIGURATION
* --------------------------------------
*/
// TODO * If there was a Spring Boot starter for thymeleaf-spring5 most probably some
// or all of these
// TODO resolver and engine beans would not need to be specifically declared here.
@Bean
public SpringResourceTemplateResolver thymeleafTemplateResolver() {
final SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(this.applicationContext);
resolver.setPrefix("classpath:/templates/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCacheable(false);
resolver.setCheckExistence(true);
return resolver;
}
@Bean
public ISpringWebFluxTemplateEngine thymeleafTemplateEngine() {
// We override here the SpringTemplateEngine instance that would otherwise be
// instantiated by
// Spring Boot because we want to apply the SpringWebFlux-specific context
// factory, link builder...
final SpringWebFluxTemplateEngine templateEngine = new SpringWebFluxTemplateEngine();
templateEngine.setTemplateResolver(thymeleafTemplateResolver());
return templateEngine;
}
@Bean
public ThymeleafReactiveViewResolver thymeleafChunkedAndDataDrivenViewResolver() {
final ThymeleafReactiveViewResolver viewResolver = new ThymeleafReactiveViewResolver();
viewResolver.setTemplateEngine(thymeleafTemplateEngine());
viewResolver.setOrder(1);
viewResolver.setResponseMaxChunkSizeBytes(8192); // OUTPUT BUFFER size limit
return viewResolver;
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright 2014-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;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.ViewResolverRegistry;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;
/**
* @author Rob Winch
* @since 5.0
*/
@Configuration
public class ThymeleafWebfluxConfig implements WebFluxConfigurer {
@Autowired(required = false)
List<ViewResolver> views = new ArrayList<>();
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
for (ViewResolver view : this.views) {
registry.viewResolver(view);
}
}
}

View File

@@ -1,14 +0,0 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- <logger name="org.springframework.security" level="DEBUG"/> -->
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>