diff --git a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/ClientServerHttpSessionAttributesDeltaIntegrationTests.java b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/ClientServerHttpSessionAttributesDeltaIntegrationTests.java index bad5608..bacaa99 100644 --- a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/ClientServerHttpSessionAttributesDeltaIntegrationTests.java +++ b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/ClientServerHttpSessionAttributesDeltaIntegrationTests.java @@ -25,16 +25,12 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Date; -import java.util.Properties; -import java.util.concurrent.TimeUnit; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; -import org.apache.geode.cache.Cache; -import org.apache.geode.cache.GemFireCache; import org.apache.geode.cache.client.ClientCache; import org.springframework.beans.factory.annotation.Value; @@ -42,13 +38,14 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; -import org.springframework.data.gemfire.CacheFactoryBean; -import org.springframework.data.gemfire.client.ClientCacheFactoryBean; -import org.springframework.data.gemfire.client.PoolFactoryBean; -import org.springframework.data.gemfire.server.CacheServerFactoryBean; +import org.springframework.data.gemfire.config.annotation.CacheServerApplication; +import org.springframework.data.gemfire.config.annotation.CacheServerConfigurer; +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.Session; import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession; +import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.FileSystemUtils; @@ -169,7 +166,9 @@ public class ClientServerHttpSessionAttributesDeltaIntegrationTests extends Abst assertThat(reloadedSession.getAttribute("attrOne")).isEqualTo(1); } - @EnableGemFireHttpSession + @ClientCacheApplication + @EnableGemFireHttpSession(poolName = "DEFAULT", sessionSerializerBeanName = + GemFireHttpSessionConfiguration.SESSION_DATA_SERIALIZER_BEAN_NAME) @SuppressWarnings("unused") static class SpringSessionDataGemFireClientConfiguration { @@ -178,42 +177,16 @@ public class ClientServerHttpSessionAttributesDeltaIntegrationTests extends Abst return new PropertySourcesPlaceholderConfigurer(); } - Properties gemfireProperties() { + @Bean ClientCacheConfigurer clientCachePoolPortConfigurer( + @Value("${spring.session.data.gemfire.port:" + DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) { - Properties gemfireProperties = new Properties(); + return (beanName, clientCacheFactoryBean) -> { - gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL); + clientCacheFactoryBean.setServers(Collections.singleton( + new ConnectionEndpoint(SpringSessionDataGemFireServerConfiguration.SERVER_HOSTNAME, port))); - return gemfireProperties; - } - - @Bean - ClientCacheFactoryBean gemfireCache() { - - ClientCacheFactoryBean clientCacheFactory = new ClientCacheFactoryBean(); - - clientCacheFactory.setClose(true); - clientCacheFactory.setProperties(gemfireProperties()); - - return clientCacheFactory; - } - - @Bean - PoolFactoryBean gemfirePool(@Value("${spring.session.data.gemfire.port:" - + DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) { - - PoolFactoryBean poolFactory = new PoolFactoryBean(); - - poolFactory.setKeepAlive(false); - poolFactory.setPingInterval(TimeUnit.SECONDS.toMillis(5)); - poolFactory.setReadTimeout(2000); // 2 seconds - poolFactory.setRetryAttempts(1); - poolFactory.setSubscriptionEnabled(true); - - poolFactory.setServers(Collections.singletonList(new ConnectionEndpoint( - SpringSessionDataGemFireServerConfiguration.SERVER_HOSTNAME, port))); - - return poolFactory; + clientCacheFactoryBean.setSubscriptionEnabled(true); + }; } // used for debugging purposes @@ -234,7 +207,9 @@ public class ClientServerHttpSessionAttributesDeltaIntegrationTests extends Abst } } - @EnableGemFireHttpSession(maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS) + @CacheServerApplication(name = "ClientServerHttpSessionAttributesDeltaIntegrationTests") + @EnableGemFireHttpSession(maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS, + sessionSerializerBeanName = GemFireHttpSessionConfiguration.SESSION_DATA_SERIALIZER_BEAN_NAME) @SuppressWarnings("unused") static class SpringSessionDataGemFireServerConfiguration { @@ -245,53 +220,24 @@ public class ClientServerHttpSessionAttributesDeltaIntegrationTests extends Abst return new PropertySourcesPlaceholderConfigurer(); } - Properties gemfireProperties() { - - Properties gemfireProperties = new Properties(); - - gemfireProperties.setProperty("name", name()); - gemfireProperties.setProperty("mcast-port", "0"); - gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL); - - return gemfireProperties; - } - - String name() { - return ClientServerHttpSessionAttributesDeltaIntegrationTests.class.getName(); - } - @Bean - CacheFactoryBean gemfireCache() { - - CacheFactoryBean gemfireCache = new CacheFactoryBean(); - - gemfireCache.setClose(true); - gemfireCache.setProperties(gemfireProperties()); - - return gemfireCache; - } - - @Bean - CacheServerFactoryBean gemfireCacheServer(GemFireCache gemfireCache, + CacheServerConfigurer cacheServerPortConfigurer( @Value("${spring.session.data.gemfire.port:" + DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) { - CacheServerFactoryBean cacheServerFactory = new CacheServerFactoryBean(); - - cacheServerFactory.setCache((Cache) gemfireCache); - cacheServerFactory.setAutoStartup(true); - cacheServerFactory.setBindAddress(SERVER_HOSTNAME); - cacheServerFactory.setPort(port); - - return cacheServerFactory; + return (beanName, cacheServerFactoryBean) -> { + cacheServerFactoryBean.setAutoStartup(true); + cacheServerFactoryBean.setBindAddress(SERVER_HOSTNAME); + cacheServerFactoryBean.setPort(port); + }; } @SuppressWarnings("resource") public static void main(String[] args) throws IOException { - AnnotationConfigApplicationContext context = + AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringSessionDataGemFireServerConfiguration.class); - context.registerShutdownHook(); + applicationContext.registerShutdownHook(); writeProcessControlFile(WORKING_DIRECTORY); } diff --git a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/ClientServerProxyRegionSessionOperationsIntegrationTests.java b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/ClientServerProxyRegionSessionOperationsIntegrationTests.java index fa9a3dc..a62e82e 100644 --- a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/ClientServerProxyRegionSessionOperationsIntegrationTests.java +++ b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/ClientServerProxyRegionSessionOperationsIntegrationTests.java @@ -141,7 +141,8 @@ public class ClientServerProxyRegionSessionOperationsIntegrationTests extends Ab assertThat(loadedSession).isNotNull(); assertThat(loadedSession.getId()).isEqualTo(session.getId()); assertThat(loadedSession.getCreationTime()).isEqualTo(session.getCreationTime()); - assertThat(loadedSession.getLastAccessedTime().compareTo(session.getLastAccessedTime())).isGreaterThanOrEqualTo(0); + assertThat(loadedSession.getLastAccessedTime().compareTo(session.getLastAccessedTime())) + .isGreaterThanOrEqualTo(0); sessionEvent = this.sessionEventListener.waitForSessionEvent(500); diff --git a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/serialization/data/support/DataSerializerSessionSerializerAdapterIntegrationTests.java b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/serialization/data/support/DataSerializerSessionSerializerAdapterIntegrationTests.java new file mode 100644 index 0000000..7dc3402 --- /dev/null +++ b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/serialization/data/support/DataSerializerSessionSerializerAdapterIntegrationTests.java @@ -0,0 +1,105 @@ +/* + * 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 org.springframework.session.data.gemfire.serialization.data.support; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +import java.io.DataInput; +import java.io.DataOutput; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.gemfire.support.GemfireBeanFactoryLocator; +import org.springframework.session.Session; +import org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.DeltaCapableGemFireSession; +import org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.DeltaCapableGemFireSessionAttributes; +import org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSession; +import org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSessionAttributes; +import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration; +import org.springframework.session.data.gemfire.serialization.SessionSerializer; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Integration tests for {@link DataSerializerSessionSerializerAdapter}. + * + * @author John Blum + * @see java.io.DataInput + * @see java.io.DataOutput + * @see org.junit.Test + * @see org.mockito.Mockito + * @see org.springframework.context.annotation.Bean + * @see org.springframework.context.annotation.Configuration + * @see org.springframework.data.gemfire.support.GemfireBeanFactoryLocator + * @see org.springframework.session.Session + * @see org.springframework.session.data.gemfire.serialization.SessionSerializer + * @see org.springframework.session.data.gemfire.serialization.data.support.DataSerializerSessionSerializerAdapter + * @see org.springframework.test.context.ContextConfiguration + * @see org.springframework.test.context.junit4.SpringRunner + * @since 2.0.0 + */ +@RunWith(SpringRunner.class) +@ContextConfiguration +public class DataSerializerSessionSerializerAdapterIntegrationTests { + + @Autowired + private DataSerializerSessionSerializerAdapter dataSerializer; + + @Autowired + @Qualifier(GemFireHttpSessionConfiguration.SESSION_SERIALIZER_BEAN_ALIAS) + private SessionSerializer sessionSerializer; + + @Test + public void constructsAndAutowiresDataSerializerSessionSerializerAdapter() { + + assertThat(this.dataSerializer).isNotNull(); + assertThat(this.sessionSerializer).isNotNull(); + assertThat(this.dataSerializer.getId()).isEqualTo(0xBAC2BAC); + assertThat(this.dataSerializer.getSupportedClasses()) + .containsExactly(GemFireSession.class, GemFireSessionAttributes.class, DeltaCapableGemFireSession.class, + DeltaCapableGemFireSessionAttributes.class); + } + + @Configuration + @SuppressWarnings("unused") + static class TestConfiguration { + + @Bean + GemfireBeanFactoryLocator beanFactoryLocator(BeanFactory beanFactory) { + return GemfireBeanFactoryLocator.newBeanFactoryLocator(beanFactory, "sessionBeanFactory"); + } + + @Bean + DataSerializerSessionSerializerAdapter dataSerializer() { + return new DataSerializerSessionSerializerAdapter(); + } + + @Bean + @Qualifier(GemFireHttpSessionConfiguration.SESSION_SERIALIZER_BEAN_ALIAS) + @SuppressWarnings("unchecked") + SessionSerializer mockSessionSerializer() { + return mock(SessionSerializer.class, "SessionSerializer"); + } + } +} diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/config/annotation/web/http/GemFireHttpSessionConfiguration.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/config/annotation/web/http/GemFireHttpSessionConfiguration.java index 46e4041..8303f95 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/config/annotation/web/http/GemFireHttpSessionConfiguration.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/config/annotation/web/http/GemFireHttpSessionConfiguration.java @@ -39,18 +39,16 @@ import org.apache.geode.pdx.PdxSerializer; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Condition; -import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportAware; import org.springframework.core.annotation.AnnotationAttributes; -import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotationMetadata; import org.springframework.data.gemfire.CacheFactoryBean; import org.springframework.data.gemfire.GemfireOperations; @@ -58,8 +56,6 @@ import org.springframework.data.gemfire.GemfireTemplate; import org.springframework.data.gemfire.IndexFactoryBean; import org.springframework.data.gemfire.IndexType; import org.springframework.data.gemfire.RegionAttributesFactoryBean; -import org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer; -import org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer; import org.springframework.data.gemfire.config.xml.GemfireConstants; import org.springframework.session.Session; import org.springframework.session.SessionRepository; @@ -153,14 +149,9 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat */ public static final String SESSION_DATA_SERIALIZER_BEAN_NAME = "SessionDataSerializer"; public static final String SESSION_PDX_SERIALIZER_BEAN_NAME = "SessionPdxSerializer"; + public static final String SESSION_SERIALIZER_BEAN_ALIAS = "SessionSerializerRegisteredBeanAlias"; - public static final String SESSION_SERIALIZER_QUALIFIER_PROPERTY_NAME = - "spring.session.data.geode.serializer.qualifier"; - - public static final String SESSION_SERIALIZER_REGISTERED_ALIAS = - "org.springframework.session.data.geode.serializer.registeredAlias"; - - public static final String DEFAULT_SESSION_SERIALIZER_BEAN_NAME = SESSION_DATA_SERIALIZER_BEAN_NAME; + public static final String DEFAULT_SESSION_SERIALIZER_BEAN_NAME = SESSION_PDX_SERIALIZER_BEAN_NAME; /** * Defaults names of all {@link Session} attributes that will be indexed by Apache Geode. @@ -185,21 +176,34 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat private String[] indexableSessionAttributes = DEFAULT_INDEXABLE_SESSION_ATTRIBUTES; + /** + * Sets a reference the Spring {@link ApplicationContext}. + * + * @param applicationContext reference to the Spring {@link ApplicationContext}. + * @throws BeansException if the reference cannot be stored. + * @see org.springframework.context.ApplicationContext + */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { super.setApplicationContext(applicationContext); this.applicationContext = applicationContext; } + /** + * Returns a reference to the Spring {@link ApplicationContext}. + * + * @return a reference to the Spring {@link ApplicationContext}. + * @see org.springframework.context.ApplicationContext + */ protected ApplicationContext getApplicationContext() { return Optional.ofNullable(this.applicationContext) .orElseThrow(() -> newIllegalStateException("The ApplicationContext was not properly configured")); } /** - * Sets a reference to the {@link ClassLoader} used by the Spring container to load bean class types. + * Sets a reference to the {@link ClassLoader} used by the Spring container to load bean {@link Class class types}. * - * @param beanClassLoader {@link ClassLoader} used by the Spring container to load bean class types. + * @param beanClassLoader {@link ClassLoader} used by the Spring container to load bean {@link Class class types}. * @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(ClassLoader) * @see java.lang.ClassLoader */ @@ -207,6 +211,24 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat this.beanClassLoader = beanClassLoader; } + /** + * Returns a reference to the {@link ClassLoader} used by the Spring container to load bean + * {@link Class class types}. + * + * @return the {@link ClassLoader} used by the Spring container to load bean {@link Class class types}. + * @see java.lang.ClassLoader + */ + protected ClassLoader getBeanClassLoader() { + return this.beanClassLoader; + } + + /** + * Returns a reference to the Spring container {@link ConfigurableBeanFactory}. + * + * @return a reference to the Spring container {@link ConfigurableBeanFactory}. + * @see org.springframework.beans.factory.config.ConfigurableBeanFactory + * @see #getApplicationContext() + */ protected ConfigurableBeanFactory getBeanFactory() { ApplicationContext applicationContext = getApplicationContext(); @@ -218,16 +240,6 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat ConfigurableBeanFactory.class.getName(), ObjectUtils.nullSafeClassName(applicationContext))); } - /** - * Returns a reference to the {@link ClassLoader} used by the Spring container to load bean class types. - * - * @return the {@link ClassLoader} used by the Spring container to load bean class types. - * @see java.lang.ClassLoader - */ - protected ClassLoader getBeanClassLoader() { - return this.beanClassLoader; - } - /** * Gets the {@link ClientRegionShortcut} used to configure the data management policy of the {@link ClientCache} * {@link Region} that will store {@link Session} state. @@ -457,28 +469,31 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat @PostConstruct public void init() { - - System.err.printf("SETTING SYSTEM PROPERTY (%s) TO (%s) %n", SESSION_SERIALIZER_QUALIFIER_PROPERTY_NAME, - getSessionSerializerBeanName()); - - System.setProperty(SESSION_SERIALIZER_QUALIFIER_PROPERTY_NAME, getSessionSerializerBeanName()); - getBeanFactory().registerAlias(getSessionSerializerBeanName(), SESSION_SERIALIZER_REGISTERED_ALIAS); + getBeanFactory().registerAlias(getSessionSerializerBeanName(), SESSION_SERIALIZER_BEAN_ALIAS); } @Bean - @DependsOn({ SESSION_DATA_SERIALIZER_BEAN_NAME, SESSION_PDX_SERIALIZER_BEAN_NAME }) - public ClientCacheConfigurer sessionClientCacheConfigurer( - @Qualifier(SESSION_SERIALIZER_REGISTERED_ALIAS) SessionSerializer sessionSerializer) { + BeanPostProcessor sessionSerializerConfigurationBeanPostProcessor() { - return (beanName, clientCacheFactoryBean) -> configureSerialization(clientCacheFactoryBean, sessionSerializer); + return new BeanPostProcessor() { + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + + if (bean instanceof CacheFactoryBean) { + + SessionSerializer sessionSerializer = resolveSessionSerializer(); + + configureSerialization((CacheFactoryBean) bean, sessionSerializer); + } + + return bean; + } + }; } - @Bean - @DependsOn({ SESSION_DATA_SERIALIZER_BEAN_NAME, SESSION_PDX_SERIALIZER_BEAN_NAME }) - public PeerCacheConfigurer sessionPeerCacheConfigurer( - @Qualifier(SESSION_SERIALIZER_REGISTERED_ALIAS) SessionSerializer sessionSerializer) { - - return (beanName, cacheFactoryBean) -> configureSerialization(cacheFactoryBean, sessionSerializer); + private SessionSerializer resolveSessionSerializer() { + return getApplicationContext().getBean(SESSION_SERIALIZER_BEAN_ALIAS, SessionSerializer.class); } @SuppressWarnings("unchecked") @@ -497,8 +512,6 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat (PdxSerializer) sessionSerializer, cacheFactoryBean.getPdxSerializer())); } else { - // TODO add more intelligence to figure out what type of serializer has been configured - // (e.g. PDX or DataSerialization based serializers) Optional.ofNullable(sessionSerializer) .ifPresent(serializer -> cacheFactoryBean.setPdxSerializer(ComposablePdxSerializer.compose( @@ -508,28 +521,14 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat } } - /** - * Defines the {@link SessionRepository} bean used to interact with Apache Geode or Pivotal GemFire - * as the Spring Session provider. - * - * @param gemfireOperations instance of {@link GemfireOperations} used to manage {@link Session} state - * in Apache Geode or Pivotal GemFire. - * @return a {@link GemFireOperationsSessionRepository} for managing (clustering/replicating) {@link Session} state - * in Apache Geode or Pivotal GemFire. - * @see org.springframework.session.data.gemfire.GemFireOperationsSessionRepository - * @see org.springframework.data.gemfire.GemfireOperations - */ - @Bean - public GemFireOperationsSessionRepository sessionRepository( - @Qualifier("sessionRegionTemplate") GemfireOperations gemfireOperations) { + @Bean(SESSION_DATA_SERIALIZER_BEAN_NAME) + public Object sessionDataSerializer() { + return new DataSerializableSessionSerializer(); + } - GemFireOperationsSessionRepository sessionRepository = - new GemFireOperationsSessionRepository(gemfireOperations); - - sessionRepository.setMaxInactiveIntervalInSeconds(getMaxInactiveIntervalInSeconds()); - sessionRepository.setUseDataSerialization(isUsingDataSerialization()); - - return sessionRepository; + @Bean(SESSION_PDX_SERIALIZER_BEAN_NAME) + public Object sessionPdxSerializer() { + return new PdxSerializableSessionSerializer(); } /** @@ -634,16 +633,28 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat return new GemfireTemplate(gemfireCache.getRegion(getSessionRegionName())); } - @Bean(SESSION_DATA_SERIALIZER_BEAN_NAME) - //@Conditional(DataSerializableSessionSerializerCondition.class) - public Object sessionDataSerializer() { - return new PdxSerializableSessionSerializer(); - } + /** + * Defines the {@link SessionRepository} bean used to interact with Apache Geode or Pivotal GemFire + * as the Spring Session provider. + * + * @param gemfireOperations instance of {@link GemfireOperations} used to manage {@link Session} state + * in Apache Geode or Pivotal GemFire. + * @return a {@link GemFireOperationsSessionRepository} for managing (clustering/replicating) {@link Session} state + * in Apache Geode or Pivotal GemFire. + * @see org.springframework.session.data.gemfire.GemFireOperationsSessionRepository + * @see org.springframework.data.gemfire.GemfireOperations + */ + @Bean + public GemFireOperationsSessionRepository sessionRepository( + @Qualifier("sessionRegionTemplate") GemfireOperations gemfireOperations) { - @Bean(SESSION_PDX_SERIALIZER_BEAN_NAME) - //@Conditional(PdxSerializableSessionSerializerCondition.class) - public Object sessionPdxSerializer() { - return new DataSerializableSessionSerializer(); + GemFireOperationsSessionRepository sessionRepository = + new GemFireOperationsSessionRepository(gemfireOperations); + + sessionRepository.setMaxInactiveIntervalInSeconds(getMaxInactiveIntervalInSeconds()); + sessionRepository.setUseDataSerialization(isUsingDataSerialization()); + + return sessionRepository; } /** @@ -695,40 +706,4 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat return sessionAttributesIndex; } - - public static class DataSerializableSessionSerializerCondition implements Condition { - - @Override - public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { - - System.err.printf("%1$s-System.getProperty(%2$s) = '%3$s'%n", getClass().getSimpleName(), - SESSION_SERIALIZER_QUALIFIER_PROPERTY_NAME, - System.getProperty(SESSION_SERIALIZER_QUALIFIER_PROPERTY_NAME)); - - System.err.printf("%1$s-Environment.get(%2$s) = '%3$s'%n", getClass().getSimpleName(), - SESSION_SERIALIZER_QUALIFIER_PROPERTY_NAME, - context.getEnvironment().getProperty(SESSION_SERIALIZER_QUALIFIER_PROPERTY_NAME)); - - return SESSION_DATA_SERIALIZER_BEAN_NAME - .equals(context.getEnvironment().getProperty(SESSION_SERIALIZER_QUALIFIER_PROPERTY_NAME)); - } - } - - public static class PdxSerializableSessionSerializerCondition implements Condition { - - @Override - public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { - - System.err.printf("%1$s-System.getProperty(%2$s) = '%3$s'%n", getClass().getSimpleName(), - SESSION_SERIALIZER_QUALIFIER_PROPERTY_NAME, - System.getProperty(SESSION_SERIALIZER_QUALIFIER_PROPERTY_NAME)); - - System.err.printf("%1$s-Environment.get(%2$s) = '%3$s'%n", getClass().getSimpleName(), - SESSION_SERIALIZER_QUALIFIER_PROPERTY_NAME, - context.getEnvironment().getProperty(SESSION_SERIALIZER_QUALIFIER_PROPERTY_NAME)); - - return SESSION_PDX_SERIALIZER_BEAN_NAME - .equals(context.getEnvironment().getProperty(SESSION_SERIALIZER_QUALIFIER_PROPERTY_NAME)); - } - } } diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/AbstractDataSerializableSessionSerializer.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/AbstractDataSerializableSessionSerializer.java index e6a88e1..bc3eb67 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/AbstractDataSerializableSessionSerializer.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/AbstractDataSerializableSessionSerializer.java @@ -58,13 +58,6 @@ public abstract class AbstractDataSerializableSessionSerializer extends DataS return DEFAULT_ALLOW_JAVA_SERIALIZATION; } - @Override - @SuppressWarnings("unchecked") - public boolean canSerialize(Class type) { - return stream(nullSafeArray(getSupportedClasses(), Class.class)) - .anyMatch(supportedClass -> supportedClass.isAssignableFrom(type)); - } - @Override @SuppressWarnings("unchecked") public boolean toData(Object session, DataOutput out) throws IOException { @@ -95,6 +88,14 @@ public abstract class AbstractDataSerializableSessionSerializer extends DataS return DataSerializer.readObject(in); } + @Override + @SuppressWarnings("unchecked") + public boolean canSerialize(Class type) { + return stream(nullSafeArray(getSupportedClasses(), Class.class)) + .filter(it -> type != null) + .anyMatch(supportedClass -> supportedClass.isAssignableFrom(type)); + } + protected T safeRead(DataInput in, DataInputReader reader) { try { return reader.doRead(in); diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionAttributesSerializer.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionAttributesSerializer.java index f72c78a..9e3a4ed 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionAttributesSerializer.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionAttributesSerializer.java @@ -18,18 +18,28 @@ package org.springframework.session.data.gemfire.serialization.data.provider; import static org.springframework.data.gemfire.util.ArrayUtils.asArray; import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeSet; +import static org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.DeltaCapableGemFireSessionAttributes; import static org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSessionAttributes; import java.io.DataInput; import java.io.DataOutput; import java.util.Set; +import org.springframework.session.Session; +import org.springframework.session.data.gemfire.serialization.SessionSerializer; import org.springframework.session.data.gemfire.serialization.data.AbstractDataSerializableSessionSerializer; /** - * The {@link DataSerializableSessionAttributesSerializer} class... + * The {@link DataSerializableSessionAttributesSerializer} class is an implementation of the {@link SessionSerializer} + * interface used to serialize a Spring {@link Session} attributes using the GemFire/Geode's Data Serialization + * framework. * * @author John Blum + * @see java.io.DataInput + * @see java.io.DataOutput + * @see org.springframework.session.Session + * @see org.springframework.session.data.gemfire.serialization.SessionSerializer + * @see org.springframework.session.data.gemfire.serialization.data.AbstractDataSerializableSessionSerializer * @since 2.0.0 */ @SuppressWarnings("unused") @@ -47,7 +57,7 @@ public class DataSerializableSessionAttributesSerializer @Override public Class[] getSupportedClasses() { - return asArray(GemFireSessionAttributes.class); + return asArray(GemFireSessionAttributes.class, DeltaCapableGemFireSessionAttributes.class); } @Override diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionSerializer.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionSerializer.java index 7b7c0b3..90eba3a 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionSerializer.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionSerializer.java @@ -25,16 +25,25 @@ import java.time.Instant; import java.util.Collections; import java.util.Set; +import org.springframework.session.Session; +import org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.DeltaCapableGemFireSession; import org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSession; import org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSessionAttributes; +import org.springframework.session.data.gemfire.serialization.SessionSerializer; import org.springframework.session.data.gemfire.serialization.data.AbstractDataSerializableSessionSerializer; import org.springframework.session.data.gemfire.support.AbstractSession; import org.springframework.util.StringUtils; /** - * The {@link DataSerializableSessionSerializer} class... + * The {@link DataSerializableSessionSerializer} class is an implementation of the {@link SessionSerializer} interface + * used to serialize a Spring {@link Session} using the GemFire/Geode's Data Serialization framework. * * @author John Blum + * @see java.io.DataInput + * @see java.io.DataOutput + * @see org.springframework.session.Session + * @see org.springframework.session.data.gemfire.serialization.SessionSerializer + * @see org.springframework.session.data.gemfire.serialization.data.AbstractDataSerializableSessionSerializer * @since 2.0.0 */ @SuppressWarnings("unused") @@ -52,7 +61,7 @@ public class DataSerializableSessionSerializer extends AbstractDataSerializableS @Override public Class[] getSupportedClasses() { - return asArray(GemFireSession.class); + return asArray(GemFireSession.class, DeltaCapableGemFireSession.class); } @Override diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/support/DataSerializerSessionSerializerAdapter.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/support/DataSerializerSessionSerializerAdapter.java index a643cfd..60eb428 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/support/DataSerializerSessionSerializerAdapter.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/support/DataSerializerSessionSerializerAdapter.java @@ -24,43 +24,58 @@ import java.io.DataInput; import java.io.DataOutput; import java.util.Optional; -import javax.annotation.Resource; +import org.apache.geode.DataSerializer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.session.Session; +import org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.DeltaCapableGemFireSession; +import org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.DeltaCapableGemFireSessionAttributes; +import org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSession; +import org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSessionAttributes; import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration; import org.springframework.session.data.gemfire.serialization.SessionSerializer; +import org.springframework.stereotype.Component; /** - * The DataSerializerSessionSerializerAdapter class... + * The {@link DataSerializerSessionSerializerAdapter} class is a two-way Adapter adapting a {@link SessionSerializer} + * instance as an instance of {@link DataSerializer} in a GemFire/Geode context, or adapting a {@link DataSerializer} + * as a {@link SessionSerializer} in a Spring Session context. * * @author John Blum + * @see org.apache.geode.DataSerializer + * @see org.springframework.session.Session + * @see org.springframework.session.data.gemfire.serialization.SessionSerializer + * @see org.springframework.session.data.gemfire.serialization.data.support.WirableDataSerializer + * @see org.springframework.stereotype.Component * @since 2.0.0 */ @SuppressWarnings("unused") +@Component("org.springfamework.session.data.gemfire.serialization.data.support.DataSerializerSessionSerializerAdapter") public class DataSerializerSessionSerializerAdapter extends WirableDataSerializer { static { register(DataSerializerSessionSerializerAdapter.class); } - @Resource(name = "${" + GemFireHttpSessionConfiguration.SESSION_SERIALIZER_QUALIFIER_PROPERTY_NAME - + ":" + GemFireHttpSessionConfiguration.SESSION_DATA_SERIALIZER_BEAN_NAME + "}") private SessionSerializer sessionSerializer; public DataSerializerSessionSerializerAdapter() { autowire(); } - public DataSerializerSessionSerializerAdapter(SessionSerializer sessionSerializer) { - this.sessionSerializer = Optional.ofNullable(sessionSerializer) - .orElseThrow(() -> newIllegalArgumentException("SessionSerializer is required")); - } - @Override public int getId() { return 0x0BAC2BAC; } + @Autowired + @Qualifier(GemFireHttpSessionConfiguration.SESSION_SERIALIZER_BEAN_ALIAS) + public final void setSessionSerializer(SessionSerializer sessionSerializer) { + this.sessionSerializer = Optional.ofNullable(sessionSerializer) + .orElseThrow(() -> newIllegalArgumentException("SessionSerializer is required")); + } + protected SessionSerializer getSessionSerializer() { return Optional.ofNullable(this.sessionSerializer) .orElseThrow(() -> newIllegalStateException("SessionSerializer was not properly configured")); @@ -68,7 +83,8 @@ public class DataSerializerSessionSerializerAdapter extends W @Override public Class[] getSupportedClasses() { - return asArray(Session.class); + return asArray(GemFireSession.class, GemFireSessionAttributes.class, DeltaCapableGemFireSession.class, + DeltaCapableGemFireSessionAttributes.class); } @Override diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/support/WirableDataSerializer.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/support/WirableDataSerializer.java index 894c071..85f9a56 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/support/WirableDataSerializer.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/support/WirableDataSerializer.java @@ -18,31 +18,47 @@ package org.springframework.session.data.gemfire.serialization.data.support; import static org.springframework.data.gemfire.support.GemfireBeanFactoryLocator.newBeanFactoryLocator; +import java.util.Optional; + +import org.apache.geode.DataSerializer; + import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.wiring.BeanConfigurerSupport; import org.springframework.session.data.gemfire.serialization.data.AbstractDataSerializableSessionSerializer; +import org.springframework.stereotype.Component; /** - * The WirableDataSerializer class... + * {@link WirableDataSerializer} is an abstract base class supporting auto-wiring of a non-managed, + * GemFire/Geode {@link DataSerializer}, Spring {@link Component}. * * @author John Blum + * @see org.apache.geode.DataSerializer + * @see org.springframework.beans.factory.BeanFactory + * @see org.springframework.beans.factory.wiring.BeanConfigurerSupport + * @see org.springframework.session.data.gemfire.serialization.data.AbstractDataSerializableSessionSerializer * @since 2.0.0 */ -public abstract class WirableDataSerializer extends AbstractDataSerializableSessionSerializer { +abstract class WirableDataSerializer extends AbstractDataSerializableSessionSerializer { protected final void autowire() { - BeanConfigurerSupport beanConfigurer = newBeanConfigurer(locateBeanFactory()); - - beanConfigurer.configureBean(this); - beanConfigurer.destroy(); + locateBeanFactory().map(this::newBeanConfigurer).ifPresent(beanConfigurer -> { + beanConfigurer.configureBean(this); + beanConfigurer.destroy(); + }); } - private BeanFactory locateBeanFactory() { - return newBeanFactoryLocator().useBeanFactory(); + Optional locateBeanFactory() { + + try { + return Optional.ofNullable(newBeanFactoryLocator().useBeanFactory()); + } + catch (Exception ignore) { + return Optional.empty(); + } } - private BeanConfigurerSupport newBeanConfigurer(BeanFactory beanFactory) { + BeanConfigurerSupport newBeanConfigurer(BeanFactory beanFactory) { BeanConfigurerSupport beanConfigurer = new BeanConfigurerSupport(); diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/pdx/AbstractPdxSerializableSessionSerializer.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/pdx/AbstractPdxSerializableSessionSerializer.java index e681b1d..b775557 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/pdx/AbstractPdxSerializableSessionSerializer.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/pdx/AbstractPdxSerializableSessionSerializer.java @@ -26,19 +26,23 @@ import org.springframework.session.Session; import org.springframework.session.data.gemfire.serialization.SessionSerializer; /** - * The {@link AbstractPdxSerializableSessionSerializer} class... + * The {@link AbstractPdxSerializableSessionSerializer} class is an abstract base class containing functionality common + * to all GemFire/Geode PDX-based {@link SessionSerializer} implementations. + * + * This class also implements GemFire/Geode's {@link PdxSerializer} interface, adapting it to the Spring Session, + * Data GemFire {@link SessionSerializer} interface. * * @author John Blum + * @see org.apache.geode.pdx.PdxReader + * @see org.apache.geode.pdx.PdxWriter + * @see org.apache.geode.pdx.PdxSerializer + * @see org.springframework.session.Session + * @see org.springframework.session.data.gemfire.serialization.SessionSerializer * @since 2.0.0 */ public abstract class AbstractPdxSerializableSessionSerializer implements PdxSerializer, SessionSerializer { - @Override - public boolean canSerialize(Class type) { - return Optional.ofNullable(type).map(Session.class::isAssignableFrom).orElse(false); - } - @Override @SuppressWarnings("unchecked") public boolean toData(Object session, PdxWriter writer) { @@ -60,4 +64,9 @@ public abstract class AbstractPdxSerializableSessionSerializer deserialize(reader)) .orElse(null); } + + @Override + public boolean canSerialize(Class type) { + return Optional.ofNullable(type).map(Session.class::isAssignableFrom).orElse(false); + } } diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/pdx/provider/PdxSerializableSessionSerializer.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/pdx/provider/PdxSerializableSessionSerializer.java index 6641b3f..8402a0f 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/pdx/provider/PdxSerializableSessionSerializer.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/pdx/provider/PdxSerializableSessionSerializer.java @@ -29,29 +29,31 @@ import java.util.Set; import org.apache.geode.pdx.PdxReader; import org.apache.geode.pdx.PdxWriter; +import org.springframework.session.Session; +import org.springframework.session.data.gemfire.serialization.SessionSerializer; import org.springframework.session.data.gemfire.serialization.pdx.AbstractPdxSerializableSessionSerializer; import org.springframework.session.data.gemfire.support.AbstractSession; /** - * The PdxSerializableSessionSerializer class... + * The {@link PdxSerializableSessionSerializer} class is an implementation of the {@link SessionSerializer} interface + * used to serialize a Spring {@link Session} using the GemFire/Geode's PDX Serialization framework. * * @author John Blum - * @since 1.0.0 + * @see org.apache.geode.pdx.PdxReader + * @see org.apache.geode.pdx.PdxWriter + * @see org.springframework.session.Session + * @see org.springframework.session.data.gemfire.serialization.SessionSerializer + * @see org.springframework.session.data.gemfire.serialization.pdx.AbstractPdxSerializableSessionSerializer + * @since 2.0.0 */ @SuppressWarnings("unused") public class PdxSerializableSessionSerializer extends AbstractPdxSerializableSessionSerializer { - @Override - public boolean canSerialize(Class type) { - return Optional.ofNullable(type).map(GemFireSession.class::isAssignableFrom).orElse(false); - } - @Override @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") public void serialize(GemFireSession session, PdxWriter writer) { synchronized (session) { - writer.writeString("id", session.getId()); writer.writeLong("creationTime", session.getCreationTime().toEpochMilli()); writer.writeLong("lastAccessedTime", session.getLastAccessedTime().toEpochMilli()); @@ -102,4 +104,9 @@ public class PdxSerializableSessionSerializer extends AbstractPdxSerializableSes return session; } + + @Override + public boolean canSerialize(Class type) { + return Optional.ofNullable(type).map(GemFireSession.class::isAssignableFrom).orElse(false); + } } diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/pdx/support/ComposablePdxSerializer.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/pdx/support/ComposablePdxSerializer.java index bf7f476..d380d3a 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/pdx/support/ComposablePdxSerializer.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/pdx/support/ComposablePdxSerializer.java @@ -34,23 +34,19 @@ import org.apache.geode.pdx.PdxSerializer; import org.apache.geode.pdx.PdxWriter; /** - * The ComposablePdxSerializer class... + * The {@link ComposablePdxSerializer} class is a composite of {@link PdxSerializer} objects implementing + * the Composite Software Design Pattern. * * @author John Blum - * @since 1.0.0 + * @see java.lang.Iterable + * @see org.apache.geode.pdx.PdxSerializer + * @since 2.0.0 */ @SuppressWarnings("unused") -public class ComposablePdxSerializer implements Iterable, PdxSerializer { +public class ComposablePdxSerializer implements PdxSerializer, Iterable { private final List pdxSerializers; - private ComposablePdxSerializer(List pdxSerializers) { - - this.pdxSerializers = Optional.ofNullable(pdxSerializers) - .map(it -> Collections.unmodifiableList(pdxSerializers)) - .orElseThrow(() -> newIllegalArgumentException("PdxSerializers [%s] are required", pdxSerializers)); - } - public static PdxSerializer compose(PdxSerializer... pdxSerializers) { return compose(Arrays.asList(nullSafeArray(pdxSerializers, PdxSerializer.class))); } @@ -66,6 +62,13 @@ public class ComposablePdxSerializer implements Iterable, PdxSeri : new ComposablePdxSerializer(pdxSerializerList))); } + private ComposablePdxSerializer(List pdxSerializers) { + + this.pdxSerializers = Optional.ofNullable(pdxSerializers) + .map(it -> Collections.unmodifiableList(pdxSerializers)) + .orElseThrow(() -> newIllegalArgumentException("PdxSerializers [%s] are required", pdxSerializers)); + } + @Override public Iterator iterator() { return this.pdxSerializers.iterator(); diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/pdx/support/PdxSerializerSessionSerializerAdapter.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/pdx/support/PdxSerializerSessionSerializerAdapter.java index 80929bc..2219273 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/pdx/support/PdxSerializerSessionSerializerAdapter.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/pdx/support/PdxSerializerSessionSerializerAdapter.java @@ -21,6 +21,7 @@ import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newI import java.util.Optional; import org.apache.geode.pdx.PdxReader; +import org.apache.geode.pdx.PdxSerializer; import org.apache.geode.pdx.PdxWriter; import org.springframework.session.Session; @@ -28,9 +29,15 @@ import org.springframework.session.data.gemfire.serialization.SessionSerializer; import org.springframework.session.data.gemfire.serialization.pdx.AbstractPdxSerializableSessionSerializer; /** - * The {@link PdxSerializerSessionSerializerAdapter} class... + * The {@link PdxSerializerSessionSerializerAdapter} class is a two-way Adapter adapting a {@link SessionSerializer} + * instance as an instance of {@link PdxSerializer} in a GemFire/Geode context, or adapting a {@link PdxSerializer} + * as a {@link SessionSerializer} in a Spring Session context. * * @author John Blum + * @see org.apache.geode.pdx.PdxSerializer + * @see org.springframework.session.Session + * @see org.springframework.session.data.gemfire.serialization.SessionSerializer + * @see org.springframework.session.data.gemfire.serialization.pdx.AbstractPdxSerializableSessionSerializer * @since 2.0.0 */ @SuppressWarnings("unused") diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/config/annotation/web/http/GemFireHttpSessionConfigurationTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/config/annotation/web/http/GemFireHttpSessionConfigurationTests.java index 17c7376..bd4591b 100644 --- a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/config/annotation/web/http/GemFireHttpSessionConfigurationTests.java +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/config/annotation/web/http/GemFireHttpSessionConfigurationTests.java @@ -28,7 +28,6 @@ import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; -import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; @@ -78,11 +77,6 @@ public class GemFireHttpSessionConfigurationTests { private GemFireHttpSessionConfiguration gemfireConfiguration; - @AfterClass - public static void tearDown() { - System.clearProperty(GemFireHttpSessionConfiguration.SESSION_SERIALIZER_QUALIFIER_PROPERTY_NAME); - } - @SuppressWarnings("unchecked") protected T getField(Object obj, String fieldName) { try { @@ -324,7 +318,7 @@ public class GemFireHttpSessionConfigurationTests { } @Test - public void postConstructInitSetsSystemPropertyAndRegistersBeanAlias() { + public void postConstructInitRegistersBeanAlias() { ConfigurableListableBeanFactory mockBeanFactory = mock(ConfigurableListableBeanFactory.class); @@ -332,20 +326,18 @@ public class GemFireHttpSessionConfigurationTests { given(mockApplicationContext.getBeanFactory()).willReturn(mockBeanFactory); - assertThat(System.getProperty(GemFireHttpSessionConfiguration.SESSION_SERIALIZER_REGISTERED_ALIAS)).isNull(); + assertThat(System.getProperty(GemFireHttpSessionConfiguration.SESSION_SERIALIZER_BEAN_ALIAS)).isNull(); this.gemfireConfiguration.setApplicationContext(mockApplicationContext); this.gemfireConfiguration.setSessionSerializerBeanName("testSessionSerializer"); this.gemfireConfiguration.init(); assertThat(this.gemfireConfiguration.getApplicationContext()).isSameAs(mockApplicationContext); - assertThat(System.getProperty(GemFireHttpSessionConfiguration.SESSION_SERIALIZER_QUALIFIER_PROPERTY_NAME)) - .isEqualTo("testSessionSerializer"); verify(mockApplicationContext, times(1)).getBeanFactory(); verify(mockBeanFactory, times(1)).registerAlias(eq("testSessionSerializer"), - eq(GemFireHttpSessionConfiguration.SESSION_SERIALIZER_REGISTERED_ALIAS)); + eq(GemFireHttpSessionConfiguration.SESSION_SERIALIZER_BEAN_ALIAS)); } @Test diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/SessionSerializerTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/SessionSerializerTests.java new file mode 100644 index 0000000..4298e1d --- /dev/null +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/SessionSerializerTests.java @@ -0,0 +1,83 @@ +/* + * 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 org.springframework.session.data.gemfire.serialization; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +/** + * Unit tests for the {@link SessionSerializer} interface. + * + * @author John Blum + * @see org.junit.Test + * @see org.mockito.Mock + * @see org.mockito.Mockito + * @see org.springframework.session.data.gemfire.serialization.SessionSerializer + * @since 2.0.0 + */ +@RunWith(MockitoJUnitRunner.class) +public class SessionSerializerTests { + + @Mock + private SessionSerializer sessionSerializer; + + @Test + @SuppressWarnings("unchecked") + public void canSerializeWithSerializableObjectReturnsTrue() { + + when(this.sessionSerializer.canSerialize(any(Class.class))).thenReturn(true); + when(this.sessionSerializer.canSerialize(any(Object.class))).thenCallRealMethod(); + + assertThat(this.sessionSerializer.canSerialize("test")).isTrue(); + + verify(this.sessionSerializer, times(1)).canSerialize(eq("test")); + verify(this.sessionSerializer, times(1)).canSerialize(eq(String.class)); + } + + @Test + @SuppressWarnings("unchecked") + public void canSerializeWithNullReturnFalse() { + + assertThat(this.sessionSerializer.canSerialize((Object) null)).isFalse(); + + verify(this.sessionSerializer, times(1)).canSerialize(eq((Object) null)); + verify(this.sessionSerializer, never()).canSerialize(any(Class.class)); + } + + @Test + @SuppressWarnings("unchecked") + public void canSerializeWithNonSerializableObjectReturnFalse() { + + when(this.sessionSerializer.canSerialize(any(Class.class))).thenReturn(false); + when(this.sessionSerializer.canSerialize(any(Object.class))).thenCallRealMethod(); + + assertThat(this.sessionSerializer.canSerialize("test")).isFalse(); + + verify(this.sessionSerializer, times(1)).canSerialize(eq("test")); + verify(this.sessionSerializer, times(1)).canSerialize(eq(String.class)); + } +} diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/AbstractDataSerializableSessionSerializerTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/AbstractDataSerializableSessionSerializerTests.java new file mode 100644 index 0000000..cc1f792 --- /dev/null +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/AbstractDataSerializableSessionSerializerTests.java @@ -0,0 +1,296 @@ +/* + * 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 org.springframework.session.data.gemfire.serialization.data; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.data.gemfire.util.ArrayUtils.asArray; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.session.Session; +import org.springframework.session.data.gemfire.serialization.SerializationException; +import org.springframework.session.data.gemfire.serialization.data.AbstractDataSerializableSessionSerializer.DataInputReader; +import org.springframework.session.data.gemfire.serialization.data.AbstractDataSerializableSessionSerializer.DataOutputWriter; + +/** + * Unit tests for {@link AbstractDataSerializableSessionSerializer}. + * + * @author John Blum + * @see org.junit.Test + * @see org.mockito.Mock + * @see org.mockito.Mockito + * @see org.mockito.Spy + * @see org.springframework.session.data.gemfire.serialization.data.AbstractDataSerializableSessionSerializer + * @since 2.0.0 + */ +@RunWith(MockitoJUnitRunner.class) +public class AbstractDataSerializableSessionSerializerTests { + + @Spy + private AbstractDataSerializableSessionSerializer sessionSerializer; + + @Mock + private DataInput mockDataInput; + + @Mock + private DataOutput mockDataOuput; + + @Mock + private Session mockSession; + + @Test + public void constructAbstractDataSerializableSessionSerializer() { + + assertThat(this.sessionSerializer).isNotNull(); + assertThat(this.sessionSerializer.getId()).isEqualTo(0xA11ACE5); + assertThat(this.sessionSerializer.getSupportedClasses()).isEmpty(); + assertThat(this.sessionSerializer.allowJavaSerialization()).isTrue(); + } + + @Test + public void toDataCallsSerializeForSerializableObjectAndReturnsTrue() throws IOException { + + when(this.sessionSerializer.canSerialize(any(Session.class))).thenReturn(true); + + assertThat(this.sessionSerializer.toData(this.mockSession, this.mockDataOuput)).isTrue(); + + verify(this.sessionSerializer, times(1)).canSerialize(eq(this.mockSession)); + verify(this.sessionSerializer, times(1)) + .serialize(eq(this.mockSession), eq(this.mockDataOuput)); + } + + @Test + public void toDataWithNonSerializableObjectReturnsFalse() throws IOException { + + when(this.sessionSerializer.canSerialize(any())).thenReturn(false); + + assertThat(this.sessionSerializer.toData("test", this.mockDataOuput)).isFalse(); + + verify(this.sessionSerializer, times(1)).canSerialize(eq("test")); + verify(this.sessionSerializer, never()).serialize(any(), any(DataOutput.class)); + } + + @Test + public void toDataWithNullReturnsFalse() throws IOException { + + assertThat(this.sessionSerializer.toData(null, this.mockDataOuput)).isFalse(); + + verify(this.sessionSerializer, never()).canSerialize(any()); + verify(this.sessionSerializer, never()).serialize(any(), any(DataOutput.class)); + } + + @Test + public void serializeObjectDefaultsAllowJavaSerializationToTrue() throws IOException { + + this.sessionSerializer.serializeObject("test", this.mockDataOuput); + + verify(this.sessionSerializer, times(1)) + .serializeObject(eq("test"), eq(this.mockDataOuput), eq(true)); + } + + @Test + public void serializeObjectWithAllowJavaSerializationSetToFalse() throws IOException { + this.sessionSerializer.serializeObject("test", this.mockDataOuput, false); + } + + @Test + public void fromDataCallsDeserialize() throws IOException, ClassNotFoundException { + + when(this.sessionSerializer.deserialize(any(DataInput.class))).thenReturn(this.mockSession); + + assertThat(this.sessionSerializer.fromData(this.mockDataInput)).isEqualTo(this.mockSession); + + verify(this.sessionSerializer, times(1)).deserialize(eq(this.mockDataInput)); + } + + @Test + public void canSerializeSerializableType() { + + when(this.sessionSerializer.getSupportedClasses()).thenReturn(asArray(Session.class)); + + assertThat(this.sessionSerializer.canSerialize(this.mockSession)).isTrue(); + + verify(this.sessionSerializer, times(1)).getSupportedClasses(); + } + + @Test + public void canSerializeSerializableSubType() { + + when(this.sessionSerializer.getSupportedClasses()).thenReturn(asArray(Number.class, Integer.class, Long.class)); + + assertThat(this.sessionSerializer.canSerialize((short) 64)).isTrue(); + + verify(this.sessionSerializer, times(1)).getSupportedClasses(); + } + + @Test + public void cannotSerializeNonSerializableType() { + + when(this.sessionSerializer.getSupportedClasses()).thenReturn(asArray(Session.class)); + + assertThat(this.sessionSerializer.canSerialize("sessionId")).isFalse(); + + verify(this.sessionSerializer, times(1)).getSupportedClasses(); + } + + @Test + public void cannotSerializeNull() { + + when(this.sessionSerializer.getSupportedClasses()).thenReturn(asArray(Object.class)); + + assertThat(this.sessionSerializer.canSerialize(null)).isFalse(); + + verify(this.sessionSerializer, times(1)).getSupportedClasses(); + } + + @Test + public void cannotSerializeWhenSupportClassesAreEmpty() { + + when(this.sessionSerializer.getSupportedClasses()).thenReturn(asArray()); + + assertThat(this.sessionSerializer.canSerialize("test")).isFalse(); + + verify(this.sessionSerializer, times(1)).getSupportedClasses(); + } + + @Test + public void cannotSerializeWhenSupportClassesAreNull() { + + when(this.sessionSerializer.getSupportedClasses()).thenReturn(null); + + assertThat(this.sessionSerializer.canSerialize("test")).isFalse(); + + verify(this.sessionSerializer, times(1)).getSupportedClasses(); + } + + @Test + @SuppressWarnings("unchecked") + public void safeReadReturnsValue() throws IOException, ClassNotFoundException { + + DataInputReader mockDataInputReader = mock(DataInputReader.class); + + when(mockDataInputReader.doRead(any(DataInput.class))).thenReturn("test"); + + assertThat(this.sessionSerializer.safeRead(this.mockDataInput, mockDataInputReader)).isEqualTo("test"); + + verify(mockDataInputReader, times(1)).doRead(eq(this.mockDataInput)); + } + + @Test(expected = SerializationException.class) + @SuppressWarnings("unchecked") + public void safeReadHandlesClassCastException() throws IOException, ClassNotFoundException { + + DataInputReader mockDataInputReader = mock(DataInputReader.class); + + when(mockDataInputReader.doRead(any(DataInput.class))).thenThrow(new ClassNotFoundException("test")); + + try { + this.sessionSerializer.safeRead(this.mockDataInput, mockDataInputReader); + } + catch (Exception expected) { + + assertThat(expected).isInstanceOf(SerializationException.class); + assertThat(expected).hasCauseInstanceOf(ClassNotFoundException.class); + assertThat(expected.getCause()).hasMessage("test"); + assertThat(expected.getCause()).hasNoCause(); + + throw expected; + } + finally { + verify(mockDataInputReader, times(1)).doRead(eq(this.mockDataInput)); + } + } + + @Test(expected = SerializationException.class) + @SuppressWarnings("unchecked") + public void safeReadHandlesIOException() throws IOException, ClassNotFoundException { + + DataInputReader mockDataInputReader = mock(DataInputReader.class); + + when(mockDataInputReader.doRead(any(DataInput.class))).thenAnswer(invocation -> + invocation.getArgument(0).readUTF()); + + when(this.mockDataInput.readUTF()).thenThrow(new IOException("test")); + + try { + this.sessionSerializer.safeRead(this.mockDataInput, mockDataInputReader); + } + catch (Exception expected) { + + assertThat(expected).isInstanceOf(SerializationException.class); + assertThat(expected).hasCauseInstanceOf(IOException.class); + assertThat(expected.getCause()).hasMessage("test"); + assertThat(expected.getCause()).hasNoCause(); + + throw expected; + } + finally { + verify(mockDataInputReader, times(1)).doRead(eq(this.mockDataInput)); + verify(this.mockDataInput, times(1)).readUTF(); + } + } + + @Test + public void safeWriteWritesValue() throws IOException { + + DataOutputWriter mockDataOutputWriter = mock(DataOutputWriter.class); + + this.sessionSerializer.safeWrite(this.mockDataOuput, mockDataOutputWriter); + + verify(mockDataOutputWriter, times(1)).doWrite(eq(this.mockDataOuput)); + } + + @Test(expected = SerializationException.class) + public void safeWriteHandlesIOException() throws IOException { + + DataOutputWriter mockDataOutputWriter = mock(DataOutputWriter.class); + + doThrow(new IOException("test")).when(mockDataOutputWriter).doWrite(any(DataOutput.class)); + + try { + this.sessionSerializer.safeWrite(this.mockDataOuput, mockDataOutputWriter); + } + catch (Exception expected ) { + + assertThat(expected).isInstanceOf(SerializationException.class); + assertThat(expected).hasCauseInstanceOf(IOException.class); + assertThat(expected.getCause()).hasMessage("test"); + assertThat(expected.getCause()).hasNoCause(); + + throw expected; + } + finally { + verify(mockDataOutputWriter, times(1)).doWrite(eq(this.mockDataOuput)); + } + } +} diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionAttributesSerializerTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionAttributesSerializerTests.java index 3a9f824..f2cdb16 100644 --- a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionAttributesSerializerTests.java +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionAttributesSerializerTests.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.springframework.data.gemfire.util.CollectionUtils.asSet; +import static org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.DeltaCapableGemFireSessionAttributes; import static org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSessionAttributes; import java.io.DataInput; @@ -62,8 +63,9 @@ public class DataSerializableSessionAttributesSerializerTests { } @Test - public void supportedClassesContainsGemFireSessionAttributes() { + public void supportedClassesContainsGemFireSessionAttributesAndSubTypes() { assertThat(this.sessionAttributesSerializer.getSupportedClasses()).contains(GemFireSessionAttributes.class); + assertThat(this.sessionAttributesSerializer.getSupportedClasses()).contains(DeltaCapableGemFireSessionAttributes.class); } @Test diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionSerializerTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionSerializerTests.java index fe03cd3..76db58e 100644 --- a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionSerializerTests.java +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionSerializerTests.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.springframework.data.gemfire.util.CollectionUtils.asSet; +import static org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.DeltaCapableGemFireSession; import static org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSession; import static org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSessionAttributes; @@ -73,8 +74,9 @@ public class DataSerializableSessionSerializerTests { } @Test - public void supportedClassContainsGemFireSession() { + public void supportedClassContainsGemFireSessionAndSubTypes() { assertThat(this.sessionSerializer.getSupportedClasses()).contains(GemFireSession.class); + assertThat(this.sessionSerializer.getSupportedClasses()).contains(DeltaCapableGemFireSession.class); } @Test diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/support/DataSerializerSessionSerializerAdapterUnitTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/support/DataSerializerSessionSerializerAdapterUnitTests.java new file mode 100644 index 0000000..e350185 --- /dev/null +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/support/DataSerializerSessionSerializerAdapterUnitTests.java @@ -0,0 +1,137 @@ +/* + * 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 org.springframework.session.data.gemfire.serialization.data.support; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.DataInput; +import java.io.DataOutput; + +import org.junit.Test; + +import org.springframework.session.Session; +import org.springframework.session.data.gemfire.serialization.SessionSerializer; + +/** + * Unit tests for {@link DataSerializerSessionSerializerAdapter}. + * + * @author John Blum + * @see java.io.DataInput + * @see java.io.DataOutput + * @see org.junit.Test + * @see org.mockito.Mockito + * @see org.springframework.session.Session + * @see org.springframework.session.data.gemfire.serialization.SessionSerializer + * @see org.springframework.session.data.gemfire.serialization.data.support.DataSerializerSessionSerializerAdapter + * @since 2.0.0 + */ +public class DataSerializerSessionSerializerAdapterUnitTests { + + @Test + @SuppressWarnings("unchecked") + public void setAndGetSessionSerializerReturnsExpected() { + + SessionSerializer mockSessionSerializer = mock(SessionSerializer.class); + + DataSerializerSessionSerializerAdapter dataSerializer = + new DataSerializerSessionSerializerAdapter<>(); + + dataSerializer.setSessionSerializer(mockSessionSerializer); + + assertThat(dataSerializer.getSessionSerializer()).isSameAs(mockSessionSerializer); + } + + @Test(expected = IllegalArgumentException.class) + public void setSessionSerializerToNullThrowsIllegalArgumentException() { + + try { + new DataSerializerSessionSerializerAdapter<>().setSessionSerializer(null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("SessionSerializer is required"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @SuppressWarnings("unchecked") + @Test(expected = IllegalStateException.class) + public void getUninitializedSessionSerializerThrowsIllegalStateException() { + + try { + new DataSerializerSessionSerializerAdapter<>().getSessionSerializer(); + } + catch (IllegalStateException expected) { + + assertThat(expected).hasMessage("SessionSerializer was not properly configured"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test + @SuppressWarnings("unchecked") + public void serializeDelegatesToSessionSerializerSerialize() { + + DataOutput mockDataOutput = mock(DataOutput.class); + + Session mockSession = mock(Session.class); + + SessionSerializer mockSessionSerializer = mock(SessionSerializer.class); + + DataSerializerSessionSerializerAdapter dataSerializer = new DataSerializerSessionSerializerAdapter<>(); + + dataSerializer.setSessionSerializer(mockSessionSerializer); + + assertThat(dataSerializer.getSessionSerializer()).isSameAs(mockSessionSerializer); + + dataSerializer.serialize(mockSession, mockDataOutput); + + verify(mockSessionSerializer, times(1)).serialize(eq(mockSession), eq(mockDataOutput)); + } + + @Test + @SuppressWarnings("unchecked") + public void deserializeDelegatesToSessionSerializerDeserialize() { + + DataInput mockDataInput = mock(DataInput.class); + + Session mockSession = mock(Session.class); + + SessionSerializer mockSessionSerializer = mock(SessionSerializer.class); + + when(mockSessionSerializer.deserialize(any(DataInput.class))).thenReturn(mockSession); + + DataSerializerSessionSerializerAdapter dataSerializer = new DataSerializerSessionSerializerAdapter<>(); + + dataSerializer.setSessionSerializer(mockSessionSerializer); + + assertThat(dataSerializer.getSessionSerializer()).isSameAs(mockSessionSerializer); + assertThat(dataSerializer.deserialize(mockDataInput)).isEqualTo(mockSession); + + verify(mockSessionSerializer, times(1)).deserialize(eq(mockDataInput)); + } +} diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/support/WirableDataSerializerTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/support/WirableDataSerializerTests.java new file mode 100644 index 0000000..e5824fa --- /dev/null +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/support/WirableDataSerializerTests.java @@ -0,0 +1,135 @@ +/* + * 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 org.springframework.session.data.gemfire.serialization.data.support; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.wiring.BeanConfigurerSupport; +import org.springframework.data.gemfire.support.GemfireBeanFactoryLocator; +import org.springframework.session.Session; + +/** + * The WirableDataSerializerTests class... + * + * @author John Blum + * @since 1.0.0 + */ +@RunWith(MockitoJUnitRunner.class) +public class WirableDataSerializerTests { + + @Mock + private ConfigurableListableBeanFactory mockBeanFactory; + + @Spy + private WirableDataSerializer dataSerializer; + + @Test + public void autowiresThis() { + + AtomicReference beanConfigurerReference = new AtomicReference<>(null); + + doReturn(Optional.of(this.mockBeanFactory)).when(this.dataSerializer).locateBeanFactory(); + + doAnswer(invocation -> { + BeanConfigurerSupport beanConfigurer = spy((BeanConfigurerSupport) invocation.callRealMethod()); + doNothing().when(beanConfigurer).configureBean(any()); + beanConfigurerReference.compareAndSet(null, beanConfigurer); + return beanConfigurer; + }).when(this.dataSerializer).newBeanConfigurer(any(BeanFactory.class)); + + this.dataSerializer.autowire(); + + assertThat(beanConfigurerReference.get()).isNotNull(); + + verify(this.dataSerializer, times(1)).locateBeanFactory(); + verify(this.dataSerializer, times(1)).newBeanConfigurer(eq(this.mockBeanFactory)); + verify(beanConfigurerReference.get(), times(1)).configureBean(eq(this.dataSerializer)); + verify(beanConfigurerReference.get(), times(1)).destroy(); + } + + @Test + public void noAutowiringWhenBeanFactoryCannotBeLocated() { + + doReturn(Optional.empty()).when(this.dataSerializer).locateBeanFactory(); + + this.dataSerializer.autowire(); + + verify(this.dataSerializer, times(1)).locateBeanFactory(); + verify(this.dataSerializer, never()).newBeanConfigurer(any(BeanFactory.class)); + } + + @Test + public void locatesBeanFactory() { + + GemfireBeanFactoryLocator beanFactoryLocator = null; + + try { + when(this.mockBeanFactory.getAliases(anyString())).thenReturn(new String[0]); + + beanFactoryLocator = GemfireBeanFactoryLocator.newBeanFactoryLocator(this.mockBeanFactory, + "testBeanFactory"); + + assertThat(this.dataSerializer.locateBeanFactory().orElse(null)).isSameAs(this.mockBeanFactory); + } + finally { + verify(this.mockBeanFactory, times(1)).getAliases(eq("testBeanFactory")); + Optional.ofNullable(beanFactoryLocator).ifPresent(GemfireBeanFactoryLocator::destroy); + } + } + + @Test + public void unableToLocateBeanFactory() { + assertThat(this.dataSerializer.locateBeanFactory().orElse(null)).isNull(); + } + + @Test + public void constructsNewBeanConfigurerSupportWithBeanFactory() { + + BeanConfigurerSupport beanConfigurer = null; + + try { + beanConfigurer = this.dataSerializer.newBeanConfigurer(this.mockBeanFactory); + + assertThat(beanConfigurer).isNotNull(); + } + finally { + Optional.ofNullable(beanConfigurer).ifPresent(BeanConfigurerSupport::destroy); + } + } +} diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/pdx/AbstractPdxSerializableSessionSerializerTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/pdx/AbstractPdxSerializableSessionSerializerTests.java new file mode 100644 index 0000000..ce0ec93 --- /dev/null +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/pdx/AbstractPdxSerializableSessionSerializerTests.java @@ -0,0 +1,147 @@ +/* + * 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 org.springframework.session.data.gemfire.serialization.pdx; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSession; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import org.apache.geode.pdx.PdxReader; +import org.apache.geode.pdx.PdxWriter; + +import org.springframework.session.Session; + +/** + * Unit tests for {@link AbstractPdxSerializableSessionSerializer}. + * + * @author John Blum + * @see org.junit.Test + * @see org.mockito.Mock + * @see org.mockito.Mockito + * @see org.mockito.Spy + * @see org.apache.geode.pdx.PdxReader + * @see org.apache.geode.pdx.PdxWriter + * @see org.springframework.session.Session + * @see org.springframework.session.data.gemfire.serialization.pdx.AbstractPdxSerializableSessionSerializer + * @since 2.0.0 + */ +@RunWith(MockitoJUnitRunner.class) +public class AbstractPdxSerializableSessionSerializerTests { + + @Spy + private AbstractPdxSerializableSessionSerializer sessionSerializer; + + @Mock + private PdxReader mockPdxReader; + + @Mock + private PdxWriter mockPdxWriter; + + @Mock + private Session mockSession; + + @Test + public void toDataSerializesSessionAndReturnsTrue() { + + when(this.sessionSerializer.canSerialize(any(Object.class))).thenReturn(true); + + assertThat(this.sessionSerializer.toData(this.mockSession, this.mockPdxWriter)).isTrue(); + + verify(this.sessionSerializer, times(1)) + .serialize(eq(this.mockSession), eq(this.mockPdxWriter)); + } + + @Test + public void toDataWithNonSerializableObjectReturnsFalse() { + + when(this.sessionSerializer.canSerialize(any(Object.class))).thenReturn(false); + + assertThat(this.sessionSerializer.toData("test", this.mockPdxWriter)).isFalse(); + + verify(this.sessionSerializer, never()).serialize(any(), any(PdxWriter.class)); + } + + @Test + public void toDataWithNullReturnsFalse() { + + assertThat(this.sessionSerializer.toData(null, this.mockPdxWriter)).isFalse(); + + verify(this.sessionSerializer, never()).serialize(any(), any(PdxWriter.class)); + } + + @Test + public void fromDataWithSessionTypeReturnsSessionObject() { + + when(this.sessionSerializer.deserialize(eq(this.mockPdxReader))).thenReturn(this.mockSession); + + assertThat(this.sessionSerializer.fromData(Session.class, this.mockPdxReader)).isEqualTo(this.mockSession); + + verify(this.sessionSerializer, times(1)).canSerialize(eq(Session.class)); + verify(this.sessionSerializer, times(1)).deserialize(eq(this.mockPdxReader)); + } + + @Test + public void fromDataWithNonSerializableTypeReturnsNull() { + + when(this.sessionSerializer.canSerialize(any(Class.class))).thenReturn(false); + + assertThat(this.sessionSerializer.fromData(Object.class, this.mockPdxReader)).isNull(); + + verify(this.sessionSerializer, times(1)).canSerialize(eq(Object.class)); + verify(this.sessionSerializer, never()).deserialize(any(PdxReader.class)); + } + + @Test + public void fromDataWithNullReturnsNull() { + + assertThat(this.sessionSerializer.fromData(null, this.mockPdxReader)).isNull(); + + verify(this.sessionSerializer, never()).canSerialize(any()); + verify(this.sessionSerializer, never()).deserialize(any(PdxReader.class)); + } + + @Test + public void canSerializeSessionIsTrue() { + assertThat(this.sessionSerializer.canSerialize(Session.class)).isTrue(); + } + + @Test + public void canSerializeGemFireSessionIsTrue() { + assertThat(this.sessionSerializer.canSerialize(GemFireSession.class)).isTrue(); + } + + @Test + public void canSerializeObjectIsFalse() { + assertThat(this.sessionSerializer.canSerialize(Object.class)).isFalse(); + } + + @Test + public void canSerializeNullIsFalse() { + assertThat(this.sessionSerializer.canSerialize(null)).isFalse(); + } +} diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/pdx/provider/PdxSerializableSessionSerializerTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/pdx/provider/PdxSerializableSessionSerializerTests.java new file mode 100644 index 0000000..bb43968 --- /dev/null +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/pdx/provider/PdxSerializableSessionSerializerTests.java @@ -0,0 +1,165 @@ +/* + * 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 org.springframework.session.data.gemfire.serialization.pdx.provider; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.DeltaCapableGemFireSession; +import static org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSession; + +import java.time.Duration; +import java.time.Instant; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import org.apache.geode.pdx.PdxReader; +import org.apache.geode.pdx.PdxWriter; + +import org.springframework.session.FindByIndexNameSessionRepository; + +/** + * The PdxSerializableSessionSerializerTests class... + * + * @author John Blum + * @since 1.0.0 + */ +@RunWith(MockitoJUnitRunner.class) +public class PdxSerializableSessionSerializerTests { + + @Mock + private PdxReader mockPdxReader; + + @Mock + private PdxWriter mockPdxWriter; + + private PdxSerializableSessionSerializer sessionSerializer = new PdxSerializableSessionSerializer(); + + @Test + public void serializeSessionIsCorrect() { + + GemFireSession session = GemFireSession.create(); + + session.setMaxInactiveInterval(Duration.ofMinutes(30)); + session.setAttribute("attributeOne", "valueOne"); + session.setAttribute("attributeTwo", "valueTwo"); + + this.sessionSerializer.serialize(session, this.mockPdxWriter); + + verify(this.mockPdxWriter, times(1)) + .writeString(eq("id"), eq(session.getId())); + + verify(this.mockPdxWriter, times(1)) + .writeLong(eq("creationTime"), eq(session.getCreationTime().toEpochMilli())); + + verify(this.mockPdxWriter, times(1)) + .writeLong(eq("lastAccessedTime"), eq(session.getLastAccessedTime().toEpochMilli())); + + verify(this.mockPdxWriter, times(1)) + .writeLong(eq("maxInactiveIntervalInSeconds"), eq(session.getMaxInactiveInterval().getSeconds())); + + verify(this.mockPdxWriter, times(1)) + .writeString(eq("principalName"), eq(session.getPrincipalName())); + + verify(this.mockPdxWriter, times(1)) + .writeObject(eq("attributes"), eq(new HashMap<>(session.getAttributes()))); + } + + @Test + public void newMapCopiesMap() { + + Map mapCopy = this.sessionSerializer.newMap(Collections.singletonMap("testKey", "testValue")); + + assertThat(mapCopy).isInstanceOf(HashMap.class); + assertThat(mapCopy).hasSize(1); + assertThat(mapCopy).containsKey("testKey"); + assertThat(mapCopy.get("testKey")).isEqualTo("testValue"); + } + + @Test + @SuppressWarnings("unchecked") + public void deserializeSessionIsCorrect() { + + Duration expectedMaxInactiveInterval = Duration.ofMinutes(30); + + Instant expectedCreationTime = Instant.now(); + Instant expectedLastAccessedTime = Instant.now(); + + Map expectedAttributes = new HashMap<>(2); + + expectedAttributes.put("attributeOne", "valueOne"); + expectedAttributes.put("attributeTwo", "valueTwo"); + expectedAttributes.put(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, "jonDoe"); + + when(this.mockPdxReader.readString(eq("id"))).thenReturn("123"); + when(this.mockPdxReader.readLong(eq("creationTime"))).thenReturn(expectedCreationTime.toEpochMilli()); + + when(this.mockPdxReader.readLong(eq("lastAccessedTime"))) + .thenReturn(expectedLastAccessedTime.toEpochMilli()); + + when(this.mockPdxReader.readLong(eq("maxInactiveIntervalInSeconds"))) + .thenReturn(expectedMaxInactiveInterval.getSeconds()); + + when(this.mockPdxReader.readString(eq("principalName"))).thenReturn("jonDoe"); + when(this.mockPdxReader.readObject(eq("attributes"))).thenReturn(expectedAttributes); + + GemFireSession session = this.sessionSerializer.deserialize(this.mockPdxReader); + + assertThat(session).isNotNull(); + assertThat(session.getId()).isEqualTo("123"); + assertThat(session.getCreationTime()).isEqualTo(expectedCreationTime); + assertThat(session.getLastAccessedTime()).isEqualTo(expectedLastAccessedTime); + assertThat(session.getMaxInactiveInterval()).isEqualTo(expectedMaxInactiveInterval); + assertThat(session.getPrincipalName()).isEqualTo("jonDoe"); + assertThat(this.sessionSerializer.newMap(session.getAttributes())).isEqualTo(expectedAttributes); + + verify(this.mockPdxReader, times(1)).readString(eq("id")); + verify(this.mockPdxReader, times(1)).readLong(eq("creationTime")); + verify(this.mockPdxReader, times(1)).readLong(eq("lastAccessedTime")); + verify(this.mockPdxReader, times(1)).readLong(eq("maxInactiveIntervalInSeconds")); + verify(this.mockPdxReader, times(1)).readString(eq("principalName")); + verify(this.mockPdxReader, times(1)).readObject(eq("attributes")); + } + + @Test + public void canSerializeGemFireSessionClassTypeIsTrue() { + assertThat(this.sessionSerializer.canSerialize(GemFireSession.class)).isTrue(); + } + + @Test + public void canSerializeDeltaCapableGemFireSessionClassTypeIsTrue() { + assertThat(this.sessionSerializer.canSerialize(DeltaCapableGemFireSession.class)).isTrue(); + } + + @Test + public void canSerializeObjectClassTypeIsFalse() { + assertThat(this.sessionSerializer.canSerialize(Object.class)).isFalse(); + } + + @Test + public void canSerializeNullClassTypeIsFalse() { + assertThat(this.sessionSerializer.canSerialize(null)).isFalse(); + } +} diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/pdx/support/ComposablePdxSerializerTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/pdx/support/ComposablePdxSerializerTests.java new file mode 100644 index 0000000..71bdcd1 --- /dev/null +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/pdx/support/ComposablePdxSerializerTests.java @@ -0,0 +1,219 @@ +/* + * 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 org.springframework.session.data.gemfire.serialization.pdx.support; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import org.apache.geode.pdx.PdxReader; +import org.apache.geode.pdx.PdxSerializer; +import org.apache.geode.pdx.PdxWriter; + +/** + * Unit tests for {@link ComposablePdxSerializer}. + * + * @author John Blum + * @see org.junit.Test + * @see org.mockito.Mock + * @see org.mockito.Mockito + * @see org.apache.geode.pdx.PdxReader + * @see org.apache.geode.pdx.PdxSerializer + * @see org.apache.geode.pdx.PdxWriter + * @see org.springframework.session.data.gemfire.serialization.pdx.support.ComposablePdxSerializer + * @since 2.0.0 + */ +@RunWith(MockitoJUnitRunner.class) +public class ComposablePdxSerializerTests { + + @Mock + private PdxReader mockPdxReader; + + @Mock + private PdxWriter mockPdxWriter; + + @Mock + private PdxSerializer mockPdxSerializerOne; + + @Mock + private PdxSerializer mockPdxSerializerTwo; + + @Test + public void composeArrayWithTwoPdxSerializers() { + + PdxSerializer pdxSerializer = + ComposablePdxSerializer.compose(this.mockPdxSerializerOne, this.mockPdxSerializerTwo); + + assertThat(pdxSerializer).isInstanceOf(ComposablePdxSerializer.class); + assertThat((ComposablePdxSerializer) pdxSerializer) + .contains(this.mockPdxSerializerOne, this.mockPdxSerializerTwo); + } + + @Test + public void composeArrayWithOnePdxSerializer() { + assertThat(ComposablePdxSerializer.compose(this.mockPdxSerializerOne)).isSameAs(this.mockPdxSerializerOne); + } + + @Test + public void composeArrayWithNoPdxSerializers() { + assertThat(ComposablePdxSerializer.compose()).isNull(); + } + + @Test + public void composeIterableWithTwoPdxSerializers() { + + PdxSerializer pdxSerializer = + ComposablePdxSerializer.compose(Arrays.asList(this.mockPdxSerializerOne, this.mockPdxSerializerTwo)); + + assertThat(pdxSerializer).isInstanceOf(ComposablePdxSerializer.class); + assertThat((ComposablePdxSerializer) pdxSerializer) + .contains(this.mockPdxSerializerOne, this.mockPdxSerializerTwo); + } + + @Test + public void composeIterableWithOnePdxSerializer() { + assertThat(ComposablePdxSerializer.compose(Collections.singleton(this.mockPdxSerializerTwo))) + .isSameAs(this.mockPdxSerializerTwo); + } + + @Test + public void composeIterableWithNoPdxSerializers() { + assertThat(ComposablePdxSerializer.compose(Collections.emptyList())).isNull(); + } + + @Test + public void toDataSerializesObjectWithFirstPdxSerializer() { + + when(this.mockPdxSerializerOne.toData(any(), any(PdxWriter.class))).thenReturn(true); + + PdxSerializer pdxSerializer = + ComposablePdxSerializer.compose(this.mockPdxSerializerOne, this.mockPdxSerializerTwo); + + assertThat(pdxSerializer).isNotNull(); + assertThat(pdxSerializer.toData("test", this.mockPdxWriter)).isTrue(); + + verify(this.mockPdxSerializerOne, times(1)) + .toData(eq("test"), eq(this.mockPdxWriter)); + + verify(this.mockPdxSerializerTwo, never()).toData(any(), any(PdxWriter.class)); + } + + @Test + public void toDataSerializesObjectWithSecondPdxSerializer() { + + when(this.mockPdxSerializerOne.toData(any(), any(PdxWriter.class))).thenReturn(false); + when(this.mockPdxSerializerTwo.toData(any(), any(PdxWriter.class))).thenReturn(true); + + PdxSerializer pdxSerializer = + ComposablePdxSerializer.compose(this.mockPdxSerializerOne, this.mockPdxSerializerTwo); + + assertThat(pdxSerializer).isNotNull(); + assertThat(pdxSerializer.toData("test", this.mockPdxWriter)).isTrue(); + + verify(this.mockPdxSerializerOne, times(1)) + .toData(eq("test"), eq(this.mockPdxWriter)); + + verify(this.mockPdxSerializerTwo, times(1)) + .toData(eq("test"), eq(this.mockPdxWriter)); + } + + @Test + public void toDataCannotSerializeObject() { + + when(this.mockPdxSerializerOne.toData(any(), any(PdxWriter.class))).thenReturn(false); + when(this.mockPdxSerializerTwo.toData(any(), any(PdxWriter.class))).thenReturn(false); + + PdxSerializer pdxSerializer = + ComposablePdxSerializer.compose(this.mockPdxSerializerOne, this.mockPdxSerializerTwo); + + assertThat(pdxSerializer).isNotNull(); + assertThat(pdxSerializer.toData("test", this.mockPdxWriter)).isFalse(); + + verify(this.mockPdxSerializerOne, times(1)) + .toData(eq("test"), eq(this.mockPdxWriter)); + + verify(this.mockPdxSerializerTwo, times(1)) + .toData(eq("test"), eq(this.mockPdxWriter)); + } + + @Test + public void fromDataDeserializesObjectWithFirstPdxSerializer() { + + when(this.mockPdxSerializerOne.fromData(any(Class.class), any(PdxReader.class))).thenReturn("test"); + + PdxSerializer pdxSerializer = + ComposablePdxSerializer.compose(this.mockPdxSerializerOne, this.mockPdxSerializerTwo); + + assertThat(pdxSerializer).isNotNull(); + assertThat(pdxSerializer.fromData(Object.class, this.mockPdxReader)).isEqualTo("test"); + + verify(this.mockPdxSerializerOne, times(1)) + .fromData(eq(Object.class), eq(this.mockPdxReader)); + + verify(this.mockPdxSerializerTwo, never()).fromData(any(Class.class), any(PdxReader.class)); + } + + @Test + public void fromDataDeserializesObjectWithSecondPdxSerializer() { + + when(this.mockPdxSerializerOne.fromData(any(Class.class), any(PdxReader.class))).thenReturn(null); + when(this.mockPdxSerializerTwo.fromData(any(Class.class), any(PdxReader.class))).thenReturn("two"); + + PdxSerializer pdxSerializer = + ComposablePdxSerializer.compose(this.mockPdxSerializerOne, this.mockPdxSerializerTwo); + + assertThat(pdxSerializer).isNotNull(); + assertThat(pdxSerializer.fromData(Object.class, this.mockPdxReader)).isEqualTo("two"); + + verify(this.mockPdxSerializerOne, times(1)) + .fromData(eq(Object.class), eq(this.mockPdxReader)); + + verify(this.mockPdxSerializerTwo, times(1)) + .fromData(eq(Object.class), eq(this.mockPdxReader)); + } + + @Test + public void fromDataCannotDeserializeObject() { + + when(this.mockPdxSerializerOne.fromData(any(Class.class), any(PdxReader.class))).thenReturn(null); + when(this.mockPdxSerializerTwo.fromData(any(Class.class), any(PdxReader.class))).thenReturn(null); + + PdxSerializer pdxSerializer = + ComposablePdxSerializer.compose(this.mockPdxSerializerOne, this.mockPdxSerializerTwo); + + assertThat(pdxSerializer).isNotNull(); + assertThat(pdxSerializer.fromData(Object.class, this.mockPdxReader)).isNull(); + + verify(this.mockPdxSerializerOne, times(1)) + .fromData(eq(Object.class), eq(this.mockPdxReader)); + + verify(this.mockPdxSerializerTwo, times(1)) + .fromData(eq(Object.class), eq(this.mockPdxReader)); + } +} diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/pdx/support/PdxSerializerSessionSerializerAdapterTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/pdx/support/PdxSerializerSessionSerializerAdapterTests.java new file mode 100644 index 0000000..c9fe76c --- /dev/null +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/pdx/support/PdxSerializerSessionSerializerAdapterTests.java @@ -0,0 +1,139 @@ +/* + * 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 org.springframework.session.data.gemfire.serialization.pdx.support; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import org.apache.geode.pdx.PdxReader; +import org.apache.geode.pdx.PdxWriter; + +import org.springframework.session.Session; +import org.springframework.session.data.gemfire.serialization.SessionSerializer; + +/** + * Unit tests for {@link PdxSerializerSessionSerializerAdapter}. + * + * @author John Blum + * @see org.junit.Test + * @see org.mockito.Mock + * @see org.mockito.Mockito + * @see org.apache.geode.pdx.PdxReader + * @see org.apache.geode.pdx.PdxWriter + * @see org.springframework.session.Session + * @see org.springframework.session.data.gemfire.serialization.pdx.support.PdxSerializerSessionSerializerAdapter + * @since 2.0.0 + */ +@RunWith(MockitoJUnitRunner.class) +public class PdxSerializerSessionSerializerAdapterTests { + + @Mock + private PdxReader mockPdxReader; + + @Mock + private PdxWriter mockPdxWriter; + + @Mock + private Session mockSession; + + @Mock + private SessionSerializer mockSessionSerializer; + + @Test + public void constructPdxSerializerSessionSerializerAdapterWithSessionSerializer() { + + PdxSerializerSessionSerializerAdapter sessionSerializerAdapter = + new PdxSerializerSessionSerializerAdapter<>(this.mockSessionSerializer); + + assertThat(sessionSerializerAdapter).isNotNull(); + assertThat(sessionSerializerAdapter.getSessionSerializer()).isSameAs(this.mockSessionSerializer); + } + + @Test(expected = IllegalArgumentException.class) + public void constructPdxSerializerSessionSerializerAdapterWithNull() { + + try { + new PdxSerializerSessionSerializerAdapter<>(null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("SessionSerializer is required"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test + public void serializeSessionCallsSessionSerializerSerialize() { + + PdxSerializerSessionSerializerAdapter sessionSerializerAdapter = + new PdxSerializerSessionSerializerAdapter<>(this.mockSessionSerializer); + + sessionSerializerAdapter.serialize(this.mockSession, this.mockPdxWriter); + + verify(this.mockSessionSerializer, times(1)) + .serialize(eq(this.mockSession), eq(this.mockPdxWriter)); + } + + @Test + public void deserializeSessionCallsSessionSerializerDeserialize() { + + when(this.mockSessionSerializer.deserialize(any(PdxReader.class))).thenReturn(this.mockSession); + + PdxSerializerSessionSerializerAdapter sessionSerializerAdapter = + new PdxSerializerSessionSerializerAdapter<>(this.mockSessionSerializer); + + assertThat(sessionSerializerAdapter.deserialize(this.mockPdxReader)).isEqualTo(this.mockSession); + + verify(this.mockSessionSerializer, times(1)).deserialize(eq(this.mockPdxReader)); + } + + @Test + public void toDataSerializesSessionWithSessionSerializer() { + + PdxSerializerSessionSerializerAdapter sessionSerializerAdapter = + new PdxSerializerSessionSerializerAdapter<>(this.mockSessionSerializer); + + sessionSerializerAdapter.toData(this.mockSession, this.mockPdxWriter); + + verify(this.mockSessionSerializer, times(1)) + .serialize(eq(this.mockSession), eq(this.mockPdxWriter)); + } + + @Test + public void fromDataDeserializesSessionWithSessionSerializer() { + + when(this.mockSessionSerializer.deserialize(any(PdxReader.class))).thenReturn(this.mockSession); + + PdxSerializerSessionSerializerAdapter sessionSerializerAdapter = + new PdxSerializerSessionSerializerAdapter<>(this.mockSessionSerializer); + + assertThat(sessionSerializerAdapter.fromData(Session.class, this.mockPdxReader)).isEqualTo(this.mockSession); + + verify(this.mockSessionSerializer, times(1)).deserialize(eq(this.mockPdxReader)); + } +}