com.ibm.streamsx.topology.internal.core.PlacementInfo.java Source code

Java tutorial

Introduction

Here is the source code for com.ibm.streamsx.topology.internal.core.PlacementInfo.java

Source

/*
# Licensed Materials - Property of IBM
# Copyright IBM Corp. 2015  
*/
package com.ibm.streamsx.topology.internal.core;

import static com.ibm.streamsx.topology.builder.BVirtualMarker.ISOLATE;
import static com.ibm.streamsx.topology.generator.operator.OpProperties.CONFIG;
import static com.ibm.streamsx.topology.generator.operator.OpProperties.PLACEMENT;
import static com.ibm.streamsx.topology.generator.operator.OpProperties.PLACEMENT_EXPLICIT_COLOCATE_ID;
import static com.ibm.streamsx.topology.generator.operator.OpProperties.PLACEMENT_RESOURCE_TAGS;
import static com.ibm.streamsx.topology.internal.gson.GsonUtilities.array;
import static com.ibm.streamsx.topology.internal.gson.GsonUtilities.jstring;
import static com.ibm.streamsx.topology.internal.gson.GsonUtilities.object;
import static com.ibm.streamsx.topology.internal.gson.GsonUtilities.objectCreate;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.ibm.streamsx.topology.builder.BOperatorInvocation;
import com.ibm.streamsx.topology.context.Placeable;
import com.ibm.streamsx.topology.generator.spl.GraphUtilities;
import com.ibm.streamsx.topology.internal.gson.GsonUtilities;

/**
 * Manages fusing of Placeables. 
 */
class PlacementInfo {

    private static final AtomicLong nextFuseId = new AtomicLong();

    private static boolean hasPlacement(Placeable<?> element) {
        return object(element.operator()._json(), CONFIG, PLACEMENT) != null;
    }

    private static JsonObject placement(Placeable<?> element) {
        return objectCreate(element.operator()._json(), CONFIG, PLACEMENT);
    }

    /**
     * Fuse a number of placeables.
     * If fusing occurs then the fusing id
     * is set as explicitColocate in the placement JSON object in
     * the operator's config.
     * @throws IllegalArgumentException if Placeables are from different
     *          topologies or if Placeable.isPlaceable()==false.
     */

    static boolean colocate(Placeable<?> first, Placeable<?>... toFuse) {

        Set<Placeable<?>> elements = new HashSet<>();
        elements.add(first);
        elements.addAll(Arrays.asList(toFuse));

        // check high level constraints
        for (Placeable<?> element : elements) {
            if (!element.isPlaceable())
                throw new IllegalArgumentException("Placeable.isPlaceable()==false");

            if (!first.topology().equals(element.topology()))
                throw new IllegalArgumentException("Different topologies: " + first.topology().getName() + " and "
                        + element.topology().getName());
        }

        if (elements.size() < 2)
            return false;

        disallowColocateInLowLatency(elements);
        disallowColocateIsolatedOpWithParent(first, toFuse);

        String fusingId = null;
        for (Placeable<?> element : elements) {
            JsonObject placement = placement(element);
            fusingId = jstring(placement, PLACEMENT_EXPLICIT_COLOCATE_ID);
            if (fusingId != null) {
                break;
            }
        }
        if (fusingId == null) {
            fusingId = "__jaa_colocate" + nextFuseId.incrementAndGet();
        }

        Set<String> fusedResourceTags = new HashSet<>();

        // Determine the union of all resource tags for the colocated operators
        for (Placeable<?> element : elements) {
            JsonObject placement = placement(element);

            if (placement.has(PLACEMENT_RESOURCE_TAGS)) {
                addToSet(fusedResourceTags, array(placement, PLACEMENT_RESOURCE_TAGS));
            }
        }

        JsonArray fusedResourceTagsJson = setToArray(fusedResourceTags);

        // And finally update all the JSON info
        for (Placeable<?> element : elements) {
            JsonObject placement = placement(element);
            placement.addProperty(PLACEMENT_EXPLICIT_COLOCATE_ID, fusingId);
            placement.add(PLACEMENT_RESOURCE_TAGS, fusedResourceTagsJson);
        }

        return true;
    }

    private static void addToSet(Set<String> set, JsonArray array) {
        for (JsonElement item : array)
            set.add(item.getAsString());
    }

    private static JsonArray setToArray(Set<String> set) {
        JsonArray array = new JsonArray();
        for (String item : set)
            if (!item.isEmpty())
                array.add(new JsonPrimitive(item));
        return array;
    }

    /** throw if s1.isolate().filter().colocate(s1) */
    private static void disallowColocateIsolatedOpWithParent(Placeable<?> first, Placeable<?>... toFuse) {
        JsonObject graph = first.builder()._complete();
        JsonObject colocateOp = first.operator()._complete();
        Set<JsonObject> parents = GraphUtilities.getUpstream(colocateOp, graph);
        if (!parents.isEmpty()) {
            JsonObject isolate = parents.iterator().next();
            String kind = jstring(isolate, "kind");
            if (!ISOLATE.kind().equals(kind))
                return;
            parents = GraphUtilities.getUpstream(isolate, graph);
            if (parents.isEmpty())
                return;
            JsonObject isolateParentOp = parents.iterator().next();
            for (Placeable<?> placeable : toFuse) {
                JsonObject tgtOp = placeable.operator()._complete();
                if (jstring(tgtOp, "name").equals(jstring(isolateParentOp, "name")))
                    throw new IllegalStateException("Illegal to colocate an isolated stream with its parent.");
            }
        }
    }

    // A short term concession to the fact that colocate()
    // and low latency regions aren't playing well together
    // i.e., the low latency guarantee is being violated.
    // So disallow that configuration for now.
    private static void disallowColocateInLowLatency(Set<Placeable<?>> elements) {
        for (Placeable<?> element : elements) {
            BOperatorInvocation op = element.operator();
            if (element.builder().isInLowLatencyRegion(op))
                throw new IllegalStateException("colocate() is not allowed in a low latency region");
        }
    }

    static Set<String> getResourceTags(Placeable<?> element) {

        if (!hasPlacement(element))
            return Collections.emptySet();

        JsonObject placement = placement(element);
        if (!placement.has(PLACEMENT_RESOURCE_TAGS))
            return Collections.emptySet();

        Set<String> elementResourceTags = new HashSet<>();
        addToSet(elementResourceTags, array(placement, PLACEMENT_RESOURCE_TAGS));
        return Collections.unmodifiableSet(elementResourceTags);
    }

    static void addResourceTags(Placeable<?> element, String... tags) {
        if (Objects.requireNonNull(tags).length == 0)
            return;

        Set<String> elementResourceTags = new HashSet<>();
        elementResourceTags.addAll(Arrays.asList(tags));

        JsonObject placement = placement(element);

        if (placement.has(PLACEMENT_RESOURCE_TAGS))
            addToSet(elementResourceTags, array(placement, PLACEMENT_RESOURCE_TAGS));

        if (placement.has(PLACEMENT_EXPLICIT_COLOCATE_ID))
            findAllColocatedResourceTags(element, jstring(placement, PLACEMENT_EXPLICIT_COLOCATE_ID),
                    elementResourceTags);

        JsonArray resourceTags = setToArray(elementResourceTags);
        placement.add(PLACEMENT_RESOURCE_TAGS, resourceTags);

        if (placement.has(PLACEMENT_EXPLICIT_COLOCATE_ID))
            updateAllColocatedResourceTags(element, jstring(placement, PLACEMENT_EXPLICIT_COLOCATE_ID),
                    resourceTags);
    }

    private static void findAllColocatedResourceTags(Placeable<?> element, String colocateId,
            Set<String> elementResourceTags) {
        JsonObject graph = element.builder()._complete();

        GsonUtilities.objectArray(graph, "operators", op -> {
            JsonObject placement = object(op, CONFIG, PLACEMENT);
            if (placement != null) {
                if (colocateId.equals(jstring(placement, PLACEMENT_EXPLICIT_COLOCATE_ID))) {
                    if (placement.has(PLACEMENT_RESOURCE_TAGS))
                        addToSet(elementResourceTags, array(placement, PLACEMENT_RESOURCE_TAGS));
                }
            }

        });
    }

    private static void updateAllColocatedResourceTags(Placeable<?> element, String colocateId,
            JsonArray resourceTags) {
        JsonObject graph = element.builder()._complete();

        GsonUtilities.objectArray(graph, "operators", op -> {
            JsonObject placement = object(op, CONFIG, PLACEMENT);
            if (placement != null) {
                if (colocateId.equals(jstring(placement, PLACEMENT_EXPLICIT_COLOCATE_ID))) {
                    placement.add(PLACEMENT_RESOURCE_TAGS, resourceTags);
                }
            }

        });
    }
}