From 2561d036ee386f463eaf800e5e769ecd48ae8804 Mon Sep 17 00:00:00 2001 From: John Blum Date: Tue, 28 Nov 2017 20:58:27 -0800 Subject: [PATCH] Adapt to API changes in Spring Session core 2.0.0.RC2. --- ...actGemFireOperationsSessionRepository.java | 171 ++-- ...emFireCacheTypeAwareRegionFactoryBean.java | 7 +- .../data/gemfire/support/GemFireUtils.java | 42 +- .../data/gemfire/support/SessionIdHolder.java | 120 +++ ...mFireOperationsSessionRepositoryTests.java | 854 ++++++++++-------- .../gemfire/support/GemFireUtilsTests.java | 172 +++- .../gemfire/support/SessionIdHolderTests.java | 133 +++ 7 files changed, 1053 insertions(+), 446 deletions(-) create mode 100644 spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/SessionIdHolder.java create mode 100644 spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/SessionIdHolderTests.java diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepository.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepository.java index 1ade757..4181b04 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepository.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepository.java @@ -17,6 +17,7 @@ package org.springframework.session.data.gemfire; import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException; +import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException; import java.io.DataInput; import java.io.DataOutput; @@ -59,6 +60,7 @@ import org.springframework.session.Session; import org.springframework.session.SessionRepository; import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration; import org.springframework.session.data.gemfire.support.GemFireUtils; +import org.springframework.session.data.gemfire.support.SessionIdHolder; import org.springframework.session.events.SessionCreatedEvent; import org.springframework.session.events.SessionDeletedEvent; import org.springframework.session.events.SessionDestroyedEvent; @@ -79,9 +81,11 @@ import org.apache.commons.logging.LogFactory; * @see org.apache.geode.DataSerializer * @see org.apache.geode.Delta * @see org.apache.geode.Instantiator + * @see org.apache.geode.cache.EntryEvent + * @see org.apache.geode.cache.Operation * @see org.apache.geode.cache.Region - * @see org.apache.geode.cache.util.CacheListenerAdapter * @see org.springframework.beans.factory.InitializingBean + * @see org.springframework.context.ApplicationEvent * @see org.springframework.context.ApplicationEventPublisher * @see org.springframework.context.ApplicationEventPublisherAware * @see org.springframework.data.gemfire.GemfireOperations @@ -91,6 +95,10 @@ import org.apache.commons.logging.LogFactory; * @see org.springframework.session.SessionRepository * @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration * @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession + * @see org.springframework.session.events.SessionCreatedEvent + * @see org.springframework.session.events.SessionDeletedEvent + * @see org.springframework.session.events.SessionDestroyedEvent + * @see org.springframework.session.events.SessionExpiredEvent * @since 1.1.0 */ public abstract class AbstractGemFireOperationsSessionRepository extends CacheListenerAdapter @@ -130,13 +138,11 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi /** * Constructs a new instance of {@link Log} using Apache Commons {@link LogFactory}. * - * Used in testing to override the {@link Log} implementation with a mock. - * - * @return an instance of {@link Log} constructed from Apache commons-logging {@link LogFactory}. + * @return a new instance of {@link Log} constructed from Apache commons-logging {@link LogFactory}. * @see org.apache.commons.logging.LogFactory#getLog(Class) * @see org.apache.commons.logging.Log */ - Log newLogger() { + private Log newLogger() { return LogFactory.getLog(getClass()); } @@ -232,8 +238,11 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi * @see #getMaxInactiveInterval() */ public int getMaxInactiveIntervalInSeconds() { - return Optional.ofNullable(getMaxInactiveInterval()).map(Duration::getSeconds) - .map(Long::intValue).orElse(0); + + return Optional.ofNullable(getMaxInactiveInterval()) + .map(Duration::getSeconds) + .map(Long::intValue) + .orElse(0); } /** @@ -292,17 +301,17 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi /* (non-Javadoc) */ boolean isCreate(EntryEvent event) { - return (isCreate(event.getOperation()) && isNotUpdate(event) && isSessionOrNull(event.getNewValue())); + return isCreate(event.getOperation()) && isNotUpdate(event) && isSession(event.getNewValue()); } /* (non-Javadoc) */ private boolean isCreate(Operation operation) { - return (operation.isCreate() && !Operation.LOCAL_LOAD_CREATE.equals(operation)); + return operation.isCreate() && !Operation.LOCAL_LOAD_CREATE.equals(operation); } /* (non-Javadoc) */ private boolean isNotUpdate(EntryEvent event) { - return (isNotProxyRegion() || !this.cachedSessionIds.contains(ObjectUtils.nullSafeHashCode(event.getKey()))); + return isNotProxyRegion() || !this.cachedSessionIds.contains(ObjectUtils.nullSafeHashCode(event.getKey())); } /* (non-Javadoc) */ @@ -316,70 +325,120 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi } /** - * Used to determine whether the developer is storing (HTTP) Sessions with other, arbitrary application - * domain objects in the same GemFire cache {@link Region}; crazier things have happened! + * Used to determine whether the application developer is storing (HTTP) Sessions with other, arbitrary + * application domain objects in the same GemFire cache {@link Region}; crazier things have happened! * * @param obj {@link Object} to evaluate. - * @return a boolean value indicating whether the {@link Object} from the entry event is indeed - * a {@link Session}. + * @return a boolean value indicating whether the old/new {@link Object} from the {@link Region} + * {@link EntryEvent} is indeed a {@link Session}. * @see org.springframework.session.Session */ - private boolean isSessionOrNull(Object obj) { - return (obj instanceof Session || obj == null); + private boolean isSession(Object obj) { + return obj instanceof Session; } + /** + * Forgets the given {@link Object session ID}. + * + * @param sessionId {@link Object} containing the session ID to forget. + * @return a boolean value indicating whether the given session ID was even being remembered. + * @see #remember(Object) + */ boolean forget(Object sessionId) { return this.cachedSessionIds.remove(ObjectUtils.nullSafeHashCode(sessionId)); } + /** + * Rememvers the given {@link Object session ID}. + * + * @param sessionId {@link Object} containing the session ID to remember. + * @return a boolean value whether Spring Session is interested in and will remember + * this given session ID. + * @see #forget(Object) + */ @SuppressWarnings("all") boolean remember(Object sessionId) { - return (isProxyRegion() && this.cachedSessionIds.add(ObjectUtils.nullSafeHashCode(sessionId))); - } - - /* (non-Javadoc) */ - Session toSession(Object obj) { - return (obj instanceof Session ? (Session) obj : null); + return isProxyRegion() && this.cachedSessionIds.add(ObjectUtils.nullSafeHashCode(sessionId)); } + /** + * Casts the given {@link Object} into a {@link Session} iff the {@link Object} is a {@link Session}. + * + * Otherwise, this method attempts to use the supplied {@link String session ID} to create a {@link Session} + * containing only the ID. + * + * @param obj {@link Object} to evaluate as a {@link Session}. + * @param sessionId {@link String} containing the session ID. + * @return a {@link Session} from the given {@link Object} + * or a {@link Session} containing only the supplied {@link String session ID}. + * @throws IllegalStateException if the given {@link Object} is not a {@link Session} + * and {@link String session ID} was not supplied. + */ + Session toSession(Object obj, String sessionId) { + + return obj instanceof Session ? (Session) obj + : Optional.ofNullable(sessionId) + .filter(StringUtils::hasText) + .map(SessionIdHolder::create) + .orElseThrow(() -> newIllegalStateException( + "Minimally, the session ID [%s] must be known to trigger a Session event", sessionId)); + } /** * Callback method triggered when an entry is created in the GemFire cache {@link Region}. * - * @param event {@link EntryEvent} containing the details of the cache {@link Region} operation. + * @param event {@link EntryEvent} containing the details of the cache operation. * @see org.apache.geode.cache.EntryEvent * @see #handleCreated(String, Session) */ @Override public void afterCreate(EntryEvent event) { - if (isCreate(event)) { - handleCreated(event.getKey().toString(), toSession(event.getNewValue())); - } + + Optional.ofNullable(event) + .filter(this::isCreate) + .ifPresent(it -> { + + String sessionId = it.getKey().toString(); + + handleCreated(sessionId, toSession(it.getNewValue(), sessionId)); + }); } /** - * Callback method triggered when an entry is destroyed in the GemFire cache - * {@link Region}. + * Callback method triggered when an entry is destroyed in the GemFire cache {@link Region}. * - * @param event an EntryEvent containing the details of the cache operation. + * @param event {@link EntryEvent} containing the details of the cache operation. * @see org.apache.geode.cache.EntryEvent * @see #handleDestroyed(String, Session) */ @Override public void afterDestroy(EntryEvent event) { - handleDestroyed(event.getKey().toString(), toSession(event.getOldValue())); + + Optional.ofNullable(event) + .ifPresent(it -> { + + String sessionId = event.getKey().toString(); + + handleDestroyed(sessionId, toSession(event.getOldValue(), sessionId)); + }); } /** - * Callback method triggered when an entry is invalidated in the GemFire cache - * {@link Region}. + * Callback method triggered when an entry is invalidated in the GemFire cache {@link Region}. * - * @param event an EntryEvent containing the details of the cache operation. + * @param event {@link EntryEvent} containing the details of the cache operation. * @see org.apache.geode.cache.EntryEvent * @see #handleExpired(String, Session) */ @Override public void afterInvalidate(EntryEvent event) { - handleExpired(event.getKey().toString(), toSession(event.getOldValue())); + + Optional.ofNullable(event) + .ifPresent(it -> { + + String sessionId = event.getKey().toString(); + + handleExpired(sessionId, toSession(event.getOldValue(), sessionId)); + }); } /** @@ -402,12 +461,12 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi * @param session a reference to the Session triggering the event. * @see org.springframework.session.events.SessionCreatedEvent * @see org.springframework.session.Session - * @see #newSessionCreatedEvent(Session, String) + * @see #newSessionCreatedEvent(Session) * @see #publishEvent(ApplicationEvent) */ protected void handleCreated(String sessionId, Session session) { remember(sessionId); - publishEvent(newSessionCreatedEvent(session, sessionId)); + publishEvent(newSessionCreatedEvent(session)); } /** @@ -417,13 +476,13 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi * @param session a reference to the Session triggering the event. * @see org.springframework.session.events.SessionDeletedEvent * @see org.springframework.session.Session - * @see #newSessionDeletedEvent(Session, String) + * @see #newSessionDeletedEvent(Session) * @see #publishEvent(ApplicationEvent) * @see #forget(Object) */ protected void handleDeleted(String sessionId, Session session) { forget(sessionId); - publishEvent(newSessionDeletedEvent(session, sessionId)); + publishEvent(newSessionDeletedEvent(session)); } /** @@ -433,13 +492,13 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi * @param session a reference to the Session triggering the event. * @see org.springframework.session.events.SessionDestroyedEvent * @see org.springframework.session.Session - * @see #newSessionDestroyedEvent(Session, String) + * @see #newSessionDestroyedEvent(Session) * @see #publishEvent(ApplicationEvent) * @see #forget(Object) */ protected void handleDestroyed(String sessionId, Session session) { forget(sessionId); - publishEvent(newSessionDestroyedEvent(session, sessionId)); + publishEvent(newSessionDestroyedEvent(session)); } /** @@ -449,41 +508,33 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi * @param session a reference to the Session triggering the event. * @see org.springframework.session.events.SessionExpiredEvent * @see org.springframework.session.Session - * @see #newSessionExpiredEvent(Session, String) + * @see #newSessionExpiredEvent(Session) * @see #publishEvent(ApplicationEvent) * @see #forget(Object) */ protected void handleExpired(String sessionId, Session session) { forget(sessionId); - publishEvent(newSessionExpiredEvent(session, sessionId)); + publishEvent(newSessionExpiredEvent(session)); } /* (non-Javadoc) */ - private SessionCreatedEvent newSessionCreatedEvent(Session session, String sessionId) { - - return (session != null ? new SessionCreatedEvent(this, session) - : new SessionCreatedEvent(this, sessionId)); + private SessionCreatedEvent newSessionCreatedEvent(Session session) { + return new SessionCreatedEvent(this, session); } /* (non-Javadoc) */ - private SessionDeletedEvent newSessionDeletedEvent(Session session, String sessionId) { - - return (session != null ? new SessionDeletedEvent(this, session) - : new SessionDeletedEvent(this, sessionId)); + private SessionDeletedEvent newSessionDeletedEvent(Session session) { + return new SessionDeletedEvent(this, session); } /* (non-Javadoc) */ - private SessionDestroyedEvent newSessionDestroyedEvent(Session session, String sessionId) { - - return (session != null ? new SessionDestroyedEvent(this, session) - : new SessionDestroyedEvent(this, sessionId)); + private SessionDestroyedEvent newSessionDestroyedEvent(Session session) { + return new SessionDestroyedEvent(this, session); } /* (non-Javadoc) */ - private SessionExpiredEvent newSessionExpiredEvent(Session session, String sessionId) { - - return (session != null ? new SessionExpiredEvent(this, session) - : new SessionExpiredEvent(this, sessionId)); + private SessionExpiredEvent newSessionExpiredEvent(Session session) { + return new SessionExpiredEvent(this, session); } /** @@ -498,8 +549,8 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi try { getApplicationEventPublisher().publishEvent(event); } - catch (Throwable t) { - getLogger().error(String.format("Error occurred publishing event [%s]", event), t); + catch (Throwable cause) { + getLogger().error(String.format("Error occurred while publishing event [%s]", event), cause); } } diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/config/annotation/web/http/support/GemFireCacheTypeAwareRegionFactoryBean.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/config/annotation/web/http/support/GemFireCacheTypeAwareRegionFactoryBean.java index cde75ed..2e86e6d 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/config/annotation/web/http/support/GemFireCacheTypeAwareRegionFactoryBean.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/config/annotation/web/http/support/GemFireCacheTypeAwareRegionFactoryBean.java @@ -189,9 +189,10 @@ public class GemFireCacheTypeAwareRegionFactoryBean */ @SuppressWarnings("unchecked") protected Interest[] registerInterests(boolean register) { - return (!register ? new Interest[0] : new Interest[] { - new Interest<>("ALL_KEYS", InterestResultPolicy.KEYS) - }); + + return register + ? new Interest[] { new Interest<>("ALL_KEYS", InterestResultPolicy.KEYS) } + : new Interest[0]; } /** diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/GemFireUtils.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/GemFireUtils.java index 150c314..3ab7056 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/GemFireUtils.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/GemFireUtils.java @@ -18,21 +18,26 @@ package org.springframework.session.data.gemfire.support; import java.io.Closeable; import java.io.IOException; +import java.util.Optional; import org.apache.geode.cache.Cache; import org.apache.geode.cache.DataPolicy; import org.apache.geode.cache.GemFireCache; import org.apache.geode.cache.Region; +import org.apache.geode.cache.RegionAttributes; import org.apache.geode.cache.RegionShortcut; import org.apache.geode.cache.client.ClientCache; import org.apache.geode.cache.client.ClientRegionShortcut; import org.apache.geode.internal.cache.GemFireCacheImpl; /** - * GemFireUtils is an abstract, extensible utility class for working with GemFire types - * and functionality and is used by Spring Session's GemFire adapter support classes. + * {@link GemFireUtils} is an abstract, extensible utility class for working with GemFire objects and types. * * @author John Blum + * @see org.apache.geode.cache.Cache + * @see org.apache.geode.cache.GemFireCache + * @see org.apache.geode.cache.Region + * @see org.apache.geode.cache.client.ClientCache * @since 1.1.0 */ public abstract class GemFireUtils { @@ -62,14 +67,17 @@ public abstract class GemFireUtils { /** * Determines whether the GemFire cache is a client. * - * @param gemFireCache a reference to the GemFire cache. + * @param gemfireCache a reference to the GemFire cache. * @return a boolean value indicating whether the GemFire cache is a client. * @see org.apache.geode.cache.client.ClientCache * @see org.apache.geode.cache.GemFireCache */ - public static boolean isClient(GemFireCache gemFireCache) { - boolean client = (gemFireCache instanceof ClientCache); - client &= (!(gemFireCache instanceof GemFireCacheImpl) || ((GemFireCacheImpl) gemFireCache).isClient()); + public static boolean isClient(GemFireCache gemfireCache) { + + boolean client = (gemfireCache instanceof ClientCache); + + client &= (!(gemfireCache instanceof GemFireCacheImpl) || ((GemFireCacheImpl) gemfireCache).isClient()); + return client; } @@ -127,16 +135,28 @@ public abstract class GemFireUtils { } /** - * Determines whether the given {@link Region} is a PROXY, which would be indicated by the {@link Region} - * having a {@link DataPolicy} of {@link DataPolicy#EMPTY}. + * Determines whether the given {@link Region} is a {@literal PROXY}. * - * @param region {@link Region} to evaluate. - * @return a boolean value indicating whether the {@link Region} is a PROXY. + * @param region {@link Region} to evaluate as a {@literal PROXY}; must not be {@literal null}. + * @return a boolean value indicating whether the {@link Region} is a {@literal PROXY}. * @see org.apache.geode.cache.DataPolicy * @see org.apache.geode.cache.Region */ public static boolean isProxy(Region region) { - return DataPolicy.EMPTY.equals(region.getAttributes().getDataPolicy()); + + RegionAttributes regionAttributes = region.getAttributes(); + + DataPolicy regionDataPolicy = regionAttributes.getDataPolicy(); + + boolean proxy = DataPolicy.EMPTY.equals(regionDataPolicy); + + proxy |= proxy || Optional.ofNullable(regionDataPolicy) + .filter(DataPolicy.PARTITION::equals) + .map(it -> regionAttributes.getPartitionAttributes()) + .filter(partitionAttributes -> partitionAttributes.getLocalMaxMemory() <= 0) + .isPresent(); + + return proxy; } /** diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/SessionIdHolder.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/SessionIdHolder.java new file mode 100644 index 0000000..f2aa4ea --- /dev/null +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/support/SessionIdHolder.java @@ -0,0 +1,120 @@ +/* + * 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.support; + +import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException; + +import java.util.Optional; + +import org.apache.geode.cache.EntryEvent; +import org.apache.geode.cache.Operation; +import org.apache.geode.cache.Region; + +import org.springframework.session.Session; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; + +/** + * {@link SessionIdHolder} class is a Spring Session {@link Session} implementation that only holds + * the {@link String ID} of the {@link Session}. + * + * This implementation is only used in case Apache Geode or Pivotal GemFire returns a {@literal null} (old) value + * in a {@link Region} {@link EntryEvent} triggered by a {@link Operation#DESTROY} or {@link Operation#INVALIDATE} + * operation. + * + * @author John Blum + * @see org.apache.geode.cache.EntryEvent + * @see org.apache.geode.cache.Operation + * @see org.apache.geode.cache.Region + * @see org.springframework.session.data.gemfire.support.SessionIdHolder + * @since 2.0.0 + */ +public final class SessionIdHolder extends AbstractSession { + + private final String sessionId; + + /*** + * Factory method to create an instance of the {@link SessionIdHolder} initialized with + * the given {@link String session ID}. + * + * @param sessionId {@link String} containing the session ID used to initialize + * the new instance of {@link SessionIdHolder}. + * @return a new instance of {@link SessionIdHolder} initialized with + * the given {@link String session ID}. + * @throws IllegalArgumentException if session ID is {@literal null} or empty. + * @see #SessionIdHolder(String) + */ + public static SessionIdHolder create(String sessionId) { + return new SessionIdHolder(sessionId); + } + + /** + * Constructs a new instance of the {@link SessionIdHolder} initialized with + * the given {@link String session ID}. + * + * @param sessionId {@link String} containing the session ID used to initialize + * the new instance of {@link SessionIdHolder}. + * @throws IllegalArgumentException if session ID is {@literal null} or empty. + */ + public SessionIdHolder(String sessionId) { + + this.sessionId = Optional.ofNullable(sessionId) + .filter(StringUtils::hasText) + .orElseThrow(() -> newIllegalArgumentException("Session ID [%s] is required", sessionId)); + } + + /** + * Returns the {@link String ID} of this {@link Session}. + * + * @return the {@link String ID} of this {@link Session}. + */ + @Override + public String getId() { + return this.sessionId; + } + + @Override + public boolean equals(Object obj) { + + if (this == obj) { + return true; + } + + if (!(obj instanceof Session)) { + return false; + } + + Session that = (Session) obj; + + return ObjectUtils.nullSafeEquals(this.getId(), that.getId()); + } + + @Override + public int hashCode() { + + int hashValue = 17; + + hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getId()); + + return hashValue; + } + + @Override + public String toString() { + return getId(); + } +} diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepositoryTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepositoryTests.java index e9c0c5a..0c37cff 100644 --- a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepositoryTests.java +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepositoryTests.java @@ -17,7 +17,6 @@ package org.springframework.session.data.gemfire; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -25,14 +24,18 @@ import static org.mockito.ArgumentMatchers.isA; import static org.mockito.ArgumentMatchers.same; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willAnswer; -import static org.mockito.BDDMockito.willThrow; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; 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.DeltaCapableGemFireSessionAttributes; @@ -76,6 +79,7 @@ import org.springframework.data.gemfire.GemfireTemplate; import org.springframework.session.FindByIndexNameSessionRepository; import org.springframework.session.Session; import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration; +import org.springframework.session.data.gemfire.support.GemFireUtils; import org.springframework.session.events.AbstractSessionEvent; import org.springframework.session.events.SessionCreatedEvent; import org.springframework.session.events.SessionDeletedEvent; @@ -111,22 +115,21 @@ public class AbstractGemFireOperationsSessionRepositoryTests { private AbstractGemFireOperationsSessionRepository sessionRepository; - @Mock - private Session mockExpiringSession; - @Mock private Log mockLog; + @Mock + private Session mockSession; + @Before + @SuppressWarnings("all") public void setup() { - this.sessionRepository = spy(new TestGemFireOperationsSessionRepository(new GemfireTemplate()) { + GemfireTemplate gemfireTemplate = new GemfireTemplate(mock(Region.class)); - @Override - Log newLogger() { - return mockLog; - } - }); + this.sessionRepository = spy(new TestGemFireOperationsSessionRepository(gemfireTemplate)); + + doReturn(this.mockLog).when(this.sessionRepository).getLogger(); } @SuppressWarnings("unchecked") @@ -188,22 +191,9 @@ public class AbstractGemFireOperationsSessionRepositoryTests { return sessionRepository; } - @Test(expected = IllegalArgumentException.class) - public void constructGemFireOperationsSessionRepositoryWithNullTemplate() { - try { - new TestGemFireOperationsSessionRepository(null); - } - catch (IllegalArgumentException expected) { - assertThat(expected).hasMessage("GemfireOperations is required"); - assertThat(expected).hasNoCause(); - - throw expected; - } - } - @Test @SuppressWarnings("unchecked") - public void gemfireOperationsSessionRepositoryIsProperlyConstructedAndInitialized() throws Exception { + public void constructGemFireOperationsSessionRepositoryAndInitialize() throws Exception { ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); @@ -211,17 +201,16 @@ public class AbstractGemFireOperationsSessionRepositoryTests { Region mockRegion = mock(Region.class); - given(mockRegion.getFullPath()).willReturn("/Example"); - given(mockRegion.getAttributesMutator()).willReturn(mockAttributesMutator); + when(mockRegion.getAttributesMutator()).thenReturn(mockAttributesMutator); + when(mockRegion.getFullPath()).thenReturn(GemFireUtils.toRegionPath("Example")); GemfireTemplate template = new GemfireTemplate(mockRegion); AbstractGemFireOperationsSessionRepository sessionRepository = new TestGemFireOperationsSessionRepository(template); - ApplicationEventPublisher applicationEventPublisher = sessionRepository.getApplicationEventPublisher(); - - assertThat(applicationEventPublisher).isNotNull(); + assertThat(sessionRepository.getApplicationEventPublisher()).isNotNull(); + assertThat(sessionRepository.getApplicationEventPublisher()).isNotEqualTo(mockApplicationEventPublisher); assertThat(sessionRepository.getFullyQualifiedRegionName()).isNull(); assertThat(sessionRepository.getMaxInactiveIntervalInSeconds()) .isEqualTo(GemFireHttpSessionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS); @@ -232,7 +221,8 @@ public class AbstractGemFireOperationsSessionRepositoryTests { sessionRepository.afterPropertiesSet(); assertThat(sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); - assertThat(sessionRepository.getFullyQualifiedRegionName()).isEqualTo("/Example"); + assertThat(sessionRepository.getFullyQualifiedRegionName()) + .isEqualTo(GemFireUtils.toRegionPath("Example")); assertThat(sessionRepository.getMaxInactiveIntervalInSeconds()).isEqualTo(300); assertThat(sessionRepository.getTemplate()).isSameAs(template); @@ -241,8 +231,39 @@ public class AbstractGemFireOperationsSessionRepositoryTests { verify(mockAttributesMutator, times(1)).addCacheListener(same(sessionRepository)); } + @Test(expected = IllegalArgumentException.class) + public void constructGemFireOperationsSessionRepositoryWithNullTemplate() { + + try { + new TestGemFireOperationsSessionRepository(null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("GemfireOperations is required"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @SuppressWarnings("all") + @Test(expected = IllegalArgumentException.class) + public void setApplicationEventListenerToNull() { + + try { + this.sessionRepository.setApplicationEventPublisher(null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("ApplicationEventPublisher is required"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + @Test - public void maxInactiveIntervalInSecondsAllowsNegativeValuesAndExtremelyLargeValues() { + public void maxInactiveIntervalInSecondsAllowsExtremelyLargeAndNegativeValues() { assertThat(this.sessionRepository.getMaxInactiveIntervalInSeconds()) .isEqualTo(GemFireHttpSessionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS); @@ -267,261 +288,121 @@ public class AbstractGemFireOperationsSessionRepositoryTests { @Test public void isCreateWithCreateOperationReturnsTrue() { - EntryEvent mockEvent = - this.mockEntryEvent(Operation.CREATE, "123", null, this.mockExpiringSession); + EntryEvent mockEntryEvent = + mockEntryEvent(Operation.CREATE, "12345", null, this.mockSession); withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); - assertThat(this.sessionRepository.isCreate(mockEvent)).isTrue(); + assertThat(this.sessionRepository.isCreate(mockEntryEvent)).isTrue(); - verify(mockEvent, times(1)).getOperation(); - verify(mockEvent, times(1)).getKey(); - verify(mockEvent, times(1)).getNewValue(); - verify(mockEvent, never()).getOldValue(); + verify(mockEntryEvent, times(1)).getOperation(); + verify(mockEntryEvent, times(1)).getKey(); + verify(mockEntryEvent, times(1)).getNewValue(); + verify(mockEntryEvent, never()).getOldValue(); + verifyZeroInteractions(this.mockSession); } @Test public void isCreateWithCreateOperationAndNonProxyRegionReturnsTrue() { - EntryEvent mockEvent = - this.mockEntryEvent(Operation.CREATE, "123", null, null); + EntryEvent mockEntryEvent = + this.mockEntryEvent(Operation.CREATE, "12345", null, this.mockSession); withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.NORMAL)); - this.sessionRepository.remember("123"); + this.sessionRepository.remember("12345"); - assertThat(this.sessionRepository.isCreate(mockEvent)).isTrue(); + assertThat(this.sessionRepository.isCreate(mockEntryEvent)).isTrue(); - verify(mockEvent, times(1)).getOperation(); - verify(mockEvent, never()).getKey(); - verify(mockEvent, times(1)).getNewValue(); - verify(mockEvent, never()).getOldValue(); + verify(mockEntryEvent, times(1)).getOperation(); + verify(mockEntryEvent, never()).getKey(); + verify(mockEntryEvent, times(1)).getNewValue(); + verify(mockEntryEvent, never()).getOldValue(); + verifyZeroInteractions(this.mockSession); } @Test public void isCreateWithLocalLoadCreateOperationReturnsFalse() { - EntryEvent mockEvent = - this.mockEntryEvent(Operation.LOCAL_LOAD_CREATE, "123", null, this.mockExpiringSession); - - withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); - - assertThat(this.sessionRepository.isCreate(mockEvent)).isFalse(); - - verify(mockEvent, times(1)).getOperation(); - verify(mockEvent, never()).getKey(); - verify(mockEvent, never()).getNewValue(); - verify(mockEvent, never()).getOldValue(); - } - - @Test - public void isCreateWithUpdateOperationReturnsFalse() { - - EntryEvent mockEvent = - this.mockEntryEvent(Operation.UPDATE, "123", null, this.mockExpiringSession); - - withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); - - assertThat(this.sessionRepository.isCreate(mockEvent)).isFalse(); - - verify(mockEvent, times(1)).getOperation(); - verify(mockEvent, never()).getKey(); - verify(mockEvent, never()).getNewValue(); - verify(mockEvent, never()).getOldValue(); - } - - @Test - public void isCreateWithRememberedSessionIdReturnsFalse() { - - EntryEvent mockEvent = - this.mockEntryEvent(Operation.CREATE, "123", null, this.mockExpiringSession); - - withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); - - this.sessionRepository.remember("123"); - - assertThat(this.sessionRepository.isCreate(mockEvent)).isFalse(); - - verify(mockEvent, times(1)).getOperation(); - verify(mockEvent, times(1)).getKey(); - verify(mockEvent, never()).getNewValue(); - verify(mockEvent, never()).getOldValue(); - } - - @Test - public void isCreateWithTombstoneReturnsFalse() { - - EntryEvent mockEvent = - this.mockEntryEvent(Operation.CREATE, "123", null, new Tombstone()); - - withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); - - assertThat(this.sessionRepository.isCreate(mockEvent)).isFalse(); - - verify(mockEvent, times(1)).getOperation(); - verify(mockEvent, times(1)).getKey(); - verify(mockEvent, times(1)).getNewValue(); - verify(mockEvent, never()).getOldValue(); - } - - @Test - @SuppressWarnings("unchecked") - public void afterCreateWithSessionPublishesSessionCreatedEvent() { - - String sessionId = "abc123"; - Session mockSession = mock(Session.class); - - given(mockSession.getId()).willReturn(sessionId); - - ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - - willAnswer(invocation -> { - - ApplicationEvent applicationEvent = invocation.getArgument(0); - - assertThat(applicationEvent).isInstanceOf(SessionCreatedEvent.class); - - AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; - - assertThat(sessionEvent.getSource()) - .isEqualTo(AbstractGemFireOperationsSessionRepositoryTests.this.sessionRepository); - assertThat(sessionEvent.getSession()).isEqualTo(mockSession); - assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); - - return null; - }).given(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); - EntryEvent mockEntryEvent = - this.mockEntryEvent(Operation.CREATE, sessionId, null, mockSession); + this.mockEntryEvent(Operation.LOCAL_LOAD_CREATE, "12345", null, this.mockSession); withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); - this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - this.sessionRepository.afterCreate(mockEntryEvent); - - assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); - - verify(mockEntryEvent, times(1)).getOperation(); - verify(mockEntryEvent, times(2)).getKey(); - verify(mockEntryEvent, times(2)).getNewValue(); - verify(mockEntryEvent, never()).getOldValue(); - verify(mockSession, times(1)).getId(); - verify(mockApplicationEventPublisher, times(1)) - .publishEvent(isA(SessionCreatedEvent.class)); - } - - @Test - @SuppressWarnings("unchecked") - public void afterCreateWithSessionIdPublishesSessionCreatedEvent() { - - String sessionId = "abc123"; - - ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - - willAnswer(invocation -> { - - ApplicationEvent applicationEvent = invocation.getArgument(0); - - assertThat(applicationEvent).isInstanceOf(SessionCreatedEvent.class); - - AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; - - assertThat(sessionEvent.getSource()) - .isEqualTo(AbstractGemFireOperationsSessionRepositoryTests.this.sessionRepository); - assertThat(sessionEvent.getSession()).isNull(); - assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); - - return null; - }).given(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); - - EntryEvent mockEntryEvent = - this.mockEntryEvent(Operation.CREATE, sessionId, null, null); - - withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); - - this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - this.sessionRepository.afterCreate(mockEntryEvent); - - assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); - - verify(mockEntryEvent, times(1)).getOperation(); - verify(mockEntryEvent, times(2)).getKey(); - verify(mockEntryEvent, times(2)).getNewValue(); - verify(mockEntryEvent, never()).getOldValue(); - verify(mockApplicationEventPublisher, times(1)) - .publishEvent(isA(SessionCreatedEvent.class)); - } - - @Test - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void afterCreateForDestroyOperationDoesNotPublishSessionCreatedEvent() { - Region mockRegion = mockRegion("Example", DataPolicy.EMPTY); - - TestGemFireOperationsSessionRepository sessionRepository = - new TestGemFireOperationsSessionRepository(new GemfireTemplate(mockRegion)) { - - @Override - protected void handleCreated(String sessionId, Session session) { - fail("handleCreated(..) should not have been called"); - } - }; - - EntryEvent mockEntryEvent = - mockEntryEvent(Operation.DESTROY, null, null, null); - - sessionRepository.afterCreate(mockEntryEvent); + assertThat(this.sessionRepository.isCreate(mockEntryEvent)).isFalse(); verify(mockEntryEvent, times(1)).getOperation(); verify(mockEntryEvent, never()).getKey(); verify(mockEntryEvent, never()).getNewValue(); verify(mockEntryEvent, never()).getOldValue(); + verifyZeroInteractions(this.mockSession); } @Test - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void afterCreateForModificationDoesNotPublishSessionCreatedEvent() { - - Region mockRegion = mockRegion("Example", DataPolicy.EMPTY); - - TestGemFireOperationsSessionRepository sessionRepository = - new TestGemFireOperationsSessionRepository(new GemfireTemplate(mockRegion)) { - - @Override - protected void handleCreated(String sessionId, Session session) { - fail("handleCreated(..) should not have been called"); - } - }; + public void isCreateWithRememberedSessionIdReturnsFalse() { EntryEvent mockEntryEvent = - this.mockEntryEvent(Operation.CREATE, "123", null, null); + this.mockEntryEvent(Operation.CREATE, "12345", null, this.mockSession); - sessionRepository.remember("123"); - sessionRepository.afterCreate(mockEntryEvent); + withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); + + this.sessionRepository.remember("12345"); + + assertThat(this.sessionRepository.isCreate(mockEntryEvent)).isFalse(); verify(mockEntryEvent, times(1)).getOperation(); verify(mockEntryEvent, times(1)).getKey(); verify(mockEntryEvent, never()).getNewValue(); verify(mockEntryEvent, never()).getOldValue(); + verifyZeroInteractions(this.mockSession); } @Test - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void afterCreateForNonSessionTypeDoesNotPublishSessionCreatedEvent() { + public void isCreateWithUpdateOperationReturnsFalse() { - Region mockRegion = mockRegion("Example", DataPolicy.EMPTY); + Session mockOldValue = mock(Session.class); - TestGemFireOperationsSessionRepository sessionRepository = - new TestGemFireOperationsSessionRepository(new GemfireTemplate(mockRegion)) { + EntryEvent mockEntryEvent = + this.mockEntryEvent(Operation.UPDATE, "12345", mockOldValue, this.mockSession); - @Override - protected void handleCreated(String sessionId, Session session) { - fail("handleCreated(..) should not have been called"); - } - }; + withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); - EntryEvent mockEntryEvent = mockEntryEvent(Operation.CREATE, null, null, new Tombstone()); + assertThat(this.sessionRepository.isCreate(mockEntryEvent)).isFalse(); - sessionRepository.afterCreate((EntryEvent) mockEntryEvent); + verify(mockEntryEvent, times(1)).getOperation(); + verify(mockEntryEvent, never()).getKey(); + verify(mockEntryEvent, never()).getNewValue(); + verify(mockEntryEvent, never()).getOldValue(); + verifyZeroInteractions(mockOldValue); + verifyZeroInteractions(this.mockSession); + } + + @Test + public void isCreateWithTombstoneReturnsFalse() { + + EntryEvent mockEntryEvent = + this.mockEntryEvent(Operation.CREATE, "12345", null, new Tombstone()); + + withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); + + assertThat(this.sessionRepository.isCreate(mockEntryEvent)).isFalse(); + + verify(mockEntryEvent, times(1)).getOperation(); + verify(mockEntryEvent, times(1)).getKey(); + verify(mockEntryEvent, times(1)).getNewValue(); + verify(mockEntryEvent, never()).getOldValue(); + verifyZeroInteractions(this.mockSession); + } + + @Test + public void isCreateWithNullReturnsFalse() { + + EntryEvent mockEntryEvent = + this.mockEntryEvent(Operation.CREATE, "12345", null, null); + + withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); + + assertThat(this.sessionRepository.isCreate(mockEntryEvent)).isFalse(); verify(mockEntryEvent, times(1)).getOperation(); verify(mockEntryEvent, times(1)).getKey(); @@ -530,18 +411,252 @@ public class AbstractGemFireOperationsSessionRepositoryTests { } @Test - @SuppressWarnings("unchecked") - public void afterDestroyWithSessionPublishesSessionDestroyedEvent() { + public void toSessionWithSession() { + assertThat(this.sessionRepository.toSession(this.mockSession, "12345")).isSameAs(this.mockSession); + } - String sessionId = "def456"; + @Test + public void toSessionWithTombstoneAndSessionId() { - Session mockSession = mock(Session.class); + Tombstone tombstone = new Tombstone(); - given(mockSession.getId()).willReturn(sessionId); + Session session = this.sessionRepository.toSession(tombstone, "12345"); + + assertThat(session).isNotNull(); + assertThat(session).isNotSameAs(tombstone); + assertThat(session.getId()).isEqualTo("12345"); + } + + @Test(expected = IllegalStateException.class) + public void toSessionWithNullSessionAndEmptySessionId() { + + try { + this.sessionRepository.toSession(null, " "); + } + catch (IllegalStateException expected) { + + assertThat(expected).hasMessage("Minimally, the session ID [ ] must be known to trigger a Session event"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test(expected = IllegalStateException.class) + public void toSessionWithNullSessionAndNullSessionId() { + + try { + this.sessionRepository.toSession(null, null); + } + catch (IllegalStateException expected) { + + assertThat(expected).hasMessage("Minimally, the session ID [null] must be known to trigger a Session event"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test + public void afterCreateHandlesNullEntryEvent() { ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - willAnswer(invocation -> { + this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); + + assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); + + this.sessionRepository.afterCreate(null); + + verify(this.sessionRepository, never()).handleCreated(anyString(), any()); + verifyZeroInteractions(mockApplicationEventPublisher); + } + + @Test + @SuppressWarnings("unchecked") + public void afterCreateWithNewSessionPublishesSessionCreatedEvent() { + + String sessionId = "12345"; + + when(this.mockSession.getId()).thenReturn(sessionId); + + ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); + + doAnswer(invocation -> { + + ApplicationEvent applicationEvent = invocation.getArgument(0); + + assertThat(applicationEvent).isInstanceOf(SessionCreatedEvent.class); + + AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; + + assertThat(sessionEvent.getSession()).isEqualTo(this.mockSession); + assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); + assertThat(sessionEvent.getSource()) + .isEqualTo(AbstractGemFireOperationsSessionRepositoryTests.this.sessionRepository); + + return null; + + }).when(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); + + EntryEvent mockEntryEvent = + this.mockEntryEvent(Operation.CREATE, sessionId, null, this.mockSession); + + withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); + + this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); + + assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); + + this.sessionRepository.afterCreate(mockEntryEvent); + + verify(mockEntryEvent, times(1)).getOperation(); + verify(mockEntryEvent, times(2)).getKey(); + verify(mockEntryEvent, times(2)).getNewValue(); + verify(mockEntryEvent, never()).getOldValue(); + verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); + verify(this.mockSession, times(1)).getId(); + verify(this.sessionRepository, times(1)) + .handleCreated(eq(sessionId), eq(this.mockSession)); + verify(mockApplicationEventPublisher, times(1)) + .publishEvent(isA(SessionCreatedEvent.class)); + } + + @Test + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void afterCreateForCreateOperationDoesNotPublishSessionCreatedEventWhenSessionIdIsRemembered() { + + EntryEvent mockEntryEvent = + this.mockEntryEvent(Operation.CREATE, "12345", null, this.mockSession); + + withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); + + this.sessionRepository.remember("12345"); + this.sessionRepository.afterCreate(mockEntryEvent); + + verify(mockEntryEvent, times(1)).getOperation(); + verify(mockEntryEvent, times(1)).getKey(); + verify(mockEntryEvent, never()).getNewValue(); + verify(mockEntryEvent, never()).getOldValue(); + verifyZeroInteractions(this.mockSession); + verify(this.sessionRepository, never()).handleCreated(anyString(), any()); + } + + @Test + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void afterCreateForLocalLoadCreateOperationDoesNotPublishSessionCreatedEvent() { + + EntryEvent mockEntryEvent = + this.mockEntryEvent(Operation.LOCAL_LOAD_CREATE, "12345", null, this.mockSession); + + withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.REPLICATE)); + + this.sessionRepository.afterCreate(mockEntryEvent); + + verify(mockEntryEvent, times(1)).getOperation(); + verify(mockEntryEvent, never()).getKey(); + verify(mockEntryEvent, never()).getNewValue(); + verify(mockEntryEvent, never()).getOldValue(); + verifyZeroInteractions(this.mockSession); + verify(this.sessionRepository, never()).handleCreated(anyString(), any()); + } + + @Test + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void afterCreateForDestroyOperationDoesNotPublishSessionCreatedEvent() { + + EntryEvent mockEntryEvent = + mockEntryEvent(Operation.DESTROY, "12345", null, null); + + this.sessionRepository.afterCreate(mockEntryEvent); + + verify(mockEntryEvent, times(1)).getOperation(); + verify(mockEntryEvent, never()).getKey(); + verify(mockEntryEvent, never()).getNewValue(); + verify(mockEntryEvent, never()).getOldValue(); + verify(this.sessionRepository, never()).handleCreated(anyString(), any()); + } + + @Test + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void afterCreateForInvalidateOperationDoesNotPublishSessionCreatedEvent() { + + EntryEvent mockEntryEvent = + mockEntryEvent(Operation.INVALIDATE, "12345", null, this.mockSession); + + this.sessionRepository.afterCreate(mockEntryEvent); + + verify(mockEntryEvent, times(1)).getOperation(); + verify(mockEntryEvent, never()).getKey(); + verify(mockEntryEvent, never()).getNewValue(); + verify(mockEntryEvent, never()).getOldValue(); + verifyZeroInteractions(this.mockSession); + verify(this.sessionRepository, never()).handleCreated(anyString(), any()); + } + + @Test + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void afterCreateForUpdateOperationDoesNotPublishSessionCreatedEvent() { + + Session mockOldValue = mock(Session.class); + + EntryEvent mockEntryEvent = + mockEntryEvent(Operation.UPDATE, "12345", mockOldValue, this.mockSession); + + this.sessionRepository.afterCreate(mockEntryEvent); + + verify(mockEntryEvent, times(1)).getOperation(); + verify(mockEntryEvent, never()).getKey(); + verify(mockEntryEvent, never()).getNewValue(); + verify(mockEntryEvent, never()).getOldValue(); + verifyZeroInteractions(mockOldValue); + verifyZeroInteractions(this.mockSession); + verify(this.sessionRepository, never()).handleCreated(anyString(), any()); + } + + @Test + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void afterCreateWithTombstoneDoesNotPublishSessionCreatedEvent() { + + EntryEvent mockEntryEvent = mockEntryEvent(Operation.CREATE, "12345", null, new Tombstone()); + + withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); + + this.sessionRepository.afterCreate(mockEntryEvent); + + verify(mockEntryEvent, times(1)).getOperation(); + verify(mockEntryEvent, times(1)).getKey(); + verify(mockEntryEvent, times(1)).getNewValue(); + verify(mockEntryEvent, never()).getOldValue(); + verify(this.sessionRepository, never()).handleCreated(anyString(), any()); + } + + @Test + public void afterDestroyHandlesNullEntryEvent() { + + ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); + + this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); + + assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); + + this.sessionRepository.afterDestroy(null); + + verify(this.sessionRepository, never()).handleDestroyed(anyString(), any()); + verifyZeroInteractions(mockApplicationEventPublisher); + } + + @Test + @SuppressWarnings("unchecked") + public void afterDestroyWithSessionPublishesSessionDestroyedEvent() { + + String sessionId = "12345"; + + when(this.mockSession.getId()).thenReturn(sessionId); + + ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); + + doAnswer(invocation -> { ApplicationEvent applicationEvent = invocation.getArgument(0); @@ -549,26 +664,31 @@ public class AbstractGemFireOperationsSessionRepositoryTests { AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; + assertThat(sessionEvent.getSession()).isEqualTo(this.mockSession); + assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); assertThat(sessionEvent.getSource()) .isEqualTo(AbstractGemFireOperationsSessionRepositoryTests.this.sessionRepository); - assertThat(sessionEvent.getSession()).isEqualTo(mockSession); - assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); return null; - }).given(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); + + }).when(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); EntryEvent mockEntryEvent = - this.mockEntryEvent(Operation.DESTROY, sessionId, mockSession, null); + this.mockEntryEvent(Operation.DESTROY, sessionId, this.mockSession, null); this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - this.sessionRepository.afterDestroy(mockEntryEvent); assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); + this.sessionRepository.afterDestroy(mockEntryEvent); + verify(mockEntryEvent, times(1)).getKey(); verify(mockEntryEvent, never()).getNewValue(); verify(mockEntryEvent, times(1)).getOldValue(); - verify(mockSession, times(1)).getId(); + verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); + verify(this.mockSession, times(1)).getId(); + verify(this.sessionRepository, times(1)) + .handleDestroyed(eq(sessionId), isA(Session.class)); verify(mockApplicationEventPublisher, times(1)) .publishEvent(isA(SessionDestroyedEvent.class)); } @@ -577,11 +697,11 @@ public class AbstractGemFireOperationsSessionRepositoryTests { @SuppressWarnings("unchecked") public void afterDestroyWithSessionIdPublishesSessionDestroyedEvent() { - String sessionId = "def456"; + String sessionId = "12345"; ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - willAnswer(invocation -> { + doAnswer(invocation -> { ApplicationEvent applicationEvent = invocation.getArgument(0); @@ -589,38 +709,46 @@ public class AbstractGemFireOperationsSessionRepositoryTests { AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; + Session session = sessionEvent.getSession(); + + assertThat(session).isNotNull(); + assertThat(session.getId()).isEqualTo(sessionId); + assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); assertThat(sessionEvent.getSource()) .isEqualTo(AbstractGemFireOperationsSessionRepositoryTests.this.sessionRepository); - assertThat(sessionEvent.getSession()).isNull(); - assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); return null; - }).given(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); + + }).when(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); EntryEvent mockEntryEvent = this.mockEntryEvent(Operation.DESTROY, sessionId, null, null); this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - this.sessionRepository.afterDestroy(mockEntryEvent); assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); + this.sessionRepository.afterDestroy(mockEntryEvent); + verify(mockEntryEvent, times(1)).getKey(); verify(mockEntryEvent, never()).getNewValue(); verify(mockEntryEvent, times(1)).getOldValue(); + verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); + verify(this.sessionRepository, times(1)) + .handleDestroyed(eq(sessionId), isA(Session.class)); verify(mockApplicationEventPublisher, times(1)) .publishEvent(isA(SessionDestroyedEvent.class)); } @Test @SuppressWarnings({ "unchecked", "rawtypes" }) - public void afterDestroyWithNonSessionTypePublishesSessionDestroyedEventWithSessionId() { + public void afterDestroyWithTombstonePublishesSessionDestroyedEventWithSessionId() { - String sessionId = "def456"; + String sessionId = "12345"; ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - willAnswer(invocation -> { + doAnswer(invocation -> { ApplicationEvent applicationEvent = invocation.getArgument(0); @@ -628,41 +756,62 @@ public class AbstractGemFireOperationsSessionRepositoryTests { AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; + Session session = sessionEvent.getSession(); + + assertThat(session).isNotNull(); + assertThat(session.getId()).isEqualTo(sessionId); + assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); assertThat(sessionEvent.getSource()) .isEqualTo(AbstractGemFireOperationsSessionRepositoryTests.this.sessionRepository); - assertThat(sessionEvent.getSession()).isNull(); - assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); return null; - }).given(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); + + }).when(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); EntryEvent mockEntryEvent = mockEntryEvent(Operation.DESTROY, sessionId, new Tombstone(), null); this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - this.sessionRepository.afterDestroy((EntryEvent) mockEntryEvent); assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); + this.sessionRepository.afterDestroy((EntryEvent) mockEntryEvent); + verify(mockEntryEvent, times(1)).getKey(); verify(mockEntryEvent, never()).getNewValue(); verify(mockEntryEvent, times(1)).getOldValue(); + verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); + verify(this.sessionRepository, times(1)) + .handleDestroyed(eq(sessionId), isA(Session.class)); verify(mockApplicationEventPublisher, times(1)) .publishEvent(isA(SessionDestroyedEvent.class)); } + @Test + public void afterInvalidateHandlesNullEntryEvent() { + + ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); + + this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); + + assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); + + this.sessionRepository.afterInvalidate(null); + + verify(this.sessionRepository, never()).handleExpired(anyString(), any()); + verifyZeroInteractions(mockApplicationEventPublisher); + } + @Test @SuppressWarnings("unchecked") public void afterInvalidateWithSessionPublishesSessionExpiredEvent() { - String sessionId = "ghi789"; + String sessionId = "12345"; - Session mockSession = mock(Session.class); - - given(mockSession.getId()).willReturn(sessionId); + when(this.mockSession.getId()).thenReturn(sessionId); ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - willAnswer(invocation -> { + doAnswer(invocation -> { ApplicationEvent applicationEvent = invocation.getArgument(0); @@ -670,38 +819,44 @@ public class AbstractGemFireOperationsSessionRepositoryTests { AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; + assertThat(sessionEvent.getSession()).isEqualTo(this.mockSession); + assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); assertThat(sessionEvent.getSource()) .isEqualTo(AbstractGemFireOperationsSessionRepositoryTests.this.sessionRepository); - assertThat(sessionEvent.getSession()).isEqualTo(mockSession); - assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); return null; - }).given(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); + + }).when(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); EntryEvent mockEntryEvent = this.mockEntryEvent(Operation.INVALIDATE, sessionId, mockSession, null); this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - this.sessionRepository.afterInvalidate(mockEntryEvent); assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); + this.sessionRepository.afterInvalidate(mockEntryEvent); + verify(mockEntryEvent, times(1)).getKey(); verify(mockEntryEvent, never()).getNewValue(); verify(mockEntryEvent, times(1)).getOldValue(); - verify(mockSession, times(1)).getId(); - verify(mockApplicationEventPublisher, times(1)).publishEvent(isA(SessionExpiredEvent.class)); + verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); + verify(this.mockSession, times(1)).getId(); + verify(this.sessionRepository, times(1)) + .handleExpired(eq(sessionId), eq(this.mockSession)); + verify(mockApplicationEventPublisher, times(1)) + .publishEvent(isA(SessionExpiredEvent.class)); } @Test @SuppressWarnings("unchecked") public void afterInvalidateWithSessionIdPublishesSessionExpiredEvent() { - String sessionId = "ghi789"; + String sessionId = "12345"; ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - willAnswer(invocation -> { + doAnswer(invocation -> { ApplicationEvent applicationEvent = invocation.getArgument(0); @@ -709,38 +864,46 @@ public class AbstractGemFireOperationsSessionRepositoryTests { AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; + Session session = sessionEvent.getSession(); + + assertThat(session).isNotNull(); + assertThat(session.getId()).isEqualTo(sessionId); + assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); assertThat(sessionEvent.getSource()) .isEqualTo(AbstractGemFireOperationsSessionRepositoryTests.this.sessionRepository); - assertThat(sessionEvent.getSession()).isNull(); - assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); return null; - }).given(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); + + }).when(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); EntryEvent mockEntryEvent = this.mockEntryEvent(Operation.INVALIDATE, sessionId, null, null); this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - this.sessionRepository.afterInvalidate(mockEntryEvent); assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); + this.sessionRepository.afterInvalidate(mockEntryEvent); + verify(mockEntryEvent, times(1)).getKey(); verify(mockEntryEvent, never()).getNewValue(); verify(mockEntryEvent, times(1)).getOldValue(); + verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); + verify(this.sessionRepository, times(1)) + .handleExpired(eq(sessionId), isA(Session.class)); verify(mockApplicationEventPublisher, times(1)) .publishEvent(isA(SessionExpiredEvent.class)); } @Test @SuppressWarnings({ "unchecked", "rawtypes" }) - public void afterInvalidateWithNonSessionTypePublishesSessionExpiredEventWithSessionId() { + public void afterInvalidateWithTombstonePublishesSessionExpiredEventWithSessionId() { - String sessionId = "ghi789"; + String sessionId = "12345"; ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - willAnswer(invocation -> { + doAnswer(invocation -> { ApplicationEvent applicationEvent = invocation.getArgument(0); @@ -748,27 +911,32 @@ public class AbstractGemFireOperationsSessionRepositoryTests { AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; + Session session = sessionEvent.getSession(); + + assertThat(session).isNotNull(); + assertThat(session.getId()).isEqualTo(sessionId); + assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); assertThat(sessionEvent.getSource()) .isEqualTo(AbstractGemFireOperationsSessionRepositoryTests.this.sessionRepository); - assertThat(sessionEvent.getSession()).isNull(); - assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); return null; - }).given(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); - EntryEvent mockEntryEvent = mock(EntryEvent.class); + }).when(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); - given(mockEntryEvent.getKey()).willReturn(sessionId); - given(mockEntryEvent.getOldValue()).willReturn(new Tombstone()); + EntryEvent mockEntryEvent = mockEntryEvent(Operation.INVALIDATE, sessionId, new Tombstone(), null); this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - this.sessionRepository.afterInvalidate((EntryEvent) mockEntryEvent); assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); + this.sessionRepository.afterInvalidate((EntryEvent) mockEntryEvent); + verify(mockEntryEvent, times(1)).getKey(); verify(mockEntryEvent, never()).getNewValue(); verify(mockEntryEvent, times(1)).getOldValue(); + verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); + verify(this.sessionRepository, times(1)) + .handleExpired(eq(sessionId), isA(Session.class)); verify(mockApplicationEventPublisher, times(1)) .publishEvent(isA(SessionExpiredEvent.class)); } @@ -778,13 +946,12 @@ public class AbstractGemFireOperationsSessionRepositoryTests { String sessionId = "123456789"; - Session mockSession = mock(Session.class); - - given(mockSession.getId()).willReturn(sessionId); + when(this.mockSession.getId()).thenReturn(sessionId); ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); willAnswer(new Answer() { + int index = 0; Class[] expectedSessionTypes = { @@ -798,20 +965,20 @@ public class AbstractGemFireOperationsSessionRepositoryTests { AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; - assertThat(sessionEvent.getSource()) - .isEqualTo(AbstractGemFireOperationsSessionRepositoryTests.this.sessionRepository); assertThat(sessionEvent.getSession()).isEqualTo(mockSession); assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); + assertThat(sessionEvent.getSource()) + .isEqualTo(AbstractGemFireOperationsSessionRepositoryTests.this.sessionRepository); return null; } }).given(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); EntryEvent mockCreateEvent = - this.mockEntryEvent(Operation.CREATE, sessionId, null, mockSession); + this.mockEntryEvent(Operation.CREATE, sessionId, null, this.mockSession); EntryEvent mockExpireEvent = - this.mockEntryEvent(Operation.INVALIDATE, sessionId, mockSession, null); + this.mockEntryEvent(Operation.INVALIDATE, sessionId, this.mockSession, null); withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); @@ -831,7 +998,12 @@ public class AbstractGemFireOperationsSessionRepositoryTests { verify(mockExpireEvent, times(1)).getKey(); verify(mockExpireEvent, never()).getNewValue(); verify(mockExpireEvent, times(1)).getOldValue(); - verify(mockSession, times(3)).getId(); + verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); + verify(this.mockSession, times(3)).getId(); + verify(this.sessionRepository, times(2)) + .handleCreated(eq(sessionId), eq(this.mockSession)); + verify(this.sessionRepository, times(1)) + .handleExpired(eq(sessionId), eq(this.mockSession)); verify(mockApplicationEventPublisher, times(2)) .publishEvent(isA(SessionCreatedEvent.class)); verify(mockApplicationEventPublisher, times(1)) @@ -841,28 +1013,25 @@ public class AbstractGemFireOperationsSessionRepositoryTests { @Test public void deleteSessionCallsDeleteSessionId() { - Session mockSession = mock(Session.class); - doNothing().when(this.sessionRepository).deleteById(anyString()); - given(mockSession.getId()).willReturn("2"); + when(this.mockSession.getId()).thenReturn("2"); - assertThat(this.sessionRepository.delete(mockSession)).isNull(); + assertThat(this.sessionRepository.delete(this.mockSession)).isNull(); + verify(this.mockSession, times(1)).getId(); verify(this.sessionRepository, times(1)).deleteById(eq("2")); } @Test public void handleDeletedWithSessionPublishesSessionDeletedEvent() { - String sessionId = "abc123"; + String sessionId = "12345"; - Session mockSession = mock(Session.class); - - given(mockSession.getId()).willReturn(sessionId); + when(this.mockSession.getId()).thenReturn(sessionId); ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - willAnswer(invocation -> { + doAnswer(invocation -> { ApplicationEvent applicationEvent = invocation.getArgument(0); @@ -870,82 +1039,53 @@ public class AbstractGemFireOperationsSessionRepositoryTests { AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; + assertThat(sessionEvent.getSession()).isEqualTo(this.mockSession); + assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); assertThat(sessionEvent.getSource()) .isEqualTo(AbstractGemFireOperationsSessionRepositoryTests.this.sessionRepository); - assertThat(sessionEvent.getSession()).isEqualTo(mockSession); - assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); return null; - }).given(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); + + }).when(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - this.sessionRepository.handleDeleted(sessionId, mockSession); assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); - verify(mockSession, times(1)).getId(); + this.sessionRepository.handleDeleted(sessionId, this.mockSession); + + verify(this.mockSession, times(1)).getId(); + verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); verify(mockApplicationEventPublisher, times(1)) .publishEvent(isA(SessionDeletedEvent.class)); } - @Test - public void handleDeletedWithSessionIdPublishesSessionDeletedEvent() { - - String sessionId = "abc123"; - - ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - - willAnswer(invocation -> { - - ApplicationEvent applicationEvent = invocation.getArgument(0); - - assertThat(applicationEvent).isInstanceOf(SessionDeletedEvent.class); - - AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; - - assertThat(sessionEvent.getSource()).isEqualTo(AbstractGemFireOperationsSessionRepositoryTests.this.sessionRepository); - assertThat(sessionEvent.getSession()).isNull(); - assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); - - return null; - }).given(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); - - this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - this.sessionRepository.handleDeleted(sessionId, null); - - assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); - - verify(mockApplicationEventPublisher, times(1)).publishEvent(isA(SessionDeletedEvent.class)); - } - @Test public void publishEventHandlesThrowable() { + ApplicationEvent mockApplicationEvent = mock(ApplicationEvent.class); ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - willThrow(new IllegalStateException("test")).given(mockApplicationEventPublisher) + doThrow(new IllegalStateException("test")).when(mockApplicationEventPublisher) .publishEvent(any(ApplicationEvent.class)); this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - this.sessionRepository.publishEvent(mockApplicationEvent); assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); + this.sessionRepository.publishEvent(mockApplicationEvent); + verify(mockApplicationEventPublisher, times(1)).publishEvent(eq(mockApplicationEvent)); verify(this.mockLog, times(1)) - .error(eq(String.format("Error occurred publishing event [%s]", mockApplicationEvent)), + .error(eq(String.format("Error occurred while publishing event [%s]", mockApplicationEvent)), isA(IllegalStateException.class)); } @Test public void touchSetsLastAccessedTime() { - - Session mockSession = mock(Session.class); - - assertThat(this.sessionRepository.touch(mockSession)).isSameAs(mockSession); - - verify(mockSession, times(1)).setLastAccessedTime(any(Instant.class)); + assertThat(this.sessionRepository.touch(this.mockSession)).isSameAs(this.mockSession); + verify(this.mockSession, times(1)).setLastAccessedTime(any(Instant.class)); } @Test @@ -980,10 +1120,12 @@ public class AbstractGemFireOperationsSessionRepositoryTests { @Test(expected = IllegalArgumentException.class) public void constructGemFireSessionWithUnspecifiedId() { + try { new GemFireSession(" "); } catch (IllegalArgumentException expected) { + assertThat(expected).hasMessage("ID is required"); assertThat(expected).hasNoCause(); @@ -1029,10 +1171,12 @@ public class AbstractGemFireOperationsSessionRepositoryTests { @Test(expected = IllegalArgumentException.class) public void constructGemFireSessionWithNullSession() { + try { new GemFireSession((Session) null); } catch (IllegalArgumentException expected) { + assertThat(expected).hasMessage("The Session to copy cannot be null"); assertThat(expected).hasNoCause(); @@ -1739,11 +1883,11 @@ public class AbstractGemFireOperationsSessionRepositoryTests { throw new UnsupportedOperationException("Not Implemented"); } - public Map findByIndexNameAndIndexValue(String indexName, String indexValue) { + public Session findById(String id) { throw new UnsupportedOperationException("Not Implemented"); } - public Session findById(String id) { + public Map findByIndexNameAndIndexValue(String indexName, String indexValue) { throw new UnsupportedOperationException("Not Implemented"); } diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/GemFireUtilsTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/GemFireUtilsTests.java index da6aae0..fef0e21 100644 --- a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/GemFireUtilsTests.java +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/GemFireUtilsTests.java @@ -18,10 +18,11 @@ package org.springframework.session.data.gemfire.support; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; -import static org.mockito.BDDMockito.willThrow; +import static org.mockito.Mockito.doThrow; 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.Closeable; import java.io.IOException; @@ -31,6 +32,7 @@ import org.junit.Test; import org.apache.geode.cache.Cache; import org.apache.geode.cache.DataPolicy; import org.apache.geode.cache.GemFireCache; +import org.apache.geode.cache.PartitionAttributes; import org.apache.geode.cache.Region; import org.apache.geode.cache.RegionAttributes; import org.apache.geode.cache.RegionShortcut; @@ -38,34 +40,44 @@ import org.apache.geode.cache.client.ClientCache; import org.apache.geode.cache.client.ClientRegionShortcut; /** - * The GemFireUtilsTest class is a test suite of test cases testing the contract and - * functionality of the GemFireUtils utility class. + * Unit tests for {@link GemFireUtils}. * * @author John Blum * @since 1.1.0 * @see org.junit.Test * @see org.mockito.Mockito + * @see org.apache.geode.cache.Cache + * @see org.apache.geode.cache.GemFireCache + * @see org.apache.geode.cache.Region + * @see org.apache.geode.cache.client.ClientCache * @see org.springframework.session.data.gemfire.support.GemFireUtils */ public class GemFireUtilsTests { @Test - public void closeNonNullCloseableSuccessfullyReturnsTrue() throws IOException { + public void closeNonNullCloseableReturnsTrue() throws IOException { + Closeable mockCloseable = mock(Closeable.class); + assertThat(GemFireUtils.close(mockCloseable)).isTrue(); + verify(mockCloseable, times(1)).close(); } @Test - public void closeNonNullCloseableObjectThrowingIOExceptionReturnsFalse() throws IOException { + public void closeNonNullCloseableThrowingIOExceptionReturnsFalse() throws IOException { + Closeable mockCloseable = mock(Closeable.class); - willThrow(new IOException("test")).given(mockCloseable).close(); + + doThrow(new IOException("test")).when(mockCloseable).close(); + assertThat(GemFireUtils.close(mockCloseable)).isFalse(); + verify(mockCloseable, times(1)).close(); } @Test - public void closeNullCloseableObjectReturnsFalse() { + public void closeNullCloseableReturnsFalse() { assertThat(GemFireUtils.close(null)).isFalse(); } @@ -101,6 +113,7 @@ public class GemFireUtilsTests { @Test public void clientRegionShortcutIsLocal() { + assertThat(GemFireUtils.isLocal(ClientRegionShortcut.LOCAL)).isTrue(); assertThat(GemFireUtils.isLocal(ClientRegionShortcut.LOCAL_HEAP_LRU)).isTrue(); assertThat(GemFireUtils.isLocal(ClientRegionShortcut.LOCAL_OVERFLOW)).isTrue(); @@ -110,6 +123,7 @@ public class GemFireUtilsTests { @Test public void clientRegionShortcutIsNotLocal() { + assertThat(GemFireUtils.isLocal(ClientRegionShortcut.CACHING_PROXY)).isFalse(); assertThat(GemFireUtils.isLocal(ClientRegionShortcut.CACHING_PROXY_HEAP_LRU)).isFalse(); assertThat(GemFireUtils.isLocal(ClientRegionShortcut.CACHING_PROXY_OVERFLOW)).isFalse(); @@ -123,6 +137,7 @@ public class GemFireUtilsTests { @Test public void clientRegionShortcutIsNotProxy() { + assertThat(GemFireUtils.isProxy(ClientRegionShortcut.CACHING_PROXY)).isFalse(); assertThat(GemFireUtils.isProxy(ClientRegionShortcut.CACHING_PROXY_HEAP_LRU)).isFalse(); assertThat(GemFireUtils.isProxy(ClientRegionShortcut.CACHING_PROXY_OVERFLOW)).isFalse(); @@ -134,19 +149,14 @@ public class GemFireUtilsTests { } @Test - public void regionShortcutIsProxy() { - assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_PROXY)).isTrue(); - assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_PROXY_REDUNDANT)).isTrue(); - assertThat(GemFireUtils.isProxy(RegionShortcut.REPLICATE_PROXY)).isTrue(); - } + public void emptyRegionIsProxy() { - @Test - public void regionIsProxy() { Region mockRegion = mock(Region.class); + RegionAttributes mockRegionAttributes = mock(RegionAttributes.class); - given(mockRegion.getAttributes()).willReturn(mockRegionAttributes); - given(mockRegionAttributes.getDataPolicy()).willReturn(DataPolicy.EMPTY); + when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes); + when(mockRegionAttributes.getDataPolicy()).thenReturn(DataPolicy.EMPTY); assertThat(GemFireUtils.isProxy(mockRegion)).isTrue(); @@ -155,8 +165,54 @@ public class GemFireUtilsTests { } @Test - public void regionIsNotProxy() { + public void partitionRegionWithNoLocalMaxMemoryIsProxy() { + Region mockRegion = mock(Region.class); + + RegionAttributes mockRegionAttributes = mock(RegionAttributes.class); + + PartitionAttributes mockPartitionAttributes = mock(PartitionAttributes.class); + + when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes); + when(mockRegionAttributes.getDataPolicy()).thenReturn(DataPolicy.PARTITION); + when(mockRegionAttributes.getPartitionAttributes()).thenReturn(mockPartitionAttributes); + when(mockPartitionAttributes.getLocalMaxMemory()).thenReturn(0); + + assertThat(GemFireUtils.isProxy(mockRegion)).isTrue(); + + verify(mockRegion, times(1)).getAttributes(); + verify(mockRegionAttributes, times(1)).getDataPolicy(); + verify(mockRegionAttributes, times(1)).getPartitionAttributes(); + verify(mockPartitionAttributes, times(1)).getLocalMaxMemory(); + } + + @Test + public void partitionRegionWithNegativeLocalMaxMemoryIsProxy() { + + Region mockRegion = mock(Region.class); + + RegionAttributes mockRegionAttributes = mock(RegionAttributes.class); + + PartitionAttributes mockPartitionAttributes = mock(PartitionAttributes.class); + + when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes); + when(mockRegionAttributes.getDataPolicy()).thenReturn(DataPolicy.PARTITION); + when(mockRegionAttributes.getPartitionAttributes()).thenReturn(mockPartitionAttributes); + when(mockPartitionAttributes.getLocalMaxMemory()).thenReturn(-1); + + assertThat(GemFireUtils.isProxy(mockRegion)).isTrue(); + + verify(mockRegion, times(1)).getAttributes(); + verify(mockRegionAttributes, times(1)).getDataPolicy(); + verify(mockRegionAttributes, times(1)).getPartitionAttributes(); + verify(mockPartitionAttributes, times(1)).getLocalMaxMemory(); + } + + @Test + public void normalRegionIsNotProxy() { + + Region mockRegion = mock(Region.class); + RegionAttributes mockRegionAttributes = mock(RegionAttributes.class); given(mockRegion.getAttributes()).willReturn(mockRegionAttributes); @@ -168,8 +224,89 @@ public class GemFireUtilsTests { verify(mockRegionAttributes, times(1)).getDataPolicy(); } + @Test + public void partitionRegionWithLocalMaxMemoryIsNotProxy() { + + Region mockRegion = mock(Region.class); + + RegionAttributes mockRegionAttributes = mock(RegionAttributes.class); + + PartitionAttributes mockPartitionAttributes = mock(PartitionAttributes.class); + + when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes); + when(mockRegionAttributes.getDataPolicy()).thenReturn(DataPolicy.PARTITION); + when(mockRegionAttributes.getPartitionAttributes()).thenReturn(mockPartitionAttributes); + when(mockPartitionAttributes.getLocalMaxMemory()).thenReturn(1); + + assertThat(GemFireUtils.isProxy(mockRegion)).isFalse(); + + verify(mockRegion, times(1)).getAttributes(); + verify(mockRegionAttributes, times(1)).getDataPolicy(); + verify(mockRegionAttributes, times(1)).getPartitionAttributes(); + verify(mockPartitionAttributes, times(1)).getLocalMaxMemory(); + } + + @Test + public void partitionRegionWithNoPartitionAttributesIsNotProxy() { + + Region mockRegion = mock(Region.class); + + RegionAttributes mockRegionAttributes = mock(RegionAttributes.class); + + when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes); + when(mockRegionAttributes.getDataPolicy()).thenReturn(DataPolicy.PARTITION); + when(mockRegionAttributes.getPartitionAttributes()).thenReturn(null); + + assertThat(GemFireUtils.isProxy(mockRegion)).isFalse(); + + verify(mockRegion, times(1)).getAttributes(); + verify(mockRegionAttributes, times(1)).getDataPolicy(); + verify(mockRegionAttributes, times(1)).getPartitionAttributes(); + } + + @Test + public void preloadedRegionIsNotProxy() { + + Region mockRegion = mock(Region.class); + + RegionAttributes mockRegionAttributes = mock(RegionAttributes.class); + + given(mockRegion.getAttributes()).willReturn(mockRegionAttributes); + given(mockRegionAttributes.getDataPolicy()).willReturn(DataPolicy.PRELOADED); + + assertThat(GemFireUtils.isProxy(mockRegion)).isFalse(); + + verify(mockRegion, times(1)).getAttributes(); + verify(mockRegionAttributes, times(1)).getDataPolicy(); + } + + @Test + public void replicateRegionIsNotProxy() { + + Region mockRegion = mock(Region.class); + + RegionAttributes mockRegionAttributes = mock(RegionAttributes.class); + + given(mockRegion.getAttributes()).willReturn(mockRegionAttributes); + given(mockRegionAttributes.getDataPolicy()).willReturn(DataPolicy.REPLICATE); + + assertThat(GemFireUtils.isProxy(mockRegion)).isFalse(); + + verify(mockRegion, times(1)).getAttributes(); + verify(mockRegionAttributes, times(1)).getDataPolicy(); + } + + @Test + public void regionShortcutIsProxy() { + + assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_PROXY)).isTrue(); + assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_PROXY_REDUNDANT)).isTrue(); + assertThat(GemFireUtils.isProxy(RegionShortcut.REPLICATE_PROXY)).isTrue(); + } + @Test public void regionShortcutIsNotProxy() { + assertThat(GemFireUtils.isProxy(RegionShortcut.LOCAL)).isFalse(); assertThat(GemFireUtils.isProxy(RegionShortcut.LOCAL_HEAP_LRU)).isFalse(); assertThat(GemFireUtils.isProxy(RegionShortcut.LOCAL_OVERFLOW)).isFalse(); @@ -194,6 +331,7 @@ public class GemFireUtilsTests { @Test public void toRegionPath() { + assertThat(GemFireUtils.toRegionPath("A")).isEqualTo("/A"); assertThat(GemFireUtils.toRegionPath("Example")).isEqualTo("/Example"); assertThat(GemFireUtils.toRegionPath("/Example")).isEqualTo("//Example"); diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/SessionIdHolderTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/SessionIdHolderTests.java new file mode 100644 index 0000000..c6c0853 --- /dev/null +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/support/SessionIdHolderTests.java @@ -0,0 +1,133 @@ +/* + * 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.support; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; + +import org.springframework.session.Session; + +/** + * Unit tests for {@link SessionIdHolder}. + * + * @author John Blum + * @see org.junit.Test + * @see org.springframework.session.Session + * @see org.springframework.session.data.gemfire.support.SessionIdHolder + * @since 2.0.0 + */ +public class SessionIdHolderTests { + + @Test + public void createSessionIdHolderWithId() { + + SessionIdHolder session = SessionIdHolder.create("12345"); + + assertThat(session).isNotNull(); + assertThat(session.getId()).isEqualTo("12345"); + } + + @Test(expected = IllegalArgumentException.class) + public void createSessionIdHolderWithEmptyId() { + + try { + SessionIdHolder.create(" "); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("Session ID [ ] is required"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test(expected = IllegalArgumentException.class) + public void createSessionIdHolderWithNoId() { + + try { + SessionIdHolder.create(null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("Session ID [null] is required"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test + public void equalsWithSameSessionReturnsTrue() { + + Session session = SessionIdHolder.create("12345"); + + assertThat(session.equals(session)).isTrue(); + } + + @Test + public void equalsWithEqualSessionsReturnsTrue() { + + Session sessionOne = SessionIdHolder.create("12345"); + Session sessionTwo = SessionIdHolder.create("12345"); + + assertThat(sessionOne.equals(sessionTwo)).isTrue(); + } + + @Test + @SuppressWarnings("all") + public void equalsWithUnequalSessionsReturnsFalse() { + + Session sessionOne = SessionIdHolder.create("123"); + Session sessionTwo = SessionIdHolder.create("12345"); + + assertThat(sessionOne.equals(sessionTwo)).isFalse(); + assertThat(sessionTwo.equals(null)).isFalse(); + } + + @Test + public void hashCodeForSameSessionIsEqual() { + + Session session = SessionIdHolder.create("12345"); + + assertThat(session.hashCode()).isEqualTo(session.hashCode()); + } + + @Test + public void hashCodeWithEqualSessionsIsEqual() { + + Session sessionOne = SessionIdHolder.create("12345"); + Session sessionTwo = SessionIdHolder.create("12345"); + + assertThat(sessionOne.hashCode()).isEqualTo(sessionTwo.hashCode()); + } + + @Test + public void hashCodeForUnequalSessionsAreNotEqual() { + + Session sessionOne = SessionIdHolder.create("123"); + Session sessionTwo = SessionIdHolder.create("12345"); + + assertThat(sessionOne.hashCode()).isNotEqualTo(sessionTwo.hashCode()); + } + + @Test + public void toStringReturnsSessionId() { + assertThat(SessionIdHolder.create("12345").toString()).isEqualTo("12345"); + } +}