implement basic configurations + monitoring

This commit is contained in:
Simon Baslé
2015-06-16 16:38:31 +02:00
parent 3041a76439
commit 259823afb3
6 changed files with 458 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2012-2015 the original author or authors
*
* Licensed 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.springframework.data.couchbase.config;
/**
* Contains default bean names that will be used when no "id" is supplied to the beans.
*
* @author Michael Nitschinger
* @author Simon Baslé
*/
public class BeanNames {
/**
* Refers to the "<couchbase:cluster />" bean.
*/
static final String COUCHBASE_CLUSTER = "cluster";
/**
* Refers to the "<couchbase:bucket />" bean.
*/
static final String COUCHBASE_BUCKET = "bucket";
/**
* Refers to the "<couchbase:template />" bean.
*/
static final String COUCHBASE_TEMPLATE = "couchbaseTemplate";
/**
* Refers to the "<couchbase:translation-service />" bean
*/
static final String TRANSLATION_SERVICE = "translationService";
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2012-2015 the original author or authors
*
* Licensed 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.springframework.data.couchbase.config;
import com.couchbase.client.java.Bucket;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.couchbase.monitor.ClientInfo;
import org.springframework.data.couchbase.monitor.ClusterInfo;
import org.springframework.util.StringUtils;
/**
* Enables Parsing of the "<couchbase:jmx />" configuration bean.
* <p/>
* In order to enable JMX, different JmxComponents need to be registered. The dependency to the original
* {@link Bucket} object is solved through the "bucket-ref" attribute.
*
* @author Michael Nitschinger
* @author Simon Baslé
*/
public class CouchbaseJmxParser implements BeanDefinitionParser {
/**
* Parse the element and dispatch the registration of the JMX components.
*
* @param element the XML element which contains the attributes.
* @param parserContext encapsulates the parsing state and configuration.
* @return null, because no bean instance needs to be returned.
*/
public BeanDefinition parse(final Element element, final ParserContext parserContext) {
String bucketName = element.getAttribute("bucket-ref");
if (!StringUtils.hasText(bucketName)) {
bucketName = BeanNames.COUCHBASE_BUCKET;
}
registerJmxComponents(bucketName, element, parserContext);
return null;
}
/**
* Register the JMX components in the context.
*
* @param element the XML element which contains the attributes.
* @param parserContext encapsulates the parsing state and configuration.
* @parma refBucketName the reference name to the couchbase bucket.
*/
protected void registerJmxComponents(final String refBucketName,
final Element element, final ParserContext parserContext) {
Object eleSource = parserContext.extractSource(element);
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
createBeanDefEntry(ClientInfo.class, compositeDef, refBucketName, eleSource, parserContext);
createBeanDefEntry(ClusterInfo.class, compositeDef, refBucketName, eleSource, parserContext);
parserContext.registerComponent(compositeDef);
}
/**
* Creates Bean Definitions for JMX components and adds them as a nested component.
*
* @param clazz the class type to register.
* @param compositeDef component that can hold nested components.
* @param refName the reference name to the couchbase client.
* @param eleSource source element to reference.
* @param parserContext encapsulates the parsing state and configuration.
*/
protected void createBeanDefEntry(final Class<?> clazz, final CompositeComponentDefinition compositeDef,
final String refName, final Object eleSource, final ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
builder.getRawBeanDefinition().setSource(eleSource);
builder.addConstructorArgReference(refName);
BeanDefinition assertDef = builder.getBeanDefinition();
String assertName = parserContext.getReaderContext().registerWithGeneratedName(assertDef);
compositeDef.addNestedComponent(new BeanComponentDefinition(assertDef, assertName));
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2012-2015 the original author or authors
*
* Licensed 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.springframework.data.couchbase.config;
import org.springframework.beans.factory.xml.NamespaceHandler;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
/**
* {@link NamespaceHandler} for Couchbase configuration.
* <p/>
* This handler acts as a container for one or more bean parsers and registers them. During parsing, the elements
* get analyzed and the appropriate registered parser is called.
*
* @author Michael Nitschinger
*/
public class CouchbaseNamespaceHandler extends NamespaceHandlerSupport {
/**
* Register bean definition parsers in the namespace handler.
*/
public final void init() {
//TODO repositories (CouchbaseRepositoryConfigurationExtension and RepositoryBeanDefinitionParser)
//TODO bucket
//TODO cluster
registerBeanDefinitionParser("jmx", new CouchbaseJmxParser());
registerBeanDefinitionParser("template", new CouchbaseTemplateParser());
//TODO translation service
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2012-2015 the original author or authors
*
* Licensed 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.springframework.data.couchbase.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.util.StringUtils;
/**
* Parser for "<couchbase:template />" bean definitions.
* <p/>
* The outcome of this bean definition parser will be a constructed {@link CouchbaseTemplate}.
*
* @author Michael Nitschinger
*/
public class CouchbaseTemplateParser extends AbstractSingleBeanDefinitionParser {
/**
* Resolve the bean ID and assign a default if not set.
*
* @param element the XML element which contains the attributes.
* @param definition the bean definition to work with.
* @param parserContext encapsulates the parsing state and configuration.
* @return the ID to work with.
*/
@Override
protected String resolveId(final Element element, final AbstractBeanDefinition definition, final ParserContext parserContext) {
String id = super.resolveId(element, definition, parserContext);
return StringUtils.hasText(id) ? id : BeanNames.COUCHBASE_TEMPLATE;
}
/**
* Defines the bean class that will be constructed.
*
* @param element the XML element which contains the attributes.
* @return the class type to instantiate.
*/
@Override
protected Class getBeanClass(final Element element) {
return CouchbaseTemplate.class;
}
/**
* Parse the bean definition and build up the bean.
*
* @param element the XML element which contains the attributes.
* @param bean the builder which builds the bean.
*/
@Override
protected void doParse(final Element element, final BeanDefinitionBuilder bean) {
String bucketRef = element.getAttribute("bucket-ref");
String converterRef = element.getAttribute("converter-ref");
String translationServiceRef = element.getAttribute("translation-service-ref");
bean.addConstructorArgReference(StringUtils.hasText(bucketRef) ? bucketRef : BeanNames.COUCHBASE_BUCKET);
if (StringUtils.hasText(converterRef)) {
bean.addConstructorArgReference(converterRef);
}
if (StringUtils.hasText(translationServiceRef)) {
bean.addConstructorArgReference(translationServiceRef);
}
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2012-2015 the original author or authors
*
* Licensed 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.springframework.data.couchbase.monitor;
import java.net.InetAddress;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.bucket.BucketInfo;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedResource;
/**
* Exposes basic client information.
*
* @author Michael Nitschinger
* @author Simon Baslé
*/
@ManagedResource(description = "Client Information")
public class ClientInfo {
private final Bucket bucket;
private final BucketInfo info;
public ClientInfo(final Bucket bucket) {
this.bucket = bucket;
this.info = bucket.bucketManager().info();
}
@ManagedAttribute(description = "Hostnames of connected nodes")
public String getHostNames() {
StringBuilder result = new StringBuilder();
for (InetAddress node : info.nodeList()) {
result.append(node.toString()).append(",");
}
return result.toString();
}
@ManagedAttribute(description = "Number of connected nodes")
public int getNumberOfNodes() {
return info.nodeCount();
}
//TODO obtain count of available nodes vs unavailable ones and expose it
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright 2012-2015 the original author or authors
*
* Licensed 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.springframework.data.couchbase.monitor;
import java.net.InetAddress;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.bucket.BucketInfo;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedMetric;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.web.client.RestTemplate;
/**
* Exposes basic cluster information.
*
* @author Michael Nitschinger
* @author Simon Baslé
*/
@ManagedResource(description = "Cluster Information")
public class ClusterInfo {
private final RestTemplate template;
private final Bucket bucket;
private final BucketInfo info;
public ClusterInfo(final Bucket bucket) {
this.template = new RestTemplate();
this.bucket = bucket;
this.info = bucket.bucketManager().info();
}
@ManagedMetric(description = "Total RAM assigned")
public long getTotalRAMAssigned() {
return convertPotentialLong(parseStorageTotals().get("ram").get("total"));
}
@ManagedMetric(description = "Total RAM used")
public long getTotalRAMUsed() {
return convertPotentialLong(parseStorageTotals().get("ram").get("used"));
}
@ManagedMetric(description = "Total Disk Space assigned")
public long getTotalDiskAssigned() {
return convertPotentialLong(parseStorageTotals().get("hdd").get("total"));
}
@ManagedMetric(description = "Total Disk Space used")
public long getTotalDiskUsed() {
return convertPotentialLong(parseStorageTotals().get("hdd").get("used"));
}
@ManagedMetric(description = "Total Disk Space free")
public long getTotalDiskFree() {
return convertPotentialLong(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");
}
/**
* Depending on the value size, either int or long can be passed in and get
* converted to long.
*
* @param value the value to convert.
* @return the converted value.
*/
private long convertPotentialLong(Object value) {
if (value instanceof Integer) {
return new Long((Integer) value);
}
else if (value instanceof Long) {
return (Long) value;
}
else {
throw new IllegalStateException("Cannot convert value to long: " + value);
}
}
protected String randomAvailableHostname() {
List<InetAddress> available = info.nodeList();
Collections.shuffle(available);
return available.get(0).getHostName();
}
private HashMap<String, Object> fetchPoolInfo() {
return template.getForObject("http://"
+ randomAvailableHostname() + ":8091/pools/default", HashMap.class);
}
private HashMap<String, HashMap> parseStorageTotals() {
HashMap<String, Object> stats = fetchPoolInfo();
return (HashMap<String, HashMap>) stats.get("storageTotals");
}
}