com.spotify.trickle.PreparedGraph.java Source code

Java tutorial

Introduction

Here is the source code for com.spotify.trickle.PreparedGraph.java

Source

/*
 * Copyright 2013-2014 Spotify AB. All rights reserved.
 *
 * The contents of this file are 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.
 */

package com.spotify.trickle;

import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.Uninterruptibles;

import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.builder;
import static com.google.common.util.concurrent.Futures.allAsList;
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;

/**
 * A decorator class for Graph that holds bound values for input names thus making
 * graph runnable.
 *
 * This class is immutable and thread safe. Calls to any of the bind methods will
 * return a new instance containing that binding.
 *
 * @param <R>  The return type of the graph
 */
final class PreparedGraph<R> extends Graph<R> {

    private final GraphBuilder<R> graph;
    private final ImmutableMap<Input<?>, Object> inputBindings;
    private final boolean debug;

    private PreparedGraph(GraphBuilder<R> graph, ImmutableMap<Input<?>, Object> inputBindings, boolean debug) {
        this.graph = checkNotNull(graph, "graph");
        this.inputBindings = checkNotNull(inputBindings, "inputBindings");
        this.debug = debug;
    }

    PreparedGraph(GraphBuilder<R> graph, boolean debug) {
        this(graph, ImmutableMap.<Input<?>, Object>of(), debug);
    }

    @Override
    public <P> Graph<R> bind(Input<P> input, P value) {
        return addToInputs(input, value);
    }

    @Override
    public <P> Graph<R> bind(Input<P> input, ListenableFuture<P> inputFuture) {
        return addToInputs(input, inputFuture);
    }

    @Override
    public Graph<R> debug(boolean debug) {
        return new PreparedGraph<R>(graph, inputBindings, debug);
    }

    @Override
    public ListenableFuture<R> run() {
        return run(sameThreadExecutor());
    }

    @Override
    public ListenableFuture<R> run(Executor executor) {
        return run(TraverseState.empty(executor, debug));
    }

    @Override
    ListenableFuture<R> run(TraverseState state) {
        state.addBindings(inputBindings);
        return future(state);
    }

    private ListenableFuture<R> future(final TraverseState state) {
        final ImmutableList.Builder<ListenableFuture<?>> futuresListBuilder = builder();

        // get node and value dependencies
        for (Dep<?> input : graph.getInputs()) {
            final ListenableFuture<?> inputFuture = input.getFuture(state);
            futuresListBuilder.add(inputFuture);
        }

        final ImmutableList<ListenableFuture<?>> futures = futuresListBuilder.build();

        final TraverseState.FutureCallInformation currentCall = state.record(this, futures);

        // future for signaling propagation - needs to include predecessors, too
        List<ListenableFuture<?>> mustHappenBefore = Lists.newArrayList(futures);
        for (Graph<?> predecessor : graph.getPredecessors()) {
            mustHappenBefore.add(state.futureForGraph(predecessor));
        }

        final ListenableFuture<List<Object>> allFuture = allAsList(mustHappenBefore);

        checkArgument(graph.getInputs().size() == futures.size(), "sanity check result: insane");

        return Futures.withFallback(nodeFuture(futures, allFuture, state.getExecutor()),
                new NodeExecutionFallback<R>(graph, currentCall, state));
    }

    private ListenableFuture<R> nodeFuture(final ImmutableList<ListenableFuture<?>> values,
            final ListenableFuture<List<Object>> doneSignal, final Executor executor) {
        return Futures.transform(doneSignal, new AsyncFunction<List<Object>, R>() {
            @Override
            public ListenableFuture<R> apply(List<Object> input) {
                // the input future is not going to be null unless there's a Trickle bug, so we should
                // be fine with an NPE in that case
                //noinspection NullableProblems
                return graph.getNode().run(Lists.transform(values, new Function<ListenableFuture<?>, Object>() {
                    @Override
                    public Object apply(ListenableFuture<?> input) {
                        return inputValueFromFuture(input);
                    }
                }));
            }
        }, executor);
    }

    private Object inputValueFromFuture(ListenableFuture<?> input) {
        try {
            return Uninterruptibles.getUninterruptibly(input);
        } catch (ExecutionException e) {
            Throwables.propagateIfInstanceOf(e.getCause(), GraphExecutionException.class);
            throw Throwables.propagate(e);
        }
    }

    private PreparedGraph<R> addToInputs(Input<?> input, Object value) {
        checkState(!inputBindings.containsKey(input), "Duplicate binding for input: " + input);

        return new PreparedGraph<R>(graph,
                ImmutableMap.<Input<?>, Object>builder().putAll(inputBindings).put(input, value).build(), debug);
    }

    @Override
    public String name() {
        return graph.name();
    }

    @Override
    public List<? extends NodeInfo> arguments() {
        return graph.arguments();
    }

    @Override
    public Iterable<? extends NodeInfo> predecessors() {
        return graph.predecessors();
    }

    @Override
    public Type type() {
        return graph.type();
    }

    @Override
    public String toString() {
        return graph.name();
    }

}