diff --git a/src/main/java/org/springframework/data/repository/reactive/ReactivePagingAndSortingRepository.java b/src/main/java/org/springframework/data/repository/reactive/ReactiveSortingRepository.java similarity index 76% rename from src/main/java/org/springframework/data/repository/reactive/ReactivePagingAndSortingRepository.java rename to src/main/java/org/springframework/data/repository/reactive/ReactiveSortingRepository.java index 15c94aa5b..40476c6ac 100644 --- a/src/main/java/org/springframework/data/repository/reactive/ReactivePagingAndSortingRepository.java +++ b/src/main/java/org/springframework/data/repository/reactive/ReactiveSortingRepository.java @@ -18,8 +18,6 @@ package org.springframework.data.repository.reactive; import java.io.Serializable; import org.reactivestreams.Publisher; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.repository.NoRepositoryBean; @@ -27,18 +25,17 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; /** - * Extension of {@link ReactiveCrudRepository} to provide additional methods to retrieve entities using the pagination - * and sorting abstraction. + * Extension of {@link ReactiveCrudRepository} to provide additional methods to retrieve entities using the sorting + * abstraction. * * @author Mark Paluch * @since 2.0 * @see Sort - * @see Pageable * @see Mono * @see Flux */ @NoRepositoryBean -public interface ReactivePagingAndSortingRepository extends ReactiveCrudRepository { +public interface ReactiveSortingRepository extends ReactiveCrudRepository { /* * (non-Javadoc) @@ -68,12 +65,4 @@ public interface ReactivePagingAndSortingRepository * @return all entities sorted by the given options */ Flux findAll(Sort sort); - - /** - * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object. - * - * @param pageable - * @return a page of entities - */ - Mono> findAll(Pageable pageable); } diff --git a/src/main/java/org/springframework/data/repository/reactive/RxJavaCrudRepository.java b/src/main/java/org/springframework/data/repository/reactive/RxJava1CrudRepository.java similarity index 91% rename from src/main/java/org/springframework/data/repository/reactive/RxJavaCrudRepository.java rename to src/main/java/org/springframework/data/repository/reactive/RxJava1CrudRepository.java index a03886e12..d551a430a 100644 --- a/src/main/java/org/springframework/data/repository/reactive/RxJavaCrudRepository.java +++ b/src/main/java/org/springframework/data/repository/reactive/RxJava1CrudRepository.java @@ -21,6 +21,7 @@ import org.reactivestreams.Publisher; import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.Repository; +import rx.Completable; import rx.Observable; import rx.Single; @@ -34,7 +35,7 @@ import rx.Single; * @see Observable */ @NoRepositoryBean -public interface RxJavaCrudRepository extends Repository { +public interface RxJava1CrudRepository extends Repository { /** * Saves a given entity. Use the returned instance for further operations as the save operation might have changed the @@ -70,7 +71,7 @@ public interface RxJavaCrudRepository extends Reposi * @return the entity with the given id or {@literal null} if none found * @throws IllegalArgumentException if {@code id} is {@literal null} */ - Single findOne(ID id); + Observable findOne(ID id); /** * Retrieves an entity by its id supplied by a {@link Single}. @@ -79,7 +80,7 @@ public interface RxJavaCrudRepository extends Reposi * @return the entity with the given id or {@literal null} if none found * @throws IllegalArgumentException if {@code id} is {@literal null} */ - Single findOne(Single id); + Observable findOne(Single id); /** * Returns whether an entity with the given id exists. @@ -135,7 +136,7 @@ public interface RxJavaCrudRepository extends Reposi * @param id must not be {@literal null}. * @throws IllegalArgumentException in case the given {@code id} is {@literal null} */ - Single delete(ID id); + Completable delete(ID id); /** * Deletes a given entity. @@ -143,7 +144,7 @@ public interface RxJavaCrudRepository extends Reposi * @param entity * @throws IllegalArgumentException in case the given entity is {@literal null}. */ - Single delete(T entity); + Completable delete(T entity); /** * Deletes the given entities. @@ -151,7 +152,7 @@ public interface RxJavaCrudRepository extends Reposi * @param entities * @throws IllegalArgumentException in case the given {@link Iterable} is {@literal null}. */ - Single delete(Iterable entities); + Completable delete(Iterable entities); /** * Deletes the given entities. @@ -159,10 +160,10 @@ public interface RxJavaCrudRepository extends Reposi * @param entityStream * @throws IllegalArgumentException in case the given {@link Publisher} is {@literal null}. */ - Single delete(Observable entityStream); + Completable delete(Observable entityStream); /** * Deletes all entities managed by the repository. */ - Single deleteAll(); + Completable deleteAll(); } diff --git a/src/main/java/org/springframework/data/repository/reactive/RxJavaPagingAndSortingRepository.java b/src/main/java/org/springframework/data/repository/reactive/RxJava1SortingRepository.java similarity index 72% rename from src/main/java/org/springframework/data/repository/reactive/RxJavaPagingAndSortingRepository.java rename to src/main/java/org/springframework/data/repository/reactive/RxJava1SortingRepository.java index 452e696dd..c09064396 100644 --- a/src/main/java/org/springframework/data/repository/reactive/RxJavaPagingAndSortingRepository.java +++ b/src/main/java/org/springframework/data/repository/reactive/RxJava1SortingRepository.java @@ -17,9 +17,6 @@ package org.springframework.data.repository.reactive; import java.io.Serializable; -import org.reactivestreams.Publisher; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.repository.NoRepositoryBean; @@ -27,19 +24,18 @@ import rx.Observable; import rx.Single; /** - * Extension of {@link RxJavaCrudRepository} to provide additional methods to retrieve entities using the pagination and sorting + * Extension of {@link RxJava1CrudRepository} to provide additional methods to retrieve entities using the sorting * abstraction. * * @author Mark Paluch * @since 2.0 * @see Sort - * @see Pageable * @see Single * @see Observable - * @see RxJavaCrudRepository + * @see RxJava1CrudRepository */ @NoRepositoryBean -public interface RxJavaPagingAndSortingRepository extends RxJavaCrudRepository { +public interface RxJava1SortingRepository extends RxJava1CrudRepository { /* * (non-Javadoc) @@ -69,12 +65,4 @@ public interface RxJavaPagingAndSortingRepository ex * @return all entities sorted by the given options */ Observable findAll(Sort sort); - - /** - * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object. - * - * @param pageable - * @return a page of entities - */ - Single> findAll(Pageable pageable); } 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 9e099eb05..5229c9c06 100644 --- a/src/main/java/org/springframework/data/repository/util/QueryExecutionConverters.java +++ b/src/main/java/org/springframework/data/repository/util/QueryExecutionConverters.java @@ -28,6 +28,7 @@ import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.GenericConverter; import org.springframework.core.convert.support.ConfigurableConversionService; +import org.springframework.data.repository.util.ReactiveWrappers.ReactiveLibrary; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -189,7 +190,7 @@ public abstract class QueryExecutionConverters { if (ReactiveWrappers.isAvailable()) { - if (ReactiveWrappers.RXJAVA1_PRESENT) { + if (ReactiveWrappers.isAvailable(ReactiveLibrary.RXJAVA1)) { conversionService.addConverter(PublisherToRxJava1CompletableConverter.INSTANCE); conversionService.addConverter(RxJava1CompletableToPublisherConverter.INSTANCE); @@ -206,7 +207,7 @@ public abstract class QueryExecutionConverters { conversionService.addConverter(RxJava1ObservableToFluxConverter.INSTANCE); } - if (ReactiveWrappers.RXJAVA2_PRESENT) { + if (ReactiveWrappers.isAvailable(ReactiveLibrary.RXJAVA2)) { conversionService.addConverter(PublisherToRxJava2CompletableConverter.INSTANCE); conversionService.addConverter(RxJava2CompletableToPublisherConverter.INSTANCE); @@ -231,19 +232,20 @@ public abstract class QueryExecutionConverters { conversionService.addConverter(RxJava2MaybeToFluxConverter.INSTANCE); } - if (ReactiveWrappers.PROJECT_REACTOR_PRESENT) { + if (ReactiveWrappers.isAvailable(ReactiveLibrary.PROJECT_REACTOR)) { conversionService.addConverter(PublisherToMonoConverter.INSTANCE); conversionService.addConverter(PublisherToFluxConverter.INSTANCE); } - if (ReactiveWrappers.RXJAVA1_PRESENT) { + if (ReactiveWrappers.isAvailable(ReactiveLibrary.RXJAVA1)) { conversionService.addConverter(RxJava1SingleToObservableConverter.INSTANCE); conversionService.addConverter(RxJava1ObservableToSingleConverter.INSTANCE); } - if (ReactiveWrappers.RXJAVA2_PRESENT) { + if (ReactiveWrappers.isAvailable(ReactiveLibrary.RXJAVA2)) { conversionService.addConverter(RxJava2SingleToObservableConverter.INSTANCE); conversionService.addConverter(RxJava2ObservableToSingleConverter.INSTANCE); + conversionService.addConverter(RxJava2ObservableToMaybeConverter.INSTANCE); } } } @@ -1084,7 +1086,24 @@ public abstract class QueryExecutionConverters { @Override public io.reactivex.Single convert(io.reactivex.Observable source) { - return source.singleElement().toSingle(); + return source.singleOrError(); + } + } + + /** + * A {@link Converter} to convert a {@link Observable} to {@link Maybe}. + * + * @author Mark Paluch + * @author 2.0 + */ + public enum RxJava2ObservableToMaybeConverter + implements Converter, io.reactivex.Maybe> { + + INSTANCE; + + @Override + public io.reactivex.Maybe convert(io.reactivex.Observable source) { + return source.singleElement(); } } 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 8f11c9ac5..20d4c63d0 100644 --- a/src/main/java/org/springframework/data/repository/util/ReactiveWrapperConverters.java +++ b/src/main/java/org/springframework/data/repository/util/ReactiveWrapperConverters.java @@ -22,6 +22,7 @@ import org.reactivestreams.Publisher; import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.convert.converter.Converter; 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; @@ -52,13 +53,13 @@ public class ReactiveWrapperConverters { static { - if (ReactiveWrappers.RXJAVA1_PRESENT) { + if (ReactiveWrappers.isAvailable(ReactiveLibrary.RXJAVA1)) { REACTIVE_WRAPPERS.add(RxJava1SingleWrapper.INSTANCE); REACTIVE_WRAPPERS.add(RxJava1ObservableWrapper.INSTANCE); } - if (ReactiveWrappers.RXJAVA2_PRESENT) { + if (ReactiveWrappers.isAvailable(ReactiveLibrary.RXJAVA2)) { REACTIVE_WRAPPERS.add(RxJava2SingleWrapper.INSTANCE); REACTIVE_WRAPPERS.add(RxJava2MaybeWrapper.INSTANCE); @@ -66,7 +67,7 @@ public class ReactiveWrapperConverters { REACTIVE_WRAPPERS.add(RxJava2FlowableWrapper.INSTANCE); } - if (ReactiveWrappers.PROJECT_REACTOR_PRESENT) { + if (ReactiveWrappers.isAvailable(ReactiveLibrary.PROJECT_REACTOR)) { REACTIVE_WRAPPERS.add(FluxWrapper.INSTANCE); REACTIVE_WRAPPERS.add(MonoWrapper.INSTANCE); 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 f4338e71b..91d144d07 100644 --- a/src/main/java/org/springframework/data/repository/util/ReactiveWrappers.java +++ b/src/main/java/org/springframework/data/repository/util/ReactiveWrappers.java @@ -59,13 +59,13 @@ import rx.Single; @UtilityClass public class ReactiveWrappers { - static final boolean PROJECT_REACTOR_PRESENT = ClassUtils.isPresent("reactor.core.publisher.Mono", + private static final boolean PROJECT_REACTOR_PRESENT = ClassUtils.isPresent("reactor.core.publisher.Mono", ReactiveWrappers.class.getClassLoader()); - static final boolean RXJAVA1_PRESENT = ClassUtils.isPresent("rx.Completable", + private static final boolean RXJAVA1_PRESENT = ClassUtils.isPresent("rx.Completable", ReactiveWrappers.class.getClassLoader()); - static final boolean RXJAVA2_PRESENT = ClassUtils.isPresent("io.reactivex.Flowable", + private static final boolean RXJAVA2_PRESENT = ClassUtils.isPresent("io.reactivex.Flowable", ReactiveWrappers.class.getClassLoader()); private static final Map, Descriptor> REACTIVE_WRAPPERS; @@ -107,7 +107,30 @@ public class ReactiveWrappers { * @return {@literal true} if reactive support is available. */ public static boolean isAvailable() { - return RXJAVA1_PRESENT || RXJAVA2_PRESENT || PROJECT_REACTOR_PRESENT; + return isAvailable(ReactiveLibrary.PROJECT_REACTOR) || isAvailable(ReactiveLibrary.RXJAVA1) + || isAvailable(ReactiveLibrary.RXJAVA2); + } + + /** + * 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, "ReactiveLibrary must not be null!"); + + switch (reactiveLibrary) { + case PROJECT_REACTOR: + return PROJECT_REACTOR_PRESENT; + case RXJAVA1: + return RXJAVA1_PRESENT; + case RXJAVA2: + return RXJAVA2_PRESENT; + } + + throw new IllegalArgumentException(String.format("ReactiveLibrary %s not supported", reactiveLibrary)); } /** @@ -207,4 +230,13 @@ public class ReactiveWrappers { } return Optional.empty(); } + + /** + * Enumeration of supported reactive libraries. + * + * @author Mark Paluch + */ + enum ReactiveLibrary { + PROJECT_REACTOR, RXJAVA1, RXJAVA2; + } } 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 3d0ca06bf..1d4a0eeac 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 @@ -30,12 +30,12 @@ import org.reactivestreams.Publisher; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.reactive.ReactiveCrudRepository; -import org.springframework.data.repository.reactive.ReactivePagingAndSortingRepository; -import org.springframework.data.repository.reactive.RxJavaCrudRepository; +import org.springframework.data.repository.reactive.ReactiveSortingRepository; +import org.springframework.data.repository.reactive.RxJava1CrudRepository; import org.springframework.data.repository.util.QueryExecutionConverters; /** - * Unit tests for {@link ConvertingMethodParameterRepositoryInformation}. + * Unit tests for {@link ReactiveRepositoryInformation}. * * @author Mark Paluch */ @@ -47,8 +47,8 @@ public class ReactiveRepositoryInformationUnitTests { @Test public void discoversMethodWithoutComparingReturnType() throws Exception { - Method method = RxJavaInterfaceWithGenerics.class.getMethod("deleteAll"); - RepositoryMetadata metadata = new DefaultRepositoryMetadata(RxJavaInterfaceWithGenerics.class); + Method method = RxJava1InterfaceWithGenerics.class.getMethod("deleteAll"); + RepositoryMetadata metadata = new DefaultRepositoryMetadata(RxJava1InterfaceWithGenerics.class); DefaultRepositoryInformation information = new DefaultRepositoryInformation(metadata, REPOSITORY, null); Method reference = information.getTargetClassMethod(method); @@ -62,8 +62,8 @@ public class ReactiveRepositoryInformationUnitTests { DefaultConversionService conversionService = new DefaultConversionService(); QueryExecutionConverters.registerConvertersIn(conversionService); - Method method = RxJavaInterfaceWithGenerics.class.getMethod("save", Observable.class); - RepositoryMetadata metadata = new DefaultRepositoryMetadata(RxJavaInterfaceWithGenerics.class); + Method method = RxJava1InterfaceWithGenerics.class.getMethod("save", Observable.class); + RepositoryMetadata metadata = new DefaultRepositoryMetadata(RxJava1InterfaceWithGenerics.class); DefaultRepositoryInformation information = new ReactiveRepositoryInformation(metadata, REPOSITORY, null, conversionService); @@ -79,7 +79,7 @@ public class ReactiveRepositoryInformationUnitTests { DefaultConversionService conversionService = new DefaultConversionService(); QueryExecutionConverters.registerConvertersIn(conversionService); - Method method = ReactivePagingAndSortingRepository.class.getMethod("save", Publisher.class); + Method method = ReactiveSortingRepository.class.getMethod("save", Publisher.class); RepositoryMetadata metadata = new DefaultRepositoryMetadata(ReactiveJavaInterfaceWithGenerics.class); DefaultRepositoryInformation information = new ReactiveRepositoryInformation(metadata, REPOSITORY, null, conversionService); @@ -124,7 +124,8 @@ public class ReactiveRepositoryInformationUnitTests { assertThat(reference.getParameterTypes()[0], is(equalTo(Object.class))); } - interface RxJavaInterfaceWithGenerics extends RxJavaCrudRepository {} + interface RxJava1InterfaceWithGenerics extends RxJava1CrudRepository + {} interface ReactiveJavaInterfaceWithGenerics extends ReactiveCrudRepository {} 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 3d1a47083..0dd0fad95 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,7 +15,10 @@ */ package org.springframework.data.repository.core.support; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; import java.io.Serializable; @@ -28,7 +31,7 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.data.repository.Repository; -import org.springframework.data.repository.reactive.ReactivePagingAndSortingRepository; +import org.springframework.data.repository.reactive.ReactiveSortingRepository; import org.springframework.data.repository.util.QueryExecutionConverters; import reactor.core.publisher.Mono; @@ -46,7 +49,7 @@ public class ReactiveWrapperRepositoryFactorySupportUnitTests { DummyRepositoryFactory factory; - @Mock ReactivePagingAndSortingRepository backingRepo; + @Mock ReactiveSortingRepository backingRepo; @Mock ObjectRepositoryCustom customImplementation; @Before diff --git a/src/test/java/org/springframework/data/repository/util/ReactiveWrapperConvertersUnitTests.java b/src/test/java/org/springframework/data/repository/util/ReactiveWrapperConvertersUnitTests.java index 2b61c02db..7f6e098ba 100644 --- a/src/test/java/org/springframework/data/repository/util/ReactiveWrapperConvertersUnitTests.java +++ b/src/test/java/org/springframework/data/repository/util/ReactiveWrapperConvertersUnitTests.java @@ -15,13 +15,12 @@ */ package org.springframework.data.repository.util; -import static org.assertj.core.api.AssertionsForClassTypes.*; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import org.junit.Test; import org.reactivestreams.Publisher; -import io.reactivex.Flowable; -import org.springframework.data.repository.util.ReactiveWrapperConverters; +import io.reactivex.Maybe; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import rx.Completable; @@ -202,6 +201,27 @@ public class ReactiveWrapperConvertersUnitTests { assertThat(ReactiveWrapperConverters.toWrapper(foo, Flux.class)).isInstanceOf(Flux.class); } + /** + * @see DATACMNS-836 + */ + @Test + public void toWrapperShouldConvertRxJava2ObservableToSingle() { + + io.reactivex.Observable foo = io.reactivex.Observable.just("foo"); + assertThat(ReactiveWrapperConverters.toWrapper(foo, io.reactivex.Single.class)) + .isInstanceOf(io.reactivex.Single.class); + } + + /** + * @see DATACMNS-836 + */ + @Test + public void toWrapperShouldConvertRxJava2ObservableToMaybe() { + + io.reactivex.Observable foo = io.reactivex.Observable.empty(); + assertThat(ReactiveWrapperConverters.toWrapper(foo, Maybe.class)).isInstanceOf(Maybe.class); + } + /** * @see DATACMNS-836 */