All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.smallrye.reactive.messaging.providers.wiring.Graph Maven / Gradle / Ivy

package io.smallrye.reactive.messaging.providers.wiring;

import static io.smallrye.reactive.messaging.providers.i18n.ProviderLogging.log;

import java.util.*;
import java.util.stream.Collectors;

import io.smallrye.reactive.messaging.ChannelRegistry;
import io.smallrye.reactive.messaging.providers.i18n.ProviderLogging;

public class Graph {

    private final Set resolved;
    private final Set unresolved;
    private final boolean isClosed;
    private final Set inbound;
    private final Set outbound;
    private final List errors = new ArrayList<>();

    public Graph(boolean strict, Set resolved, Set unresolved) {
        this.resolved = resolved;
        this.unresolved = unresolved;
        this.isClosed = computeClosure();

        if (strict) {
            log.strictModeEnabled();
        }
        if (strict && !isClosed) {
            errors.add(OpenGraphException.openGraphException(this.resolved, unresolved));
        }

        detectCycles();

        if (!strict && !isClosed) {
            StringBuffer message = new StringBuffer(
                    "Some components are not connected to either downstream consumers or upstream producers:\n");
            this.resolved.stream().filter(component -> !component.isDownstreamResolved())
                    .forEach(c -> message.append("\t- ").append(c).append(" has no downstream\n"));
            this.unresolved.stream().filter(component -> !component.isDownstreamResolved())
                    .forEach(c -> message.append("\t- ").append(c).append(" has no downstream\n"));
            this.unresolved.stream()
                    .filter(c -> !c.isUpstreamResolved())
                    .forEach(c -> {
                        if (c.upstreams().isEmpty()) {
                            message.append("\t- ").append(c).append(" has no upstream\n");
                        } else {
                            message.append("\t- ").append(c).append(" does not have all its requested upstreams: ")
                                    .append(c.upstreams()).append("\n");
                        }
                    });
            ProviderLogging.log.reportWiringFailures(message.toString());
        }

        this.inbound = this.resolved.stream()
                .filter(c -> c.isUpstreamResolved() && c.upstreams().isEmpty() && isDownstreamFullyResolved(c))
                .map(c -> (Wiring.PublishingComponent) c)
                .collect(Collectors.toSet());
        // Verify that the full chain is ok
        this.outbound = this.resolved.stream()
                .filter(c -> c.isDownstreamResolved() && c.downstreams().isEmpty() && isUpstreamFullyResolved(c))
                .map(c -> (Wiring.ConsumingComponent) c)
                .collect(Collectors.toSet());

        // Validate
        for (Wiring.Component component : this.resolved) {
            try {
                component.validate();
            } catch (WiringException e) {
                errors.add(e);
            }
        }
    }

    public List getWiringErrors() {
        return Collections.unmodifiableList(errors);
    }

    public void materialize(ChannelRegistry registry) {
        log.startMaterialization();
        long begin = System.nanoTime();
        Set materialized = new HashSet<>();
        List current = new ArrayList<>(inbound);
        while (!current.isEmpty()) {
            List downstreams = new ArrayList<>();
            List toBeMaterialized = new ArrayList<>();
            for (Wiring.Component cmp : current) {
                if (!materialized.contains(cmp) && !toBeMaterialized.contains(cmp)
                        && allUpstreamsMaterialized(cmp, materialized)) {
                    // Sort the component that can be materialized during this iteration
                    // Emitter component first.
                    if (cmp instanceof Wiring.EmitterComponent) {
                        toBeMaterialized.add(0, cmp);
                    } else {
                        toBeMaterialized.add(cmp);
                    }
                }
            }

            toBeMaterialized.forEach(c -> {
                c.materialize(registry);
                downstreams.addAll(c.downstreams());
                materialized.add(c);
            });

            current.removeAll(materialized);
            current.addAll(downstreams);
        }
        long duration = System.nanoTime() - begin;
        log.materializationCompleted(duration);
    }

    private boolean allUpstreamsMaterialized(Wiring.Component cmp, Set materialized) {
        for (Wiring.Component upstream : cmp.upstreams()) {
            if (!materialized.contains(upstream)) {
                return false;
            }
        }
        return true;
    }

    public Set getResolvedComponents() {
        return resolved;
    }

    public boolean isClosed() {
        return isClosed;
    }

    public Set getInbound() {
        return inbound;
    }

    public Set getOutbound() {
        return outbound;
    }

    private boolean isUpstreamFullyResolved(Wiring.Component component) {
        if (!component.isUpstreamResolved()) {
            return false;
        }
        for (Wiring.Component upstream : component.upstreams()) {
            if (!isUpstreamFullyResolved(upstream)) {
                return false;
            }
        }
        return true;
    }

    private boolean isDownstreamFullyResolved(Wiring.Component component) {
        if (!component.isDownstreamResolved()) {
            return false;
        }
        for (Wiring.Component downstream : component.downstreams()) {
            if (!isDownstreamFullyResolved(downstream)) {
                return false;
            }
        }
        return true;
    }

    public Set getUnresolvedComponents() {
        return Collections.unmodifiableSet(unresolved);
    }

    public boolean hasWiringErrors() {
        return !errors.isEmpty();
    }

    private boolean computeClosure() {
        if (!unresolved.isEmpty()) {
            return false;
        }

        for (Wiring.Component component : resolved) {
            if (!component.isUpstreamResolved()) {
                return false;
            }
            // One test from the TCK deploys an unused connector.
            if (!(component instanceof Wiring.InboundConnectorComponent) && !component.isDownstreamResolved()) {
                return false;
            } else if (component instanceof Wiring.InboundConnectorComponent && !component.isDownstreamResolved()) {
                ProviderLogging.log.connectorWithoutDownstream(component);
                return false;
            }
        }

        return true;
    }

    private void detectCycles() throws CycleException {
        for (Wiring.Component component : resolved) {
            Set traces = new HashSet<>();
            detectCycles(traces, component);
        }
    }

    private void detectCycles(Set traces, Wiring.Component component) throws CycleException {
        traces.add(component);
        for (Wiring.Component downstream : component.downstreams()) {
            if (traces.contains(downstream)) {
                throw new CycleException(component, downstream);
            } else {
                traces.add(downstream);
                detectCycles(new HashSet<>(traces), downstream);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy