com.vaushell.superpipes.dispatch.Dispatcher.java Source code

Java tutorial

Introduction

Here is the source code for com.vaushell.superpipes.dispatch.Dispatcher.java

Source

/*
 * Copyright (C) 2013 Fabien Vauchelles (fabien_AT_vauchelles_DOT_com).
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3, 29 June 2007, of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 */

package com.vaushell.superpipes.dispatch;

import com.vaushell.superpipes.nodes.A_Node;
import com.vaushell.superpipes.tools.ThrowableHelper;
import com.vaushell.superpipes.tools.scribe.code.A_ValidatorCode;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Flow dispatcher.
 *
 * @author Fabien Vauchelles (fabien_AT_vauchelles_DOT_com)
 */
public final class Dispatcher {
    // PUBLIC
    public Dispatcher() {
        this.nodes = new HashMap<>();
        this.routes = new HashMap<>();
        this.commonsProperties = new HashMap<>();
        this.datas = null;
        this.vCodeFactory = null;
        this.eMailer = new ErrorMailer();
    }

    public Path getDatas() {
        return datas;
    }

    public A_ValidatorCode.I_Factory getVCodeFactory() {
        return vCodeFactory;
    }

    /**
     * Return common properties set.
     *
     * @param ID Common ID
     * @return Common properties set
     */
    public ConfigProperties getCommon(final String ID) {
        return commonsProperties.get(ID);
    }

    /**
     * Add a node to the flow.
     *
     * @param nodeID Node's ID
     * @param clazz Node's type class
     * @param commons commons properties set reference
     * @return the node
     */
    public A_Node addNode(final String nodeID, final Class<?> clazz, final List<ConfigProperties> commons) {
        return addNode(nodeID, clazz.getName(), commons);
    }

    /**
     * Add a node to the flow.
     *
     * @param nodeID Node's ID
     * @param type Node's type
     * @param commons commons properties set reference
     * @return the node
     */
    public A_Node addNode(final String nodeID, final String type, final List<ConfigProperties> commons) {
        if (nodeID == null || type == null || commons == null) {
            throw new IllegalArgumentException();
        }

        if (nodes.containsKey(nodeID)) {
            throw new IllegalArgumentException("node '" + nodeID + "' already exists");
        }

        if (LOGGER.isTraceEnabled()) {
            LOGGER.trace("[" + getClass().getSimpleName() + "] addNode : nodeID=" + nodeID + " / type=" + type
                    + " / commons.length=" + commons.size());
        }

        try {
            final A_Node node = (A_Node) Class.forName(type).newInstance();
            node.setParameters(nodeID, this, commons);

            nodes.put(nodeID, node);

            return node;
        } catch (final ClassNotFoundException | IllegalAccessException | InstantiationException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Add a route between 2 nodes.
     *
     * @param sourceID source node ID
     * @param destinationID destination node ID
     */
    public void addRoute(final String sourceID, final String destinationID) {
        if (sourceID == null || destinationID == null) {
            throw new IllegalArgumentException();
        }

        if (LOGGER.isTraceEnabled()) {
            LOGGER.trace("[" + getClass().getSimpleName() + "] addRoute : sourceID=" + sourceID
                    + " / destinationID=" + destinationID);
        }

        if (!nodes.containsKey(sourceID)) {
            throw new IllegalArgumentException("Cannot find route source '" + sourceID + "'");
        }

        if (!nodes.containsKey(destinationID)) {
            throw new IllegalArgumentException("Cannot find route destination '" + destinationID + "'");
        }

        Set<String> subRoutes = routes.get(sourceID);
        if (subRoutes == null) {
            subRoutes = new HashSet<>();
            routes.put(sourceID, subRoutes);
        }

        subRoutes.add(destinationID);
    }

    /**
     * Add common configuration.
     *
     * @param ID Common ID
     * @param properties The properties
     */
    public void addCommon(final String ID, final ConfigProperties properties) {
        if (ID == null || properties == null || properties.isEmpty()) {
            throw new IllegalArgumentException();
        }

        if (LOGGER.isTraceEnabled()) {
            LOGGER.trace(
                    "[" + getClass().getSimpleName() + "] addCommon : ID=" + ID + " / properties=" + properties);
        }

        commonsProperties.put(ID, properties);
    }

    /**
     * Post an error.
     *
     * @param th the error
     * @param message the message (could be null)
     */
    public void postError(final Throwable th, final Message message) {
        if (th == null) {
            throw new IllegalArgumentException();
        }

        if (LOGGER.isTraceEnabled()) {
            LOGGER.trace("[" + getClass().getSimpleName() + "] postError : th=" + th + " / message="
                    + Message.formatSimple(message));
        }

        if (message == null) {
            LOGGER.error(ThrowableHelper.formatPlainText(th));

            eMailer.receiveError(ThrowableHelper.formatHTML(th));
        } else {
            LOGGER.error(
                    String.format("%s%n%s", ThrowableHelper.formatPlainText(th), Message.formatPlainText(message)));

            eMailer.receiveError(
                    String.format("%s%n%s%n", ThrowableHelper.formatHTML(th), Message.formatHTML(message)));
        }
    }

    /**
     * Start the dispatcher.
     *
     * @throws Exception
     */
    public void start() throws Exception {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("[" + getClass().getSimpleName() + "] start");
        }

        // Prepare nodes
        for (final A_Node node : nodes.values()) {
            node.prepare();
        }

        // Start error mailer
        eMailer.start();

        // Start nodes
        for (final A_Node node : nodes.values()) {
            node.start();
        }
    }

    /**
     * Stop the dispatcher and wait all nodes to stop.
     *
     * @throws Exception
     */
    public void stopAndWait() throws Exception {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("[" + getClass().getSimpleName() + "] stopAndWait");
        }

        for (final A_Node node : nodes.values()) {
            node.stopMe();
        }

        for (final A_Node node : nodes.values()) {
            try {
                node.join();
            } catch (final InterruptedException ex) {
                // Ignore
            }
        }

        // Error mailer
        eMailer.stopMe();
        eMailer.join();

        for (final A_Node node : nodes.values()) {
            node.terminate();
        }
    }

    /**
     * Send a message, from a node.
     *
     * @param sourceID source node ID
     * @param message message
     * @throws java.lang.Exception
     */
    public void sendMessage(final String sourceID, final Message message) throws Exception {

        if (sourceID == null || message == null) {
            throw new IllegalArgumentException();
        }

        if (LOGGER.isTraceEnabled()) {
            LOGGER.trace("[" + getClass().getSimpleName() + "] sendMessage : sourceID=" + sourceID + " / message="
                    + Message.formatSimple(message));
        }

        final Set<String> subRoutes = routes.get(sourceID);
        if (subRoutes != null) {
            for (final String destinationID : subRoutes) {
                final A_Node destination = nodes.get(destinationID);

                destination.receiveMessage(message);
            }
        }

    }

    /**
     * Init configuration.
     *
     * @param config Configuration
     * @param datas Path to store datas
     * @param vCodeFactory Valication code factory
     * @throws java.lang.Exception
     */
    public void init(final XMLConfiguration config, final Path datas, final A_ValidatorCode.I_Factory vCodeFactory)
            throws Exception {
        if (config == null || datas == null) {
            throw new IllegalArgumentException();
        }

        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("[" + getClass().getSimpleName() + "] load() : datas=" + datas);
        }

        if (Files.notExists(datas)) {
            Files.createDirectory(datas);
        } else {
            if (!Files.isDirectory(datas)) {
                throw new IllegalArgumentException("Path '" + datas.toString() + "' should be a directory");
            }
        }

        this.datas = datas;
        this.vCodeFactory = vCodeFactory;

        // Load mailer
        eMailer.load(config.configurationAt("mailer"));

        // Load commons
        commonsProperties.clear();
        final List<HierarchicalConfiguration> cCommons = config.configurationsAt("commons.common");
        if (cCommons != null) {
            for (final HierarchicalConfiguration cCommon : cCommons) {
                final ConfigProperties cProperties = new ConfigProperties();
                cProperties.readProperties(cCommon);

                addCommon(cCommon.getString("[@id]"), cProperties);
            }
        }

        // Load nodes
        nodes.clear();
        final List<HierarchicalConfiguration> cNodes = config.configurationsAt("nodes.node");
        if (cNodes != null) {
            for (final HierarchicalConfiguration cNode : cNodes) {
                final List<ConfigProperties> commons = new ArrayList<>();

                final String commonsID = cNode.getString("[@commons]");
                if (commonsID != null) {
                    for (final String commonID : commonsID.split(",")) {
                        final ConfigProperties common = getCommon(commonID);
                        if (common != null) {
                            commons.add(common);
                        }
                    }
                }

                final A_Node node = addNode(cNode.getString("[@id]"), cNode.getString("[@type]"), commons);

                node.load(cNode);
            }
        }

        // Load routes
        routes.clear();
        final List<HierarchicalConfiguration> cRoutes = config.configurationsAt("routes.route");
        if (cRoutes != null) {
            for (final HierarchicalConfiguration cRoute : cRoutes) {
                final String sourceID = cRoute.getString("[@source]");
                final String destinationID = cRoute.getString("[@destination]");

                addRoute(sourceID, destinationID);
            }
        }
    }

    // DEFAULT
    final HashMap<String, A_Node> nodes;
    final HashMap<String, Set<String>> routes;
    final ErrorMailer eMailer;

    // PRIVATE
    private static final Logger LOGGER = LoggerFactory.getLogger(Dispatcher.class);
    private final HashMap<String, ConfigProperties> commonsProperties;
    private Path datas;
    private A_ValidatorCode.I_Factory vCodeFactory;
}