001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.server.reactive;
018
019import java.util.concurrent.*;
020
021/**
022 * SPI that bridges a third-party reactive return type to one of the two JDK-native shapes that the
023 * {@link ReactiveResponseProcessor} understands: a {@link CompletionStage} (single value) or a
024 * {@link java.util.concurrent.Flow.Publisher Flow.Publisher} (a stream of values).
025 *
026 * <h5 class='topic'>Why this exists</h5>
027 * <p>
028 * The opt-in {@code juneau-rest-server-reactive} module handles {@link java.util.concurrent.Flow.Publisher Flow.Publisher}
029 * (and, via the existing {@code AsyncResponseProcessor}, {@link CompletionStage}) using only JDK APIs &mdash;
030 * no external dependency. Libraries such as Project Reactor ({@code Mono} / {@code Flux}), RxJava 3
031 * ({@code Single} / {@code Maybe} / {@code Flowable} / {@code Observable}), and the
032 * Reactive-Streams {@code org.reactivestreams.Publisher} are NOT on the core classpath. The opt-in
033 * {@code juneau-rest-server-reactive-reactor} module ships {@code ReactiveStreamsAdapter} implementations that
034 * convert those types into the JDK-native shapes, so the streaming / buffering / SSE / NDJSON plumbing
035 * is implemented exactly once in the core.
036 *
037 * <h5 class='topic'>Registration</h5>
038 * <p>
039 * Adapters are discovered via the JDK {@link java.util.ServiceLoader} mechanism. A bridge module (or a
040 * consumer who wants a custom adapter) ships a
041 * {@code META-INF/services/ReactiveStreamsAdapter} file listing the
042 * implementation class names. The {@link ReactiveResponseProcessor} loads the providers once (lazily,
043 * at first use) and skips any provider whose backing library is absent from the runtime classpath
044 * (a {@link NoClassDefFoundError} / {@link java.util.ServiceConfigurationError} on instantiation is swallowed).
045 * This means a single {@code juneau-rest-server-reactive-reactor} jar can declare Reactor, RxJava, and
046 * Reactive-Streams adapters while the consumer pulls only the {@code provided}-scope libraries they
047 * actually want.
048 *
049 * @see ReactiveResponseProcessor
050 * @see Adaptation
051 * @since 10.0.0
052 */
053public interface ReactiveStreamsAdapter {
054
055   /**
056    * Returns whether this adapter recognizes the supplied return value.
057    *
058    * <p>
059    * Implementations must return {@code false} (rather than throw) for values they do not handle, and
060    * must be cheap to call &mdash; this is invoked on the response hot path for every non-null handler
061    * return value once at least one adapter is registered.
062    *
063    * @param value The {@code @RestOp} handler return value. Never {@code null}.
064    * @return {@code true} if {@link #adapt(Object)} can convert this value.
065    */
066   boolean canAdapt(Object value);
067
068   /**
069    * Converts the supplied reactive value to a JDK-native {@link Adaptation}.
070    *
071    * <p>
072    * Only called when {@link #canAdapt(Object)} returned {@code true} for the same value.
073    *
074    * @param value The reactive value to adapt. Never {@code null}.
075    * @return The adapted single-value or streaming shape. Never {@code null}.
076    */
077   Adaptation adapt(Object value);
078}