Skip to content

Commit

Permalink
Fix SessionCreatedEvent not fired correctly
Browse files Browse the repository at this point in the history
- add relevant test
- use the MapListener for all supported events: `SessionCreatedEvent`, `SessionExpiredEvent`, `SessionDeletedEvent`
- remove CoherenceSessionCreatedEventHandler
- rename SessionRemovedMapListener to CoherenceSessionEventMapListener

backport of afd6efe
  • Loading branch information
ghillert committed Jul 8, 2024
1 parent 0412db4 commit 92798da
Show file tree
Hide file tree
Showing 12 changed files with 147 additions and 133 deletions.
20 changes: 17 additions & 3 deletions coherence-spring-docs/src/main/asciidoc/spring-session.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -354,14 +354,14 @@ The invoked `HelloController` will display a simple counter that is incremented
stored in the HTTP Session.

[[spring-session-expiration-strategies]]
=== Session Expiration Strategies
== Session Expiration Strategies

When dealing with the expiration of cache entries, you generally have 2 options in Coherence:

- Set the expiration time for each put operation explicitly
- Configure cache expiration on a per-cache-basis in your `coherence-cache-config.xml` file

==== Setting the Expiration Time for Each Put Operation
=== Setting the Expiration Time for Each Put Operation

When you define a session timeout via the application, for example `@EnableCoherenceHttpSession(sessionTimeoutInSeconds = 1000)`,
the session expiration will be set for each put-operation in `CoherenceIndexedSessionRepository`.
Expand Down Expand Up @@ -389,7 +389,7 @@ public static SessionRepositoryCustomizer<CoherenceIndexedSessionRepository> ses
If you are using Spring Boot, you can also set the session timeout in your `application.properties` or
`application.yaml` file using the `spring.session.timeout` property: `spring.session.timeout = 10m`.

==== Configuring the Expiration Time in the `coherence-cache-config.xml`
=== Configuring the Expiration Time in `coherence-cache-config.xml`

If you rather prefer defining the session expiration timeouts in your `coherence-cache-config.xml` file, you
should set the session timeout in the application to `0`, for instance `@EnableCoherenceHttpSession(sessionTimeoutInSeconds = 0)`.
Expand All @@ -406,3 +406,17 @@ on _Controlling the Growth of a Local Cache_ in the Coherence reference guide.
IMPORTANT: The underlying expiry delay parameter in Coherence is defined as an integer and is expressed in milliseconds.
Therefore, the maximum amount of time can never exceed Integer.MAX_VALUE (2147483647) milliseconds or approximately 24
days.

== Using HttpSessionListener

If you plan on use the `HttpSessionListener` interface, you can register a `HttpSessionListener` bean in your Spring
configuration. The `HttpSessionListener` will be registered with the `ServletContext` and will be notified of session
creation and destruction events.

[source,java,indent=1,subs="verbatim,quotes,attributes"]
----
@Bean
MyHttpSessionListener myHttpSessionListener() {
return new MyHttpSessionListener();
}
----
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2023, Oracle and/or its affiliates.
* Copyright (c) 2021, 2024, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* https://oss.oracle.com/licenses/upl.
Expand All @@ -12,12 +12,10 @@
import java.util.Map;
import java.util.Set;

import com.oracle.coherence.spring.session.events.CoherenceSessionCreatedEventHandler;
import com.oracle.coherence.spring.session.events.SessionRemovedMapListener;
import com.oracle.coherence.spring.session.events.CoherenceSessionEventMapListener;
import com.oracle.coherence.spring.session.support.PrincipalNameExtractor;
import com.tangosol.net.NamedCache;
import com.tangosol.net.cache.CacheMap;
import com.tangosol.net.events.NamedEventInterceptor;
import com.tangosol.util.Filter;
import com.tangosol.util.filter.EqualsFilter;
import jakarta.annotation.PostConstruct;
Expand Down Expand Up @@ -105,15 +103,8 @@ public CoherenceIndexedSessionRepository(com.tangosol.net.Session coherenceSessi
public void init() {
this.sessionCache = this.coherenceSession.getCache(this.sessionMapName);

final CoherenceSessionCreatedEventHandler coherenceSessionEventHandler = new CoherenceSessionCreatedEventHandler(this.eventPublisher);
final SessionRemovedMapListener sessionRemovedMapListener = new SessionRemovedMapListener(this.eventPublisher);

coherenceSessionEventHandler.setScopeName(this.coherenceSession.getScopeName());
coherenceSessionEventHandler.setCacheName(this.sessionCache.getCacheName());

final CoherenceSessionEventMapListener sessionRemovedMapListener = new CoherenceSessionEventMapListener(this.eventPublisher);
this.sessionCache.addMapListener(sessionRemovedMapListener);
final NamedEventInterceptor<?> interceptor = new NamedEventInterceptor<>(CoherenceSessionCreatedEventHandler.class.getName(), coherenceSessionEventHandler);
this.coherenceSession.getInterceptorRegistry().registerEventInterceptor(interceptor);

if (logger.isDebugEnabled()) {
final String maxInactiveInterval =
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates.
* Copyright (c) 2021, 2024, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* https://oss.oracle.com/licenses/upl.
Expand All @@ -16,34 +16,42 @@

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.session.MapSession;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDeletedEvent;
import org.springframework.session.events.SessionExpiredEvent;
import org.springframework.util.Assert;

/**
* Custom Map Event that handles the deletion and expiration of a session. The handled event is then propagated as a
* Custom Map Event that subscribes to Coherence MapEvents. The handled event is then propagated as a
* Spring application event via Spring's {@link ApplicationEventPublisher}.
*
* @author Gunnar Hillert
* @since 3.0
*/
public class SessionRemovedMapListener implements MapListener<String, MapSession> {
public class CoherenceSessionEventMapListener implements MapListener<String, MapSession> {

private static final Log logger = LogFactory.getLog(SessionRemovedMapListener.class);
private static final Log logger = LogFactory.getLog(CoherenceSessionEventMapListener.class);

private final ApplicationEventPublisher eventPublisher;

public SessionRemovedMapListener(ApplicationEventPublisher eventPublisher) {
public CoherenceSessionEventMapListener(ApplicationEventPublisher eventPublisher) {
Assert.notNull(eventPublisher, "eventPublisher must not be null");
this.eventPublisher = eventPublisher;
}

@Override
public void entryInserted(MapEvent<String, MapSession> evt) {
public void entryInserted(MapEvent<String, MapSession> event) {
MapSession session = event.getNewValue();
if (event instanceof CacheEvent && session != null) {
if (logger.isDebugEnabled()) {
logger.debug(SessionDebugMessageUtils.createSessionEventMessage(SessionEvent.CREATED, session));
}
this.eventPublisher.publishEvent(new SessionCreatedEvent(this, session));
}
}

@Override
public void entryUpdated(MapEvent<String, MapSession> evt) {
public void entryUpdated(MapEvent<String, MapSession> event) {
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@

import java.util.Map;

import com.oracle.coherence.spring.session.support.MyHttpSessionListener;
import com.oracle.coherence.spring.session.support.SessionEventApplicationListener;
import com.tangosol.net.Coherence;
import com.tangosol.net.Session;
import com.tangosol.net.cache.CacheMap;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
Expand All @@ -22,6 +27,8 @@
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.FlushMode;
import org.springframework.session.MapSession;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDeletedEvent;
import org.springframework.util.StringUtils;

import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -50,6 +57,12 @@ abstract class AbstractCoherenceIndexedSessionRepositoryTests {
@Autowired
private CoherenceIndexedSessionRepository repository;

@Autowired
private SessionEventApplicationListener sessionEventRegistry;

@Autowired
private MyHttpSessionListener myHttpSessionListener;

protected String sessionName;

AbstractCoherenceIndexedSessionRepositoryTests() {
Expand All @@ -63,6 +76,11 @@ protected String getLocalClusterName() {
return null;
}

@AfterEach
void tearDown() {
this.myHttpSessionListener.reset();
}

@Test
void verifyCoherenceIndexedSessionRepositoryProperties() {
assertThat(this.repository.isUseEntryProcessor()).isEqualTo(this.expectedToUseEntryProcessor);
Expand All @@ -81,9 +99,26 @@ void createAndDestroyCoherenceSession() {

assertThat(cacheMap.get(sessionId)).isEqualTo(sessionToSave.getDelegate());

assertThat(this.sessionEventRegistry.receivedEvent(sessionId)).isTrue();
assertThat(this.sessionEventRegistry.<SessionCreatedEvent>getEvent(sessionId))
.isInstanceOf(SessionCreatedEvent.class);
this.sessionEventRegistry.clearSessionEvents();

assertThat(this.myHttpSessionListener.getSessionsCreatedCount()).isEqualTo(1);
assertThat(this.myHttpSessionListener.getSessionsDestroyedCount()).isEqualTo(0);

this.repository.deleteById(sessionId);

assertThat(cacheMap.get(sessionId)).isNull();

this.sessionEventRegistry.getEvent(sessionId);

assertThat(this.sessionEventRegistry.receivedEvent(sessionId)).isTrue();
assertThat(this.sessionEventRegistry.<SessionDeletedEvent>getEvent(sessionId))
.isInstanceOf(SessionDeletedEvent.class);

assertThat(this.myHttpSessionListener.getSessionsCreatedCount()).isEqualTo(1);
assertThat(this.myHttpSessionListener.getSessionsDestroyedCount()).isEqualTo(1);
}

@Test
Expand Down Expand Up @@ -323,4 +358,17 @@ void createSessionWithSecurityContextAndFindByPrincipal() {
this.repository.deleteById(session.getId());
assertThat(this.repository.findById(session.getId())).isNull();
}

@Configuration
static class CommonConfig {
@Bean
SessionEventApplicationListener sessionEventRegistry() {
return new SessionEventApplicationListener();
}

@Bean
MyHttpSessionListener myHttpSessionListener() {
return new MyHttpSessionListener();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates.
* Copyright (c) 2021, 2024, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* https://oss.oracle.com/licenses/upl.
Expand All @@ -16,7 +16,7 @@
import com.tangosol.net.NamedCache;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -57,8 +57,8 @@ public abstract class AbstractSessionEventTests {
@Autowired
private SessionEventApplicationListener sessionEventApplicationListener;

@BeforeEach
void setup() {
@AfterEach
void shutdown() {
this.sessionEventApplicationListener.clearSessionEvents();
}

Expand Down Expand Up @@ -158,7 +158,7 @@ void saveUpdatesTimeToLiveTest() {
}

@Test
void changeSessionIdNoEventTest() {
void changeSessionIdTest() {
final Session sessionToSave = this.repository.createSession();
sessionToSave.setMaxInactiveInterval(Duration.ofMinutes(30));
this.repository.save(sessionToSave);
Expand All @@ -171,7 +171,7 @@ void changeSessionIdNoEventTest() {
sessionToSave.changeSessionId();
this.repository.save(sessionToSave);

assertThat(this.sessionEventApplicationListener.receivedEvent(sessionToSave.getId())).isFalse();
assertThat(this.sessionEventApplicationListener.receivedEvent(sessionToSave.getId())).isTrue();

this.repository.deleteById(sessionToSave.getId());
}
Expand Down
Loading

0 comments on commit 92798da

Please sign in to comment.