Search in sources :

Example 1 with ServerSentEvent

use of akka.http.javadsl.model.sse.ServerSentEvent in project ditto by eclipse.

the class ThingsSseRouteBuilder method createSseRoute.

private Route createSseRoute(final RequestContext ctx, final CompletionStage<DittoHeaders> dittoHeadersStage, final JsonPointer fieldPointer, final Map<String, String> parameters) {
    @Nullable final var filterString = parameters.get(PARAM_FILTER);
    final List<String> namespaces = getNamespaces(parameters.get(PARAM_NAMESPACES));
    final List<ThingId> targetThingIds = getThingIds(parameters.get(ThingsParameter.IDS.toString()));
    @Nullable final ThingFieldSelector fields = getFieldSelector(parameters.get(ThingsParameter.FIELDS.toString()));
    @Nullable final ThingFieldSelector extraFields = getFieldSelector(parameters.get(PARAM_EXTRA_FIELDS));
    final CompletionStage<SignalEnrichmentFacade> facadeStage = signalEnrichmentProvider == null ? CompletableFuture.completedStage(null) : signalEnrichmentProvider.getFacade(ctx.getRequest());
    final var sseSourceStage = facadeStage.thenCompose(facade -> dittoHeadersStage.thenCompose(dittoHeaders -> sseAuthorizationEnforcer.checkAuthorization(ctx, dittoHeaders).thenApply(unused -> {
        if (filterString != null) {
            // will throw an InvalidRqlExpressionException if the RQL expression was not valid:
            queryFilterCriteriaFactory.filterCriteria(filterString, dittoHeaders);
        }
        final Source<SessionedJsonifiable, SupervisedStream.WithQueue> publisherSource = SupervisedStream.sourceQueue(10);
        return publisherSource.viaMat(KillSwitches.single(), Keep.both()).mapMaterializedValue(pair -> {
            final SupervisedStream.WithQueue withQueue = pair.first();
            final KillSwitch killSwitch = pair.second();
            final String connectionCorrelationId = dittoHeaders.getCorrelationId().orElseThrow(() -> new IllegalStateException("Expected correlation-id in SSE DittoHeaders: " + dittoHeaders));
            final var jsonSchemaVersion = dittoHeaders.getSchemaVersion().orElse(JsonSchemaVersion.LATEST);
            sseConnectionSupervisor.supervise(withQueue.getSupervisedStream(), connectionCorrelationId, dittoHeaders);
            final var authorizationContext = dittoHeaders.getAuthorizationContext();
            final var connect = new Connect(withQueue.getSourceQueue(), connectionCorrelationId, STREAMING_TYPE_SSE, jsonSchemaVersion, null, Set.of(), authorizationContext);
            final var startStreaming = StartStreaming.getBuilder(StreamingType.EVENTS, connectionCorrelationId, authorizationContext).withNamespaces(namespaces).withFilter(filterString).withExtraFields(extraFields).build();
            Patterns.ask(streamingActor, connect, LOCAL_ASK_TIMEOUT).thenApply(ActorRef.class::cast).thenAccept(streamingSessionActor -> streamingSessionActor.tell(startStreaming, ActorRef.noSender())).exceptionally(e -> {
                killSwitch.abort(e);
                return null;
            });
            return NotUsed.getInstance();
        }).mapAsync(streamingConfig.getParallelism(), jsonifiable -> postprocess(jsonifiable, facade, targetThingIds, namespaces, fieldPointer, fields)).mapConcat(jsonValues -> jsonValues).map(jsonValue -> {
            THINGS_SSE_COUNTER.increment();
            return ServerSentEvent.create(jsonValue.toString());
        }).log("SSE " + PATH_THINGS).viaMat(eventSniffer.toAsyncFlow(ctx.getRequest()), Keep.none()).keepAlive(Duration.ofSeconds(1), ServerSentEvent::heartbeat);
    })));
    return completeOKWithFuture(sseSourceStage, EventStreamMarshalling.toEventStream());
}
Also used : PFBuilder(akka.japi.pf.PFBuilder) Arrays(java.util.Arrays) StreamingType(org.eclipse.ditto.internal.utils.pubsub.StreamingType) AbstractRoute(org.eclipse.ditto.gateway.service.endpoints.routes.AbstractRoute) ConditionChecker.checkNotNull(org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull) RequestContext(akka.http.javadsl.server.RequestContext) DittoMetrics(org.eclipse.ditto.internal.utils.metrics.DittoMetrics) RqlPredicateParser(org.eclipse.ditto.rql.parser.RqlPredicateParser) KillSwitch(akka.stream.KillSwitch) ServerSentEvent(akka.http.javadsl.model.sse.ServerSentEvent) DittoRuntimeException(org.eclipse.ditto.base.model.exceptions.DittoRuntimeException) DittoHeaders(org.eclipse.ditto.base.model.headers.DittoHeaders) Keep(akka.stream.javadsl.Keep) ActorRef(akka.actor.ActorRef) Duration(java.time.Duration) Map(java.util.Map) Patterns(akka.pattern.Patterns) Message(org.eclipse.ditto.messages.model.Message) JsonValue(org.eclipse.ditto.json.JsonValue) PartialFunction(scala.PartialFunction) StreamingConfig(org.eclipse.ditto.gateway.service.util.config.streaming.StreamingConfig) MessageCommand(org.eclipse.ditto.messages.model.signals.commands.MessageCommand) QueryFilterCriteriaFactory(org.eclipse.ditto.rql.query.filter.QueryFilterCriteriaFactory) SupervisedStream(org.eclipse.ditto.gateway.service.streaming.actors.SupervisedStream) Collection(java.util.Collection) Set(java.util.Set) ThingFieldSelector(org.eclipse.ditto.things.model.ThingFieldSelector) UUID(java.util.UUID) ThingId(org.eclipse.ditto.things.model.ThingId) StreamingSession(org.eclipse.ditto.gateway.service.streaming.actors.StreamingSession) Collectors(java.util.stream.Collectors) StandardCharsets(java.nio.charset.StandardCharsets) EventStreamMarshalling(akka.http.javadsl.marshalling.sse.EventStreamMarshalling) List(java.util.List) CompletionStage(java.util.concurrent.CompletionStage) Stream(java.util.stream.Stream) NotUsed(akka.NotUsed) ThingsParameter(org.eclipse.ditto.gateway.service.endpoints.routes.things.ThingsParameter) Accept(akka.http.javadsl.model.headers.Accept) JsonFieldSelector(org.eclipse.ditto.json.JsonFieldSelector) ResourcePlaceholder(org.eclipse.ditto.protocol.placeholders.ResourcePlaceholder) Optional(java.util.Optional) Pattern(java.util.regex.Pattern) SignalEnrichmentFacade(org.eclipse.ditto.internal.models.signalenrichment.SignalEnrichmentFacade) Connect(org.eclipse.ditto.gateway.service.streaming.Connect) SessionedJsonifiable(org.eclipse.ditto.gateway.service.streaming.actors.SessionedJsonifiable) StartStreaming(org.eclipse.ditto.gateway.service.streaming.StartStreaming) Route(akka.http.javadsl.server.Route) Counter(org.eclipse.ditto.internal.utils.metrics.instruments.counter.Counter) Source(akka.stream.javadsl.Source) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) EventSniffer(org.eclipse.ditto.gateway.service.endpoints.utils.EventSniffer) StatusCodes(akka.http.javadsl.model.StatusCodes) OptionalInt(java.util.OptionalInt) Supplier(java.util.function.Supplier) ActorSelection(akka.actor.ActorSelection) GatewaySignalEnrichmentProvider(org.eclipse.ditto.gateway.service.endpoints.utils.GatewaySignalEnrichmentProvider) MessageFormat(java.text.MessageFormat) JsonObject(org.eclipse.ditto.json.JsonObject) PathMatchers(akka.http.javadsl.server.PathMatchers) SignalEnrichmentFailedException(org.eclipse.ditto.base.model.exceptions.SignalEnrichmentFailedException) Charset(java.nio.charset.Charset) UriEncoding(org.eclipse.ditto.base.service.UriEncoding) StreamSupport(java.util.stream.StreamSupport) ThingEvent(org.eclipse.ditto.things.model.signals.events.ThingEvent) JsonPointer(org.eclipse.ditto.json.JsonPointer) Nullable(javax.annotation.Nullable) Thing(org.eclipse.ditto.things.model.Thing) TopicPathPlaceholder(org.eclipse.ditto.protocol.placeholders.TopicPathPlaceholder) JsonSchemaVersion(org.eclipse.ditto.base.model.json.JsonSchemaVersion) HttpHeader(akka.http.javadsl.model.HttpHeader) KillSwitches(akka.stream.KillSwitches) TimePlaceholder(org.eclipse.ditto.placeholders.TimePlaceholder) RouteDirectives(akka.http.javadsl.server.directives.RouteDirectives) MediaTypes(akka.http.javadsl.model.MediaTypes) SearchSource(org.eclipse.ditto.internal.utils.search.SearchSource) Collections(java.util.Collections) NotThreadSafe(javax.annotation.concurrent.NotThreadSafe) KillSwitch(akka.stream.KillSwitch) ActorRef(akka.actor.ActorRef) SignalEnrichmentFacade(org.eclipse.ditto.internal.models.signalenrichment.SignalEnrichmentFacade) Connect(org.eclipse.ditto.gateway.service.streaming.Connect) ServerSentEvent(akka.http.javadsl.model.sse.ServerSentEvent) SupervisedStream(org.eclipse.ditto.gateway.service.streaming.actors.SupervisedStream) ThingFieldSelector(org.eclipse.ditto.things.model.ThingFieldSelector) SessionedJsonifiable(org.eclipse.ditto.gateway.service.streaming.actors.SessionedJsonifiable) ThingId(org.eclipse.ditto.things.model.ThingId) Nullable(javax.annotation.Nullable)

Example 2 with ServerSentEvent

use of akka.http.javadsl.model.sse.ServerSentEvent in project ditto by eclipse.

the class ThingsSseRouteBuilder method createMessagesSseRoute.

private Route createMessagesSseRoute(final RequestContext ctx, final CompletionStage<DittoHeaders> dittoHeadersStage, final String thingId, final String messagePath) {
    final List<ThingId> targetThingIds = List.of(ThingId.of(thingId));
    final CompletionStage<SignalEnrichmentFacade> facadeStage = signalEnrichmentProvider == null ? CompletableFuture.completedStage(null) : signalEnrichmentProvider.getFacade(ctx.getRequest());
    final var sseSourceStage = facadeStage.thenCompose(facade -> dittoHeadersStage.thenCompose(dittoHeaders -> sseAuthorizationEnforcer.checkAuthorization(ctx, dittoHeaders).thenApply(unused -> {
        final Source<SessionedJsonifiable, SupervisedStream.WithQueue> publisherSource = SupervisedStream.sourceQueue(10);
        return publisherSource.viaMat(KillSwitches.single(), Keep.both()).mapMaterializedValue(pair -> {
            final SupervisedStream.WithQueue withQueue = pair.first();
            final KillSwitch killSwitch = pair.second();
            final String connectionCorrelationId = dittoHeaders.getCorrelationId().orElseThrow(() -> new IllegalStateException("Expected correlation-id in SSE DittoHeaders: " + dittoHeaders));
            final var jsonSchemaVersion = dittoHeaders.getSchemaVersion().orElse(JsonSchemaVersion.LATEST);
            sseConnectionSupervisor.supervise(withQueue.getSupervisedStream(), connectionCorrelationId, dittoHeaders);
            final var authorizationContext = dittoHeaders.getAuthorizationContext();
            final var connect = new Connect(withQueue.getSourceQueue(), connectionCorrelationId, STREAMING_TYPE_SSE, jsonSchemaVersion, null, Set.of(), authorizationContext);
            final String resourcePathRqlStatement;
            if (INBOX_OUTBOX_WITH_SUBJECT_PATTERN.matcher(messagePath).matches()) {
                resourcePathRqlStatement = String.format("eq(resource:path,'%s')", messagePath);
            } else {
                resourcePathRqlStatement = String.format("like(resource:path,'%s*')", messagePath);
            }
            final var startStreaming = StartStreaming.getBuilder(StreamingType.MESSAGES, connectionCorrelationId, authorizationContext).withFilter(MessageFormat.format("and(" + "eq(entity:id,''{0}'')," + "{1}" + ")", thingId, resourcePathRqlStatement)).build();
            Patterns.ask(streamingActor, connect, LOCAL_ASK_TIMEOUT).thenApply(ActorRef.class::cast).thenAccept(streamingSessionActor -> streamingSessionActor.tell(startStreaming, ActorRef.noSender())).exceptionally(e -> {
                killSwitch.abort(e);
                return null;
            });
            return NotUsed.getInstance();
        }).filter(jsonifiable -> jsonifiable.getJsonifiable() instanceof MessageCommand).mapAsync(streamingConfig.getParallelism(), jsonifiable -> postprocessMessages(targetThingIds, (MessageCommand<?, ?>) jsonifiable.getJsonifiable(), facade, jsonifiable)).mapConcat(messages -> messages).map(message -> {
            THINGS_SSE_COUNTER.increment();
            final Optional<Charset> charset = determineCharsetFromContentType(message.getContentType());
            final String data = message.getRawPayload().map(body -> new String(body.array(), charset.orElse(StandardCharsets.UTF_8))).orElse("");
            return ServerSentEvent.create(data, message.getSubject());
        }).log("SSE " + PATH_THINGS + "/" + messagePath).keepAlive(Duration.ofSeconds(1), ServerSentEvent::heartbeat);
    })));
    return completeOKWithFuture(sseSourceStage, EventStreamMarshalling.toEventStream());
}
Also used : PFBuilder(akka.japi.pf.PFBuilder) Arrays(java.util.Arrays) StreamingType(org.eclipse.ditto.internal.utils.pubsub.StreamingType) AbstractRoute(org.eclipse.ditto.gateway.service.endpoints.routes.AbstractRoute) ConditionChecker.checkNotNull(org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull) RequestContext(akka.http.javadsl.server.RequestContext) DittoMetrics(org.eclipse.ditto.internal.utils.metrics.DittoMetrics) RqlPredicateParser(org.eclipse.ditto.rql.parser.RqlPredicateParser) KillSwitch(akka.stream.KillSwitch) ServerSentEvent(akka.http.javadsl.model.sse.ServerSentEvent) DittoRuntimeException(org.eclipse.ditto.base.model.exceptions.DittoRuntimeException) DittoHeaders(org.eclipse.ditto.base.model.headers.DittoHeaders) Keep(akka.stream.javadsl.Keep) ActorRef(akka.actor.ActorRef) Duration(java.time.Duration) Map(java.util.Map) Patterns(akka.pattern.Patterns) Message(org.eclipse.ditto.messages.model.Message) JsonValue(org.eclipse.ditto.json.JsonValue) PartialFunction(scala.PartialFunction) StreamingConfig(org.eclipse.ditto.gateway.service.util.config.streaming.StreamingConfig) MessageCommand(org.eclipse.ditto.messages.model.signals.commands.MessageCommand) QueryFilterCriteriaFactory(org.eclipse.ditto.rql.query.filter.QueryFilterCriteriaFactory) SupervisedStream(org.eclipse.ditto.gateway.service.streaming.actors.SupervisedStream) Collection(java.util.Collection) Set(java.util.Set) ThingFieldSelector(org.eclipse.ditto.things.model.ThingFieldSelector) UUID(java.util.UUID) ThingId(org.eclipse.ditto.things.model.ThingId) StreamingSession(org.eclipse.ditto.gateway.service.streaming.actors.StreamingSession) Collectors(java.util.stream.Collectors) StandardCharsets(java.nio.charset.StandardCharsets) EventStreamMarshalling(akka.http.javadsl.marshalling.sse.EventStreamMarshalling) List(java.util.List) CompletionStage(java.util.concurrent.CompletionStage) Stream(java.util.stream.Stream) NotUsed(akka.NotUsed) ThingsParameter(org.eclipse.ditto.gateway.service.endpoints.routes.things.ThingsParameter) Accept(akka.http.javadsl.model.headers.Accept) JsonFieldSelector(org.eclipse.ditto.json.JsonFieldSelector) ResourcePlaceholder(org.eclipse.ditto.protocol.placeholders.ResourcePlaceholder) Optional(java.util.Optional) Pattern(java.util.regex.Pattern) SignalEnrichmentFacade(org.eclipse.ditto.internal.models.signalenrichment.SignalEnrichmentFacade) Connect(org.eclipse.ditto.gateway.service.streaming.Connect) SessionedJsonifiable(org.eclipse.ditto.gateway.service.streaming.actors.SessionedJsonifiable) StartStreaming(org.eclipse.ditto.gateway.service.streaming.StartStreaming) Route(akka.http.javadsl.server.Route) Counter(org.eclipse.ditto.internal.utils.metrics.instruments.counter.Counter) Source(akka.stream.javadsl.Source) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) EventSniffer(org.eclipse.ditto.gateway.service.endpoints.utils.EventSniffer) StatusCodes(akka.http.javadsl.model.StatusCodes) OptionalInt(java.util.OptionalInt) Supplier(java.util.function.Supplier) ActorSelection(akka.actor.ActorSelection) GatewaySignalEnrichmentProvider(org.eclipse.ditto.gateway.service.endpoints.utils.GatewaySignalEnrichmentProvider) MessageFormat(java.text.MessageFormat) JsonObject(org.eclipse.ditto.json.JsonObject) PathMatchers(akka.http.javadsl.server.PathMatchers) SignalEnrichmentFailedException(org.eclipse.ditto.base.model.exceptions.SignalEnrichmentFailedException) Charset(java.nio.charset.Charset) UriEncoding(org.eclipse.ditto.base.service.UriEncoding) StreamSupport(java.util.stream.StreamSupport) ThingEvent(org.eclipse.ditto.things.model.signals.events.ThingEvent) JsonPointer(org.eclipse.ditto.json.JsonPointer) Nullable(javax.annotation.Nullable) Thing(org.eclipse.ditto.things.model.Thing) TopicPathPlaceholder(org.eclipse.ditto.protocol.placeholders.TopicPathPlaceholder) JsonSchemaVersion(org.eclipse.ditto.base.model.json.JsonSchemaVersion) HttpHeader(akka.http.javadsl.model.HttpHeader) KillSwitches(akka.stream.KillSwitches) TimePlaceholder(org.eclipse.ditto.placeholders.TimePlaceholder) RouteDirectives(akka.http.javadsl.server.directives.RouteDirectives) MediaTypes(akka.http.javadsl.model.MediaTypes) SearchSource(org.eclipse.ditto.internal.utils.search.SearchSource) Collections(java.util.Collections) NotThreadSafe(javax.annotation.concurrent.NotThreadSafe) KillSwitch(akka.stream.KillSwitch) ActorRef(akka.actor.ActorRef) MessageCommand(org.eclipse.ditto.messages.model.signals.commands.MessageCommand) SignalEnrichmentFacade(org.eclipse.ditto.internal.models.signalenrichment.SignalEnrichmentFacade) Connect(org.eclipse.ditto.gateway.service.streaming.Connect) ServerSentEvent(akka.http.javadsl.model.sse.ServerSentEvent) Charset(java.nio.charset.Charset) SupervisedStream(org.eclipse.ditto.gateway.service.streaming.actors.SupervisedStream) SessionedJsonifiable(org.eclipse.ditto.gateway.service.streaming.actors.SessionedJsonifiable) ThingId(org.eclipse.ditto.things.model.ThingId)

Aggregations

NotUsed (akka.NotUsed)2 ActorRef (akka.actor.ActorRef)2 ActorSelection (akka.actor.ActorSelection)2 EventStreamMarshalling (akka.http.javadsl.marshalling.sse.EventStreamMarshalling)2 HttpHeader (akka.http.javadsl.model.HttpHeader)2 MediaTypes (akka.http.javadsl.model.MediaTypes)2 StatusCodes (akka.http.javadsl.model.StatusCodes)2 Accept (akka.http.javadsl.model.headers.Accept)2 ServerSentEvent (akka.http.javadsl.model.sse.ServerSentEvent)2 PathMatchers (akka.http.javadsl.server.PathMatchers)2 RequestContext (akka.http.javadsl.server.RequestContext)2 Route (akka.http.javadsl.server.Route)2 RouteDirectives (akka.http.javadsl.server.directives.RouteDirectives)2 PFBuilder (akka.japi.pf.PFBuilder)2 Patterns (akka.pattern.Patterns)2 KillSwitch (akka.stream.KillSwitch)2 KillSwitches (akka.stream.KillSwitches)2 Keep (akka.stream.javadsl.Keep)2 Source (akka.stream.javadsl.Source)2 Charset (java.nio.charset.Charset)2