Move ReactiveWrappers into o.s.d.util package.
ReactiveWrappers is not tied to repositories so it does not need to reside in the repository.util package. This utility is now located in the data.util package for a better module design and to prevent package cycles. See #2708
This commit is contained in:
@@ -25,6 +25,7 @@ import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.core.DecoratingProxy;
|
||||
import org.springframework.data.domain.AuditorAware;
|
||||
import org.springframework.data.domain.ReactiveAuditorAware;
|
||||
import org.springframework.data.util.ReactiveWrappers;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -38,8 +39,6 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
class AuditingBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {
|
||||
|
||||
private static final boolean PROJECT_REACTOR_PRESENT = ClassUtils.isPresent("reactor.core.publisher.Flux",
|
||||
AuditingBeanRegistrationAotProcessor.class.getClassLoader());
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@@ -50,7 +49,7 @@ class AuditingBeanRegistrationAotProcessor implements BeanRegistrationAotProcess
|
||||
generationContext.getRuntimeHints());
|
||||
}
|
||||
|
||||
if (PROJECT_REACTOR_PRESENT && isReactiveAuditorAware(registeredBean)) {
|
||||
if (ReactiveWrappers.PROJECT_REACTOR_PRESENT && isReactiveAuditorAware(registeredBean)) {
|
||||
return (generationContext, beanRegistrationCode) -> registerSpringProxy(ReactiveAuditorAware.class,
|
||||
generationContext.getRuntimeHints());
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
|
||||
import org.springframework.data.querydsl.QuerydslUtils;
|
||||
import org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor;
|
||||
import org.springframework.data.util.ReactiveWrappers;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import com.querydsl.core.types.Predicate;
|
||||
|
||||
@@ -50,7 +50,7 @@ class QuerydslHints implements RuntimeHintsRegistrar {
|
||||
builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS);
|
||||
});
|
||||
|
||||
if (ClassUtils.isPresent("reactor.core.publisher.Flux", classLoader)) {
|
||||
if (ReactiveWrappers.PROJECT_REACTOR_PRESENT) {
|
||||
|
||||
// repository infrastructure
|
||||
hints.reflection().registerTypes(Arrays.asList( //
|
||||
|
||||
@@ -36,8 +36,8 @@ import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuer
|
||||
import org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery;
|
||||
import org.springframework.data.repository.query.QueryByExampleExecutor;
|
||||
import org.springframework.data.repository.query.ReactiveQueryByExampleExecutor;
|
||||
import org.springframework.data.util.ReactiveWrappers;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} holding required hints to bootstrap data repositories. <br />
|
||||
@@ -49,9 +49,6 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
class RepositoryRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
private static final boolean PROJECT_REACTOR_PRESENT = ClassUtils.isPresent("reactor.core.publisher.Flux",
|
||||
RepositoryRuntimeHints.class.getClassLoader());
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
|
||||
@@ -71,7 +68,7 @@ class RepositoryRuntimeHints implements RuntimeHintsRegistrar {
|
||||
builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS);
|
||||
});
|
||||
|
||||
if (PROJECT_REACTOR_PRESENT) {
|
||||
if (ReactiveWrappers.PROJECT_REACTOR_PRESENT) {
|
||||
|
||||
// repository infrastructure
|
||||
hints.reflection().registerTypes(Arrays.asList( //
|
||||
|
||||
@@ -28,9 +28,9 @@ import org.springframework.data.repository.core.CrudMethods;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.util.QueryExecutionConverters;
|
||||
import org.springframework.data.repository.util.ReactiveWrapperConverters;
|
||||
import org.springframework.data.repository.util.ReactiveWrappers;
|
||||
import org.springframework.data.util.KotlinReflectionUtils;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.data.util.ReactiveWrappers;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.MethodLookup.MethodPredicate;
|
||||
import org.springframework.data.repository.util.QueryExecutionConverters;
|
||||
import org.springframework.data.repository.util.ReactiveWrapperConverters;
|
||||
import org.springframework.data.repository.util.ReactiveWrappers;
|
||||
import org.springframework.data.util.ReactiveWrappers;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
@@ -19,13 +19,12 @@ import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.util.ReactiveWrapperConverters;
|
||||
import org.springframework.data.repository.util.ReactiveWrappers;
|
||||
import org.springframework.data.util.ReactiveWrappers;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,7 +33,7 @@ import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.MethodLookup.InvokedMethod;
|
||||
import org.springframework.data.repository.core.support.RepositoryInvocationMulticaster.NoOpRepositoryInvocationMulticaster;
|
||||
import org.springframework.data.repository.util.ReactiveWrapperConverters;
|
||||
import org.springframework.data.repository.util.ReactiveWrappers;
|
||||
import org.springframework.data.util.ReactiveWrappers;
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -27,15 +27,14 @@ import java.util.Collection;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.core.KotlinDetector;
|
||||
import org.springframework.data.repository.core.support.RepositoryMethodInvocationListener.RepositoryMethodInvocation;
|
||||
import org.springframework.data.repository.core.support.RepositoryMethodInvocationListener.RepositoryMethodInvocationResult;
|
||||
import org.springframework.data.repository.core.support.RepositoryMethodInvocationListener.RepositoryMethodInvocationResult.State;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.util.ReactiveWrapperConverters;
|
||||
import org.springframework.data.repository.util.ReactiveWrappers;
|
||||
import org.springframework.data.util.KotlinReflectionUtils;
|
||||
import org.springframework.data.util.ReactiveWrappers;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,7 +27,6 @@ import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
@@ -37,7 +36,7 @@ import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConverterFactory;
|
||||
import org.springframework.core.convert.support.ConfigurableConversionService;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.data.repository.util.ReactiveWrappers.ReactiveLibrary;
|
||||
import org.springframework.data.util.ReactiveWrappers.ReactiveLibrary;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -56,7 +55,7 @@ import org.springframework.util.ClassUtils;
|
||||
* @author Christoph Strobl
|
||||
* @author Hantsy Bai
|
||||
* @since 2.0
|
||||
* @see ReactiveWrappers
|
||||
* @see org.springframework.data.util.ReactiveWrappers
|
||||
* @see ReactiveAdapterRegistry
|
||||
*/
|
||||
public abstract class ReactiveWrapperConverters {
|
||||
@@ -64,11 +63,14 @@ public abstract class ReactiveWrapperConverters {
|
||||
private static final List<ReactiveTypeWrapper<?>> REACTIVE_WRAPPERS = new ArrayList<>();
|
||||
private static final GenericConversionService GENERIC_CONVERSION_SERVICE = new GenericConversionService();
|
||||
|
||||
private static final boolean RXJAVA3_PRESENT = ReactiveWrappers.isAvailable(ReactiveLibrary.RXJAVA3);
|
||||
private static final boolean REACTOR_PRESENT = ReactiveWrappers.isAvailable(ReactiveLibrary.PROJECT_REACTOR);
|
||||
private static final boolean KOTLIN_COROUTNES_PRESENT = ReactiveWrappers
|
||||
private static final boolean RXJAVA3_PRESENT = org.springframework.data.util.ReactiveWrappers
|
||||
.isAvailable(ReactiveLibrary.RXJAVA3);
|
||||
private static final boolean REACTOR_PRESENT = org.springframework.data.util.ReactiveWrappers
|
||||
.isAvailable(ReactiveLibrary.PROJECT_REACTOR);
|
||||
private static final boolean KOTLIN_COROUTNES_PRESENT = org.springframework.data.util.ReactiveWrappers
|
||||
.isAvailable(ReactiveLibrary.KOTLIN_COROUTINES);
|
||||
private static final boolean MUTINY_PRESENT = ReactiveWrappers.isAvailable(ReactiveLibrary.MUTINY);
|
||||
private static final boolean MUTINY_PRESENT = org.springframework.data.util.ReactiveWrappers
|
||||
.isAvailable(ReactiveLibrary.MUTINY);
|
||||
|
||||
static {
|
||||
|
||||
@@ -548,8 +550,8 @@ public abstract class ReactiveWrapperConverters {
|
||||
|
||||
static {
|
||||
|
||||
if (ReactiveWrappers.isAvailable(ReactiveLibrary.PROJECT_REACTOR)) {
|
||||
REACTIVE_ADAPTER_REGISTRY = new ReactiveAdapterRegistry();
|
||||
if (org.springframework.data.util.ReactiveWrappers.PROJECT_REACTOR_PRESENT) {
|
||||
REACTIVE_ADAPTER_REGISTRY = ReactiveAdapterRegistry.getSharedInstance();
|
||||
} else {
|
||||
REACTIVE_ADAPTER_REGISTRY = null;
|
||||
}
|
||||
|
||||
@@ -18,18 +18,7 @@ package org.springframework.data.repository.util;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.ReactiveTypeDescriptor;
|
||||
import org.springframework.data.util.ProxyUtils;
|
||||
import org.springframework.data.util.ReflectionUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
|
||||
/**
|
||||
* Utility class to expose details about reactive wrapper types. This class exposes whether a reactive wrapper is
|
||||
@@ -54,33 +43,21 @@ import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
* @see io.smallrye.mutiny.Uni
|
||||
* @see Mono
|
||||
* @see Flux
|
||||
* @deprecated since 3.0, use {@link org.springframework.data.util.ReactiveWrappers} instead as the utility was moved
|
||||
* into the {@code org.springframework.data.util} package.
|
||||
*/
|
||||
@Deprecated(since = "3.0", forRemoval = true)
|
||||
public abstract class ReactiveWrappers {
|
||||
|
||||
private static final boolean PROJECT_REACTOR_PRESENT = ClassUtils.isPresent("reactor.core.publisher.Flux",
|
||||
ReactiveWrappers.class.getClassLoader());
|
||||
|
||||
private static final boolean RXJAVA3_PRESENT = ClassUtils.isPresent("io.reactivex.rxjava3.core.Flowable",
|
||||
ReactiveWrappers.class.getClassLoader());
|
||||
|
||||
private static final boolean KOTLIN_COROUTINES_PRESENT = ClassUtils.isPresent("kotlinx.coroutines.reactor.MonoKt",
|
||||
ReactiveWrappers.class.getClassLoader());
|
||||
|
||||
private static final boolean MUTINY_PRESENT = ClassUtils.isPresent("io.smallrye.mutiny.Multi",
|
||||
ReactiveWrappers.class.getClassLoader());
|
||||
|
||||
private static final Map<Class<?>, Boolean> IS_REACTIVE_TYPE = new ConcurrentReferenceHashMap<>();
|
||||
|
||||
private static final boolean IS_REACTIVE_AVAILABLE = Arrays.stream(ReactiveLibrary.values())
|
||||
.anyMatch(ReactiveWrappers::isAvailable);
|
||||
|
||||
private ReactiveWrappers() {}
|
||||
|
||||
/**
|
||||
* Enumeration of supported reactive libraries.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @deprecated use {@link org.springframework.data.util.ReactiveWrappers.ReactiveLibrary} instead.
|
||||
*/
|
||||
@Deprecated(since = "3.0", forRemoval = true)
|
||||
public enum ReactiveLibrary {
|
||||
|
||||
PROJECT_REACTOR, RXJAVA3, KOTLIN_COROUTINES, MUTINY;
|
||||
@@ -93,7 +70,7 @@ public abstract class ReactiveWrappers {
|
||||
* @return {@literal true} if reactive support is available.
|
||||
*/
|
||||
public static boolean isAvailable() {
|
||||
return IS_REACTIVE_AVAILABLE;
|
||||
return org.springframework.data.util.ReactiveWrappers.isAvailable();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,13 +85,14 @@ public abstract class ReactiveWrappers {
|
||||
|
||||
switch (reactiveLibrary) {
|
||||
case PROJECT_REACTOR:
|
||||
return PROJECT_REACTOR_PRESENT;
|
||||
return org.springframework.data.util.ReactiveWrappers.PROJECT_REACTOR_PRESENT;
|
||||
case RXJAVA3:
|
||||
return RXJAVA3_PRESENT;
|
||||
return org.springframework.data.util.ReactiveWrappers.RXJAVA3_PRESENT;
|
||||
case KOTLIN_COROUTINES:
|
||||
return PROJECT_REACTOR_PRESENT && KOTLIN_COROUTINES_PRESENT;
|
||||
return org.springframework.data.util.ReactiveWrappers.PROJECT_REACTOR_PRESENT
|
||||
&& org.springframework.data.util.ReactiveWrappers.KOTLIN_COROUTINES_PRESENT;
|
||||
case MUTINY:
|
||||
return MUTINY_PRESENT;
|
||||
return org.springframework.data.util.ReactiveWrappers.MUTINY_PRESENT;
|
||||
default:
|
||||
throw new IllegalArgumentException(String.format("Reactive library %s not supported", reactiveLibrary));
|
||||
}
|
||||
@@ -127,7 +105,7 @@ public abstract class ReactiveWrappers {
|
||||
* @return {@literal true} if the {@code type} is a supported reactive wrapper type.
|
||||
*/
|
||||
public static boolean supports(Class<?> type) {
|
||||
return isAvailable() && IS_REACTIVE_TYPE.computeIfAbsent(type, key -> isWrapper(ProxyUtils.getUserClass(key)));
|
||||
return org.springframework.data.util.ReactiveWrappers.supports(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,9 +118,7 @@ public abstract class ReactiveWrappers {
|
||||
|
||||
Assert.notNull(type, "Type must not be null");
|
||||
|
||||
return Arrays.stream(type.getMethods())//
|
||||
.flatMap(ReflectionUtils::returnTypeAndParameters)//
|
||||
.anyMatch(ReactiveWrappers::supports);
|
||||
return org.springframework.data.util.ReactiveWrappers.usesReactiveType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,7 +131,7 @@ public abstract class ReactiveWrappers {
|
||||
|
||||
Assert.notNull(type, "Candidate type must not be null");
|
||||
|
||||
return findDescriptor(type).map(ReactiveTypeDescriptor::isNoValue).orElse(false);
|
||||
return org.springframework.data.util.ReactiveWrappers.isNoValueType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,7 +144,7 @@ public abstract class ReactiveWrappers {
|
||||
|
||||
Assert.notNull(type, "Candidate type must not be null");
|
||||
|
||||
return findDescriptor(type).map(it -> !it.isMultiValue() && !it.isNoValue()).orElse(false);
|
||||
return org.springframework.data.util.ReactiveWrappers.isSingleValueType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,46 +159,7 @@ public abstract class ReactiveWrappers {
|
||||
|
||||
Assert.notNull(type, "Candidate type must not be null");
|
||||
|
||||
// Prevent single-types with a multi-hierarchy supertype to be reported as multi type
|
||||
// See Mono implements Publisher
|
||||
return isSingleValueType(type) ? false
|
||||
: findDescriptor(type).map(ReactiveTypeDescriptor::isMultiValue).orElse(false);
|
||||
return org.springframework.data.util.ReactiveWrappers.isMultiValueType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given type is a reactive wrapper type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private static boolean isWrapper(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Candidate type must not be null");
|
||||
|
||||
return isNoValueType(type) || isSingleValueType(type) || isMultiValueType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a {@link ReactiveTypeDescriptor} for the given wrapper type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private static Optional<ReactiveTypeDescriptor> findDescriptor(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Wrapper type must not be null");
|
||||
|
||||
ReactiveAdapterRegistry adapterRegistry = ReactiveWrapperConverters.RegistryHolder.REACTIVE_ADAPTER_REGISTRY;
|
||||
|
||||
if (adapterRegistry == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
ReactiveAdapter adapter = adapterRegistry.getAdapter(type);
|
||||
if (adapter != null && adapter.getDescriptor().isDeferred()) {
|
||||
return Optional.of(adapter.getDescriptor());
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright 2016-2022 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
|
||||
*
|
||||
* https://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.data.util;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.ReactiveTypeDescriptor;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
|
||||
/**
|
||||
* Utility class to expose details about reactive wrapper types. This class exposes whether a reactive wrapper is
|
||||
* supported in general and whether a particular type is suitable for no-value/single-value/multi-value usage.
|
||||
* <p>
|
||||
* Supported types are discovered by their availability on the class path. This class is typically used to determine
|
||||
* multiplicity and whether a reactive wrapper type is acceptable for a specific operation.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @author Gerrit Meier
|
||||
* @author Hantsy Bai
|
||||
* @since 3.0
|
||||
* @see org.reactivestreams.Publisher
|
||||
* @see io.reactivex.rxjava3.core.Single
|
||||
* @see io.reactivex.rxjava3.core.Maybe
|
||||
* @see io.reactivex.rxjava3.core.Observable
|
||||
* @see io.reactivex.rxjava3.core.Completable
|
||||
* @see io.reactivex.rxjava3.core.Flowable
|
||||
* @see io.smallrye.mutiny.Multi
|
||||
* @see io.smallrye.mutiny.Uni
|
||||
* @see Mono
|
||||
* @see Flux
|
||||
*/
|
||||
public abstract class ReactiveWrappers {
|
||||
|
||||
public static final boolean PROJECT_REACTOR_PRESENT = ClassUtils.isPresent("reactor.core.publisher.Flux",
|
||||
ReactiveWrappers.class.getClassLoader());
|
||||
|
||||
public static final boolean RXJAVA3_PRESENT = ClassUtils.isPresent("io.reactivex.rxjava3.core.Flowable",
|
||||
ReactiveWrappers.class.getClassLoader());
|
||||
|
||||
public static final boolean KOTLIN_COROUTINES_PRESENT = ClassUtils.isPresent("kotlinx.coroutines.reactor.MonoKt",
|
||||
ReactiveWrappers.class.getClassLoader());
|
||||
|
||||
public static final boolean MUTINY_PRESENT = ClassUtils.isPresent("io.smallrye.mutiny.Multi",
|
||||
ReactiveWrappers.class.getClassLoader());
|
||||
|
||||
public static final boolean IS_REACTIVE_AVAILABLE = Arrays.stream(ReactiveLibrary.values())
|
||||
.anyMatch(ReactiveWrappers::isAvailable);
|
||||
private static final Map<Class<?>, Boolean> IS_REACTIVE_TYPE = new ConcurrentReferenceHashMap<>();
|
||||
|
||||
private ReactiveWrappers() {}
|
||||
|
||||
/**
|
||||
* Enumeration of supported reactive libraries.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public enum ReactiveLibrary {
|
||||
|
||||
PROJECT_REACTOR, RXJAVA3, KOTLIN_COROUTINES, MUTINY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@literal true} if reactive support is available. More specifically, whether any of the libraries defined
|
||||
* in {@link ReactiveLibrary} are on the class path.
|
||||
*
|
||||
* @return {@literal true} if reactive support is available.
|
||||
*/
|
||||
public static boolean isAvailable() {
|
||||
return IS_REACTIVE_AVAILABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@literal true} if the {@link ReactiveLibrary} is available.
|
||||
*
|
||||
* @param reactiveLibrary must not be {@literal null}.
|
||||
* @return {@literal true} if the {@link ReactiveLibrary} is available.
|
||||
*/
|
||||
public static boolean isAvailable(ReactiveLibrary reactiveLibrary) {
|
||||
|
||||
Assert.notNull(reactiveLibrary, "Reactive library must not be null");
|
||||
|
||||
switch (reactiveLibrary) {
|
||||
case PROJECT_REACTOR:
|
||||
return PROJECT_REACTOR_PRESENT;
|
||||
case RXJAVA3:
|
||||
return RXJAVA3_PRESENT;
|
||||
case KOTLIN_COROUTINES:
|
||||
return PROJECT_REACTOR_PRESENT && KOTLIN_COROUTINES_PRESENT;
|
||||
case MUTINY:
|
||||
return MUTINY_PRESENT;
|
||||
default:
|
||||
throw new IllegalArgumentException(String.format("Reactive library %s not supported", reactiveLibrary));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@literal true} if the {@code type} is a supported reactive wrapper type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return {@literal true} if the {@code type} is a supported reactive wrapper type.
|
||||
*/
|
||||
public static boolean supports(Class<?> type) {
|
||||
return isAvailable() && IS_REACTIVE_TYPE.computeIfAbsent(type, key -> isWrapper(ProxyUtils.getUserClass(key)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given type uses any reactive wrapper type in its method signatures.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static boolean usesReactiveType(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Type must not be null");
|
||||
|
||||
return Arrays.stream(type.getMethods())//
|
||||
.flatMap(ReflectionUtils::returnTypeAndParameters)//
|
||||
.anyMatch(ReactiveWrappers::supports);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@literal true} if {@code type} is a reactive wrapper type that contains no value.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return {@literal true} if {@code type} is a reactive wrapper type that contains no value.
|
||||
*/
|
||||
public static boolean isNoValueType(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Candidate type must not be null");
|
||||
|
||||
return findDescriptor(type).map(ReactiveTypeDescriptor::isNoValue).orElse(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@literal true} if {@code type} is a reactive wrapper type for a single value.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return {@literal true} if {@code type} is a reactive wrapper type for a single value.
|
||||
*/
|
||||
public static boolean isSingleValueType(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Candidate type must not be null");
|
||||
|
||||
return findDescriptor(type).map(it -> !it.isMultiValue() && !it.isNoValue()).orElse(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@literal true} if {@code type} is a reactive wrapper type supporting multiple values ({@code 0..N}
|
||||
* elements).
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return {@literal true} if {@code type} is a reactive wrapper type supporting multiple values ({@code 0..N}
|
||||
* elements).
|
||||
*/
|
||||
public static boolean isMultiValueType(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Candidate type must not be null");
|
||||
|
||||
// Prevent single-types with a multi-hierarchy supertype to be reported as multi type
|
||||
// See Mono implements Publisher
|
||||
return isSingleValueType(type) ? false
|
||||
: findDescriptor(type).map(ReactiveTypeDescriptor::isMultiValue).orElse(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given type is a reactive wrapper type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private static boolean isWrapper(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Candidate type must not be null");
|
||||
|
||||
return isNoValueType(type) || isSingleValueType(type) || isMultiValueType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a {@link ReactiveTypeDescriptor} for the given wrapper type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private static Optional<ReactiveTypeDescriptor> findDescriptor(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Wrapper type must not be null");
|
||||
|
||||
ReactiveAdapterRegistry adapterRegistry = RegistryHolder.REACTIVE_ADAPTER_REGISTRY;
|
||||
|
||||
if (adapterRegistry == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
ReactiveAdapter adapter = adapterRegistry.getAdapter(type);
|
||||
if (adapter != null && adapter.getDescriptor().isDeferred()) {
|
||||
return Optional.of(adapter.getDescriptor());
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Holder for delayed initialization of {@link ReactiveAdapterRegistry}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static class RegistryHolder {
|
||||
|
||||
static final @Nullable ReactiveAdapterRegistry REACTIVE_ADAPTER_REGISTRY;
|
||||
|
||||
static {
|
||||
|
||||
if (ReactiveWrappers.isAvailable(ReactiveWrappers.ReactiveLibrary.PROJECT_REACTOR)) {
|
||||
REACTIVE_ADAPTER_REGISTRY = ReactiveAdapterRegistry.getSharedInstance();
|
||||
} else {
|
||||
REACTIVE_ADAPTER_REGISTRY = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user