Package net.kuujo.vertigo.impl

Source Code of net.kuujo.vertigo.impl.ContextBuilder

/*
* Copyright 2014 the original author or authors.
*
* 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 net.kuujo.vertigo.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import net.kuujo.vertigo.component.ComponentConfig;
import net.kuujo.vertigo.component.ComponentContext;
import net.kuujo.vertigo.component.InstanceContext;
import net.kuujo.vertigo.component.ModuleConfig;
import net.kuujo.vertigo.component.VerticleConfig;
import net.kuujo.vertigo.component.impl.DefaultInstanceContext;
import net.kuujo.vertigo.component.impl.DefaultModuleContext;
import net.kuujo.vertigo.component.impl.DefaultVerticleContext;
import net.kuujo.vertigo.hook.IOHook;
import net.kuujo.vertigo.io.connection.ConnectionConfig;
import net.kuujo.vertigo.io.connection.impl.DefaultConnectionContext;
import net.kuujo.vertigo.io.connection.impl.DefaultInputConnectionContext;
import net.kuujo.vertigo.io.connection.impl.DefaultOutputConnectionContext;
import net.kuujo.vertigo.io.impl.DefaultInputContext;
import net.kuujo.vertigo.io.impl.DefaultOutputContext;
import net.kuujo.vertigo.io.port.InputPortContext;
import net.kuujo.vertigo.io.port.OutputPortContext;
import net.kuujo.vertigo.io.port.impl.DefaultInputPortContext;
import net.kuujo.vertigo.io.port.impl.DefaultOutputPortContext;
import net.kuujo.vertigo.io.stream.impl.DefaultOutputStreamContext;
import net.kuujo.vertigo.network.MalformedNetworkException;
import net.kuujo.vertigo.network.NetworkConfig;
import net.kuujo.vertigo.network.NetworkContext;
import net.kuujo.vertigo.network.impl.DefaultNetworkContext;

/**
* A context builder.
*
* @author <a href="http://github.com/kuujo">Jordan Halterman</a>
*/
public final class ContextBuilder {
  private static final String COMPONENT_ADDRESS_PATTERN = System.getProperty("vertigo.component.address", "%1$s.%2$s.%3$s");

  /**
   * Builds a network context from a network definition.
   *
   * @param network The network definition.
   * @param cluster The cluster to which the network belongs.
   * @return A new network context.
   * @throws MalformedNetworkException If the network is malformed.
   */
  public static NetworkContext buildContext(NetworkConfig network, String cluster) {
    DefaultNetworkContext.Builder context = DefaultNetworkContext.Builder.newBuilder();

    // Set basic network configuration options.
    context.setName(network.getName());
    context.setVersion(UUID.randomUUID().toString());
    context.setAddress(String.format("%s.%s", cluster, network.getName()));
    context.setConfig(network);
    context.setCluster(cluster);
    context.setStatusAddress(String.format("%s.%s.__status", cluster, network.getName()));

    // Set up network components without inputs. Inputs are stored in a map so
    // that they can be set up after all component instances have been set up.
    Map<String, ComponentContext<?>> components = new HashMap<>();
    for (ComponentConfig<?> component : network.getComponents()) {
      if (component.getType().equals(ComponentConfig.Type.MODULE)) {
        // Set up basic module configuration options.
        DefaultModuleContext.Builder module = DefaultModuleContext.Builder.newBuilder();
        module.setName(component.getName());
        String address = String.format(COMPONENT_ADDRESS_PATTERN, cluster, network.getName(), component.getName());
        module.setAddress(address);
        module.setStatusAddress(String.format("%s.__status", address));
        module.setModule(((ModuleConfig) component).getModule());
        module.setConfig(component.getConfig());
        module.setGroup(component.getGroup());
        module.setHooks(component.getHooks());

        // Set up module instances.
        List<InstanceContext> instances = new ArrayList<>();
        for (int i = 1; i <= component.getInstances(); i++) {
          DefaultInstanceContext.Builder instance = DefaultInstanceContext.Builder.newBuilder();
          instance.setAddress(String.format("%s-%d", address, i));
          instance.setStatusAddress(String.format("%s-%d.__status", address, i));
          instance.setNumber(i);
          instance.setInput(DefaultInputContext.Builder.newBuilder().build());
          instance.setOutput(DefaultOutputContext.Builder.newBuilder().build());
          instances.add(instance.build());
        }
        module.setInstances(instances);

        components.put(component.getName(), module.build());
      } else {
        // Set up basic verticle configuration options.
        DefaultVerticleContext.Builder verticle = DefaultVerticleContext.Builder.newBuilder();
        verticle.setName(component.getName());
        String address = String.format(COMPONENT_ADDRESS_PATTERN, cluster, network.getName(), component.getName());
        verticle.setAddress(address);
        verticle.setStatusAddress(String.format("%s.__status", address));
        verticle.setMain(((VerticleConfig) component).getMain());
        verticle.setWorker(((VerticleConfig) component).isWorker());
        verticle.setMultiThreaded(((VerticleConfig) component).isMultiThreaded());
        verticle.setConfig(component.getConfig());
        verticle.setGroup(component.getGroup());
        verticle.setHooks(component.getHooks());

        // Set up module instances.
        List<InstanceContext> instances = new ArrayList<>();
        for (int i = 1; i <= component.getInstances(); i++) {
          DefaultInstanceContext.Builder instance = DefaultInstanceContext.Builder.newBuilder();
          instance.setAddress(String.format("%s-%d", address, i));
          instance.setStatusAddress(String.format("%s-%d.__status", address, i));
          instance.setNumber(i);
          instance.setInput(DefaultInputContext.Builder.newBuilder().build());
          instance.setOutput(DefaultOutputContext.Builder.newBuilder().build());
          instances.add(instance.build());
        }
        verticle.setInstances(instances);

        components.put(component.getName(), verticle.build());
      }
    }

    // Iterate through connections and create connection contexts.
    // For each input connection, an internal input connection is created
    // for each instance of the source component. Corresponding output connections
    // are assigned to each output connection. In other words, each internal
    // output connection can send to multiple addresses, but each internal input
    // connection only listens on a single event bus address for messages from a
    // single instance of the source component. This simplifies back pressure and
    // resolving ordering issues in many-to-many component relationships.
    for (ConnectionConfig connection : network.getConnections()) {
      ComponentContext<?> source = components.get(connection.getSource().getComponent());
      ComponentContext<?> target = components.get(connection.getTarget().getComponent());

      // Only add connections if both components are currently in the network configuration.
      // If a component is added to the configuration later then the context will need to
      // be rebuilt.
      if (source != null && target != null) {
        for (InstanceContext sourceInstance : source.instances()) {
          // Check if the port already exists on the source's output.
          DefaultOutputPortContext.Builder output = null;
          for (OutputPortContext port : sourceInstance.output().ports()) {
            if (port.name().equals(connection.getSource().getPort())) {
              output = DefaultOutputPortContext.Builder.newBuilder(port);
              break;
            }
          }

          // If the output port doesn't already exist then add it.
          if (output == null) {
            OutputPortContext port = DefaultOutputPortContext.Builder.newBuilder()
                .setAddress(String.format("out:%s@%s.%s.%s[%d]", connection.getSource().getPort(), cluster, network.getName(), source.name(), sourceInstance.number()))
                .setName(connection.getSource().getPort())
                .build();
            DefaultOutputContext.Builder.newBuilder(sourceInstance.output())
                .addPort(port).build();
            output = DefaultOutputPortContext.Builder.newBuilder(port);
          }

          // Set up an output stream from the output port.
          DefaultOutputStreamContext.Builder outStream = DefaultOutputStreamContext.Builder.newBuilder();
          outStream.setAddress(String.format("out:%s@%s.%s.%s[%d]->in:%s@%s.%s.%s[]", connection.getSource().getPort(), cluster, network.getName(), source.name(), sourceInstance.number(), connection.getTarget().getPort(), cluster, network.getName(), target.name()));
          outStream.setSelector(connection.getSelector());

          // For each target instance, add a unique input connection for the output.
          for (InstanceContext targetInstance : target.instances()) {
            // Check if the port already exists on the target's input.
            DefaultInputPortContext.Builder input = null;
            for (InputPortContext port : targetInstance.input().ports()) {
              if (port.name().equals(connection.getTarget().getPort())) {
                input = DefaultInputPortContext.Builder.newBuilder(port);
                break;
              }
            }

            // If the input port doesn't already exist then add it.
            if (input == null) {
              InputPortContext port = DefaultInputPortContext.Builder.newBuilder()
                  .setAddress(String.format("in:%s@%s.%s.%s[%d]", connection.getTarget().getPort(), cluster, network.getName(), target.name(), targetInstance.number()))
                  .setName(connection.getTarget().getPort())
                  .build();
              DefaultInputContext.Builder.newBuilder(targetInstance.input())
                  .addPort(port).build();
              input = DefaultInputPortContext.Builder.newBuilder(port);
            }

            // Add an input connection to the input port.
            DefaultInputConnectionContext.Builder inConnection = DefaultInputConnectionContext.Builder.newBuilder();
            String address = String.format("out:%s@%s.%s.%s[%d]->in:%s@%s.%s.%s[%d]", connection.getSource().getPort(), cluster, network.getName(), source.name(), sourceInstance.number(), connection.getTarget().getPort(), cluster, network.getName(), target.name(), targetInstance.number());
            inConnection.setAddress(address);
            inConnection.setSource(DefaultConnectionContext.DefaultSourceContext.Builder.newBuilder()
                .setComponent(connection.getSource().getComponent())
                .setPort(connection.getSource().getPort())
                .setInstance(sourceInstance.number()).build());
            inConnection.setTarget(DefaultConnectionContext.DefaultTargetContext.Builder.newBuilder()
                .setComponent(connection.getTarget().getComponent())
                .setPort(connection.getTarget().getPort())
                .setInstance(targetInstance.number()).build());

            // Add input level hooks to the input.
            inConnection.setHooks(connection.getTarget().getHooks());

            // Add connection level hooks to the input.
            for (IOHook hook : connection.getHooks()) {
              inConnection.addHook(hook);
            }

            // Add the connection to the target input port.
            input.addConnection(inConnection.build()).build();

            // Add the new output connection to the output stream. This creates a one-to-many
            // relationship between output connections and input connections, and input
            // connections maintain a many-to-one relationship with output connections.
            DefaultOutputConnectionContext.Builder outConnection = DefaultOutputConnectionContext.Builder.newBuilder();
            outConnection.setAddress(address);
            outConnection.setSource(DefaultConnectionContext.DefaultSourceContext.Builder.newBuilder()
                .setComponent(connection.getSource().getComponent())
                .setPort(connection.getSource().getPort()).build());
            outConnection.setTarget(DefaultConnectionContext.DefaultTargetContext.Builder.newBuilder()
                .setComponent(connection.getTarget().getComponent())
                .setPort(connection.getTarget().getPort()).build());

            // Add output level hooks to the output.
            outConnection.setHooks(connection.getSource().getHooks());

            // Add connection level hooks to the output.
            for (IOHook hook : connection.getHooks()) {
              outConnection.addHook(hook);
            }
            outStream.addConnection(outConnection.build());
          }

          // Add the connection to the source instance's out port.
          output.addStream(outStream.build()).build();
        }
      }
    }

    // Set the components on the network context and build the final context.
    context.setComponents(components.values());
    return context.build();
  }

}
TOP

Related Classes of net.kuujo.vertigo.impl.ContextBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.