org.jboss.resteasy.reactive.RestMulti Maven / Gradle / Ivy
package org.jboss.resteasy.reactive;
import static io.smallrye.mutiny.helpers.ParameterValidation.MAPPER_RETURNED_NULL;
import static io.smallrye.mutiny.helpers.ParameterValidation.nonNull;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Flow;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.LongFunction;
import org.jboss.resteasy.reactive.common.util.CaseInsensitiveMap;
import org.jboss.resteasy.reactive.common.util.MultivaluedTreeMap;
import io.smallrye.mutiny.Context;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.groups.MultiMerge;
import io.smallrye.mutiny.helpers.EmptyUniSubscription;
import io.smallrye.mutiny.helpers.Subscriptions;
import io.smallrye.mutiny.infrastructure.Infrastructure;
import io.smallrye.mutiny.operators.AbstractMulti;
import io.smallrye.mutiny.operators.AbstractUni;
import io.smallrye.mutiny.subscription.ContextSupport;
import io.smallrye.mutiny.subscription.MultiSubscriber;
import io.smallrye.mutiny.subscription.UniSubscriber;
import io.smallrye.mutiny.subscription.UniSubscription;
/**
* A wrapper around {@link Multi} that gives resource methods a way to specify the HTTP status code and HTTP headers
* when streaming a result.
*/
@SuppressWarnings("ReactiveStreamsUnusedPublisher")
public abstract class RestMulti extends AbstractMulti {
public abstract Integer getStatus();
public abstract Map> getHeaders();
public static RestMulti.SyncRestMulti.Builder fromMultiData(Multi multi) {
return new RestMulti.SyncRestMulti.Builder<>(multi);
}
public static RestMulti fromUniResponse(Uni uni,
Function> dataExtractor) {
return fromUniResponse(uni, dataExtractor, null, null);
}
public static RestMulti fromUniResponse(Uni uni,
Function> dataExtractor,
Function>> headersExtractor) {
return fromUniResponse(uni, dataExtractor, headersExtractor, null);
}
public static RestMulti fromUniResponse(Uni uni,
Function> dataExtractor,
Function>> headersExtractor,
Function statusExtractor) {
Function super T, ? extends Multi extends R>> actualDataExtractor = Infrastructure
.decorate(nonNull(dataExtractor, "dataExtractor"));
return (RestMulti) Infrastructure.onMultiCreation(new AsyncRestMulti<>(uni, actualDataExtractor,
headersExtractor, statusExtractor));
}
public static class SyncRestMulti extends RestMulti {
private final Multi multi;
private final Integer status;
private final MultivaluedTreeMap headers;
private final long demand;
private final boolean encodeAsJsonArray;
@Override
public void subscribe(MultiSubscriber super T> subscriber) {
multi.subscribe(Infrastructure.onMultiSubscription(multi, subscriber));
}
private SyncRestMulti(Builder builder) {
this.multi = builder.multi;
this.status = builder.status;
this.headers = builder.headers;
this.demand = builder.demand;
this.encodeAsJsonArray = builder.encodeAsJsonArray;
}
@Override
public Integer getStatus() {
return status;
}
@Override
public Map> getHeaders() {
return headers;
}
public long getDemand() {
return demand;
}
public boolean encodeAsJsonArray() {
return encodeAsJsonArray;
}
public static class Builder {
private final Multi multi;
private final MultivaluedTreeMap headers = new CaseInsensitiveMap<>();
private Integer status;
private long demand = 1;
private boolean encodeAsJsonArray = true;
private Builder(Multi multi) {
this.multi = Objects.requireNonNull(multi, "multi cannot be null");
}
/**
* Configure the {@code demand} signaled to the wrapped {@link Multi}, defaults to {@code 1}.
*
*
* A demand of {@code 1} guarantees serial/sequential processing, any higher demand supports
* concurrent processing. A demand greater {@code 1}, with concurrent {@link Multi} processing,
* does not guarantee element order - this means that elements emitted by the
* {@link RestMulti#fromMultiData(Multi) RestMulti.fromMultiData(Multi)} source Multi
}
* will be produced in a non-deterministic order.
*
* @see MultiMerge#withConcurrency(int) Multi.createBy().merging().withConcurrency(int)
* @see Multi#capDemandsTo(long)
* @see Multi#capDemandsUsing(LongFunction)
*/
public Builder withDemand(long demand) {
if (demand <= 0) {
throw new IllegalArgumentException("Demand must be greater than zero");
}
this.demand = demand;
return this;
}
/**
* Configure whether objects produced by the wrapped {@link Multi} are encoded as JSON array elements, which is the
* default.
*
*
* {@code encodeAsJsonArray(false)} produces separate JSON objects.
*
*
* This property is only used for JSON object results and ignored for SSE and chunked streaming.
*/
public Builder encodeAsJsonArray(boolean encodeAsJsonArray) {
this.encodeAsJsonArray = encodeAsJsonArray;
return this;
}
public Builder status(int status) {
this.status = status;
return this;
}
public Builder header(String name, String value) {
if (value == null) {
headers.remove(name);
return this;
}
headers.add(name, value);
return this;
}
public RestMulti build() {
return new SyncRestMulti<>(this);
}
}
}
// Copied from: io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni while adding header and status extraction
public static class AsyncRestMulti extends RestMulti {
private final Function super I, ? extends Multi extends O>> dataExtractor;
private final Function statusExtractor;
private final Function>> headersExtractor;
private final AtomicReference status;
private final AtomicReference