All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.smallrye.reactive.messaging.providers.SubscriberMediator Maven / Gradle / Ivy
package io.smallrye.reactive.messaging.providers;
import static io.smallrye.reactive.messaging.providers.i18n.ProviderExceptions.ex;
import static io.smallrye.reactive.messaging.providers.i18n.ProviderLogging.log;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Flow;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.eclipse.microprofile.reactive.messaging.Acknowledgment;
import org.eclipse.microprofile.reactive.messaging.Message;
import org.eclipse.microprofile.reactive.streams.operators.SubscriberBuilder;
import org.reactivestreams.Subscriber;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.helpers.Subscriptions;
import io.smallrye.mutiny.subscription.MultiSubscriber;
import io.smallrye.reactive.messaging.MediatorConfiguration;
import io.smallrye.reactive.messaging.Shape;
import io.smallrye.reactive.messaging.providers.helpers.ClassUtils;
import io.smallrye.reactive.messaging.providers.helpers.IgnoringSubscriber;
import io.smallrye.reactive.messaging.providers.helpers.MultiUtils;
import mutiny.zero.flow.adapters.AdaptersToFlow;
public class SubscriberMediator extends AbstractMediator {
private Multi> source;
private Flow.Subscriber> subscriber;
private Function>, Multi>> function;
/**
* Keep track of the subscription to cancel it once the scope is terminated.
*/
private final AtomicReference subscription = new AtomicReference<>();
// Supported signatures:
// 1. Subscriber> method()
// 2. Subscriber method()
// 3. CompletionStage method(Message m) + Uni variant
// 4. CompletionStage method(I i) - + Uni variant
// 5. void/? method(Message m) - The support of this method has been removed (CES - Reactive Hangout 2018/09/11).
// 6. void/? method(I i)
public SubscriberMediator(MediatorConfiguration configuration) {
super(configuration);
if (configuration.shape() != Shape.SUBSCRIBER) {
throw ex.illegalArgumentForSubscriberShape(configuration.shape());
}
}
@Override
public void initialize(Object bean) {
super.initialize(bean);
switch (configuration.consumption()) {
case STREAM_OF_MESSAGE: // 1
case STREAM_OF_PAYLOAD: // 2
processMethodReturningASubscriber();
break;
case MESSAGE: // 3 (5 being dropped)
case PAYLOAD: // 4 or 6
if (ClassUtils.isAssignable(configuration.getReturnType(), CompletionStage.class)) {
// Case 3, 4
processMethodReturningACompletionStage();
} else if (ClassUtils.isAssignable(configuration.getReturnType(), Uni.class)) {
// Case 3, 4 - Uni Variant
processMethodReturningAUni();
} else {
// Case 6 (5 being dropped)
processMethodReturningVoid();
}
break;
default:
throw ex.illegalArgumentForUnexpectedConsumption(configuration.consumption());
}
assert this.subscriber != null;
}
@Override
public Flow.Subscriber> getComputedSubscriber() {
return subscriber;
}
@Override
public boolean isConnected() {
return source != null;
}
@Override
public void connectToUpstream(Multi> publisher) {
this.source = convert(publisher);
}
@SuppressWarnings({ "ReactiveStreamsSubscriberImplementation" })
@Override
public void run() {
assert this.source != null;
assert this.function != null;
assert this.subscriber != null;
AtomicReference syncErrorCatcher = new AtomicReference<>();
Flow.Subscriber> delegate = this.subscriber;
Flow.Subscriber> delegating = new MultiSubscriber>() {
@Override
public void onSubscribe(Flow.Subscription s) {
subscription.set(s);
delegate.onSubscribe(new Flow.Subscription() {
@Override
public void request(long n) {
s.request(n);
}
@Override
public void cancel() {
s.cancel();
}
});
}
@Override
public void onItem(Message item) {
try {
delegate.onNext(item);
} catch (Exception e) {
log.messageProcessingException(configuration.methodAsString(), e);
syncErrorCatcher.set(e);
}
}
@Override
public void onFailure(Throwable t) {
log.messageProcessingException(configuration.methodAsString(), t);
syncErrorCatcher.set(t);
delegate.onError(t);
}
@Override
public void onCompletion() {
delegate.onComplete();
}
};
Multi> subscriberSource = decorateSubscriberSource(this.source);
function.apply(subscriberSource).subscribe(delegating);
// Check if a synchronous error has been caught
Throwable throwable = syncErrorCatcher.get();
if (throwable != null) {
throw ex.weavingForIncoming(configuration.getIncoming(), throwable);
}
}
private void processMethodReturningVoid() {
this.subscriber = IgnoringSubscriber.INSTANCE;
if (configuration.isBlocking()) {
if (configuration.isBlockingExecutionOrdered()) {
this.function = upstream -> MultiUtils.handlePreProcessingAcknowledgement(upstream, configuration)
.onItem().transformToUniAndConcatenate(msg -> invokeBlocking(msg, msg.getPayload())
.onItemOrFailure().transformToUni(handleInvocationResult(msg)))
.onFailure()
.invoke(failure -> health.reportApplicationFailure(configuration.methodAsString(), failure));
} else {
this.function = upstream -> MultiUtils.handlePreProcessingAcknowledgement(upstream, configuration)
.onItem().transformToUniAndMerge(msg -> invokeBlocking(msg, msg.getPayload())
.onItemOrFailure().transformToUni(handleInvocationResult(msg)))
.onFailure()
.invoke(failure -> health.reportApplicationFailure(configuration.methodAsString(), failure));
}
} else {
this.function = upstream -> MultiUtils.handlePreProcessingAcknowledgement(upstream, configuration)
.onItem()
.transformToUniAndConcatenate(
msg -> invokeOnMessageContext(msg, msg.getPayload())
.onItemOrFailure().transformToUni(handleInvocationResult(msg)))
.onFailure().invoke(failure -> health.reportApplicationFailure(configuration.methodAsString(), failure));
}
}
private BiFunction>> handleInvocationResult(
Message m) {
return (success, failure) -> {
if (failure != null) {
if (configuration.getAcknowledgment() == Acknowledgment.Strategy.POST_PROCESSING) {
return Uni.createFrom().completionStage(m.nack(failure).thenApply(x -> m));
} else {
// Invocation failed, but the message may have been already acknowledged (PRE or MANUAL), so
// we cannot nack. We propagate the failure downstream.
return Uni.createFrom().failure(failure);
}
} else {
if (configuration.getAcknowledgment() == Acknowledgment.Strategy.POST_PROCESSING) {
return Uni.createFrom().completionStage(m.ack().thenApply(x -> m));
} else {
return Uni.createFrom().item(m);
}
}
};
}
private void processMethodReturningACompletionStage() {
this.subscriber = IgnoringSubscriber.INSTANCE;
boolean invokeWithPayload = MediatorConfiguration.Consumption.PAYLOAD == configuration.consumption();
if (configuration.isBlocking()) {
if (configuration.isBlockingExecutionOrdered()) {
this.function = upstream -> MultiUtils.handlePreProcessingAcknowledgement(upstream, configuration)
.onItem().transformToUniAndConcatenate(msg -> invokeBlockingAndHandleOutcome(invokeWithPayload, msg))
.onFailure().invoke(this::reportFailure);
} else {
this.function = upstream -> MultiUtils.handlePreProcessingAcknowledgement(upstream, configuration)
.onItem().transformToUniAndMerge(msg -> invokeBlockingAndHandleOutcome(invokeWithPayload, msg))
.onFailure().invoke(this::reportFailure);
}
} else {
this.function = upstream -> MultiUtils.handlePreProcessingAcknowledgement(upstream, configuration)
.onItem().transformToUniAndConcatenate(msg -> {
Uni uni;
if (invokeWithPayload) {
uni = invokeOnMessageContext(msg, msg.getPayload())
.onItem().transformToUni(cs -> Uni.createFrom().completionStage((CompletionStage) cs));
} else {
uni = invokeOnMessageContext(msg, msg)
.onItem().transformToUni(cs -> Uni.createFrom().completionStage((CompletionStage) cs));
}
return uni.onItemOrFailure().transformToUni(handleInvocationResult(msg));
})
.onFailure().invoke(this::reportFailure);
}
}
private Uni> invokeBlockingAndHandleOutcome(boolean invokeWithPayload, Message msg) {
Uni uni;
if (invokeWithPayload) {
uni = invokeBlocking(msg, msg.getPayload());
} else {
uni = invokeBlocking(msg, msg);
}
return uni.onItemOrFailure().transformToUni(handleInvocationResult(msg));
}
private void reportFailure(Throwable failure) {
log.messageProcessingException(configuration.methodAsString(), failure);
health.reportApplicationFailure(configuration.methodAsString(), failure);
}
private void processMethodReturningAUni() {
this.subscriber = IgnoringSubscriber.INSTANCE;
boolean invokeWithPayload = MediatorConfiguration.Consumption.PAYLOAD == configuration.consumption();
if (configuration.isBlocking()) {
if (configuration.isBlockingExecutionOrdered()) {
this.function = upstream -> MultiUtils.handlePreProcessingAcknowledgement(upstream, configuration)
.onItem().transformToUniAndConcatenate(msg -> invokeBlockingAndHandleOutcome(invokeWithPayload, msg))
.onFailure().invoke(this::reportFailure);
} else {
this.function = upstream -> MultiUtils.handlePreProcessingAcknowledgement(upstream, configuration)
.onItem().transformToUniAndMerge(msg -> invokeBlockingAndHandleOutcome(invokeWithPayload, msg))
.onFailure().invoke(this::reportFailure);
}
} else {
this.function = upstream -> MultiUtils.handlePreProcessingAcknowledgement(upstream, configuration)
.onItem().transformToUniAndConcatenate(msg -> {
Uni uni;
if (invokeWithPayload) {
uni = invokeOnMessageContext(msg, msg.getPayload())
.onItem().transformToUni(u -> (Uni) u);
} else {
uni = invokeOnMessageContext(msg, msg)
.onItem().transformToUni(u -> (Uni) u);
}
return uni.onItemOrFailure().transformToUni(handleInvocationResult(msg));
})
.onFailure().invoke(this::reportFailure);
}
}
@SuppressWarnings("unchecked")
private void processMethodReturningASubscriber() {
Object result = invoke();
if (!(result instanceof Flow.Subscriber) && !(result instanceof SubscriberBuilder) && !(result instanceof Subscriber)) {
throw ex.illegalStateExceptionForSubscriberOrSubscriberBuilder(result.getClass().getName());
}
if (configuration.consumption() == MediatorConfiguration.Consumption.STREAM_OF_PAYLOAD) {
Flow.Subscriber userSubscriber;
if (result instanceof Flow.Subscriber) {
userSubscriber = (Flow.Subscriber) result;
} else if (result instanceof Subscriber) {
userSubscriber = AdaptersToFlow.subscriber((Subscriber) result);
} else {
userSubscriber = AdaptersToFlow.subscriber(((SubscriberBuilder) result).build());
}
SubscriberWrapper> wrapper = new SubscriberWrapper<>(userSubscriber, Message::getPayload,
(m, t) -> {
if (configuration.getAcknowledgment() == Acknowledgment.Strategy.POST_PROCESSING) {
if (t != null) {
return m.nack(t);
} else {
return m.ack();
}
} else {
CompletableFuture future = new CompletableFuture<>();
if (t != null) {
future.completeExceptionally(t);
} else {
future.complete(null);
}
return future;
}
});
this.function = upstream -> MultiUtils.handlePreProcessingAcknowledgement(upstream, configuration);
this.subscriber = wrapper;
} else {
Flow.Subscriber> sub;
if (result instanceof Flow.Subscriber) {
sub = (Flow.Subscriber>) result;
} else if (result instanceof Subscriber) {
sub = AdaptersToFlow.subscriber((Subscriber>) result);
} else {
sub = AdaptersToFlow.subscriber(((SubscriberBuilder, Void>) result).build());
}
this.function = upstream -> MultiUtils.handlePreProcessingAcknowledgement(upstream, configuration);
this.subscriber = sub;
}
}
@Override
public void terminate() {
Subscriptions.cancel(subscription);
}
}