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

net.kuujo.vertigo.network.Network Maven / Gradle / Ivy

There is a newer version: 0.7.0-RC3
Show newest version
/*
 * Copyright 2013 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.network;

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

import net.kuujo.vertigo.context.MalformedContextException;
import net.kuujo.vertigo.context.NetworkContext;
import net.kuujo.vertigo.serializer.Serializable;

import org.vertx.java.core.json.JsonArray;
import org.vertx.java.core.json.JsonObject;

/**
 * A Vertigo network.
 *
 * @author Jordan Halterman
 */
public class Network implements Serializable {
  public static final String ADDRESS = "address";
  public static final String BROADCAST = "broadcast";
  public static final String AUDITORS = "auditors";
  public static final String ACKING = "acking";
  public static final String ACK_TIMEOUT = "timeout";
  public static final String ACK_DELAY = "ack_delay";
  public static final String COMPONENTS = "components";

  public static final long DEFAULT_ACK_TIMEOUT = 30000;
  public static final long DEFAULT_ACK_DELAY = 0;

  private JsonObject definition;

  public Network() {
    definition = new JsonObject();
    init();
  }

  private Network(JsonObject definition) {
    this.definition = definition;
    init();
  }

  public Network(String address) {
    definition = new JsonObject().putString(ADDRESS, address);
    init();
  }

  /**
   * Creates a network from JSON.
   *
   * @param json
   *   A JSON representation of the network.
   * @return
   *   A new network instance.
   * @throws MalformedNetworkException
   *   If the network definition is malformed.
   */
  public static Network fromJson(JsonObject json) throws MalformedNetworkException {
    JsonObject components = json.getObject(COMPONENTS);
    if (components == null) {
      components = new JsonObject();
      json.putObject(COMPONENTS, components);
    }

    for (String address : components.getFieldNames()) {
      JsonObject componentInfo = components.getObject(address);
      if (componentInfo == null) {
        components.removeField(address);
      }
      else {
        componentInfo.putString(Component.ADDRESS, address);
        // Construct the component instance to make sure the definition is not malformed.
        Component.fromJson(componentInfo);
      }
    }
    return new Network(json);
  }

  /**
   * Initializes the internal definition.
   */
  private void init() {
    String address = definition.getString(ADDRESS);
    if (address == null) {
      address = UUID.randomUUID().toString();
      definition.putString(ADDRESS, address);
    }

    String broadcast = definition.getString(BROADCAST);
    if (broadcast == null) {
      definition.putString(BROADCAST, String.format("%s.%s", address, BROADCAST));
    }
  }

  /**
   * Returns the network address.
   *
   * This is the event bus address at which the network's coordinator will register
   * a handler for components to connect to once deployed.
   *
   * @return
   *   The network address.
   */
  public String getAddress() {
    return definition.getString(ADDRESS);
  }

  /**
   * Returns the number of network auditors.
   *
   * @return
   *   The number of network auditors.
   */
  public int getNumAuditors() {
    return definition.getInteger(AUDITORS, 1);
  }

  /**
   * Sets the number of network auditors.
   *
   * This is the number of auditor verticle instances that will be used to track
   * messages throughout a network. If the network is slowing due to acking
   * overflows, you may need to increase the number of network auditors.
   *
   * @param numAuditors
   *   The number of network auditors.
   * @return
   *   The called network instance.
   */
  public Network setNumAuditors(int numAuditors) {
    definition.putNumber(AUDITORS, numAuditors);
    return this;
  }

  /**
   * Enables acking on the network.
   *
   * When acking is enabled, network auditors will track message trees throughout
   * the network and notify messages sources once messages have completed processing.
   *
   * @return
   *   The called network instance.
   */
  public Network enableAcking() {
    definition.putBoolean(ACKING, true);
    return this;
  }

  /**
   * Disables acking on the network.
   *
   * When acking is disabled, network auditors will immediately ack any new
   * messages that are made known to them.
   *
   * @return
   *   The called network instance.
   */
  public Network disableAcking() {
    definition.putBoolean(ACKING, false);
    return this;
  }

  /**
   * Returns a boolean indicating whether acking is enabled.
   *
   * @return
   *   Indicates whether acking is enabled for the network.
   */
  public boolean isAckingEnabled() {
    return definition.getBoolean(ACKING, true);
  }

  /**
   * Sets the network ack timeout.
   *
   * This indicates the maximum amount of time an auditor will hold message
   * information in memory before considering it to be timed out.
   *
   * @param timeout
   *   An ack timeout.
   * @return
   *   The called network instance.
   */
  public Network setAckTimeout(long timeout) {
    definition.putNumber(ACK_TIMEOUT, timeout);
    return this;
  }

  /**
   * Gets the network ack timeout.
   *
   * @return
   *   Ack timeout for the network. Defaults to 30000
   */
  public long getAckTimeout() {
    return definition.getLong(ACK_TIMEOUT, DEFAULT_ACK_TIMEOUT);
  }

  /**
   * Sets the network ack delay.
   *
   * This indicates the amount of time an auditor will wait before notifying a
   * message source that a message has complete processing. If a new descendant
   * message is created during the delay period, the auditor will continue tracking
   * the tree and will not notify the message source until the new message and
   * its descendants have completed processing. This allows component implementations
   * to hold messages in memory for some period before creating new children. It
   * may also help resolve issues where blocking is preventing auditors from working
   * properly.
   *
   * @param delay
   *   The ack delay.
   * @return
   *   The called network instance.
   */
  public Network setAckDelay(long delay) {
    definition.putNumber(ACK_DELAY, delay);
    return this;
  }

  /**
   * Gets the network ack delay.
   *
   * @return
   *   Ack delay for the network. Defaults to 0
   */
  public long getAckDelay() {
    return definition.getLong(ACK_DELAY, DEFAULT_ACK_DELAY);
  }

  /**
   * Gets a list of network components.
   *
   * @return
   *   A list of network components.
   */
  public List> getComponents() {
    List> components = new ArrayList>();
    JsonObject componentInfo = definition.getObject(COMPONENTS);
    if (componentInfo == null) {
      componentInfo = new JsonObject();
      definition.putObject(COMPONENTS, componentInfo);
    }

    for (String address : componentInfo.getFieldNames()) {
      Component component = getComponent(address);
      if (component != null) {
        components.add(component);
      }
    }
    return components;
  }

  /**
   * Gets a component by address.
   *
   * @param address
   *   The component address.
   * @return
   *   A component instance, or null if the component does not exist in the network.
   */
  public Component getComponent(String address) {
    JsonObject components = definition.getObject(COMPONENTS);
    if (components == null) {
      components = new JsonObject();
      definition.putObject(COMPONENTS, components);
    }

    if (components.getFieldNames().contains(address)) {
      try {
        return Component.fromJson(components.getObject(address));
      }
      catch (MalformedNetworkException e) {
        return null;
      }
    }
    return null;
  }

  /**
   * Adds a component to the network.
   *
   * @param component
   *   The component to add.
   * @return
   *   The added component instance.
   */
  public > T addComponent(T component) {
    JsonObject components = definition.getObject(COMPONENTS);
    if (components == null) {
      components = new JsonObject();
      definition.putObject(COMPONENTS, components);
    }
    components.putObject(component.getAddress(), component.getState());
    return component;
  }

  /**
   * Adds a verticle component to the network.
   *
   * @param address
   *   The component address. This is the address to which other components may
   *   connect to listen to the component's output.
   * @return
   *   The new verticle component instance.
   */
  public Verticle addVerticle(String address) {
    return addComponent(new Verticle(address));
  }

  /**
   * Adds a verticle component to the network.
   *
   * @param address
   *   The component address. This is the address to which other components may
   *   connect to listen to the component's output.
   * @param main
   *   The verticle main.
   * @return
   *   The new verticle component instance.
   */
  public Verticle addVerticle(String address, String main) {
    return addComponent(new Verticle(address).setMain(main));
  }

  /**
   * Adds a verticle component to the network.
   *
   * @param address
   *   The component address. This is the address to which other components may
   *   connect to listen to the component's output.
   * @param main
   *   The verticle main.
   * @param config
   *   The verticle component configuration. This configuration will be made
   *   available as the normal Vert.x container configuration within the verticle.
   * @return
   *   The new verticle component instance.
   */
  public Verticle addVerticle(String address, String main, JsonObject config) {
    return addComponent(new Verticle(address).setMain(main).setConfig(config));
  }

  /**
   * Adds a verticle component to the network.
   *
   * @param address
   *   The component address. This is the address to which other components may
   *   connect to listen to the component's output.
   * @param main
   *   The verticle main.
   * @param instances
   *   The number of component instances.
   * @return
   *   The new verticle component instance.
   */
  public Verticle addVerticle(String address, String main, int instances) {
    return addComponent(new Verticle(address).setMain(main).setInstances(instances));
  }

  /**
   * Adds a verticle component to the network.
   *
   * @param address
   *   The component address. This is the address to which other components may
   *   connect to listen to the component's output.
   * @param main
   *   The verticle main.
   * @param config
   *   The verticle component configuration. This configuration will be made
   *   available as the normal Vert.x container configuration within the verticle.
   * @param instances
   *   The number of component instances.
   * @return
   *   The new verticle component instance.
   */
  public Verticle addVerticle(String address, String main, JsonObject config, int instances) {
    return addComponent(new Verticle(address).setMain(main).setConfig(config).setInstances(instances));
  }

  /**
   * Adds a module component to the network.
   *
   * @param address
   *   The component address. This is the address to which other components may
   *   connect to listen to the component's output.
   * @return
   *   The new module component instance.
   */
  public Module addModule(String address) {
    return addComponent(new Module(address));
  }

  /**
   * Adds a module component to the network.
   *
   * @param address
   *   The component address. This is the address to which other components may
   *   connect to listen to the component's output.
   * @param moduleName
   *   The module name.
   * @return
   *   The new module component instance.
   */
  public Module addModule(String address, String moduleName) {
    return addComponent(new Module(address).setModule(moduleName));
  }

  /**
   * Adds a module component to the network.
   *
   * @param address
   *   The component address. This is the address to which other components may
   *   connect to listen to the component's output.
   * @param moduleName
   *   The module name.
   * @param config
   *   The module component configuration. This configuration will be made
   *   available as the normal Vert.x container configuration within the module.
   * @return
   *   The new module component instance.
   */
  public Module addModule(String address, String moduleName, JsonObject config) {
    return addComponent(new Module(address).setModule(moduleName).setConfig(config));
  }

  /**
   * Adds a module component to the network.
   *
   * @param address
   *   The component address. This is the address to which other components may
   *   connect to listen to the component's output.
   * @param moduleName
   *   The module name.
   * @param instances
   *   The number of component instances.
   * @return
   *   The new module component instance.
   */
  public Module addModule(String address, String moduleName, int instances) {
    return addComponent(new Module(address).setModule(moduleName).setInstances(instances));
  }

  /**
   * Adds a module component to the network.
   *
   * @param address
   *   The component address. This is the address to which other components may
   *   connect to listen to the component's output.
   * @param moduleName
   *   The module name.
   * @param config
   *   The module component configuration. This configuration will be made
   *   available as the normal Vert.x container configuration within the module.
   * @param instances
   *   The number of component instances.
   * @return
   *   The new module component instance.
   */
  public Module addModule(String address, String moduleName, JsonObject config, int instances) {
    return addComponent(new Module(address).setModule(moduleName).setConfig(config).setInstances(instances));
  }

  /**
   * Creates a network context from network definition.
   *
   * @return
   *   A network context.
   * @throws MalformedNetworkException
   *   If the network definition is invalid.
   */
  public NetworkContext createContext() throws MalformedNetworkException {
    JsonObject context = definition.copy();

    String address = context.getString(ADDRESS);
    if (address == null) {
      address = UUID.randomUUID().toString();
      context.putString(ADDRESS, address);
    }

    context.putString(BROADCAST, context.getString(BROADCAST, String.format("%s.%s", address, BROADCAST)));
    context.putBoolean(ACKING, context.getBoolean(ACKING, true));
    context.putNumber(ACK_TIMEOUT, context.getLong(ACK_TIMEOUT, DEFAULT_ACK_TIMEOUT));
    context.putNumber(ACK_DELAY, context.getLong(ACK_DELAY, DEFAULT_ACK_DELAY));

    int numAuditors = context.getInteger(AUDITORS, 1);
    JsonArray auditors = new JsonArray();
    for (int i = 0; i < numAuditors; i++) {
      auditors.add(String.format("%s.auditor.%d", getAddress(), i+1));
    }
    context.putArray(AUDITORS, auditors);

    JsonObject componentDefs = definition.getObject(COMPONENTS);
    if (componentDefs == null) {
      componentDefs = new JsonObject();
    }

    JsonObject componentContexts = new JsonObject();
    for (String componentAddress : componentDefs.getFieldNames()) {
      JsonObject componentContext = getComponent(componentAddress).createContext().getState();
      if (componentContext != null) {
        // Adding the parent context here causes a circular reference in serialization.
        // componentContexts.putObject(componentAddress, componentContext.putObject("parent", context));
        componentContexts.putObject(componentAddress, componentContext);
      }
    }
    context.putObject(COMPONENTS, componentContexts);
    try {
      return NetworkContext.fromJson(context);
    }
    catch (MalformedContextException e) {
      throw new MalformedNetworkException(e);
    }
  }

  @Override
  public JsonObject getState() {
    return definition;
  }

  @Override
  public void setState(JsonObject state) {
    definition = state;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy