org.streamingpool.ext.tensorics.streamfactory.DetailedTensoricsExpressionStreamFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.streamingpool.ext.tensorics.streamfactory.DetailedTensoricsExpressionStreamFactory.java

Source

// @formatter:off
/**
*
* This file is part of streaming pool (http://www.streamingpool.org).
*
* Copyright (c) 2017-present, CERN. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// @formatter:on

package org.streamingpool.ext.tensorics.streamfactory;

import static io.reactivex.Flowable.fromPublisher;
import static io.reactivex.Flowable.zip;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.streamingpool.core.domain.ErrorStreamPair;
import org.streamingpool.core.service.DiscoveryService;
import org.streamingpool.core.service.StreamFactory;
import org.streamingpool.core.service.StreamId;
import org.streamingpool.core.service.streamid.OverlapBufferStreamId;
import org.streamingpool.ext.tensorics.evaluation.BufferedEvaluation;
import org.streamingpool.ext.tensorics.evaluation.ContinuousEvaluation;
import org.streamingpool.ext.tensorics.evaluation.EvaluationStrategy;
import org.streamingpool.ext.tensorics.evaluation.TriggeredEvaluation;
import org.streamingpool.ext.tensorics.exception.NoBufferedStreamSpecifiedException;
import org.streamingpool.ext.tensorics.expression.UnresolvedStreamIdBasedExpression;
import org.streamingpool.ext.tensorics.streamid.DetailedExpressionStreamId;
import org.tensorics.core.expressions.Placeholder;
import org.tensorics.core.resolve.domain.DetailedExpressionResult;
import org.tensorics.core.resolve.engine.ResolvedContextDidNotGrowException;
import org.tensorics.core.resolve.engine.ResolvingEngine;
import org.tensorics.core.resolve.options.HandleWithFirstCapableAncestorStrategy;
import org.tensorics.core.tree.domain.Contexts;
import org.tensorics.core.tree.domain.EditableResolvingContext;
import org.tensorics.core.tree.domain.Expression;
import org.tensorics.core.tree.domain.ResolvingContext;
import org.tensorics.core.tree.walking.Trees;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;

import io.reactivex.Flowable;
import io.reactivex.functions.Function;

/**
 * {@link StreamFactory} that creates {@link DetailedExpressionStreamId}s with {@link ContinuousEvaluation} and
 * {@link TriggeredEvaluation} as {@link EvaluationStrategy}.
 *
 * @author acalia
 */
public class DetailedTensoricsExpressionStreamFactory implements StreamFactory {

    private static final Logger LOGGER = LoggerFactory.getLogger(DetailedTensoricsExpressionStreamFactory.class);

    private static final HandleWithFirstCapableAncestorStrategy EXCEPTION_HANDLING_STRATEGY = new HandleWithFirstCapableAncestorStrategy();

    private static final Function<Object[], Boolean> TRIGGER_CONTEXT_COMBINER = (
            Object... entriesToCombine) -> true;

    private static final Function<Object[], ResolvingContext> CONTEXT_COMBINER = (Object... entriesToCombine) -> {
        EditableResolvingContext context = Contexts.newResolvingContext();
        for (Object entry : entriesToCombine) {
            if (entry instanceof ExpToValue) {
                ExpToValue castedEntry = (ExpToValue) entry;
                context.put(castedEntry.node, castedEntry.value);
            }
        }

        return context;
    };

    private final ResolvingEngine engine;

    public DetailedTensoricsExpressionStreamFactory(ResolvingEngine engine) {
        this.engine = engine;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> ErrorStreamPair<T> create(StreamId<T> id, DiscoveryService discoveryService) {
        if (!(id instanceof DetailedExpressionStreamId)) {
            return ErrorStreamPair.empty();
        }
        DetailedExpressionStreamId<?, ?> tensoricsId = (DetailedExpressionStreamId<?, ?>) id;

        /* NOTE!!!!!! This class does not creates buffered ids anymore, use BufferedTensoricsExpressionStreamFactory */
        if (tensoricsId.initialContext()
                .resolvedValueOf(Placeholder.ofClass(EvaluationStrategy.class)) instanceof BufferedEvaluation) {
            return ErrorStreamPair.empty();
        }

        return ErrorStreamPair.ofData((Flowable<T>) resolvedStream(tensoricsId, discoveryService));
    }

    private <T, E extends Expression<T>> Flowable<DetailedExpressionResult<T, E>> resolvedStream(
            DetailedExpressionStreamId<T, E> id, DiscoveryService discoveryService) {
        E expression = id.expression();
        ResolvingContext initialCtx = id.initialContext();

        Map<Expression<Object>, StreamId<Object>> streamIds = streamIdsFrom(id);
        ImmutableMultimap.Builder<StreamId<?>, Flowable<ExpToValue>> builder = ImmutableMultimap.builder();
        for (Entry<Expression<Object>, StreamId<Object>> entry : streamIds.entrySet()) {
            Flowable<?> plainObservable = fromPublisher(discoveryService.discover(entry.getValue()));
            Flowable<ExpToValue> mappedObservable;
            mappedObservable = plainObservable.map(obj -> new ExpToValue(entry.getKey(), obj));
            builder.put(entry.getValue(), mappedObservable);
        }
        ImmutableMultimap<StreamId<?>, Flowable<ExpToValue>> observableEntries = builder.build();

        EvaluationStrategy evaluationStrategy = initialCtx
                .resolvedValueOf(Placeholder.ofClass(EvaluationStrategy.class));

        Flowable<?> triggerObservable = triggerObservable(observableEntries, evaluationStrategy, discoveryService);
        return triggerObservable
                .withLatestFrom(observableEntries.values().toArray(new Flowable[] {}), CONTEXT_COMBINER)
                .map(ctx -> {
                    EditableResolvingContext fullContext = Contexts.newResolvingContext();
                    fullContext.putAllNew(ctx);
                    fullContext.putAllNew(initialCtx);
                    return engine.resolveDetailed(expression, fullContext, EXCEPTION_HANDLING_STRATEGY);
                });
    }

    /* package visibility for testing */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    @VisibleForTesting
    <T extends Expression<?>> Map<Expression<Object>, StreamId<Object>> streamIdsFrom(
            DetailedExpressionStreamId<?, T> id) {
        Expression<?> rootExpression = id.expression();
        ResolvingContext initialCtx = id.initialContext();

        Collection<UnresolvedStreamIdBasedExpression> unresolvedStreamIdExpressions = Trees
                .findNodesOfClass(rootExpression, UnresolvedStreamIdBasedExpression.class);

        Builder<Expression<Object>, StreamId<Object>> mapBuilder = ImmutableMap.builder();
        for (UnresolvedStreamIdBasedExpression<Object> unresolvedStreamIdExpression : unresolvedStreamIdExpressions) {
            try {
                Expression<StreamId<Object>> streamIdExpression = unresolvedStreamIdExpression.streamIdExpression();
                StreamId<Object> streamId = engine.resolve(streamIdExpression, initialCtx);
                mapBuilder.put(unresolvedStreamIdExpression, streamId);
            } catch (ResolvedContextDidNotGrowException ex) {
                throw new RuntimeException(format(
                        "Context did not grow while resolving the StreamId of expression. "
                                + "This is most probably because the initial context (%s) did not contain the value of the current UnresolvedStreamIdBasedExpression (%s).",
                        initialCtx, unresolvedStreamIdExpression), ex);
            }
        }
        return mapBuilder.build();
    }

    private static Flowable<?> triggerObservable(Multimap<StreamId<?>, ? extends Flowable<?>> flowables,
            EvaluationStrategy strategy, DiscoveryService discoveryService) {
        if (strategy instanceof ContinuousEvaluation) {
            Collection<? extends Flowable<?>> streams = flowables.values();
            if (streams.isEmpty()) {
                LOGGER.warn("The expression does not contain any streams. "
                        + "Therefore it will never emit! This rarely might be what you want ;-)");
            }
            return Flowable.combineLatest(streams, TRIGGER_CONTEXT_COMBINER);
        }
        if (strategy instanceof BufferedEvaluation) {
            List<? extends Flowable<?>> triggeringObservables = flowables.entries().stream()
                    .filter(e -> (e.getKey() instanceof OverlapBufferStreamId)).map(Entry::getValue)
                    .collect(toList());
            if (triggeringObservables.isEmpty()) {
                throw new NoBufferedStreamSpecifiedException();
            }
            return zip(triggeringObservables, ImmutableSet::of);
        }
        if (strategy instanceof TriggeredEvaluation) {
            return fromPublisher(discoveryService.discover(((TriggeredEvaluation) strategy).triggeringStreamId()));
        }
        throw new IllegalArgumentException(
                "Unknown evaluationStrategy '" + strategy + "'. Cannot create trigger Observable.");
    }

    private static final class ExpToValue {

        public ExpToValue(Expression<Object> node, Object value) {
            super();
            this.node = node;
            this.value = value;
        }

        private final Expression<Object> node;
        private final Object value;

        @Override
        public String toString() {
            return "ExpToValue [node=" + node + ", value=" + value + "]";
        }

    }

}