diff --git a/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java b/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java new file mode 100644 index 00000000..5d9829e2 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java @@ -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 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> getInitialEntitySet() throws ClassNotFoundException { + String basePackage = getMappingBasePackage(); + Set> initialEntitySet = new HashSet>(); + + 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. + *

+ *

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.

+ * + * @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; + } +} diff --git a/src/main/java/org/springframework/data/couchbase/config/BeanNames.java b/src/main/java/org/springframework/data/couchbase/config/BeanNames.java new file mode 100644 index 00000000..006fd186 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/config/BeanNames.java @@ -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 bean. + */ + static final String COUCHBASE_ENV = "couchbaseEnv"; + /** + * Refers to the "" bean. + */ + static final String COUCHBASE_CLUSTER = "couchbaseCluster"; + + /** + * Refers to the "" bean. + */ + static final String COUCHBASE_BUCKET = "couchbaseBucket"; + + /** + * Refers to the "" bean. + */ + static final String COUCHBASE_TEMPLATE = "couchbaseTemplate"; + + /** + * Refers to the "" bean + */ + static final String TRANSLATION_SERVICE = "couchbaseTranslationService"; + + +} diff --git a/src/main/java/org/springframework/data/couchbase/config/CouchbaseEnvironmentNoShutdownProxy.java b/src/main/java/org/springframework/data/couchbase/config/CouchbaseEnvironmentNoShutdownProxy.java new file mode 100644 index 00000000..6733a942 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/config/CouchbaseEnvironmentNoShutdownProxy.java @@ -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 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(); + } +} diff --git a/src/main/java/org/springframework/data/couchbase/config/CouchbaseJmxParser.java b/src/main/java/org/springframework/data/couchbase/config/CouchbaseJmxParser.java new file mode 100644 index 00000000..22c2969d --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/config/CouchbaseJmxParser.java @@ -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 "" configuration bean. + *

+ * 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)); + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/config/CouchbaseNamespaceHandler.java b/src/main/java/org/springframework/data/couchbase/config/CouchbaseNamespaceHandler.java new file mode 100644 index 00000000..6e4d50d6 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/config/CouchbaseNamespaceHandler.java @@ -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. + *

+ * 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 + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/config/CouchbaseTemplateParser.java b/src/main/java/org/springframework/data/couchbase/config/CouchbaseTemplateParser.java new file mode 100644 index 00000000..c4b0de93 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/config/CouchbaseTemplateParser.java @@ -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 "" bean definitions. + *

+ * 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); + } + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/Document.java b/src/main/java/org/springframework/data/couchbase/core/mapping/Document.java new file mode 100644 index 00000000..5c36f6cf --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/Document.java @@ -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; + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/Field.java b/src/main/java/org/springframework/data/couchbase/core/mapping/Field.java new file mode 100644 index 00000000..0f7a9a22 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/Field.java @@ -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 ""; + +} diff --git a/src/main/java/org/springframework/data/couchbase/monitor/ClientInfo.java b/src/main/java/org/springframework/data/couchbase/monitor/ClientInfo.java new file mode 100644 index 00000000..a5bc7eb0 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/monitor/ClientInfo.java @@ -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 + +} diff --git a/src/main/java/org/springframework/data/couchbase/monitor/ClusterInfo.java b/src/main/java/org/springframework/data/couchbase/monitor/ClusterInfo.java new file mode 100644 index 00000000..094c26e5 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/monitor/ClusterInfo.java @@ -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 available = info.nodeList(); + Collections.shuffle(available); + return available.get(0).getHostName(); + } + + private HashMap fetchPoolInfo() { + return template.getForObject("http://" + + randomAvailableHostname() + ":8091/pools/default", HashMap.class); + } + + private HashMap parseStorageTotals() { + HashMap stats = fetchPoolInfo(); + return (HashMap) stats.get("storageTotals"); + } + +}