Introduce usingDataSerialization boolean flag fixing bug when DataSerializer is not the DataSerializableSessionSerializer provided by SSDG.
Rename exposeSpringSessionGemFireConfigurationAsProperties() to exposeSpringSessionGemFireConfiguration().
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user