org.wildfly.clustering.session.cache.CachedSessionManager Maven / Gradle / Ivy
/*
* Copyright The WildFly Authors
* SPDX-License-Identifier: Apache-2.0
*/
package org.wildfly.clustering.session.cache;
import java.util.Optional;
import java.util.concurrent.CompletionStage;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import org.wildfly.clustering.cache.batch.Batch;
import org.wildfly.clustering.server.cache.Cache;
import org.wildfly.clustering.server.cache.CacheFactory;
import org.wildfly.clustering.session.ImmutableSession;
import org.wildfly.clustering.session.Session;
import org.wildfly.clustering.session.SessionManager;
import org.wildfly.clustering.session.SessionStatistics;
import org.wildfly.common.function.Functions;
/**
* A concurrent session manager, that can share session references across concurrent threads.
* @param the session context type
* @author Paul Ferraro
*/
public class CachedSessionManager implements SessionManager {
private final SessionManager manager;
private final Cache>> sessionCache;
private final BiFunction>> sessionCreator;
private final BiFunction>> sessionFinder;
private final UnaryOperator> validator = new UnaryOperator<>() {
@Override
public Session apply(Session session) {
// If session was invalidated by a concurrent thread, return null instead of an invalid session
// This will reduce the likelihood that a duplicate invalidation request (e.g. from a double-clicked logout) results in an ISE
if (session != null && !session.isValid()) {
session.close();
return null;
}
return session;
}
};
public CachedSessionManager(SessionManager manager, CacheFactory cacheFactory) {
this.manager = manager;
this.sessionCreator = new BiFunction<>() {
@Override
public CompletionStage> apply(String id, Runnable closeTask) {
return manager.createSessionAsync(id).thenApply(session -> new CachedSession<>(session, closeTask));
}
};
Function> empty = closeTask -> {
closeTask.run();
return null;
};
this.sessionFinder = new BiFunction<>() {
@Override
public CompletionStage> apply(String id, Runnable closeTask) {
return manager.findSessionAsync(id).thenApply(session -> (session != null) ? new CachedSession<>(session, closeTask) : empty.apply(closeTask));
}
};
this.sessionCache = cacheFactory.createCache(Functions.discardingConsumer(), new Consumer>>() {
@Override
public void accept(CompletionStage> future) {
future.thenAccept(session -> Optional.ofNullable(session).map(Supplier::get).ifPresent(Session::close));
}
});
}
@Override
public CompletionStage> createSessionAsync(String id) {
return this.sessionCache.computeIfAbsent(id, this.sessionCreator).thenApply(Function.identity());
}
@Override
public CompletionStage> findSessionAsync(String id) {
return this.sessionCache.computeIfAbsent(id, this.sessionFinder).thenApply(this.validator);
}
@Override
public CompletionStage findImmutableSessionAsync(String id) {
return this.manager.findImmutableSessionAsync(id);
}
@Override
public Session getDetachedSession(String id) {
return this.manager.getDetachedSession(id);
}
@Override
public Supplier getIdentifierFactory() {
return this.manager.getIdentifierFactory();
}
@Override
public void start() {
this.manager.start();
}
@Override
public void stop() {
this.manager.stop();
}
@Override
public Supplier getBatchFactory() {
return this.manager.getBatchFactory();
}
@Override
public SessionStatistics getStatistics() {
return this.manager.getStatistics();
}
}