PageRenderTime 54ms CodeModel.GetById 2ms RepoModel.GetById 0ms app.codeStats 0ms

/messaging/src/main/java/org/axonframework/queryhandling/DefaultQueryGateway.java

http://github.com/AxonFramework/AxonFramework
Java | 253 lines | 146 code | 26 blank | 81 comment | 5 complexity | 2c5311244faf07f4ba291c79e085d2bf MD5 | raw file
  1. /*
  2. * Copyright (c) 2010-2020. Axon Framework
  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. * http://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.axonframework.queryhandling;
  17. import org.axonframework.common.AxonConfigurationException;
  18. import org.axonframework.common.Registration;
  19. import org.axonframework.messaging.IllegalPayloadAccessException;
  20. import org.axonframework.messaging.Message;
  21. import org.axonframework.messaging.MessageDispatchInterceptor;
  22. import org.axonframework.messaging.responsetypes.ResponseType;
  23. import java.util.List;
  24. import java.util.Objects;
  25. import java.util.concurrent.CompletableFuture;
  26. import java.util.concurrent.CopyOnWriteArrayList;
  27. import java.util.concurrent.TimeUnit;
  28. import java.util.stream.Stream;
  29. import static java.util.Arrays.asList;
  30. import static org.axonframework.common.BuilderUtils.assertNonNull;
  31. import static org.axonframework.messaging.GenericMessage.asMessage;
  32. import static org.axonframework.queryhandling.GenericQueryResponseMessage.asResponseMessage;
  33. /**
  34. * Implementation of the QueryGateway interface that allows the registration of dispatchInterceptors.
  35. *
  36. * @author Marc Gathier
  37. * @author Allard Buijze
  38. * @author Steven van Beelen
  39. * @since 3.1
  40. */
  41. public class DefaultQueryGateway implements QueryGateway {
  42. private final QueryBus queryBus;
  43. private final List<MessageDispatchInterceptor<? super QueryMessage<?, ?>>> dispatchInterceptors;
  44. /**
  45. * Instantiate a {@link DefaultQueryGateway} based on the fields contained in the {@link Builder}.
  46. * <p>
  47. * Will assert that the {@link QueryBus} is not {@code null}, and will throw an {@link AxonConfigurationException}
  48. * if it is {@code null}.
  49. *
  50. * @param builder the {@link Builder} used to instantiate a {@link DefaultQueryGateway} instance
  51. */
  52. protected DefaultQueryGateway(Builder builder) {
  53. builder.validate();
  54. this.queryBus = builder.queryBus;
  55. this.dispatchInterceptors = builder.dispatchInterceptors;
  56. }
  57. /**
  58. * Instantiate a Builder to be able to create a {@link DefaultQueryGateway}.
  59. * <p>
  60. * The {@code dispatchInterceptors} is defaulted to an empty list. The {@link QueryBus} is a
  61. * <b>hard requirement</b> and as such should be provided.
  62. *
  63. * @return a Builder to be able to create a {@link DefaultQueryGateway}
  64. */
  65. public static Builder builder() {
  66. return new Builder();
  67. }
  68. @Override
  69. public <R, Q> CompletableFuture<R> query(String queryName, Q query, ResponseType<R> responseType) {
  70. CompletableFuture<QueryResponseMessage<R>> queryResponse = queryBus
  71. .query(processInterceptors(new GenericQueryMessage<>(asMessage(query), queryName, responseType)));
  72. CompletableFuture<R> result = new CompletableFuture<>();
  73. queryResponse.exceptionally(cause -> asResponseMessage(responseType.responseMessagePayloadType(), cause))
  74. .thenAccept(queryResponseMessage -> {
  75. try {
  76. if (queryResponseMessage.isExceptional()) {
  77. result.completeExceptionally(queryResponseMessage.exceptionResult());
  78. } else {
  79. result.complete(queryResponseMessage.getPayload());
  80. }
  81. } catch (Exception e) {
  82. result.completeExceptionally(e);
  83. }
  84. });
  85. return result;
  86. }
  87. @Override
  88. public <R, Q> Stream<R> scatterGather(String queryName,
  89. Q query,
  90. ResponseType<R> responseType,
  91. long timeout,
  92. TimeUnit timeUnit) {
  93. GenericQueryMessage<?, R> queryMessage = new GenericQueryMessage<>(asMessage(query), queryName, responseType);
  94. return queryBus.scatterGather(processInterceptors(queryMessage), timeout, timeUnit)
  95. .map(QueryResponseMessage::getPayload);
  96. }
  97. /**
  98. * @deprecated in favour of the {{@link #subscriptionQuery(String, Object, ResponseType, ResponseType, int)}}
  99. */
  100. @Deprecated
  101. @Override
  102. public <Q, I, U> SubscriptionQueryResult<I, U> subscriptionQuery(String queryName,
  103. Q query,
  104. ResponseType<I> initialResponseType,
  105. ResponseType<U> updateResponseType,
  106. SubscriptionQueryBackpressure backpressure,
  107. int updateBufferSize) {
  108. SubscriptionQueryMessage<?, I, U> interceptedQuery =
  109. getSubscriptionQueryMessage(queryName, query, initialResponseType, updateResponseType);
  110. SubscriptionQueryResult<QueryResponseMessage<I>, SubscriptionQueryUpdateMessage<U>> result =
  111. queryBus.subscriptionQuery(interceptedQuery, backpressure, updateBufferSize);
  112. return getSubscriptionQueryResult(result);
  113. }
  114. @Override
  115. public <Q, I, U> SubscriptionQueryResult<I, U> subscriptionQuery(String queryName,
  116. Q query,
  117. ResponseType<I> initialResponseType,
  118. ResponseType<U> updateResponseType,
  119. int updateBufferSize) {
  120. SubscriptionQueryMessage<?, I, U> interceptedQuery =
  121. getSubscriptionQueryMessage(queryName, query, initialResponseType, updateResponseType);
  122. SubscriptionQueryResult<QueryResponseMessage<I>, SubscriptionQueryUpdateMessage<U>> result =
  123. queryBus.subscriptionQuery(interceptedQuery, updateBufferSize);
  124. return getSubscriptionQueryResult(result);
  125. }
  126. private <Q, I, U> SubscriptionQueryMessage<?, I, U> getSubscriptionQueryMessage(String queryName, Q query,
  127. ResponseType<I> initialResponseType,
  128. ResponseType<U> updateResponseType) {
  129. SubscriptionQueryMessage<?, I, U> subscriptionQueryMessage = new GenericSubscriptionQueryMessage<>(
  130. asMessage(query), queryName, initialResponseType, updateResponseType
  131. );
  132. return processInterceptors(subscriptionQueryMessage);
  133. }
  134. private <I, U> DefaultSubscriptionQueryResult<I, U> getSubscriptionQueryResult(
  135. SubscriptionQueryResult<QueryResponseMessage<I>, SubscriptionQueryUpdateMessage<U>> result) {
  136. return new DefaultSubscriptionQueryResult<>(
  137. result.initialResult()
  138. .filter(initialResult -> Objects.nonNull(initialResult.getPayload()))
  139. .map(Message::getPayload)
  140. .onErrorMap(e -> e instanceof IllegalPayloadAccessException ? e.getCause() : e),
  141. result.updates()
  142. .filter(update -> Objects.nonNull(update.getPayload()))
  143. .map(SubscriptionQueryUpdateMessage::getPayload),
  144. result
  145. );
  146. }
  147. @Override
  148. public Registration registerDispatchInterceptor(
  149. MessageDispatchInterceptor<? super QueryMessage<?, ?>> interceptor) {
  150. dispatchInterceptors.add(interceptor);
  151. return () -> dispatchInterceptors.remove(interceptor);
  152. }
  153. @SuppressWarnings("unchecked")
  154. private <Q, R, T extends QueryMessage<Q, R>> T processInterceptors(T query) {
  155. T intercepted = query;
  156. for (MessageDispatchInterceptor<? super QueryMessage<?, ?>> interceptor : dispatchInterceptors) {
  157. intercepted = (T) interceptor.handle(intercepted);
  158. }
  159. return intercepted;
  160. }
  161. /**
  162. * Builder class to instantiate a {@link DefaultQueryGateway}.
  163. * <p>
  164. * The {@code dispatchInterceptors} is defaulted to an empty list. The {@link QueryBus} is a
  165. * <b>hard requirement</b> and as such should be provided.
  166. */
  167. public static class Builder {
  168. private QueryBus queryBus;
  169. private List<MessageDispatchInterceptor<? super QueryMessage<?, ?>>> dispatchInterceptors =
  170. new CopyOnWriteArrayList<>();
  171. /**
  172. * Sets the {@link QueryBus} to deliver {@link QueryMessage}s on received in this {@link QueryGateway}
  173. * implementation.
  174. *
  175. * @param queryBus a {@link QueryBus} to deliver {@link QueryMessage}s on received in this {@link QueryGateway}
  176. * implementation
  177. * @return the current Builder instance, for fluent interfacing
  178. */
  179. public Builder queryBus(QueryBus queryBus) {
  180. assertNonNull(queryBus, "QueryBus may not be null");
  181. this.queryBus = queryBus;
  182. return this;
  183. }
  184. /**
  185. * Sets the {@link List} of {@link MessageDispatchInterceptor}s for {@link QueryMessage}s. Are invoked when a
  186. * query is being dispatched.
  187. *
  188. * @param dispatchInterceptors which are invoked when a query is being dispatched
  189. * @return the current Builder instance, for fluent interfacing
  190. */
  191. public Builder dispatchInterceptors(
  192. MessageDispatchInterceptor<? super QueryMessage<?, ?>>... dispatchInterceptors) {
  193. return dispatchInterceptors(asList(dispatchInterceptors));
  194. }
  195. /**
  196. * Sets the {@link List} of {@link MessageDispatchInterceptor}s for {@link QueryMessage}s. Are invoked when a
  197. * query is being dispatched.
  198. *
  199. * @param dispatchInterceptors which are invoked when a query is being dispatched
  200. * @return the current Builder instance, for fluent interfacing
  201. */
  202. public Builder dispatchInterceptors(
  203. List<MessageDispatchInterceptor<? super QueryMessage<?, ?>>> dispatchInterceptors) {
  204. this.dispatchInterceptors = dispatchInterceptors != null && !dispatchInterceptors.isEmpty()
  205. ? new CopyOnWriteArrayList<>(dispatchInterceptors)
  206. : new CopyOnWriteArrayList<>();
  207. return this;
  208. }
  209. /**
  210. * Initializes a {@link DefaultQueryGateway} as specified through this Builder.
  211. *
  212. * @return a {@link DefaultQueryGateway} as specified through this Builder
  213. */
  214. public DefaultQueryGateway build() {
  215. return new DefaultQueryGateway(this);
  216. }
  217. /**
  218. * Validates whether the fields contained in this Builder are set accordingly.
  219. *
  220. * @throws AxonConfigurationException if one field is asserted to be incorrect according to the Builder's
  221. * specifications
  222. */
  223. protected void validate() throws AxonConfigurationException {
  224. assertNonNull(queryBus, "The QueryBus is a hard requirement and should be provided");
  225. }
  226. }
  227. }