package org.pac4j.vertx.core.store; import io.vertx.rxjava.core.Vertx; import io.vertx.rxjava.core.shareddata.AsyncMap; import org.pac4j.core.exception.TechnicalException; import org.pac4j.core.store.Store; import rx.Observable; import rx.functions.Func1; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * Pac4j shared store implementation based on vert.x clustered shared data. */ public class VertxClusteredMapStore<K, V> extends VertxMapStoreBase implements Store<K, V> { private final Vertx rxVertx; private final int blockingTimeoutSeconds; public VertxClusteredMapStore(final io.vertx.core.Vertx vertx) { this(vertx, 1); } public VertxClusteredMapStore(final io.vertx.core.Vertx vertx, final int timeoutSeconds) { rxVertx = Vertx.newInstance(vertx); blockingTimeoutSeconds = timeoutSeconds; } @Override public Optional<V> get(K key) { voidAsyncOpToBlocking(map -> map.getObservable((key))); final CompletableFuture<V> valueFuture = new CompletableFuture<>(); rxVertx.sharedData().<K, V>getClusterWideMapObservable(PAC4J_SHARED_DATA_KEY) .flatMap(map -> map.getObservable(key)) .subscribe(valueFuture::complete); try { return Optional.ofNullable(valueFuture.get(blockingTimeoutSeconds, TimeUnit.SECONDS)); } catch (InterruptedException|ExecutionException|TimeoutException e) { throw new TechnicalException(e); } } @Override public void set(K key, V value) { voidAsyncOpToBlocking(map -> map.putObservable(key, value)); } @Override public void remove(K key) { voidAsyncOpToBlocking(map -> map.removeObservable(key)); } public void voidAsyncOpToBlocking(Func1<AsyncMap, Observable> asyncOp) { CompletableFuture<Void> future = new CompletableFuture<>(); rxVertx.sharedData().getClusterWideMapObservable(PAC4J_SHARED_DATA_KEY) .map(asyncOp) .subscribe(result -> future.complete(null)); try { future.get(blockingTimeoutSeconds, TimeUnit.SECONDS); } catch (InterruptedException|ExecutionException |TimeoutException e) { throw new TechnicalException(e); } } }