org.openremote.server.route.SubflowRoute.java Source code

Java tutorial

Introduction

Here is the source code for org.openremote.server.route.SubflowRoute.java

Source

/*
 * Copyright 2015, OpenRemote Inc.
 *
 * See the CONTRIBUTORS.txt file in the distribution for a
 * full listing of individual contributors.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package org.openremote.server.route;

import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.camel.CamelContext;
import org.apache.camel.LoggingLevel;
import org.apache.camel.model.ProcessorDefinition;
import org.openremote.server.catalog.WidgetNodeDescriptor;
import org.openremote.shared.catalog.CatalogCategory;
import org.openremote.shared.flow.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

public class SubflowRoute extends NodeRoute {

    private static final Logger LOG = LoggerFactory.getLogger(SubflowRoute.class);

    public static final String WIDGET_COMPONENT = "or-console-widget-composite";

    public static class Descriptor extends WidgetNodeDescriptor {

        @Override
        public NodeColor getColor() {
            return NodeColor.VIRTUAL;
        }

        @Override
        public CatalogCategory getCatalogCategory() {
            return null;
        }

        @Override
        public String getType() {
            return Node.TYPE_SUBFLOW;
        }

        @Override
        public String getTypeLabel() {
            return Node.TYPE_SUBFLOW_LABEL;
        }

        @Override
        public Node initialize(Node node) {
            node = super.initialize(node);
            node.setClientAccess(true);
            return node;
        }

        @Override
        protected ObjectNode getInitialProperties() {
            return WIDGET_INITIAL_PROPERTIES.deepCopy().put(PROPERTY_COMPONENT, WIDGET_COMPONENT);
        }

        @Override
        public NodeRoute createRoute(CamelContext context, Flow flow, Node node) {
            return new SubflowRoute(context, flow, node);
        }
    }

    private static final String SUBFLOW_CORRELATION_STACK = "SUBFLOW_CORRELATION_STACK";

    public static void pushOntoCorrelationStack(Map<String, Object> headers, String identifier) {
        // Create or get existing stack from headers
        @SuppressWarnings("unchecked")
        Stack<String> subflowCorrelationStack = hasCorrelationStack(headers)
                ? (Stack<String>) headers.get(SUBFLOW_CORRELATION_STACK)
                : new Stack<>();

        LOG.debug("Pushing identifier'" + identifier + "' onto correlation stack: " + subflowCorrelationStack);
        subflowCorrelationStack.push(identifier);
        LOG.debug("Setting correlation stack header: " + subflowCorrelationStack);
        headers.put(SUBFLOW_CORRELATION_STACK, subflowCorrelationStack);
    }

    public static String peekCorrelationStack(Map<String, Object> headers, boolean root, boolean removeHeader) {
        String instanceId = null;
        if (hasCorrelationStack(headers)) {

            @SuppressWarnings("unchecked")
            Stack<String> correlationStack = (Stack<String>) headers.get(SUBFLOW_CORRELATION_STACK);

            if (correlationStack.size() > 0) {
                if (root) {
                    instanceId = correlationStack.get(0);
                    LOG.debug("Got correlation stack root element: " + instanceId);
                } else {
                    instanceId = correlationStack.peek();
                    LOG.debug("Got correlation stack current element: " + instanceId);
                }
            }

            if (removeHeader)
                headers.remove(SUBFLOW_CORRELATION_STACK);
        }
        return instanceId;
    }

    public static Map<String, Object> copyCorrelationStack(Map<String, Object> headers, boolean popStack) {
        Map<String, Object> headersCopy = new HashMap<>(headers);
        if (hasCorrelationStack(headers)) {

            @SuppressWarnings("unchecked")
            Stack<String> correlationStack = (Stack<String>) headers.get(SUBFLOW_CORRELATION_STACK);

            @SuppressWarnings("unchecked")
            Stack<String> correlationStackCopy = (Stack<String>) correlationStack.clone();

            if (correlationStackCopy.size() > 0 && popStack) {
                correlationStackCopy.pop();
            }
            headersCopy.put(SUBFLOW_CORRELATION_STACK, correlationStackCopy);
        }
        return headersCopy;
    }

    public static boolean hasCorrelationStack(Map<String, Object> headers) {
        return headers.containsKey(SUBFLOW_CORRELATION_STACK);
    }

    public SubflowRoute(CamelContext context, Flow flow, Node node) {
        super(context, flow, node);
    }

    @Override
    protected void configureProcessing(ProcessorDefinition routeDefinition) throws Exception {

        routeDefinition.process(exchange -> {
            LOG.debug("Processing exchange for subflow: " + getNode());
            String destinationSinkId = getSlotId(exchange);
            Slot destinationSink = getNode().findSlot(destinationSinkId);
            LOG.debug("Found destination sink: " + destinationSink);
            if (destinationSink.getPeerId() != null) {

                LOG.debug("Found destination peer sink: " + destinationSink.getPeerId());

                pushOntoCorrelationStack(exchange.getIn().getHeaders(), getNode().getId());

                sendExchangeCopy(destinationSink.getPeerId(), exchange, false);

            } else {
                LOG.debug("No peer for destination sink slot, stopping exchange: " + destinationSink);
            }
        }).id(getProcessorId("toSubflowPeer"))
                .log(LoggingLevel.DEBUG, LOG,
                        "<<< Subflow " + getRouteDescription() + " done processing: '${body}'")
                .stop().id(getProcessorId("stopSubflow"));

        LOG.debug("Handling subflow source slots: " + getNode());

        for (Slot sourceSlot : getNode().findSlots(Slot.TYPE_SOURCE)) {

            LOG.debug("Handling subflow source slot: " + sourceSlot);
            if (sourceSlot.getPeerId() == null) {
                LOG.debug("No peer for source slot, not registering any asynchronous consumer: " + sourceSlot);
                continue;
            }

            LOG.debug("Consuming from source peer asynchronous queue: " + sourceSlot.getPeerId());
            from("seda:" + sourceSlot.getPeerId() + "?multipleConsumers=true").routeId(getRouteId(sourceSlot))
                    .process(exchange -> {
                        LOG.debug("Received message from asynchronous queue: " + sourceSlot.getPeerId());

                        String currentInstanceId = peekCorrelationStack(exchange.getIn().getHeaders(), false,
                                false);

                        // If there is no correlation stack in the message, it is for "all" and we take it
                        if (currentInstanceId == null || getNode().getId().equals(currentInstanceId)) {

                            LOG.debug("Correlation found for '" + getNode() + "', finding wires of: " + sourceSlot);

                            Wire[] sourceWires = getFlow().findWiresForSource(sourceSlot.getId());
                            for (Wire sourceWire : sourceWires) {
                                sendExchangeCopy(sourceWire.getSinkId(), exchange, true);
                            }

                        } else {
                            LOG.debug("No correlation found for received message on: " + getNode());
                        }
                    }).id(getProcessorId(sourceSlot, "toSubflowWires"));

        }
    }

}