/* * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.messaging.tcp.reactor; import java.time.Duration; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import reactor.core.publisher.Mono; import reactor.core.publisher.MonoProcessor; import org.springframework.util.Assert; import org.springframework.util.concurrent.FailureCallback; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureCallback; import org.springframework.util.concurrent.ListenableFutureCallbackRegistry; import org.springframework.util.concurrent.SuccessCallback; /** * Adapts {@link Mono} to {@link ListenableFuture} optionally converting the * result Object type {@code <S>} to the expected target type {@code <T>}. * * @author Rossen Stoyanchev * @since 5.0 * @param <S> the type of object expected from the {@link Mono} * @param <T> the type of object expected from the {@link ListenableFuture} */ abstract class AbstractMonoToListenableFutureAdapter<S, T> implements ListenableFuture<T> { private final MonoProcessor<S> monoProcessor; private final ListenableFutureCallbackRegistry<T> registry = new ListenableFutureCallbackRegistry<>(); protected AbstractMonoToListenableFutureAdapter(Mono<S> mono) { Assert.notNull(mono, "Mono must not be null"); this.monoProcessor = mono .doOnSuccess(result -> { T adapted; try { adapted = adapt(result); } catch (Throwable ex) { registry.failure(ex); return; } registry.success(adapted); }) .doOnError(this.registry::failure) .subscribe(); } @Override public T get() throws InterruptedException { S result = this.monoProcessor.block(); return adapt(result); } @Override public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { Assert.notNull(unit, "TimeUnit must not be null"); Duration duration = Duration.ofMillis(TimeUnit.MILLISECONDS.convert(timeout, unit)); S result = this.monoProcessor.block(duration); return adapt(result); } @Override public boolean cancel(boolean mayInterruptIfRunning) { if (isCancelled()) { return false; } this.monoProcessor.cancel(); return true; } @Override public boolean isCancelled() { return this.monoProcessor.isCancelled(); } @Override public boolean isDone() { return this.monoProcessor.isTerminated(); } @Override public void addCallback(ListenableFutureCallback<? super T> callback) { this.registry.addCallback(callback); } @Override public void addCallback(SuccessCallback<? super T> successCallback, FailureCallback failureCallback) { this.registry.addSuccessCallback(successCallback); this.registry.addFailureCallback(failureCallback); } protected abstract T adapt(S result); }