From b2dfa69b3d7e6fb33dd0e1555a76764e46e048ef Mon Sep 17 00:00:00 2001 From: Greg Turnquist Date: Tue, 31 Jul 2018 19:19:02 -0500 Subject: [PATCH] Utilize container's classloader Resolves #15 --- .../http/MongoHttpSessionConfiguration.java | 21 +++++- .../ReactiveMongoWebSessionConfiguration.java | 21 +++++- .../integration/AbstractClassLoaderTest.java | 70 +++++++++++++++++++ .../MongoRepositoryJdkSerializationITest.java | 1 - .../ReactiveConfigurationTest.java | 60 ++++++++++++++++ .../TraditionalConfigurationTest.java | 34 +++++++++ 6 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/springframework/session/data/mongo/integration/AbstractClassLoaderTest.java create mode 100644 src/test/java/org/springframework/session/data/mongo/integration/ReactiveConfigurationTest.java create mode 100644 src/test/java/org/springframework/session/data/mongo/integration/TraditionalConfigurationTest.java diff --git a/src/main/java/org/springframework/session/data/mongo/config/annotation/web/http/MongoHttpSessionConfiguration.java b/src/main/java/org/springframework/session/data/mongo/config/annotation/web/http/MongoHttpSessionConfiguration.java index 5d4aaa7..c8df913 100644 --- a/src/main/java/org/springframework/session/data/mongo/config/annotation/web/http/MongoHttpSessionConfiguration.java +++ b/src/main/java/org/springframework/session/data/mongo/config/annotation/web/http/MongoHttpSessionConfiguration.java @@ -15,16 +15,22 @@ */ package org.springframework.session.data.mongo.config.annotation.web.http; +import java.time.Duration; + +import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.EmbeddedValueResolverAware; 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.serializer.support.DeserializingConverter; +import org.springframework.core.serializer.support.SerializingConverter; import org.springframework.core.type.AnnotationMetadata; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration; import org.springframework.session.data.mongo.AbstractMongoSessionConverter; +import org.springframework.session.data.mongo.JdkMongoSessionConverter; import org.springframework.session.data.mongo.MongoOperationsSessionRepository; import org.springframework.util.StringUtils; import org.springframework.util.StringValueResolver; @@ -39,12 +45,13 @@ import org.springframework.util.StringValueResolver; */ @Configuration public class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguration - implements EmbeddedValueResolverAware, ImportAware { + implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware { private AbstractMongoSessionConverter mongoSessionConverter; private Integer maxInactiveIntervalInSeconds; private String collectionName; private StringValueResolver embeddedValueResolver; + private ClassLoader classLoader; @Bean public MongoOperationsSessionRepository mongoSessionRepository(MongoOperations mongoOperations) { @@ -54,6 +61,12 @@ public class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguratio if (this.mongoSessionConverter != null) { repository.setMongoSessionConverter(this.mongoSessionConverter); + } else { + JdkMongoSessionConverter mongoSessionConverter = new JdkMongoSessionConverter( + new SerializingConverter(), + new DeserializingConverter(this.classLoader), + Duration.ofSeconds(MongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL)); + repository.setMongoSessionConverter(mongoSessionConverter); } if (StringUtils.hasText(this.collectionName)) { @@ -89,6 +102,12 @@ public class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguratio this.mongoSessionConverter = mongoSessionConverter; } + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { this.embeddedValueResolver = resolver; } diff --git a/src/main/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfiguration.java b/src/main/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfiguration.java index 06abd3c..40274c7 100644 --- a/src/main/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfiguration.java +++ b/src/main/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfiguration.java @@ -15,17 +15,24 @@ */ package org.springframework.session.data.mongo.config.annotation.web.reactive; +import java.time.Duration; + +import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.EmbeddedValueResolverAware; 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.serializer.support.DeserializingConverter; +import org.springframework.core.serializer.support.SerializingConverter; import org.springframework.core.type.AnnotationMetadata; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.session.config.annotation.web.server.SpringWebSessionConfiguration; import org.springframework.session.data.mongo.AbstractMongoSessionConverter; +import org.springframework.session.data.mongo.JdkMongoSessionConverter; +import org.springframework.session.data.mongo.MongoOperationsSessionRepository; import org.springframework.session.data.mongo.ReactiveMongoOperationsSessionRepository; import org.springframework.util.StringUtils; import org.springframework.util.StringValueResolver; @@ -38,7 +45,7 @@ import org.springframework.util.StringValueResolver; */ @Configuration public class ReactiveMongoWebSessionConfiguration extends SpringWebSessionConfiguration - implements EmbeddedValueResolverAware, ImportAware { + implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware { private AbstractMongoSessionConverter mongoSessionConverter; private Integer maxInactiveIntervalInSeconds; @@ -47,6 +54,7 @@ public class ReactiveMongoWebSessionConfiguration extends SpringWebSessionConfig @Autowired(required = false) private MongoOperations mongoOperations; + private ClassLoader classLoader; @Bean public ReactiveMongoOperationsSessionRepository reactiveMongoOperationsSessionRepository(ReactiveMongoOperations operations) { @@ -55,6 +63,12 @@ public class ReactiveMongoWebSessionConfiguration extends SpringWebSessionConfig if (this.mongoSessionConverter != null) { repository.setMongoSessionConverter(this.mongoSessionConverter); + } else { + JdkMongoSessionConverter mongoSessionConverter = new JdkMongoSessionConverter( + new SerializingConverter(), + new DeserializingConverter(this.classLoader), + Duration.ofSeconds(ReactiveMongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL)); + repository.setMongoSessionConverter(mongoSessionConverter); } if (this.maxInactiveIntervalInSeconds != null) { @@ -92,6 +106,11 @@ public class ReactiveMongoWebSessionConfiguration extends SpringWebSessionConfig } + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + @Override public void setEmbeddedValueResolver(StringValueResolver embeddedValueResolver) { this.embeddedValueResolver = embeddedValueResolver; diff --git a/src/test/java/org/springframework/session/data/mongo/integration/AbstractClassLoaderTest.java b/src/test/java/org/springframework/session/data/mongo/integration/AbstractClassLoaderTest.java new file mode 100644 index 0000000..77a7f1b --- /dev/null +++ b/src/test/java/org/springframework/session/data/mongo/integration/AbstractClassLoaderTest.java @@ -0,0 +1,70 @@ +/* + * 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 org.springframework.session.data.mongo.integration; + +import static org.assertj.core.api.AssertionsForClassTypes.*; + +import java.lang.reflect.Field; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.core.serializer.DefaultDeserializer; +import org.springframework.core.serializer.support.DeserializingConverter; +import org.springframework.session.data.mongo.AbstractMongoSessionConverter; +import org.springframework.session.data.mongo.JdkMongoSessionConverter; +import org.springframework.util.ReflectionUtils; + +/** + * Verify container's {@link ClassLoader} is injected into session converter (reactive and traditional). + * + * @author Greg Turnquist + */ +public abstract class AbstractClassLoaderTest extends AbstractITest { + + @Autowired + T sessionRepository; + + @Autowired + ApplicationContext applicationContext; + + @Test + public void verifyContainerClassLoaderLoadedIntoConverter() { + + Field mongoSessionConverterField = ReflectionUtils.findField(sessionRepository.getClass(), "mongoSessionConverter"); + ReflectionUtils.makeAccessible(mongoSessionConverterField); + AbstractMongoSessionConverter sessionConverter = (AbstractMongoSessionConverter) ReflectionUtils.getField(mongoSessionConverterField, this.sessionRepository); + + assertThat(sessionConverter).isInstanceOf(JdkMongoSessionConverter.class); + + JdkMongoSessionConverter jdkMongoSessionConverter = (JdkMongoSessionConverter) sessionConverter; + + Field converterField = ReflectionUtils.findField(JdkMongoSessionConverter.class, "deserializer"); + ReflectionUtils.makeAccessible(converterField); + DeserializingConverter deserializingConverter = (DeserializingConverter) ReflectionUtils.getField(converterField, jdkMongoSessionConverter); + + Field deserializerField = ReflectionUtils.findField(DeserializingConverter.class, "deserializer"); + ReflectionUtils.makeAccessible(deserializerField); + DefaultDeserializer deserializer = (DefaultDeserializer) ReflectionUtils.getField(deserializerField, deserializingConverter); + + Field classLoaderField = ReflectionUtils.findField(DefaultDeserializer.class, "classLoader"); + ReflectionUtils.makeAccessible(classLoaderField); + ClassLoader classLoader = (ClassLoader) ReflectionUtils.getField(classLoaderField, deserializer); + + assertThat(classLoader).isEqualTo(applicationContext.getClassLoader()); + } + +} diff --git a/src/test/java/org/springframework/session/data/mongo/integration/MongoRepositoryJdkSerializationITest.java b/src/test/java/org/springframework/session/data/mongo/integration/MongoRepositoryJdkSerializationITest.java index b361667..3460eab 100644 --- a/src/test/java/org/springframework/session/data/mongo/integration/MongoRepositoryJdkSerializationITest.java +++ b/src/test/java/org/springframework/session/data/mongo/integration/MongoRepositoryJdkSerializationITest.java @@ -21,7 +21,6 @@ import java.time.Duration; import java.util.Map; import org.junit.Test; - import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.session.data.mongo.AbstractMongoSessionConverter; diff --git a/src/test/java/org/springframework/session/data/mongo/integration/ReactiveConfigurationTest.java b/src/test/java/org/springframework/session/data/mongo/integration/ReactiveConfigurationTest.java new file mode 100644 index 0000000..3f2f18b --- /dev/null +++ b/src/test/java/org/springframework/session/data/mongo/integration/ReactiveConfigurationTest.java @@ -0,0 +1,60 @@ +/* + * 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 org.springframework.session.data.mongo.integration; + +import java.io.IOException; + +import de.flapdoodle.embed.mongo.MongodExecutable; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; +import org.springframework.data.mongodb.core.ReactiveMongoOperations; +import org.springframework.data.mongodb.core.ReactiveMongoTemplate; +import org.springframework.session.data.mongo.ReactiveMongoOperationsSessionRepository; +import org.springframework.session.data.mongo.config.annotation.web.reactive.EnableMongoWebSession; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.util.SocketUtils; + +import com.mongodb.reactivestreams.client.MongoClient; +import com.mongodb.reactivestreams.client.MongoClients; + +/** + * @author Greg Turnquist + */ +@ContextConfiguration +public class ReactiveConfigurationTest extends AbstractClassLoaderTest { + + @Configuration + @EnableMongoWebSession + static class Config { + + private int embeddedMongoPort = SocketUtils.findAvailableTcpPort(); + + @Bean(initMethod = "start", destroyMethod = "stop") + public MongodExecutable embeddedMongoServer() throws IOException { + return MongoITestUtils.embeddedMongoServer(this.embeddedMongoPort); + } + + @Bean + @DependsOn("embeddedMongoServer") + public ReactiveMongoOperations mongoOperations() { + + MongoClient mongo = MongoClients.create("mongodb://localhost:" + this.embeddedMongoPort); + return new ReactiveMongoTemplate(mongo, "test"); + } + + } +} diff --git a/src/test/java/org/springframework/session/data/mongo/integration/TraditionalConfigurationTest.java b/src/test/java/org/springframework/session/data/mongo/integration/TraditionalConfigurationTest.java new file mode 100644 index 0000000..f7dab9a --- /dev/null +++ b/src/test/java/org/springframework/session/data/mongo/integration/TraditionalConfigurationTest.java @@ -0,0 +1,34 @@ +/* + * 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 org.springframework.session.data.mongo.integration; + +import org.springframework.context.annotation.Configuration; +import org.springframework.session.data.mongo.MongoOperationsSessionRepository; +import org.springframework.session.data.mongo.config.annotation.web.http.EnableMongoHttpSession; +import org.springframework.session.data.mongo.integration.AbstractMongoRepositoryITest.BaseConfig; +import org.springframework.test.context.ContextConfiguration; + +/** + * @author Greg Turnquist + */ +@ContextConfiguration +public class TraditionalConfigurationTest extends AbstractClassLoaderTest { + + @Configuration + @EnableMongoHttpSession + static class Config extends BaseConfig { + } +}