akka.japi.pf.PFBuilder Java Examples

The following examples show how to use akka.japi.pf.PFBuilder. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: StoppingSupervisorWithoutLoggingActorKilledExceptionStrategy.java    From flink with Apache License 2.0 6 votes vote down vote up
@Override
public SupervisorStrategy create() {
	return new OneForOneStrategy(
		false,
		new PFBuilder<Throwable, SupervisorStrategy.Directive>()
			.match(
				Exception.class,
				(Exception e) -> {
					if (e instanceof ActorKilledException) {
						LOG.debug("Actor was killed. Stopping it now.", e);
					} else {
						LOG.error("Actor failed with exception. Stopping it now.", e);
					}
					return SupervisorStrategy.Stop$.MODULE$;
				})
			.build());
}
 
Example #2
Source File: StoppingSupervisorWithoutLoggingActorKilledExceptionStrategy.java    From Flink-CEPplus with Apache License 2.0 6 votes vote down vote up
@Override
public SupervisorStrategy create() {
	return new OneForOneStrategy(
		false,
		new PFBuilder<Throwable, SupervisorStrategy.Directive>()
			.match(
				Exception.class,
				(Exception e) -> {
					if (e instanceof ActorKilledException) {
						LOG.debug("Actor was killed. Stopping it now.", e);
					} else {
						LOG.error("Actor failed with exception. Stopping it now.", e);
					}
					return SupervisorStrategy.Stop$.MODULE$;
				})
			.build());
}
 
Example #3
Source File: MongoSearchUpdaterFlow.java    From ditto with Eclipse Public License 2.0 6 votes vote down vote up
private Source<WriteResultAndErrors, NotUsed> executeBulkWrite(
        final List<AbstractWriteModel> abstractWriteModels) {
    final List<WriteModel<Document>> writeModels = abstractWriteModels.stream()
            .map(AbstractWriteModel::toMongo)
            .collect(Collectors.toList());
    return Source.fromPublisher(collection.bulkWrite(writeModels, new BulkWriteOptions().ordered(false)))
            .map(bulkWriteResult -> WriteResultAndErrors.success(abstractWriteModels, bulkWriteResult))
            .recoverWithRetries(1, new PFBuilder<Throwable, Source<WriteResultAndErrors, NotUsed>>()
                    .match(MongoBulkWriteException.class, bulkWriteException ->
                            Source.single(WriteResultAndErrors.failure(abstractWriteModels, bulkWriteException))
                    )
                    .matchAny(error ->
                            Source.single(WriteResultAndErrors.unexpectedError(abstractWriteModels, error))
                    )
                    .build()
            );
}
 
Example #4
Source File: MongoOpsUtil.java    From ditto with Eclipse Public License 2.0 6 votes vote down vote up
private static Source<Optional<Throwable>, NotUsed> doDeleteByFilter(final MongoCollection<Document> collection,
        final Bson filter) {
    // https://stackoverflow.com/a/33164008
    // claims unordered bulk ops halve MongoDB load
    final List<WriteModel<Document>> writeModel =
            Collections.singletonList(new DeleteManyModel<>(filter));
    final BulkWriteOptions options = new BulkWriteOptions().ordered(false);
    return Source.fromPublisher(collection.bulkWrite(writeModel, options))
            .map(result -> {
                if (LOGGER.isDebugEnabled()) {
                    // in contrast to Bson, BsonDocument has meaningful toString()
                    final BsonDocument filterBsonDoc = BsonUtil.toBsonDocument(filter);
                    LOGGER.debug("Deleted <{}> documents from collection <{}>. Filter was <{}>.",
                            result.getDeletedCount(), collection.getNamespace(), filterBsonDoc);
                }
                return Optional.<Throwable>empty();
            })
            .recoverWithRetries(RETRY_ATTEMPTS, new PFBuilder<Throwable, Source<Optional<Throwable>, NotUsed>>()
                    .matchAny(throwable -> Source.single(Optional.of(throwable)))
                    .build());
}
 
Example #5
Source File: MongoTimestampPersistence.java    From ditto with Eclipse Public License 2.0 6 votes vote down vote up
private static Source<Success, NotUsed> repeatableCreateCappedCollectionSource(
        final MongoDatabase database,
        final String collectionName,
        final long cappedCollectionSizeInBytes) {

    final CreateCollectionOptions collectionOptions = new CreateCollectionOptions()
            .capped(true)
            .sizeInBytes(cappedCollectionSizeInBytes)
            .maxDocuments(1);

    return Source.lazily(
            () -> Source.fromPublisher(database.createCollection(collectionName, collectionOptions)))
            .mapMaterializedValue(whatever -> NotUsed.getInstance())
            .withAttributes(Attributes.inputBuffer(1, 1))
            .recoverWithRetries(1, new PFBuilder<Throwable, Source<Success, NotUsed>>()
                    .match(MongoCommandException.class,
                            MongoTimestampPersistence::isCollectionAlreadyExistsError,
                            error -> Source.single(Success.SUCCESS))
                    .build());

}
 
Example #6
Source File: SearchSource.java    From ditto with Eclipse Public License 2.0 6 votes vote down vote up
private Source<Pair<String, JsonObject>, NotUsed> retrieveThingForElement(final String thingId) {
    if (thingIdOnly) {
        final JsonObject idOnlyThingJson = JsonObject.newBuilder().set(Thing.JsonFields.ID, thingId).build();
        return Source.single(Pair.create(thingId, idOnlyThingJson));
    } else {
        return retrieveThing(thingId, fields)
                .map(thingJson -> Pair.create(thingId, thingJson))
                .recoverWithRetries(1,
                        new PFBuilder<Throwable, Graph<SourceShape<Pair<String, JsonObject>>, NotUsed>>()
                                .match(ThingNotAccessibleException.class, thingNotAccessible -> {
                                    // out-of-sync thing detected
                                    final ThingsOutOfSync thingsOutOfSync =
                                            ThingsOutOfSync.of(Collections.singletonList(ThingId.of(thingId)),
                                                    getDittoHeaders());

                                    pubSubMediator.tell(
                                            DistPubSubAccess.publishViaGroup(ThingsOutOfSync.TYPE, thingsOutOfSync),
                                            ActorRef.noSender());
                                    return Source.empty();
                                })
                                .build()
                );
    }
}
 
Example #7
Source File: ResumeSource.java    From ditto with Eclipse Public License 2.0 6 votes vote down vote up
/**
 * Create a flow from seeds to either elements or failure with reason and the previous N elements that passed
 * through the stream before the failure. For maximum information, even elements emitted 2 or more failures ago
 * are kept in the look-behind queue.
 * <ul>
 * <li>Emits when: stream created by {@code resume} emits or fails.</li>
 * <li>Completes when: a stream created by {@code resume} completes.</li>
 * <li>Cancels when: downstream cancels.</li>
 * <li>Fails when: never.</li>
 * </ul>
 *
 * @param resume Creator of a stream of elements from a resumption seed.
 * @param lookBehind How many elements to keep in memory to create the next seed on failure.
 * @param <S> Type of seeds.
 * @param <E> Type of elements.
 * @return A never-failing flow.
 */
private static <S, E> Flow<S, Either<FailureWithLookBehind<E>, E>, NotUsed> resumeWithFailuresAppended(
        final Function<S, Source<E, ?>> resume, final int lookBehind,
        final Function<Throwable, Optional<Throwable>> resumeOrMapError) {

    return Flow.<S>create()
            .flatMapConcat(seed -> resume.apply(seed)
                    .<Envelope<E>>map(Element::new)
                    .concat(Source.single(new EndOfStream<>()))
                    .recoverWithRetries(1,
                            new PFBuilder<Throwable, Graph<SourceShape<Envelope<E>>, NotUsed>>()
                                    .matchAny(error -> resumeOrMapError.apply(error)
                                            .<Source<Envelope<E>, NotUsed>>map(Source::failed)
                                            .orElseGet(() -> Source.single(new Error<>(error)))
                                    )
                                    .build())
            )
            .via(new EndStreamOnEOS<>())
            .buffer(1, OverflowStrategy.backpressure())
            .statefulMapConcat(() -> new StatefulLookBehindFunction<>(lookBehind));
}
 
Example #8
Source File: EscalatingSupervisorStrategy.java    From flink with Apache License 2.0 5 votes vote down vote up
@Override
public SupervisorStrategy create() {
	return new OneForOneStrategy(
		false,
		new PFBuilder<Throwable, SupervisorStrategy.Directive>()
			.matchAny(
				(ignored) -> SupervisorStrategy.escalate())
			.build());
}
 
Example #9
Source File: S3.java    From ts-reaktive with MIT License 5 votes vote down vote up
/**
   * Reads the stream of events written to S3 using {@link #store(String, Seq)} before.
   */
  public Source<com.tradeshift.reaktive.protobuf.Query.EventEnvelope, NotUsed> loadEvents(String key) {
      return download(key)
.recoverWith(new PFBuilder<Throwable, Source<ByteString,NotUsed>>()
	.matchAny(x -> Source.empty()) // not found -> no data
	.build()
)
      .via(DelimitedProtobufFraming.instance)
      .map(bs -> com.tradeshift.reaktive.protobuf.Query.EventEnvelope.parseFrom(bs.iterator().asInputStream()));
  }
 
Example #10
Source File: S3.java    From ts-reaktive with MIT License 5 votes vote down vote up
/**
  * Loads the last known written offset from S3, or returns 0 if not found
  */
 public CompletionStage<Long> loadOffset() {
     return download("_lastOffset")
 		.reduce((bs1, bs2) -> bs1.concat(bs2))
 		.map(bs -> Long.parseLong(bs.utf8String()))
 		.recoverWith(new PFBuilder<Throwable, Source<Long,NotUsed>>()
 			.matchAny(x -> Source.single(0L)) // not found -> start at 0
	.build()
)
 		.runWith(Sink.head(), materializer);
 }
 
Example #11
Source File: MongoThingsSearchPersistence.java    From ditto with Eclipse Public License 2.0 5 votes vote down vote up
private static PartialFunction<Throwable, Throwable> handleMongoExecutionTimeExceededException() {
    return new PFBuilder<Throwable, Throwable>()
            .match(Throwable.class, error ->
                    error instanceof MongoExecutionTimeoutException
                            ? GatewayQueryTimeExceededException.newBuilder().build()
                            : error
            )
            .build();
}
 
Example #12
Source File: MongoThingsSearchUpdaterPersistence.java    From ditto with Eclipse Public License 2.0 5 votes vote down vote up
@Override
public Source<List<Throwable>, NotUsed> purge(final CharSequence namespace) {
    final Bson filter = thingNamespaceFilter(namespace);
    final Bson update = new BsonDocument().append(AbstractWriteModel.SET,
            new BsonDocument().append(FIELD_DELETE_AT, new BsonDateTime(0L)));
    final UpdateOptions updateOptions = new UpdateOptions().bypassDocumentValidation(true);
    final WriteModel<Document> writeModel = new UpdateManyModel<>(filter, update, updateOptions);

    return Source.fromPublisher(collection.bulkWrite(Collections.singletonList(writeModel)))
            .map(bulkWriteResult -> Collections.<Throwable>emptyList())
            .recoverWithRetries(1, new PFBuilder<Throwable, Source<List<Throwable>, NotUsed>>()
                    .matchAny(throwable -> Source.single(Collections.singletonList(throwable)))
                    .build());
}
 
Example #13
Source File: ThingsSearchCursorTest.java    From ditto with Eclipse Public License 2.0 5 votes vote down vote up
@Test
public void decodingInvalidCursorsFailsWithInvalidCursorException() {
    assertThat(
            ThingsSearchCursor.decode("null", materializer)
                    .<Object>map(x -> x)
                    .recover(new PFBuilder<Throwable, Object>().matchAny(x -> x).build())
                    .runWith(Sink.head(), materializer)
                    .toCompletableFuture()
                    .join())
            .isInstanceOf(InvalidOptionException.class);

}
 
Example #14
Source File: SearchActor.java    From ditto with Eclipse Public License 2.0 5 votes vote down vote up
private <T> Flow<T, Object, NotUsed> stopTimerAndHandleError(final StartedTimer searchTimer,
        final WithDittoHeaders<?> command) {
    return Flow.<T, Object>fromFunction(
            element -> {
                stopTimer(searchTimer);
                return element;
            })
            .recoverWithRetries(1, new PFBuilder<Throwable, Graph<SourceShape<Object>, NotUsed>>()
                    .matchAny(error -> {
                        stopTimer(searchTimer);
                        return Source.single(asDittoRuntimeException(error, command));
                    })
                    .build()
            );
}
 
Example #15
Source File: ThingsSearchCursor.java    From ditto with Eclipse Public License 2.0 5 votes vote down vote up
private static PartialFunction<Throwable, Throwable> createDecodeErrorMapper() {
    // offer no explanation for non-decodable cursors.
    return new PFBuilder<Throwable, Throwable>()
            .matchAny(error -> {
                final Throwable e = error instanceof CompletionException ? error.getCause() : error;
                LOG.info("Failed to decode cursor: {} '{}' due to {}", e.getClass(), e.getMessage(),
                        Objects.toString(e.getCause()));
                return invalidCursorBuilder().build();
            })
            .build();
}
 
Example #16
Source File: RootRoute.java    From ditto with Eclipse Public License 2.0 5 votes vote down vote up
private static Route javaFunctionToRoute(final Function<akka.http.scaladsl.server.RequestContext,
        CompletionStage<RouteResult>> function) {

    final Function1<akka.http.scaladsl.server.RequestContext, Future<RouteResult>> scalaFunction =
            new PFBuilder<akka.http.scaladsl.server.RequestContext, Future<RouteResult>>()
                    .matchAny(scalaRequestContext -> FutureConverters.toScala(function.apply(scalaRequestContext)))
                    .build();
    return RouteAdapter.asJava(scalaFunction);
}
 
Example #17
Source File: MongoOpsUtil.java    From ditto with Eclipse Public License 2.0 5 votes vote down vote up
private static Source<Optional<Throwable>, NotUsed> doDrop(final MongoCollection<Document> collection) {
    return Source.fromPublisher(collection.drop())
            .map(result -> {
                LOGGER.debug("Successfully dropped collection <{}>.", collection.getNamespace());
                return Optional.<Throwable>empty();
            })
            .recoverWithRetries(RETRY_ATTEMPTS, new PFBuilder<Throwable, Source<Optional<Throwable>, NotUsed>>()
                    .matchAny(throwable -> Source.single(Optional.of(throwable)))
                    .build());
}
 
Example #18
Source File: IndexOperations.java    From ditto with Eclipse Public License 2.0 5 votes vote down vote up
private static PartialFunction<Throwable, Source<Success, NotUsed>> buildDropIndexRecovery(
        final String indexDescription) {
    return new PFBuilder<Throwable, Source<Success, NotUsed>>()
            .match(MongoCommandException.class, IndexOperations::isIndexNotFound, throwable -> {
                LOGGER.debug("Index <{}> could not be dropped because it does not exist (anymore).",
                        indexDescription);
                return Source.single(Success.SUCCESS);
            })
            .build();
}
 
Example #19
Source File: EventSnapshotCleanupCoordinator.java    From ditto with Eclipse Public License 2.0 5 votes vote down vote up
@Override
protected Source<CleanupPersistenceResponse, NotUsed> getSource() {

    final PartialFunction<EntityIdWithRevision, CompletionStage<CleanupPersistenceResponse>>
            askShardRegionForCleanupByTagType =
            new PFBuilder<EntityIdWithRevision, CompletionStage<CleanupPersistenceResponse>>()
                    .match(ThingTag.class, thingTag ->
                            askShardRegionForCleanup(shardRegions.things(), ThingCommand.RESOURCE_TYPE, thingTag))
                    .match(PolicyTag.class, policyTag ->
                            askShardRegionForCleanup(shardRegions.policies(), PolicyCommand.RESOURCE_TYPE,
                                    policyTag))
                    .match(ConnectionTag.class, connTag ->
                            askShardRegionForCleanup(shardRegions.connections(), ConnectivityCommand.RESOURCE_TYPE,
                                    connTag))
                    .matchAny(e -> {
                        final String errorMessage = "Unexpected entity ID type: " + e;
                        log.error(errorMessage);
                        final CleanupPersistenceResponse failureResponse =
                                CleanupPersistenceResponse.failure(e.getEntityId(),
                                        DittoHeaders.newBuilder().putHeader(ERROR_MESSAGE_HEADER, errorMessage)
                                                .build());
                        return CompletableFuture.completedFuture(failureResponse);
                    })
                    .build();

    return getEntityIdWithRevisionSource()
            .mapAsync(config.getParallelism(), askShardRegionForCleanupByTagType::apply)
            .via(reportToSelf()) // include self-reporting for acknowledged
            .log(EventSnapshotCleanupCoordinator.class.getSimpleName(), log);
}
 
Example #20
Source File: WireTransferServiceImpl.java    From reactive-stock-trader with Apache License 2.0 5 votes vote down vote up
@SuppressWarnings("unchecked")
private <A, B, C> PartialFunction<Pair<A, C>, Pair<B, C>> collectFirst(PartialFunction<A, B> pf) {
    FI.TypedPredicate<Pair> isDefinedOnFirst = p -> pf.isDefinedAt((((Pair<A, C>) p).first()));
    FI.Apply<Pair, Pair<B, C>> applyOnFirst = p -> Pair.create((pf.apply((A) p.first())), (C) p.second());
    return new PFBuilder<Pair<A, C>, Pair<B, C>>()
        .match(Pair.class, isDefinedOnFirst, applyOnFirst)
        .build();
}
 
Example #21
Source File: WireTransferServiceImpl.java    From reactive-stock-trader with Apache License 2.0 5 votes vote down vote up
private Source<Pair<TransferRequest, Offset>, ?> transferRequestSource(AggregateEventTag<TransferEvent> tag, Offset offset) {
    return transferRepository
        .eventStream(tag, offset)
        .collect(collectByEvent(
            new PFBuilder<TransferEvent, TransferRequest>()
                .match(TransferEvent.TransferInitiated.class, this::requestFunds)
                .match(TransferEvent.FundsRetrieved.class, this::sendFunds)
                .build()
        ));
}
 
Example #22
Source File: ThingsSseRouteBuilder.java    From ditto with Eclipse Public License 2.0 4 votes vote down vote up
private static PartialFunction<HttpHeader, Accept> newAcceptHeaderExtractor() {
    return new PFBuilder<HttpHeader, Accept>()
            .match(Accept.class, ThingsSseRouteBuilder::matchesTextEventStream, accept -> accept)
            .build();
}
 
Example #23
Source File: ThingsSseRouteBuilder.java    From ditto with Eclipse Public License 2.0 4 votes vote down vote up
private Route createSearchSseRoute(final RequestContext ctx, final CompletionStage<DittoHeaders> dittoHeadersStage,
        final Map<String, String> parameters) {

    if (proxyActor == null) {
        return complete(StatusCodes.NOT_IMPLEMENTED);
    }

    final CompletionStage<Source<ServerSentEvent, NotUsed>> sseSourceStage =
            dittoHeadersStage.thenApply(dittoHeaders -> {
                sseAuthorizationEnforcer.checkAuthorization(ctx, dittoHeaders);

                final SearchSourceBuilder searchSourceBuilder = SearchSource.newBuilder()
                        .pubSubMediator(pubSubMediator)
                        .conciergeForwarder(proxyActor)
                        .filter(parameters.get(PARAM_FILTER))
                        .options(parameters.get(PARAM_OPTION))
                        .fields(parameters.get(PARAM_FIELDS))
                        .namespaces(parameters.get(PARAM_NAMESPACES))
                        .dittoHeaders(dittoHeaders);

                // ctx.getRequest().getHeader(LastEventId.class) is not working
                ctx.getRequest()
                        .getHeader(LAST_EVENT_ID_HEADER)
                        .ifPresent(lastEventId -> searchSourceBuilder.lastThingId(lastEventId.value()));

                return searchSourceBuilder.build()
                        .startAsPair(builder -> {})
                        .via(AbstractRoute.throttleByConfig(streamingConfig.getSseConfig().getThrottlingConfig()))
                        .map(pair -> {
                            SEARCH_SSE_COUNTER.increment();
                            return ServerSentEvent.create(pair.second().toString(),
                                    Optional.empty(),
                                    Optional.of(pair.first()),
                                    OptionalInt.empty()
                            );
                        })
                        .recoverWithRetries(1, new PFBuilder<Throwable, Source<ServerSentEvent, NotUsed>>()
                                .match(DittoRuntimeException.class, dittoRuntimeException -> Source.single(
                                        ServerSentEvent.create(dittoRuntimeException.toJsonString())
                                ))
                                .build())
                        .log("SSE " + PATH_SEARCH)
                        .via(eventSniffer.toAsyncFlow(ctx.getRequest()));
            });

    return completeOKWithFuture(sseSourceStage, EventStreamMarshalling.toEventStream());
}
 
Example #24
Source File: WebSocketRoute.java    From ditto with Eclipse Public License 2.0 4 votes vote down vote up
private Flow<DittoRuntimeException, Message, NotUsed> createOutgoing(
        final JsonSchemaVersion version,
        final CharSequence connectionCorrelationId,
        final DittoHeaders additionalHeaders,
        final ProtocolAdapter adapter,
        final HttpRequest request,
        final WebsocketConfig websocketConfig,
        @Nullable final SignalEnrichmentFacade signalEnrichmentFacade) {

    final Optional<JsonWebToken> optJsonWebToken = extractJwtFromRequestIfPresent(request);

    final Source<SessionedJsonifiable, ActorRef> publisherSource =
            Source.actorPublisher(EventAndResponsePublisher.props(
                    websocketConfig.getPublisherBackpressureBufferSize()));

    final Source<SessionedJsonifiable, NotUsed> eventAndResponseSource = publisherSource.mapMaterializedValue(
            publisherActor -> {
                webSocketSupervisor.supervise(publisherActor, connectionCorrelationId, additionalHeaders);
                streamingActor.tell(
                        new Connect(publisherActor, connectionCorrelationId, STREAMING_TYPE_WS, version,
                                optJsonWebToken.map(JsonWebToken::getExpirationTime).orElse(null)),
                        ActorRef.noSender());
                return NotUsed.getInstance();
            })
            .recoverWithRetries(1, new PFBuilder<Throwable, Source<SessionedJsonifiable, NotUsed>>()
                    .match(GatewayWebsocketSessionExpiredException.class,
                            ex -> {
                                LOGGER.withCorrelationId(connectionCorrelationId)
                                        .info("WebSocket connection terminated because JWT expired!");
                                return Source.empty();
                            }).match(GatewayWebsocketSessionClosedException.class,
                            ex -> {
                                LOGGER.withCorrelationId(connectionCorrelationId).info("WebSocket connection" +
                                        " terminated because authorization context changed!");
                                return Source.empty();
                            })
                    .build());

    final Flow<DittoRuntimeException, SessionedJsonifiable, NotUsed> errorFlow =
            Flow.fromFunction(SessionedJsonifiable::error);

    final int signalEnrichmentParallelism = streamingConfig.getParallelism();
    final Flow<SessionedJsonifiable, Message, NotUsed> messageFlow =
            Flow.<SessionedJsonifiable>create()
                    .mapAsync(signalEnrichmentParallelism, postprocess(adapter, signalEnrichmentFacade))
                    .mapConcat(x -> x)
                    .via(Flow.fromFunction(result -> {
                        LOGGER.withCorrelationId(connectionCorrelationId)
                                .debug("Sending outgoing WebSocket message: {}", result);
                        return result;
                    }))
                    .via(outgoingMessageSniffer.toAsyncFlow(request))
                    .<Message>map(TextMessage::create)
                    .via(Flow.fromFunction(msg -> {
                        OUT_COUNTER.increment();
                        return msg;
                    }));

    return joinOutgoingFlows(eventAndResponseSource, errorFlow, messageFlow);
}
 
Example #25
Source File: ThingsAggregatorProxyActor.java    From ditto with Eclipse Public License 2.0 4 votes vote down vote up
private void handleSourceRef(final SourceRef sourceRef, final List<ThingId> thingIds,
        final Command<?> originatingCommand, final ActorRef originatingSender) {
    final Function<Jsonifiable<?>, PlainJson> thingPlainJsonSupplier;
    final Function<List<PlainJson>, CommandResponse<?>> overallResponseSupplier;
    final Function<List<PlainJson>, List<PlainJson>> plainJsonSorter = supplyPlainJsonSorter(thingIds);

    if (originatingCommand instanceof SudoRetrieveThings) {
        thingPlainJsonSupplier = supplyPlainJsonFromSudoRetrieveThingResponse();
        overallResponseSupplier = supplySudoRetrieveThingsResponse(originatingCommand.getDittoHeaders());
    } else {
        thingPlainJsonSupplier = supplyPlainJsonFromRetrieveThingResponse();
        final String namespace = ((RetrieveThings) originatingCommand).getNamespace().orElse(null);
        overallResponseSupplier = supplyRetrieveThingsResponse(originatingCommand.getDittoHeaders(), namespace);
    }

    final StartedTimer timer = DittoMetrics.expiringTimer(TRACE_AGGREGATOR_RETRIEVE_THINGS)
            .tag("size", Integer.toString(thingIds.size()))
            .build();

    final CompletionStage<List<PlainJson>> o =
            (CompletionStage<List<PlainJson>>) sourceRef.getSource()
                    .orElse(Source.single(ThingNotAccessibleException.fromMessage("Thing could not be accessed.",
                            DittoHeaders.empty())))
                    .filterNot(el -> el instanceof DittoRuntimeException)
                    .map(param -> thingPlainJsonSupplier.apply((Jsonifiable<?>) param))
                    .log("retrieve-thing-response", log)
                    .recover(new PFBuilder()
                            .match(NoSuchElementException.class,
                                    nsee -> overallResponseSupplier.apply(Collections.emptyList()))
                            .build()
                    )
                    .runWith(Sink.seq(), actorMaterializer);

    final CompletionStage<? extends CommandResponse<?>> commandResponseCompletionStage = o
            .thenApply(plainJsonSorter)
            .thenApply(overallResponseSupplier::apply)
            .thenApply(list -> {
                stopTimer(timer);
                return list;
            });

    PatternsCS.pipe(commandResponseCompletionStage, getContext().dispatcher()).to(originatingSender);
}