Skip to content

Commit

Permalink
Merge pull request #149 from pferraro/main
Browse files Browse the repository at this point in the history
Update to wildfly-clustering 1.0.8.Final.
  • Loading branch information
pferraro authored Mar 6, 2024
2 parents 2b60e2e + 4542300 commit ff30bee
Show file tree
Hide file tree
Showing 20 changed files with 157 additions and 290 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

# wildfly-clustering-spring

wildfly-clustering-spring is a set of Spring modules providing distributed session management for Spring Session and Spring Web (including Spring MVC and Spring Flux) using Infinispan.
wildfly-clustering-spring is a set of Spring modules providing distributed session management for Spring Session and Spring Flux using Infinispan.
This brings the same clustering features of WildFly's distributed session managers to the Spring ecosystem, including:

* Servlet 6.0 specification compliance (excluding [limitations inherent to Spring Session and Spring Web](#notes)).
Expand All @@ -28,8 +28,8 @@ This brings the same clustering features of WildFly's distributed session manage

## Modules

* [Spring Session](session/README.md)
* [Spring Web](web/README.md)
* [Spring Session](session/README.md), providing traditional HttpSession management for use with Jakarta Servlet applications and Spring MVC.
* [Spring Web](web/README.md), providing reactive WebSession management for use with Spring Flux.

## Notes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import org.wildfly.clustering.session.SessionManagerConfiguration;
import org.wildfly.clustering.session.SessionManagerFactory;
import org.wildfly.clustering.session.SessionManagerFactoryConfiguration;
import org.wildfly.clustering.session.spec.SessionSpecificationProvider;
import org.wildfly.clustering.spring.context.SessionManagerBean;
import org.wildfly.clustering.spring.context.SessionMarshallerFactory;
import org.wildfly.common.function.Functions;
Expand All @@ -38,7 +37,7 @@
* Spring configuration bean for a distributable session repository.
* @author Paul Ferraro
*/
public abstract class SessionManagementConfiguration<S, C, L> implements SessionManagerFactoryConfiguration<Void>, SessionManagerConfiguration<C>, EnvironmentAware, ImportAware, ResourceLoaderAware, Consumer<AnnotationAttributes>, Supplier<SessionSpecificationProvider<S, C, L>> {
public abstract class SessionManagementConfiguration<S, C, L> implements SessionManagerFactoryConfiguration<Void>, SessionManagerConfiguration<C>, EnvironmentAware, ImportAware, ResourceLoaderAware, Consumer<AnnotationAttributes> {

private final Class<? extends Annotation> annotationClass;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.wildfly.clustering.session.infinispan.embedded.InfinispanSessionManagerFactory;
import org.wildfly.clustering.session.infinispan.embedded.InfinispanSessionManagerFactoryConfiguration;
import org.wildfly.clustering.session.infinispan.embedded.metadata.SessionMetaDataKey;
import org.wildfly.clustering.session.spec.SessionEventListenerSpecificationProvider;
import org.wildfly.clustering.session.spec.SessionSpecificationProvider;
import org.wildfly.clustering.spring.context.AutoDestroyBean;

Expand All @@ -41,15 +42,17 @@
public class InfinispanSessionManagerFactoryBean<S, C, L> extends AutoDestroyBean implements SessionManagerFactory<C, Void, TransactionBatch>, InitializingBean {

private final SessionManagerFactoryConfiguration<Void> configuration;
private final SessionSpecificationProvider<S, C, L> provider;
private final SessionSpecificationProvider<S, C> sessionProvider;
private final SessionEventListenerSpecificationProvider<S, L> listenerProvider;
private final InfinispanConfiguration infinispan;
private final ChannelEmbeddedCacheManagerCommandDispatcherFactoryConfiguration embeddedCacheManagerConfiguration;

private SessionManagerFactory<C, Void, TransactionBatch> sessionManagerFactory;

public InfinispanSessionManagerFactoryBean(SessionManagerFactoryConfiguration<Void> configuration, SessionSpecificationProvider<S, C, L> provider, InfinispanConfiguration infinispan, ChannelEmbeddedCacheManagerCommandDispatcherFactoryConfiguration embeddedCacheManagerConfiguration) {
public InfinispanSessionManagerFactoryBean(SessionManagerFactoryConfiguration<Void> configuration, SessionSpecificationProvider<S, C> sessionProvider, SessionEventListenerSpecificationProvider<S, L> listenerProvider, InfinispanConfiguration infinispan, ChannelEmbeddedCacheManagerCommandDispatcherFactoryConfiguration embeddedCacheManagerConfiguration) {
this.configuration = configuration;
this.provider = provider;
this.sessionProvider = sessionProvider;
this.listenerProvider = listenerProvider;
this.infinispan = infinispan;
this.embeddedCacheManagerConfiguration = embeddedCacheManagerConfiguration;
}
Expand Down Expand Up @@ -123,7 +126,7 @@ public CacheContainerCommandDispatcherFactory getCommandDispatcherFactory() {
}
};

this.sessionManagerFactory = new InfinispanSessionManagerFactory<>(this.configuration, this.provider, infinispanConfiguration);
this.sessionManagerFactory = new InfinispanSessionManagerFactory<>(this.configuration, this.sessionProvider, this.listenerProvider, infinispanConfiguration);
this.accept(this.sessionManagerFactory::close);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.wildfly.clustering.session.SessionManagerFactory;
import org.wildfly.clustering.session.SessionManagerFactoryConfiguration;
import org.wildfly.clustering.session.infinispan.remote.HotRodSessionManagerFactory;
import org.wildfly.clustering.session.spec.SessionEventListenerSpecificationProvider;
import org.wildfly.clustering.session.spec.SessionSpecificationProvider;
import org.wildfly.clustering.spring.context.AutoDestroyBean;

Expand All @@ -28,15 +29,17 @@
public class HotRodSessionManagerFactoryBean<S, C, L> extends AutoDestroyBean implements SessionManagerFactory<C, Void, TransactionBatch>, InitializingBean {

private final SessionManagerFactoryConfiguration<Void> configuration;
private final SessionSpecificationProvider<S, C, L> specProvider;
private final SessionSpecificationProvider<S, C> sessionProvider;
private final SessionEventListenerSpecificationProvider<S, L> listenerProvider;
private final HotRodConfiguration hotrod;
private final RemoteCacheContainerProvider provider;

private SessionManagerFactory<C, Void, TransactionBatch> sessionManagerFactory;

public HotRodSessionManagerFactoryBean(SessionManagerFactoryConfiguration<Void> configuration, SessionSpecificationProvider<S, C, L> specProvider, HotRodConfiguration hotrod, RemoteCacheContainerProvider provider) {
public HotRodSessionManagerFactoryBean(SessionManagerFactoryConfiguration<Void> configuration, SessionSpecificationProvider<S, C> sessionProvider, SessionEventListenerSpecificationProvider<S, L> listenerProvider, HotRodConfiguration hotrod, RemoteCacheContainerProvider provider) {
this.hotrod = hotrod;
this.specProvider = specProvider;
this.sessionProvider = sessionProvider;
this.listenerProvider = listenerProvider;
this.provider = provider;
this.configuration = configuration;
}
Expand All @@ -63,7 +66,7 @@ public <CK, CV> RemoteCache<CK, CV> getCache() {
}
};

this.sessionManagerFactory = new HotRodSessionManagerFactory<>(this.configuration, this.specProvider, hotrodConfiguration);
this.sessionManagerFactory = new HotRodSessionManagerFactory<>(this.configuration, this.sessionProvider, this.listenerProvider, hotrodConfiguration);
this.accept(this.sessionManagerFactory::close);
}

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
<version.org.springframework.session>3.2.1</version.org.springframework.session>
<version.org.infinispan>14.0.25.Final</version.org.infinispan>
<version.org.jboss.marshalling>2.1.4.Final</version.org.jboss.marshalling>
<version.org.wildfly.clustering>1.0.7.Final</version.org.wildfly.clustering>
<version.org.wildfly.clustering>1.0.8.Final</version.org.wildfly.clustering>

<!-- Test dependency versions -->
<version.org.apache.tomcat>10.1.19</version.org.apache.tomcat>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ public String changeSessionId() {
@SuppressWarnings("unchecked")
@Override
public <T> T getAttribute(String attributeName) {
return (T) this.session.getAttributes().getAttribute(attributeName);
return (T) this.session.getAttributes().get(attributeName);
}

@Override
public Set<String> getAttributeNames() {
return this.session.getAttributes().getAttributeNames();
return this.session.getAttributes().keySet();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,17 @@ public DistributableSession(SessionManager<Void, B> manager, Session<Void> sessi
public String changeSessionId() {
Session<Void> oldSession = this.session;
String id = this.manager.getIdentifierFactory().get();
try (BatchContext context = this.resumeBatch()) {
try (BatchContext<B> context = this.manager.getBatcher().resumeBatch(this.batch)) {
Session<Void> newSession = this.manager.createSession(id);
try {
for (String name: oldSession.getAttributes().getAttributeNames()) {
newSession.getAttributes().setAttribute(name, oldSession.getAttributes().getAttribute(name));
for (Map.Entry<String, Object> entry : oldSession.getAttributes().entrySet()) {
newSession.getAttributes().put(entry.getKey(), entry.getValue());
}
newSession.getMetaData().setTimeout(oldSession.getMetaData().getTimeout());
newSession.getMetaData().setLastAccess(oldSession.getMetaData().getLastAccessStartTime(), oldSession.getMetaData().getLastAccessTime());
oldSession.invalidate();
this.session = newSession;
oldSession.close();
} catch (IllegalStateException e) {
newSession.invalidate();
throw e;
Expand Down Expand Up @@ -82,26 +83,17 @@ public String changeSessionId() {
@SuppressWarnings("unchecked")
@Override
public <T> T getAttribute(String name) {
Session<Void> session = this.session;
try (BatchContext context = this.resumeBatch()) {
return (T) session.getAttributes().getAttribute(name);
}
return (T) this.session.getAttributes().get(name);
}

@Override
public Set<String> getAttributeNames() {
Session<Void> session = this.session;
try (BatchContext context = this.resumeBatch()) {
return session.getAttributes().getAttributeNames();
}
return this.session.getAttributes().keySet();
}

@Override
public Instant getCreationTime() {
Session<Void> session = this.session;
try (BatchContext context = this.resumeBatch()) {
return session.getMetaData().getCreationTime();
}
return this.session.getMetaData().getCreationTime();
}

@Override
Expand All @@ -111,26 +103,17 @@ public String getId() {

@Override
public Instant getLastAccessedTime() {
Session<Void> session = this.session;
try (BatchContext context = this.resumeBatch()) {
return session.getMetaData().getLastAccessStartTime();
}
return this.session.getMetaData().getLastAccessTime();
}

@Override
public Duration getMaxInactiveInterval() {
Session<Void> session = this.session;
try (BatchContext context = this.resumeBatch()) {
return session.getMetaData().getTimeout();
}
return this.session.getMetaData().getTimeout();
}

@Override
public boolean isExpired() {
Session<Void> session = this.session;
try (BatchContext context = this.resumeBatch()) {
return session.getMetaData().isExpired();
}
return this.session.getMetaData().isExpired();
}

@Override
Expand All @@ -142,10 +125,7 @@ public void removeAttribute(String name) {
public void setAttribute(String name, Object value) {
Map<String, String> oldIndexes = this.indexing.getIndexResolver().resolveIndexesFor(this);

Session<Void> session = this.session;
try (BatchContext context = this.resumeBatch()) {
session.getAttributes().setAttribute(name, value);
}
this.session.getAttributes().put(name, value);

// N.B. org.springframework.session.web.http.HttpSessionAdapter already triggers HttpSessionBindingListener events
// However, Spring Session violates the servlet specification by not triggering HttpSessionAttributeListener events
Expand All @@ -169,7 +149,7 @@ public void setAttribute(String name, Object value) {
}
}
if (indexValue != null) {
String sessionId = session.getId();
String sessionId = this.session.getId();
User<Void, Void, String, String> sso = manager.createUser(indexValue, null);
sso.getSessions().addSession(sessionId, sessionId);
}
Expand All @@ -186,47 +166,27 @@ public void setLastAccessedTime(Instant instant) {

@Override
public void setMaxInactiveInterval(Duration duration) {
Session<Void> session = this.session;
try (BatchContext context = this.resumeBatch()) {
session.getMetaData().setTimeout(duration);
}
this.session.getMetaData().setTimeout(duration);
}

@Override
public boolean isNew() {
Session<Void> session = this.session;
try (BatchContext context = this.resumeBatch()) {
return session.getMetaData().isNew();
}
return this.session.getMetaData().isNew();
}

@Override
public void close() {
// Spring session lifecycle logic is a mess. Ensure we only close a session once.
if (this.closed.compareAndSet(false, true)) {
Session<Void> requestSession = this.session;
try (BatchContext context = this.resumeBatch()) {
// If batch was discarded, close it
if (this.batch.getState() == Batch.State.DISCARDED) {
this.batch.close();
}
// If batch is closed, close valid session in a new batch
try (Batch batch = (this.batch.getState() == Batch.State.CLOSED) && requestSession.isValid() ? this.manager.getBatcher().createBatch() : this.batch) {
// Ensure session is closed, even if invalid
try (Session<Void> session = requestSession) {
if (session.isValid()) {
// According to §7.6 of the servlet specification:
// The session is considered to be accessed when a request that is part of the session is first handled by the servlet container.
session.getMetaData().setLastAccess(this.startTime, Instant.now());
}
try (BatchContext<B> context = this.manager.getBatcher().resumeBatch(this.batch)) {
try (Session<Void> session = this.session) {
if (session.isValid()) {
// According to §7.6 of the servlet specification:
// The session is considered to be accessed when a request that is part of the session is first handled by the servlet container.
session.getMetaData().setLastAccess(this.startTime, Instant.now());
}
}
}
}
}

private BatchContext resumeBatch() {
B batch = (this.batch != null) && (this.batch.getState() != Batch.State.CLOSED) ? this.batch : null;
return this.manager.getBatcher().resumeBatch(batch);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpSession;
import jakarta.servlet.http.HttpSessionActivationListener;
import jakarta.servlet.http.HttpSessionBindingEvent;
import jakarta.servlet.http.HttpSessionBindingListener;

Expand All @@ -30,10 +29,10 @@ public class ImmutableSessionDestroyAction<B extends Batch> implements BiConsume

private final ApplicationEventPublisher publisher;
private final ServletContext context;
private final SessionSpecificationProvider<HttpSession, ServletContext, HttpSessionActivationListener> provider;
private final SessionSpecificationProvider<HttpSession, ServletContext> provider;
private final UserConfiguration<B> indexing;

public ImmutableSessionDestroyAction(ApplicationEventPublisher publisher, ServletContext context, SessionSpecificationProvider<HttpSession, ServletContext, HttpSessionActivationListener> provider, UserConfiguration<B> indexing) {
public ImmutableSessionDestroyAction(ApplicationEventPublisher publisher, ServletContext context, SessionSpecificationProvider<HttpSession, ServletContext> provider, UserConfiguration<B> indexing) {
this.publisher = publisher;
this.context = context;
this.provider = provider;
Expand All @@ -45,12 +44,13 @@ public void accept(ImmutableSession session, BiFunction<Object, Session, Applica
ApplicationEvent event = eventFactory.apply(this, new DistributableImmutableSession(session));
this.publisher.publishEvent(event);
HttpSession httpSession = this.provider.asSession(session, this.context);
for (Map.Entry<String, HttpSessionBindingListener> entry : session.getAttributes().getAttributes(HttpSessionBindingListener.class).entrySet()) {
HttpSessionBindingListener listener = entry.getValue();
try {
listener.valueUnbound(new HttpSessionBindingEvent(httpSession, entry.getKey(), listener));
} catch (Throwable e) {
this.context.log(e.getMessage(), e);
for (Map.Entry<String, Object> entry : session.getAttributes().entrySet()) {
if (entry.getValue() instanceof HttpSessionBindingListener listener) {
try {
listener.valueUnbound(new HttpSessionBindingEvent(httpSession, entry.getKey(), listener));
} catch (Throwable e) {
this.context.log(e.getMessage(), e);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@
import org.wildfly.clustering.server.immutable.Immutability;
import org.wildfly.clustering.session.ImmutableSession;
import org.wildfly.clustering.session.SessionManager;
import org.wildfly.clustering.session.spec.SessionSpecificationProvider;
import org.wildfly.clustering.session.spec.servlet.JakartaServletSpecificationProvider;
import org.wildfly.clustering.session.spec.servlet.HttpSessionProvider;
import org.wildfly.clustering.spring.context.config.SessionManagementConfiguration;
import org.wildfly.clustering.spring.security.SpringSecurityImmutability;
import org.wildfly.clustering.spring.session.DistributableSessionRepository;
Expand Down Expand Up @@ -70,14 +69,9 @@ protected HttpSessionConfiguration(Class<? extends Annotation> annotationClass,
this.indexResolver = defaultIndexResolver;
}

@Override
public SessionSpecificationProvider<HttpSession, ServletContext, HttpSessionActivationListener> get() {
return JakartaServletSpecificationProvider.INSTANCE;
}

@Bean
public <B extends Batch> FindByIndexNameSessionRepository<SpringSession> sessionRepository(SessionManager<Void, B> manager, UserConfiguration<B> userConfiguration) {
BiConsumer<ImmutableSession, BiFunction<Object, Session, ApplicationEvent>> sessionDestroyAction = new ImmutableSessionDestroyAction<>(this.publisher, this.getContext(), this.get(), userConfiguration);
BiConsumer<ImmutableSession, BiFunction<Object, Session, ApplicationEvent>> sessionDestroyAction = new ImmutableSessionDestroyAction<>(this.publisher, this.getContext(), HttpSessionProvider.INSTANCE, userConfiguration);
DistributableSessionRepositoryConfiguration<B> configuration = new DistributableSessionRepositoryConfiguration<>() {
@Override
public SessionManager<Void, B> getSessionManager() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* The servlet filter otherwise registered by {@link AbstractHttpSessionApplicationInitializer}.
* @author Paul Ferraro
*/
@WebFilter(filterName = AbstractHttpSessionApplicationInitializer.DEFAULT_FILTER_NAME, urlPatterns = "/*", dispatcherTypes = { DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC }, asyncSupported = true)
@WebFilter(filterName = AbstractHttpSessionApplicationInitializer.DEFAULT_FILTER_NAME, urlPatterns = "/*", dispatcherTypes = { DispatcherType.REQUEST, DispatcherType.ERROR })
public class SpringSessionFilter extends DelegatingFilterProxy {

public SpringSessionFilter() {
Expand Down
Loading

0 comments on commit ff30bee

Please sign in to comment.