From 0ab781e53719621acf20c8880c7ca62e1bd85b6d Mon Sep 17 00:00:00 2001 From: Eleftheria Stein Date: Wed, 16 Sep 2020 16:35:03 +0200 Subject: [PATCH] Consolidate Hazelcast configurations Issue gh-1584 --- .../hazelcast4/hazelcast4.gradle | 12 +- .../web/http/EnableHazelcastHttpSession.java | 2 +- .../EnableHazelcastHttpSessionSelector.java | 48 ------ .../Hazelcast4HttpSessionConfiguration.java | 161 ------------------ .../HazelcastHttpSessionConfiguration.java | 76 +++++++-- ...ring-session-sample-boot-hazelcast4.gradle | 20 +++ .../java/sample/BootTests.java | 95 +++++++++++ .../java/sample/pages/BasePage.java | 38 +++++ .../java/sample/pages/HomePage.java | 60 +++++++ .../java/sample/pages/LoginPage.java | 66 +++++++ .../resources/testcontainers.properties | 1 + .../src/main/java/sample/Application.java | 31 ++++ .../java/sample/config/IndexController.java | 30 ++++ .../java/sample/config/SecurityConfig.java | 43 +++++ .../java/sample/config/SessionConfig.java | 57 +++++++ .../src/main/resources/application.properties | 1 + .../src/main/resources/static/favicon.ico | Bin 0 -> 1150 bytes .../src/main/resources/static/images/logo.png | Bin 0 -> 1123 bytes .../src/main/resources/templates/index.html | 11 ++ .../src/main/resources/templates/layout.html | 122 +++++++++++++ ...session-sample-javaconfig-hazelcast.gradle | 1 + 21 files changed, 649 insertions(+), 226 deletions(-) delete mode 100644 spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/EnableHazelcastHttpSessionSelector.java delete mode 100644 spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/Hazelcast4HttpSessionConfiguration.java create mode 100644 spring-session-samples/spring-session-sample-boot-hazelcast4/spring-session-sample-boot-hazelcast4.gradle create mode 100644 spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/java/sample/BootTests.java create mode 100644 spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/java/sample/pages/BasePage.java create mode 100644 spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/java/sample/pages/HomePage.java create mode 100644 spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/java/sample/pages/LoginPage.java create mode 100644 spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/resources/testcontainers.properties create mode 100644 spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/java/sample/Application.java create mode 100644 spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/java/sample/config/IndexController.java create mode 100644 spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/java/sample/config/SecurityConfig.java create mode 100644 spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/java/sample/config/SessionConfig.java create mode 100644 spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/resources/application.properties create mode 100644 spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/resources/static/favicon.ico create mode 100644 spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/resources/static/images/logo.png create mode 100644 spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/resources/templates/index.html create mode 100644 spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/resources/templates/layout.html diff --git a/spring-session-hazelcast/hazelcast4/hazelcast4.gradle b/spring-session-hazelcast/hazelcast4/hazelcast4.gradle index 76b49e59..2062f6e2 100644 --- a/spring-session-hazelcast/hazelcast4/hazelcast4.gradle +++ b/spring-session-hazelcast/hazelcast4/hazelcast4.gradle @@ -1,3 +1,12 @@ +buildscript { + repositories { + maven { url 'https://repo.spring.io/plugins-release' } + } + dependencies { + classpath 'io.spring.gradle:propdeps-plugin:0.0.10.RELEASE' + } +} + plugins { id 'java-library' id 'io.spring.convention.repository' @@ -6,6 +15,7 @@ plugins { id 'io.spring.convention.checkstyle' id 'io.spring.convention.tests-configuration' id 'io.spring.convention.integration-test' + id 'propdeps' } configurations { @@ -21,7 +31,7 @@ artifacts { dependencies { compile project(':spring-session-core') - compile "com.hazelcast:hazelcast:4.0.2" + optional "com.hazelcast:hazelcast:4.0.2" compile "org.springframework:spring-context" compile "javax.annotation:javax.annotation-api" diff --git a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/EnableHazelcastHttpSession.java b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/EnableHazelcastHttpSession.java index 8953e3d8..77eedf29 100644 --- a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/EnableHazelcastHttpSession.java +++ b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/EnableHazelcastHttpSession.java @@ -67,7 +67,7 @@ import org.springframework.session.web.http.SessionRepositoryFilter; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented -@Import(EnableHazelcastHttpSessionSelector.class) +@Import(HazelcastHttpSessionConfiguration.class) @Configuration(proxyBeanMethods = false) public @interface EnableHazelcastHttpSession { diff --git a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/EnableHazelcastHttpSessionSelector.java b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/EnableHazelcastHttpSessionSelector.java deleted file mode 100644 index 84e09c93..00000000 --- a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/EnableHazelcastHttpSessionSelector.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2014-2020 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 - * - * https://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 org.springframework.session.hazelcast.config.annotation.web.http; - -import java.util.ArrayList; -import java.util.List; - -import org.springframework.context.annotation.ImportSelector; -import org.springframework.core.type.AnnotationMetadata; -import org.springframework.util.ClassUtils; - -/** - * Dynamically determines which Hazelcast configuration class to include. - * - * @author Eleftheria Stein - * @since 2.4.0 - */ -class EnableHazelcastHttpSessionSelector implements ImportSelector { - - @Override - public String[] selectImports(AnnotationMetadata importingClassMetadata) { - ClassLoader classLoader = getClass().getClassLoader(); - boolean hazelcast4OnClasspath = ClassUtils.isPresent("com.hazelcast.map.IMap", classLoader); - List classNames = new ArrayList<>(1); - if (hazelcast4OnClasspath) { - classNames.add(Hazelcast4HttpSessionConfiguration.class.getName()); - } - else { - classNames.add(HazelcastHttpSessionConfiguration.class.getName()); - } - return classNames.toArray(new String[0]); - } - -} diff --git a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/Hazelcast4HttpSessionConfiguration.java b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/Hazelcast4HttpSessionConfiguration.java deleted file mode 100644 index fa211174..00000000 --- a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/Hazelcast4HttpSessionConfiguration.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2014-2020 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 - * - * https://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 org.springframework.session.hazelcast.config.annotation.web.http; - -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import com.hazelcast.core.HazelcastInstance; - -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.ImportAware; -import org.springframework.core.annotation.AnnotationAttributes; -import org.springframework.core.type.AnnotationMetadata; -import org.springframework.session.FlushMode; -import org.springframework.session.IndexResolver; -import org.springframework.session.MapSession; -import org.springframework.session.SaveMode; -import org.springframework.session.Session; -import org.springframework.session.config.SessionRepositoryCustomizer; -import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration; -import org.springframework.session.hazelcast.Hazelcast4IndexedSessionRepository; -import org.springframework.session.hazelcast.HazelcastFlushMode; -import org.springframework.session.hazelcast.config.annotation.SpringSessionHazelcastInstance; -import org.springframework.session.web.http.SessionRepositoryFilter; -import org.springframework.util.StringUtils; - -/** - * Exposes the {@link SessionRepositoryFilter} as a bean named - * {@code springSessionRepositoryFilter}. In order to use this a single - * {@link HazelcastInstance} configured for Hazelcast 4 must be exposed as a Bean. - * - * @author Eleftheria Stein - * @since 2.4.0 - * @see EnableHazelcastHttpSession - */ -@Configuration(proxyBeanMethods = false) -public class Hazelcast4HttpSessionConfiguration extends SpringHttpSessionConfiguration implements ImportAware { - - private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS; - - private String sessionMapName = Hazelcast4IndexedSessionRepository.DEFAULT_SESSION_MAP_NAME; - - private FlushMode flushMode = FlushMode.ON_SAVE; - - private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE; - - private HazelcastInstance hazelcastInstance; - - private ApplicationEventPublisher applicationEventPublisher; - - private IndexResolver indexResolver; - - private List> sessionRepositoryCustomizers; - - @Bean - public Hazelcast4IndexedSessionRepository sessionRepository() { - Hazelcast4IndexedSessionRepository sessionRepository = new Hazelcast4IndexedSessionRepository( - this.hazelcastInstance); - sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher); - if (this.indexResolver != null) { - sessionRepository.setIndexResolver(this.indexResolver); - } - if (StringUtils.hasText(this.sessionMapName)) { - sessionRepository.setSessionMapName(this.sessionMapName); - } - sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds); - sessionRepository.setFlushMode(this.flushMode); - sessionRepository.setSaveMode(this.saveMode); - this.sessionRepositoryCustomizers - .forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository)); - return sessionRepository; - } - - public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) { - this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds; - } - - public void setSessionMapName(String sessionMapName) { - this.sessionMapName = sessionMapName; - } - - @Deprecated - public void setHazelcastFlushMode(HazelcastFlushMode hazelcastFlushMode) { - setFlushMode(hazelcastFlushMode.getFlushMode()); - } - - public void setFlushMode(FlushMode flushMode) { - this.flushMode = flushMode; - } - - public void setSaveMode(SaveMode saveMode) { - this.saveMode = saveMode; - } - - @Autowired - public void setHazelcastInstance( - @SpringSessionHazelcastInstance ObjectProvider springSessionHazelcastInstance, - ObjectProvider hazelcastInstance) { - HazelcastInstance hazelcastInstanceToUse = springSessionHazelcastInstance.getIfAvailable(); - if (hazelcastInstanceToUse == null) { - hazelcastInstanceToUse = hazelcastInstance.getObject(); - } - this.hazelcastInstance = hazelcastInstanceToUse; - } - - @Autowired - public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { - this.applicationEventPublisher = applicationEventPublisher; - } - - @Autowired(required = false) - public void setIndexResolver(IndexResolver indexResolver) { - this.indexResolver = indexResolver; - } - - @Autowired(required = false) - public void setSessionRepositoryCustomizer( - ObjectProvider> sessionRepositoryCustomizers) { - this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList()); - } - - @Override - @SuppressWarnings("deprecation") - public void setImportMetadata(AnnotationMetadata importMetadata) { - Map attributeMap = importMetadata - .getAnnotationAttributes(EnableHazelcastHttpSession.class.getName()); - AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap); - this.maxInactiveIntervalInSeconds = attributes.getNumber("maxInactiveIntervalInSeconds"); - String sessionMapNameValue = attributes.getString("sessionMapName"); - if (StringUtils.hasText(sessionMapNameValue)) { - this.sessionMapName = sessionMapNameValue; - } - FlushMode flushMode = attributes.getEnum("flushMode"); - HazelcastFlushMode hazelcastFlushMode = attributes.getEnum("hazelcastFlushMode"); - if (flushMode == FlushMode.ON_SAVE && hazelcastFlushMode != HazelcastFlushMode.ON_SAVE) { - flushMode = hazelcastFlushMode.getFlushMode(); - } - this.flushMode = flushMode; - this.saveMode = attributes.getEnum("saveMode"); - } - -} diff --git a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/HazelcastHttpSessionConfiguration.java b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/HazelcastHttpSessionConfiguration.java index 51d70fb2..71092400 100644 --- a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/HazelcastHttpSessionConfiguration.java +++ b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/HazelcastHttpSessionConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 the original author or authors. + * Copyright 2014-2020 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. @@ -35,12 +35,15 @@ import org.springframework.session.IndexResolver; import org.springframework.session.MapSession; import org.springframework.session.SaveMode; import org.springframework.session.Session; +import org.springframework.session.SessionRepository; import org.springframework.session.config.SessionRepositoryCustomizer; import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration; +import org.springframework.session.hazelcast.Hazelcast4IndexedSessionRepository; import org.springframework.session.hazelcast.HazelcastFlushMode; import org.springframework.session.hazelcast.HazelcastIndexedSessionRepository; import org.springframework.session.hazelcast.config.annotation.SpringSessionHazelcastInstance; import org.springframework.session.web.http.SessionRepositoryFilter; +import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; /** @@ -72,23 +75,23 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur private List> sessionRepositoryCustomizers; + private List> hazelcast4SessionRepositoryCustomizers; + + private static final boolean hazelcast4; + + static { + ClassLoader classLoader = HazelcastHttpSessionConfiguration.class.getClassLoader(); + hazelcast4 = ClassUtils.isPresent("com.hazelcast.map.IMap", classLoader); + } + @Bean - public HazelcastIndexedSessionRepository sessionRepository() { - HazelcastIndexedSessionRepository sessionRepository = new HazelcastIndexedSessionRepository( - this.hazelcastInstance); - sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher); - if (this.indexResolver != null) { - sessionRepository.setIndexResolver(this.indexResolver); + public SessionRepository sessionRepository() { + if (hazelcast4) { + return createHazelcast4IndexedSessionRepository(); } - if (StringUtils.hasText(this.sessionMapName)) { - sessionRepository.setSessionMapName(this.sessionMapName); + else { + return createHazelcastIndexedSessionRepository(); } - sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds); - sessionRepository.setFlushMode(this.flushMode); - sessionRepository.setSaveMode(this.saveMode); - this.sessionRepositoryCustomizers - .forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository)); - return sessionRepository; } public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) { @@ -139,6 +142,13 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList()); } + @Autowired(required = false) + public void setHazelcast4SessionRepositoryCustomizer( + ObjectProvider> sessionRepositoryCustomizers) { + this.hazelcast4SessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream() + .collect(Collectors.toList()); + } + @Override @SuppressWarnings("deprecation") public void setImportMetadata(AnnotationMetadata importMetadata) { @@ -159,4 +169,40 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur this.saveMode = attributes.getEnum("saveMode"); } + private HazelcastIndexedSessionRepository createHazelcastIndexedSessionRepository() { + HazelcastIndexedSessionRepository sessionRepository = new HazelcastIndexedSessionRepository( + this.hazelcastInstance); + sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher); + if (this.indexResolver != null) { + sessionRepository.setIndexResolver(this.indexResolver); + } + if (StringUtils.hasText(this.sessionMapName)) { + sessionRepository.setSessionMapName(this.sessionMapName); + } + sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds); + sessionRepository.setFlushMode(this.flushMode); + sessionRepository.setSaveMode(this.saveMode); + this.sessionRepositoryCustomizers + .forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository)); + return sessionRepository; + } + + private Hazelcast4IndexedSessionRepository createHazelcast4IndexedSessionRepository() { + Hazelcast4IndexedSessionRepository sessionRepository = new Hazelcast4IndexedSessionRepository( + this.hazelcastInstance); + sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher); + if (this.indexResolver != null) { + sessionRepository.setIndexResolver(this.indexResolver); + } + if (StringUtils.hasText(this.sessionMapName)) { + sessionRepository.setSessionMapName(this.sessionMapName); + } + sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds); + sessionRepository.setFlushMode(this.flushMode); + sessionRepository.setSaveMode(this.saveMode); + this.hazelcast4SessionRepositoryCustomizers + .forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository)); + return sessionRepository; + } + } diff --git a/spring-session-samples/spring-session-sample-boot-hazelcast4/spring-session-sample-boot-hazelcast4.gradle b/spring-session-samples/spring-session-sample-boot-hazelcast4/spring-session-sample-boot-hazelcast4.gradle new file mode 100644 index 00000000..f7a06a2f --- /dev/null +++ b/spring-session-samples/spring-session-sample-boot-hazelcast4/spring-session-sample-boot-hazelcast4.gradle @@ -0,0 +1,20 @@ +apply plugin: 'io.spring.convention.spring-sample-boot' + +dependencies { + compile project(':spring-session-hazelcast') + compile project(':hazelcast4') + compile "org.springframework.boot:spring-boot-starter-web" + compile "org.springframework.boot:spring-boot-starter-thymeleaf" + compile "org.springframework.boot:spring-boot-starter-security" + compile "com.hazelcast:hazelcast:4.0.2" + compile "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect" + compile "org.webjars:bootstrap" + compile "org.webjars:html5shiv" + compile "org.webjars:webjars-locator-core" + + testCompile "org.springframework.boot:spring-boot-starter-test" + testCompile "org.junit.jupiter:junit-jupiter-api" + testRuntime "org.junit.jupiter:junit-jupiter-engine" + integrationTestCompile seleniumDependencies + integrationTestCompile "org.testcontainers:testcontainers" +} diff --git a/spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/java/sample/BootTests.java b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/java/sample/BootTests.java new file mode 100644 index 00000000..32212ce6 --- /dev/null +++ b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/java/sample/BootTests.java @@ -0,0 +1,95 @@ +/* + * Copyright 2014-2020 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 + * + * https://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.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.openqa.selenium.WebDriver; +import org.testcontainers.containers.GenericContainer; +import sample.pages.HomePage; +import sample.pages.LoginPage; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDriverBuilder; + +@ExtendWith(SpringExtension.class) +@AutoConfigureMockMvc +@SpringBootTest(webEnvironment = WebEnvironment.MOCK) +class BootTests { + + private static final String DOCKER_IMAGE = "hazelcast/hazelcast:latest"; + + @Autowired + private MockMvc mockMvc; + + private WebDriver driver; + + @BeforeEach + void setup() { + this.driver = MockMvcHtmlUnitDriverBuilder.mockMvcSetup(this.mockMvc).build(); + } + + @AfterEach + void tearDown() { + this.driver.quit(); + } + + @Test + void home() { + LoginPage login = HomePage.go(this.driver); + login.assertAt(); + } + + @Test + void login() { + LoginPage login = HomePage.go(this.driver); + HomePage home = login.form().login(HomePage.class); + home.assertAt(); + home.containCookie("SESSION"); + home.doesNotContainCookie("JSESSIONID"); + } + + @Test + void logout() { + LoginPage login = HomePage.go(this.driver); + HomePage home = login.form().login(HomePage.class); + home.logout(); + login.assertAt(); + } + + @TestConfiguration + static class Config { + + @Bean + GenericContainer hazelcastContainer() { + GenericContainer hazelcastContainer = new GenericContainer(DOCKER_IMAGE).withExposedPorts(5701); + hazelcastContainer.start(); + return hazelcastContainer; + } + + } + +} diff --git a/spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/java/sample/pages/BasePage.java b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/java/sample/pages/BasePage.java new file mode 100644 index 00000000..4fe74ffa --- /dev/null +++ b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/java/sample/pages/BasePage.java @@ -0,0 +1,38 @@ +/* + * Copyright 2014-2020 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 + * + * https://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.pages; + +import org.openqa.selenium.WebDriver; + +public class BasePage { + + private WebDriver driver; + + public BasePage(WebDriver driver) { + this.driver = driver; + } + + public WebDriver getDriver() { + return this.driver; + } + + public static void get(WebDriver driver, String get) { + String baseUrl = "http://localhost"; + driver.get(baseUrl + get); + } + +} diff --git a/spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/java/sample/pages/HomePage.java b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/java/sample/pages/HomePage.java new file mode 100644 index 00000000..3c520880 --- /dev/null +++ b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/java/sample/pages/HomePage.java @@ -0,0 +1,60 @@ +/* + * Copyright 2014-2020 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 + * + * https://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.pages; + +import java.util.Set; + +import org.openqa.selenium.By; +import org.openqa.selenium.Cookie; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.PageFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +public class HomePage extends BasePage { + + public HomePage(WebDriver driver) { + super(driver); + } + + public static LoginPage go(WebDriver driver) { + get(driver, "/"); + return PageFactory.initElements(driver, LoginPage.class); + } + + public void assertAt() { + assertThat(getDriver().getTitle()).isEqualTo("Spring Session Sample - Secured Content"); + } + + public void containCookie(String cookieName) { + Set cookies = getDriver().manage().getCookies(); + assertThat(cookies).extracting("name").contains(cookieName); + } + + public void doesNotContainCookie(String cookieName) { + Set cookies = getDriver().manage().getCookies(); + assertThat(cookies).extracting("name").doesNotContain(cookieName); + } + + public HomePage logout() { + WebElement logout = getDriver().findElement(By.cssSelector("input[type=\"submit\"]")); + logout.click(); + return PageFactory.initElements(getDriver(), HomePage.class); + } + +} diff --git a/spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/java/sample/pages/LoginPage.java b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/java/sample/pages/LoginPage.java new file mode 100644 index 00000000..90f99a79 --- /dev/null +++ b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/java/sample/pages/LoginPage.java @@ -0,0 +1,66 @@ +/* + * Copyright 2014-2020 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 + * + * https://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.pages; + +import org.openqa.selenium.SearchContext; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.PageFactory; +import org.openqa.selenium.support.pagefactory.DefaultElementLocatorFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +public class LoginPage extends BasePage { + + public LoginPage(WebDriver driver) { + super(driver); + } + + public void assertAt() { + assertThat(getDriver().getTitle()).isEqualTo("Please sign in"); + } + + public Form form() { + return new Form(getDriver()); + } + + public class Form { + + @FindBy(name = "username") + private WebElement username; + + @FindBy(name = "password") + private WebElement password; + + @FindBy(tagName = "button") + private WebElement button; + + public Form(SearchContext context) { + PageFactory.initElements(new DefaultElementLocatorFactory(context), this); + } + + public T login(Class page) { + this.username.sendKeys("user"); + this.password.sendKeys("password"); + this.button.click(); + return PageFactory.initElements(getDriver(), page); + } + + } + +} diff --git a/spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/resources/testcontainers.properties b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/resources/testcontainers.properties new file mode 100644 index 00000000..e3e83419 --- /dev/null +++ b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/integration-test/resources/testcontainers.properties @@ -0,0 +1 @@ +ryuk.container.timeout=120 diff --git a/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/java/sample/Application.java b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/java/sample/Application.java new file mode 100644 index 00000000..e4ab62fa --- /dev/null +++ b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/java/sample/Application.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014-2020 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 + * + * https://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.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; + +@EnableCaching +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/java/sample/config/IndexController.java b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/java/sample/config/IndexController.java new file mode 100644 index 00000000..2da7bead --- /dev/null +++ b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/java/sample/config/IndexController.java @@ -0,0 +1,30 @@ +/* + * Copyright 2014-2020 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 + * + * https://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.config; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +public class IndexController { + + @RequestMapping("/") + public String index() { + return "index"; + } + +} diff --git a/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/java/sample/config/SecurityConfig.java b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/java/sample/config/SecurityConfig.java new file mode 100644 index 00000000..60a3cd4b --- /dev/null +++ b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/java/sample/config/SecurityConfig.java @@ -0,0 +1,43 @@ +/* + * Copyright 2014-2020 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 + * + * https://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.config; + +import org.springframework.boot.autoconfigure.security.servlet.PathRequest; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + // @formatter:off + // tag::config[] + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests((authorize) -> authorize + .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll() + .anyRequest().authenticated() + ) + .formLogin((formLogin) -> formLogin + .permitAll() + ); + } + // end::config[] + // @formatter:on + +} diff --git a/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/java/sample/config/SessionConfig.java b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/java/sample/config/SessionConfig.java new file mode 100644 index 00000000..06d95187 --- /dev/null +++ b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/java/sample/config/SessionConfig.java @@ -0,0 +1,57 @@ +/* + * Copyright 2014-2020 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 + * + * https://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.config; + +import com.hazelcast.config.AttributeConfig; +import com.hazelcast.config.Config; +import com.hazelcast.config.IndexConfig; +import com.hazelcast.config.IndexType; +import com.hazelcast.config.NetworkConfig; +import com.hazelcast.config.SerializerConfig; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.session.MapSession; +import org.springframework.session.hazelcast.Hazelcast4IndexedSessionRepository; +import org.springframework.session.hazelcast.Hazelcast4PrincipalNameExtractor; +import org.springframework.session.hazelcast.HazelcastSessionSerializer; + +// tag::class[] +@Configuration +public class SessionConfig { + + @Bean + public Config clientConfig() { + Config config = new Config(); + NetworkConfig networkConfig = config.getNetworkConfig(); + networkConfig.setPort(0); + networkConfig.getJoin().getMulticastConfig().setEnabled(false); + AttributeConfig attributeConfig = new AttributeConfig() + .setName(Hazelcast4IndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE) + .setExtractorClassName(Hazelcast4PrincipalNameExtractor.class.getName()); + config.getMapConfig(Hazelcast4IndexedSessionRepository.DEFAULT_SESSION_MAP_NAME) + .addAttributeConfig(attributeConfig).addIndexConfig( + new IndexConfig(IndexType.HASH, Hazelcast4IndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)); + SerializerConfig serializerConfig = new SerializerConfig(); + serializerConfig.setImplementation(new HazelcastSessionSerializer()).setTypeClass(MapSession.class); + config.getSerializationConfig().addSerializerConfig(serializerConfig); + return config; + + } + +} +// end::class[] diff --git a/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/resources/application.properties b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/resources/application.properties new file mode 100644 index 00000000..1b5271b5 --- /dev/null +++ b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.security.user.password=password diff --git a/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/resources/static/favicon.ico b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/resources/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..bfb9974019d4b8b978cf34ea67f0c6804f9b76c6 GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x#lFaYI*xFHzK2NLHVUHb1iG2*{Y zgB?lOds5VYkY4wx3IDZQ-O0jkQ{(?@bp=p{C&v8O!h&CXe)}H=o_u_bRSig==8O!i zc=x^YFzx?WUq6CX4M?xX{1U9V`}7vL{^Em6v8n;-RbSqS702#f2-g1(NLsD$!KwzN zS8ZJ%R&20o^8Y{o{{6r8@eNjaxV~+({;Q#3&F!=Q_uf49|L5O7|6hFn@<03RE(|p= zJ*s<_{YS^WcP_&H4q|}(5Orn`hB}ZwmBSk_@QDwvQS^g2lONu|PzTm~V&{JqG(7$3 zJq-P6H_u_H1L;#fa{vQto;~z`$*ZUT-~ayo|MAc7|C=7&#!v&-2UM#}0rtFhmTdiw fS5E!E^7Zq7x2tDJHpBVK>Hp`xeEbh92gs!XBi)`m literal 0 HcmV?d00001 diff --git a/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/resources/static/images/logo.png b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/resources/static/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..393230883fb4e04829339f7c21b6ab6de7393891 GIT binary patch literal 1123 zcmV-p1f2VcP)P000yS1^@s74{)X{0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$1xZ9fRCwBAJakr>0R&#%?*!8<7oReN z=>H6CRSb*_9RHalf3;=rzRt=3!VW|AET)fs_IS z5aXsJDqv;5UUXCgspCNG%qCu4_4m8Q8J4{_Ss=DB)G%-}OK$)A_qiv-M@As~;az|L zf?EIsP`7cheE7}?GEN=HaRK6^jLh%PvWj*xv3z@3xac)oRV-9P3=lIhyklZuIsRdC zEQk#P>`Ve}KmUCIvzcu9Tf*+YVP*hfGu{$8D7y_HfEf3kMztIrynEOQW2*Ct&A;*G zO}I7VlHICc5w;GSdsSFH1nBbCHtgWCZ;JNm@ z`XjO?7RGP?S?;`M26}|$>d$}7+Q^c380tVF%ErLJjAqwYZkA^sS%0xHF}~wxlbiD5 z`(qoR=no*41>&DbDvtqiaJE+C3lIk&fG}bK9l%ly6jZSM`Nu#MU}XG#RzRlJh~>*q zMz|m_S~GwYKiDe_40nN83W%$i`8HWIa~)&&`WqMkpMdet@v+_G7x%2~|M)9$Tk#!Y zFw1)&gOROi`~TlS|Ne#uGBU8e{?G6W7-39r7#RMspu`I>^-8__`jg@PS4KGI{tDI)dzZ%dlS>M59FF!%vUTz)~b4X4$^~`TvW7 ziIMs5zyE)kk;ES}`AIdM1t$C=P~wMS3!!9Ap!k0ap%i9haWBalIY!4O?f(oAd%r+4 zo)k2;d7%b_#Jhnp-2~zQ1Q@}XCIrw@0t|34GX2=V#dp;1>j!5(mhZn98K?y0fw-87 z<#h&!;7V3zwkKdg)?fU+ET4a(DSgvn03;_eFu%5AV7&(n_piW02N=Hpff?mLFf{&g z0%=ZYR2CvOD9l@{`Ma!o3LDu1eZb}An>(a<~Q?=8{fF=xc@W!MoZ)b z{O|{gu_$Ev|5TXe{Z}Tay7jj||2@OO@D(hD)hsRsjrwm4H=1C4P6p+J-x==O;Vpp} zelf7T{l>^3$L4GI;MZmjLQek2%+jdD-YkChTV1vRXZ6WjKj)c!`-fi-2P5B87D!gv za)A+O=YLR%j|aSGxW@31vGu^;|BMVrzfaux>)%&1LJk9(o(#_DK=28Oy@5ho@oC^@ z5m@o{&xh3zgTK{rGV>dM|NF@cm+`=a;1wp + + Secured Content + + +
+

Secured Page

+

This page is secured using Spring Boot, Spring Session, and Spring Security.

+
+ + diff --git a/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/resources/templates/layout.html b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/resources/templates/layout.html new file mode 100644 index 00000000..a5732ba8 --- /dev/null +++ b/spring-session-samples/spring-session-sample-boot-hazelcast4/src/main/resources/templates/layout.html @@ -0,0 +1,122 @@ + + + + Spring Session Sample + + + + + + + + + + + +
+ + +
+
+ Some Success message +
+
+ Fake content +
+
+ +
+
+ + + + diff --git a/spring-session-samples/spring-session-sample-javaconfig-hazelcast/spring-session-sample-javaconfig-hazelcast.gradle b/spring-session-samples/spring-session-sample-javaconfig-hazelcast/spring-session-sample-javaconfig-hazelcast.gradle index 6769dfe5..9255de93 100644 --- a/spring-session-samples/spring-session-sample-javaconfig-hazelcast/spring-session-sample-javaconfig-hazelcast.gradle +++ b/spring-session-samples/spring-session-sample-javaconfig-hazelcast/spring-session-sample-javaconfig-hazelcast.gradle @@ -2,6 +2,7 @@ apply plugin: 'io.spring.convention.spring-sample-war' dependencies { compile project(':spring-session-hazelcast') + compile project(':hazelcast4') compile "org.springframework:spring-web" compile "org.springframework.security:spring-security-config" compile "org.springframework.security:spring-security-web"