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

org.apache.brooklyn.demo.CumulusRDFApplication Maven / Gradle / Ivy

There is a newer version: 0.11.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.brooklyn.demo;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.brooklyn.api.catalog.Catalog;
import org.apache.brooklyn.api.catalog.CatalogConfig;
import org.apache.brooklyn.api.effector.Effector;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.location.Location;
import org.apache.brooklyn.api.policy.PolicySpec;
import org.apache.brooklyn.api.sensor.EnricherSpec;
import org.apache.brooklyn.api.sensor.SensorEvent;
import org.apache.brooklyn.api.sensor.SensorEventListener;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.ConfigKeys;
import org.apache.brooklyn.core.effector.EffectorBody;
import org.apache.brooklyn.core.effector.Effectors;
import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
import org.apache.brooklyn.core.entity.AbstractApplication;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.EntityInternal;
import org.apache.brooklyn.core.entity.StartableApplication;
import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
import org.apache.brooklyn.core.entity.trait.Startable;
import org.apache.brooklyn.core.location.PortRanges;
import org.apache.brooklyn.core.sensor.DependentConfiguration;
import org.apache.brooklyn.entity.java.UsesJava;
import org.apache.brooklyn.entity.java.UsesJmx;
import org.apache.brooklyn.entity.nosql.cassandra.CassandraDatacenter;
import org.apache.brooklyn.entity.nosql.cassandra.CassandraFabric;
import org.apache.brooklyn.entity.nosql.cassandra.CassandraNode;
import org.apache.brooklyn.entity.webapp.JavaWebAppService;
import org.apache.brooklyn.entity.webapp.tomcat.TomcatServer;
import org.apache.brooklyn.launcher.BrooklynLauncher;
import org.apache.brooklyn.policy.ha.ServiceFailureDetector;
import org.apache.brooklyn.policy.ha.ServiceReplacer;
import org.apache.brooklyn.policy.ha.ServiceRestarter;
import org.apache.brooklyn.util.CommandLineUtil;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.core.ResourceUtils;
import org.apache.brooklyn.util.core.config.ConfigBag;
import org.apache.brooklyn.util.core.task.DynamicTasks;
import org.apache.brooklyn.util.core.text.TemplateProcessor;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.text.Strings;
import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.net.HostAndPort;

/** CumulusRDF application with Cassandra cluster. */
@Catalog(name="Cumulus RDF Application", description="CumulusRDF Application on a Tomcat server using a multi-region Cassandra fabric")
public class CumulusRDFApplication extends AbstractApplication {

    private static final Logger log = LoggerFactory.getLogger(CumulusRDFApplication.class);

    @CatalogConfig(label="Cumulus Configuration File (URL)", priority=1)
    public static final ConfigKey CUMULUS_RDF_CONFIG_URL = ConfigKeys.newConfigKey(
        "cumulus.config.url", "URL for the YAML configuration file for CumulusRDF", "classpath://cumulus.yaml");

    @CatalogConfig(label="Cassandra Thrift Port", priority=1)
    public static final ConfigKey CASSANDRA_THRIFT_PORT = ConfigKeys.newConfigKey(
        "cumulus.cassandra.port", "Port to contact the Cassandra cluster on", 9160);

    @CatalogConfig(label="Cassandra Cluster Size", priority=1)
    public static final ConfigKey CASSANDRA_CLUSTER_SIZE = ConfigKeys.newConfigKey(
        "cumulus.cassandra.cluster.size", "Initial size of the Cassandra cluster", 2);

    @CatalogConfig(label="Multi-region Fabric", priority=1)
    public static final ConfigKey MULTI_REGION_FABRIC = ConfigKeys.newConfigKey(
        "cumulus.cassandra.fabric", "Deploy a multi-region Cassandra fabric", false);

    // TODO Fails when given two locations
    // public static final String DEFAULT_LOCATIONS = "[ jclouds:aws-ec2:us-east-1,jclouds:rackspace-cloudservers-uk ]";
    public static final String DEFAULT_LOCATIONS = "jclouds:aws-ec2:us-east-1";

    private Effector cumulusConfig = Effectors.effector(Void.class, "cumulusConfig")
            .description("Configure the CumulusRDF web application")
            .buildAbstract();

    private Entity cassandra;
    private TomcatServer webapp;
    private HostAndPort endpoint;
    private final Object endpointMutex = new Object();

    /**
     * Create the application entities:
     * 
    *
  • A {@link CassandraFabric} of {@link CassandraDatacenter}s containing {@link CassandraNode}s *
  • A {@link TomcatServer} *
*/ @Override public void initApp() { // Cassandra cluster EntitySpec clusterSpec = EntitySpec.create(CassandraDatacenter.class) .configure(CassandraDatacenter.MEMBER_SPEC, EntitySpec.create(CassandraNode.class) //FIXME can probably use JMXMP_AND_RMI now, to deploy to GCE and elsewhere .configure(UsesJmx.JMX_AGENT_MODE, UsesJmx.JmxAgentModes.JMX_RMI_CUSTOM_AGENT) .configure(UsesJmx.JMX_PORT, PortRanges.fromString("11099+")) .configure(UsesJmx.RMI_REGISTRY_PORT, PortRanges.fromString("9001+")) .configure(CassandraNode.THRIFT_PORT, PortRanges.fromInteger(getConfig(CASSANDRA_THRIFT_PORT))) .enricher(EnricherSpec.create(ServiceFailureDetector.class)) .policy(PolicySpec.create(ServiceRestarter.class) .configure(ServiceRestarter.FAILURE_SENSOR_TO_MONITOR, ServiceFailureDetector.ENTITY_FAILED))) .policy(PolicySpec.create(ServiceReplacer.class) .configure(ServiceReplacer.FAILURE_SENSOR_TO_MONITOR, ServiceRestarter.ENTITY_RESTART_FAILED)); if (getConfig(MULTI_REGION_FABRIC)) { cassandra = addChild(EntitySpec.create(CassandraFabric.class) .configure(CassandraDatacenter.CLUSTER_NAME, "Brooklyn") .configure(CassandraDatacenter.INITIAL_SIZE, getConfig(CASSANDRA_CLUSTER_SIZE)) // per location .configure(CassandraDatacenter.ENDPOINT_SNITCH_NAME, "org.apache.brooklyn.entity.nosql.cassandra.customsnitch.MultiCloudSnitch") .configure(CassandraNode.CUSTOM_SNITCH_JAR_URL, "classpath://org/apache/brooklyn/entity/nosql/cassandra/cassandra-multicloud-snitch.jar") .configure(CassandraFabric.MEMBER_SPEC, clusterSpec)); } else { cassandra = addChild(EntitySpec.create(clusterSpec) .configure(CassandraDatacenter.CLUSTER_NAME, "Brooklyn") .configure(CassandraDatacenter.INITIAL_SIZE, getConfig(CASSANDRA_CLUSTER_SIZE))); } // Tomcat web-app server webapp = addChild(EntitySpec.create(TomcatServer.class) .configure(UsesJmx.JMX_AGENT_MODE, UsesJmx.JmxAgentModes.JMX_RMI_CUSTOM_AGENT) .configure(UsesJmx.JMX_PORT, PortRanges.fromString("11099+")) .configure(UsesJmx.RMI_REGISTRY_PORT, PortRanges.fromString("9001+")) .configure(JavaWebAppService.ROOT_WAR, "https://cumulusrdf.googlecode.com/svn/wiki/downloads/cumulusrdf-1.0.1.war") .configure(UsesJava.JAVA_SYSPROPS, MutableMap.of("cumulusrdf.config-file", "/tmp/cumulus.yaml"))); // Add an effector to tomcat to reconfigure with a new YAML config file ((EntityInternal) webapp).getMutableEntityType().addEffector(cumulusConfig, new EffectorBody() { @Override public Void call(ConfigBag parameters) { // Process the YAML template given in the application config String url = Entities.getRequiredUrlConfig(CumulusRDFApplication.this, CUMULUS_RDF_CONFIG_URL); Map config; synchronized (endpointMutex) { config = MutableMap.of("cassandraHostname", endpoint.getHostText(), "cassandraThriftPort", endpoint.getPort()); } String contents = TemplateProcessor.processTemplateContents(ResourceUtils.create(CumulusRDFApplication.this).getResourceAsString(url), config); // Copy the file contents to the remote machine return DynamicTasks.queue(SshEffectorTasks.put("/tmp/cumulus.yaml").contents(contents)).get(); } }); // Listen for HOSTNAME changes from the Cassandra fabric to show at least one node is available subscriptions().subscribe(cassandra, CassandraDatacenter.HOSTNAME, new SensorEventListener() { @Override public void onEvent(SensorEvent event) { if (Strings.isNonBlank(event.getValue())) { synchronized (endpointMutex) { String hostname = Entities.submit(CumulusRDFApplication.this, DependentConfiguration.attributeWhenReady(cassandra, CassandraDatacenter.HOSTNAME)).getUnchecked(); Integer thriftPort = Entities.submit(CumulusRDFApplication.this, DependentConfiguration.attributeWhenReady(cassandra, CassandraDatacenter.THRIFT_PORT)).getUnchecked(); HostAndPort current = HostAndPort.fromParts(hostname, thriftPort); // Check if the cluster access point has changed if (!current.equals(endpoint)) { log.info("Setting cluster endpoint to {}", current.toString()); endpoint = current; // Reconfigure the CumulusRDF application and restart tomcat if necessary webapp.invoke(cumulusConfig, MutableMap.of()); if (webapp.getAttribute(Startable.SERVICE_UP)) { webapp.restart(); } } } } } }); } /** * Controls the startup locations for the webapp and the cassandra fabric. * * @see AbstractApplication#start(Collection) */ @Override public void start(Collection locations) { addLocations(locations); // The web application only needs to run in one location, use the first // TODO use a multi-region web cluster Collection first = MutableList.copyOf(Iterables.limit(locations, 1)); ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING); try { Entities.invokeEffector(this, cassandra, Startable.START, MutableMap.of("locations", locations)).getUnchecked(); Entities.invokeEffector(this, webapp, Startable.START, MutableMap.of("locations", first)).getUnchecked(); } catch (Exception e) { throw Exceptions.propagate(e); } finally { ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING); } log.info("Started CumulusRDF in " + locations); } public static void main(String[] argv) { List args = Lists.newArrayList(argv); String port = CommandLineUtil.getCommandLineOption(args, "--port", "8081+"); String locations = CommandLineUtil.getCommandLineOption(args, "--locations", DEFAULT_LOCATIONS); BrooklynLauncher launcher = BrooklynLauncher.newInstance() .application(EntitySpec.create(StartableApplication.class, CumulusRDFApplication.class).displayName("CumulusRDF application using Cassandra")) .webconsolePort(port) .locations(Strings.isBlank(locations) ? ImmutableList.of() : JavaStringEscapes.unwrapJsonishListIfPossible(locations)) .start(); Entities.dumpInfo(launcher.getApplications()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy