Introduce usingDataSerialization boolean flag fixing bug when DataSerializer is not the DataSerializableSessionSerializer provided by SSDG.

Rename exposeSpringSessionGemFireConfigurationAsProperties() to exposeSpringSessionGemFireConfiguration().
This commit is contained in:
John Blum
2018-12-06 01:04:55 -08:00
parent 6093e48fea
commit 59b58727b0
3 changed files with 279 additions and 12 deletions

View File

@@ -0,0 +1,192 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.session.data.gemfire.config.annotation.web.http;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
import java.io.DataInput;
import java.io.DataOutput;
import java.util.Arrays;
import org.junit.Test;
import org.apache.geode.DataSerializer;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.internal.InternalDataSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.data.gemfire.config.annotation.ClientCacheApplication;
import org.springframework.data.gemfire.tests.integration.SpringApplicationContextIntegrationTestsSupport;
import org.springframework.data.gemfire.tests.mock.annotation.EnableGemFireMockObjects;
import org.springframework.session.data.gemfire.serialization.SessionSerializer;
import org.springframework.session.data.gemfire.serialization.data.AbstractDataSerializableSessionSerializer;
import org.springframework.session.data.gemfire.serialization.data.provider.DataSerializableSessionSerializer;
import org.springframework.session.data.gemfire.serialization.pdx.support.PdxSerializerSessionSerializerAdapter;
/**
* The GemFireHttpSessionConfigurationIntegrationTests class...
*
* @author John Blum
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class GemFireHttpSessionConfigurationIntegrationTests extends SpringApplicationContextIntegrationTestsSupport {
private void assertDataSerializerRegistered(DataSerializer dataSerializer) {
assertDataSerializerRegistered(dataSerializer.getClass());
}
private void assertDataSerializerRegistered(Class<? extends DataSerializer> dataSerializerType) {
assertThat(Arrays.stream(nullSafeArray(InternalDataSerializer.getSerializers(), DataSerializer.class))
.map(Object::getClass)
.filter(dataSerializerType::isAssignableFrom)
.findFirst()
.orElse(null)).isNotNull();
}
private void testUsesDataSerialization(Class<? extends DataSerializer> expectedDataSerializerType) {
GemFireHttpSessionConfiguration configuration =
getApplicationContext().getBean(GemFireHttpSessionConfiguration.class);
assertThat(configuration).isNotNull();
assertThat(configuration.isUsingDataSerialization()).isTrue();
GemFireCache gemfireCache = getApplicationContext().getBean(GemFireCache.class);
assertThat(gemfireCache).isNotNull();
assertThat(gemfireCache.getPdxSerializer()).isNull();
DataSerializer dataSerializer =
getApplicationContext().getBean(configuration.getSessionSerializerBeanName(), expectedDataSerializerType);
assertThat(dataSerializer).isInstanceOf(expectedDataSerializerType);
assertDataSerializerRegistered(dataSerializer);
}
@Test
public void usesDataSerializationWhenDataSerializableSessionSerializerConfigured() {
newApplicationContext(DataSerializableSessionSerializerConfiguration.class);
testUsesDataSerialization(DataSerializableSessionSerializer.class);
}
@Test
public void usesDataSerializationWhenTestDataSerializerConfigured() {
newApplicationContext(TestDataSerializerConfiguration.class);
testUsesDataSerialization(TestDataSerializer.class);
}
@Test
public void notUsingDataSerializationWhenPdxConfigured() {
newApplicationContext(TestSessionSerializerConfiguration.class);
GemFireHttpSessionConfiguration configuration =
getApplicationContext().getBean(GemFireHttpSessionConfiguration.class);
assertThat(configuration).isNotNull();
assertThat(configuration.isUsingDataSerialization()).isFalse();
GemFireCache gemfireCache = getApplicationContext().getBean(GemFireCache.class);
SessionSerializer testSessionSerializer =
getApplicationContext().getBean("TestSessionSerializer", SessionSerializer.class);
assertThat(gemfireCache).isNotNull();
assertThat(gemfireCache.getPdxSerializer()).isInstanceOf(PdxSerializerSessionSerializerAdapter.class);
assertThat(((PdxSerializerSessionSerializerAdapter<?>) gemfireCache.getPdxSerializer()).getSessionSerializer())
.isEqualTo(testSessionSerializer);
assertThat(Arrays.stream(nullSafeArray(InternalDataSerializer.getSerializers(), DataSerializer.class))
.filter(testSessionSerializer::equals)
.findAny()
.orElse(null)).isNull();
}
@ClientCacheApplication
@EnableGemFireMockObjects
@EnableGemFireHttpSession(
poolName = "DEFAULT",
sessionSerializerBeanName = GemFireHttpSessionConfiguration.SESSION_DATA_SERIALIZER_BEAN_NAME
)
static class DataSerializableSessionSerializerConfiguration { }
@ClientCacheApplication
@EnableGemFireMockObjects
@EnableGemFireHttpSession(poolName = "DEFAULT", sessionSerializerBeanName = "TestDataSerializer")
static class TestDataSerializerConfiguration {
@Bean("TestDataSerializer")
DataSerializer testDataSerializer() {
return new TestDataSerializer();
}
}
@ClientCacheApplication
@EnableGemFireMockObjects
@EnableGemFireHttpSession(poolName = "DEFAULT", sessionSerializerBeanName = "TestSessionSerializer")
static class TestSessionSerializerConfiguration {
@Bean("TestSessionSerializer")
SessionSerializer testSessionSerializer() {
return mock(SessionSerializer.class);
}
}
static class TestDataSerializer extends AbstractDataSerializableSessionSerializer<Object> {
@Override
public Class<?>[] getSupportedClasses() {
return new Class[] { Object.class };
}
@Override
public void serialize(Object session, DataOutput dataOutput) {
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public Object deserialize(DataInput dataInput) {
throw new UnsupportedOperationException("Not Implemented");
}
}
static class TestSessionSerializer implements SessionSerializer<Object, DataInput, DataOutput> {
@Override
public void serialize(Object session, DataOutput dataOutput) {
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public Object deserialize(DataInput dataInput) {
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public boolean canSerialize(Class<?> type) {
return false;
}
}
}

View File

@@ -115,6 +115,12 @@ import org.springframework.util.StringUtils;
@SuppressWarnings("unused")
public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionConfiguration implements ImportAware {
/**
* Indicates whether to employ Apache Geode/Pivotal's DataSerialization framework
* for {@link Session} de/serialization.
*/
public static final boolean DEFAULT_USE_DATA_SERIALIZATION = false;
/**
* Default maximum interval in seconds in which a {@link Session} can remain inactive before it expires.
*/
@@ -187,6 +193,8 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
*/
public static final String[] DEFAULT_INDEXABLE_SESSION_ATTRIBUTES = {};
private boolean usingDataSerialization = DEFAULT_USE_DATA_SERIALIZATION;
private int maxInactiveIntervalInSeconds = DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS;
private ClientRegionShortcut clientRegionShortcut = DEFAULT_CLIENT_REGION_SHORTCUT;
@@ -423,6 +431,17 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
.orElse(DEFAULT_SESSION_SERIALIZER_BEAN_NAME);
}
/**
* Set whether to use Apache Geode / Pivotal GemFire's DataSerialization framework
* for {@link Session} de/serialization.
*
* @param useDataSerialization boolean value indicating whether to use Apache Geode
* / Pivotal GemFire's DataSerialization framework for {@link Session} de/serialization.
*/
private void setUseDataSerialization(boolean useDataSerialization) {
this.usingDataSerialization = useDataSerialization;
}
/**
* Determine whether the configured serialization strategy is using Apache Geode / Pivotal GemFire's
* DataSerialization framework.
@@ -432,7 +451,9 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
* @see #getSessionSerializerBeanName()
*/
protected boolean isUsingDataSerialization() {
return SESSION_DATA_SERIALIZER_BEAN_NAME.equals(getSessionSerializerBeanName());
return this.usingDataSerialization
|| SESSION_DATA_SERIALIZER_BEAN_NAME.equals(getSessionSerializerBeanName());
}
/**
@@ -442,6 +463,7 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
* @param importMetadata {@link AnnotationMetadata} of the application class importing
* this {@link Configuration} class.
* @see org.springframework.core.type.AnnotationMetadata
* @see #applySpringSessionGemFireConfigurer()
*/
public void setImportMetadata(AnnotationMetadata importMetadata) {
@@ -490,7 +512,7 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
applySpringSessionGemFireConfigurer();
}
private void applySpringSessionGemFireConfigurer() {
void applySpringSessionGemFireConfigurer() {
resolveSpringSessionGemFireConfigurer()
.map(this::applyClientRegionShortcut)
@@ -620,12 +642,15 @@ public class GemFireHttpSessionConfiguration extends AbstractGemFireHttpSessionC
private void configureSerialization(CacheFactoryBean cacheFactoryBean, SessionSerializer sessionSerializer) {
if (sessionSerializer instanceof DataSerializer) {
if (sessionSerializer instanceof DataSerializableSessionSerializer) {
DataSerializableSessionSerializer.register();
}
else {
DataSerializer.register(sessionSerializer.getClass());
}
setUseDataSerialization(true);
}
else if (sessionSerializer instanceof PdxSerializer) {
cacheFactoryBean.setPdxSerializer(ComposablePdxSerializer.compose(

View File

@@ -17,9 +17,13 @@
package org.springframework.session.data.gemfire.config.annotation.web.http;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
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.verifyZeroInteractions;
@@ -43,6 +47,7 @@ import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
@@ -50,6 +55,7 @@ import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.GemfireOperations;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.data.gemfire.RegionAttributesFactoryBean;
import org.springframework.data.gemfire.util.ArrayUtils;
import org.springframework.session.Session;
import org.springframework.session.data.gemfire.GemFireOperationsSessionRepository;
import org.springframework.session.data.gemfire.config.annotation.web.http.support.GemFireCacheTypeAwareRegionFactoryBean;
@@ -86,8 +92,11 @@ public class GemFireHttpSessionConfigurationTests {
protected <T> T getField(Object obj, String fieldName) {
try {
Field field = resolveField(obj, fieldName);
field.setAccessible(true);
return (T) field.get(obj);
}
catch (NoSuchFieldException cause) {
@@ -115,11 +124,6 @@ public class GemFireHttpSessionConfigurationTests {
return field;
}
@SafeVarargs
private static <T> T[] toArray(T... array) {
return array;
}
@Before
public void setup() {
@@ -321,20 +325,23 @@ public class GemFireHttpSessionConfigurationTests {
Map<String, Object> annotationAttributes = new HashMap<>(4);
annotationAttributes.put("clientRegionShortcut", ClientRegionShortcut.CACHING_PROXY);
annotationAttributes.put("indexableSessionAttributes", toArray("one", "two", "three"));
annotationAttributes.put("exposeConfigurationAsProperties", Boolean.TRUE);
annotationAttributes.put("indexableSessionAttributes", ArrayUtils.asArray("one", "two", "three"));
annotationAttributes.put("maxInactiveIntervalInSeconds", 600);
annotationAttributes.put("poolName", "TestPool");
annotationAttributes.put("serverRegionShortcut", RegionShortcut.REPLICATE);
annotationAttributes.put("regionName", "TEST");
annotationAttributes.put("sessionExpirationPolicyBeanName", "testSessionExpirationPolicy");
annotationAttributes.put("sessionSerializerBeanName", "testSessionSerializer");
given(mockAnnotationMetadata.getAnnotationAttributes(eq(EnableGemFireHttpSession.class.getName())))
.willReturn(annotationAttributes);
when(mockAnnotationMetadata.getAnnotationAttributes(eq(EnableGemFireHttpSession.class.getName())))
.thenReturn(annotationAttributes);
this.gemfireConfiguration.setImportMetadata(mockAnnotationMetadata);
assertThat(this.gemfireConfiguration.getClientRegionShortcut()).isEqualTo(ClientRegionShortcut.CACHING_PROXY);
assertThat(this.gemfireConfiguration.getIndexableSessionAttributes()).isEqualTo(toArray("one", "two", "three"));
assertThat(this.gemfireConfiguration.getIndexableSessionAttributes())
.isEqualTo(ArrayUtils.asArray("one", "two", "three"));
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(600);
assertThat(this.gemfireConfiguration.getPoolName()).isEqualTo("TestPool");
assertThat(this.gemfireConfiguration.getServerRegionShortcut()).isEqualTo(RegionShortcut.REPLICATE);
@@ -342,8 +349,51 @@ public class GemFireHttpSessionConfigurationTests {
assertThat(this.gemfireConfiguration.getSessionSerializerBeanName()).isEqualTo("testSessionSerializer");
verify(mockAnnotationMetadata, times(1))
.getAnnotationAttributes(eq(EnableGemFireHttpSession.class.getName()));
.getAnnotationAttributes(eq(EnableGemFireHttpSession.class.getName())); }
@Test
public void applyConfigurationFromNonExistingSpringSessionGemFireConfigurer() {
this.gemfireConfiguration.applySpringSessionGemFireConfigurer();
verify(this.gemfireConfiguration, never()).setClientRegionShortcut(any(ClientRegionShortcut.class));
verify(this.gemfireConfiguration, never()).setIndexableSessionAttributes(any(String[].class));
verify(this.gemfireConfiguration, never()).setMaxInactiveIntervalInSeconds(anyInt());
verify(this.gemfireConfiguration, never()).setPoolName(anyString());
verify(this.gemfireConfiguration, never()).setServerRegionShortcut(any(RegionShortcut.class));
verify(this.gemfireConfiguration, never()).setSessionRegionName(anyString());
verify(this.gemfireConfiguration, never()).setSessionSerializerBeanName(anyString());
}
@Test(expected = NoUniqueBeanDefinitionException.class)
public void applyConfigurationFromMultipleSpringSessionGemFireConfigurersThrowsException() {
ApplicationContext mockApplicationContext = mock(ApplicationContext.class);
when(mockApplicationContext.getBean(eq(SpringSessionGemFireConfigurer.class)))
.thenThrow(new NoUniqueBeanDefinitionException(SpringSessionGemFireConfigurer.class, 2, "TEST"));
this.gemfireConfiguration.setApplicationContext(mockApplicationContext);
try {
this.gemfireConfiguration.applySpringSessionGemFireConfigurer();
}
catch (NoUniqueBeanDefinitionException expected) {
assertThat(expected).hasMessageContaining("TEST");
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(this.gemfireConfiguration, never()).setClientRegionShortcut(any(ClientRegionShortcut.class));
verify(this.gemfireConfiguration, never()).setIndexableSessionAttributes(any(String[].class));
verify(this.gemfireConfiguration, never()).setMaxInactiveIntervalInSeconds(anyInt());
verify(this.gemfireConfiguration, never()).setPoolName(anyString());
verify(this.gemfireConfiguration, never()).setServerRegionShortcut(any(RegionShortcut.class));
verify(this.gemfireConfiguration, never()).setSessionRegionName(anyString());
verify(this.gemfireConfiguration, never()).setSessionSerializerBeanName(anyString());
}
}
@Test