/* * 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; import net.kuujo.vertigo.cluster.Cluster; import net.kuujo.vertigo.cluster.impl.DefaultCluster; import net.kuujo.vertigo.cluster.manager.impl.ClusterAgent; import net.kuujo.vertigo.network.ActiveNetwork; import net.kuujo.vertigo.network.NetworkConfig; import net.kuujo.vertigo.network.impl.DefaultNetworkConfig; import net.kuujo.vertigo.util.Addresses; import net.kuujo.vertigo.util.Configs; import net.kuujo.vertigo.util.ContextUri; import org.vertx.java.core.AsyncResult; import org.vertx.java.core.Handler; import org.vertx.java.core.Vertx; import org.vertx.java.core.impl.DefaultFutureResult; import org.vertx.java.core.json.JsonObject; import org.vertx.java.platform.Container; import org.vertx.java.platform.Verticle; /** * The primary Vertigo API.<p> * * This is the core API for working with Vertigo clusters and networks. * To create a {@link Vertigo} instance within a Vert.x {@link Verticle}, * simply pass the <code>Verticle</code> to the <code>Vertigo</code> * constructor.<p> * * <pre> * {@code * Vertigo vertigo = new Vertigo(this); * } * </pre><p> * * To create a new network use the {@link Vertigo#createNetwork(String)} * methods.<p> * * All networks must be deployed to named clusters. Vertigo clusters are * simple collections of verticles that handle deployment and monitoring * of components within a Vertigo network. Cluster nodes can either be * deployed using the <code>vertx</code> command with the <code>vertigo-cluster</code> * module or programmatically using this API.<p> * * To deploy a cluster use the {@link Vertigo#deployCluster(String, Handler)} methods.<p> * * <pre> * {@code * vertigo.deployCluster("test-cluster", new Handler<AsyncResult<Cluster>>() { * public void handle(AsyncResult<Cluster> result) { * if (result.succeeded()) { * Cluster cluster = result.result(); * } * } * }); * } * </pre><p> * * The {@link Cluster} can be used to operate on a specific cluster, * or networks can be deployed to named clusters through this API. To get * a running cluster call the {@link Vertigo#getCluster(String, Handler)} method. To * deploy a network to a cluster use the {@link Vertigo#deployNetwork(String, NetworkConfig)} * methods.<p> * * <pre> * {@code * cluster.deployNetwork(network, new Handler<AsyncResult<ActiveNetwork>>() { * public void handle(AsyncResult<ActiveNetwork> result) { * ActiveNetwork network = result.result(); * } * }); * } * </pre><p> * * When a network is deployed to a cluster, the cluster first checks to determine * whether the network is already running in the cluster. If the network is already * running then <em>the new configuration will be merged with the running configuration.</em> * This is important to remember. It allows users to reconfigure running networks, * but it also means that a network deployment will not fail if a network of the * same name is already running in the cluster. Networks should be named carefully * in order to prevent merging two unrelated networks.<p> * * Clusters also provide a logical separation of networks within the Vert.x cluster. * While Vertigo will merge networks of the same name within a single cluster, networks * of the same name can be deployed in separate clusters simultaneously. Even if the * network configurations are the same, Vertigo will ensure that event bus addresses * do not clash across clusters, so it's okay to deploy, for instance, a "test" and * "live" cluster within the same Vert.x cluster. * * @author <a href="http://github.com/kuujo">Jordan Halterman</a> */ public class Vertigo { private static final String CLUSTER_MAIN_PROPERTY_NAME = "net.kuujo.vertigo.cluster"; private final Vertx vertx; private final Container container; private static String clusterMain; public Vertigo(Verticle verticle) { this(verticle.getVertx(), verticle.getContainer()); } public Vertigo(Vertx vertx, Container container) { this.vertx = vertx; this.container = container; } /** * Creates a new randomy named network. * * @return A new network instance. * @see Vertigo#createNetework(String) */ public NetworkConfig createNetwork() { return new DefaultNetworkConfig(); } /** * Creates a new network. * * @param name The network name. * @return A new network instance. * @see Vertigo#createNetwork() */ public NetworkConfig createNetwork(String name) { return new DefaultNetworkConfig(name); } /** * Creates a network configuration from json. * * @param json A json network configuration. * @return A network configuration. * @see Vertigo#createNetwork(String) */ public NetworkConfig createNetwork(JsonObject json) { return Configs.createNetwork(json); } /** * Loads the current cluster verticle. */ private String getClusterMain() { if (clusterMain == null) { try { clusterMain = System.getProperty(CLUSTER_MAIN_PROPERTY_NAME); } catch (Exception e) { } clusterMain = ClusterAgent.class.getName(); } return clusterMain; } /** * Deploys a randomly named unique cluster. * * @param doneHandler An asynchronous handler to be called once the cluster * has been deployed. The handler will be called with a {@link Cluster} * which can be used to manage networks running in the cluster. * @return The Vertigo instance. */ public Vertigo deployCluster(Handler<AsyncResult<Cluster>> doneHandler) { return deployCluster(ContextUri.createUniqueScheme(), doneHandler); } /** * Deploys a single node cluster at the given address. * * @param cluster The cluster event bus address. * @return The Vertigo instance. */ public Vertigo deployCluster(String cluster) { return deployCluster(cluster, null, 1, null); } /** * Deploys a single node cluster at the given address. * * @param cluster The cluster event bus address. * @param doneHandler An asynchronous handler to be called once the cluster * has been deployed. The handler will be called with a {@link Cluster} * which can be used to manage networks running in the cluster. * @return The Vertigo instance. */ public Vertigo deployCluster(String cluster, Handler<AsyncResult<Cluster>> doneHandler) { return deployCluster(cluster, null, 1, doneHandler); } /** * Deploys a single node cluster to a specific cluster group. * * @param cluster The cluster event bus address. * @param group The cluster group to which to deploy the node. * @return The Vertigo instance. */ public Vertigo deployCluster(String cluster, String group) { return deployCluster(cluster, group, 1, null); } /** * Deploys a single node cluster to a specific cluster group. * * @param cluster The cluster event bus address. * @param group The cluster group to which to deploy the node. * @param doneHandler An asynchronous handler to be called once the cluster * has been deployed. The handler will be called with a {@link Cluster} * which can be used to manage networks running in the cluster. * @return The Vertigo instance. */ public Vertigo deployCluster(String cluster, String group, Handler<AsyncResult<Cluster>> doneHandler) { return deployCluster(cluster, group, 1, doneHandler); } /** * Deploys multiple nodes within a cluster at the given address. * * @param cluster The cluster event bus address. * @param nodes The number of nodes to deploy. * @return The Vertigo instance. */ public Vertigo deployCluster(String cluster, int nodes) { return deployCluster(cluster, null, nodes, null); } /** * Deploys multiple nodes within a cluster at the given address. * * @param cluster The cluster event bus address. * @param nodes The number of nodes to deploy. * @param doneHandler An asynchronous handler to be called once the cluster * has been deployed. The handler will be called with a {@link Cluster} * which can be used to manage networks running in the cluster. * @return The Vertigo instance. */ public Vertigo deployCluster(String cluster, int nodes, Handler<AsyncResult<Cluster>> doneHandler) { return deployCluster(cluster, null, nodes, doneHandler); } /** * Deploys multiple cluster nodes to a specific cluster group. * * @param cluster The cluster event bus address. * @param group The cluster group to which to deploy the nodes. * @param nodes The number of nodes to deploy. * @return The Vertigo instance. */ public Vertigo deployCluster(String cluster, String group, int nodes) { return deployCluster(cluster, group, nodes, null); } /** * Deploys multiple cluster nodes to a specific cluster group. * * @param cluster The cluster event bus address. * @param group The cluster group to which to deploy the nodes. * @param nodes The number of nodes to deploy. * @param doneHandler An asynchronous handler to be called once the cluster * has been deployed. The handler will be called with a {@link Cluster} * which can be used to manage networks running in the cluster. * @return The Vertigo instance. */ public Vertigo deployCluster(final String cluster, final String group, int nodes, final Handler<AsyncResult<Cluster>> doneHandler) { JsonObject config = new JsonObject() .putString("cluster", cluster) .putString("group", group); container.deployVerticle(getClusterMain(), config, nodes, new Handler<AsyncResult<String>>() { @Override public void handle(AsyncResult<String> result) { if (result.failed()) { new DefaultFutureResult<Cluster>(result.cause()).setHandler(doneHandler); } else { getCluster(cluster, doneHandler); } } }); return this; } /** * Loads a cluster. * * @param address The cluster address. * @param resultHandler An asynchronous handler to be called once complete. * @return The Vertigo instance. */ public Vertigo getCluster(String address, Handler<AsyncResult<Cluster>> resultHandler) { Cluster cluster = new DefaultCluster(address, vertx, container); cluster.ping(resultHandler); return this; } /** * Deploys a bare network to an anonymous local-only cluster.<p> * * The network will be deployed with no components and no connections. You * can add components and connections to the network with an {@link ActiveNetwork} * instance. * * @param name The name of the network to deploy. * @return The Vertigo instance. */ public Vertigo deployNetwork(String name) { return deployNetwork(name, (Handler<AsyncResult<ActiveNetwork>>) null); } /** * Deploys a bare network to an anonymous local-only cluster.<p> * * The network will be deployed with no components and no connections. You * can add components and connections to the network with an {@link ActiveNetwork} * instance. * * @param name The name of the network to deploy. * @param doneHandler An asynchronous handler to be called once the network has * completed deployment. The handler will be called with an {@link ActiveNetwork} * instance which can be used to add or remove components and connections from * the network. * @return The Vertigo instance. */ public Vertigo deployNetwork(final String name, final Handler<AsyncResult<ActiveNetwork>> doneHandler) { final String cluster = Addresses.createUniqueAddress(); container.deployVerticle(ClusterAgent.class.getName(), new JsonObject().putString("cluster", cluster).putBoolean("local", true), new Handler<AsyncResult<String>>() { @Override public void handle(AsyncResult<String> result) { if (result.failed()) { new DefaultFutureResult<ActiveNetwork>(result.cause()).setHandler(doneHandler); } else { deployNetwork(cluster, name, doneHandler); } } }); return this; } /** * Deploys a json network to an anonynous local-only cluster.<p> * * The JSON network configuration will be converted to a {@link NetworkConfig} before * being deployed to the cluster. The conversion is done synchronously, so if the * configuration is invalid then this method may throw an exception. * * @param network The JSON network configuration. For the configuration format see * the project documentation. * @return The Vertigo instance. */ public Vertigo deployNetwork(JsonObject network) { return deployNetwork(network, null); } /** * Deploys a json network to an anonynous local-only cluster.<p> * * The JSON network configuration will be converted to a {@link NetworkConfig} before * being deployed to the cluster. The conversion is done synchronously, so if the * configuration is invalid then this method may throw an exception. * * @param network The JSON network configuration. For the configuration format see * the project documentation. * @param doneHandler An asynchronous handler to be called once the network has * completed deployment. The handler will be called with an {@link ActiveNetwork} * instance which can be used to add or remove components and connections from * the network. * @return The Vertigo instance. */ public Vertigo deployNetwork(final JsonObject network, final Handler<AsyncResult<ActiveNetwork>> doneHandler) { final String cluster = Addresses.createUniqueAddress(); container.deployVerticle(ClusterAgent.class.getName(), new JsonObject().putString("cluster", cluster).putBoolean("local", true), new Handler<AsyncResult<String>>() { @Override public void handle(AsyncResult<String> result) { if (result.failed()) { new DefaultFutureResult<ActiveNetwork>(result.cause()).setHandler(doneHandler); } else { deployNetwork(cluster, network, doneHandler); } } }); return this; } /** * Deploys a network to an anonymous local-only cluster.<p> * * If the given network configuration's name matches the name of a network * that is already running in the cluster then the given configuration will * be <b>merged</b> with the running network's configuration. This allows networks * to be dynamically updated with partial configurations. If the configuration * matches the already running configuration then no changes will occur, so it's * not necessary to check whether a network is already running if the configuration * has not been altered. * * @param network The configuration of the network to deploy. * @return The Vertigo instance. */ public Vertigo deployNetwork(NetworkConfig network) { return deployNetwork(network, null); } /** * Deploys a network to an anonymous local-only cluster.<p> * * If the given network configuration's name matches the name of a network * that is already running in the cluster then the given configuration will * be <b>merged</b> with the running network's configuration. This allows networks * to be dynamically updated with partial configurations. If the configuration * matches the already running configuration then no changes will occur, so it's * not necessary to check whether a network is already running if the configuration * has not been altered. * * @param network The configuration of the network to deploy. * @param doneHandler An asynchronous handler to be called once the network has * completed deployment. The handler will be called with an {@link ActiveNetwork} * instance which can be used to add or remove components and connections from * the network. * @return The Vertigo instance. */ public Vertigo deployNetwork(final NetworkConfig network, final Handler<AsyncResult<ActiveNetwork>> doneHandler) { final String cluster = Addresses.createUniqueAddress(); container.deployVerticle(ClusterAgent.class.getName(), new JsonObject().putString("cluster", cluster).putBoolean("local", true), new Handler<AsyncResult<String>>() { @Override public void handle(AsyncResult<String> result) { if (result.failed()) { new DefaultFutureResult<ActiveNetwork>(result.cause()).setHandler(doneHandler); } else { deployNetwork(cluster, network, doneHandler); } } }); return this; } /** * Deploys a bare network to a specific cluster.<p> * * The network will be deployed with no components and no connections. You * can add components and connections to the network with an {@link ActiveNetwork} * instance. * * @param cluster The cluster to which to deploy the network. * @param name The name of the network to deploy. * @return The Vertigo instance. */ public Vertigo deployNetwork(String cluster, String name) { return deployNetwork(cluster, name, null); } /** * Deploys a bare network to a specific cluster.<p> * * The network will be deployed with no components and no connections. You * can add components and connections to the network with an {@link ActiveNetwork} * instance. * * @param cluster The cluster to which to deploy the network. * @param name The name of the network to deploy. * @param doneHandler An asynchronous handler to be called once the network has * completed deployment. The handler will be called with an {@link ActiveNetwork} * instance which can be used to add or remove components and connections from * the network. * @return The Vertigo instance. */ public Vertigo deployNetwork(String cluster, final String name, final Handler<AsyncResult<ActiveNetwork>> doneHandler) { getCluster(cluster, new Handler<AsyncResult<Cluster>>() { @Override public void handle(AsyncResult<Cluster> result) { if (result.failed()) { new DefaultFutureResult<ActiveNetwork>(result.cause()).setHandler(doneHandler); } else { result.result().deployNetwork(name, doneHandler); } } }); return this; } /** * Deploys a json network to a specific cluster.<p> * * The JSON network configuration will be converted to a {@link NetworkConfig} before * being deployed to the cluster. The conversion is done synchronously, so if the * configuration is invalid then this method may throw an exception. * * @param cluster The cluster to which to deploy the network. * @param network The JSON network configuration. For the configuration format see * the project documentation. * @return The Vertigo instance. */ public Vertigo deployNetwork(String cluster, JsonObject network) { return deployNetwork(cluster, network, null); } /** * Deploys a json network to a specific cluster.<p> * * The JSON network configuration will be converted to a {@link NetworkConfig} before * being deployed to the cluster. The conversion is done synchronously, so if the * configuration is invalid then this method may throw an exception. * * @param cluster The cluster to which to deploy the network. * @param network The JSON network configuration. For the configuration format see * the project documentation. * @param doneHandler An asynchronous handler to be called once the network has * completed deployment. The handler will be called with an {@link ActiveNetwork} * instance which can be used to add or remove components and connections from * the network. * @return The Vertigo instance. */ public Vertigo deployNetwork(String cluster, final JsonObject network, final Handler<AsyncResult<ActiveNetwork>> doneHandler) { getCluster(cluster, new Handler<AsyncResult<Cluster>>() { @Override public void handle(AsyncResult<Cluster> result) { if (result.failed()) { new DefaultFutureResult<ActiveNetwork>(result.cause()).setHandler(doneHandler); } else { result.result().deployNetwork(network, doneHandler); } } }); return this; } /** * Deploys a network to a specific cluster.<p> * * If the given network configuration's name matches the name of a network * that is already running in the cluster then the given configuration will * be <b>merged</b> with the running network's configuration. This allows networks * to be dynamically updated with partial configurations. If the configuration * matches the already running configuration then no changes will occur, so it's * not necessary to check whether a network is already running if the configuration * has not been altered. * * @param cluster The cluster to which to deploy the network. * @param network The configuration of the network to deploy. * @return The Vertigo instance. */ public Vertigo deployNetwork(String cluster, NetworkConfig network) { return deployNetwork(cluster, network, null); } /** * Deploys a network to a specific cluster.<p> * * If the given network configuration's name matches the name of a network * that is already running in the cluster then the given configuration will * be <b>merged</b> with the running network's configuration. This allows networks * to be dynamically updated with partial configurations. If the configuration * matches the already running configuration then no changes will occur, so it's * not necessary to check whether a network is already running if the configuration * has not been altered. * * @param cluster The cluster to which to deploy the network. * @param network The configuration of the network to deploy. * @param doneHandler An asynchronous handler to be called once the network has * completed deployment. The handler will be called with an {@link ActiveNetwork} * instance which can be used to add or remove components and connections from * the network. * @return The Vertigo instance. */ public Vertigo deployNetwork(String cluster, final NetworkConfig network, final Handler<AsyncResult<ActiveNetwork>> doneHandler) { getCluster(cluster, new Handler<AsyncResult<Cluster>>() { @Override public void handle(AsyncResult<Cluster> result) { if (result.failed()) { new DefaultFutureResult<ActiveNetwork>(result.cause()).setHandler(doneHandler); } else { result.result().deployNetwork(network, doneHandler); } } }); return this; } /** * Undeploys a complete network from the given cluster.<p> * * This method does not require a network configuration for undeployment. Vertigo * will load the configuration from the fault-tolerant data store and undeploy * components internally. This allows networks to be undeployed without the network * configuration. * * @param cluster The cluster from which to undeploy the network. * @param name The name of the network to undeploy. * @return The Vertigo instance. */ public Vertigo undeployNetwork(String cluster, String name) { return undeployNetwork(cluster, name, null); } /** * Undeploys a complete network from the given cluster.<p> * * This method does not require a network configuration for undeployment. Vertigo * will load the configuration from the fault-tolerant data store and undeploy * components internally. This allows networks to be undeployed without the network * configuration. * * @param cluster The cluster from which to undeploy the network. * @param name The name of the network to undeploy. * @param doneHandler An asynchronous handler to be called once the network is undeployed. * @return The Vertigo instance. */ public Vertigo undeployNetwork(String cluster, final String name, final Handler<AsyncResult<Void>> doneHandler) { getCluster(cluster, new Handler<AsyncResult<Cluster>>() { @Override public void handle(AsyncResult<Cluster> result) { if (result.failed()) { new DefaultFutureResult<Void>(result.cause()).setHandler(doneHandler); } else { result.result().undeployNetwork(name, doneHandler); } } }); return this; } /** * Undeploys a network from the given cluster from a json configuration.<p> * * The JSON configuration will immediately be converted to a {@link NetworkConfig} prior * to undeploying the network. In order to undeploy the entire network, the configuration * should be the same as the deployed configuration. Vertigo will use configuration * components to determine whether two configurations are identical. If the given * configuration is not identical to the running configuration, any components or * connections in the json configuration that are present in the running network * will be closed and removed from the running network. * * @param cluster The cluster from which to undeploy the network. * @param network The JSON configuration to undeploy. For the configuration format see * the project documentation. * @return The Vertigo instance. */ public Vertigo undeployNetwork(String cluster, JsonObject network) { return undeployNetwork(cluster, network, null); } /** * Undeploys a network from the given cluster from a json configuration.<p> * * The JSON configuration will immediately be converted to a {@link NetworkConfig} prior * to undeploying the network. In order to undeploy the entire network, the configuration * should be the same as the deployed configuration. Vertigo will use configuration * components to determine whether two configurations are identical. If the given * configuration is not identical to the running configuration, any components or * connections in the json configuration that are present in the running network * will be closed and removed from the running network. * * @param cluster The cluster from which to undeploy the network. * @param network The JSON configuration to undeploy. For the configuration format see * the project documentation. * @param doneHandler An asynchronous handler to be called once the configuration is undeployed. * @return The Vertigo instance. */ public Vertigo undeployNetwork(String cluster, final JsonObject network, final Handler<AsyncResult<Void>> doneHandler) { getCluster(cluster, new Handler<AsyncResult<Cluster>>() { @Override public void handle(AsyncResult<Cluster> result) { if (result.failed()) { new DefaultFutureResult<Void>(result.cause()).setHandler(doneHandler); } else { result.result().undeployNetwork(network, doneHandler); } } }); return this; } /** * Undeploys a network from the given cluster.<p> * * This method supports both partial and complete undeployment of networks. When * undeploying networks by specifying a {@link NetworkConfig}, the network configuration * should contain all components and connections that are being undeployed. If the * configuration's components and connections match all deployed components and * connections then the entire network will be undeployed. * * @param cluster The cluster from which to undeploy the network. * @param network The network configuration to undeploy. * @return The Vertigo instance. */ public Vertigo undeployNetwork(String cluster, NetworkConfig network) { return undeployNetwork(cluster, network, null); } /** * Undeploys a network from the given cluster.<p> * * This method supports both partial and complete undeployment of networks. When * undeploying networks by specifying a {@link NetworkConfig}, the network configuration * should contain all components and connections that are being undeployed. If the * configuration's components and connections match all deployed components and * connections then the entire network will be undeployed. * * @param cluster The cluster from which to undeploy the network. * @param network The network configuration to undeploy. * @param doneHandler An asynchronous handler to be called once the configuration is undeployed. * @return The Vertigo instance. */ public Vertigo undeployNetwork(String cluster, final NetworkConfig network, final Handler<AsyncResult<Void>> doneHandler) { getCluster(cluster, new Handler<AsyncResult<Cluster>>() { @Override public void handle(AsyncResult<Cluster> result) { if (result.failed()) { new DefaultFutureResult<Void>(result.cause()).setHandler(doneHandler); } else { result.result().undeployNetwork(network, doneHandler); } } }); return this; } }