http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/nosql/pom.xml ---------------------------------------------------------------------- diff --cc brooklyn-library/software/nosql/pom.xml index 0000000,4fd2d18..32dbeb6 mode 000000,100644..100644 --- a/brooklyn-library/software/nosql/pom.xml +++ b/brooklyn-library/software/nosql/pom.xml @@@ -1,0 -1,291 +1,300 @@@ + <?xml version="1.0" encoding="UTF-8"?> + <!-- + 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. + --> + <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <artifactId>brooklyn-software-nosql</artifactId> + <packaging>jar</packaging> + <name>Brooklyn NoSQL Data Store Software Entities</name> + <description> + Brooklyn entities for NoSQL data store software entities + </description> + + <parent> + <groupId>org.apache.brooklyn</groupId> + <artifactId>brooklyn-parent</artifactId> + <version>0.9.0-SNAPSHOT</version> <!-- BROOKLYN_VERSION --> + <relativePath>../../parent/pom.xml</relativePath> + </parent> + + <dependencies> + <dependency> + <groupId>org.apache.brooklyn</groupId> + <artifactId>brooklyn-software-base</artifactId> + <version>${project.version}</version> + <exclusions> + <!-- Dependency versions mismatch between transitive dependencies, declare explicitly --> + <exclusion> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + </exclusion> + <exclusion> + <groupId>commons-codec</groupId> + <artifactId>commons-codec</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.brooklyn</groupId> + <artifactId>brooklyn-software-webapp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.brooklyn</groupId> + <!-- just to access DatastoreMixins --> + <artifactId>brooklyn-software-database</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.brooklyn</groupId> + <artifactId>brooklyn-api</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.brooklyn</groupId> + <artifactId>brooklyn-core</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.brooklyn</groupId> + <artifactId>brooklyn-policy</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.brooklyn</groupId> + <artifactId>brooklyn-utils-common</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </dependency> + <dependency> + <groupId>com.google.code.findbugs</groupId> + <artifactId>jsr305</artifactId> + </dependency> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + + <!-- for mongodb sensors --> + <dependency> + <groupId>org.mongodb</groupId> + <artifactId>mongo-java-driver</artifactId> + <version>${mongodb.version}</version> + </dependency> + + <!-- for redis testing --> + <dependency> + <groupId>redis.clients</groupId> + <artifactId>jedis</artifactId> + <version>${redis.version}</version> + <scope>test</scope> + </dependency> + + <!-- for cassandra testing --> + <dependency> + <groupId>com.netflix.astyanax</groupId> + <artifactId>astyanax</artifactId> + <version>${astyanax.version}</version> + <scope>test</scope> + <exclusions> + <!-- Dependency versions mismatch between transitive dependencies, declare explicitly --> + <exclusion> + <artifactId>slf4j-log4j12</artifactId> + <groupId>org.slf4j</groupId> + </exclusion> + <exclusion> + <artifactId>log4j</artifactId> + <groupId>log4j</groupId> + </exclusion> + <exclusion> + <groupId>commons-codec</groupId> + <artifactId>commons-codec</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>${jclouds.groupId}.provider</groupId> + <artifactId>rackspace-cloudservers-uk</artifactId> + <version>${jclouds.version}</version> + <scope>test</scope> + </dependency> + + <!-- for couchdb testing --> + <dependency> + <groupId>com.google.code.jcouchdb</groupId> + <artifactId>jcouchdb</artifactId> + <version>${jcouchdb.version}</version> + <scope>test</scope> + <exclusions> + <!-- Dependency versions mismatch between transitive dependencies, declare explicitly --> + <exclusion> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + </exclusion> + <exclusion> + <groupId>commons-codec</groupId> + <artifactId>commons-codec</artifactId> + </exclusion> + <exclusion> + <artifactId>slf4j-log4j12</artifactId> + <groupId>org.slf4j</groupId> + </exclusion> + <exclusion> + <artifactId>log4j</artifactId> + <groupId>log4j</groupId> + </exclusion> + <exclusion> + <artifactId>httpcore</artifactId> + <groupId>org.apache.httpcomponents</groupId> + </exclusion> + </exclusions> + </dependency> + ++ <!-- for hazelcast testing --> ++ <dependency> ++ <groupId>com.hazelcast</groupId> ++ <artifactId>hazelcast-client</artifactId> ++ <version>${hazelcast.version}</version> ++ <scope>test</scope> ++ </dependency> ++ + <!-- for solr testing --> + <dependency> + <groupId>org.apache.solr</groupId> + <artifactId>solr-solrj</artifactId> + <version>${solr.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>${jclouds.groupId}.provider</groupId> + <artifactId>aws-ec2</artifactId> + <version>${jclouds.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.testng</groupId> + <artifactId>testng</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.brooklyn</groupId> + <artifactId>brooklyn-test-support</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.brooklyn</groupId> + <artifactId>brooklyn-core</artifactId> + <version>${project.version}</version><!--$NO-MVN-MAN-VER$--> + <classifier>tests</classifier> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.brooklyn</groupId> + <artifactId>brooklyn-software-base</artifactId> + <version>${project.version}</version> + <classifier>tests</classifier> + <scope>test</scope> + </dependency> + <!-- bring in jclouds for testing --> + <dependency> + <groupId>org.apache.brooklyn</groupId> + <artifactId>brooklyn-locations-jclouds</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + + <!-- Transitive dependencies, declared explicitly due to version mismatch --> + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + <version>${commons-logging.version}</version> + </dependency> + <dependency> + <groupId>commons-codec</groupId> + <artifactId>commons-codec</artifactId> + <version>${commons-codec.version}</version> + </dependency> + </dependencies> + <build> + <pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.rat</groupId> + <artifactId>apache-rat-plugin</artifactId> + <configuration> + <excludes combine.children="append"> + <!-- + Configuration artifacts (for installations) are based on templated defaults for + the given components. These are files "without any degree of creativity" from the + perspective of the Brooklyn/Apache contribution. + --> + <exclude>src/main/resources/org/apache/brooklyn/entity/nosql/cassandra/cassandra-1.2.yaml</exclude> + <exclude>src/main/resources/org/apache/brooklyn/entity/nosql/cassandra/cassandra-2.0.yaml</exclude> + <exclude>src/main/resources/org/apache/brooklyn/entity/nosql/cassandra/cassandra-rackdc.properties</exclude> + <exclude>src/main/resources/org/apache/brooklyn/entity/nosql/couchdb/couch.ini</exclude> + <exclude>src/main/resources/org/apache/brooklyn/entity/nosql/couchdb/couch.uri</exclude> ++ <exclude>src/main/resources/org/apache/brooklyn/entity/nosql/hazelcast/hazelcast-brooklyn.xml</exclude> + <exclude>src/main/resources/org/apache/brooklyn/entity/nosql/mongodb/default.conf</exclude> + <exclude>src/main/resources/org/apache/brooklyn/entity/nosql/mongodb/default-mongod.conf</exclude> + <exclude>src/test/resources/test-mongodb.conf</exclude> + <exclude>src/test/resources/test-mongodb-configserver.conf</exclude> + <exclude>src/test/resources/test-mongodb-router.conf</exclude> + <exclude>src/test/resources/mongodb-keyfile</exclude> + <exclude>src/main/resources/org/apache/brooklyn/entity/nosql/redis/redis.conf</exclude> + <exclude>src/main/resources/org/apache/brooklyn/entity/nosql/redis/slave.conf</exclude> + <exclude>src/main/resources/org/apache/brooklyn/entity/nosql/riak/app.config</exclude> + <exclude>src/main/resources/org/apache/brooklyn/entity/nosql/riak/vm.args</exclude> + <exclude>src/main/resources/org/apache/brooklyn/entity/nosql/riak/riak.conf</exclude> + <exclude>src/main/resources/org/apache/brooklyn/entity/nosql/riak/riak-mac.conf</exclude> + <exclude>src/main/resources/org/apache/brooklyn/entity/nosql/solr/solr.xml</exclude> + + <!-- + The source code for cassandra-multicloud-snitch.jar is in sandbox/cassandra-multicloud-snitch. + This snitch handles Cassandra datacenters in different cloud providers. + The source will be contributed to the Cassandra project; when it is available in the + Cassandra distro (and when we don't want to give backwards compatibility support for + older Cassandra versions), then we can delete it from Brooklyn. + --> + <exclude>**/src/main/resources/brooklyn/entity/nosql/cassandra/cassandra-multicloud-snitch.jar</exclude> + + <!-- + This is a trivial Solr example, used for testing that a simple definition is deployed + correctly. It is "without any degree of creativity". It is stored as a binary tgz, rather + than us generating the tgz as part of the build, to keep the build process and testing + simpler. + --> + <exclude>**/src/test/resources/solr/example.tgz</exclude> + </excludes> + </configuration> + </plugin> + </plugins> + </pluginManagement> + </build> + + </project>
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeImpl.java ---------------------------------------------------------------------- diff --cc brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeImpl.java index 0000000,14867de..933e818 mode 000000,100644..100644 --- a/brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeImpl.java +++ b/brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeImpl.java @@@ -1,0 -1,108 +1,109 @@@ + /* + * 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.entity.nosql.couchdb; + + import java.util.concurrent.TimeUnit; + + import javax.annotation.Nullable; + ++import org.apache.brooklyn.core.entity.EntityFunctions; + import org.apache.brooklyn.entity.software.base.SoftwareProcessImpl; + import org.apache.brooklyn.entity.webapp.JavaWebAppSoftwareProcessImpl; + import org.apache.brooklyn.entity.webapp.WebAppServiceMethods; + import org.apache.brooklyn.feed.http.HttpFeed; + import org.apache.brooklyn.feed.http.HttpPollConfig; + import org.apache.brooklyn.feed.http.HttpValueFunctions; + import org.apache.brooklyn.util.core.flags.TypeCoercions; + import org.apache.brooklyn.util.guava.Functionals; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + + import com.google.common.base.Function; + import com.google.common.base.Functions; + + /** + * Implementation of {@link CouchDBNode}. + */ + public class CouchDBNodeImpl extends SoftwareProcessImpl implements CouchDBNode { + + private static final Logger log = LoggerFactory.getLogger(CouchDBNodeImpl.class); + + public CouchDBNodeImpl() { + } + + public Integer getHttpPort() { return getAttribute(CouchDBNode.HTTP_PORT); } + public Integer getHttpsPort() { return getAttribute(CouchDBNode.HTTPS_PORT); } + public String getClusterName() { return getAttribute(CouchDBNode.CLUSTER_NAME); } + + @Override + public Class<CouchDBNodeDriver> getDriverInterface() { + return CouchDBNodeDriver.class; + } + + private volatile HttpFeed httpFeed; + + @Override + protected void connectSensors() { + super.connectSensors(); + + connectServiceUpIsRunning(); + + boolean retrieveUsageMetrics = getConfig(RETRIEVE_USAGE_METRICS); + + httpFeed = HttpFeed.builder() + .entity(this) + .period(500, TimeUnit.MILLISECONDS) + .baseUri(String.format("http://%s:%d/_stats", getAttribute(HOSTNAME), getHttpPort())) + .poll(new HttpPollConfig<Integer>(REQUEST_COUNT) + .onSuccess(HttpValueFunctions.jsonContents(new String[] { "httpd", "requests", "count" }, Integer.class)) - .onFailureOrException(Functions.constant(-1)) ++ .onFailureOrException(EntityFunctions.attribute(this, REQUEST_COUNT)) + .enabled(retrieveUsageMetrics)) + .poll(new HttpPollConfig<Integer>(ERROR_COUNT) + .onSuccess(HttpValueFunctions.jsonContents(new String[] { "httpd_status_codes", "404", "count" }, Integer.class)) + .onFailureOrException(Functions.constant(-1)) + .enabled(retrieveUsageMetrics)) + .poll(new HttpPollConfig<Integer>(TOTAL_PROCESSING_TIME) + .onSuccess(HttpValueFunctions.jsonContents(new String[] { "couchdb", "request_time", "count" }, Integer.class)) + .onFailureOrException(Functions.constant(-1)) + .enabled(retrieveUsageMetrics)) + .poll(new HttpPollConfig<Integer>(MAX_PROCESSING_TIME) + .onSuccess(Functionals.chain(HttpValueFunctions.jsonContents(new String[] { "couchdb", "request_time", "max" }, Double.class), TypeCoercions.function(Integer.class))) + .onFailureOrException(Functions.constant(-1)) + .enabled(retrieveUsageMetrics)) + .build(); + + WebAppServiceMethods.connectWebAppServerPolicies(this); + } + + @Override + public void disconnectSensors() { + super.disconnectSensors(); + if (httpFeed != null) httpFeed.stop(); + disconnectServiceUpIsRunning(); + } + + /** @see JavaWebAppSoftwareProcessImpl#postStop() */ + @Override + protected void postStop() { + super.postStop(); + // zero our workrate derived workrates. + sensors().set(REQUESTS_PER_SECOND_LAST, 0D); + sensors().set(REQUESTS_PER_SECOND_IN_WINDOW, 0D); + } + } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastCluster.java ---------------------------------------------------------------------- diff --cc brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastCluster.java index 0000000,0000000..3962fd1 new file mode 100644 --- /dev/null +++ b/brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastCluster.java @@@ -1,0 -1,0 +1,59 @@@ ++/* ++ * 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.entity.nosql.hazelcast; ++ ++import java.util.List; ++ ++import com.google.common.reflect.TypeToken; ++ ++import org.apache.brooklyn.api.catalog.Catalog; ++import org.apache.brooklyn.api.entity.ImplementedBy; ++import org.apache.brooklyn.api.sensor.AttributeSensor; ++import org.apache.brooklyn.util.core.flags.SetFromFlag; ++ ++import org.apache.brooklyn.config.ConfigKey; ++import org.apache.brooklyn.core.config.ConfigKeys; ++import org.apache.brooklyn.core.sensor.BasicAttributeSensorAndConfigKey; ++import org.apache.brooklyn.core.sensor.Sensors; ++import org.apache.brooklyn.entity.group.DynamicCluster; ++ ++/** ++ * A cluster of {@link HazelcastNode}s based on {@link DynamicCluster}. ++ */ ++@Catalog(name="Hazelcast Cluster", description="Hazelcast is a clustering and highly scalable data distribution platform for Java.") ++ ++@ImplementedBy(HazelcastClusterImpl.class) ++public interface HazelcastCluster extends DynamicCluster { ++ ++ @SetFromFlag("clusterName") ++ BasicAttributeSensorAndConfigKey<String> CLUSTER_NAME = new BasicAttributeSensorAndConfigKey<String>(String.class, ++ "hazelcast.cluster.name", "Name of the Hazelcast cluster", "HazelcastCluster"); ++ ++ @SetFromFlag("clusterPassword") ++ ConfigKey<String> CLUSTER_PASSWORD = ++ ConfigKeys.newStringConfigKey("hazelcast.cluster.password", "Hazelcast cluster password."); ++ ++ @SuppressWarnings("serial") ++ AttributeSensor<List<String>> PUBLIC_CLUSTER_NODES = Sensors.newSensor(new TypeToken<List<String>>() {}, ++ "hazelcast.cluster.public.nodes", "List of public addresses of all nodes in the cluster"); ++ ++ String getClusterName(); ++ ++ String getClusterPassword(); ++} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastClusterImpl.java ---------------------------------------------------------------------- diff --cc brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastClusterImpl.java index 0000000,0000000..854c0a3 new file mode 100644 --- /dev/null +++ b/brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastClusterImpl.java @@@ -1,0 -1,0 +1,125 @@@ ++/* ++ * 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.entity.nosql.hazelcast; ++ ++import java.util.Collection; ++import java.util.List; ++import java.util.concurrent.atomic.AtomicInteger; ++ ++import org.slf4j.Logger; ++import org.slf4j.LoggerFactory; ++ ++import com.google.common.collect.Lists; ++import org.apache.brooklyn.api.entity.Entity; ++ ++import org.apache.brooklyn.entity.group.AbstractMembershipTrackingPolicy; ++import org.apache.brooklyn.entity.group.DynamicClusterImpl; ++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.core.entity.Attributes; ++import org.apache.brooklyn.core.entity.EntityInternal; ++import org.apache.brooklyn.util.text.Strings; ++ ++public class HazelcastClusterImpl extends DynamicClusterImpl implements HazelcastCluster { ++ private static final Logger LOG = LoggerFactory.getLogger(HazelcastClusterImpl.class); ++ ++ private static final AtomicInteger nextMemberId = new AtomicInteger(0); ++ ++ @Override ++ protected EntitySpec<?> getMemberSpec() { ++ EntitySpec<?> spec = EntitySpec.create(config().get(HazelcastCluster.MEMBER_SPEC)); ++ ++ spec.configure(HazelcastNode.NODE_CLUSTER_NAME, config().get(HazelcastCluster.CLUSTER_NAME)); ++ spec.configure(HazelcastNode.GROUP_NAME, config().get(HazelcastCluster.CLUSTER_NAME)); ++ ++ if (LOG.isInfoEnabled()) { ++ LOG.info("Cluster name : {} : used as a group name", getConfig(HazelcastNode.GROUP_NAME)); ++ } ++ ++ spec.configure(HazelcastNode.GROUP_PASSWORD, getClusterPassword()); ++ ++ return spec; ++ } ++ ++ @Override ++ public void init() { ++ super.init(); ++ ++ String clusterPassword = getClusterPassword(); ++ ++ if (Strings.isBlank(clusterPassword)) { ++ if (LOG.isInfoEnabled()) { ++ LOG.info(this + " cluster password not provided for " + CLUSTER_PASSWORD.getName() + " : generating random password"); ++ } ++ config().set(CLUSTER_PASSWORD, Strings.makeRandomId(12)); ++ } ++ ++ policies().add(PolicySpec.create(MemberTrackingPolicy.class) ++ .displayName("Hazelcast members tracker") ++ .configure("group", this)); ++ } ++ ++ public static class MemberTrackingPolicy extends AbstractMembershipTrackingPolicy { ++ @Override ++ protected void onEntityChange(Entity member) { ++ } ++ ++ @Override ++ protected void onEntityAdded(Entity member) { ++ if (member.getAttribute(HazelcastNode.NODE_NAME) == null) { ++ ((EntityInternal) member).sensors().set(HazelcastNode.NODE_NAME, "hazelcast-" + nextMemberId.incrementAndGet()); ++ if (LOG.isInfoEnabled()) { ++ LOG.info("Node {} added to the cluster", member); ++ } ++ } ++ } ++ ++ @Override ++ protected void onEntityRemoved(Entity member) { ++ } ++ }; ++ ++ @Override ++ public String getClusterName() { ++ return getConfig(CLUSTER_NAME); ++ } ++ ++ @Override ++ public String getClusterPassword() { ++ return getConfig(CLUSTER_PASSWORD); ++ } ++ ++ @Override ++ protected void initEnrichers() { ++ super.initEnrichers(); ++ ++ } ++ ++ @Override ++ public void start(Collection<? extends Location> locations) { ++ super.start(locations); ++ ++ List<String> clusterNodes = Lists.newArrayList(); ++ for (Entity member : getMembers()) { ++ clusterNodes.add(member.getAttribute(Attributes.ADDRESS)); ++ } ++ sensors().set(PUBLIC_CLUSTER_NODES, clusterNodes); ++ } ++} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastNode.java ---------------------------------------------------------------------- diff --cc brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastNode.java index 0000000,0000000..768179c new file mode 100644 --- /dev/null +++ b/brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastNode.java @@@ -1,0 -1,0 +1,101 @@@ ++/* ++ * 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.entity.nosql.hazelcast; ++ ++import org.apache.brooklyn.api.catalog.Catalog; ++import org.apache.brooklyn.api.entity.ImplementedBy; ++import org.apache.brooklyn.util.core.flags.SetFromFlag; ++import org.apache.brooklyn.config.ConfigKey; ++import org.apache.brooklyn.core.config.ConfigKeys; ++import org.apache.brooklyn.core.location.PortRanges; ++import org.apache.brooklyn.core.sensor.BasicAttributeSensorAndConfigKey; ++import org.apache.brooklyn.core.sensor.PortAttributeSensorAndConfigKey; ++import org.apache.brooklyn.core.sensor.BasicAttributeSensorAndConfigKey.StringAttributeSensorAndConfigKey; ++import org.apache.brooklyn.entity.software.base.SoftwareProcess; ++import org.apache.brooklyn.entity.java.UsesJava; ++import org.apache.brooklyn.entity.java.UsesJmx; ++import org.apache.brooklyn.util.javalang.JavaClassNames; ++ ++/** ++ * An {@link brooklyn.entity.Entity} that represents an Hazelcast node ++ */ ++@Catalog(name="Hazelcast Node", description="Hazelcast is a clustering and highly scalable data distribution platform for Java.") ++ ++@ImplementedBy(HazelcastNodeImpl.class) ++public interface HazelcastNode extends SoftwareProcess, UsesJava, UsesJmx { ++ @SetFromFlag("version") ++ ConfigKey<String> SUGGESTED_VERSION = ConfigKeys.newConfigKeyWithDefault(SoftwareProcess.SUGGESTED_VERSION, "3.5.4"); ++ ++ @SetFromFlag("downloadUrl") ++ BasicAttributeSensorAndConfigKey<String> DOWNLOAD_URL = new BasicAttributeSensorAndConfigKey<String>( ++ SoftwareProcess.DOWNLOAD_URL, "https://repo1.maven.org/maven2/com/hazelcast/hazelcast/${version}/hazelcast-${version}.jar"); ++ ++ @SetFromFlag("configTemplateUrl") ++ ConfigKey<String> CONFIG_TEMPLATE_URL = ConfigKeys.newStringConfigKey( ++ "hazelcast.node.config.templateUrl", "Template file (in freemarker format) for the Hazelcat config file", ++ JavaClassNames.resolveClasspathUrl(HazelcastNode.class, "hazelcast-brooklyn.xml")); ++ ++ @SetFromFlag("configFileName") ++ ConfigKey<String> CONFIG_FILE_NAME = ConfigKeys.newStringConfigKey( ++ "hazelcast.node.config.fileName", "Name of the Hazelcast config file", "hazelcast.xml"); ++ ++ @SetFromFlag("nodeName") ++ StringAttributeSensorAndConfigKey NODE_NAME = new StringAttributeSensorAndConfigKey("hazelcast.node.name", ++ "Node name (or randomly selected if not set", null); ++ ++ @SetFromFlag("nodeHeapMemorySize") ++ ConfigKey<String> NODE_HEAP_MEMORY_SIZE = ConfigKeys.newStringConfigKey( ++ "hazelcast.node.heap.memory.size", "Node's heap memory size (-Xmx and -Xms) in megabytes. Default: 256m", "256m"); ++ ++ @SetFromFlag("nodePort") ++ PortAttributeSensorAndConfigKey NODE_PORT = new PortAttributeSensorAndConfigKey("hazelcast.node.port", "Hazelcast communication port", PortRanges.fromString("5701+")); ++ ++ @SetFromFlag("nodeClusterName") ++ BasicAttributeSensorAndConfigKey<String> NODE_CLUSTER_NAME = new BasicAttributeSensorAndConfigKey<String>(String.class, ++ "hazelcast.node.cluster.name", "Name of the Hazelcast cluster which node is part of", ""); ++ ++ /** ++ * Specifies the group name in the configuration file. Each Hazelcast cluster has a separate group. ++ */ ++ @SetFromFlag("groupName") ++ ConfigKey<String> GROUP_NAME = ConfigKeys.newStringConfigKey("hazelcast.group.name", ++ "Group name", "brooklyn"); ++ ++ @SetFromFlag("groupPassword") ++ ConfigKey<String> GROUP_PASSWORD = ConfigKeys.newStringConfigKey("hazelcast.group.password", ++ "Group password", "brooklyn"); ++ ++ String getNodeName(); ++ ++ Integer getNodePort(); ++ ++ String getGroupName(); ++ ++ String getGroupPassword(); ++ ++ String getHostname(); ++ ++ String getHostAddress(); ++ ++ String getPrivateIpAddress(); ++ ++ String getListenAddress(); ++ ++ String getHeapMemorySize(); ++} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastNodeDriver.java ---------------------------------------------------------------------- diff --cc brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastNodeDriver.java index 0000000,0000000..8cf1e0c new file mode 100644 --- /dev/null +++ b/brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastNodeDriver.java @@@ -1,0 -1,0 +1,25 @@@ ++/* ++ * 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.entity.nosql.hazelcast; ++ ++import org.apache.brooklyn.entity.software.base.SoftwareProcessDriver; ++ ++public interface HazelcastNodeDriver extends SoftwareProcessDriver { ++ ++} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastNodeImpl.java ---------------------------------------------------------------------- diff --cc brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastNodeImpl.java index 0000000,0000000..6d13b74 new file mode 100644 --- /dev/null +++ b/brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastNodeImpl.java @@@ -1,0 -1,0 +1,146 @@@ ++/* ++ * 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.entity.nosql.hazelcast; ++ ++import java.net.URI; ++import java.util.concurrent.TimeUnit; ++ ++import org.slf4j.Logger; ++import org.slf4j.LoggerFactory; ++import org.apache.brooklyn.core.entity.Attributes; ++import org.apache.brooklyn.core.location.access.BrooklynAccessUtils; ++import org.apache.brooklyn.entity.software.base.SoftwareProcessImpl; ++import org.apache.brooklyn.feed.http.HttpFeed; ++import org.apache.brooklyn.feed.http.HttpPollConfig; ++import org.apache.brooklyn.feed.http.HttpValueFunctions; ++import org.apache.brooklyn.util.text.Strings; ++ ++import com.google.common.base.Functions; ++import com.google.common.net.HostAndPort; ++ ++public class HazelcastNodeImpl extends SoftwareProcessImpl implements HazelcastNode { ++ ++ private static final Logger LOG = LoggerFactory.getLogger(HazelcastNodeImpl.class); ++ ++ HttpFeed httpFeed; ++ ++ @Override ++ public Class<HazelcastNodeDriver> getDriverInterface() { ++ return HazelcastNodeDriver.class; ++ } ++ ++ @Override ++ protected void connectSensors() { ++ super.connectSensors(); ++ ++ if (LOG.isDebugEnabled()) { ++ LOG.debug("Connecting sensors for node: {} ", getAttribute(Attributes.HOSTNAME)); ++ } ++ ++ HostAndPort hp = BrooklynAccessUtils.getBrooklynAccessibleAddress(this, getNodePort()); ++ ++ String nodeUri = String.format("http://%s:%d/hazelcast/rest/cluster", hp.getHostText(), hp.getPort()); ++ sensors().set(Attributes.MAIN_URI, URI.create(nodeUri)); ++ ++ if (LOG.isDebugEnabled()) { ++ LOG.debug("Node {} is using {} as a main URI", this, nodeUri); ++ } ++ ++ httpFeed = HttpFeed.builder() ++ .entity(this) ++ .period(3000, TimeUnit.MILLISECONDS) ++ .baseUri(nodeUri) ++ .poll(new HttpPollConfig<Boolean>(SERVICE_UP) ++ .onSuccess(HttpValueFunctions.responseCodeEquals(200)) ++ .onFailureOrException(Functions.constant(false))) ++ .build(); ++ } ++ ++ @Override ++ protected void disconnectSensors() { ++ if (httpFeed != null) { ++ httpFeed.stop(); ++ } ++ ++ if (LOG.isDebugEnabled()) { ++ LOG.debug("Disconnecting sensors for node: {} ", getAttribute(Attributes.HOSTNAME)); ++ } ++ ++ super.disconnectSensors(); ++ disconnectServiceUpIsRunning(); ++ } ++ ++ ++ @Override ++ public String getGroupName() { ++ return getConfig(HazelcastNode.GROUP_NAME); ++ } ++ ++ @Override ++ public String getGroupPassword() { ++ return getConfig(HazelcastNode.GROUP_PASSWORD); ++ } ++ ++ @Override ++ public String getNodeName() { ++ return getAttribute(HazelcastNode.NODE_NAME); ++ } ++ ++ @Override ++ public Integer getNodePort() { ++ return getAttribute(HazelcastNode.NODE_PORT); ++ } ++ ++ @Override ++ public String getHostname() { ++ return getAttribute(HOSTNAME); ++ } ++ ++ @Override ++ public String getHostAddress() { ++ return getAttribute(ADDRESS); ++ } ++ ++ @Override ++ public String getPrivateIpAddress() { ++ return getAttribute(SUBNET_ADDRESS); ++ } ++ ++ @Override ++ public String getListenAddress() { ++ String listenAddress = getPrivateIpAddress(); ++ ++ if (Strings.isBlank(listenAddress)) { ++ listenAddress = getAttribute(ADDRESS); ++ } ++ ++ if (LOG.isInfoEnabled()) { ++ LOG.info("Node {} is listening on {}", this, listenAddress); ++ } ++ ++ return listenAddress; ++ } ++ ++ ++ @Override ++ public String getHeapMemorySize() { ++ return getConfig(HazelcastNode.NODE_HEAP_MEMORY_SIZE); ++ } ++ ++} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastNodeSshDriver.java ---------------------------------------------------------------------- diff --cc brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastNodeSshDriver.java index 0000000,0000000..2a9b9c5 new file mode 100644 --- /dev/null +++ b/brooklyn-library/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastNodeSshDriver.java @@@ -1,0 -1,0 +1,164 @@@ ++/* ++ * 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.entity.nosql.hazelcast; ++ ++import static java.lang.String.format; ++ ++import java.util.List; ++ ++import org.slf4j.Logger; ++import org.slf4j.LoggerFactory; ++ ++import org.apache.brooklyn.api.entity.Entity; ++import org.apache.brooklyn.core.entity.Entities; ++ ++import org.apache.brooklyn.entity.java.JavaSoftwareProcessSshDriver; ++import org.apache.brooklyn.location.ssh.SshMachineLocation; ++import org.apache.brooklyn.util.collections.MutableMap; ++import org.apache.brooklyn.util.os.Os; ++import org.apache.brooklyn.util.ssh.BashCommands; ++import org.apache.brooklyn.util.text.Strings; ++ ++import com.google.common.base.Joiner; ++import com.google.common.collect.ImmutableList; ++import com.google.common.collect.Lists; ++ ++public class HazelcastNodeSshDriver extends JavaSoftwareProcessSshDriver implements HazelcastNodeDriver { ++ ++ private static final Logger LOG = LoggerFactory.getLogger(HazelcastNodeSshDriver.class); ++ ++ public HazelcastNodeSshDriver(HazelcastNodeImpl entity, SshMachineLocation machine) { ++ super(entity, machine); ++ } ++ ++ @Override ++ public void preInstall() { ++ resolver = Entities.newDownloader(this); ++ } ++ ++ @Override ++ public void install() { ++ List<String> urls = resolver.getTargets(); ++ String saveAs = resolver.getFilename(); ++ ++ List<String> commands = ImmutableList.<String>builder() ++ .add(BashCommands.installJavaLatestOrWarn()) ++ .addAll(BashCommands.commandsToDownloadUrlsAs(urls, saveAs)) ++ .build(); ++ ++ newScript(INSTALLING).body.append(commands).execute(); ++ } ++ ++ @Override ++ public void customize() { ++ if (LOG.isInfoEnabled()) { ++ LOG.info("Customizing {}", entity.getAttribute(HazelcastNode.NODE_NAME)); ++ } ++ ++ ImmutableList.Builder<String> commands = new ImmutableList.Builder<String>() ++ .add("mkdir -p lib conf log") ++ .add(String.format("cp %s/%s %s/lib/", getInstallDir(), resolver.getFilename(), getRunDir())); ++ ++ newScript(CUSTOMIZING) ++ .body.append(commands.build()) ++ .failOnNonZeroResultCode() ++ .execute(); ++ ++ copyTemplate(entity.getConfig(HazelcastNode.CONFIG_TEMPLATE_URL), Os.mergePathsUnix(getRunDir(), "conf", getConfigFileName())); ++ ++ } ++ ++ @Override ++ public void launch() { ++ ++ entity.sensors().set(HazelcastNode.PID_FILE, Os.mergePathsUnix(getRunDir(), PID_FILENAME)); ++ ++ String maxHeapMemorySize = getHeapMemorySize(); ++ ++ if (LOG.isInfoEnabled()) { ++ LOG.info("Launching {} with heap memory of {}", entity, maxHeapMemorySize); ++ } ++ ++ // Setting initial heap size (Xms) size to match max heap size (Xms) at first ++ String initialHeapMemorySize = maxHeapMemorySize; ++ ++ ImmutableList<String> commands = ImmutableList.<String>builder() ++ .add(format("nohup java -cp ./lib/%s", resolver.getFilename())) ++ .add(format("-Xmx%s -Xms%s", maxHeapMemorySize, initialHeapMemorySize)) ++ .add(format("-Dhazelcast.config=./conf/%s", getConfigFileName())) ++ .add(format("com.hazelcast.core.server.StartServer >> %s 2>&1 </dev/null &", getLogFileLocation())) ++ .build(); ++ ++ newScript(MutableMap.of(USE_PID_FILE, true), LAUNCHING) ++ .updateTaskAndFailOnNonZeroResultCode() ++ .body.append(Joiner.on(" ").join(commands)) ++ .execute(); ++ } ++ ++ public String getConfigFileName() { ++ return entity.getConfig(HazelcastNode.CONFIG_FILE_NAME); ++ } ++ ++ public String getHeapMemorySize() { ++ return entity.getConfig(HazelcastNode.NODE_HEAP_MEMORY_SIZE); ++ } ++ ++ @Override ++ public boolean isRunning() { ++ return newScript(MutableMap.of(USE_PID_FILE, true), CHECK_RUNNING).execute() == 0; ++ } ++ ++ @Override ++ public void stop() { ++ newScript(MutableMap.of(USE_PID_FILE, true), STOPPING).execute(); ++ } ++ ++ @Override ++ public void kill() { ++ newScript(MutableMap.of(USE_PID_FILE, true), KILLING).execute(); ++ } ++ ++ public List<String> getHazelcastNodesList() { ++ List<String> result = Lists.newArrayList(); ++ ++ if (Strings.isBlank(entity.getAttribute(HazelcastNode.NODE_CLUSTER_NAME))) { ++ result.add(String.format("%s:%d", entity.getAttribute(HazelcastNode.SUBNET_ADDRESS), ++ entity.getAttribute(HazelcastNode.NODE_PORT))); ++ } else { ++ HazelcastCluster cluster = (HazelcastCluster) entity.getParent(); ++ ++ for (Entity member : cluster.getMembers()) { ++ String address = Entities.attributeSupplierWhenReady(member, HazelcastNode.SUBNET_ADDRESS).get(); ++ Integer port = Entities.attributeSupplierWhenReady(member, HazelcastNode.NODE_PORT).get(); ++ String addressAndPort = String.format("%s:%d", address, port); ++ ++ if (LOG.isInfoEnabled()) { ++ LOG.info("Adding {} to the members' list of {}", addressAndPort, entity.getAttribute(HazelcastNode.NODE_NAME)); ++ } ++ result.add(addressAndPort); ++ } ++ } ++ return result; ++ } ++ ++ @Override ++ protected String getLogFileLocation() { ++ return Os.mergePathsUnix(getRunDir(),"/log/out.log"); ++ } ++} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/nosql/src/main/resources/org/apache/brooklyn/entity/nosql/hazelcast/hazelcast-brooklyn.xml ---------------------------------------------------------------------- diff --cc brooklyn-library/software/nosql/src/main/resources/org/apache/brooklyn/entity/nosql/hazelcast/hazelcast-brooklyn.xml index 0000000,0000000..2f4a263 new file mode 100644 --- /dev/null +++ b/brooklyn-library/software/nosql/src/main/resources/org/apache/brooklyn/entity/nosql/hazelcast/hazelcast-brooklyn.xml @@@ -1,0 -1,0 +1,64 @@@ ++[#ftl] ++<?xml version="1.0" encoding="UTF-8"?> ++ ++<hazelcast xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ++ xsi:schemaLocation="http://www.hazelcast.com/schema/config ++ http://www.hazelcast.com/schema/config/hazelcast-config-3.5.xsd" ++ xmlns="http://www.hazelcast.com/schema/config"> ++ ++ <properties> ++ <property name="hazelcast.discovery.enabled">true</property> ++ </properties> ++ ++ <group> ++ <name>${entity.groupName}</name> ++ <password>${entity.groupPassword}</password> ++ </group> ++ <management-center enabled="false">http://localhost:8080/mancenter</management-center> ++ <network> ++ <port auto-increment="true" port-count="100">${entity.nodePort?c}</port> ++ <outbound-ports> ++ <!-- ++ Allowed port range when connecting to other nodes. ++ 0 or * means use system provided port. ++ --> ++ <ports>0</ports> ++ </outbound-ports> ++ ++ <join> ++ <multicast enabled="false" /> ++ ++ <tcp-ip enabled="true"> ++ [#list driver.hazelcastNodesList as member] ++ <member>${member}</member> ++ [/#list] ++ </tcp-ip> ++ <aws enabled="false" /> ++ </join> ++ ++ <ssl enabled="false"/> ++ <socket-interceptor enabled="false"/> ++ ++ </network> ++ <partition-group enabled="false"/> ++ ++ <map name="default"> ++ <in-memory-format>BINARY</in-memory-format> ++ <backup-count>1</backup-count> ++ <async-backup-count>0</async-backup-count> ++ <time-to-live-seconds>0</time-to-live-seconds> ++ <max-idle-seconds>0</max-idle-seconds> ++ <eviction-policy>NONE</eviction-policy> ++ <max-size policy="PER_NODE">0</max-size> ++ <eviction-percentage>25</eviction-percentage> ++ <min-eviction-check-millis>100</min-eviction-check-millis> ++ <merge-policy>com.hazelcast.map.merge.PutIfAbsentMapMergePolicy</merge-policy> ++ </map> ++ ++ <serialization> ++ <portable-version>0</portable-version> ++ </serialization> ++ ++ <services enable-defaults="true"/> ++ ++</hazelcast> http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastClusterEc2LiveTest.java ---------------------------------------------------------------------- diff --cc brooklyn-library/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastClusterEc2LiveTest.java index 0000000,0000000..111ba9e new file mode 100644 --- /dev/null +++ b/brooklyn-library/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastClusterEc2LiveTest.java @@@ -1,0 -1,0 +1,47 @@@ ++/* ++ * 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.entity.nosql.hazelcast; ++ ++import org.slf4j.Logger; ++import org.slf4j.LoggerFactory; ++import org.testng.annotations.Test; ++ ++import org.apache.brooklyn.entity.AbstractEc2LiveTest; ++import org.apache.brooklyn.api.location.Location; ++ ++public class HazelcastClusterEc2LiveTest extends AbstractEc2LiveTest { ++ @SuppressWarnings("unused") ++ private static final Logger LOG = LoggerFactory.getLogger(HazelcastClusterEc2LiveTest.class); ++ ++ @Override ++ protected void doTest(Location loc) throws Exception { ++ HazelcastTestHelper.testHazelcastCluster(app, loc); ++ } ++ ++ @Test(enabled = false) ++ public void testDummy() { ++ } // Convince TestNG IDE integration that this really does have test methods ++ ++ ++ @Test(groups = {"Live", "Live-sanity"}) ++ @Override ++ public void test_CentOS_6_3() throws Exception { ++ super.test_CentOS_6_3(); ++ } ++} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastClusterNodeIntegrationTest.java ---------------------------------------------------------------------- diff --cc brooklyn-library/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastClusterNodeIntegrationTest.java index 0000000,0000000..dc89934 new file mode 100644 --- /dev/null +++ b/brooklyn-library/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastClusterNodeIntegrationTest.java @@@ -1,0 -1,0 +1,49 @@@ ++/* ++ * 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.entity.nosql.hazelcast; ++ ++import org.apache.brooklyn.api.location.Location; ++import org.apache.brooklyn.core.entity.Entities; ++import org.apache.brooklyn.core.test.entity.TestApplication; ++ ++import org.testng.annotations.AfterMethod; ++import org.testng.annotations.BeforeMethod; ++import org.testng.annotations.Test; ++import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation; ++ ++public class HazelcastClusterNodeIntegrationTest { ++ private TestApplication app; ++ private Location location; ++ ++ @BeforeMethod(alwaysRun = true) ++ public void setup() throws Exception { ++ app = TestApplication.Factory.newManagedInstanceForTests();; ++ location = new LocalhostMachineProvisioningLocation(); ++ } ++ ++ @AfterMethod(alwaysRun = true) ++ public void shutdown() { ++ Entities.destroyAll(app.getManagementContext()); ++ } ++ ++ @Test(groups = {"Integration"}) ++ public void testHazelcastCluster() { ++ HazelcastTestHelper.testHazelcastCluster(app, location); ++ } ++} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastClusterSoftlayerLiveTest.java ---------------------------------------------------------------------- diff --cc brooklyn-library/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastClusterSoftlayerLiveTest.java index 0000000,0000000..412ce87 new file mode 100644 --- /dev/null +++ b/brooklyn-library/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastClusterSoftlayerLiveTest.java @@@ -1,0 -1,0 +1,47 @@@ ++/* ++ * 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.entity.nosql.hazelcast; ++ ++import org.slf4j.Logger; ++import org.slf4j.LoggerFactory; ++import org.testng.annotations.Test; ++ ++import org.apache.brooklyn.entity.AbstractSoftlayerLiveTest; ++import org.apache.brooklyn.api.location.Location; ++ ++public class HazelcastClusterSoftlayerLiveTest extends AbstractSoftlayerLiveTest { ++ @SuppressWarnings("unused") ++ private static final Logger LOG = LoggerFactory.getLogger(HazelcastClusterSoftlayerLiveTest.class); ++ ++ @Override ++ protected void doTest(Location loc) throws Exception { ++ HazelcastTestHelper.testHazelcastCluster(app, loc); ++ } ++ ++ @Test(enabled = false) ++ public void testDummy() { ++ } // Convince TestNG IDE integration that this really does have test methods ++ ++ ++ @Test(groups = {"Live", "Live-sanity"}) ++ @Override ++ public void test_Ubuntu_12_0_4() throws Exception { ++ super.test_Ubuntu_12_0_4(); ++ } ++} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastNodeIntegrationTest.java ---------------------------------------------------------------------- diff --cc brooklyn-library/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastNodeIntegrationTest.java index 0000000,0000000..26a18c5 new file mode 100644 --- /dev/null +++ b/brooklyn-library/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastNodeIntegrationTest.java @@@ -1,0 -1,0 +1,107 @@@ ++/* ++ * 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.entity.nosql.hazelcast; ++ ++import static org.testng.Assert.assertEquals; ++ ++import java.net.URISyntaxException; ++ ++import org.apache.brooklyn.api.entity.EntitySpec; ++import org.apache.brooklyn.api.location.Location; ++import org.apache.brooklyn.core.entity.Attributes; ++import org.apache.brooklyn.core.entity.Entities; ++import org.apache.brooklyn.core.entity.EntityAsserts; ++import org.apache.brooklyn.core.entity.trait.Startable; ++import org.apache.brooklyn.core.test.entity.TestApplication; ++import org.apache.brooklyn.util.http.HttpTool; ++import org.apache.brooklyn.util.http.HttpToolResponse; ++import org.apache.http.client.methods.HttpGet; ++import com.hazelcast.core.HazelcastInstance; ++import com.hazelcast.core.IMap; ++ ++import org.testng.annotations.AfterMethod; ++import org.testng.annotations.BeforeMethod; ++import org.testng.annotations.Test; ++import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation; ++ ++import com.google.common.collect.ImmutableList; ++ ++public class HazelcastNodeIntegrationTest { ++ protected TestApplication app; ++ protected Location testLocation; ++ protected HazelcastNode hazelcastNode; ++ ++ @BeforeMethod(alwaysRun = true) ++ public void setup() throws Exception { ++ app = TestApplication.Factory.newManagedInstanceForTests();; ++ testLocation = new LocalhostMachineProvisioningLocation(); ++ } ++ ++ @AfterMethod(alwaysRun = true) ++ public void shutdown() { ++ Entities.destroyAll(app.getManagementContext()); ++ } ++ ++ @Test(groups = {"Integration"}) ++ public void testHazelcastStartupAndShutdown() { ++ hazelcastNode = app.createAndManageChild(EntitySpec.create(HazelcastNode.class)); ++ app.start(ImmutableList.of(testLocation)); ++ EntityAsserts.assertAttributeEqualsEventually(hazelcastNode, Startable.SERVICE_UP, true); ++ ++ hazelcastNode.stop(); ++ EntityAsserts.assertAttributeEqualsEventually(hazelcastNode, Startable.SERVICE_UP, false); ++ } ++ ++ @Test(groups = {"Integration"}) ++ public void testHazelcastRestInterface() throws URISyntaxException { ++ hazelcastNode = app.createAndManageChild(EntitySpec.create(HazelcastNode.class)); ++ app.start(ImmutableList.of(testLocation)); ++ ++ EntityAsserts.assertAttributeEqualsEventually(hazelcastNode, Startable.SERVICE_UP, true); ++ EntityAsserts.assertAttributeEquals(hazelcastNode, HazelcastNode.NODE_PORT, 5701); ++ ++ String baseUri = String.format("http://%s:%d/hazelcast/rest/cluster", hazelcastNode.getAttribute(Attributes.HOSTNAME), hazelcastNode.getAttribute(HazelcastNode.NODE_PORT)); ++ HttpToolResponse response = HttpTool.execAndConsume( ++ HttpTool.httpClientBuilder().build(), ++ new HttpGet(baseUri)); ++ assertEquals(response.getResponseCode(), 200); ++ } ++ ++ @Test(groups = {"Integration"}) ++ public void testHazelcastClient() throws URISyntaxException { ++ hazelcastNode = app.createAndManageChild(EntitySpec.create(HazelcastNode.class)); ++ app.start(ImmutableList.of(testLocation)); ++ ++ EntityAsserts.assertAttributeEqualsEventually(hazelcastNode, Startable.SERVICE_UP, true); ++ HazelcastTestHelper helper = new HazelcastTestHelper(hazelcastNode.getAttribute(Attributes.HOSTNAME), hazelcastNode.getAttribute(HazelcastNode.NODE_PORT)); ++ ++ HazelcastInstance client = helper.getClient(); ++ HazelcastInstance client2 = helper.getClient(); ++ ++ client.getMap(HazelcastTestHelper.GROUP_NAME).put("A", "a"); ++ client2.getMap(HazelcastTestHelper.GROUP_NAME).put("B", "b"); ++ ++ final IMap<Object, Object> map = client.getMap(HazelcastTestHelper.GROUP_NAME); ++ assertEquals("a", map.get("A")); ++ assertEquals("b", map.get("B")); ++ ++ client.shutdown(); ++ client2.shutdown(); ++ } ++} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastTestHelper.java ---------------------------------------------------------------------- diff --cc brooklyn-library/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastTestHelper.java index 0000000,0000000..b48e0bd new file mode 100644 --- /dev/null +++ b/brooklyn-library/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/hazelcast/HazelcastTestHelper.java @@@ -1,0 -1,0 +1,76 @@@ ++/* ++ * 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.entity.nosql.hazelcast; ++ ++import org.apache.brooklyn.api.entity.EntitySpec; ++import org.apache.brooklyn.api.location.Location; ++import org.apache.brooklyn.core.entity.Attributes; ++import org.apache.brooklyn.core.entity.EntityAsserts; ++import org.apache.brooklyn.core.test.entity.TestApplication; ++import org.slf4j.Logger; ++import org.slf4j.LoggerFactory; ++ ++import com.google.common.collect.ImmutableList; ++import com.google.common.collect.Iterables; ++import com.hazelcast.client.HazelcastClient; ++import com.hazelcast.client.config.ClientConfig; ++import com.hazelcast.core.HazelcastInstance; ++ ++public class HazelcastTestHelper { ++ private static final Logger LOG = LoggerFactory.getLogger(HazelcastTestHelper.class); ++ private static ClientConfig clientConfig; ++ ++ public static final String GROUP_NAME = "brooklyn"; ++ public static final String GROUP_PASS = "brooklyn"; ++ ++ public HazelcastTestHelper(String hazelcastAddress, Integer hazelcastPort) { ++ clientConfig = new ClientConfig(); ++ clientConfig.getGroupConfig().setName(GROUP_NAME).setPassword(GROUP_PASS); ++ clientConfig.getNetworkConfig().addAddress(String.format("%s:%d", hazelcastAddress, hazelcastPort)); ++ } ++ ++ public HazelcastInstance getClient() { ++ HazelcastInstance client = HazelcastClient.newHazelcastClient(clientConfig); ++ LOG.info("Hazelcast client {}", client.getName()); ++ ++ return client; ++ } ++ ++ public static void testHazelcastCluster(TestApplication app, Location loc) { ++ HazelcastCluster cluster = app.createAndManageChild(EntitySpec.create(HazelcastCluster.class) ++ .configure(HazelcastCluster.INITIAL_SIZE, 3) ++ .configure(HazelcastCluster.MEMBER_SPEC, EntitySpec.create(HazelcastNode.class))); ++ app.start(ImmutableList.of(loc)); ++ ++ EntityAsserts.assertAttributeEqualsEventually(cluster, HazelcastNode.SERVICE_UP, true); ++ ++ HazelcastNode first = (HazelcastNode) Iterables.get(cluster.getMembers(), 0); ++ HazelcastNode second = (HazelcastNode) Iterables.get(cluster.getMembers(), 1); ++ ++ assertNodesUpAndInCluster(first, second); ++ ++ EntityAsserts.assertAttributeEqualsEventually(cluster, Attributes.SERVICE_UP, true); ++ } ++ ++ private static void assertNodesUpAndInCluster(final HazelcastNode... nodes) { ++ for (final HazelcastNode node : nodes) { ++ EntityAsserts.assertAttributeEqualsEventually(node, HazelcastNode.SERVICE_UP, true); ++ } ++ } ++} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsService.java ---------------------------------------------------------------------- diff --cc brooklyn-library/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsService.java index 0000000,2896b48..6e55d5d mode 000000,100644..100644 --- a/brooklyn-library/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsService.java +++ b/brooklyn-library/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsService.java @@@ -1,0 -1,59 +1,74 @@@ + /* + * 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.entity.dns; + + import java.util.Map; + + import org.apache.brooklyn.api.entity.Entity; + import org.apache.brooklyn.api.entity.Group; + import org.apache.brooklyn.api.sensor.AttributeSensor; + import org.apache.brooklyn.config.ConfigKey; + import org.apache.brooklyn.core.config.ConfigKeys; + import org.apache.brooklyn.core.entity.Attributes; + import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; + import org.apache.brooklyn.core.entity.trait.Startable; + import org.apache.brooklyn.core.location.geo.HostGeoInfo; -import org.apache.brooklyn.core.sensor.BasicAttributeSensor; ++import org.apache.brooklyn.core.sensor.Sensors; ++import org.apache.brooklyn.util.core.flags.SetFromFlag; + + import com.google.common.reflect.TypeToken; + + public interface AbstractGeoDnsService extends Entity { + - public static final ConfigKey<Boolean> INCLUDE_HOMELESS_ENTITIES = ConfigKeys.newBooleanConfigKey("geodns.includeHomeless", "Whether to include entities whose geo-coordinates cannot be inferred", false); - public static final ConfigKey<Boolean> USE_HOSTNAMES = ConfigKeys.newBooleanConfigKey("geodns.useHostnames", "Whether to use the hostname for the returned value for routing, rather than IP address (defaults to true)", true); - - public static final AttributeSensor<Lifecycle> SERVICE_STATE_ACTUAL = Attributes.SERVICE_STATE_ACTUAL; - public static final AttributeSensor<Boolean> SERVICE_UP = Startable.SERVICE_UP; - public static final AttributeSensor<String> HOSTNAME = Attributes.HOSTNAME; - public static final AttributeSensor<String> ADDRESS = Attributes.ADDRESS; - @SuppressWarnings("serial") - public static final AttributeSensor<Map<String,String>> TARGETS = new BasicAttributeSensor<Map<String,String>>( - new TypeToken<Map<String,String>>() {}, "geodns.targets", "Map of targets currently being managed (entity ID to URL)"); - - public void setServiceState(Lifecycle state); ++ @SetFromFlag("includeHomelessEntities") ++ ConfigKey<Boolean> INCLUDE_HOMELESS_ENTITIES = ConfigKeys.newBooleanConfigKey( ++ "geodns.includeHomeless", "Whether to include entities whose geo-coordinates cannot be inferred", false); ++ ++ @SetFromFlag("useHostnames") ++ ConfigKey<Boolean> USE_HOSTNAMES = ConfigKeys.newBooleanConfigKey( ++ "geodns.useHostnames", "Whether to use the hostname for the returned value for routing, rather than IP address (defaults to true)", true); ++ ++ @SetFromFlag("provider") ++ ConfigKey<Group> ENTITY_PROVIDER = ConfigKeys.newConfigKey(Group.class, ++ "geodns.entityProvider", "The group whose members should be tracked"); ++ ++ /** @see Lifecycle#RUNNING */ ++ @SetFromFlag("filterForRunning") ++ ConfigKey<Boolean> FILTER_FOR_RUNNING = ConfigKeys.newBooleanConfigKey( ++ "geodns.filterForRunning", "Whether to only track targets whose status is \"RUNNING\"", true); ++ ++ AttributeSensor<Lifecycle> SERVICE_STATE_ACTUAL = Attributes.SERVICE_STATE_ACTUAL; ++ AttributeSensor<Boolean> SERVICE_UP = Startable.SERVICE_UP; ++ AttributeSensor<String> HOSTNAME = Attributes.HOSTNAME; ++ AttributeSensor<String> ADDRESS = Attributes.ADDRESS; ++ ++ AttributeSensor<Map<String,String>> TARGETS = Sensors.newSensor(new TypeToken<Map<String, String>>() {}, ++ "geodns.targets", "Map of targets currently being managed (entity ID to URL)"); ++ ++ void setServiceState(Lifecycle state); + + /** sets target to be a group whose *members* will be searched (non-Group items not supported) */ + // prior to 0.7.0 the API accepted non-group items, but did not handle them correctly - public void setTargetEntityProvider(final Group entityProvider); ++ void setTargetEntityProvider(final Group entityProvider); + + /** should return the hostname which this DNS service is configuring */ - public String getHostname(); ++ String getHostname(); + - public Map<Entity, HostGeoInfo> getTargetHosts(); ++ Map<Entity, HostGeoInfo> getTargetHosts(); + }
