@Override public Flux<U> updates() { return delegate.updates(); }
@Override public Flux<SubscriptionQueryUpdateMessage<U>> updates() { return delegate.updates().map(serializer::deserialize); }
@Override public <Q, I, U> SubscriptionQueryResult<I, U> subscriptionQuery(String queryName, Q query, ResponseType<I> initialResponseType, ResponseType<U> updateResponseType, SubscriptionQueryBackpressure backpressure, int updateBufferSize) { SubscriptionQueryMessage<Q, I, U> subscriptionQueryMessage = new GenericSubscriptionQueryMessage<>(query, queryName, initialResponseType, updateResponseType); SubscriptionQueryResult<QueryResponseMessage<I>, SubscriptionQueryUpdateMessage<U>> result = queryBus .subscriptionQuery(processInterceptors(subscriptionQueryMessage), backpressure, updateBufferSize); return new DefaultSubscriptionQueryResult<>( result.initialResult() .filter(initialResult -> Objects.nonNull(initialResult.getPayload())) .map(Message::getPayload) .onErrorMap(e -> e instanceof IllegalPayloadAccessException ? e.getCause() : e), result.updates() .filter(update -> Objects.nonNull(update.getPayload())) .map(SubscriptionQueryUpdateMessage::getPayload), result ); }
/** * Delegates handling of initial result and incremental updates to the provided consumers. Subscription to the * incremental updates is done after the initial result is retrieved and its consumer is invoked. If anything goes * wrong during invoking or consuming initial result or incremental updates, subscription is cancelled. * * @param initialResultConsumer The consumer to be invoked when the initial result is retrieved * @param updateConsumer The consumer to be invoked when incremental updates are emitted */ default void handle(Consumer<? super I> initialResultConsumer, Consumer<? super U> updateConsumer) { initialResult().subscribe(initialResult -> { try { initialResultConsumer.accept(initialResult); updates().subscribe(updateConsumer::accept); } catch (Exception e) { cancel(); } }, t -> cancel()); } }
private void subscribe(SubscriptionQuery query) { String subscriptionId = query.getSubscriptionIdentifier(); SubscriptionQueryResult<QueryResponseMessage<Object>, SubscriptionQueryUpdateMessage<Object>> result = localSegment .subscriptionQuery(serializer.deserialize(query)); Disposable disposable = result.updates().subscribe( u -> publisher.publish(serializer.serialize(u, subscriptionId)), e -> publisher.publish(serializer.serializeCompleteExceptionally(subscriptionId, e)), () -> publisher.publish(serializer.serializeComplete(subscriptionId))); Registration registration = () -> { disposable.dispose(); return true; }; subscriptions.computeIfAbsent(subscriptionId,id -> new DisposableResult<>(result, registration)); }
@Override public Flux<U> updates() { return delegate.updates(); }
@Override public Flux<SubscriptionQueryUpdateMessage<U>> updates() { return delegate.updates().map(serializer::deserialize); }
@GetMapping(value = "/rooms/{roomId}/messages/subscribe", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<ChatMessage> subscribeRoomMessages(@PathVariable String roomId) { RoomMessagesQuery query = new RoomMessagesQuery(roomId); SubscriptionQueryResult<List<ChatMessage>, ChatMessage> result; result = gateway.subscriptionQuery(query, multipleInstancesOf(ChatMessage.class), instanceOf(ChatMessage.class)); /* If you only want to send new messages to the client, you could simply do: return result.updates(); However, in our implementation we want to provide both existing messages and new ones, so we combine the initial result and the updates in a single flux. */ Flux<ChatMessage> initialResult = result.initialResult().flatMapMany(Flux::fromIterable); return Flux.concat(initialResult, result.updates()); }
/** * Delegates handling of initial result and incremental updates to the provided consumers. Subscription to the * incremental updates is done after the initial result is retrieved and its consumer is invoked. If anything goes * wrong during invoking or consuming initial result or incremental updates, subscription is cancelled. * * @param initialResultConsumer The consumer to be invoked when the initial result is retrieved * @param updateConsumer The consumer to be invoked when incremental updates are emitted */ default void handle(Consumer<? super I> initialResultConsumer, Consumer<? super U> updateConsumer) { initialResult().subscribe(initialResult -> { try { initialResultConsumer.accept(initialResult); updates().subscribe(updateConsumer::accept); } catch (Exception e) { cancel(); } }, t -> cancel()); } }
/** * Delegates handling of initial result and incremental updates to the provided consumers. Subscription to the * incremental updates is done after the initial result is retrieved and its consumer is invoked. If anything goes * wrong during invoking or consuming initial result or incremental updates, subscription is cancelled. * * @param initialResultConsumer The consumer to be invoked when the initial result is retrieved * @param updateConsumer The consumer to be invoked when incremental updates are emitted */ default void handle(Consumer<? super I> initialResultConsumer, Consumer<? super U> updateConsumer) { initialResult().subscribe(initialResult -> { try { initialResultConsumer.accept(initialResult); updates().subscribe(updateConsumer::accept); } catch (Exception e) { cancel(); } }, t -> cancel()); } }
@GetMapping("/subscribe/order-book/{orderBookId}") public Flux<ServerSentEvent<OrderBookView>> subscribeToOrderBook(@PathVariable String orderBookId) { SubscriptionQueryResult<OrderBookView, OrderBookView> subscription = queryGateway .subscriptionQuery(new OrderBookByIdQuery(new OrderBookId(orderBookId)), ResponseTypes.instanceOf(OrderBookView.class), ResponseTypes.instanceOf(OrderBookView.class)); return subscription.initialResult().concatWith(subscription.updates()) .map(ob -> ServerSentEvent.builder(ob).build()); }
@Override public <Q, I, U> SubscriptionQueryResult<I, U> subscriptionQuery(String queryName, Q query, ResponseType<I> initialResponseType, ResponseType<U> updateResponseType, SubscriptionQueryBackpressure backpressure, int updateBufferSize) { SubscriptionQueryMessage<Q, I, U> subscriptionQueryMessage = new GenericSubscriptionQueryMessage<>(query, queryName, initialResponseType, updateResponseType); SubscriptionQueryResult<QueryResponseMessage<I>, SubscriptionQueryUpdateMessage<U>> result = queryBus .subscriptionQuery(processInterceptors(subscriptionQueryMessage), backpressure, updateBufferSize); return new DefaultSubscriptionQueryResult<>( result.initialResult() .filter(initialResult -> Objects.nonNull(initialResult.getPayload())) .map(Message::getPayload) .onErrorMap(e -> e instanceof IllegalPayloadAccessException ? e.getCause() : e), result.updates() .filter(update -> Objects.nonNull(update.getPayload())) .map(SubscriptionQueryUpdateMessage::getPayload), result ); }
@Override public <Q, I, U> SubscriptionQueryResult<I, U> subscriptionQuery(String queryName, Q query, ResponseType<I> initialResponseType, ResponseType<U> updateResponseType, SubscriptionQueryBackpressure backpressure, int updateBufferSize) { SubscriptionQueryMessage<Q, I, U> subscriptionQueryMessage = new GenericSubscriptionQueryMessage<>(query, queryName, initialResponseType, updateResponseType); SubscriptionQueryResult<QueryResponseMessage<I>, SubscriptionQueryUpdateMessage<U>> result = queryBus .subscriptionQuery(processInterceptors(subscriptionQueryMessage), backpressure, updateBufferSize); return new DefaultSubscriptionQueryResult<>( result.initialResult() .filter(initialResult -> Objects.nonNull(initialResult.getPayload())) .map(QueryResponseMessage::getPayload), result.updates() .filter(update -> Objects.nonNull(update.getPayload())) .map(SubscriptionQueryUpdateMessage::getPayload), result ); }
@Override @Synchronized protected int sizeInBackEnd(Query<CardSummary, Void> query) { if (countQueryResult != null) { countQueryResult.cancel(); countQueryResult = null; } CountCardSummariesQuery countCardSummariesQuery = new CountCardSummariesQuery(filter); log.trace("submitting {}", countCardSummariesQuery); countQueryResult = queryGateway.subscriptionQuery(countCardSummariesQuery, ResponseTypes.instanceOf(CountCardSummariesResponse.class), ResponseTypes.instanceOf(CountChangedUpdate.class)); /* When the count changes (new giftcards issued), the UI has to do an entirely new query (this is * how the Vaadin grid works). When we're bulk issuing, it doesn't make sense to do that on every single * issue event. Therefore, we buffer the updates for 250 milliseconds using reactor, and do the new * query at most once per 250ms. */ countQueryResult.updates().buffer(Duration.ofMillis(250)).subscribe( countChanged -> { log.trace("processing query update for {}: {}", countCardSummariesQuery, countChanged); /* This won't do, would lead to immediate new queries, looping a few times. */ // fireEvent(new DataChangeEvent(this)); executorService.execute(() -> fireEvent(new DataChangeEvent<>(this))); }); return countQueryResult.initialResult().block().getCount(); }
private void subscribe(SubscriptionQuery query) { String subscriptionId = query.getSubscriptionIdentifier(); SubscriptionQueryResult<QueryResponseMessage<Object>, SubscriptionQueryUpdateMessage<Object>> result = localSegment .subscriptionQuery(serializer.deserialize(query)); Disposable disposable = result.updates().subscribe( u -> publisher.publish(serializer.serialize(u, subscriptionId)), e -> publisher.publish(serializer.serializeCompleteExceptionally(subscriptionId, e)), () -> publisher.publish(serializer.serializeComplete(subscriptionId))); Registration registration = () -> { disposable.dispose(); return true; }; subscriptions.computeIfAbsent(subscriptionId,id -> new DisposableResult<>(result, registration)); }
fetchQueryResult.updates().subscribe( cardSummary -> { log.trace("processing query update for {}: {}", fetchCardSummariesQuery, cardSummary);