You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2015/12/23 12:06:45 UTC

[22/71] [abbrv] incubator-brooklyn git commit: Merge commit 'e430723' into reorg2

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();
+ }