diff --git a/pom.xml b/pom.xml
index 1058b52d..20701336 100644
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,11 @@
spring-context
${spring}
+
+ org.springframework
+ spring-web
+ ${spring}
+
cglib
cglib
diff --git a/src/main/java/com/couchbase/spring/monitor/AbstractMonitor.java b/src/main/java/com/couchbase/spring/monitor/AbstractMonitor.java
index 653844c4..afc47c1d 100644
--- a/src/main/java/com/couchbase/spring/monitor/AbstractMonitor.java
+++ b/src/main/java/com/couchbase/spring/monitor/AbstractMonitor.java
@@ -1,8 +1,13 @@
package com.couchbase.spring.monitor;
import com.couchbase.client.CouchbaseClient;
+import org.springframework.web.client.RestTemplate;
+import java.net.InetSocketAddress;
import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
/**
@@ -12,6 +17,8 @@ public abstract class AbstractMonitor {
private CouchbaseClient client;
+ private RestTemplate template = new RestTemplate();
+
protected AbstractMonitor(final CouchbaseClient client) {
this.client = client;
}
@@ -20,6 +27,16 @@ public abstract class AbstractMonitor {
return client;
}
+ protected RestTemplate getTemplate() {
+ return template;
+ }
+
+ protected String randomAvailableHostname() {
+ List available = (ArrayList) client.getAvailableServers();
+ Collections.shuffle(available);
+ return ((InetSocketAddress) available.get(0)).getHostName();
+ }
+
/**
* Fetches stats for all nodes.
*
diff --git a/src/main/java/com/couchbase/spring/monitor/ClientInfo.java b/src/main/java/com/couchbase/spring/monitor/ClientInfo.java
new file mode 100644
index 00000000..85f29aef
--- /dev/null
+++ b/src/main/java/com/couchbase/spring/monitor/ClientInfo.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (C) 2009-2012 Couchbase, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING
+ * IN THE SOFTWARE.
+ */
+package com.couchbase.spring.monitor;
+
+import com.couchbase.client.CouchbaseClient;
+import org.springframework.jmx.export.annotation.ManagedAttribute;
+import org.springframework.jmx.export.annotation.ManagedResource;
+
+import java.net.SocketAddress;
+
+/**
+ * Exposes basic client information.
+ */
+@ManagedResource(description = "Client Information")
+public class ClientInfo extends AbstractMonitor {
+
+ public ClientInfo(final CouchbaseClient client) {
+ super(client);
+ }
+
+ @ManagedAttribute(description = "Hostnames of connected nodes")
+ public String getHostNames() {
+ StringBuilder result = new StringBuilder();
+ for (SocketAddress node : getStats().keySet()) {
+ result.append(node.toString()).append(",");
+ }
+ return result.toString();
+ }
+
+ @ManagedAttribute(description = "Number of connected nodes")
+ public int getNumberOfNodes() {
+ return getStats().keySet().size();
+ }
+
+ @ManagedAttribute(description = "Number of connected active nodes")
+ public int getNumberOfActiveNodes() {
+ return getClient().getAvailableServers().size();
+ }
+
+ @ManagedAttribute(description = "Number of connected inactive nodes")
+ public int getNumberOfInactiveNodes() {
+ return getClient().getUnavailableServers().size();
+ }
+
+}
diff --git a/src/main/java/com/couchbase/spring/monitor/ClusterInfo.java b/src/main/java/com/couchbase/spring/monitor/ClusterInfo.java
index 7cdc0bc9..0c0a4f3d 100644
--- a/src/main/java/com/couchbase/spring/monitor/ClusterInfo.java
+++ b/src/main/java/com/couchbase/spring/monitor/ClusterInfo.java
@@ -1,13 +1,15 @@
package com.couchbase.spring.monitor;
import com.couchbase.client.CouchbaseClient;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.jmx.export.annotation.ManagedAttribute;
-import org.springframework.jmx.export.annotation.ManagedOperation;
+import org.springframework.jmx.export.annotation.ManagedMetric;
import org.springframework.jmx.export.annotation.ManagedResource;
-import java.net.SocketAddress;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.Arrays;
+import java.util.HashMap;
/**
* Exposes basic cluster information.
@@ -19,31 +21,54 @@ public class ClusterInfo extends AbstractMonitor {
super(client);
}
- @ManagedAttribute(description = "All hostnames of nodes in the cluster")
- public String getHostNames() {
- StringBuilder result = new StringBuilder();
- for (SocketAddress node : getStats().keySet()) {
- result.append(node.toString()).append(",");
- }
- return result.toString();
+ @ManagedMetric(description = "Total RAM assigned")
+ public long getTotalRAMAssigned() {
+ return (Long) parseStorageTotals().get("ram").get("total");
}
- @ManagedAttribute(description = "Number of nodes currently in the cluster")
- public int getNumberOfNodes() {
- return getStats().keySet().size();
+ @ManagedMetric(description = "Total RAM used")
+ public long getTotalRAMUsed() {
+ return (Long) parseStorageTotals().get("ram").get("used");
}
- @ManagedAttribute(description = "Couchbase Server versions of nodes in the cluster")
- public String getNodeVersions() {
- StringBuilder result = new StringBuilder();
- for(Map.Entry> entry : getStats().entrySet()) {
- for(Map.Entry stat : entry.getValue().entrySet()) {
- if (stat.getKey().equals("ep_version")) {
- result.append(stat.getValue()).append(",");
- }
- }
- }
- return result.toString();
+ @ManagedMetric(description = "Total Disk Space assigned")
+ public long getTotalDiskAssigned() {
+ return (Long) parseStorageTotals().get("hdd").get("total");
+ }
+
+ @ManagedMetric(description = "Total Disk Space used")
+ public long getTotalDiskUsed() {
+ return (Long) parseStorageTotals().get("hdd").get("used");
+ }
+
+ @ManagedMetric(description = "Total Disk Space free")
+ public long getTotalDiskFree() {
+ return (Long) parseStorageTotals().get("hdd").get("free");
+ }
+
+ @ManagedAttribute(description = "Cluster is Balanced")
+ public boolean getIsBalanced() {
+ return (Boolean) fetchPoolInfo().get("balanced");
+ }
+
+ @ManagedAttribute(description = "Rebalance Status")
+ public String getRebalanceStatus() {
+ return (String) fetchPoolInfo().get("rebalanceStatus");
+ }
+
+ @ManagedAttribute(description = "Maximum Available Buckets")
+ public int getMaxBuckets() {
+ return (Integer) fetchPoolInfo().get("maxBucketCount");
+ }
+
+ private HashMap fetchPoolInfo() {
+ return getTemplate().getForObject("http://"
+ + randomAvailableHostname() + ":8091/pools/default", HashMap.class);
+ }
+
+ private HashMap parseStorageTotals() {
+ HashMap stats = fetchPoolInfo();
+ return (HashMap) stats.get("storageTotals");
}
}
diff --git a/src/test/java/com/couchbase/spring/monitor/ClientInfoTest.java b/src/test/java/com/couchbase/spring/monitor/ClientInfoTest.java
new file mode 100644
index 00000000..fbd98f11
--- /dev/null
+++ b/src/test/java/com/couchbase/spring/monitor/ClientInfoTest.java
@@ -0,0 +1,36 @@
+package com.couchbase.spring.monitor;
+
+import com.couchbase.client.CouchbaseClient;
+import com.couchbase.spring.TestApplicationConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import static junit.framework.Assert.assertNotNull;
+import static org.junit.Assert.assertFalse;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(classes = TestApplicationConfig.class)
+public class ClientInfoTest {
+
+ @Autowired
+ private CouchbaseClient client;
+
+ private ClientInfo ci;
+
+ @Before
+ public void setup() {
+ ci = new ClientInfo(client);
+ }
+
+ @Test
+ public void hostNames() {
+ String hostnames = ci.getHostNames();
+ assertNotNull(hostnames);
+ assertFalse(hostnames.isEmpty());
+ }
+
+}
diff --git a/src/test/java/com/couchbase/spring/monitor/ClusterInfoTest.java b/src/test/java/com/couchbase/spring/monitor/ClusterInfoTest.java
new file mode 100644
index 00000000..3fc41317
--- /dev/null
+++ b/src/test/java/com/couchbase/spring/monitor/ClusterInfoTest.java
@@ -0,0 +1,40 @@
+package com.couchbase.spring.monitor;
+
+import com.couchbase.client.CouchbaseClient;
+import com.couchbase.spring.TestApplicationConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(classes = TestApplicationConfig.class)
+public class ClusterInfoTest {
+
+ @Autowired
+ private CouchbaseClient client;
+
+ private ClusterInfo ci;
+
+ @Before
+ public void setup() {
+ ci = new ClusterInfo(client);
+ }
+
+ @Test
+ public void totalRAMAssigned() {
+ assertTrue(ci.getTotalRAMAssigned() > 0);
+ }
+
+ @Test
+ public void totalRAMUsed() {
+ assertTrue(ci.getTotalRAMUsed() > 0);
+ }
+
+}