package io.crnk.reactive.internal; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import io.crnk.core.engine.result.Result; import io.crnk.core.engine.result.ResultFactory; import reactor.core.publisher.Mono; import reactor.util.context.Context; /** * Implementation based on Reactor library. Attempts to pass along request context in subscriber context using {@value SUBSCRIBER_CONTEXT_KEY}. */ public class MonoResultFactory implements ResultFactory { public static final String SUBSCRIBER_CONTEXT_KEY = "crnkContext"; private ThreadLocal threadLocal = new ThreadLocal(); @Override public <T> Result<T> just(T object) { Mono<T> mono = Mono.just(object); return toResult(mono); } private <T> Result<T> toResult(Mono<T> mono) { Object context = threadLocal.get(); if (context != null) { mono.subscriberContext(it -> it.put(SUBSCRIBER_CONTEXT_KEY, context)); } return new MonoResult(mono); } @Override public <T> Result<List<T>> zip(List<Result<T>> results) { if (results.isEmpty()) { return just(new ArrayList<>()); } List<Mono<T>> monos = new ArrayList<>(); for (Result<T> result : results) { monos.add(((MonoResult) result).getMono()); } Mono<List<T>> zipped = Mono.zip(monos, a -> Arrays.asList((T[]) a)); return toResult(zipped); } @Override public boolean isAsync() { return true; } @Override public Object getThreadContext() { Object context = threadLocal.get(); if (context == null) { throw new UnsupportedOperationException("context not available"); } return context; } @Override public boolean hasThreadContext() { return threadLocal.get() != null; } @Override public <T> Result<T> attachContext(Result<T> result, Object context) { MonoResult monoResult = (MonoResult) result; return new MonoResult<>(monoResult.getMono().subscriberContext(Context.of(SUBSCRIBER_CONTEXT_KEY, context))); } @Override public <T> Result<List<T>> all(List<Result<T>> results) { if (results.size() == 1) { return results.get(0).map(it -> Arrays.asList(it)); } throw new UnsupportedOperationException("bulk support not yet implemented"); } @Override public Result<Object> getContext() { return new MonoResult(Mono.subscriberContext().map(it -> it.get(SUBSCRIBER_CONTEXT_KEY))); } @Override public void setThreadContext(Object context) { threadLocal.set(context); } @Override public void clearContext() { threadLocal.remove(); } }