/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/TailableCursorRequest.java

https://github.com/spring-projects/spring-data-mongodb · Java · 269 lines · 97 code · 47 blank · 125 comment · 0 complexity · 841003e2f50459b3899acffc5d26ae6c MD5 · raw file

  1. /*
  2. * Copyright 2018-2022 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * https://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.data.mongodb.core.messaging;
  17. import java.util.Optional;
  18. import org.bson.Document;
  19. import org.springframework.data.mongodb.core.messaging.SubscriptionRequest.RequestOptions;
  20. import org.springframework.data.mongodb.core.messaging.TailableCursorRequest.TailableCursorRequestOptions.TailableCursorRequestOptionsBuilder;
  21. import org.springframework.data.mongodb.core.query.Query;
  22. import org.springframework.lang.Nullable;
  23. import org.springframework.util.Assert;
  24. /**
  25. * {@link SubscriptionRequest} implementation to be used to listen to query results in a
  26. * <a href="https://docs.mongodb.com/manual/core/capped-collections/">Capped Collection</a> using a
  27. * <a href="https://docs.mongodb.com/manual/core/tailable-cursors/">Tailable Cursor</a>.
  28. * <br />
  29. * The most trivial use case is subscribing to all events of a specific {@link com.mongodb.client.MongoCollection
  30. * collection}.
  31. *
  32. * <pre>
  33. * <code>
  34. * TailableCursorRequest&lt;Document&gt; request = new TailableCursorRequest&lt;&gt;(System.out::println, () -> "collection-name");
  35. * </code>
  36. * </pre>
  37. *
  38. * {@link TailableCursorRequestBuilder} offers a fluent API for creating {@link TailableCursorRequest} with
  39. * {@link TailableCursorRequestOptions} in one go.
  40. *
  41. * <pre>
  42. * <code>
  43. * TailableCursorRequest&lt;Document&gt; request = TailableCursorRequest.builder()
  44. * .collection("collection-name")
  45. * .publishTo(System.out::println)
  46. * .build();
  47. * </code>
  48. * </pre>
  49. *
  50. * @author Christoph Strobl
  51. * @author Mark Paluch
  52. * @since 2.1
  53. */
  54. public class TailableCursorRequest<T> implements SubscriptionRequest<Document, T, RequestOptions> {
  55. private final MessageListener<Document, ? super T> messageListener;
  56. private final TailableCursorRequestOptions options;
  57. /**
  58. * Create a new {@link TailableCursorRequest} with options, passing {@link Message messages} to the given
  59. * {@link MessageListener}.
  60. *
  61. * @param messageListener must not be {@literal null}.
  62. * @param options must not be {@literal null}.
  63. */
  64. public TailableCursorRequest(MessageListener<Document, ? super T> messageListener, RequestOptions options) {
  65. Assert.notNull(messageListener, "MessageListener must not be null");
  66. Assert.notNull(options, "Options must not be null");
  67. this.messageListener = messageListener;
  68. this.options = options instanceof TailableCursorRequestOptions ? (TailableCursorRequestOptions) options
  69. : TailableCursorRequestOptions.of(options);
  70. }
  71. @Override
  72. public MessageListener<Document, ? super T> getMessageListener() {
  73. return messageListener;
  74. }
  75. @Override
  76. public TailableCursorRequestOptions getRequestOptions() {
  77. return options;
  78. }
  79. /**
  80. * Obtain a shiny new {@link TailableCursorRequestBuilder} and start defining options in this fancy fluent way. Just
  81. * don't forget to call {@link TailableCursorRequestBuilder#build() build()} when your're done.
  82. *
  83. * @return new instance of {@link TailableCursorRequestBuilder}.
  84. */
  85. public static TailableCursorRequestBuilder builder() {
  86. return new TailableCursorRequestBuilder();
  87. }
  88. /**
  89. * Obtain a shiny new {@link TailableCursorRequestBuilder} and start defining options in this fancy fluent way. Just
  90. * don't forget to call {@link TailableCursorRequestBuilder#build() build()} when your're done.
  91. *
  92. * @return new instance of {@link TailableCursorRequestBuilder}.
  93. */
  94. public static <T> TailableCursorRequestBuilder<T> builder(MessageListener<Document, ? super T> listener) {
  95. TailableCursorRequestBuilder<T> builder = new TailableCursorRequestBuilder<>();
  96. return builder.publishTo(listener);
  97. }
  98. /**
  99. * {@link SubscriptionRequest.RequestOptions} implementation specific to a {@link TailableCursorRequest}.
  100. *
  101. * @author Christoph Strobl
  102. * @since 2.1
  103. */
  104. public static class TailableCursorRequestOptions implements SubscriptionRequest.RequestOptions {
  105. private @Nullable String collectionName;
  106. private @Nullable Query query;
  107. TailableCursorRequestOptions() {}
  108. public static TailableCursorRequestOptions of(RequestOptions options) {
  109. return builder().collection(options.getCollectionName()).build();
  110. }
  111. /**
  112. * Obtain a shiny new {@link TailableCursorRequestOptionsBuilder} and start defining options in this fancy fluent
  113. * way. Just don't forget to call {@link TailableCursorRequestOptionsBuilder#build() build()} when your're done.
  114. *
  115. * @return new instance of {@link TailableCursorRequestOptionsBuilder}.
  116. */
  117. public static TailableCursorRequestOptionsBuilder builder() {
  118. return new TailableCursorRequestOptionsBuilder();
  119. }
  120. @Override
  121. public String getCollectionName() {
  122. return collectionName;
  123. }
  124. public Optional<Query> getQuery() {
  125. return Optional.ofNullable(query);
  126. }
  127. /**
  128. * Builder for creating {@link TailableCursorRequestOptions}.
  129. *
  130. * @author Christoph Strobl
  131. * @since 2.1
  132. */
  133. public static class TailableCursorRequestOptionsBuilder {
  134. private @Nullable String collectionName;
  135. private @Nullable Query query;
  136. private TailableCursorRequestOptionsBuilder() {}
  137. /**
  138. * Set the collection name to tail.
  139. *
  140. * @param collection must not be {@literal null} nor {@literal empty}.
  141. * @return this.
  142. */
  143. public TailableCursorRequestOptionsBuilder collection(String collection) {
  144. Assert.hasText(collection, "Collection must not be null nor empty");
  145. this.collectionName = collection;
  146. return this;
  147. }
  148. /**
  149. * Set the filter to apply.
  150. *
  151. * @param filter the {@link Query } to apply for filtering events. Must not be {@literal null}.
  152. * @return this.
  153. */
  154. public TailableCursorRequestOptionsBuilder filter(Query filter) {
  155. Assert.notNull(filter, "Filter must not be null");
  156. this.query = filter;
  157. return this;
  158. }
  159. /**
  160. * @return the built {@link TailableCursorRequestOptions}.
  161. */
  162. public TailableCursorRequestOptions build() {
  163. TailableCursorRequestOptions options = new TailableCursorRequestOptions();
  164. options.collectionName = collectionName;
  165. options.query = query;
  166. return options;
  167. }
  168. }
  169. }
  170. /**
  171. * Builder for creating {@link TailableCursorRequest}.
  172. *
  173. * @author Mark Paluch
  174. * @since 2.1
  175. * @see TailableCursorRequestOptions
  176. */
  177. public static class TailableCursorRequestBuilder<T> {
  178. private @Nullable MessageListener<Document, ? super T> listener;
  179. private TailableCursorRequestOptionsBuilder delegate = TailableCursorRequestOptions.builder();
  180. private TailableCursorRequestBuilder() {}
  181. /**
  182. * Set the name of the {@link com.mongodb.client.MongoCollection} to listen to.
  183. *
  184. * @param collectionName must not be {@literal null} nor empty.
  185. * @return this.
  186. */
  187. public TailableCursorRequestBuilder<T> collection(String collectionName) {
  188. Assert.hasText(collectionName, "CollectionName must not be null");
  189. delegate.collection(collectionName);
  190. return this;
  191. }
  192. /**
  193. * Set the {@link MessageListener} event {@link Message messages} will be published to.
  194. *
  195. * @param messageListener must not be {@literal null}.
  196. * @return this.
  197. */
  198. public TailableCursorRequestBuilder<T> publishTo(MessageListener<Document, ? super T> messageListener) {
  199. Assert.notNull(messageListener, "MessageListener must not be null");
  200. this.listener = messageListener;
  201. return this;
  202. }
  203. /**
  204. * Set the filter to apply.
  205. *
  206. * @param filter the {@link Query } to apply for filtering events. Must not be {@literal null}.
  207. * @return this.
  208. */
  209. public TailableCursorRequestBuilder<T> filter(Query filter) {
  210. Assert.notNull(filter, "Filter must not be null");
  211. delegate.filter(filter);
  212. return this;
  213. }
  214. /**
  215. * @return the build {@link ChangeStreamRequest}.
  216. */
  217. public TailableCursorRequest<T> build() {
  218. Assert.notNull(listener, "MessageListener must not be null");
  219. return new TailableCursorRequest<>(listener, delegate.build());
  220. }
  221. }
  222. }