Add support for SessionChangedEvents.
Resolves gh-39.
This commit is contained in:
@@ -42,14 +42,15 @@ import org.apache.geode.cache.query.Index;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport;
|
||||
import org.springframework.data.gemfire.util.RegionUtils;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
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.events.AbstractSessionEvent;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -61,15 +62,16 @@ import org.springframework.util.StringUtils;
|
||||
* @see java.net.URL
|
||||
* @see java.time.Instant
|
||||
* @see org.junit.Test
|
||||
* @see org.apache.geode.cache.DataPolicy
|
||||
* @see org.apache.geode.cache.ExpirationAttributes
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see org.apache.geode.cache.query.Index
|
||||
* @see org.springframework.context.ApplicationEvent
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
* @see org.springframework.context.ApplicationListener
|
||||
* @see org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport
|
||||
* @see org.springframework.session.Session
|
||||
* @see org.springframework.session.SessionRepository
|
||||
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
|
||||
* @see org.springframework.session.events.AbstractSessionEvent
|
||||
* @since 1.1.0
|
||||
@@ -422,12 +424,12 @@ public abstract class AbstractGemFireIntegrationTests extends ForkingClientServe
|
||||
* @see org.springframework.context.ApplicationListener
|
||||
* @see org.springframework.session.events.AbstractSessionEvent
|
||||
*/
|
||||
public static class SessionEventListener implements ApplicationListener<AbstractSessionEvent> {
|
||||
public static class SessionEventListener implements ApplicationListener<ApplicationEvent> {
|
||||
|
||||
private volatile AbstractSessionEvent sessionEvent;
|
||||
private volatile ApplicationEvent sessionEvent;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends AbstractSessionEvent> T getSessionEvent() {
|
||||
public <T extends ApplicationEvent> T getSessionEvent() {
|
||||
|
||||
T sessionEvent = (T) this.sessionEvent;
|
||||
|
||||
@@ -436,11 +438,12 @@ public abstract class AbstractGemFireIntegrationTests extends ForkingClientServe
|
||||
return sessionEvent;
|
||||
}
|
||||
|
||||
public void onApplicationEvent(AbstractSessionEvent event) {
|
||||
@Override
|
||||
public void onApplicationEvent(@NonNull ApplicationEvent event) {
|
||||
this.sessionEvent = event;
|
||||
}
|
||||
|
||||
public <T extends AbstractSessionEvent> T waitForSessionEvent(long duration) {
|
||||
public <T extends ApplicationEvent> T waitForSessionEvent(long duration) {
|
||||
|
||||
waitOn(() -> SessionEventListener.this.sessionEvent != null, duration);
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.gemfire.config.annotation.web.http;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -34,11 +33,13 @@ import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.RegionShortcut;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.gemfire.config.annotation.PeerCacheApplication;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests;
|
||||
import org.springframework.session.data.gemfire.GemFireOperationsSessionRepository;
|
||||
import org.springframework.session.data.gemfire.events.SessionChangedEvent;
|
||||
import org.springframework.session.data.gemfire.support.GemFireUtils;
|
||||
import org.springframework.session.events.AbstractSessionEvent;
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
@@ -125,6 +126,24 @@ public class EnableGemFireHttpSessionEventsIntegrationTests extends AbstractGemF
|
||||
assertThat(createdSession.isExpired()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sessionChangedEvent() {
|
||||
|
||||
Session expectedSession = save(createSession());
|
||||
|
||||
ApplicationEvent sessionEvent = this.sessionEventListener.getSessionEvent();
|
||||
|
||||
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
|
||||
|
||||
expectedSession.setAttribute("testKey", "testValue");
|
||||
save(touch(expectedSession));
|
||||
|
||||
sessionEvent = this.sessionEventListener.waitForSessionEvent(500L);
|
||||
|
||||
assertThat(sessionEvent).isInstanceOf(SessionChangedEvent.class);
|
||||
assertThat(((SessionChangedEvent) sessionEvent).<Session>getSession()).isEqualTo(expectedSession);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getExistingNonExpiredSession() {
|
||||
|
||||
@@ -212,7 +231,7 @@ public class EnableGemFireHttpSessionEventsIntegrationTests extends AbstractGemF
|
||||
assertThat(sessionEvent).isInstanceOf(SessionDeletedEvent.class);
|
||||
assertThat(sessionEvent.<Session>getSession()).isEqualTo(expectedSession);
|
||||
assertThat(sessionEvent.getSessionId()).isEqualTo(expiredSession.getId());
|
||||
assertThat(this.gemfireSessionRepository.<Session>findById(sessionEvent.getSessionId())).isNull();
|
||||
assertThat(this.gemfireSessionRepository.findById(sessionEvent.getSessionId())).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -220,7 +239,7 @@ public class EnableGemFireHttpSessionEventsIntegrationTests extends AbstractGemF
|
||||
|
||||
String expectedSessionId = UUID.randomUUID().toString();
|
||||
|
||||
assertThat(this.gemfireSessionRepository.<Session>findById(expectedSessionId)).isNull();
|
||||
assertThat(this.gemfireSessionRepository.findById(expectedSessionId)).isNull();
|
||||
|
||||
this.gemfireSessionRepository.deleteById(expectedSessionId);
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
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.events.SessionChangedEvent;
|
||||
import org.springframework.session.data.gemfire.support.GemFireUtils;
|
||||
import org.springframework.session.data.gemfire.support.IsDirtyPredicate;
|
||||
import org.springframework.session.data.gemfire.support.SessionIdHolder;
|
||||
@@ -1541,6 +1542,25 @@ public abstract class AbstractGemFireOperationsSessionRepository
|
||||
.publishEvent(newSessionExpiredEvent(toSession(event.getOldValue(), it.getKey()))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method triggered when an entry is updated in the {@link Session} cache {@link Region}.
|
||||
*
|
||||
* @param event {@link EntryEvent} containing the details of the cache operation.
|
||||
* @see org.springframework.session.data.gemfire.events.SessionChangedEvent
|
||||
* @see org.springframework.session.Session
|
||||
* @see org.apache.geode.cache.EntryEvent
|
||||
* @see #newSessionChangedEvent(Session)
|
||||
* @see #publishEvent(ApplicationEvent)
|
||||
* @see #toSession(Object, Object)
|
||||
*/
|
||||
@Override
|
||||
public void afterUpdate(EntryEvent<Object, Session> event) {
|
||||
|
||||
Optional.ofNullable(event)
|
||||
.ifPresent(it -> getSessionRepository()
|
||||
.publishEvent(newSessionChangedEvent(toSession(event.getNewValue(), it.getKey()))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link SessionCreatedEvent} initialized with the given {@link Session},
|
||||
* using the {@link #getSessionRepository() SessionRepository} as the event source.
|
||||
@@ -1555,6 +1575,20 @@ public abstract class AbstractGemFireOperationsSessionRepository
|
||||
return new SessionCreatedEvent(getSessionRepository(), session);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link SessionChangedEvent} initialized with the given {@link Session},
|
||||
* using the {@link #getSessionRepository() SessionRepository} as the event source.
|
||||
*
|
||||
* @param session {@link Session} that is the subject of the {@link ApplicationEvent change event}.
|
||||
* @return a new {@link SessionChangedEvent}.
|
||||
* @see org.springframework.session.data.gemfire.events.SessionChangedEvent
|
||||
* @see org.springframework.session.Session
|
||||
* @see #getSessionRepository()
|
||||
*/
|
||||
protected SessionChangedEvent newSessionChangedEvent(Session session) {
|
||||
return new SessionChangedEvent(getSessionRepository(), session);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link SessionDeletedEvent} initialized with the given {@link Session},
|
||||
* using the {@link #getSessionRepository() SessionRepository} as the event source.
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.events;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.session.Session;
|
||||
|
||||
/**
|
||||
* {@link SessionChangedEvent} is a Spring {@link ApplicationEvent} fire when the {@link Session} state changes.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.context.ApplicationEvent
|
||||
* @see org.springframework.session.Session
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public class SessionChangedEvent extends ApplicationEvent {
|
||||
|
||||
private final Session session;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@link SessionChangedEvent} initialized with the given {@link Object source}
|
||||
* and {@link Session}.
|
||||
*
|
||||
* @param source {@link Object} referencing the source of the event.
|
||||
* @param session {@link Session} that changed.
|
||||
* @see org.springframework.session.Session
|
||||
*/
|
||||
public SessionChangedEvent(Object source, Session session) {
|
||||
|
||||
super(source);
|
||||
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link Session} that was changed.
|
||||
*
|
||||
* @param <S> {@link Class type} of {@link Session}.
|
||||
* @return a reference to the {@link Session} that is the subject of the change event.
|
||||
* @see org.springframework.session.Session
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S extends Session> S getSession() {
|
||||
return (S) this.session;
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,7 @@ import org.springframework.lang.Nullable;
|
||||
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.events.SessionChangedEvent;
|
||||
import org.springframework.session.data.gemfire.support.DeltaAwareDirtyPredicate;
|
||||
import org.springframework.session.data.gemfire.support.EqualsDirtyPredicate;
|
||||
import org.springframework.session.data.gemfire.support.GemFireOperationsSessionRepositorySupport;
|
||||
@@ -1401,6 +1402,41 @@ public class AbstractGemFireOperationsSessionRepositoryTests {
|
||||
verify(this.sessionRepository, never()).publishEvent(any(ApplicationEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void afterUpdateHandlesSessionPublishesSessionChangedEvent() {
|
||||
|
||||
SessionEventHandlerCacheListenerAdapter sessionEventHandler =
|
||||
spy(this.sessionRepository.newSessionEventHandler());
|
||||
|
||||
EntryEvent mockEntryEvent = mock(EntryEvent.class);
|
||||
|
||||
when(mockEntryEvent.getKey()).thenReturn("1");
|
||||
when(mockEntryEvent.getNewValue()).thenReturn(this.mockSession);
|
||||
|
||||
sessionEventHandler.afterUpdate(mockEntryEvent);
|
||||
|
||||
verify(mockEntryEvent, times(1)).getKey();
|
||||
verify(mockEntryEvent, times(1)).getNewValue();
|
||||
verify(mockEntryEvent, never()).getOldValue();
|
||||
verify(sessionEventHandler, times(1)).newSessionChangedEvent(eq(this.mockSession));
|
||||
verify(sessionEventHandler, times(1)).toSession(eq(this.mockSession), eq("1"));
|
||||
verify(this.sessionRepository, times(1)).publishEvent(isA(SessionChangedEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterUpdateHandlesNullEntryEventDoesNotPublishSessionChangedEvent() {
|
||||
|
||||
SessionEventHandlerCacheListenerAdapter sessionEventHandler =
|
||||
spy(this.sessionRepository.newSessionEventHandler());
|
||||
|
||||
sessionEventHandler.afterUpdate(null);
|
||||
|
||||
verify(sessionEventHandler, never()).newSessionChangedEvent(any(Session.class));
|
||||
verify(sessionEventHandler, never()).toSession(any(), any());
|
||||
verify(this.sessionRepository, never()).publishEvent(any(ApplicationEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sessionCreateCreateExpireRecreatePublishesSessionEventsCreateExpireCreate() {
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.events;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.session.Session;
|
||||
|
||||
/**
|
||||
* Unit Tests for {@link SessionChangedEvent}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.mockito.Mock
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.mockito.junit.MockitoJUnitRunner
|
||||
* @see org.springframework.session.Session
|
||||
* @since 2.2.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SessionChangedEventUnitTests {
|
||||
|
||||
@Mock
|
||||
private Session mockSession;
|
||||
|
||||
@Test
|
||||
public void constructSessionChangedEvent() {
|
||||
|
||||
Object source = new Object();
|
||||
|
||||
SessionChangedEvent event = new SessionChangedEvent(source, this.mockSession);
|
||||
|
||||
assertThat(event).isNotNull();
|
||||
assertThat(event.getSource()).isEqualTo(source);
|
||||
assertThat(event.<Session>getSession()).isEqualTo(this.mockSession);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user