diff --git a/src/main/java/org/springframework/data/repository/core/RepositoryMetadata.java b/src/main/java/org/springframework/data/repository/core/RepositoryMetadata.java index 1d2c72d07..b0e336d74 100644 --- a/src/main/java/org/springframework/data/repository/core/RepositoryMetadata.java +++ b/src/main/java/org/springframework/data/repository/core/RepositoryMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2015 the original author or authors. + * Copyright 2011-2016 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. @@ -83,4 +83,12 @@ public interface RepositoryMetadata { * @since 1.11 */ Set> getAlternativeDomainTypes(); + + /** + * Returns whether the repository is a reactive one, i.e. if it uses reactive types in one of its methods. + * + * @return + * @since 2.0 + */ + boolean isReactiveRepository(); } diff --git a/src/main/java/org/springframework/data/repository/core/support/AbstractRepositoryMetadata.java b/src/main/java/org/springframework/data/repository/core/support/AbstractRepositoryMetadata.java index 190dc129d..4614489e0 100644 --- a/src/main/java/org/springframework/data/repository/core/support/AbstractRepositoryMetadata.java +++ b/src/main/java/org/springframework/data/repository/core/support/AbstractRepositoryMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2015 the original author or authors. + * Copyright 2011-2016 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. @@ -25,6 +25,7 @@ import org.springframework.data.repository.Repository; 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.ReactiveWrappers; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.ReflectionUtils; import org.springframework.data.util.TypeInformation; @@ -126,6 +127,15 @@ public abstract class AbstractRepositoryMetadata implements RepositoryMetadata { return Collections.emptySet(); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.RepositoryMetadata#isReactiveRepository() + */ + @Override + public boolean isReactiveRepository() { + return ReactiveWrappers.usesReactiveType(repositoryInterface); + } + /** * Recursively unwraps well known wrapper types from the given {@link TypeInformation}. * diff --git a/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java b/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java index 71f9ce485..dcaa267f2 100644 --- a/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java +++ b/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java @@ -47,11 +47,12 @@ import org.springframework.util.ClassUtils; * * @author Oliver Gierke * @author Thomas Darimont + * @author Mark Paluch */ class DefaultRepositoryInformation implements RepositoryInformation { - @SuppressWarnings("rawtypes") private static final TypeVariable>[] PARAMETERS = Repository.class - .getTypeParameters(); + @SuppressWarnings("rawtypes") // + private static final TypeVariable>[] PARAMETERS = Repository.class.getTypeParameters(); private static final String DOMAIN_TYPE_NAME = PARAMETERS[0].getName(); private static final String ID_TYPE_NAME = PARAMETERS[1].getName(); @@ -334,42 +335,13 @@ class DefaultRepositoryInformation implements RepositoryInformation { return metadata.getAlternativeDomainTypes(); } - /** - * Checks the given method's parameters to match the ones of the given base class method. Matches generic arguments - * against the ones bound in the given repository interface. - * - * @param method - * @param baseClassMethod - * @return + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.RepositoryMetadata#isReactiveRepository() */ - private boolean parametersMatch(Method method, Method baseClassMethod) { - - Class[] methodParameterTypes = method.getParameterTypes(); - Type[] genericTypes = baseClassMethod.getGenericParameterTypes(); - Class[] types = baseClassMethod.getParameterTypes(); - - for (int i = 0; i < genericTypes.length; i++) { - - Type genericType = genericTypes[i]; - Class type = types[i]; - MethodParameter parameter = new MethodParameter(method, i); - Class parameterType = resolveParameterType(parameter, metadata.getRepositoryInterface()); - - if (genericType instanceof TypeVariable) { - - if (!matchesGenericType((TypeVariable) genericType, ResolvableType.forMethodParameter(parameter))) { - return false; - } - - continue; - } - - if (!type.isAssignableFrom(parameterType) || !type.equals(methodParameterTypes[i])) { - return false; - } - } - - return true; + @Override + public boolean isReactiveRepository() { + return metadata.isReactiveRepository(); } /** @@ -408,4 +380,42 @@ class DefaultRepositoryInformation implements RepositoryInformation { return false; } + + /** + * Checks the given method's parameters to match the ones of the given base class method. Matches generic arguments + * against the ones bound in the given repository interface. + * + * @param method + * @param baseClassMethod + * @return + */ + private boolean parametersMatch(Method method, Method baseClassMethod) { + + Class[] methodParameterTypes = method.getParameterTypes(); + Type[] genericTypes = baseClassMethod.getGenericParameterTypes(); + Class[] types = baseClassMethod.getParameterTypes(); + + for (int i = 0; i < genericTypes.length; i++) { + + Type genericType = genericTypes[i]; + Class type = types[i]; + MethodParameter parameter = new MethodParameter(method, i); + Class parameterType = resolveParameterType(parameter, metadata.getRepositoryInterface()); + + if (genericType instanceof TypeVariable) { + + if (!matchesGenericType((TypeVariable) genericType, ResolvableType.forMethodParameter(parameter))) { + return false; + } + + continue; + } + + if (!type.isAssignableFrom(parameterType) || !type.equals(methodParameterTypes[i])) { + return false; + } + } + + return true; + } } diff --git a/src/main/java/org/springframework/data/repository/core/support/QueryExecutionResultHandler.java b/src/main/java/org/springframework/data/repository/core/support/QueryExecutionResultHandler.java index 8fc5e3f8e..a7dd4ae82 100644 --- a/src/main/java/org/springframework/data/repository/core/support/QueryExecutionResultHandler.java +++ b/src/main/java/org/springframework/data/repository/core/support/QueryExecutionResultHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2016 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. @@ -72,14 +72,15 @@ class QueryExecutionResultHandler { TypeDescriptor targetType = TypeDescriptor.valueOf(expectedReturnType); - if(conversionService.canConvert(WRAPPER_TYPE, returnTypeDescriptor) - && !conversionService.canBypassConvert(WRAPPER_TYPE, targetType)) { + if (conversionService.canConvert(WRAPPER_TYPE, returnTypeDescriptor) + && !conversionService.canBypassConvert(WRAPPER_TYPE, targetType)) { return conversionService.convert(new NullableWrapper(result), expectedReturnType); } - if(result != null && conversionService.canConvert(TypeDescriptor.valueOf(result.getClass()), returnTypeDescriptor) - && !conversionService.canBypassConvert(TypeDescriptor.valueOf(result.getClass()), targetType)) { + if (result != null + && conversionService.canConvert(TypeDescriptor.valueOf(result.getClass()), returnTypeDescriptor) + && !conversionService.canBypassConvert(TypeDescriptor.valueOf(result.getClass()), targetType)) { return conversionService.convert(result, expectedReturnType); } diff --git a/src/main/java/org/springframework/data/repository/core/support/ReactiveRepositoryInformation.java b/src/main/java/org/springframework/data/repository/core/support/ReactiveRepositoryInformation.java index da387019f..5fc749187 100644 --- a/src/main/java/org/springframework/data/repository/core/support/ReactiveRepositoryInformation.java +++ b/src/main/java/org/springframework/data/repository/core/support/ReactiveRepositoryInformation.java @@ -17,15 +17,21 @@ package org.springframework.data.repository.core.support; import static org.springframework.core.GenericTypeResolver.*; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; + import java.lang.reflect.Method; import java.lang.reflect.Type; +import java.util.Arrays; import java.util.function.BiPredicate; import org.springframework.core.MethodParameter; import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.util.QueryExecutionConverters; +import org.springframework.data.repository.util.ReactiveWrapperConverters; import org.springframework.util.Assert; /** @@ -33,29 +39,22 @@ import org.springframework.util.Assert; * converted for invocation of implementation methods. * * @author Mark Paluch + * @author Oliver Gierke * @since 2.0 */ public class ReactiveRepositoryInformation extends DefaultRepositoryInformation { - private final ConversionService conversionService; - /** - * Creates a new {@link ReactiveRepositoryInformation} for the given repository interface and repository base class - * using a {@link ConversionService}. + * Creates a new {@link ReactiveRepositoryInformation} for the given {@link RepositoryMetadata}, repository base + * class, custom implementation and {@link ConversionService}. * * @param metadata must not be {@literal null}. * @param repositoryBaseClass must not be {@literal null}. - * @param customImplementationClass - * @param conversionService must not be {@literal null}. + * @param customImplementationClass can be {@literal null}. */ public ReactiveRepositoryInformation(RepositoryMetadata metadata, Class repositoryBaseClass, - Class customImplementationClass, ConversionService conversionService) { - + Class customImplementationClass) { super(metadata, repositoryBaseClass, customImplementationClass); - - Assert.notNull(conversionService, "Conversion service must not be null!"); - - this.conversionService = conversionService; } /** @@ -63,8 +62,8 @@ public class ReactiveRepositoryInformation extends DefaultRepositoryInformation * at the target class. Returns the given method if the given base class does not declare the method given. Takes * generics into account. * - * @param method must not be {@literal null} - * @param baseClass + * @param method must not be {@literal null}. + * @param baseClass can be {@literal null}. * @return */ @Override @@ -76,13 +75,13 @@ public class ReactiveRepositoryInformation extends DefaultRepositoryInformation if (usesParametersWithReactiveWrappers(method)) { - Method candidate = getMethodCandidate(method, baseClass, new AssignableWrapperMatch(method)); + Method candidate = getMethodCandidate(method, baseClass, new AssignableWrapperMatch(method.getParameterTypes())); if (candidate != null) { return candidate; } - candidate = getMethodCandidate(method, baseClass, new WrapperConversionMatch(method, conversionService)); + candidate = getMethodCandidate(method, baseClass, WrapperConversionMatch.of(method.getParameterTypes())); if (candidate != null) { return candidate; @@ -90,31 +89,50 @@ public class ReactiveRepositoryInformation extends DefaultRepositoryInformation } Method candidate = getMethodCandidate(method, baseClass, - new MatchParameterOrComponentType(method, getRepositoryInterface())); + MatchParameterOrComponentType.of(method, getRepositoryInterface())); - if (candidate != null) { - return candidate; - } - - return method; + return candidate != null ? candidate : method; } - private boolean usesParametersWithReactiveWrappers(Method method) { + /** + * Checks whether the type is a wrapper without unwrapping support. Reactive wrappers don't like to be unwrapped. + * + * @param parameterType must not be {@literal null}. + * @return + */ + static boolean isNonUnwrappingWrapper(Class parameterType) { - boolean wantsWrappers = false; + Assert.notNull(parameterType, "Parameter type must not be null!"); - for (Class parameterType : method.getParameterTypes()) { - - if (isNonUnwrappingWrapper(parameterType)) { - wantsWrappers = true; - break; - } - } - - return wantsWrappers; + return QueryExecutionConverters.supports(parameterType) + && !QueryExecutionConverters.supportsUnwrapping(parameterType); } - private Method getMethodCandidate(Method method, Class baseClass, BiPredicate, Integer> predicate) { + /** + * Returns whether the given {@link Method} uses a reactive wrapper type as parameter. + * + * @param method must not be {@literal null}. + * @return + */ + private static boolean usesParametersWithReactiveWrappers(Method method) { + + Assert.notNull(method, "Method must not be null!"); + + return Arrays.stream(method.getParameterTypes())// + .anyMatch(ReactiveRepositoryInformation::isNonUnwrappingWrapper); + } + + /** + * Returns a candidate method from the base class for the given one or the method given in the first place if none one + * the base class matches. + * + * @param method must not be {@literal null}. + * @param baseClass must not be {@literal null}. + * @param predicate must not be {@literal null}. + * @return + */ + private static Method getMethodCandidate(Method method, Class baseClass, + BiPredicate, Integer> predicate) { for (Method baseClassMethod : baseClass.getMethods()) { @@ -143,12 +161,13 @@ public class ReactiveRepositoryInformation extends DefaultRepositoryInformation * Checks the given method's parameters to match the ones of the given base class method. Matches generic arguments * against the ones bound in the given repository interface. * - * @param method - * @param baseClassMethod - * @param predicate + * @param method must not be {@literal null}. + * @param baseClassMethod must not be {@literal null}. + * @param predicate must not be {@literal null}. * @return */ - private boolean parametersMatch(Method method, Method baseClassMethod, BiPredicate, Integer> predicate) { + private static boolean parametersMatch(Method method, Method baseClassMethod, + BiPredicate, Integer> predicate) { Type[] genericTypes = baseClassMethod.getGenericParameterTypes(); Class[] types = baseClassMethod.getParameterTypes(); @@ -162,46 +181,34 @@ public class ReactiveRepositoryInformation extends DefaultRepositoryInformation return true; } - /** - * Checks whether the type is a wrapper without unwrapping support. Reactive wrappers don't like to be unwrapped. - * - * @param parameterType - * @return - */ - static boolean isNonUnwrappingWrapper(Class parameterType) { - return QueryExecutionConverters.supports(parameterType) - && !QueryExecutionConverters.supportsUnwrapping(parameterType); - } - /** * {@link BiPredicate} to check whether a method parameter is a {@link #isNonUnwrappingWrapper(Class)} and can be * converted into a different wrapper. Usually {@link rx.Observable} to {@link org.reactivestreams.Publisher} * conversion. */ + @RequiredArgsConstructor(staticName = "of") static class WrapperConversionMatch implements BiPredicate, Integer> { - final Method declaredMethod; - final Class[] declaredParameterTypes; - final ConversionService conversionService; - - public WrapperConversionMatch(Method declaredMethod, ConversionService conversionService) { - - this.declaredMethod = declaredMethod; - this.declaredParameterTypes = declaredMethod.getParameterTypes(); - this.conversionService = conversionService; - } + private static final ConversionService CONVERSION_SERVICE = ReactiveWrapperConverters + .registerConvertersIn(new DefaultConversionService()); + private final Class[] declaredParameterTypes; + /* + * (non-Javadoc) + * @see java.util.function.BiPredicate#test(java.lang.Object, java.lang.Object) + */ @Override public boolean test(Class candidateParameterType, Integer index) { - if (isNonUnwrappingWrapper(candidateParameterType) && isNonUnwrappingWrapper(declaredParameterTypes[index])) { - - if (conversionService.canConvert(declaredParameterTypes[index], candidateParameterType)) { - return true; - } + if (!isNonUnwrappingWrapper(candidateParameterType)) { + return false; } - return false; + if (!isNonUnwrappingWrapper(declaredParameterTypes[index])) { + return false; + } + + return CONVERSION_SERVICE.canConvert(declaredParameterTypes[index], candidateParameterType); } } @@ -210,28 +217,27 @@ public class ReactiveRepositoryInformation extends DefaultRepositoryInformation * a declared parameter. Usually {@link reactor.core.publisher.Flux} vs. {@link org.reactivestreams.Publisher} * conversion. */ + @RequiredArgsConstructor(staticName = "of") static class AssignableWrapperMatch implements BiPredicate, Integer> { - final Method declaredMethod; - final Class[] declaredParameterTypes; - - public AssignableWrapperMatch(Method declaredMethod) { - - this.declaredMethod = declaredMethod; - this.declaredParameterTypes = declaredMethod.getParameterTypes(); - } + private final Class[] declaredParameterTypes; + /* + * (non-Javadoc) + * @see java.util.function.BiPredicate#test(java.lang.Object, java.lang.Object) + */ @Override public boolean test(Class candidateParameterType, Integer index) { - if (isNonUnwrappingWrapper(candidateParameterType) && isNonUnwrappingWrapper(declaredParameterTypes[index])) { - - if (declaredParameterTypes[index].isAssignableFrom(candidateParameterType)) { - return true; - } + if (!isNonUnwrappingWrapper(candidateParameterType)) { + return false; } - return false; + if (!isNonUnwrappingWrapper(declaredParameterTypes[index])) { + return false; + } + + return declaredParameterTypes[index].isAssignableFrom(candidateParameterType); } } @@ -241,31 +247,29 @@ public class ReactiveRepositoryInformation extends DefaultRepositoryInformation * * @see QueryExecutionConverters */ + @RequiredArgsConstructor(access = AccessLevel.PRIVATE) static class MatchParameterOrComponentType implements BiPredicate, Integer> { - final Method declaredMethod; - final Class[] declaredParameterTypes; - final Class repositoryInterface; + private final Method declaredMethod; + private final Class[] declaredParameterTypes; + private final Class repositoryInterface; - public MatchParameterOrComponentType(Method declaredMethod, Class repositoryInterface) { - - this.declaredMethod = declaredMethod; - this.declaredParameterTypes = declaredMethod.getParameterTypes(); - this.repositoryInterface = repositoryInterface; + public static MatchParameterOrComponentType of(Method declaredMethod, Class repositoryInterface) { + return new MatchParameterOrComponentType(declaredMethod, declaredMethod.getParameterTypes(), repositoryInterface); } + /* + * (non-Javadoc) + * @see java.util.function.BiPredicate#test(java.lang.Object, java.lang.Object) + */ @Override public boolean test(Class candidateParameterType, Integer index) { MethodParameter parameter = new MethodParameter(declaredMethod, index); Class parameterType = resolveParameterType(parameter, repositoryInterface); - if (!candidateParameterType.isAssignableFrom(parameterType) - || !candidateParameterType.equals(declaredParameterTypes[index])) { - return false; - } - - return true; + return candidateParameterType.isAssignableFrom(parameterType) + && candidateParameterType.equals(declaredParameterTypes[index]); } } } diff --git a/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java b/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java index 4f0366e53..e7f65eae8 100644 --- a/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java +++ b/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java @@ -37,6 +37,7 @@ import org.springframework.core.GenericTypeResolver; import org.springframework.core.MethodParameter; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.Repository; @@ -51,6 +52,7 @@ import org.springframework.data.repository.query.QueryLookupStrategy.Key; import org.springframework.data.repository.query.QueryMethod; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.util.ClassUtils; +import org.springframework.data.repository.util.ReactiveWrapperConverters; import org.springframework.data.util.ReflectionUtils; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -79,7 +81,6 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware, private ClassLoader classLoader = org.springframework.util.ClassUtils.getDefaultClassLoader(); private EvaluationContextProvider evaluationContextProvider = DefaultEvaluationContextProvider.INSTANCE; private BeanFactory beanFactory; - private ConversionService conversionService; private QueryCollectingQueryCreationListener collectingListener = new QueryCollectingQueryCreationListener(); @@ -105,16 +106,6 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware, this.namedQueries = namedQueries == null ? PropertiesBasedNamedQueries.EMPTY : namedQueries; } - /** - * Configures a {@link ConversionService} instance to convert method parameters when calling implementation methods on - * the base class or a custom implementation. - * - * @param conversionService the conversionService to set. - */ - public void setConversionService(ConversionService conversionService) { - this.conversionService = conversionService; - } - /* * (non-Javadoc) * @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang.ClassLoader) @@ -232,12 +223,9 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware, result.addAdvice(new QueryExecutorMethodInterceptor(information)); - if (conversionService == null) { - result.addAdvice(new ImplementationMethodExecutionInterceptor(information, customImplementation, target)); - } else { - result.addAdvice(new ConvertingImplementationMethodExecutionInterceptor(information, customImplementation, target, - conversionService)); - } + result.addAdvice(information.isReactiveRepository() + ? new ConvertingImplementationMethodExecutionInterceptor(information, customImplementation, target) + : new ImplementationMethodExecutionInterceptor(information, customImplementation, target)); return (T) result.getProxy(classLoader); } @@ -272,16 +260,9 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware, Class repositoryBaseClass = this.repositoryBaseClass == null ? getRepositoryBaseClass(metadata) : this.repositoryBaseClass; - if (conversionService == null) { - repositoryInformation = new DefaultRepositoryInformation(metadata, repositoryBaseClass, - customImplementationClass); - } else { - // TODO: Not sure this is the best idea but at some point we need to distinguish between - // methods that want to get unwrapped data from wrapped parameters and those which are - // just fine with wrappers because at some point a converting interceptor kicks in - repositoryInformation = new ReactiveRepositoryInformation(metadata, repositoryBaseClass, - customImplementationClass, conversionService); - } + repositoryInformation = metadata.isReactiveRepository() + ? new ReactiveRepositoryInformation(metadata, repositoryBaseClass, customImplementationClass) + : new DefaultRepositoryInformation(metadata, repositoryBaseClass, customImplementationClass); repositoryInformationCache.put(cacheKey, repositoryInformation); return repositoryInformation; @@ -602,18 +583,18 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware, */ public class ConvertingImplementationMethodExecutionInterceptor extends ImplementationMethodExecutionInterceptor { - private final ConversionService conversionService; + private final ConversionService conversionService = ReactiveWrapperConverters + .registerConvertersIn(new DefaultConversionService()); /** * @param repositoryInformation * @param customImplementation * @param target - * @param conversionService */ public ConvertingImplementationMethodExecutionInterceptor(RepositoryInformation repositoryInformation, - Object customImplementation, Object target, ConversionService conversionService) { + Object customImplementation, Object target) { + super(repositoryInformation, customImplementation, target); - this.conversionService = conversionService; } /* (non-Javadoc) diff --git a/src/main/java/org/springframework/data/repository/query/ResultProcessor.java b/src/main/java/org/springframework/data/repository/query/ResultProcessor.java index f5ba30731..f99a06a78 100644 --- a/src/main/java/org/springframework/data/repository/query/ResultProcessor.java +++ b/src/main/java/org/springframework/data/repository/query/ResultProcessor.java @@ -158,7 +158,7 @@ public class ResultProcessor { return (T) new StreamQueryResultHandler(type, converter).handle(source); } - if(ReactiveWrapperConverters.supports(source.getClass())){ + if (ReactiveWrapperConverters.supports(source.getClass())) { return (T) ReactiveWrapperConverters.map(source, converter::convert); } diff --git a/src/main/java/org/springframework/data/repository/util/QueryExecutionConverters.java b/src/main/java/org/springframework/data/repository/util/QueryExecutionConverters.java index 601325e82..aca776c1f 100644 --- a/src/main/java/org/springframework/data/repository/util/QueryExecutionConverters.java +++ b/src/main/java/org/springframework/data/repository/util/QueryExecutionConverters.java @@ -15,6 +15,10 @@ */ package org.springframework.data.repository.util; +import scala.Function0; +import scala.Option; +import scala.runtime.AbstractFunction0; + import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -33,10 +37,6 @@ import org.springframework.util.concurrent.ListenableFuture; import com.google.common.base.Optional; -import scala.Function0; -import scala.Option; -import scala.runtime.AbstractFunction0; - /** * Converters to potentially wrap the execution of a repository method into a variety of wrapper types potentially being * available on the classpath. Currently supported: @@ -105,7 +105,7 @@ public abstract class QueryExecutionConverters { UNWRAPPERS.add(ScalOptionUnwrapper.INSTANCE); } - if(ReactiveWrappers.isAvailable()) { + if (ReactiveWrappers.isAvailable()) { WRAPPER_TYPES.addAll(ReactiveWrappers.getNoValueTypes()); WRAPPER_TYPES.addAll(ReactiveWrappers.getSingleValueTypes()); WRAPPER_TYPES.addAll(ReactiveWrappers.getMultiValueTypes()); diff --git a/src/main/java/org/springframework/data/repository/util/ReactiveWrapperConverters.java b/src/main/java/org/springframework/data/repository/util/ReactiveWrapperConverters.java index 47647d73e..c6a0a85ef 100644 --- a/src/main/java/org/springframework/data/repository/util/ReactiveWrapperConverters.java +++ b/src/main/java/org/springframework/data/repository/util/ReactiveWrapperConverters.java @@ -17,19 +17,6 @@ package org.springframework.data.repository.util; import static org.springframework.data.repository.util.ReactiveWrapperConverters.RegistryHolder.*; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; - -import org.reactivestreams.Publisher; -import org.springframework.core.ReactiveAdapterRegistry; -import org.springframework.core.convert.converter.Converter; -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.util.Assert; -import org.springframework.util.ClassUtils; - import io.reactivex.BackpressureStrategy; import io.reactivex.Flowable; import io.reactivex.Maybe; @@ -40,6 +27,20 @@ import rx.Completable; import rx.Observable; import rx.Single; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +import org.reactivestreams.Publisher; +import org.springframework.core.ReactiveAdapterRegistry; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.converter.Converter; +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.util.Assert; +import org.springframework.util.ClassUtils; + /** * Conversion support for reactive wrapper types. This class is a reactive extension to * {@link QueryExecutionConverters}. @@ -89,7 +90,7 @@ public class ReactiveWrapperConverters { * * @param conversionService must not be {@literal null}. */ - public static void registerConvertersIn(ConfigurableConversionService conversionService) { + public static ConversionService registerConvertersIn(ConfigurableConversionService conversionService) { Assert.notNull(conversionService, "ConversionService must not be null!"); @@ -151,6 +152,8 @@ public class ReactiveWrapperConverters { conversionService.addConverter(RxJava2ObservableToMaybeConverter.INSTANCE); } } + + return conversionService; } /** @@ -200,14 +203,11 @@ public class ReactiveWrapperConverters { Assert.notNull(stream, "Stream must not be null!"); Assert.notNull(converter, "Converter must not be null!"); - for (ReactiveTypeWrapper reactiveWrapper : REACTIVE_WRAPPERS) { - - if (ClassUtils.isAssignable(reactiveWrapper.getWrapperClass(), stream.getClass())) { - return (T) reactiveWrapper.map(stream, converter); - } - } - - throw new IllegalStateException(String.format("Cannot apply converter to %s", stream)); + return REACTIVE_WRAPPERS.stream()// + .filter(it -> ClassUtils.isAssignable(it.getWrapperClass(), stream.getClass()))// + .findFirst()// + .map(it -> (T) it.map(stream, converter))// + .orElseThrow(() -> new IllegalStateException(String.format("Cannot apply converter to %s", stream))); } // ------------------------------------------------------------------------- diff --git a/src/main/java/org/springframework/data/repository/util/ReactiveWrappers.java b/src/main/java/org/springframework/data/repository/util/ReactiveWrappers.java index fa888d031..b0b1f5c0d 100644 --- a/src/main/java/org/springframework/data/repository/util/ReactiveWrappers.java +++ b/src/main/java/org/springframework/data/repository/util/ReactiveWrappers.java @@ -15,6 +15,14 @@ */ package org.springframework.data.repository.util; +import lombok.experimental.UtilityClass; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import rx.Completable; +import rx.Observable; +import rx.Single; + +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; @@ -25,16 +33,10 @@ import java.util.stream.Collectors; import org.reactivestreams.Publisher; import org.springframework.core.ReactiveAdapter.Descriptor; +import org.springframework.data.util.ReflectionUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; -import lombok.experimental.UtilityClass; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import rx.Completable; -import rx.Observable; -import rx.Single; - /** * 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. @@ -44,6 +46,7 @@ import rx.Single; * * @author Mark Paluch * @author Christoph Strobl + * @author Oliver Gierke * @since 2.0 * @see org.reactivestreams.Publisher * @see rx.Single @@ -71,6 +74,15 @@ public class ReactiveWrappers { private static final Map, Descriptor> REACTIVE_WRAPPERS; + /** + * Enumeration of supported reactive libraries. + * + * @author Mark Paluch + */ + static enum ReactiveLibrary { + PROJECT_REACTOR, RXJAVA1, RXJAVA2; + } + static { Map, Descriptor> reactiveWrappers = new LinkedHashMap<>(5); @@ -102,14 +114,13 @@ public class ReactiveWrappers { } /** - * Returns {@literal true} if reactive support is available. More specifically, whether RxJava1/2 or Project Reactor - * libraries are on the class path. + * 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 isAvailable(ReactiveLibrary.PROJECT_REACTOR) || isAvailable(ReactiveLibrary.RXJAVA1) - || isAvailable(ReactiveLibrary.RXJAVA2); + return Arrays.stream(ReactiveLibrary.values()).anyMatch(ReactiveWrappers::isAvailable); } /** @@ -120,7 +131,7 @@ public class ReactiveWrappers { */ public static boolean isAvailable(ReactiveLibrary reactiveLibrary) { - Assert.notNull(reactiveLibrary, "ReactiveLibrary must not be null!"); + Assert.notNull(reactiveLibrary, "Reactive library must not be null!"); switch (reactiveLibrary) { case PROJECT_REACTOR: @@ -129,9 +140,9 @@ public class ReactiveWrappers { return RXJAVA1_PRESENT; case RXJAVA2: return RXJAVA2_PRESENT; + default: + throw new IllegalArgumentException(String.format("Reactive library %s not supported", reactiveLibrary)); } - - throw new IllegalArgumentException(String.format("ReactiveLibrary %s not supported", reactiveLibrary)); } /** @@ -144,8 +155,19 @@ public class ReactiveWrappers { return isWrapper(ClassUtils.getUserClass(type)); } - private static boolean isWrapper(Class type) { - return isNoValueType(type) || isSingleValueType(type) || isMultiValueType(type); + /** + * 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); } /** @@ -156,7 +178,7 @@ public class ReactiveWrappers { */ public static boolean isNoValueType(Class type) { - Assert.notNull(type, "Class must not be null!"); + Assert.notNull(type, "Candidate type must not be null!"); return findDescriptor(type).map(Descriptor::isNoValue).orElse(false); } @@ -169,10 +191,9 @@ public class ReactiveWrappers { */ public static boolean isSingleValueType(Class type) { - Assert.notNull(type, "Class must not be null!"); + Assert.notNull(type, "Candidate type must not be null!"); - return findDescriptor(type).map((descriptor) -> !descriptor.isMultiValue() && !descriptor.isNoValue()) - .orElse(false); + return findDescriptor(type).map(it -> !it.isMultiValue() && !it.isNoValue()).orElse(false); } /** @@ -185,63 +206,75 @@ public class ReactiveWrappers { */ public static boolean isMultiValueType(Class type) { - Assert.notNull(type, "Class must not be null!"); + 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 - if (isSingleValueType(type)) { - return false; - } - - return findDescriptor(type).map(Descriptor::isMultiValue).orElse(false); + return isSingleValueType(type) ? false : findDescriptor(type).map(Descriptor::isMultiValue).orElse(false); } /** - * Returns a collection of No-Value wrapper types. + * Returns a collection of no-value wrapper types. * - * @return a collection of No-Value wrapper types. + * @return a collection of no-value wrapper types. */ public static Collection> getNoValueTypes() { - return REACTIVE_WRAPPERS.entrySet().stream().filter(entry -> entry.getValue().isNoValue()).map(Entry::getKey) - .collect(Collectors.toList()); + + return REACTIVE_WRAPPERS.entrySet().stream()// + .filter(entry -> entry.getValue().isNoValue())// + .map(Entry::getKey).collect(Collectors.toList()); } /** - * Returns a collection of Single-Value wrapper types. + * Returns a collection of single-value wrapper types. * - * @return a collection of Single-Value wrapper types. + * @return a collection of single-value wrapper types. */ public static Collection> getSingleValueTypes() { - return REACTIVE_WRAPPERS.entrySet().stream().filter(entry -> !entry.getValue().isMultiValue()).map(Entry::getKey) - .collect(Collectors.toList()); + + return REACTIVE_WRAPPERS.entrySet().stream()// + .filter(entry -> !entry.getValue().isMultiValue())// + .map(Entry::getKey).collect(Collectors.toList()); } /** - * Returns a collection of Multi-Value wrapper types. + * Returns a collection of multi-value wrapper types. * - * @return a collection of Multi-Value wrapper types. + * @return a collection of multi-value wrapper types. */ public static Collection> getMultiValueTypes() { - return REACTIVE_WRAPPERS.entrySet().stream().filter(entry -> entry.getValue().isMultiValue()).map(Entry::getKey) + + return REACTIVE_WRAPPERS.entrySet().stream()// + .filter(entry -> entry.getValue().isMultiValue())// + .map(Entry::getKey)// .collect(Collectors.toList()); } - private static Optional findDescriptor(Class rhsType) { + /** + * Returns whether the given type is a reactive wrapper type. + * + * @param type must not be {@literal null}. + * @return + */ + private static boolean isWrapper(Class type) { - for (Class type : REACTIVE_WRAPPERS.keySet()) { - if (org.springframework.util.ClassUtils.isAssignable(type, rhsType)) { - return Optional.ofNullable(REACTIVE_WRAPPERS.get(type)); - } - } - return Optional.empty(); + Assert.notNull(type, "Candidate type must not be null!"); + + return isNoValueType(type) || isSingleValueType(type) || isMultiValueType(type); } /** - * Enumeration of supported reactive libraries. + * Looks up a {@link Descriptor} for the given wrapper type. * - * @author Mark Paluch + * @param type must not be {@literal null}. + * @return */ - enum ReactiveLibrary { - PROJECT_REACTOR, RXJAVA1, RXJAVA2; + private static Optional findDescriptor(Class type) { + + Assert.notNull(type, "Wrapper type must not be null!"); + + return REACTIVE_WRAPPERS.entrySet().stream()// + .filter(it -> ClassUtils.isAssignable(it.getKey(), type))// + .findFirst().map(it -> it.getValue()); } } diff --git a/src/main/java/org/springframework/data/util/ReflectionUtils.java b/src/main/java/org/springframework/data/util/ReflectionUtils.java index 5a28940f0..9f7158dd7 100644 --- a/src/main/java/org/springframework/data/util/ReflectionUtils.java +++ b/src/main/java/org/springframework/data/util/ReflectionUtils.java @@ -20,6 +20,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Arrays; import java.util.stream.Stream; import org.springframework.beans.BeanUtils; @@ -264,6 +265,23 @@ public abstract class ReflectionUtils { return null; } + /** + * Returns a {@link Stream} of the return and parameters types of the given {@link Method}. + * + * @param method must not be {@literal null}. + * @return + * @since 2.0 + */ + public static Stream> returnTypeAndParameters(Method method) { + + Assert.notNull(method, "Method must not be null!"); + + Stream> returnType = Stream.of(method.getReturnType()); + Stream> parameterTypes = Arrays.stream(method.getParameterTypes()); + + return Stream.concat(returnType, parameterTypes); + } + private static final boolean argumentsMatch(Class[] parameterTypes, Object[] arguments) { if (parameterTypes.length != arguments.length) { diff --git a/src/main/java/org/springframework/data/util/StreamUtils.java b/src/main/java/org/springframework/data/util/StreamUtils.java index bf2deffb7..fdb6c75ba 100644 --- a/src/main/java/org/springframework/data/util/StreamUtils.java +++ b/src/main/java/org/springframework/data/util/StreamUtils.java @@ -51,8 +51,8 @@ public class StreamUtils { Spliterator spliterator = Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL); Stream stream = StreamSupport.stream(spliterator, false); - return iterator instanceof CloseableIterator ? stream.onClose(new CloseableIteratorDisposingRunnable( - (CloseableIterator) iterator)) : stream; + return iterator instanceof CloseableIterator + ? stream.onClose(new CloseableIteratorDisposingRunnable((CloseableIterator) iterator)) : stream; } /** diff --git a/src/test/java/org/springframework/data/repository/core/support/DummyRepositoryInformation.java b/src/test/java/org/springframework/data/repository/core/support/DummyRepositoryInformation.java index bea708e63..e75ebb16b 100644 --- a/src/test/java/org/springframework/data/repository/core/support/DummyRepositoryInformation.java +++ b/src/test/java/org/springframework/data/repository/core/support/DummyRepositoryInformation.java @@ -93,4 +93,9 @@ public final class DummyRepositoryInformation implements RepositoryInformation { public Set> getAlternativeDomainTypes() { return metadata.getAlternativeDomainTypes(); } + + @Override + public boolean isReactiveRepository() { + return metadata.isReactiveRepository(); + } } diff --git a/src/test/java/org/springframework/data/repository/core/support/QueryExecutorMethodInterceptorUnitTests.java b/src/test/java/org/springframework/data/repository/core/support/QueryExecutorMethodInterceptorUnitTests.java index bdf761e9d..a5698befe 100644 --- a/src/test/java/org/springframework/data/repository/core/support/QueryExecutorMethodInterceptorUnitTests.java +++ b/src/test/java/org/springframework/data/repository/core/support/QueryExecutorMethodInterceptorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2015 the original author or authors. + * Copyright 2011-2016 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. @@ -37,6 +37,7 @@ import org.springframework.data.repository.query.QueryLookupStrategy.Key; * Unit test for {@link QueryExecuterMethodInterceptor}. * * @author Oliver Gierke + * @author Mark Paluch */ @RunWith(MockitoJUnitRunner.class) public class QueryExecutorMethodInterceptorUnitTests { @@ -61,6 +62,7 @@ public class QueryExecutorMethodInterceptorUnitTests { when(factory.getQueryLookupStrategy(any(Key.class))).thenReturn(strategy); factory.new QueryExecutorMethodInterceptor(information); + verify(strategy, times(0)).resolveQuery(any(Method.class), any(RepositoryMetadata.class), any(ProjectionFactory.class), any(NamedQueries.class)); } diff --git a/src/test/java/org/springframework/data/repository/core/support/ReactiveRepositoryInformationUnitTests.java b/src/test/java/org/springframework/data/repository/core/support/ReactiveRepositoryInformationUnitTests.java index 63fb8d0d0..9e24f8b60 100644 --- a/src/test/java/org/springframework/data/repository/core/support/ReactiveRepositoryInformationUnitTests.java +++ b/src/test/java/org/springframework/data/repository/core/support/ReactiveRepositoryInformationUnitTests.java @@ -18,6 +18,8 @@ package org.springframework.data.repository.core.support; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import rx.Observable; + import java.io.Serializable; import java.lang.reflect.Method; @@ -32,18 +34,20 @@ import org.springframework.data.repository.reactive.ReactiveSortingRepository; import org.springframework.data.repository.reactive.RxJava1CrudRepository; import org.springframework.data.repository.util.ReactiveWrapperConverters; -import rx.Observable; - /** * Unit tests for {@link ReactiveRepositoryInformation}. * * @author Mark Paluch + * @author Oliver Gierke */ @RunWith(MockitoJUnitRunner.class) public class ReactiveRepositoryInformationUnitTests { static final Class REPOSITORY = ReactiveJavaInterfaceWithGenerics.class; + /** + * @see DATACMNS-836 + */ @Test public void discoversMethodWithoutComparingReturnType() throws Exception { @@ -56,6 +60,9 @@ public class ReactiveRepositoryInformationUnitTests { assertThat(reference.getName(), is("deleteAll")); } + /** + * @see DATACMNS-836 + */ @Test public void discoversMethodWithConvertibleArguments() throws Exception { @@ -64,8 +71,7 @@ public class ReactiveRepositoryInformationUnitTests { Method method = RxJava1InterfaceWithGenerics.class.getMethod("save", Observable.class); RepositoryMetadata metadata = new DefaultRepositoryMetadata(RxJava1InterfaceWithGenerics.class); - DefaultRepositoryInformation information = new ReactiveRepositoryInformation(metadata, REPOSITORY, null, - conversionService); + DefaultRepositoryInformation information = new ReactiveRepositoryInformation(metadata, REPOSITORY, null); Method reference = information.getTargetClassMethod(method); assertEquals(ReactiveCrudRepository.class, reference.getDeclaringClass()); @@ -73,16 +79,15 @@ public class ReactiveRepositoryInformationUnitTests { assertThat(reference.getParameterTypes()[0], is(equalTo(Publisher.class))); } + /** + * @see DATACMNS-836 + */ @Test public void discoversMethodAssignableArguments() throws Exception { - DefaultConversionService conversionService = new DefaultConversionService(); - ReactiveWrapperConverters.registerConvertersIn(conversionService); - Method method = ReactiveSortingRepository.class.getMethod("save", Publisher.class); RepositoryMetadata metadata = new DefaultRepositoryMetadata(ReactiveJavaInterfaceWithGenerics.class); - DefaultRepositoryInformation information = new ReactiveRepositoryInformation(metadata, REPOSITORY, null, - conversionService); + DefaultRepositoryInformation information = new ReactiveRepositoryInformation(metadata, REPOSITORY, null); Method reference = information.getTargetClassMethod(method); assertEquals(ReactiveCrudRepository.class, reference.getDeclaringClass()); @@ -90,16 +95,15 @@ public class ReactiveRepositoryInformationUnitTests { assertThat(reference.getParameterTypes()[0], is(equalTo(Publisher.class))); } + /** + * @see DATACMNS-836 + */ @Test public void discoversMethodExactIterableArguments() throws Exception { - DefaultConversionService conversionService = new DefaultConversionService(); - ReactiveWrapperConverters.registerConvertersIn(conversionService); - Method method = ReactiveJavaInterfaceWithGenerics.class.getMethod("save", Iterable.class); RepositoryMetadata metadata = new DefaultRepositoryMetadata(ReactiveJavaInterfaceWithGenerics.class); - DefaultRepositoryInformation information = new ReactiveRepositoryInformation(metadata, REPOSITORY, null, - conversionService); + DefaultRepositoryInformation information = new ReactiveRepositoryInformation(metadata, REPOSITORY, null); Method reference = information.getTargetClassMethod(method); assertEquals(ReactiveCrudRepository.class, reference.getDeclaringClass()); @@ -107,6 +111,9 @@ public class ReactiveRepositoryInformationUnitTests { assertThat(reference.getParameterTypes()[0], is(equalTo(Iterable.class))); } + /** + * @see DATACMNS-836 + */ @Test public void discoversMethodExactObjectArguments() throws Exception { @@ -115,8 +122,7 @@ public class ReactiveRepositoryInformationUnitTests { Method method = ReactiveJavaInterfaceWithGenerics.class.getMethod("save", Object.class); RepositoryMetadata metadata = new DefaultRepositoryMetadata(ReactiveJavaInterfaceWithGenerics.class); - DefaultRepositoryInformation information = new ReactiveRepositoryInformation(metadata, REPOSITORY, null, - conversionService); + DefaultRepositoryInformation information = new ReactiveRepositoryInformation(metadata, REPOSITORY, null); Method reference = information.getTargetClassMethod(method); assertEquals(ReactiveCrudRepository.class, reference.getDeclaringClass()); diff --git a/src/test/java/org/springframework/data/repository/core/support/ReactiveWrapperRepositoryFactorySupportUnitTests.java b/src/test/java/org/springframework/data/repository/core/support/ReactiveWrapperRepositoryFactorySupportUnitTests.java index e53b6871b..2eb06863f 100644 --- a/src/test/java/org/springframework/data/repository/core/support/ReactiveWrapperRepositoryFactorySupportUnitTests.java +++ b/src/test/java/org/springframework/data/repository/core/support/ReactiveWrapperRepositoryFactorySupportUnitTests.java @@ -15,8 +15,12 @@ */ package org.springframework.data.repository.core.support; +import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; +import reactor.core.publisher.Mono; +import rx.Single; + import java.io.Serializable; import org.junit.Before; @@ -31,9 +35,6 @@ import org.springframework.data.repository.Repository; import org.springframework.data.repository.reactive.ReactiveSortingRepository; import org.springframework.data.repository.util.ReactiveWrapperConverters; -import reactor.core.publisher.Mono; -import rx.Single; - /** * Unit tests for {@link RepositoryFactorySupport} using reactive wrapper types. * @@ -56,7 +57,6 @@ public class ReactiveWrapperRepositoryFactorySupportUnitTests { ReactiveWrapperConverters.registerConvertersIn(defaultConversionService); factory = new DummyRepositoryFactory(backingRepo); - factory.setConversionService(defaultConversionService); } /** @@ -102,6 +102,7 @@ public class ReactiveWrapperRepositoryFactorySupportUnitTests { * @see DATACMNS-836 */ @Test + @SuppressWarnings("unchecked") public void callsMethodOnBaseImplementationWithTypeConversion() { Single ids = Single.just(1L); diff --git a/src/test/java/org/springframework/data/repository/query/ResultProcessorUnitTests.java b/src/test/java/org/springframework/data/repository/query/ResultProcessorUnitTests.java index 567b53844..b3aff8cd8 100644 --- a/src/test/java/org/springframework/data/repository/query/ResultProcessorUnitTests.java +++ b/src/test/java/org/springframework/data/repository/query/ResultProcessorUnitTests.java @@ -19,6 +19,11 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.mockito.Mockito.*; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import rx.Observable; +import rx.Single; + import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -40,11 +45,6 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import rx.Observable; -import rx.Single; - /** * Unit tests for {@link ResultProcessor}. * @@ -109,7 +109,7 @@ public class ResultProcessorUnitTests { ResultProcessor information = getProcessor("findAllProjection"); List> source = new ArrayList>( - Arrays.asList(Collections. singletonMap("lastname", "Matthews"))); + Arrays.asList(Collections.singletonMap("lastname", "Matthews"))); List result = information.processResult(source); @@ -278,6 +278,7 @@ public class ResultProcessorUnitTests { Object result = getProcessor("findMonoSample").processResult(samples); assertThat(result, is(instanceOf(Mono.class))); + Object content = ((Mono) result).block(); assertThat(content, is(instanceOf(Sample.class))); @@ -295,6 +296,7 @@ public class ResultProcessorUnitTests { Object result = getProcessor("findSingleSample").processResult(samples); assertThat(result, is(instanceOf(Single.class))); + Object content = ((Single) result).toBlocking().value(); assertThat(content, is(instanceOf(Sample.class))); @@ -304,20 +306,16 @@ public class ResultProcessorUnitTests { * @see DATACMNS-836 */ @Test + @SuppressWarnings("unchecked") public void refrainsFromProjectingUsingReactiveWrappersIfThePreparingConverterReturnsACompatibleInstance() throws Exception { ResultProcessor processor = getProcessor("findMonoSampleDto"); - Object result = processor.processResult(Mono.just(new Sample("Dave", "Matthews")), new Converter() { - - @Override - public Object convert(Object source) { - return new SampleDto(); - } - }); + Object result = processor.processResult(Mono.just(new Sample("Dave", "Matthews")), source -> new SampleDto()); assertThat(result, is(instanceOf(Mono.class))); + Object content = ((Mono) result).block(); assertThat(content, is(instanceOf(SampleDto.class))); @@ -335,6 +333,7 @@ public class ResultProcessorUnitTests { Object result = getProcessor("findFluxProjection").processResult(samples); assertThat(result, is(instanceOf(Flux.class))); + List content = ((Flux) result).collectList().block(); assertThat(content, is(not(empty()))); @@ -353,6 +352,7 @@ public class ResultProcessorUnitTests { Object result = getProcessor("findObservableProjection").processResult(samples); assertThat(result, is(instanceOf(Observable.class))); + List content = ((Observable) result).toList().toBlocking().single(); assertThat(content, is(not(empty())));