Add support for configuring Apache Geode/Pivotal GemFire's DataSerialization framework server-side when using Spring Session.
Resovles gh-11.
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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.serialization.data.support;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
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.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
|
||||
import static org.springframework.session.data.gemfire.serialization.data.support.DataSerializableSessionSerializerInitializer.InitializingGemFireOperationsSessionRepository;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.apache.geode.DataSerializer;
|
||||
import org.apache.geode.cache.Cache;
|
||||
import org.apache.geode.cache.CacheClosedException;
|
||||
import org.apache.geode.internal.InternalDataSerializer;
|
||||
|
||||
import org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests;
|
||||
import org.springframework.session.data.gemfire.serialization.data.provider.DataSerializableSessionAttributesSerializer;
|
||||
import org.springframework.session.data.gemfire.serialization.data.provider.DataSerializableSessionSerializer;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DataSerializableSessionSerializerInitializer}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.mockito.Mock
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.mockito.Spy
|
||||
* @see org.mockito.junit.MockitoJUnitRunner
|
||||
* @see org.apache.geode.DataSerializer
|
||||
* @see org.apache.geode.internal.InternalDataSerializer
|
||||
* @see org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests
|
||||
* @see org.springframework.session.data.gemfire.serialization.data.provider.DataSerializableSessionAttributesSerializer
|
||||
* @see org.springframework.session.data.gemfire.serialization.data.provider.DataSerializableSessionSerializer
|
||||
* @see org.springframework.session.data.gemfire.serialization.data.support.DataSerializableSessionSerializerInitializer
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DataSerializableSessionSerializerInitializerIntegrationTests extends AbstractGemFireIntegrationTests {
|
||||
|
||||
@Mock
|
||||
private Cache mockCache;
|
||||
|
||||
@Test
|
||||
public void constructDataSerializableSessionSerializerInitializerWithGemFireCache() {
|
||||
|
||||
DataSerializableSessionSerializerInitializer initializer =
|
||||
new DataSerializableSessionSerializerInitializer(this.mockCache);
|
||||
|
||||
assertThat(initializer.getGemFireCache().orElse(null)).isSameAs(this.mockCache);
|
||||
assertThat(initializer.getLogger()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructDataSerializableSessionSerializerInitializerWithNull() {
|
||||
|
||||
DataSerializableSessionSerializerInitializer initializer =
|
||||
new DataSerializableSessionSerializerInitializer(null);
|
||||
|
||||
assertThat(initializer.getGemFireCache().orElse(null)).isNull();
|
||||
assertThat(initializer.getLogger()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ofFactoryMethodInitializesDataSerializableSessionSerializerInitializer() {
|
||||
|
||||
DataSerializableSessionSerializerInitializer initializer =
|
||||
DataSerializableSessionSerializerInitializer.of(this.mockCache);
|
||||
|
||||
assertThat(initializer).isNotNull();
|
||||
assertThat(initializer.getGemFireCache().orElse(null)).isSameAs(this.mockCache);
|
||||
assertThat(initializer.getLogger()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initializeWithCacheAndPropertiesParametersSetGemFireCacheReferenceAndCallsDoInitialize() {
|
||||
|
||||
DataSerializableSessionSerializerInitializer initializer =
|
||||
spy(DataSerializableSessionSerializerInitializer.of(null));
|
||||
|
||||
doNothing().when(initializer).doInitialization();
|
||||
|
||||
assertThat(initializer).isNotNull();
|
||||
assertThat(initializer.getGemFireCache().orElse(null)).isNull();
|
||||
|
||||
initializer.initialize(this.mockCache, new Properties());
|
||||
|
||||
assertThat(initializer.getGemFireCache().orElse(null)).isSameAs(this.mockCache);
|
||||
|
||||
verify(initializer, times(1)).doInitialization();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doInitializationIsCorrect() {
|
||||
|
||||
assertThat(InitializingGemFireOperationsSessionRepository.INSTANCE.isDataSerializationConfigured()).isFalse();
|
||||
|
||||
DataSerializableSessionSerializerInitializer initializer =
|
||||
DataSerializableSessionSerializerInitializer.of(this.mockCache);
|
||||
|
||||
assertThat(initializer).isNotNull();
|
||||
assertThat(initializer.getGemFireCache().orElse(null)).isSameAs(this.mockCache);
|
||||
|
||||
initializer.doInitialization();
|
||||
|
||||
assertThat(InitializingGemFireOperationsSessionRepository.INSTANCE.isDataSerializationConfigured()).isTrue();
|
||||
|
||||
List<Class> registeredDataSerializerTypes =
|
||||
Arrays.stream(nullSafeArray(InternalDataSerializer.getSerializers(), DataSerializer.class))
|
||||
.map(Object::getClass)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertThat(registeredDataSerializerTypes)
|
||||
.containsExactly(DataSerializableSessionSerializer.class, DataSerializableSessionAttributesSerializer.class);
|
||||
}
|
||||
|
||||
@Test(expected = CacheClosedException.class)
|
||||
public void doInitializationWhenGemFireCacheIsNotPresentThrowsCacheClosedException() {
|
||||
|
||||
DataSerializableSessionSerializerInitializer initializer =
|
||||
spy(DataSerializableSessionSerializerInitializer.of(null));
|
||||
|
||||
assertThat(initializer).isNotNull();
|
||||
assertThat(initializer.getGemFireCache().orElse(null)).isNull();
|
||||
|
||||
try {
|
||||
initializer.doInitialization();
|
||||
}
|
||||
finally {
|
||||
|
||||
assertThat(initializer.getGemFireCache().orElse(null)).isNull();
|
||||
assertThat(InternalDataSerializer.getSerializers()).isEmpty();
|
||||
assertThat(InitializingGemFireOperationsSessionRepository.INSTANCE.isDataSerializationConfigured()).isFalse();
|
||||
|
||||
verify(initializer, times(1)).resolveGemFireCache();
|
||||
verify(initializer, never()).configureUseDataSerialization();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,6 +121,14 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
|
||||
|
||||
private String fullyQualifiedRegionName;
|
||||
|
||||
/**
|
||||
* Protected, default constructor used by extensions of {@link AbstractGemFireOperationsSessionRepository}
|
||||
* in order to affect and assess {@link SessionRepository} configuration and state.
|
||||
*/
|
||||
protected AbstractGemFireOperationsSessionRepository() {
|
||||
this.template = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of {@link AbstractGemFireOperationsSessionRepository}
|
||||
* with a required {@link GemfireOperations} instance used to perform Pivotal GemFire data access operations
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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.serialization.data.support;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.geode.DataSerializable;
|
||||
import org.apache.geode.DataSerializer;
|
||||
import org.apache.geode.cache.Cache;
|
||||
import org.apache.geode.cache.CacheClosedException;
|
||||
import org.apache.geode.cache.CacheFactory;
|
||||
import org.apache.geode.cache.Declarable;
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration;
|
||||
import org.springframework.session.data.gemfire.serialization.data.provider.DataSerializableSessionSerializer;
|
||||
import org.springframework.session.data.gemfire.support.GemFireOperationsSessionRepositorySupport;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Register the Spring Session for Apache Geode/Pivotal GemFire {@link DataSerializableSessionSerializer}
|
||||
* with Apache Geode/Pivotal GemFire's DataSerialization framework as the {@link DataSerializer} used to handle
|
||||
* de/serialization of the {@link Session}, the {@literal Session Attributes} and any application
|
||||
* domain model objects contained in the {@link Session} (if necessary).
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.util.Properties
|
||||
* @see org.apache.geode.DataSerializable
|
||||
* @see org.apache.geode.DataSerializer
|
||||
* @see org.apache.geode.cache.Cache
|
||||
* @see org.apache.geode.cache.CacheFactory
|
||||
* @see org.apache.geode.cache.Declarable
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.springframework.session.Session
|
||||
* @see org.springframework.session.data.gemfire.GemFireOperationsSessionRepository
|
||||
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
|
||||
* @see org.springframework.session.data.gemfire.serialization.data.provider.DataSerializableSessionSerializer
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DataSerializableSessionSerializerInitializer implements Declarable {
|
||||
|
||||
private static final String DEFAULT_SESSION_REGION_NAME =
|
||||
GemFireHttpSessionConfiguration.DEFAULT_SESSION_REGION_NAME;
|
||||
|
||||
private static final String SESSION_REGION_NAME_PROPERTY = "spring.session.data.gemfire.session.region.name";
|
||||
|
||||
private volatile GemFireCache gemfireCache;
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private String sessionRegionName;
|
||||
|
||||
/**
|
||||
* Factory method used to construct a new instance of {@link DataSerializableSessionSerializerInitializer}
|
||||
* initialized with the given, non-required {@link GemFireCache}.
|
||||
*
|
||||
* @param gemfireCache reference to the {@link GemFireCache} instance.
|
||||
* @return a new {@link DataSerializableSessionSerializerInitializer} initialized with the given, non-required
|
||||
* {@link GemFireCache}.
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see #DataSerializableSessionSerializerInitializer(GemFireCache)
|
||||
*/
|
||||
public static DataSerializableSessionSerializerInitializer of(@Nullable GemFireCache gemfireCache) {
|
||||
return new DataSerializableSessionSerializerInitializer(gemfireCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor used to construct a new, un-initialized instance of
|
||||
* {@link DataSerializableSessionSerializerInitializer}.
|
||||
*
|
||||
* For use in Apache Geode/Pivotal GemFire {@literal cache.xml}.
|
||||
*/
|
||||
public DataSerializableSessionSerializerInitializer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@link DataSerializableSessionSerializerInitializer} initialized with the given,
|
||||
* non-required {@link GemFireCache}.
|
||||
*
|
||||
* This constructor is meant to be used programmatically and users are encouraged to provide a reference to
|
||||
* the configured and initialized {@link GemFireCache} instance if available.
|
||||
*
|
||||
* @param gemfireCache reference to the {@link GemFireCache} instance. {@link GemFireCache} may be {@literal null}
|
||||
* in to order to "lazy" initialize or resolve the cache.
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
*/
|
||||
public DataSerializableSessionSerializerInitializer(@Nullable GemFireCache gemfireCache) {
|
||||
this.gemfireCache = gemfireCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link Optional} reference to the {@link GemFireCache}.
|
||||
*
|
||||
* The {@link GemFireCache} instance may be resolved lazily when the {@link #initialize(Cache, Properties)} method
|
||||
* is called, such as when processing {@literal cache.xml}.
|
||||
*
|
||||
* @return an {@link Optional} reference to the {@link GemFireCache}.
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see java.util.Optional
|
||||
*/
|
||||
protected Optional<GemFireCache> getGemFireCache() {
|
||||
return Optional.ofNullable(this.gemfireCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the configured {@link Logger} used to capture log events and messages.
|
||||
*
|
||||
* @return a reference to the configured {@link Logger} used for logging.
|
||||
* @see org.slf4j.Logger
|
||||
*/
|
||||
protected Logger getLogger() {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #doInitialization()
|
||||
*/
|
||||
@Override
|
||||
public void initialize(Cache cache, Properties parameters) {
|
||||
|
||||
this.gemfireCache = cache;
|
||||
|
||||
doInitialization();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the {@link GemFireCache} instance, configures Spring Session (for Apache Geode/Pivotal GemFire) to
|
||||
* enable and use the DataSerialization framework and finally, registers the {@link DataSerializer DataSerializers}
|
||||
* used by Spring Session and required by Apache Geode/Pivotal GemFire to de/serialize the {@link Session} objects
|
||||
* as {@link DataSerializable} {@link Class types}.
|
||||
*
|
||||
* @see #resolveGemFireCache()
|
||||
* @see #configureUseDataSerialization()
|
||||
* @see org.springframework.session.data.gemfire.serialization.data.provider.DataSerializableSessionSerializer#register()
|
||||
*/
|
||||
public void doInitialization() {
|
||||
|
||||
resolveGemFireCache();
|
||||
configureUseDataSerialization();
|
||||
DataSerializableSessionSerializer.register();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a reference to the {@link GemFireCache}.
|
||||
*
|
||||
* @return a resolved reference to the {@link GemFireCache}.
|
||||
* @throws CacheClosedException if the {@link GemFireCache} cannot be resolved.
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
*/
|
||||
protected GemFireCache resolveGemFireCache() {
|
||||
return getGemFireCache().orElseGet(CacheFactory::getAnyInstance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures Spring Session (for Apache Geode/Pivotal GemFire) to "use" Apache Geode/Pivotal GemFire's
|
||||
* DataSerialization framework and Delta capable {@link DataSerializable} Session objects.
|
||||
*
|
||||
* @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.DeltaCapableGemFireSession
|
||||
* @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.DeltaCapableGemFireSessionAttributes
|
||||
*/
|
||||
protected void configureUseDataSerialization() {
|
||||
InitializingGemFireOperationsSessionRepository.INSTANCE.setUseDataSerialization(true);
|
||||
}
|
||||
|
||||
static final class InitializingGemFireOperationsSessionRepository
|
||||
extends GemFireOperationsSessionRepositorySupport {
|
||||
|
||||
static final InitializingGemFireOperationsSessionRepository INSTANCE =
|
||||
new InitializingGemFireOperationsSessionRepository();
|
||||
|
||||
boolean isDataSerializationConfigured() {
|
||||
return isUsingDataSerialization();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.gemfire.GemfireOperations;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository;
|
||||
|
||||
/**
|
||||
* The GemFireOperationsSessionRepositorySupport class...
|
||||
*
|
||||
* @author John Blum
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public abstract class GemFireOperationsSessionRepositorySupport
|
||||
extends AbstractGemFireOperationsSessionRepository {
|
||||
|
||||
private static final String OPERATION_NOT_SUPPORTED = "Operation Not Supported";
|
||||
|
||||
protected GemFireOperationsSessionRepositorySupport() { }
|
||||
|
||||
public GemFireOperationsSessionRepositorySupport(GemfireOperations gemfireOperations) {
|
||||
super(gemfireOperations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Session> findByIndexNameAndIndexValue(String indexName, String indexValue) {
|
||||
throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session createSession() {
|
||||
throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(String id) {
|
||||
throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session findById(String id) {
|
||||
throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Session session) {
|
||||
throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user