io.smallrye.reactive.messaging.providers.extension.AbstractEmitter Maven / Gradle / Ivy
package io.smallrye.reactive.messaging.providers.extension;
import static io.smallrye.reactive.messaging.providers.i18n.ProviderExceptions.ex;
import java.util.concurrent.Flow.Publisher;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import org.eclipse.microprofile.reactive.messaging.Message;
import org.eclipse.microprofile.reactive.messaging.OnOverflow;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.subscription.BackPressureStrategy;
import io.smallrye.mutiny.subscription.MultiEmitter;
import io.smallrye.reactive.messaging.EmitterConfiguration;
import io.smallrye.reactive.messaging.MessagePublisherProvider;
import io.smallrye.reactive.messaging.providers.helpers.BroadcastHelper;
import io.smallrye.reactive.messaging.providers.helpers.NoStackTraceException;
public abstract class AbstractEmitter implements MessagePublisherProvider {
public static final NoStackTraceException NO_SUBSCRIBER_EXCEPTION = new NoStackTraceException(
"Unable to process message - no subscriber");
protected final AtomicReference>> internal = new AtomicReference<>();
protected final Multi> publisher;
protected final String name;
protected final AtomicReference synchronousFailure = new AtomicReference<>();
private final OnOverflow.Strategy overflow;
@SuppressWarnings("unchecked")
public AbstractEmitter(EmitterConfiguration config, long defaultBufferSize) {
this.name = config.name();
this.overflow = config.overflowBufferStrategy();
if (defaultBufferSize <= 0) {
throw ex.illegalArgumentForDefaultBuffer();
}
Consumer>> deferred = fe -> {
MultiEmitter> previous = internal.getAndSet(fe);
if (previous != null) {
previous.complete();
}
};
Multi> tempPublisher = getPublisherForStrategy(config.overflowBufferStrategy(),
config.overflowBufferSize(),
defaultBufferSize, deferred);
if (config.broadcast()) {
publisher = (Multi>) BroadcastHelper
.broadcastPublisher(tempPublisher, config.numberOfSubscriberBeforeConnecting());
} else {
publisher = tempPublisher;
}
}
public synchronized void complete() {
MultiEmitter> emitter = verify();
if (emitter != null) {
emitter.complete();
}
}
public synchronized void error(Exception e) {
if (e == null) {
throw ex.illegalArgumentForException("null");
}
MultiEmitter> emitter = verify();
if (emitter != null) {
emitter.fail(e);
}
}
public synchronized boolean isCancelled() {
MultiEmitter> emitter = internal.get();
return emitter == null || emitter.isCancelled();
}
public boolean hasRequests() {
MultiEmitter> emitter = internal.get();
return !isCancelled() && emitter.requested() > 0;
}
Multi> getPublisherForStrategy(OnOverflow.Strategy overFlowStrategy, long bufferSize,
long defaultBufferSize,
Consumer>> deferred) {
if (overFlowStrategy == null) {
overFlowStrategy = OnOverflow.Strategy.BUFFER;
}
switch (overFlowStrategy) {
case BUFFER:
if (bufferSize > 0) {
return ThrowingEmitter.create(deferred, bufferSize);
} else {
return ThrowingEmitter.create(deferred, defaultBufferSize);
}
case UNBOUNDED_BUFFER:
return Multi.createFrom().emitter(deferred, BackPressureStrategy.BUFFER);
case THROW_EXCEPTION:
return ThrowingEmitter.create(deferred, 0);
case DROP:
return Multi.createFrom().emitter(deferred, BackPressureStrategy.DROP);
case FAIL:
return Multi.createFrom().emitter(deferred, BackPressureStrategy.ERROR);
case LATEST:
return Multi.createFrom().emitter(deferred, BackPressureStrategy.LATEST);
case NONE:
return Multi.createFrom().emitter(deferred, BackPressureStrategy.IGNORE);
default:
throw ex.illegalArgumentForBackPressure(overFlowStrategy);
}
}
/**
* Creates the stream when using the default buffer size.
*
* @param defaultBufferSize the default buffer size
* @param stream the upstream
* @return the stream.
*/
Multi> getPublisherUsingBufferStrategy(long defaultBufferSize,
Multi> stream) {
int size = (int) defaultBufferSize;
return stream
.onOverflow().buffer(size - 2)
.onFailure().invoke(synchronousFailure::set);
}
@Override
public Publisher> getPublisher() {
return publisher;
}
protected synchronized void emit(Message message) {
if (message == null) {
throw ex.illegalArgumentForNullValue();
}
MultiEmitter> emitter = verify();
if (emitter == null) {
if (overflow == OnOverflow.Strategy.DROP) {
// There are no subscribers, but because we use the DROP strategy, just ignore the event.
// However, nack the message, so the sender can be aware of the rejection.
message.nack(NO_SUBSCRIBER_EXCEPTION);
}
return;
}
if (synchronousFailure.get() != null) {
throw ex.incomingNotFoundForEmitter(synchronousFailure.get());
}
if (emitter.isCancelled()) {
throw ex.illegalStateForDownstreamCancel();
}
emitter.emit(message);
if (synchronousFailure.get() != null) {
throw ex.illegalStateForEmitterWhileEmitting(synchronousFailure.get());
}
}
protected MultiEmitter> verify() {
MultiEmitter> emitter = internal.get();
if (emitter == null) {
if (overflow == OnOverflow.Strategy.DROP) {
// Just ignore the signal in this case, the message would have been dropped anyway.
return null;
} else {
throw ex.noEmitterForChannel(name);
}
}
if (emitter.isCancelled()) {
throw ex.illegalStateForCancelledSubscriber(name);
}
return emitter;
}
}