You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2015/07/24 08:41:06 UTC

[1/2] incubator-brooklyn git commit: BROOKLYN-143 - support for Hazelcast

Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master 3559b5d6d -> df4eb8a7f


BROOKLYN-143 - support for Hazelcast


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/e37f0585
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/e37f0585
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/e37f0585

Branch: refs/heads/master
Commit: e37f05856c7067081f8e37d3fbdc84408e1bbb9d
Parents: b2d3f33
Author: Yavor Yanchev <ya...@yanchev.com>
Authored: Wed May 13 03:05:54 2015 +0300
Committer: Aled Sage <al...@gmail.com>
Committed: Thu Jul 23 23:37:23 2015 -0700

----------------------------------------------------------------------
 sandbox/nosql/pom.xml                           |  14 ++
 .../nosql/hazelcast/HazelcastCluster.java       |  59 +++++++
 .../nosql/hazelcast/HazelcastClusterImpl.java   | 124 +++++++++++++++
 .../entity/nosql/hazelcast/HazelcastNode.java   |  97 +++++++++++
 .../nosql/hazelcast/HazelcastNodeDriver.java    |  25 +++
 .../nosql/hazelcast/HazelcastNodeImpl.java      | 148 +++++++++++++++++
 .../nosql/hazelcast/HazelcastNodeSshDriver.java | 159 +++++++++++++++++++
 .../nosql/hazelcast/hazelcast-brooklyn.xml      |  65 ++++++++
 .../hazelcast/HazelcastClusterEc2LiveTest.java  |  71 +++++++++
 .../HazelcastClusterSoftlayerLiveTest.java      |  71 +++++++++
 10 files changed, 833 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e37f0585/sandbox/nosql/pom.xml
----------------------------------------------------------------------
diff --git a/sandbox/nosql/pom.xml b/sandbox/nosql/pom.xml
index 2de79ac..da6fe21 100644
--- a/sandbox/nosql/pom.xml
+++ b/sandbox/nosql/pom.xml
@@ -91,6 +91,20 @@
                     </execution>
                 </executions>
             </plugin>
+            <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/brooklyn/entity/nosql/hazelcast/hazelcast-brooklyn.xml</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
 </project>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e37f0585/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastCluster.java
----------------------------------------------------------------------
diff --git a/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastCluster.java b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastCluster.java
new file mode 100644
index 0000000..48ab1aa
--- /dev/null
+++ b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastCluster.java
@@ -0,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 brooklyn.entity.nosql.hazelcast;
+
+import java.util.List;
+
+import com.google.common.reflect.TypeToken;
+
+import brooklyn.catalog.Catalog;
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.group.DynamicCluster;
+import brooklyn.entity.proxying.ImplementedBy;
+import brooklyn.event.AttributeSensor;
+import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
+import brooklyn.event.basic.Sensors;
+import brooklyn.util.flags.SetFromFlag;
+
+/**
+ * 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/e37f0585/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterImpl.java
----------------------------------------------------------------------
diff --git a/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterImpl.java b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterImpl.java
new file mode 100644
index 0000000..e911318
--- /dev/null
+++ b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterImpl.java
@@ -0,0 +1,124 @@
+/*
+ * 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 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 brooklyn.entity.Entity;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.group.AbstractMembershipTrackingPolicy;
+import brooklyn.entity.group.DynamicClusterImpl;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.location.Location;
+import brooklyn.policy.PolicySpec;
+import 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(getConfig(MEMBER_SPEC, EntitySpec.create(HazelcastNode.class)));
+        
+        spec.configure(HazelcastNode.GROUP_NAME, getConfig(HazelcastClusterImpl.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");
+            }
+            setConfig(CLUSTER_PASSWORD, Strings.makeRandomId(12));
+        }
+        
+        addPolicy(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).setAttribute(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));
+        }
+        setAttribute(PUBLIC_CLUSTER_NODES, clusterNodes);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e37f0585/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNode.java
----------------------------------------------------------------------
diff --git a/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNode.java b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNode.java
new file mode 100644
index 0000000..076b6f5
--- /dev/null
+++ b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNode.java
@@ -0,0 +1,97 @@
+/*
+ * 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 brooklyn.entity.nosql.hazelcast;
+
+import brooklyn.catalog.Catalog;
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.SoftwareProcess;
+import brooklyn.entity.java.UsesJava;
+import brooklyn.entity.java.UsesJmx;
+import brooklyn.entity.proxying.ImplementedBy;
+import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
+import brooklyn.event.basic.BasicAttributeSensorAndConfigKey.StringAttributeSensorAndConfigKey;
+import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
+import brooklyn.location.basic.PortRanges;
+import brooklyn.util.flags.SetFromFlag;
+import 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.4.2");
+    
+    @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+"));
+
+    /**
+     * 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/e37f0585/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeDriver.java
----------------------------------------------------------------------
diff --git a/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeDriver.java b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeDriver.java
new file mode 100644
index 0000000..4e53add
--- /dev/null
+++ b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeDriver.java
@@ -0,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 brooklyn.entity.nosql.hazelcast;
+
+import brooklyn.entity.basic.SoftwareProcessDriver;
+
+public interface HazelcastNodeDriver extends SoftwareProcessDriver {
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e37f0585/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeImpl.java
----------------------------------------------------------------------
diff --git a/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeImpl.java b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeImpl.java
new file mode 100644
index 0000000..0369934
--- /dev/null
+++ b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeImpl.java
@@ -0,0 +1,148 @@
+/*
+ * 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 brooklyn.entity.nosql.hazelcast;
+
+import java.net.URI;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.basic.SoftwareProcessImpl;
+import brooklyn.event.feed.http.HttpFeed;
+import brooklyn.event.feed.http.HttpPollConfig;
+import brooklyn.event.feed.http.HttpValueFunctions;
+import brooklyn.location.access.BrooklynAccessUtils;
+import 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());
+        setAttribute(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/e37f0585/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeSshDriver.java
----------------------------------------------------------------------
diff --git a/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeSshDriver.java b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeSshDriver.java
new file mode 100644
index 0000000..5527a69
--- /dev/null
+++ b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeSshDriver.java
@@ -0,0 +1,159 @@
+/*
+ * 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 brooklyn.entity.nosql.hazelcast;
+
+import static java.lang.String.format;
+
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityLocal;
+import brooklyn.entity.java.JavaSoftwareProcessSshDriver;
+import brooklyn.location.basic.SshMachineLocation;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.os.Os;
+import brooklyn.util.ssh.BashCommands;
+
+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(EntityLocal 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.setAttribute(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;
+        
+        StringBuilder commandBuilder = new StringBuilder()
+            .append(format("nohup java -cp ./lib/%s", resolver.getFilename()))
+            .append(format(" -Xmx%s -Xms%s", maxHeapMemorySize, initialHeapMemorySize))
+            .append(format(" -Dhazelcast.config=./conf/%s", getConfigFileName()))
+            .append(format(" com.hazelcast.core.server.StartServer >> %s 2>&1 </dev/null &", getLogFileLocation()));
+        
+        newScript(MutableMap.of(USE_PID_FILE, true), LAUNCHING)
+            .updateTaskAndFailOnNonZeroResultCode()
+            .body.append(commandBuilder.toString())
+            .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() throws ExecutionException, InterruptedException {
+        HazelcastCluster cluster = (HazelcastCluster) entity.getParent();
+        List<String> result = Lists.newArrayList();
+
+        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/e37f0585/sandbox/nosql/src/main/resources/brooklyn/entity/nosql/hazelcast/hazelcast-brooklyn.xml
----------------------------------------------------------------------
diff --git a/sandbox/nosql/src/main/resources/brooklyn/entity/nosql/hazelcast/hazelcast-brooklyn.xml b/sandbox/nosql/src/main/resources/brooklyn/entity/nosql/hazelcast/hazelcast-brooklyn.xml
new file mode 100644
index 0000000..459be4e
--- /dev/null
+++ b/sandbox/nosql/src/main/resources/brooklyn/entity/nosql/hazelcast/hazelcast-brooklyn.xml
@@ -0,0 +1,65 @@
+[#ftl]
+<?xml version="1.0" encoding="UTF-8"?>
+
+<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.4.xsd"
+           xmlns="http://www.hazelcast.com/schema/config"
+           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <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>
+        
+        <interfaces enabled="true">
+            <interface>${entity.listenAddress}</interface> 
+        </interfaces> 
+        
+        <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"/>
+        <symmetric-encryption 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/e37f0585/sandbox/nosql/src/test/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterEc2LiveTest.java
----------------------------------------------------------------------
diff --git a/sandbox/nosql/src/test/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterEc2LiveTest.java b/sandbox/nosql/src/test/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterEc2LiveTest.java
new file mode 100644
index 0000000..bce319c
--- /dev/null
+++ b/sandbox/nosql/src/test/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterEc2LiveTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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 brooklyn.entity.nosql.hazelcast;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.AbstractEc2LiveTest;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.location.Location;
+import brooklyn.test.EntityTestUtils;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class HazelcastClusterEc2LiveTest extends AbstractEc2LiveTest {
+    @SuppressWarnings("unused")
+    private static final Logger LOG = LoggerFactory.getLogger(HazelcastClusterEc2LiveTest.class);
+
+    @Override
+    protected void doTest(Location loc) throws Exception {
+        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));
+
+        EntityTestUtils.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);
+        
+        EntityTestUtils.assertAttributeEqualsEventually(cluster, Attributes.SERVICE_UP, true);
+    }
+    
+    private void assertNodesUpAndInCluster(final HazelcastNode... nodes) {
+        for (final HazelcastNode node : nodes) {
+            EntityTestUtils.assertAttributeEqualsEventually(node, HazelcastNode.SERVICE_UP, true);
+        }
+    }
+
+    @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/e37f0585/sandbox/nosql/src/test/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterSoftlayerLiveTest.java
----------------------------------------------------------------------
diff --git a/sandbox/nosql/src/test/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterSoftlayerLiveTest.java b/sandbox/nosql/src/test/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterSoftlayerLiveTest.java
new file mode 100644
index 0000000..4ff4ab9
--- /dev/null
+++ b/sandbox/nosql/src/test/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterSoftlayerLiveTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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 brooklyn.entity.nosql.hazelcast;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.AbstractSoftlayerLiveTest;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.location.Location;
+import brooklyn.test.EntityTestUtils;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class HazelcastClusterSoftlayerLiveTest extends AbstractSoftlayerLiveTest {
+    @SuppressWarnings("unused")
+    private static final Logger LOG = LoggerFactory.getLogger(HazelcastClusterSoftlayerLiveTest.class);
+
+    @Override
+    protected void doTest(Location loc) throws Exception {
+        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));
+
+        EntityTestUtils.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);
+        
+        EntityTestUtils.assertAttributeEqualsEventually(cluster, Attributes.SERVICE_UP, true);
+    }
+    
+    private void assertNodesUpAndInCluster(final HazelcastNode... nodes) {
+        for (final HazelcastNode node : nodes) {
+            EntityTestUtils.assertAttributeEqualsEventually(node, HazelcastNode.SERVICE_UP, true);
+        }
+    }
+
+    @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();
+    }
+}



[2/2] incubator-brooklyn git commit: This closes #767

Posted by al...@apache.org.
This closes #767


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/df4eb8a7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/df4eb8a7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/df4eb8a7

Branch: refs/heads/master
Commit: df4eb8a7fe7e8e0ba319b8051d730047c292d741
Parents: 3559b5d e37f058
Author: Aled Sage <al...@gmail.com>
Authored: Thu Jul 23 23:41:02 2015 -0700
Committer: Aled Sage <al...@gmail.com>
Committed: Thu Jul 23 23:41:02 2015 -0700

----------------------------------------------------------------------
 sandbox/nosql/pom.xml                           |  14 ++
 .../nosql/hazelcast/HazelcastCluster.java       |  59 +++++++
 .../nosql/hazelcast/HazelcastClusterImpl.java   | 124 +++++++++++++++
 .../entity/nosql/hazelcast/HazelcastNode.java   |  97 +++++++++++
 .../nosql/hazelcast/HazelcastNodeDriver.java    |  25 +++
 .../nosql/hazelcast/HazelcastNodeImpl.java      | 148 +++++++++++++++++
 .../nosql/hazelcast/HazelcastNodeSshDriver.java | 159 +++++++++++++++++++
 .../nosql/hazelcast/hazelcast-brooklyn.xml      |  65 ++++++++
 .../hazelcast/HazelcastClusterEc2LiveTest.java  |  71 +++++++++
 .../HazelcastClusterSoftlayerLiveTest.java      |  71 +++++++++
 10 files changed, 833 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/df4eb8a7/sandbox/nosql/pom.xml
----------------------------------------------------------------------