Merge pull request #41 from simonbasle/configuration

basic configuration (xml parsers and @Configuration)
This commit is contained in:
Simon Baslé
2015-06-16 20:01:42 +02:00
10 changed files with 978 additions and 0 deletions

View File

@@ -0,0 +1,193 @@
/*
* 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 java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.CouchbaseCluster;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationCondition;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy;
import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Base class for Spring Data Couchbase configuration using JavaConfig.
*
* @author Michael Nitschinger
* @author Simon Baslé
*/
@Configuration
public abstract class AbstractCouchbaseConfiguration {
/**
* The list of hostnames (or IP addresses) to bootstrap from.
*
* @return the list of bootstrap hosts.
*/
protected abstract List<String> getBootstrapHosts();
/**
* The name of the bucket to connect to.
*
* @return the name of the bucket.
*/
protected abstract String getBucketName();
/**
* The password of the bucket (can be an empty string).
*
* @return the password of the bucket.
*/
protected abstract String getBucketPassword();
/**
* Is the {@link #getEnvironment()} to be destroyed by Spring?
*
* @return true if Spring should destroy the environment with the context, false otherwise.
*/
protected boolean isEnvironmentManagedBySpring() {
return true;
}
/**
* Override this method if you want a customized {@link CouchbaseEnvironment}.
* This environment will be managed by Spring, which will call its shutdown()
* method upon bean destruction, unless you override {@link #isEnvironmentManagedBySpring()}
* as well to return false.
*
* @return a customized environment, defaults to a {@link DefaultCouchbaseEnvironment}.
*/
protected CouchbaseEnvironment getEnvironment() {
return DefaultCouchbaseEnvironment.create();
}
@Bean(destroyMethod = "shutdown", name = BeanNames.COUCHBASE_ENV)
public CouchbaseEnvironment couchbaseEnvironment() {
CouchbaseEnvironment env = getEnvironment();
if (isEnvironmentManagedBySpring()) {
return env;
}
return new CouchbaseEnvironmentNoShutdownProxy(env);
}
/**
* Returns the {@link Cluster} instance to connect to.
*
* @throws Exception on Bean construction failure.
*/
@Bean(destroyMethod = "disconnect", name = BeanNames.COUCHBASE_CLUSTER)
public Cluster couchbaseCluster() throws Exception {
return CouchbaseCluster.create(couchbaseEnvironment(), getBootstrapHosts());
}
/**
* Return the {@link Bucket} instance to connect to.
*
* @throws Exception on Bean construction failure.
*/
@Bean(destroyMethod = "close", name = BeanNames.COUCHBASE_BUCKET)
public Bucket couchbaseClient() throws Exception {
//@Bean method can use another @Bean method in the same @Configuration by directly invoking it
return couchbaseCluster().openBucket(getBucketName(), getBucketPassword());
}
/**
* Creates a {@link CouchbaseTemplate}.
*
* @throws Exception on Bean construction failure.
*/
@Bean(name = BeanNames.COUCHBASE_TEMPLATE)
public CouchbaseTemplate couchbaseTemplate() throws Exception {
//TODO use mappingCouchbaseConverter and translationService when implemented
return new CouchbaseTemplate(couchbaseClient());
}
//TODO create beans for mappingCouchbaseConverter, translationService, couchbaseMappingContext when implemented
//TODO for mappingCouchbaseConverter, allow registering of customConversions
/**
* Scans the mapping base package for classes annotated with {@link Document}.
*
* @throws ClassNotFoundException if initial entity sets could not be loaded.
*/
protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
String basePackage = getMappingBasePackage();
Set<Class<?>> initialEntitySet = new HashSet<Class<?>>();
if (StringUtils.hasText(basePackage)) {
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(false);
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class));
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(), AbstractCouchbaseConfiguration.class.getClassLoader()));
}
}
return initialEntitySet;
}
/**
* Return the base package to scan for mapped {@link Document}s. Will return the package name of the configuration
* class (the concrete class, not this one here) by default.
* <p/>
* <p>So if you have a {@code com.acme.AppConfig} extending {@link AbstractCouchbaseConfiguration} the base package
* will be considered {@code com.acme} unless the method is overridden to implement alternate behavior.</p>
*
* @return the base package to scan for mapped {@link Document} classes or {@literal null} to not enable scanning for
* entities.
*/
protected String getMappingBasePackage() {
return getClass().getPackage().getName();
}
/**
* Set to true if field names should be abbreviated with the {@link CamelCaseAbbreviatingFieldNamingStrategy}.
*
* @return true if field names should be abbreviated, default is false.
*/
protected boolean abbreviateFieldNames() {
return false;
}
/**
* Configures a {@link FieldNamingStrategy} on the CouchbaseMappingContext instance created.
*
* @return the naming strategy.
*/
protected FieldNamingStrategy fieldNamingStrategy() {
//TODO implement a CouchbaseMappingContext, use this method (update link in javadoc)
return abbreviateFieldNames() ? new CamelCaseAbbreviatingFieldNamingStrategy() : PropertyNameFieldNamingStrategy.INSTANCE;
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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:env /> bean.
*/
static final String COUCHBASE_ENV = "couchbaseEnv";
/**
* Refers to the "<couchbase:cluster />" bean.
*/
static final String COUCHBASE_CLUSTER = "couchbaseCluster";
/**
* Refers to the "<couchbase:bucket />" bean.
*/
static final String COUCHBASE_BUCKET = "couchbaseBucket";
/**
* Refers to the "<couchbase:template />" bean.
*/
static final String COUCHBASE_TEMPLATE = "couchbaseTemplate";
/**
* Refers to the "<couchbase:translation-service />" bean
*/
static final String TRANSLATION_SERVICE = "couchbaseTranslationService";
}

View File

@@ -0,0 +1,242 @@
/*
* 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.core.event.EventBus;
import com.couchbase.client.core.retry.RetryStrategy;
import com.couchbase.client.core.time.Delay;
import com.couchbase.client.deps.io.netty.channel.EventLoopGroup;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import rx.Observable;
import rx.Scheduler;
/**
* A proxy around a {@link CouchbaseEnvironment} that prevents its {@link #shutdown()} method
* to be invoked. Useful when the delegate is not to be lifecycle-managed by Spring.
*
* @author Simon Baslé
*/
public class CouchbaseEnvironmentNoShutdownProxy implements CouchbaseEnvironment {
private final CouchbaseEnvironment delegate;
public CouchbaseEnvironmentNoShutdownProxy(CouchbaseEnvironment delegate) {
this.delegate = delegate;
}
@Override
public Observable<Boolean> shutdown() {
return Observable.just(false);
}
//===== DELEGATION METHODS =====
@Override
public EventLoopGroup ioPool() {
return delegate.ioPool();
}
@Override
public Scheduler scheduler() {
return delegate.scheduler();
}
@Override
public boolean dcpEnabled() {
return delegate.dcpEnabled();
}
@Override
public boolean sslEnabled() {
return delegate.sslEnabled();
}
@Override
public String sslKeystoreFile() {
return delegate.sslKeystoreFile();
}
@Override
public String sslKeystorePassword() {
return delegate.sslKeystorePassword();
}
@Override
public boolean queryEnabled() {
return delegate.queryEnabled();
}
@Override
public int queryPort() {
return delegate.queryPort();
}
@Override
public boolean bootstrapHttpEnabled() {
return delegate.bootstrapHttpEnabled();
}
@Override
public boolean bootstrapCarrierEnabled() {
return delegate.bootstrapCarrierEnabled();
}
@Override
public int bootstrapHttpDirectPort() {
return delegate.bootstrapHttpDirectPort();
}
@Override
public int bootstrapHttpSslPort() {
return delegate.bootstrapHttpSslPort();
}
@Override
public int bootstrapCarrierDirectPort() {
return delegate.bootstrapCarrierDirectPort();
}
@Override
public int bootstrapCarrierSslPort() {
return delegate.bootstrapCarrierSslPort();
}
@Override
public int ioPoolSize() {
return delegate.ioPoolSize();
}
@Override
public int computationPoolSize() {
return delegate.computationPoolSize();
}
@Override
public Delay observeIntervalDelay() {
return delegate.observeIntervalDelay();
}
@Override
public Delay reconnectDelay() {
return delegate.reconnectDelay();
}
@Override
public Delay retryDelay() {
return delegate.retryDelay();
}
@Override
public int requestBufferSize() {
return delegate.requestBufferSize();
}
@Override
public int responseBufferSize() {
return delegate.responseBufferSize();
}
@Override
public int kvEndpoints() {
return delegate.kvEndpoints();
}
@Override
public int viewEndpoints() {
return delegate.viewEndpoints();
}
@Override
public int queryEndpoints() {
return delegate.queryEndpoints();
}
@Override
public String userAgent() {
return delegate.userAgent();
}
@Override
public String packageNameAndVersion() {
return delegate.packageNameAndVersion();
}
@Override
public RetryStrategy retryStrategy() {
return delegate.retryStrategy();
}
@Override
public long maxRequestLifetime() {
return delegate.maxRequestLifetime();
}
@Override
public long autoreleaseAfter() {
return delegate.autoreleaseAfter();
}
@Override
public long keepAliveInterval() {
return delegate.keepAliveInterval();
}
@Override
public EventBus eventBus() {
return delegate.eventBus();
}
@Override
public boolean bufferPoolingEnabled() {
return delegate.bufferPoolingEnabled();
}
@Override
public long managementTimeout() {
return delegate.managementTimeout();
}
@Override
public long queryTimeout() {
return delegate.queryTimeout();
}
@Override
public long viewTimeout() {
return delegate.viewTimeout();
}
@Override
public long kvTimeout() {
return delegate.kvTimeout();
}
@Override
public long connectTimeout() {
return delegate.connectTimeout();
}
@Override
public long disconnectTimeout() {
return delegate.disconnectTimeout();
}
@Override
public boolean dnsSrvEnabled() {
return delegate.dnsSrvEnabled();
}
}

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,43 @@
/*
* 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.core.mapping;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.data.annotation.Persistent;
/**
* Identifies a domain object to be persisted to Couchbase.
*
* @author Michael Nitschinger
*/
@Persistent
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Document {
/**
* An optional expiry time for the document.
*/
int expiry() default 0;
}

View File

@@ -0,0 +1,37 @@
/*
* 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.core.mapping;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Annotation to define custom metadata for document fields.
*
* @author Michael Nitschinger
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Field {
/**
* The key to be used to store the field inside the document.
*/
String value() default "";
}

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");
}
}