DATACOUCH-135 - Migrate to Couchbase SDK 2.0 and add N1QL find method.

Adaptation were made to use SDK 2.0:
 - Reworked the configuration approach (more parts to be configured, like Environment, Cluster and Bucket)
 - Added a new xml schema for configuration of 2.0 version
 - Adapted the template
 - Added N1QL support in the template (findByN1QL)
 - Removed the cache package
 - Adapted the repository
 - Better separated Unit Tests from Integration Tests

All integration and unit tests pass, except one (SimpleCouchbaseRepositoryTests.shouldFindCustom).

Customisation of a ViewQuery will be addressed in another ticket.
This commit is contained in:
Simon Baslé
2015-07-02 18:50:08 +02:00
parent 28dcdee1db
commit b9f23f3fd5
126 changed files with 4203 additions and 3184 deletions

View File

@@ -1,167 +0,0 @@
/*
* Copyright 2013 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.cache;
import com.couchbase.client.CouchbaseClient;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
/**
* The {@link CouchbaseCache} class implements the Spring Cache interface on top of Couchbase Server and the Couchbase
* Java SDK.
*
* @see <a href="http://static.springsource.org/spring/docs/current/spring-framework-reference/html/cache.html">
* Official Spring Cache Reference</a>
* @author Michael Nitschinger
* @author Konrad Król
*/
public class CouchbaseCache implements Cache {
/**
* The actual CouchbaseClient instance.
*/
private final CouchbaseClient client;
/**
* The name of the cache.
*/
private final String name;
/**
* TTL value for objects in this cache
*/
private final int ttl;
/**
* Construct the cache and pass in the CouchbaseClient instance.
*
* @param name the name of the cache reference.
* @param client the CouchbaseClient instance.
*/
public CouchbaseCache(final String name, final CouchbaseClient client) {
this.name = name;
this.client = client;
this.ttl = 0;
}
/**
* Construct the cache and pass in the CouchbaseClient instance.
*
* @param name the name of the cache reference.
* @param client the CouchbaseClient instance.
* @param ttl TTL value for objects in this cache
*/
public CouchbaseCache(final String name, final CouchbaseClient client, int ttl) {
this.name = name;
this.client = client;
this.ttl = ttl;
}
/**
* Returns the name of the cache.
*
* @return the name of the cache.
*/
public final String getName() {
return name;
}
/**
* Returns the actual CouchbaseClient instance.
*
* @return the actual CouchbaseClient instance.
*/
public final CouchbaseClient getNativeCache() {
return client;
}
/**
* Returns the TTL value for this cache.
*
* @return TTL value
*/
public final int getTtl() {
return ttl;
}
/**
* Get an element from the cache.
*
* @param key the key to lookup against.
* @return the fetched value from Couchbase.
*/
public final ValueWrapper get(final Object key) {
String documentId = key.toString();
Object result = client.get(documentId);
return (result != null ? new SimpleValueWrapper(result) : null);
}
@SuppressWarnings("unchecked")
public final <T> T get(final Object key, final Class<T> clazz) {
String documentId = key.toString();
return (T) client.get(documentId);
}
/**
* Store a object in Couchbase.
*
* @param key the Key of the storable object.
* @param value the Object to store.
*/
public final void put(final Object key, final Object value) {
if (value != null) {
String documentId = key.toString();
client.set(documentId, ttl, value);
} else {
evict(key);
}
}
/**
* Remove an object from Couchbase.
*
* @param key the Key of the object to delete.
*/
public final void evict(final Object key) {
String documentId = key.toString();
client.delete(documentId);
}
/**
* Clear the complete cache.
*
* Note that this action is very destructive, so only use it with care.
* Also note that "flush" may not be enabled on the bucket.
*/
public final void clear() {
client.flush();
}
/*
* (non-Javadoc)
* @see org.springframework.cache.Cache#putIfAbsent(java.lang.Object, java.lang.Object)
*/
public ValueWrapper putIfAbsent(Object key, Object value) {
if(get(key) == null) {
put(key, value);
return null;
}
return new SimpleValueWrapper(value);
}
}

View File

@@ -1,105 +0,0 @@
/*
* Copyright 2013 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.cache;
import com.couchbase.client.CouchbaseClient;
import org.springframework.cache.Cache;
import org.springframework.cache.support.AbstractCacheManager;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
/**
* The {@link CouchbaseCacheManager} orchestrates {@link CouchbaseCache} instances.
*
* Since more than one current {@link CouchbaseClient} connection can be used for caching, the
* {@link CouchbaseCacheManager} orchestrates and handles them for the Spring Cache abstraction layer.
*
* @author Michael Nitschinger
* @author Konrad Król
*/
public class CouchbaseCacheManager extends AbstractCacheManager {
/**
* Holds the reference to all stored CouchbaseClient cache connections.
*/
private final HashMap<String, CouchbaseClient> clients;
/**
* Holds the TTL configuration for each cache.
*/
private final HashMap<String, Integer> ttlConfiguration;
/**
* Construct a new CouchbaseCacheManager.
*
* @param clients one ore more CouchbaseClients to reference.
*/
public CouchbaseCacheManager(final HashMap<String, CouchbaseClient> clients) {
this.clients = clients;
this.ttlConfiguration = new HashMap<String, Integer>();
}
/**
* Construct a new CouchbaseCacheManager.
*
* @param clients one ore more CouchbaseClients to reference.
* @param ttlConfiguration one or more TTL values (in seconds)
*/
public CouchbaseCacheManager(final HashMap<String, CouchbaseClient> clients, final HashMap<String, Integer> ttlConfiguration) {
this.clients = clients;
this.ttlConfiguration = ttlConfiguration;
}
/**
* Returns a Map of all CouchbaseClients with name.
*
* @return the actual CouchbaseClient instances.
*/
public final HashMap<String, CouchbaseClient> getClients() {
return clients;
}
/**
* Populates all caches.
*
* @return a collection of loaded caches.
*/
@Override
protected final Collection<? extends Cache> loadCaches() {
Collection<Cache> caches = new LinkedHashSet<Cache>();
for (Map.Entry<String, CouchbaseClient> cache : clients.entrySet()) {
caches.add(new CouchbaseCache(cache.getKey(), cache.getValue(), getTtl(cache.getKey())));
}
return caches;
}
/**
* Returns TTL value for single cache
* @param name cache name
* @return either the cache TTL value or 0 as a default value
*/
private int getTtl ( String name ) {
Integer expirationTime = ttlConfiguration.get(name);
return (expirationTime != null ? expirationTime : 0);
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -16,14 +16,23 @@
package org.springframework.data.couchbase.config;
import com.couchbase.client.CouchbaseClient;
import java.util.Collections;
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.Configuration;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.couchbase.core.CouchbaseFactoryBean;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.convert.CustomConversions;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
@@ -37,29 +46,21 @@ import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
/**
* 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).
* The list of hostnames (or IP addresses) to bootstrap from.
*
* @return the list of bootstrap hosts.
*/
protected abstract List<String> bootstrapHosts();
protected abstract List<String> getBootstrapHosts();
/**
* The name of the bucket to connect to.
@@ -76,39 +77,54 @@ public abstract class AbstractCouchbaseConfiguration {
protected abstract String getBucketPassword();
/**
* Return the {@link CouchbaseClient} instance to connect to.
* 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 = "shutdown")
public CouchbaseClient couchbaseClient() throws Exception {
setLoggerProperty(couchbaseLogger());
return new CouchbaseClient(
bootstrapUris(bootstrapHosts()),
getBucketName(),
getBucketPassword()
);
@Bean(destroyMethod = "disconnect", name = BeanNames.COUCHBASE_CLUSTER)
public Cluster couchbaseCluster() throws Exception {
return CouchbaseCluster.create(couchbaseEnvironment(), getBootstrapHosts());
}
/**
* Specifies the logger to use (defaults to SLF4J).
* Return the {@link Bucket} instance to connect to.
*
* @return the logger property string.
* @throws Exception on Bean construction failure.
*/
protected String couchbaseLogger() {
return CouchbaseFactoryBean.DEFAULT_LOGGER_PROPERTY;
}
/**
* Prepare the logging property before initializing couchbase.
*
* @param logger the logger path to use.
*/
protected static void setLoggerProperty(final String logger) {
Properties systemProperties = System.getProperties();
systemProperties.setProperty("net.spy.log.LoggerImpl", logger);
System.setProperties(systemProperties);
@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());
}
/**
@@ -116,7 +132,7 @@ public abstract class AbstractCouchbaseConfiguration {
*
* @throws Exception on Bean construction failure.
*/
@Bean
@Bean(name = BeanNames.COUCHBASE_TEMPLATE)
public CouchbaseTemplate couchbaseTemplate() throws Exception {
return new CouchbaseTemplate(couchbaseClient(), mappingCouchbaseConverter(), translationService());
}
@@ -159,8 +175,6 @@ public abstract class AbstractCouchbaseConfiguration {
return mappingContext;
}
/**
* Register custom Converters in a {@link CustomConversions} object if required. These
* {@link CustomConversions} will be registered with the {@link #mappingCouchbaseConverter()} and
@@ -202,7 +216,7 @@ public abstract class AbstractCouchbaseConfiguration {
* 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.
* entities.
*/
protected String getMappingBasePackage() {
return getClass().getPackage().getName();
@@ -225,19 +239,4 @@ public abstract class AbstractCouchbaseConfiguration {
protected FieldNamingStrategy fieldNamingStrategy() {
return abbreviateFieldNames() ? new CamelCaseAbbreviatingFieldNamingStrategy() : PropertyNameFieldNamingStrategy.INSTANCE;
}
/**
* Converts the given list of hostnames into parsable URIs.
*
* @param hosts the list of hosts to convert.
* @return the converted URIs.
*/
protected static List<URI> bootstrapUris(List<String> hosts) throws URISyntaxException {
List<URI> uris = new ArrayList<URI>();
for (String host : hosts) {
uris.add(new URI("http://" + host + ":8091/pools"));
}
return uris;
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -20,22 +20,32 @@ 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 {
class BeanNames {
/**
* Refers to the "<couchbase:couchbase />" bean.
* Refers to the "&lt;couchbase:env /&gt;" bean.
*/
static final String COUCHBASE = "couchbase";
static final String COUCHBASE_ENV = "couchbaseEnv";
/**
* Refers to the "&lt;couchbase:cluster /&gt;" bean.
*/
static final String COUCHBASE_CLUSTER = "couchbaseCluster";
/**
* Refers to the "<couchbase:template />" bean.
* Refers to the "&lt;couchbase:bucket /&gt;" bean.
*/
static final String COUCHBASE_BUCKET = "couchbaseBucket";
/**
* Refers to the "&lt;couchbase:template /&gt;" bean.
*/
static final String COUCHBASE_TEMPLATE = "couchbaseTemplate";
/**
* Refers to the "<couchbase:translation-service />" bean
* Refers to the "&lt;couchbase:translation-service /&gt;" bean
*/
static final String TRANSLATION_SERVICE = "translationService";
static final String TRANSLATION_SERVICE = "couchbaseTranslationService";
}

View File

@@ -0,0 +1,78 @@
/*
* 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 com.couchbase.client.java.Cluster;
import com.couchbase.client.java.CouchbaseBucket;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.couchbase.core.CouchbaseExceptionTranslator;
/**
* The Factory Bean to help {@link CouchbaseBucketParser} constructing a {@link Bucket} from a given
* {@link Cluster} reference.
*
* @author Simon Baslé
*/
public class CouchbaseBucketFactoryBean extends AbstractFactoryBean<Bucket> implements PersistenceExceptionTranslator {
private final Cluster cluster;
private final String bucketName;
private final String bucketPassword;
private final PersistenceExceptionTranslator exceptionTranslator = new CouchbaseExceptionTranslator();
public CouchbaseBucketFactoryBean(Cluster cluster) {
this(cluster, null, null);
}
public CouchbaseBucketFactoryBean(Cluster cluster, String bucketName) {
this(cluster, bucketName, null);
}
public CouchbaseBucketFactoryBean(Cluster cluster, String bucketName, String bucketPassword) {
this.cluster = cluster;
this.bucketName = bucketName;
this.bucketPassword = bucketPassword;
}
@Override
public Class<?> getObjectType() {
return CouchbaseBucket.class;
}
@Override
protected Bucket createInstance() throws Exception {
if (bucketName == null) {
return cluster.openBucket();
}
else if (bucketPassword == null) {
return cluster.openBucket(bucketName);
}
else {
return cluster.openBucket(bucketName, bucketPassword);
}
}
@Override
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return exceptionTranslator.translateExceptionIfPossible(ex);
}
}

View File

@@ -0,0 +1,103 @@
/*
* 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 com.couchbase.client.java.Cluster;
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.util.StringUtils;
/**
* The parser for XML definition of a {@link Bucket}, to be constructed from a {@link Cluster} reference.
* If no reference is given, the default reference <code>{@value BeanNames#COUCHBASE_CLUSTER}</code> is used.
*
* See attributes {@link #CLUSTER_REF_ATTR}, {@link #BUCKETNAME_ATTR} and {@link #BUCKETPASSWORD_ATTR}.
*
* @author Simon Baslé
*/
public class CouchbaseBucketParser extends AbstractSingleBeanDefinitionParser {
/**
* The <code>cluster-ref</code> attribute in a bucket definition defines the cluster to build from.
*/
public static final String CLUSTER_REF_ATTR = "cluster-ref";
/**
* The <code>bucketName</code> attribute in a bucket definition defines the name of the bucket to open.
*/
public static final String BUCKETNAME_ATTR = "bucketName";
/**
* The <code>bucketPassword</code> attribute in a bucket definition defines the password of the bucket to open.
*/
public static final String BUCKETPASSWORD_ATTR = "bucketPassword";
/**
* 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_BUCKET;
}
/**
* 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 CouchbaseBucketFactoryBean.class;
}
/**
* Parse the bean definition and build up the bean.
*
* @param element the XML element which contains the attributes.
* @param builder the builder which builds the bean.
*/
@Override
protected void doParse(final Element element, final BeanDefinitionBuilder builder) {
String clusterRef = element.getAttribute(CLUSTER_REF_ATTR);
if (!StringUtils.hasText(clusterRef)) {
clusterRef = BeanNames.COUCHBASE_CLUSTER;
}
builder.addConstructorArgReference(clusterRef);
String bucketName = element.getAttribute(BUCKETNAME_ATTR);
if (StringUtils.hasText(bucketName)) {
builder.addConstructorArgValue(bucketName);
}
String bucketPassword = element.getAttribute(BUCKETPASSWORD_ATTR);
if (StringUtils.hasText(bucketPassword)) {
builder.addConstructorArgValue(bucketPassword);
}
}
}

View File

@@ -0,0 +1,156 @@
/*
* 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.ArrayList;
import java.util.List;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.CouchbaseCluster;
import com.couchbase.client.java.env.CouchbaseEnvironment;
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.util.StringUtils;
import org.springframework.util.xml.DomUtils;
/**
* The XML parser for a {@link Cluster} definition.
*
* Such a definition can be tuned by either referencing a {@link CouchbaseEnvironment} via
* the {@value #CLUSTER_ENVIRONMENT_REF} attribute or define a custom environment inline via
* the &lt;{@value #CLUSTER_ENVIRONMENT_TAG}&gt; tag (not recommended, environments should be
* shared as possible). If no environment reference or inline description is provided, the
* default environment reference {@value BeanNames#COUCHBASE_ENV} is used.
*
* To bootstrap the connection, one can provide IPs or hostnames of nodes to connect to
* via 1 or more &lt;{@value #CLUSTER_NODE_TAG}&gt; tags.
*
* @author Simon Baslé
*/
public class CouchbaseClusterParser extends AbstractSingleBeanDefinitionParser {
/**
* The &lt;node&gt; elements in a cluster definition define the bootstrap hosts to use
*/
public static final String CLUSTER_NODE_TAG = "node";
/**
* The unique &lt;env&gt; element in a cluster definition define the environment customizations.
*
* @see CouchbaseEnvironmentParser CouchbaseEnvironmentParser for the possible fields.
* @see #CLUSTER_ENVIRONMENT_REF CLUSTER_ENVIRONMENT_REF as an alternative (giving a reference to
* an env instead of inline description, lower precedence)
*/
public static final String CLUSTER_ENVIRONMENT_TAG = "env";
/**
* The &lt;env-ref&gt; attribute allows to use a reference to an {@link CouchbaseEnvironment} to
* tune the connection.
*
* @see #CLUSTER_ENVIRONMENT_TAG CLUSTER_ENVIRONMENT_TAG for an inline alternative
* (which takes priority over this reference)
*/
public static final String CLUSTER_ENVIRONMENT_REF = "env-ref";
/**
* 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_CLUSTER;
}
/**
* 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 CouchbaseCluster.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) {
bean.setFactoryMethod("create");
bean.setDestroyMethodName("disconnect");
parseEnvironment(bean, element);
List<Element> nodes = DomUtils.getChildElementsByTagName(element, CLUSTER_NODE_TAG);
if (nodes != null && nodes.size() > 0) {
List<String> bootstrapUrls = new ArrayList<String>(nodes.size());
for (int i = 0; i < nodes.size(); i++) {
bootstrapUrls.add(nodes.get(i).getTextContent());
}
bean.addConstructorArgValue(bootstrapUrls);
}
}
/**
* @return true if a custom environment was parsed and injected (either reference or inline), false if
* the default environment reference was used.
*/
protected boolean parseEnvironment(BeanDefinitionBuilder clusterBuilder, Element clusterElement) {
//any inline environment description would take precedence over a reference
Element envElement = DomUtils.getChildElementByTagName(clusterElement, CLUSTER_ENVIRONMENT_TAG);
if (envElement != null && envElement.hasAttributes()) {
injectEnvElement(clusterBuilder, envElement);
return true;
}
//secondly try to see if an env has been referenced
String envRef = clusterElement.getAttribute(CLUSTER_ENVIRONMENT_REF);
if (StringUtils.hasText(envRef)) {
injectEnvReference(clusterBuilder, envRef);
return true;
}
//if no custom value provided, consider it a reference to the default bean for Couchbase Environment
injectEnvReference(clusterBuilder, BeanNames.COUCHBASE_ENV);
return false;
}
protected void injectEnvElement(BeanDefinitionBuilder clusterBuilder, Element envElement) {
BeanDefinitionBuilder envDefinitionBuilder = BeanDefinitionBuilder
.genericBeanDefinition(CouchbaseEnvironmentFactoryBean.class);
new CouchbaseEnvironmentParser().doParse(envElement, envDefinitionBuilder);
clusterBuilder.addConstructorArgValue(envDefinitionBuilder.getBeanDefinition());
}
protected void injectEnvReference(BeanDefinitionBuilder clusterBuilder, String envRef) {
clusterBuilder.addConstructorArgReference(envRef);
}
}

View File

@@ -0,0 +1,259 @@
/*
* 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.retry.BestEffortRetryStrategy;
import com.couchbase.client.core.retry.FailFastRetryStrategy;
import com.couchbase.client.core.retry.RetryStrategy;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import org.springframework.beans.factory.config.AbstractFactoryBean;
/**
* Factory Bean to help create a CouchbaseEnvironment (by offering setters for supported tuning methods).
*
* @author Simon Baslé
*/
/*package*/ class CouchbaseEnvironmentFactoryBean extends AbstractFactoryBean<CouchbaseEnvironment> {
private static final CouchbaseEnvironment DEFAULT_ENV = DefaultCouchbaseEnvironment.create();
public static final String RETRYSTRATEGY_FAILFAST = "FailFast";
public static final String RETRYSTRATEGY_BESTEFFORT = "BestEffort";
private long managementTimeout = DEFAULT_ENV.managementTimeout();
private long queryTimeout = DEFAULT_ENV.queryTimeout();
private long viewTimeout = DEFAULT_ENV.viewTimeout();
private long kvTimeout = DEFAULT_ENV.kvTimeout();
private long connectTimeout = DEFAULT_ENV.connectTimeout();
private long disconnectTimeout = DEFAULT_ENV.disconnectTimeout();
private boolean dnsSrvEnabled = DEFAULT_ENV.dnsSrvEnabled();
private boolean dcpEnabled = DEFAULT_ENV.dcpEnabled();
private boolean sslEnabled = DEFAULT_ENV.sslEnabled();
private String sslKeystoreFile = DEFAULT_ENV.sslKeystoreFile();
private String sslKeystorePassword = DEFAULT_ENV.sslKeystorePassword();
private boolean queryEnabled = DEFAULT_ENV.queryEnabled();
private int queryPort = DEFAULT_ENV.queryPort();
private boolean bootstrapHttpEnabled = DEFAULT_ENV.bootstrapHttpEnabled();
private boolean bootstrapCarrierEnabled = DEFAULT_ENV.bootstrapCarrierEnabled();
private int bootstrapHttpDirectPort = DEFAULT_ENV.bootstrapHttpDirectPort();
private int bootstrapHttpSslPort = DEFAULT_ENV.bootstrapHttpSslPort();
private int bootstrapCarrierDirectPort = DEFAULT_ENV.bootstrapCarrierDirectPort();
private int bootstrapCarrierSslPort = DEFAULT_ENV.bootstrapCarrierSslPort();
private int ioPoolSize = DEFAULT_ENV.ioPoolSize();
private int computationPoolSize = DEFAULT_ENV.computationPoolSize();
private int responseBufferSize = DEFAULT_ENV.responseBufferSize();
private int requestBufferSize = DEFAULT_ENV.requestBufferSize();
private int kvEndpoints = DEFAULT_ENV.kvEndpoints();
private int viewEndpoints = DEFAULT_ENV.viewEndpoints();
private int queryEndpoints = DEFAULT_ENV.queryEndpoints();
private RetryStrategy retryStrategy = DEFAULT_ENV.retryStrategy();
private long maxRequestLifetime = DEFAULT_ENV.maxRequestLifetime();
private long keepAliveInterval = DEFAULT_ENV.keepAliveInterval();
private long autoreleaseAfter = DEFAULT_ENV.autoreleaseAfter();
private boolean bufferPoolingEnabled = DEFAULT_ENV.bufferPoolingEnabled();
//These are tunings that are not practical to be exposed in a xml configuration
//or not supposed to be modified that easily:
// observeIntervalDelay
// reconnectDelay
// retryDelay
// userAgent
// packageNameAndVersion
// ioPool
// scheduler
// eventBus
@Override
public Class<?> getObjectType() {
return DefaultCouchbaseEnvironment.class;
}
@Override
protected CouchbaseEnvironment createInstance() throws Exception {
return DefaultCouchbaseEnvironment.builder()
.managementTimeout(managementTimeout)
.queryTimeout(queryTimeout)
.viewTimeout(viewTimeout)
.kvTimeout(kvTimeout)
.connectTimeout(connectTimeout)
.disconnectTimeout(disconnectTimeout)
.dnsSrvEnabled(dnsSrvEnabled)
.dcpEnabled(dcpEnabled)
.sslEnabled(sslEnabled)
.sslKeystoreFile(sslKeystoreFile)
.sslKeystorePassword(sslKeystorePassword)
.queryEnabled(queryEnabled)
.queryPort(queryPort)
.bootstrapHttpEnabled(bootstrapHttpEnabled)
.bootstrapCarrierEnabled(bootstrapCarrierEnabled)
.bootstrapHttpDirectPort(bootstrapHttpDirectPort)
.bootstrapHttpSslPort(bootstrapHttpSslPort)
.bootstrapCarrierDirectPort(bootstrapCarrierDirectPort)
.bootstrapCarrierSslPort(bootstrapCarrierSslPort)
.ioPoolSize(ioPoolSize)
.computationPoolSize(computationPoolSize)
.responseBufferSize(responseBufferSize)
.requestBufferSize(requestBufferSize)
.kvEndpoints(kvEndpoints)
.viewEndpoints(viewEndpoints)
.queryEndpoints(queryEndpoints)
.retryStrategy(retryStrategy)
.maxRequestLifetime(maxRequestLifetime)
.keepAliveInterval(keepAliveInterval)
.autoreleaseAfter(autoreleaseAfter)
.bufferPoolingEnabled(bufferPoolingEnabled)
.build();
}
/**
* Sets the {@link RetryStrategy} to use from an enum-like String value.
* Either "FailFast" or "BestEffort" are recognized.
*
* @param retryStrategy the string value enum from which to choose a strategy.
*/
public void setRetryStrategy(String retryStrategy) {
if (RETRYSTRATEGY_FAILFAST.equals(retryStrategy)){
this.retryStrategy = FailFastRetryStrategy.INSTANCE;
} else if (RETRYSTRATEGY_BESTEFFORT.equals(retryStrategy)) {
this.retryStrategy = BestEffortRetryStrategy.INSTANCE;
}
}
//==== SETTERS for the factory bean ====
public void setManagementTimeout(long managementTimeout) {
this.managementTimeout = managementTimeout;
}
public void setQueryTimeout(long queryTimeout) {
this.queryTimeout = queryTimeout;
}
public void setViewTimeout(long viewTimeout) {
this.viewTimeout = viewTimeout;
}
public void setKvTimeout(long kvTimeout) {
this.kvTimeout = kvTimeout;
}
public void setConnectTimeout(long connectTimeout) {
this.connectTimeout = connectTimeout;
}
public void setDisconnectTimeout(long disconnectTimeout) {
this.disconnectTimeout = disconnectTimeout;
}
public void setDnsSrvEnabled(boolean dnsSrvEnabled) {
this.dnsSrvEnabled = dnsSrvEnabled;
}
public void setDcpEnabled(boolean dcpEnabled) {
this.dcpEnabled = dcpEnabled;
}
public void setSslEnabled(boolean sslEnabled) {
this.sslEnabled = sslEnabled;
}
public void setSslKeystoreFile(String sslKeystoreFile) {
this.sslKeystoreFile = sslKeystoreFile;
}
public void setSslKeystorePassword(String sslKeystorePassword) {
this.sslKeystorePassword = sslKeystorePassword;
}
public void setQueryEnabled(boolean queryEnabled) {
this.queryEnabled = queryEnabled;
}
public void setQueryPort(int queryPort) {
this.queryPort = queryPort;
}
public void setBootstrapHttpEnabled(boolean bootstrapHttpEnabled) {
this.bootstrapHttpEnabled = bootstrapHttpEnabled;
}
public void setBootstrapCarrierEnabled(boolean bootstrapCarrierEnabled) {
this.bootstrapCarrierEnabled = bootstrapCarrierEnabled;
}
public void setBootstrapHttpDirectPort(int bootstrapHttpDirectPort) {
this.bootstrapHttpDirectPort = bootstrapHttpDirectPort;
}
public void setBootstrapHttpSslPort(int bootstrapHttpSslPort) {
this.bootstrapHttpSslPort = bootstrapHttpSslPort;
}
public void setBootstrapCarrierDirectPort(int bootstrapCarrierDirectPort) {
this.bootstrapCarrierDirectPort = bootstrapCarrierDirectPort;
}
public void setBootstrapCarrierSslPort(int bootstrapCarrierSslPort) {
this.bootstrapCarrierSslPort = bootstrapCarrierSslPort;
}
public void setIoPoolSize(int ioPoolSize) {
this.ioPoolSize = ioPoolSize;
}
public void setComputationPoolSize(int computationPoolSize) {
this.computationPoolSize = computationPoolSize;
}
public void setResponseBufferSize(int responseBufferSize) {
this.responseBufferSize = responseBufferSize;
}
public void setRequestBufferSize(int requestBufferSize) {
this.requestBufferSize = requestBufferSize;
}
public void setKvEndpoints(int kvEndpoints) {
this.kvEndpoints = kvEndpoints;
}
public void setViewEndpoints(int viewEndpoints) {
this.viewEndpoints = viewEndpoints;
}
public void setQueryEndpoints(int queryEndpoints) {
this.queryEndpoints = queryEndpoints;
}
public void setMaxRequestLifetime(long maxRequestLifetime) {
this.maxRequestLifetime = maxRequestLifetime;
}
public void setKeepAliveInterval(long keepAliveInterval) {
this.keepAliveInterval = keepAliveInterval;
}
public void setAutoreleaseAfter(long autoreleaseAfter) {
this.autoreleaseAfter = autoreleaseAfter;
}
public void setBufferPoolingEnabled(boolean bufferPoolingEnabled) {
this.bufferPoolingEnabled = bufferPoolingEnabled;
}
}

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,141 @@
/*
* 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 static org.springframework.data.config.ParsingUtils.setPropertyValue;
import com.couchbase.client.core.retry.RetryStrategy;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
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.util.StringUtils;
/**
* Allows creation of a {@link DefaultCouchbaseEnvironment} via spring XML configuration.
* <p>
* The following properties are supported:<br/><ul>
* <li>{@link DefaultCouchbaseEnvironment.Builder#managementTimeout(long) managementTimeout}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#queryTimeout(long) queryTimeout}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#viewTimeout(long) viewTimeout}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#kvTimeout(long) kvTimeout}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#connectTimeout(long) connectTimeout}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#disconnectTimeout(long) disconnectTimeout}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#dnsSrvEnabled(boolean) dnsSrvEnabled}</li>
*
* <li>{@link DefaultCouchbaseEnvironment.Builder#dcpEnabled(boolean) dcpEnabled}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#sslEnabled(boolean) sslEnabled}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#sslKeystoreFile(String) sslKeystoreFile}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#sslKeystorePassword(String) sslKeystorePassword}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#queryEnabled(boolean) queryEnabled}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#queryPort(int) queryPort}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#bootstrapHttpEnabled(boolean) bootstrapHttpEnabled}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#bootstrapCarrierEnabled(boolean) bootstrapCarrierEnabled}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#bootstrapHttpDirectPort(int) bootstrapHttpDirectPort}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#bootstrapHttpSslPort(int) bootstrapHttpSslPort}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#bootstrapCarrierDirectPort(int) bootstrapCarrierDirectPort}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#bootstrapCarrierSslPort(int) bootstrapCarrierSslPort}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#ioPoolSize(int) ioPoolSize}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#computationPoolSize(int) computationPoolSize}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#responseBufferSize(int) responseBufferSize}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#requestBufferSize(int) requestBufferSize}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#kvEndpoints(int) kvEndpoints}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#viewEndpoints(int) viewEndpoints}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#queryEndpoints(int) queryEndpoints}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#retryStrategy(RetryStrategy) retryStrategy}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#maxRequestLifetime(long) maxRequestLifetime}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#keepAliveInterval(long) keepAliveInterval}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#autoreleaseAfter(long) autoreleaseAfter}</li>
* <li>{@link DefaultCouchbaseEnvironment.Builder#bufferPoolingEnabled(boolean) bufferPoolingEnabled}</li>
* </ul>
*
* @author Simon Baslé
*/
public class CouchbaseEnvironmentParser 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_ENV;
}
/**
* 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 CouchbaseEnvironmentFactoryBean.class;
}
/**
* Parse the bean definition and build up the bean.
*
* @param envElement the XML element which contains the attributes.
* @param envDefinitionBuilder the builder which builds the bean.
*/
@Override
protected void doParse(final Element envElement, final BeanDefinitionBuilder envDefinitionBuilder) {
setPropertyValue(envDefinitionBuilder, envElement, "managementTimeout", "managementTimeout");
setPropertyValue(envDefinitionBuilder, envElement, "queryTimeout", "queryTimeout");
setPropertyValue(envDefinitionBuilder, envElement, "viewTimeout", "viewTimeout");
setPropertyValue(envDefinitionBuilder, envElement, "kvTimeout", "kvTimeout");
setPropertyValue(envDefinitionBuilder, envElement, "connectTimeout", "connectTimeout");
setPropertyValue(envDefinitionBuilder, envElement, "disconnectTimeout", "disconnectTimeout");
setPropertyValue(envDefinitionBuilder, envElement, "dnsSrvEnabled", "dnsSrvEnabled");
setPropertyValue(envDefinitionBuilder, envElement, "dcpEnabled", "dcpEnabled");
setPropertyValue(envDefinitionBuilder, envElement, "sslEnabled", "sslEnabled");
setPropertyValue(envDefinitionBuilder, envElement, "sslKeystoreFile", "sslKeystoreFile");
setPropertyValue(envDefinitionBuilder, envElement, "sslKeystorePassword", "sslKeystorePassword");
setPropertyValue(envDefinitionBuilder, envElement, "queryEnabled", "queryEnabled");
setPropertyValue(envDefinitionBuilder, envElement, "queryPort", "queryPort");
setPropertyValue(envDefinitionBuilder, envElement, "bootstrapHttpEnabled", "bootstrapHttpEnabled");
setPropertyValue(envDefinitionBuilder, envElement, "bootstrapCarrierEnabled", "bootstrapCarrierEnabled");
setPropertyValue(envDefinitionBuilder, envElement, "bootstrapHttpDirectPort", "bootstrapHttpDirectPort");
setPropertyValue(envDefinitionBuilder, envElement, "bootstrapHttpSslPort", "bootstrapHttpSslPort");
setPropertyValue(envDefinitionBuilder, envElement, "bootstrapCarrierDirectPort", "bootstrapCarrierDirectPort");
setPropertyValue(envDefinitionBuilder, envElement, "bootstrapCarrierSslPort", "bootstrapCarrierSslPort");
setPropertyValue(envDefinitionBuilder, envElement, "ioPoolSize", "ioPoolSize");
setPropertyValue(envDefinitionBuilder, envElement, "computationPoolSize", "computationPoolSize");
setPropertyValue(envDefinitionBuilder, envElement, "responseBufferSize", "responseBufferSize");
setPropertyValue(envDefinitionBuilder, envElement, "requestBufferSize", "requestBufferSize");
setPropertyValue(envDefinitionBuilder, envElement, "kvEndpoints", "kvEndpoints");
setPropertyValue(envDefinitionBuilder, envElement, "viewEndpoints", "viewEndpoints");
setPropertyValue(envDefinitionBuilder, envElement, "queryEndpoints", "queryEndpoints");
setPropertyValue(envDefinitionBuilder, envElement, "maxRequestLifetime", "maxRequestLifetime");
setPropertyValue(envDefinitionBuilder, envElement, "keepAliveInterval", "keepAliveInterval");
setPropertyValue(envDefinitionBuilder, envElement, "autoreleaseAfter", "autoreleaseAfter");
setPropertyValue(envDefinitionBuilder, envElement, "bufferPoolingEnabled", "bufferPoolingEnabled");
//retry strategy is particular, in the xsd this is an enum (FailFast, BestEffort)
setPropertyValue(envDefinitionBuilder, envElement, "retryStrategy", "retryStrategy");
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -16,6 +16,9 @@
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;
@@ -25,15 +28,15 @@ 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;
import org.w3c.dom.Element;
/**
* 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 com.couchbase.client.CouchbaseClient} object is solved by through the "couchbase-ref" attribute.
* {@link Bucket} object is solved through the "bucket-ref" attribute.
*
* @author Michael Nitschinger
* @author Simon Baslé
*/
public class CouchbaseJmxParser implements BeanDefinitionParser {
@@ -45,27 +48,28 @@ public class CouchbaseJmxParser implements BeanDefinitionParser {
* @return null, because no bean instance needs to be returned.
*/
public BeanDefinition parse(final Element element, final ParserContext parserContext) {
String name = element.getAttribute("couchbase-ref");
if (!StringUtils.hasText(name)) {
name = BeanNames.COUCHBASE;
String bucketName = element.getAttribute("bucket-ref");
if (!StringUtils.hasText(bucketName)) {
bucketName = BeanNames.COUCHBASE_BUCKET;
}
registerJmxComponents(name, element, parserContext);
registerJmxComponents(bucketName, element, parserContext);
return null;
}
/**
* Register the JMX components in the context.
*
* @param refName the reference name to the couchbase client.
* @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 refName, final Element element, final ParserContext parserContext) {
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, refName, eleSource, parserContext);
createBeanDefEntry(ClusterInfo.class, compositeDef, refName, eleSource, parserContext);
createBeanDefEntry(ClientInfo.class, compositeDef, refBucketName, eleSource, parserContext);
createBeanDefEntry(ClusterInfo.class, compositeDef, refBucketName, eleSource, parserContext);
parserContext.registerComponent(compositeDef);
}
@@ -75,12 +79,12 @@ public class CouchbaseJmxParser implements BeanDefinitionParser {
*
* @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 refName the reference name to the couchbase bucket.
* @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) {
final String refName, final Object eleSource, final ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
builder.getRawBeanDefinition().setSource(eleSource);
builder.addConstructorArgReference(refName);

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -16,13 +16,11 @@
package org.springframework.data.couchbase.config;
import org.springframework.beans.factory.xml.NamespaceHandler;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import org.springframework.data.couchbase.repository.config.CouchbaseRepositoryConfigurationExtension;
import org.springframework.data.repository.config.RepositoryBeanDefinitionParser;
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
/**
* {@link org.springframework.beans.factory.xml.NamespaceHandler} for Couchbase configuration.
* {@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.
@@ -35,9 +33,10 @@ public class CouchbaseNamespaceHandler extends NamespaceHandlerSupport {
* Register bean definition parsers in the namespace handler.
*/
public final void init() {
RepositoryConfigurationExtension extension = new CouchbaseRepositoryConfigurationExtension();
registerBeanDefinitionParser("repositories", new RepositoryBeanDefinitionParser(extension));
registerBeanDefinitionParser("couchbase", new CouchbaseParser());
//TODO repositories (CouchbaseRepositoryConfigurationExtension and RepositoryBeanDefinitionParser)
registerBeanDefinitionParser("env", new CouchbaseEnvironmentParser());
registerBeanDefinitionParser("cluster", new CouchbaseClusterParser());
registerBeanDefinitionParser("bucket", new CouchbaseBucketParser());
registerBeanDefinitionParser("jmx", new CouchbaseJmxParser());
registerBeanDefinitionParser("template", new CouchbaseTemplateParser());
registerBeanDefinitionParser("translation-service", new CouchbaseTranslationServiceParser());

View File

@@ -1,89 +0,0 @@
/*
* Copyright 2013 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.CouchbaseClient;
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.config.ParsingUtils;
import org.springframework.data.couchbase.core.CouchbaseFactoryBean;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
import java.util.Properties;
/**
* Parser for "<couchbase:couchbase />" bean definitions.
* <p/>
* The outcome of this bean definition parser will be a constructed {@link CouchbaseClient}.
*
* @author Michael Nitschinger
*/
public class CouchbaseParser extends AbstractSingleBeanDefinitionParser {
/**
* 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 CouchbaseFactoryBean.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) {
ParsingUtils.setPropertyValue(bean, element, "host", "host");
ParsingUtils.setPropertyValue(bean, element, "bucket", "bucket");
ParsingUtils.setPropertyValue(bean, element, "password", "password");
setLogger();
}
private void setLogger() {
Properties systemProperties = System.getProperties();
systemProperties.put("net.spy.log.LoggerImpl", CouchbaseFactoryBean.DEFAULT_LOGGER_PROPERTY);
System.setProperties(systemProperties);
}
/**
* 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;
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -16,13 +16,14 @@
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;
import org.w3c.dom.Element;
/**
* Parser for "<couchbase:template />" bean definitions.
@@ -39,7 +40,6 @@ public class CouchbaseTemplateParser extends AbstractSingleBeanDefinitionParser
* @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
@@ -52,7 +52,6 @@ public class CouchbaseTemplateParser extends AbstractSingleBeanDefinitionParser
* Defines the bean class that will be constructed.
*
* @param element the XML element which contains the attributes.
*
* @return the class type to instantiate.
*/
@Override
@@ -68,11 +67,11 @@ public class CouchbaseTemplateParser extends AbstractSingleBeanDefinitionParser
*/
@Override
protected void doParse(final Element element, final BeanDefinitionBuilder bean) {
String clientRef = element.getAttribute("client-ref");
String bucketRef = element.getAttribute("bucket-ref");
String converterRef = element.getAttribute("converter-ref");
String translationServiceRef = element.getAttribute("translation-service-ref");
bean.addConstructorArgReference(StringUtils.hasText(clientRef) ? clientRef : BeanNames.COUCHBASE);
bean.addConstructorArgReference(StringUtils.hasText(bucketRef) ? bucketRef : BeanNames.COUCHBASE_BUCKET);
if (StringUtils.hasText(converterRef)) {
bean.addConstructorArgReference(converterRef);

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -17,13 +17,14 @@
package org.springframework.data.couchbase.config;
import com.fasterxml.jackson.databind.ObjectMapper;
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.convert.translation.JacksonTranslationService;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Enables Parsing of the "<couchbase:translation-service />" configuration bean.
@@ -36,7 +37,6 @@ public class CouchbaseTranslationServiceParser extends AbstractSingleBeanDefinit
* Defines the bean class that will be constructed.
*
* @param element the XML element which contains the attributes.
*
* @return the class type to instantiate.
*/
@Override
@@ -55,17 +55,18 @@ public class CouchbaseTranslationServiceParser extends AbstractSingleBeanDefinit
final String objectMapper = element.getAttribute("objectMapper");
if (StringUtils.hasText(objectMapper)) {
bean.addPropertyReference("objectMapper", objectMapper);
} else {
}
else {
bean.addPropertyValue("objectMapper", new ObjectMapper());
}
}
/**
* 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 (e.g., "translationService")
*/
@Override

View File

@@ -0,0 +1,5 @@
/**
* This package contains all classes needed for specific configuration of
* Spring Data Couchbase.
*/
package org.springframework.data.couchbase.config;

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -30,8 +30,8 @@ public interface BucketCallback<T> {
* The enclosed body will be executed on the connected bucket.
*
* @return the result of the enclosed execution.
* @throws TimeoutException if the enclosed operation timed out.
* @throws ExecutionException if the result could not be retrieved because of a thrown exception before.
* @throws TimeoutException if the enclosed operation timed out.
* @throws ExecutionException if the result could not be retrieved because of a thrown exception before.
* @throws InterruptedException if the enclosed operation was interrupted.
*/
T doInBucket() throws TimeoutException, ExecutionException, InterruptedException;

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013, 2014 the original author or authors.
* 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
* 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,

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -16,19 +16,41 @@
package org.springframework.data.couchbase.core;
import com.couchbase.client.ObservedException;
import com.couchbase.client.ObservedModifiedException;
import com.couchbase.client.ObservedTimeoutException;
import com.couchbase.client.protocol.views.InvalidViewException;
import com.couchbase.client.vbucket.ConnectionException;
import java.util.concurrent.TimeoutException;
import com.couchbase.client.core.BackpressureException;
import com.couchbase.client.core.BucketClosedException;
import com.couchbase.client.core.DocumentConcurrentlyModifiedException;
import com.couchbase.client.core.ReplicaNotConfiguredException;
import com.couchbase.client.core.RequestCancelledException;
import com.couchbase.client.core.ServiceNotAvailableException;
import com.couchbase.client.core.config.ConfigurationException;
import com.couchbase.client.core.endpoint.SSLException;
import com.couchbase.client.core.endpoint.kv.AuthenticationException;
import com.couchbase.client.core.env.EnvironmentException;
import com.couchbase.client.core.state.NotConnectedException;
import com.couchbase.client.java.error.BucketDoesNotExistException;
import com.couchbase.client.java.error.CASMismatchException;
import com.couchbase.client.java.error.DesignDocumentException;
import com.couchbase.client.java.error.DocumentAlreadyExistsException;
import com.couchbase.client.java.error.DurabilityException;
import com.couchbase.client.java.error.InvalidPasswordException;
import com.couchbase.client.java.error.RequestTooBigException;
import com.couchbase.client.java.error.TemporaryFailureException;
import com.couchbase.client.java.error.TemporaryLockFailureException;
import com.couchbase.client.java.error.TranscodingException;
import com.couchbase.client.java.error.ViewDoesNotExistException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.dao.QueryTimeoutException;
import org.springframework.dao.TransientDataAccessResourceException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import java.util.concurrent.CancellationException;
/**
* Simple {@link PersistenceExceptionTranslator} for Couchbase.
@@ -38,6 +60,7 @@ import java.util.concurrent.CancellationException;
* should not be translated.
*
* @author Michael Nitschinger
* @author Simon Baslé
*/
public class CouchbaseExceptionTranslator implements PersistenceExceptionTranslator {
@@ -45,28 +68,59 @@ public class CouchbaseExceptionTranslator implements PersistenceExceptionTransla
* Translate Couchbase specific exceptions to spring exceptions if possible.
*
* @param ex the exception to translate.
*
* @return the translated exception or null.
*/
@Override
public final DataAccessException translateExceptionIfPossible(final RuntimeException ex) {
if (ex instanceof ConnectionException) {
if (ex instanceof InvalidPasswordException
|| ex instanceof NotConnectedException
|| ex instanceof ConfigurationException
|| ex instanceof EnvironmentException
|| ex instanceof InvalidPasswordException
|| ex instanceof SSLException
|| ex instanceof ServiceNotAvailableException
|| ex instanceof BucketClosedException
|| ex instanceof BucketDoesNotExistException
|| ex instanceof AuthenticationException) {
return new DataAccessResourceFailureException(ex.getMessage(), ex);
}
if (ex instanceof ObservedException
|| ex instanceof ObservedTimeoutException
|| ex instanceof ObservedModifiedException) {
if (ex instanceof DocumentAlreadyExistsException) {
return new DuplicateKeyException(ex.getMessage(), ex);
}
if (ex instanceof CASMismatchException
|| ex instanceof DocumentConcurrentlyModifiedException
|| ex instanceof ReplicaNotConfiguredException
|| ex instanceof DurabilityException) {
return new DataIntegrityViolationException(ex.getMessage(), ex);
}
if (ex instanceof CancellationException) {
throw new OperationCancellationException(ex.getMessage(), ex);
if (ex instanceof RequestCancelledException
|| ex instanceof BackpressureException) {
return new OperationCancellationException(ex.getMessage(), ex);
}
if (ex instanceof InvalidViewException) {
throw new InvalidDataAccessResourceUsageException(ex.getMessage(), ex);
if (ex instanceof ViewDoesNotExistException
|| ex instanceof RequestTooBigException
|| ex instanceof DesignDocumentException) {
return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex);
}
if (ex instanceof TemporaryLockFailureException
|| ex instanceof TemporaryFailureException) {
return new TransientDataAccessResourceException(ex.getMessage(), ex);
}
if ((ex instanceof RuntimeException && ex.getCause() instanceof TimeoutException)) {
return new QueryTimeoutException(ex.getMessage(), ex);
}
if (ex instanceof TranscodingException) {
//note: the more specific CouchbaseQueryExecutionException should be thrown by the template
//when dealing with TranscodingException in the query/n1ql methods.
return new DataRetrievalFailureException(ex.getMessage(), ex);
}
// Unable to translate exception, therefore just throw the original!

View File

@@ -1,318 +0,0 @@
/*
* Copyright 2013 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;
import com.couchbase.client.CouchbaseClient;
import com.couchbase.client.CouchbaseConnectionFactory;
import com.couchbase.client.CouchbaseConnectionFactoryBuilder;
import net.spy.memcached.FailureMode;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Convenient Factory for configuring a {@link CouchbaseClient}.
*
* To set the properties correctly on the {@link CouchbaseClient} a {@link CouchbaseConnectionFactoryBuilder} is used.
* After all properties are set, the client is constructed and used.
*
* @author Michael Nitschinger
*/
public class CouchbaseFactoryBean implements FactoryBean<CouchbaseClient>, InitializingBean,
DisposableBean, PersistenceExceptionTranslator {
/**
* Defines the default hostname to be used if no other list is supplied.
*/
public static final String DEFAULT_NODE = "127.0.0.1";
/**
* Defines the default bucket name to be used if no other bucket name is supplied.
*/
public static final String DEFAULT_BUCKET = "default";
/**
* Defines the password of the default bucket.
*/
public static final String DEFAULT_PASSWORD = "";
/**
* The name of the default shutdown method to call when the context is destroyed.
*/
public static final String DEFAULT_DESTROY_METHOD = "shutdown";
/**
* Use SLF4J as the default logger if not instructed otherwise.
*/
public static final String DEFAULT_LOGGER_PROPERTY = "net.spy.memcached.compat.log.SLF4JLogger";
/**
* Holds the enclosed {@link CouchbaseClient}.
*/
private CouchbaseClient couchbaseClient;
/**
* The exception translator is used to properly map exceptions to spring-type exceptions.
*/
private PersistenceExceptionTranslator exceptionTranslator = new CouchbaseExceptionTranslator();
/**
* Contains the actual bucket name.
*/
private String bucket;
/**
* Contains the actual bucket password.
*/
private String password;
/**
* Contains the list of nodes to connect to.
*/
private List<URI> nodes;
/**
* The builder which allows to customize client settings.
*/
private final CouchbaseConnectionFactoryBuilder builder = new CouchbaseConnectionFactoryBuilder();
/**
* Set the observe poll interval in miliseconds.
*
* @param interval the observe poll interval.
*/
public void setObservePollInterval(final int interval) {
builder.setObsPollInterval(interval);
}
/**
* Set the maximum number of polls.
*
* @param max the maximum number of polls.
*/
public void setObservePollMax(final int max) {
builder.setObsPollMax(max);
}
/**
* Set the reconnect threshold time in seconds.
*
* @param time the reconnect threshold time.
*/
public void setReconnectThresholdTime(final int time) {
builder.setReconnectThresholdTime(time, TimeUnit.SECONDS);
}
/**
* Set the view timeout in miliseconds.
*
* @param timeout the view timeout.
*/
public void setViewTimeout(final int timeout) {
builder.setViewTimeout(timeout);
}
/**
* Set the failure mode if memcached buckets are used.
*
* See the proper values of {@link FailureMode} to use.
*
* @param mode the failure mode.
*/
public void setFailureMode(final String mode) {
builder.setFailureMode(FailureMode.valueOf(mode));
}
/**
* Set the operation timeout in miliseconds.
*
* @param timeout the operation timeout.
*/
public void setOpTimeout(final int timeout) {
builder.setOpTimeout(timeout);
}
/**
* Set the operation queue maximum block time in miliseconds.
*
* @param time the operation queue maximum block time.
*/
public void setOpQueueMaxBlockTime(final int time) {
builder.setOpQueueMaxBlockTime(time);
}
/**
* Shutdown the client when the bean is destroyed.
*
* @throws Exception if shutdown failed.
*/
@Override
public void destroy() throws Exception {
couchbaseClient.shutdown();
}
/**
* Return the underlying {@link CouchbaseClient}.
*
* @return the client object.
* @throws Exception if returning the client failed.
*/
@Override
public CouchbaseClient getObject() throws Exception {
return couchbaseClient;
}
/**
* Returns the object type of the client.
*
* @return the {@link CouchbaseClient} class.
*/
@Override
public Class<?> getObjectType() {
return CouchbaseClient.class;
}
/**
* The client should be returned as a singleton.
*
* @return returns true.
*/
@Override
public boolean isSingleton() {
return true;
}
/**
* Set the bucket to be used.
*
* @param bucket the bucket to use.
*/
public void setBucket(final String bucket) {
this.bucket = bucket;
}
/**
* Set the password.
*
* @param password the password to use.
*/
public void setPassword(final String password) {
this.password = password;
}
/**
* Set the array of nodes from a delimited list of hosts.
*
* @param hosts a comma separated list of hosts.
*/
public void setHost(final String hosts) {
this.nodes = convertHosts(hosts);
}
/**
* Convert a list of hosts into a URI format that can be used by the {@link CouchbaseClient}.
*
* To make it simple to use, the list of hosts can be passed in as a comma separated list. This list gets parsed
* and converted into a URI format that is suitable for the underlying {@link CouchbaseClient} object.
*
* @param hosts the host list to convert.
* @return the converted list with URIs.
*/
private List<URI> convertHosts(final String hosts) {
String[] split = hosts.split(",");
List<URI> nodes = new ArrayList<URI>();
try {
for (int i = 0; i < split.length; i++) {
nodes.add(new URI("http://" + split[i] + ":8091/pools"));
}
} catch (URISyntaxException ex) {
throw new BeanCreationException("Could not convert host list." + ex);
}
return nodes;
}
/**
* Set the nodes as an array of URIs.
*
* @param nodes the nodes to connect to.
*/
public void setNodes(final URI[] nodes) {
this.nodes = filterNonNullElementsAsList(nodes);
}
/**
* Convert the array of elements to a list and filter empty or null elements.
*
* @param elements the elements to convert.
* @param <T> the type of the elements.
* @return the converted list.
*/
private <T> List<T> filterNonNullElementsAsList(T[] elements) {
if (elements == null) {
return Collections.emptyList();
}
List<T> candidateElements = new ArrayList<T>();
for (T element : elements) {
if (element != null) {
candidateElements.add(element);
}
}
return Collections.unmodifiableList(candidateElements);
}
/**
* Instantiate the {@link CouchbaseClient}.
*
* @throws Exception if something goes wrong during instantiation.
*/
@Override
public void afterPropertiesSet() throws Exception {
nodes = nodes != null ? nodes : Arrays.asList(new URI("http://" + DEFAULT_NODE + ":8091/pools"));
bucket = bucket != null ? bucket : DEFAULT_BUCKET;
password = password != null ? password : DEFAULT_PASSWORD;
CouchbaseConnectionFactory factory = builder.buildCouchbaseConnection(nodes, bucket, password);
couchbaseClient = new CouchbaseClient(factory);
}
/**
* Translate exception if possible.
*
* @param ex the exception to translate.
* @return the translate exception.
*/
@Override
public DataAccessException translateExceptionIfPossible(final RuntimeException ex) {
return exceptionTranslator.translateExceptionIfPossible(ex);
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -17,20 +17,26 @@
package org.springframework.data.couchbase.core;
import com.couchbase.client.CouchbaseClient;
import com.couchbase.client.protocol.views.Query;
import com.couchbase.client.protocol.views.ViewResponse;
import net.spy.memcached.PersistTo;
import net.spy.memcached.ReplicateTo;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import java.util.Collection;
import java.util.List;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.PersistTo;
import com.couchbase.client.java.ReplicateTo;
import com.couchbase.client.java.query.Query;
import com.couchbase.client.java.query.QueryParams;
import com.couchbase.client.java.query.QueryResult;
import com.couchbase.client.java.query.Statement;
import com.couchbase.client.java.view.ViewQuery;
import com.couchbase.client.java.view.ViewResult;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
/**
* Defines common operations on the Couchbase data source, most commonly implemented by {@link CouchbaseTemplate}.
*
* @author Michael Nitschinger
* @author Simon Baslé
*/
public interface CouchbaseOperations {
@@ -169,50 +175,71 @@ public interface CouchbaseOperations {
*
* @param id the unique ID of the document.
* @param entityClass the entity to map to.
*
* @return returns the found object or null otherwise.
*/
<T> T findById(String id, Class<T> entityClass);
//TODO add javadoc link to setIncludeDocs when GA
/**
* Query a View for a list of documents of type T.
* <p/>
* <p>There is no need to {@link Query#setIncludeDocs(boolean)} explicitely, because it will be set to true all the
* time. It is valid to pass in a empty constructed {@link Query} object.</p>
* <p>There is no need to setIncludeDocs(boolean) explicitly, because it will be set to true all the
* time. It is valid to pass in a empty constructed {@link ViewQuery} object.</p>
* <p/>
* <p>This method does not work with reduced views, because they by design do not contain references to original
* objects. Use the provided {@link #queryView} method for more flexibility and direct access.</p>
*
* @param design the name of the design document.
* @param view the name of the viewName.
* @param query the Query object to customize the viewName query.
* @param query the Query object (also specifying view design document and view name).
* @param entityClass the entity to map to.
*
* @return the converted collection
*/
<T> List<T> findByView(String design, String view, Query query, Class<T> entityClass);
<T> List<T> findByView(ViewQuery query, Class<T> entityClass);
/**
* Query a View with direct access to the {@link ViewResponse}.
* Query a View with direct access to the {@link ViewResult}.
* <p>This method is available to ease the working with views by still wrapping exceptions into the Spring
* infrastructure.</p>
* <p>It is especially needed if you want to run reduced viewName queries, because they can't be mapped onto entities
* directly.</p>
*
* @param design the name of the designDocument document.
* @param view the name of the viewName.
* @param query the Query object to customize the viewName query.
*
* @return ViewResponse containing the results of the query.
* @param query the Query object (also specifying view design document and view name).
* @return ViewResult containing the results of the query.
*/
ViewResponse queryView(String design, String view, Query query);
ViewResult queryView(ViewQuery query);
/**
* Query the N1QL Service for JSON data of type T. This is done via a {@link Query} that can
* contain a {@link Statement}, additional query parameters ({@link QueryParams})
* and placeholder values if the statement contains placeholders.
* <p>Use {@link Query}'s factory methods to construct this.</p>
*
* @param n1ql the N1QL query.
* @param entityClass the target class for the returned entities.
* @param <T> the entity class
* @return the list of entities matching this query.
*/
<T> List<T> findByN1QL(Query n1ql, Class<T> entityClass);
/**
* Query the N1QL Service with direct access to the {@link QueryResult}.
* <p>
* This is done via a {@link Query} that can
* contain a {@link Statement}, additional query parameters ({@link QueryParams})
* and placeholder values if the statement contains placeholders.</p>
* <p>
* Use {@link Query}'s factory methods to construct this.</p>
*
* @param n1ql the N1QL query.
* @return {@link QueryResult} containing the results of the n1ql query.
*/
QueryResult queryN1QL(Query n1ql);
/**
* Checks if the given document exists.
*
* @param id the unique ID of the document.
*
* @return whether the document could be found or not.
*/
boolean exists(String id);
@@ -262,11 +289,17 @@ public interface CouchbaseOperations {
*
* @param action the action to execute in the callback.
* @param <T> the return type.
*
* @return the return type.
*/
<T> T execute(BucketCallback<T> action);
/**
* Returns the linked {@link Bucket} to this template.
*
* @return the client used for the template.
*/
Bucket getCouchbaseBucket();
/**
* Returns the underlying {@link CouchbaseConverter}.
*
@@ -274,11 +307,4 @@ public interface CouchbaseOperations {
*/
CouchbaseConverter getConverter();
/**
* Returns the linked {@link CouchbaseClient} to this template.
*
* @return the client used for the template.
*/
CouchbaseClient getCouchbaseClient();
}

View File

@@ -0,0 +1,33 @@
/*
* 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;
import org.springframework.dao.DataRetrievalFailureException;
/**
* An {@link DataRetrievalFailureException} that denotes an error during a query (N1QL).
*/
public class CouchbaseQueryExecutionException extends DataRetrievalFailureException {
public CouchbaseQueryExecutionException(String msg) {
super(msg);
}
public CouchbaseQueryExecutionException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013-2014 the original author or authors.
* 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
* 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,
@@ -16,22 +16,38 @@
package org.springframework.data.couchbase.core;
import com.couchbase.client.CouchbaseClient;
import com.couchbase.client.protocol.views.Query;
import com.couchbase.client.protocol.views.View;
import com.couchbase.client.protocol.views.ViewResponse;
import com.couchbase.client.protocol.views.ViewRow;
import net.spy.memcached.CASResponse;
import net.spy.memcached.CASValue;
import net.spy.memcached.PersistTo;
import net.spy.memcached.ReplicateTo;
import net.spy.memcached.internal.OperationFuture;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.PersistTo;
import com.couchbase.client.java.ReplicateTo;
import com.couchbase.client.java.document.Document;
import com.couchbase.client.java.document.RawJsonDocument;
import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.error.CASMismatchException;
import com.couchbase.client.java.error.TranscodingException;
import com.couchbase.client.java.query.Query;
import com.couchbase.client.java.query.QueryResult;
import com.couchbase.client.java.query.QueryRow;
import com.couchbase.client.java.view.ViewQuery;
import com.couchbase.client.java.view.ViewResult;
import com.couchbase.client.java.view.ViewRow;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.dao.QueryTimeoutException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService;
@@ -50,34 +66,16 @@ import org.springframework.data.couchbase.core.mapping.event.CouchbaseMappingEve
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.BeanWrapper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
/**
* @author Michael Nitschinger
* @author Oliver Gierke
* @author Simon Baslé
*/
public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventPublisherAware {
private static final Logger LOGGER = LoggerFactory.getLogger(CouchbaseTemplate.class);
private static final Collection<String> ITERABLE_CLASSES;
private static final WriteResultChecking DEFAULT_WRITE_RESULT_CHECKING = WriteResultChecking.NONE;
private final CouchbaseClient client;
protected final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
private final CouchbaseExceptionTranslator exceptionTranslator = new CouchbaseExceptionTranslator();
private final TranslationService translationService;
private ApplicationEventPublisher eventPublisher;
private CouchbaseConverter couchbaseConverter;
private WriteResultChecking writeResultChecking = DEFAULT_WRITE_RESULT_CHECKING;
private static final Collection<String> ITERABLE_CLASSES;
static {
final Set<String> iterableClasses = new HashSet<String>();
@@ -87,181 +85,66 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP
ITERABLE_CLASSES = Collections.unmodifiableCollection(iterableClasses);
}
public void setWriteResultChecking(final WriteResultChecking resultChecking) {
writeResultChecking = resultChecking == null ? DEFAULT_WRITE_RESULT_CHECKING : resultChecking;
}
private final Bucket client;
private final CouchbaseConverter converter;
private final TranslationService translationService;
public CouchbaseTemplate(final CouchbaseClient client) {
private ApplicationEventPublisher eventPublisher;
private WriteResultChecking writeResultChecking = DEFAULT_WRITE_RESULT_CHECKING;
private PersistenceExceptionTranslator exceptionTranslator = new CouchbaseExceptionTranslator();
protected final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
public CouchbaseTemplate(final Bucket client) {
this(client, null, null);
}
public CouchbaseTemplate(final CouchbaseClient client, final TranslationService translationService) {
public CouchbaseTemplate(final Bucket client, final TranslationService translationService) {
this(client, null, translationService);
}
public CouchbaseTemplate(final CouchbaseClient client, final CouchbaseConverter couchbaseConverter, final TranslationService translationService) {
public CouchbaseTemplate(final Bucket client, final CouchbaseConverter converter,
final TranslationService translationService) {
this.client = client;
this.couchbaseConverter = couchbaseConverter == null ? getDefaultConverter() : couchbaseConverter;
this.converter = converter == null ? getDefaultConverter() : converter;
this.translationService = translationService == null ? getDefaultTranslationService() : translationService;
mappingContext = this.couchbaseConverter.getMappingContext();
this.mappingContext = this.converter.getMappingContext();
}
@Override
public void setApplicationEventPublisher(final ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
private TranslationService getDefaultTranslationService() {
JacksonTranslationService t = new JacksonTranslationService();
t.afterPropertiesSet();
return t;
}
private static CouchbaseConverter getDefaultConverter() {
final MappingCouchbaseConverter converter = new MappingCouchbaseConverter(new CouchbaseMappingContext());
converter.afterPropertiesSet();
return converter;
private CouchbaseConverter getDefaultConverter() {
MappingCouchbaseConverter c = new MappingCouchbaseConverter(new CouchbaseMappingContext());
c.afterPropertiesSet();
return c;
}
private static TranslationService getDefaultTranslationService() {
final JacksonTranslationService jacksonTranslationService = new JacksonTranslationService();
jacksonTranslationService.afterPropertiesSet();
return jacksonTranslationService;
}
private Object translateEncode(final CouchbaseStorable source) {
return translationService.encode(source);
}
private CouchbaseStorable translateDecode(final String source, final CouchbaseStorable target) {
return translationService.decode(source, target);
}
@Override
public final void insert(final Object objectToInsert) {
insert(objectToInsert, PersistTo.ZERO, ReplicateTo.ZERO);
}
@Override
public final void insert(final Collection<?> batchToInsert) {
for (final Object toInsert : batchToInsert) {
insert(toInsert);
/**
* Encode a {@link CouchbaseDocument} into a storable representation (JSON) then prepare
* it for storage as a {@link Document}.
*/
private Document<String> encodeAndWrap(final CouchbaseDocument source, Long version) {
String encodedContent = translationService.encode(source);
if (version == null) {
return RawJsonDocument.create(source.getId(), source.getExpiration(), encodedContent);
}
else {
return RawJsonDocument.create(source.getId(), source.getExpiration(), encodedContent, version);
}
}
@Override
public void save(final Object objectToSave) {
save(objectToSave, PersistTo.ZERO, ReplicateTo.ZERO);
}
@Override
public void save(final Collection<?> batchToSave) {
for (final Object toSave : batchToSave) {
save(toSave);
}
}
@Override
public void update(final Object objectToUpdate) {
update(objectToUpdate, PersistTo.ZERO, ReplicateTo.ZERO);
}
@Override
public void update(final Collection<?> batchToUpdate) {
for (final Object toUpdate : batchToUpdate) {
update(toUpdate);
}
}
@Override
public final <T> T findById(final String id, final Class<T> entityClass) {
CASValue result = execute(new BucketCallback<CASValue>() {
@Override
public CASValue doInBucket() {
return client.gets(id);
}
});
if (result == null) {
return null;
}
final CouchbaseDocument converted = new CouchbaseDocument(id);
Object readEntity = couchbaseConverter.read(entityClass, (CouchbaseDocument) translateDecode(
(String) result.getValue(), converted));
final BeanWrapper<Object> beanWrapper = BeanWrapper.create(readEntity, couchbaseConverter.getConversionService());
CouchbasePersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(readEntity.getClass());
if (persistentEntity.hasVersionProperty()) {
beanWrapper.setProperty(persistentEntity.getVersionProperty(), result.getCas());
}
return (T) readEntity;
}
@Override
public <T> List<T> findByView(final String designName, final String viewName, final Query query, final Class<T> entityClass) {
if (query.willIncludeDocs()) {
query.setIncludeDocs(false);
}
if (query.willReduce()) {
query.setReduce(false);
}
final ViewResponse response = queryView(designName, viewName, query);
final List<T> result = new ArrayList<T>(response.size());
for (final ViewRow row : response) {
result.add(findById(row.getId(), entityClass));
}
return result;
}
@Override
public ViewResponse queryView(final String designName, final String viewName, final Query query) {
return execute(new BucketCallback<ViewResponse>() {
@Override
public ViewResponse doInBucket() {
final View view = client.getView(designName, viewName);
return client.query(view, query);
}
});
}
@Override
public void remove(final Object objectToRemove) {
remove(objectToRemove, PersistTo.ZERO, ReplicateTo.ZERO);
}
@Override
public void remove(final Collection<?> batchToRemove) {
for (final Object toRemove : batchToRemove) {
remove(toRemove);
}
}
@Override
public <T> T execute(final BucketCallback<T> action) {
try {
return action.doInBucket();
} catch (RuntimeException e) {
throw exceptionTranslator.translateExceptionIfPossible(e);
} catch (TimeoutException e) {
throw new QueryTimeoutException(e.getMessage(), e);
} catch (InterruptedException e) {
throw new OperationInterruptedException(e.getMessage(), e);
} catch (ExecutionException e) {
throw new OperationInterruptedException(e.getMessage(), e);
}
}
@Override
public boolean exists(final String id) {
final String result = execute(new BucketCallback<String>() {
@Override
public String doInBucket() {
return (String) client.get(id);
}
});
return result != null;
/**
* Decode a {@link Document Document&lt;String&gt;} containing a JSON string
* into a {@link CouchbaseStorable}
*/
private CouchbaseStorable decodeAndUnwrap(final Document<String> source, final CouchbaseStorable target) {
return translationService.decode(source.content(), target); //TODO rework and check
}
/**
@@ -277,201 +160,31 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP
}
}
@Override
public CouchbaseConverter getConverter() {
return couchbaseConverter;
}
@Override
public void save(Object objectToSave, final PersistTo persistTo, final ReplicateTo replicateTo) {
ensureNotIterable(objectToSave);
final BeanWrapper<Object> beanWrapper = BeanWrapper.create(objectToSave, couchbaseConverter.getConversionService());
CouchbasePersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(objectToSave.getClass());
final CouchbasePersistentProperty versionProperty = persistentEntity.getVersionProperty();
final Long version = versionProperty != null ? beanWrapper.getProperty(versionProperty, Long.class) : null;
maybeEmitEvent(new BeforeConvertEvent<Object>(objectToSave));
final CouchbaseDocument converted = new CouchbaseDocument();
couchbaseConverter.write(objectToSave, converted);
maybeEmitEvent(new BeforeSaveEvent<Object>(objectToSave, converted));
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
if (version == null) {
OperationFuture<Boolean> setFuture = client.set(converted.getId(), converted.getExpiration(),
translateEncode(converted), persistTo, replicateTo);
boolean future = setFuture.get();
if (!future) {
handleWriteResultError("Saving document failed: " + setFuture.getStatus().getMessage());
}
return future;
} else {
OperationFuture<CASResponse> casFuture = client.asyncCas(converted.getId(), version,
converted.getExpiration(), translateEncode(converted), persistTo, replicateTo);
CASResponse cas = casFuture.get();
if (cas == CASResponse.EXISTS) {
throw new OptimisticLockingFailureException("Saving document with version value failed: " + cas);
} else {
long newCas = casFuture.getCas();
beanWrapper.setProperty(versionProperty, newCas);
return true;
}
}
}
});
maybeEmitEvent(new AfterSaveEvent<Object>(objectToSave, converted));
}
@Override
public void save(Collection<?> batchToSave, PersistTo persistTo, ReplicateTo replicateTo) {
for (Object toSave : batchToSave) {
save(toSave, persistTo, replicateTo);
}
}
@Override
public void insert(Object objectToInsert, final PersistTo persistTo, final ReplicateTo replicateTo) {
ensureNotIterable(objectToInsert);
final CouchbasePersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(objectToInsert.getClass());
final BeanWrapper<Object> beanWrapper = BeanWrapper.create(objectToInsert, couchbaseConverter.getConversionService());
if (persistentEntity != null && persistentEntity.hasVersionProperty()) {
final Long version = beanWrapper.getProperty(persistentEntity.getVersionProperty(), Long.class);
if (version == 0) {
beanWrapper.setProperty(persistentEntity.getVersionProperty(), 0);
}
}
maybeEmitEvent(new BeforeConvertEvent<Object>(objectToInsert));
final CouchbaseDocument converted = new CouchbaseDocument();
couchbaseConverter.write(objectToInsert, converted);
maybeEmitEvent(new BeforeSaveEvent<Object>(objectToInsert, converted));
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
OperationFuture<Boolean> addFuture = client.add(converted.getId(), converted.getExpiration(),
translateEncode(converted), persistTo, replicateTo);
boolean result = addFuture.get();
if(result == false) {
handleWriteResultError("Inserting document failed: "
+ addFuture.getStatus().getMessage());
}
if (result && persistentEntity.hasVersionProperty()) {
beanWrapper.setProperty(persistentEntity.getVersionProperty(), addFuture.getCas());
}
return result;
}
});
maybeEmitEvent(new AfterSaveEvent<Object>(objectToInsert, converted));
}
@Override
public void insert(Collection<?> batchToInsert, PersistTo persistTo, ReplicateTo replicateTo) {
for (Object toInsert : batchToInsert) {
insert(toInsert, persistTo,replicateTo);
}
}
@Override
public void update(Object objectToUpdate, final PersistTo persistTo, final ReplicateTo replicateTo) {
ensureNotIterable(objectToUpdate);
final BeanWrapper<Object> beanWrapper = BeanWrapper.create(objectToUpdate, couchbaseConverter.getConversionService());
CouchbasePersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(objectToUpdate.getClass());
final CouchbasePersistentProperty versionProperty = persistentEntity.getVersionProperty();
final Long version = versionProperty != null ? beanWrapper.getProperty(versionProperty, Long.class) : null;
maybeEmitEvent(new BeforeConvertEvent<Object>(objectToUpdate));
final CouchbaseDocument converted = new CouchbaseDocument();
couchbaseConverter.write(objectToUpdate, converted);
maybeEmitEvent(new BeforeSaveEvent<Object>(objectToUpdate, converted));
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
if (version == null) {
return client.replace(converted.getId(), converted.getExpiration(), translateEncode(converted), persistTo,
replicateTo).get();
} else {
OperationFuture<CASResponse> casFuture = client.asyncCas(converted.getId(), version,
converted.getExpiration(), translateEncode(converted), persistTo, replicateTo);
CASResponse cas = casFuture.get();
if (cas == CASResponse.EXISTS) {
throw new OptimisticLockingFailureException("Updating document with version value failed: " + cas);
} else {
long newCas = casFuture.getCas();
beanWrapper.setProperty(versionProperty, newCas);
return true;
}
}
}
});
maybeEmitEvent(new AfterSaveEvent<Object>(objectToUpdate, converted));
}
@Override
public void update(Collection<?> batchToUpdate, PersistTo persistTo, ReplicateTo replicateTo) {
for (Object toUpdate : batchToUpdate) {
update(toUpdate, persistTo, replicateTo);
}
}
@Override
public void remove(final Object objectToRemove, final PersistTo persistTo, final ReplicateTo replicateTo) {
ensureNotIterable(objectToRemove);
maybeEmitEvent(new BeforeDeleteEvent<Object>(objectToRemove));
if (objectToRemove instanceof String) {
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
return client.delete((String) objectToRemove, persistTo, replicateTo).get();
}
});
maybeEmitEvent(new AfterDeleteEvent<Object>(objectToRemove));
return;
}
final CouchbaseDocument converted = new CouchbaseDocument();
couchbaseConverter.write(objectToRemove, converted);
execute(new BucketCallback<OperationFuture<Boolean>>() {
@Override
public OperationFuture<Boolean> doInBucket() {
return client.delete(converted.getId());
}
});
maybeEmitEvent(new AfterDeleteEvent<Object>(objectToRemove));
}
@Override
public void remove(Collection<?> batchToRemove, PersistTo persistTo, ReplicateTo replicateTo) {
for (Object toRemove : batchToRemove) {
remove(toRemove, persistTo, replicateTo);
}
}
/**
* Handle write errors according to the set {@link #writeResultChecking} setting.
*
* @param message the message to use.
*/
private void handleWriteResultError(String message) {
private void handleWriteResultError(String message, Exception cause) {
if (writeResultChecking == WriteResultChecking.NONE) {
return;
}
if (writeResultChecking == WriteResultChecking.EXCEPTION) {
throw new CouchbaseDataIntegrityViolationException(message);
} else {
LOGGER.error(message);
throw new CouchbaseDataIntegrityViolationException(message, cause);
}
else {
LOGGER.error(message, cause);
}
}
public void setWriteResultChecking(WriteResultChecking writeResultChecking) {
this.writeResultChecking = writeResultChecking == null ? DEFAULT_WRITE_RESULT_CHECKING : writeResultChecking;
}
@Override
public void setApplicationEventPublisher(final ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
/**
@@ -487,7 +200,315 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP
}
@Override
public CouchbaseClient getCouchbaseClient() {
return client;
public void save(Object objectToSave) {
save(objectToSave, PersistTo.NONE, ReplicateTo.NONE);
}
@Override
public void save(Object objectToSave, PersistTo persistTo, ReplicateTo replicateTo) {
doPersist(objectToSave, persistTo, replicateTo, false, false);
}
@Override
public void save(Collection<?> batchToSave) {
save(batchToSave, PersistTo.NONE, ReplicateTo.NONE);
}
@Override
public void save(Collection<?> batchToSave, PersistTo persistTo, ReplicateTo replicateTo) {
for (Object o : batchToSave) {
doPersist(o, persistTo, replicateTo, false, false);
}
}
@Override
public void insert(Object objectToInsert) {
insert(objectToInsert, PersistTo.NONE, ReplicateTo.NONE);
}
@Override
public void insert(Object objectToInsert, PersistTo persistTo, ReplicateTo replicateTo) {
doPersist(objectToInsert, persistTo, replicateTo, true, false);
}
@Override
public void insert(Collection<?> batchToInsert) {
insert(batchToInsert, PersistTo.NONE, ReplicateTo.NONE);
}
@Override
public void insert(Collection<?> batchToInsert, PersistTo persistTo, ReplicateTo replicateTo) {
for (Object o : batchToInsert) {
doPersist(o, persistTo, replicateTo, true, false);
}
}
@Override
public void update(Object objectToUpdate) {
update(objectToUpdate, PersistTo.NONE, ReplicateTo.NONE);
}
@Override
public void update(Object objectToUpdate, PersistTo persistTo, ReplicateTo replicateTo) {
doPersist(objectToUpdate, persistTo, replicateTo, false, true);
}
@Override
public void update(Collection<?> batchToUpdate) {
update(batchToUpdate, PersistTo.NONE, ReplicateTo.NONE);
}
@Override
public void update(Collection<?> batchToUpdate, PersistTo persistTo, ReplicateTo replicateTo) {
for (Object o : batchToUpdate) {
doPersist(o, persistTo, replicateTo, false, true);
}
}
@Override
public <T> T findById(final String id, Class<T> entityClass) {
RawJsonDocument result = execute(new BucketCallback<RawJsonDocument>() {
@Override
public RawJsonDocument doInBucket() {
return client.get(id, RawJsonDocument.class);
}
});
return mapToEntity(id, result, entityClass);
}
@Override
public <T> List<T> findByView(ViewQuery query, Class<T> entityClass) {
query.includeDocs(false);
query.reduce(false);
try {
final ViewResult response = queryView(query);
if (response.error() != null) {
throw new CouchbaseQueryExecutionException("Unable to execute view query due to the following view error: " +
response.error().toString());
}
List<ViewRow> allRows = response.allRows();
final List<T> result = new ArrayList<T>(allRows.size());
for (final ViewRow row : allRows) {
result.add(mapToEntity(row.id(), row.document(RawJsonDocument.class), entityClass));
}
return result;
}
catch (TranscodingException e) {
throw new CouchbaseQueryExecutionException("Unable to execute view query", e);
}
}
@Override
public ViewResult queryView(final ViewQuery query) {
return execute(new BucketCallback<ViewResult>() {
@Override
public ViewResult doInBucket() {
return client.query(query);
}
});
}
@Override
public <T> List<T> findByN1QL(Query n1ql, Class<T> entityClass) {
try {
QueryResult queryResult = queryN1QL(n1ql);
if (queryResult.finalSuccess()) {
List<QueryRow> allRows = queryResult.allRows();
List<T> result = new ArrayList<T>(allRows.size());
for (QueryRow row : allRows) {
String json = row.value().toString();
T decoded = translationService.decodeFragment(json, entityClass);
result.add(decoded);
}
return result;
}
else {
StringBuilder message = new StringBuilder("Unable to execute query due to the following n1ql errors: ");
for (JsonObject error : queryResult.errors()) {
message.append('\n').append(error);
}
throw new CouchbaseQueryExecutionException(message.toString());
}
}
catch (TranscodingException e) {
throw new CouchbaseQueryExecutionException("Unable to execute query", e);
}
}
@Override
public QueryResult queryN1QL(final Query query) {
return execute(new BucketCallback<QueryResult>() {
@Override
public QueryResult doInBucket() throws TimeoutException, ExecutionException, InterruptedException {
return client.query(query);
}
});
}
@Override
public boolean exists(final String id) {
return execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws TimeoutException, ExecutionException, InterruptedException {
return client.exists(id);
}
});
}
@Override
public void remove(Object objectToRemove) {
remove(objectToRemove, PersistTo.NONE, ReplicateTo.NONE);
}
@Override
public void remove(Object objectToRemove, PersistTo persistTo, ReplicateTo replicateTo) {
doRemove(objectToRemove, persistTo, replicateTo);
}
@Override
public void remove(Collection<?> batchToRemove) {
remove(batchToRemove, PersistTo.NONE, ReplicateTo.NONE);
}
@Override
public void remove(Collection<?> batchToRemove, PersistTo persistTo, ReplicateTo replicateTo) {
for (Object o : batchToRemove) {
doRemove(o, persistTo, replicateTo);
}
}
@Override
public <T> T execute(BucketCallback<T> action) {
try {
return action.doInBucket();
}
catch (RuntimeException e) {
throw exceptionTranslator.translateExceptionIfPossible(e);
}
catch (TimeoutException e) {
throw new QueryTimeoutException(e.getMessage(), e);
}
catch (InterruptedException e) {
throw new OperationInterruptedException(e.getMessage(), e);
}
catch (ExecutionException e) {
throw new OperationInterruptedException(e.getMessage(), e);
}
}
private void doPersist(Object objectToPersist, final PersistTo persistTo, final ReplicateTo replicateTo,
final boolean failOnExist, final boolean failOnMissing) {
ensureNotIterable(objectToPersist);
final String operationDesc = failOnExist ? "Insert" : failOnMissing ? "Update" : "Upsert";
final BeanWrapper<Object> beanWrapper = BeanWrapper.create(objectToPersist, converter.getConversionService());
final CouchbasePersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(objectToPersist.getClass());
final CouchbasePersistentProperty versionProperty = persistentEntity.getVersionProperty();
final Long version = versionProperty != null ? beanWrapper.getProperty(versionProperty, Long.class) : null;
maybeEmitEvent(new BeforeConvertEvent<Object>(objectToPersist));
final CouchbaseDocument converted = new CouchbaseDocument();
converter.write(objectToPersist, converted);
maybeEmitEvent(new BeforeSaveEvent<Object>(objectToPersist, converted));
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
Document<String> doc = encodeAndWrap(converted, version);
Document<String> storedDoc;
try {
if (!failOnExist && !failOnMissing) {
storedDoc = client.upsert(doc, persistTo, replicateTo);
}
else if (failOnMissing) {
storedDoc = client.replace(doc, persistTo, replicateTo);
}
else {
storedDoc = client.insert(doc, persistTo, replicateTo);
}
if (persistentEntity.hasVersionProperty() && storedDoc != null && storedDoc.cas() != 0) {
//inject new cas into the bean
beanWrapper.setProperty(versionProperty, storedDoc.cas());
return true;
}
return false;
}
catch (CASMismatchException e) {
throw new OptimisticLockingFailureException(operationDesc +
" document with version value failed: " + version);
}
catch (Exception e) {
handleWriteResultError(operationDesc + " document failed: " + e.getMessage(), e);
return false; //this could be skipped if WriteResultChecking.EXCEPTION
}
}
});
maybeEmitEvent(new AfterSaveEvent<Object>(objectToPersist, converted));
}
private void doRemove(final Object objectToRemove, final PersistTo persistTo, final ReplicateTo replicateTo) {
ensureNotIterable(objectToRemove);
maybeEmitEvent(new BeforeDeleteEvent<Object>(objectToRemove));
if (objectToRemove instanceof String) {
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
RawJsonDocument deletedDoc = client.remove((String) objectToRemove, persistTo, replicateTo,
RawJsonDocument.class);
return deletedDoc != null;
}
});
maybeEmitEvent(new AfterDeleteEvent<Object>(objectToRemove));
return;
}
final CouchbaseDocument converted = new CouchbaseDocument();
converter.write(objectToRemove, converted);
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() {
RawJsonDocument deletedDoc = client.remove(converted.getId(), persistTo, replicateTo
, RawJsonDocument.class);
return deletedDoc != null;
}
});
maybeEmitEvent(new AfterDeleteEvent<Object>(objectToRemove));
}
private <T> T mapToEntity(String id, Document<String> data, Class<T> entityClass) {
if (data == null) {
return null;
}
final CouchbaseDocument converted = new CouchbaseDocument(id);
Object readEntity = converter.read(entityClass, (CouchbaseDocument) decodeAndUnwrap(data, converted));
final BeanWrapper<Object> beanWrapper = BeanWrapper.create(readEntity, converter.getConversionService());
CouchbasePersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(readEntity.getClass());
if (persistentEntity.hasVersionProperty()) {
beanWrapper.setProperty(persistentEntity.getVersionProperty(), data.cas());
}
return (T) readEntity;
}
@Override
public Bucket getCouchbaseBucket() {
return this.client;
}
@Override
public CouchbaseConverter getConverter() {
return this.converter;
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -27,6 +27,7 @@ public class OperationCancellationException extends TransientDataAccessException
/**
* Constructor for OperationCancellationException.
*
* @param msg the detail message
*/
public OperationCancellationException(final String msg) {
@@ -35,6 +36,7 @@ public class OperationCancellationException extends TransientDataAccessException
/**
* Constructor for OperationCancellationException.
*
* @param msg the detail message
* @param cause the root cause from the data access API in use
*/

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -27,6 +27,7 @@ public class OperationInterruptedException extends TransientDataAccessException
/**
* Constructor for OperationInterruptedException.
*
* @param msg the detail message
*/
public OperationInterruptedException(final String msg) {
@@ -35,6 +36,7 @@ public class OperationInterruptedException extends TransientDataAccessException
/**
* Constructor for OperationInterruptedException.
*
* @param msg the detail message
* @param cause the root cause from the data access API in use
*/

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013, 2014 the original author or authors.
* 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
* 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,

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2014 the original author or authors.
* 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
* 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,
@@ -86,7 +86,7 @@ class ConverterRegistration {
}
/**
* Returns whether the source type is a Mongo simple one.
* Returns whether the source type is a Couchbase simple one.
*
* @return
*/
@@ -95,7 +95,7 @@ class ConverterRegistration {
}
/**
* Returns whether the target type is a Mongo simple one.
* Returns whether the target type is a Couchbase simple one.
*
* @return
*/
@@ -104,7 +104,7 @@ class ConverterRegistration {
}
/**
* Returns whether the given type is a type that Mongo can handle basically.
* Returns whether the given type is a type that Couchbase can handle basically.
*
* @param type
* @return

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -28,8 +28,8 @@ import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProper
* @author Michael Nitschinger
*/
public interface CouchbaseConverter
extends EntityConverter<CouchbasePersistentEntity<?>,
CouchbasePersistentProperty, Object, CouchbaseDocument>,
CouchbaseWriter<Object, CouchbaseDocument>,
EntityReader<Object, CouchbaseDocument> {
extends EntityConverter<CouchbasePersistentEntity<?>,
CouchbasePersistentProperty, Object, CouchbaseDocument>,
CouchbaseWriter<Object, CouchbaseDocument>,
EntityReader<Object, CouchbaseDocument> {
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -16,13 +16,13 @@
package org.springframework.data.couchbase.core.convert;
import java.util.Map;
import org.springframework.context.expression.MapAccessor;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.TypedValue;
import java.util.Map;
/**
* A property accessor for document properties.
*

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -24,5 +24,5 @@ import org.springframework.data.convert.EntityWriter;
* @author Michael Nitschinger
*/
public interface CouchbaseWriter<T, ConvertedCouchbaseDocument> extends
EntityWriter<T, ConvertedCouchbaseDocument> {
EntityWriter<T, ConvertedCouchbaseDocument> {
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -16,26 +16,30 @@
package org.springframework.data.couchbase.core.convert;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.convert.JodaTimeConverters;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.util.Assert;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Value object to capture custom conversion.
*
* <p/>
* <p>Types that can be mapped directly onto JSON are considered simple ones, because they neither need deeper
* inspection nor nested conversion.</p>
*
@@ -69,6 +73,7 @@ public class CustomConversions {
/**
* Create a new instance with a given list of conversers.
*
* @param converters the list of custom converters.
*/
public CustomConversions(final List<?> converters) {
@@ -155,10 +160,12 @@ public class CustomConversions {
for (GenericConverter.ConvertiblePair pair : genericConverter.getConvertibleTypes()) {
register(new ConverterRegistration(pair, isReading, isWriting));
}
} else if (converter instanceof Converter) {
}
else if (converter instanceof Converter) {
Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), Converter.class);
register(new ConverterRegistration(arguments[0], arguments[1], isReading, isWriting));
} else {
}
else {
throw new IllegalArgumentException("Unsupported Converter type!");
}
}
@@ -279,7 +286,7 @@ public class CustomConversions {
}
/**
* Inspects the given {@link org.springframework.core.convert.converter.GenericConverter.ConvertiblePair} for ones
* Inspects the given {@link GenericConverter.ConvertiblePair} for ones
* that have a source compatible type as source. Additionally checks assignability of the target type if one is
* given.
*
@@ -289,7 +296,7 @@ public class CustomConversions {
* @return
*/
private static Class<?> getCustomTarget(Class<?> sourceType, Class<?> requestedTargetType,
Iterable<GenericConverter.ConvertiblePair> pairs) {
Iterable<GenericConverter.ConvertiblePair> pairs) {
Assert.notNull(sourceType);
Assert.notNull(pairs);

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2014 the original author or authors.
* 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
* 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,
@@ -16,21 +16,22 @@
package org.springframework.data.couchbase.core.convert;
import org.joda.time.DateMidnight;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.util.ClassUtils;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.joda.time.DateMidnight;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.util.ClassUtils;
/**
* Out of the box conversions for java dates and calendars.
*
@@ -38,7 +39,8 @@ import java.util.List;
*/
public final class DateConverters {
private DateConverters() {}
private DateConverters() {
}
private static final boolean JODA_TIME_IS_PRESENT = ClassUtils.isPresent("org.joda.time.LocalDate", null);
@@ -85,7 +87,7 @@ public final class DateConverters {
@Override
public Long convert(Calendar source) {
return source == null ? null : source.getTimeInMillis() / 1000;
return source == null ? null : source.getTimeInMillis() / 1000;
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013-2014 the original author or authors.
* 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
* 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,
@@ -16,6 +16,13 @@
package org.springframework.data.couchbase.core.convert;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.CollectionFactory;
@@ -31,14 +38,20 @@ import org.springframework.data.mapping.AssociationHandler;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.*;
import org.springframework.data.mapping.model.BeanWrapper;
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mapping.model.ParameterValueProvider;
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
import org.springframework.data.mapping.model.PropertyValueProvider;
import org.springframework.data.mapping.model.SpELContext;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.data.mapping.model.SpELExpressionParameterValueProvider;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import java.util.*;
/**
* A mapping converter for Couchbase.
*
@@ -154,7 +167,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
protected <R> R read(final CouchbasePersistentEntity<R> entity, final CouchbaseDocument source, final Object parent) {
final DefaultSpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(source, spELContext);
ParameterValueProvider<CouchbasePersistentProperty> provider =
getParameterProvider(entity, source, evaluator, parent);
getParameterProvider(entity, source, evaluator, parent);
EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
R instance = instantiator.createInstance(entity, provider);
@@ -197,7 +210,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
* @return the actual property value.
*/
protected Object getValueInternal(final CouchbasePersistentProperty property, final CouchbaseDocument source,
final Object parent) {
final Object parent) {
return new CouchbasePropertyValueProvider(source, spELContext, parent).getPropertyValue(property);
}
@@ -211,14 +224,14 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
* @return a new parameter value provider.
*/
private ParameterValueProvider<CouchbasePersistentProperty> getParameterProvider(
final CouchbasePersistentEntity<?> entity, final CouchbaseDocument source,
final DefaultSpELExpressionEvaluator evaluator, final Object parent) {
final CouchbasePersistentEntity<?> entity, final CouchbaseDocument source,
final DefaultSpELExpressionEvaluator evaluator, final Object parent) {
CouchbasePropertyValueProvider provider = new CouchbasePropertyValueProvider(source, evaluator, parent);
PersistentEntityParameterValueProvider<CouchbasePersistentProperty> parameterProvider =
new PersistentEntityParameterValueProvider<CouchbasePersistentProperty>(entity, provider, parent);
new PersistentEntityParameterValueProvider<CouchbasePersistentProperty>(entity, provider, parent);
return new ConverterAwareSpELExpressionParameterValueProvider(evaluator, conversionService, parameterProvider,
parent);
parent);
}
/**
@@ -231,7 +244,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
*/
@SuppressWarnings("unchecked")
protected Map<Object, Object> readMap(final TypeInformation<?> type, final CouchbaseDocument source,
final Object parent) {
final Object parent) {
Assert.notNull(source);
Class<?> mapType = typeMapper.readType(source, type).getType();
@@ -368,7 +381,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
* @param entity the persistent entity to convert from.
*/
protected void writeInternal(final Object source, final CouchbaseDocument target,
final CouchbasePersistentEntity<?> entity) {
final CouchbasePersistentEntity<?> entity) {
if (source == null) {
return;
}
@@ -377,7 +390,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
throw new MappingException("No mapping metadata found for entity of type " + source.getClass().getName());
}
final BeanWrapper<Object> wrapper = BeanWrapper.create(source, conversionService);
final BeanWrapper<Object> wrapper = BeanWrapper.create(source, conversionService);
final CouchbasePersistentProperty idProperty = entity.getIdProperty();
final CouchbasePersistentProperty versionProperty = entity.getVersionProperty();
@@ -428,7 +441,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
*/
@SuppressWarnings("unchecked")
private void writePropertyInternal(final Object source, final CouchbaseDocument target,
final CouchbasePersistentProperty prop) {
final CouchbasePersistentProperty prop) {
if (source == null) {
return;
}
@@ -459,7 +472,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
addCustomTypeKeyIfNecessary(type, source, propertyDoc);
CouchbasePersistentEntity<?> entity = isSubtype(prop.getType(), source.getClass()) ? mappingContext
.getPersistentEntity(source.getClass()) : mappingContext.getPersistentEntity(type);
.getPersistentEntity(source.getClass()) : mappingContext.getPersistentEntity(type);
writeInternal(source, propertyDoc, entity);
target.put(name, propertyDoc);
}
@@ -487,7 +500,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
* @return the written couchbase document.
*/
private CouchbaseDocument writeMapInternal(final Map<Object, Object> source, final CouchbaseDocument target,
final TypeInformation<?> type) {
final TypeInformation<?> type) {
for (Map.Entry<Object, Object> entry : source.entrySet()) {
Object key = entry.getKey();
Object val = entry.getValue();
@@ -533,7 +546,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
* @return the created couchbase list.
*/
private CouchbaseList writeCollectionInternal(final Collection<?> source, final CouchbaseList target,
final TypeInformation<?> type) {
final TypeInformation<?> type) {
TypeInformation<?> componentType = type == null ? null : type.getComponentType();
for (Object element : source) {
@@ -573,7 +586,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class;
Collection<Object> items = targetType.getType().isArray() ? new ArrayList<Object>() : CollectionFactory
.createCollection(collectionType, source.size(false));
.createCollection(collectionType, source.size(false));
TypeInformation<?> componentType = targetType.getComponentType();
Class<?> rawComponentType = componentType == null ? null : componentType.getType();
@@ -709,12 +722,12 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
private final Object parent;
public CouchbasePropertyValueProvider(final CouchbaseDocument source, final SpELContext factory,
final Object parent) {
final Object parent) {
this(source, new DefaultSpELExpressionEvaluator(source, factory), parent);
}
public CouchbasePropertyValueProvider(final CouchbaseDocument source,
final DefaultSpELExpressionEvaluator evaluator, final Object parent) {
final DefaultSpELExpressionEvaluator evaluator, final Object parent) {
Assert.notNull(source);
Assert.notNull(evaluator);
@@ -744,7 +757,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
* A expression parameter value provider.
*/
private class ConverterAwareSpELExpressionParameterValueProvider extends
SpELExpressionParameterValueProvider<CouchbasePersistentProperty> {
SpELExpressionParameterValueProvider<CouchbasePersistentProperty> {
private final Object parent;
@@ -757,7 +770,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
@Override
protected <T> T potentiallyConvertSpelValue(final Object object,
final Parameter<T, CouchbasePersistentProperty> parameter) {
final Parameter<T, CouchbasePersistentProperty> parameter) {
return readValue(object, parameter.getType(), parent);
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -16,12 +16,18 @@
package org.springframework.data.couchbase.core.convert.translation;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Map;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.CouchbaseList;
@@ -29,9 +35,6 @@ import org.springframework.data.couchbase.core.mapping.CouchbaseStorable;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import java.io.*;
import java.util.Map;
/**
* A Jackson JSON Translator that implements the {@link TranslationService} contract.
*
@@ -58,11 +61,10 @@ public class JacksonTranslationService implements TranslationService, Initializi
* Encode a {@link CouchbaseStorable} to a JSON string.
*
* @param source the source document to encode.
*
* @return the encoded JSON String.
*/
@Override
public final Object encode(final CouchbaseStorable source) {
public final String encode(final CouchbaseStorable source) {
Writer writer = new StringWriter();
try {
@@ -70,7 +72,8 @@ public class JacksonTranslationService implements TranslationService, Initializi
encodeRecursive(source, generator);
generator.close();
writer.close();
} catch (IOException ex) {
}
catch (IOException ex) {
throw new RuntimeException("Could not encode JSON", ex);
}
@@ -82,7 +85,6 @@ public class JacksonTranslationService implements TranslationService, Initializi
*
* @param source the source document
* @param generator the JSON generator.
*
* @throws IOException
*/
private void encodeRecursive(final CouchbaseStorable source, final JsonGenerator generator) throws IOException {
@@ -101,7 +103,8 @@ public class JacksonTranslationService implements TranslationService, Initializi
if (simpleTypeHolder.isSimpleType(clazz) && !isEnumOrClass(clazz)) {
generator.writeObject(value);
} else {
}
else {
objectMapper.writeValue(generator, value);
}
@@ -119,11 +122,10 @@ public class JacksonTranslationService implements TranslationService, Initializi
*
* @param source the source formatted document.
* @param target the target of the populated data.
*
* @return the decoded structure.
*/
@Override
public final CouchbaseStorable decode(final Object source, final CouchbaseStorable target) {
public final CouchbaseStorable decode(final String source, final CouchbaseStorable target) {
try {
JsonParser parser = factory.createParser((String) source);
while (parser.nextToken() != null) {
@@ -131,14 +133,17 @@ public class JacksonTranslationService implements TranslationService, Initializi
if (currentToken == JsonToken.START_OBJECT) {
return decodeObject(parser, (CouchbaseDocument) target);
} else if (currentToken == JsonToken.START_ARRAY) {
}
else if (currentToken == JsonToken.START_ARRAY) {
return decodeArray(parser, new CouchbaseList());
} else {
}
else {
throw new MappingException("JSON to decode needs to start as array or object!");
}
}
parser.close();
} catch (IOException ex) {
}
catch (IOException ex) {
throw new RuntimeException("Could not decode JSON", ex);
}
return target;
@@ -149,7 +154,6 @@ public class JacksonTranslationService implements TranslationService, Initializi
*
* @param parser the JSON parser with the content.
* @param target the target where the content should be stored.
*
* @throws IOException
* @returns the decoded object.
*/
@@ -160,11 +164,14 @@ public class JacksonTranslationService implements TranslationService, Initializi
while (currentToken != null && currentToken != JsonToken.END_OBJECT) {
if (currentToken == JsonToken.START_OBJECT) {
target.put(fieldName, decodeObject(parser, new CouchbaseDocument()));
} else if (currentToken == JsonToken.START_ARRAY) {
}
else if (currentToken == JsonToken.START_ARRAY) {
target.put(fieldName, decodeArray(parser, new CouchbaseList()));
} else if (currentToken == JsonToken.FIELD_NAME) {
}
else if (currentToken == JsonToken.FIELD_NAME) {
fieldName = parser.getCurrentName();
} else {
}
else {
target.put(fieldName, decodePrimitive(currentToken, parser));
}
@@ -179,7 +186,6 @@ public class JacksonTranslationService implements TranslationService, Initializi
*
* @param parser the JSON parser with the content.
* @param target the target where the content should be stored.
*
* @throws IOException
* @returns the decoded list.
*/
@@ -189,9 +195,11 @@ public class JacksonTranslationService implements TranslationService, Initializi
while (currentToken != null && currentToken != JsonToken.END_ARRAY) {
if (currentToken == JsonToken.START_OBJECT) {
target.put(decodeObject(parser, new CouchbaseDocument()));
} else if (currentToken == JsonToken.START_ARRAY) {
}
else if (currentToken == JsonToken.START_ARRAY) {
target.put(decodeArray(parser, new CouchbaseList()));
} else {
}
else {
target.put(decodePrimitive(currentToken, parser));
}
@@ -206,9 +214,7 @@ public class JacksonTranslationService implements TranslationService, Initializi
*
* @param token the type of token.
* @param parser the parser with the content.
*
* @return the decoded primitve.
*
* @throws IOException
*/
private Object decodePrimitive(final JsonToken token, final JsonParser parser) throws IOException {
@@ -221,7 +227,8 @@ public class JacksonTranslationService implements TranslationService, Initializi
case VALUE_NUMBER_INT:
try {
return parser.getValueAsInt();
} catch (final JsonParseException e) {
}
catch (final JsonParseException e) {
return parser.getValueAsLong();
}
case VALUE_NUMBER_FLOAT:
@@ -229,7 +236,17 @@ public class JacksonTranslationService implements TranslationService, Initializi
case VALUE_NULL:
return null;
default:
throw new MappingException("Could not decode primitve value " + token);
throw new MappingException("Could not decode primitive value " + token);
}
}
@Override
public <T> T decodeFragment(String source, Class<T> target) {
try {
return objectMapper.readValue(source, target);
}
catch (IOException e) {
throw new RuntimeException("Cannot decode ad-hoc JSON", e);
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -16,6 +16,8 @@
package org.springframework.data.couchbase.core.convert.translation;
import com.couchbase.client.java.query.QueryRow;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.CouchbaseStorable;
@@ -27,12 +29,12 @@ import org.springframework.data.couchbase.core.mapping.CouchbaseStorable;
public interface TranslationService {
/**
* Encodes a {@link CouchbaseDocument} into the target format.
* Encodes a JSON String into the target format.
*
* @param source the source document to encode.
* @param source the source contents to encode.
* @return the encoded document representation.
*/
Object encode(CouchbaseStorable source);
String encode(CouchbaseStorable source);
/**
* Decodes the target format into a {@link CouchbaseDocument}
@@ -41,5 +43,15 @@ public interface TranslationService {
* @param target the target of the populated data.
* @return a properly populated document to work with.
*/
CouchbaseStorable decode(Object source, CouchbaseStorable target);
CouchbaseStorable decode(String source, CouchbaseStorable target);
/**
* Decodes an ad-hoc JSON object into a corresponding "case" class.
*
* @param source the JSON for the ad-hoc JSON object (from a N1QL {@link QueryRow} for instance).
* @param target the target class information.
* @param <T> the target class.
* @return an ad-hoc instance of the decoded JSON into the corresponding "case" class.
*/
<T> T decodeFragment(String source, Class<T> target);
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -31,7 +31,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
* @author Michael Nitschinger
*/
public class BasicCouchbasePersistentEntity<T> extends BasicPersistentEntity<T, CouchbasePersistentProperty>
implements CouchbasePersistentEntity<T>, ApplicationContextAware {
implements CouchbasePersistentEntity<T>, ApplicationContextAware {
/**
* Contains the evaluation context.
@@ -40,6 +40,7 @@ public class BasicCouchbasePersistentEntity<T> extends BasicPersistentEntity<T,
/**
* Create a new entity.
*
* @param typeInformation the type information of the entity.
*/
public BasicCouchbasePersistentEntity(final TypeInformation<T> typeInformation) {
@@ -55,9 +56,9 @@ public class BasicCouchbasePersistentEntity<T> extends BasicPersistentEntity<T,
*/
@Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
context.addPropertyAccessor(new BeanFactoryAccessor());
context.setBeanResolver(new BeanFactoryResolver(applicationContext));
context.setRootObject(applicationContext);
context.addPropertyAccessor(new BeanFactoryAccessor());
context.setBeanResolver(new BeanFactoryResolver(applicationContext));
context.setRootObject(applicationContext);
}
/**
@@ -66,9 +67,9 @@ public class BasicCouchbasePersistentEntity<T> extends BasicPersistentEntity<T,
* @return the expiration time.
*/
public int getExpiry() {
org.springframework.data.couchbase.core.mapping.Document annotation =
getType().getAnnotation(org.springframework.data.couchbase.core.mapping.Document.class);
org.springframework.data.couchbase.core.mapping.Document annotation =
getType().getAnnotation(org.springframework.data.couchbase.core.mapping.Document.class);
return annotation == null ? 0 : annotation.expiry();
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -16,6 +16,9 @@
package org.springframework.data.couchbase.core.mapping;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
import org.springframework.data.mapping.model.FieldNamingStrategy;
@@ -24,20 +27,17 @@ import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.util.StringUtils;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
/**
* Implements annotated property representations of a given Field instance.
*
* <p/>
* <p>This object is used to gather information out of properties on objects that need to be persisted. For example, it
* supports overriding of the actual property name by providing custom annotations.</p>
*
* @author Michael Nitschinger
*/
public class BasicCouchbasePersistentProperty
extends AnnotationBasedPersistentProperty<CouchbasePersistentProperty>
implements CouchbasePersistentProperty {
extends AnnotationBasedPersistentProperty<CouchbasePersistentProperty>
implements CouchbasePersistentProperty {
private final FieldNamingStrategy fieldNamingStrategy;
@@ -50,11 +50,11 @@ public class BasicCouchbasePersistentProperty
* @param simpleTypeHolder the type holder.
*/
public BasicCouchbasePersistentProperty(final Field field, final PropertyDescriptor propertyDescriptor,
final CouchbasePersistentEntity<?> owner, final SimpleTypeHolder simpleTypeHolder,
final FieldNamingStrategy fieldNamingStrategy) {
final CouchbasePersistentEntity<?> owner, final SimpleTypeHolder simpleTypeHolder,
final FieldNamingStrategy fieldNamingStrategy) {
super(field, propertyDescriptor, owner, simpleTypeHolder);
this.fieldNamingStrategy = fieldNamingStrategy == null ? PropertyNameFieldNamingStrategy.INSTANCE
: fieldNamingStrategy;
: fieldNamingStrategy;
}
/**
@@ -67,14 +67,14 @@ public class BasicCouchbasePersistentProperty
/**
* Returns the field name of the property.
*
* <p/>
* The field name can be different from the actual property name by using a
* custom annotation.
*/
@Override
public String getFieldName() {
org.springframework.data.couchbase.core.mapping.Field annotation = getField().
getAnnotation(org.springframework.data.couchbase.core.mapping.Field.class);
getAnnotation(org.springframework.data.couchbase.core.mapping.Field.class);
if (annotation != null && StringUtils.hasText(annotation.value())) {
return annotation.value();
@@ -84,7 +84,7 @@ public class BasicCouchbasePersistentProperty
if (!StringUtils.hasText(fieldName)) {
throw new MappingException(String.format("Invalid (null or empty) field name returned for property %s by %s!",
this, fieldNamingStrategy.getClass()));
this, fieldNamingStrategy.getClass()));
}
return fieldName;

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -16,20 +16,20 @@
package org.springframework.data.couchbase.core.mapping;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.data.mapping.model.SimpleTypeHolder;
/**
* A {@link CouchbaseDocument} is an abstract representation of a document stored inside Couchbase Server.
*
* <p/>
* <p>It acts like a {@link HashMap}, but only allows those types to be written that are supported by the underlying
* storage format, which is currently JSON. Note that JSON conversion is not happening here, but performed at a
* different stage based on the payload stored in the {@link CouchbaseDocument}.</p>
*
* <p/>
* <p>In addition to the actual content, meta data is also stored. This especially refers to the document ID and its
* expiration time. Note that this information is not mandatory, since documents can be nested and therefore only the
* topmost document most likely has an ID.</p>
@@ -115,7 +115,7 @@ public class CouchbaseDocument implements CouchbaseStorable {
*
* @param key the key of the attribute.
* @return the value to which the specified key is mapped, or
* null if does not contain a mapping for the key.
* null if does not contain a mapping for the key.
*/
public final Object get(final String key) {
return payload.get(key);
@@ -123,7 +123,7 @@ public class CouchbaseDocument implements CouchbaseStorable {
/**
* Returns the current payload, including all recursive elements.
*
* <p/>
* It either returns the raw results or makes sure that the recusrive elements
* are also exported properly.
*
@@ -134,7 +134,8 @@ public class CouchbaseDocument implements CouchbaseStorable {
for (Map.Entry<String, Object> entry : payload.entrySet()) {
if (entry.getValue() instanceof CouchbaseDocument) {
toExport.put(entry.getKey(), ((CouchbaseDocument) entry.getValue()).export());
} else if (entry.getValue() instanceof CouchbaseList) {
}
else if (entry.getValue() instanceof CouchbaseList) {
toExport.put(entry.getKey(), ((CouchbaseList) entry.getValue()).export());
}
}
@@ -187,7 +188,8 @@ public class CouchbaseDocument implements CouchbaseStorable {
for (Object value : payload.values()) {
if (value instanceof CouchbaseDocument) {
totalSize += ((CouchbaseDocument) value).size(true);
} else if (value instanceof CouchbaseList) {
}
else if (value instanceof CouchbaseList) {
totalSize += ((CouchbaseList) value).size(true);
}
}
@@ -197,7 +199,7 @@ public class CouchbaseDocument implements CouchbaseStorable {
/**
* Returns the underlying payload.
*
* <p/>
* <p>Note that unlike {@link #export()}, the nested objects are not converted, so the "raw" map is returned.</p>
*
* @return the underlying payload.
@@ -208,7 +210,7 @@ public class CouchbaseDocument implements CouchbaseStorable {
/**
* Returns the expiration time of the document.
*
* <p/>
* If the expiration time is 0, then the document will be persisted until
* deleted manually ("forever").
*
@@ -220,7 +222,7 @@ public class CouchbaseDocument implements CouchbaseStorable {
/**
* Set the expiration time of the document.
*
* <p/>
* If the expiration time is 0, then the document will be persisted until
* deleted manually ("forever").
*
@@ -255,19 +257,19 @@ public class CouchbaseDocument implements CouchbaseStorable {
/**
* Verifies that only values of a certain and supported type
* can be stored.
*
* <p/>
* <p>If this is not the case, a {@link IllegalArgumentException} is
* thrown.</p>
*
* @param value the object to verify its type.
*/
private void verifyValueType(final Object value) {
if(value == null) {
if (value == null) {
return;
}
final Class<?> clazz = value.getClass();
if (simpleTypeHolder.isSimpleType(clazz)) {
return;
return;
}
throw new IllegalArgumentException("Attribute of type " + clazz.getCanonicalName() + " cannot be stored and must be converted.");
}
@@ -280,9 +282,9 @@ public class CouchbaseDocument implements CouchbaseStorable {
@Override
public String toString() {
return "CouchbaseDocument{" +
"id=" + id +
", exp=" + expiration +
", payload=" + payload +
'}';
"id=" + id +
", exp=" + expiration +
", payload=" + payload +
'}';
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -16,16 +16,16 @@
package org.springframework.data.couchbase.core.mapping;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.data.mapping.model.SimpleTypeHolder;
/**
* A {@link CouchbaseList} is an abstract list that represents an array stored in a (most of the times JSON) document.
*
* <p/>
* <p>This {@link CouchbaseList} is part of the potentially nested structure inside one or more
* {@link CouchbaseDocument}s. It can also contain them recursively, depending on how the document is modeled.</p>
*/
@@ -78,9 +78,10 @@ public class CouchbaseList implements CouchbaseStorable {
additionalTypes.add(CouchbaseDocument.class);
additionalTypes.add(CouchbaseList.class);
if (simpleTypeHolder != null) {
this.simpleTypeHolder = new SimpleTypeHolder(additionalTypes, simpleTypeHolder);
} else {
this.simpleTypeHolder = new SimpleTypeHolder(additionalTypes, true);
this.simpleTypeHolder = new SimpleTypeHolder(additionalTypes, simpleTypeHolder);
}
else {
this.simpleTypeHolder = new SimpleTypeHolder(additionalTypes, true);
}
}
@@ -133,7 +134,8 @@ public class CouchbaseList implements CouchbaseStorable {
for (Object value : payload) {
if (value instanceof CouchbaseDocument) {
totalSize += ((CouchbaseDocument) value).size(true);
} else if (value instanceof CouchbaseList) {
}
else if (value instanceof CouchbaseList) {
totalSize += ((CouchbaseList) value).size(true);
}
}
@@ -143,7 +145,7 @@ public class CouchbaseList implements CouchbaseStorable {
/**
* Returns the current payload, including all recursive elements.
*
* <p/>
* It either returns the raw results or makes sure that the recusrive elements
* are also exported properly.
*
@@ -157,7 +159,8 @@ public class CouchbaseList implements CouchbaseStorable {
if (entry instanceof CouchbaseDocument) {
toExport.remove(elem);
toExport.add(elem, ((CouchbaseDocument) entry).export());
} else if (entry instanceof CouchbaseList) {
}
else if (entry instanceof CouchbaseList) {
toExport.remove(elem);
toExport.add(elem, ((CouchbaseList) entry).export());
}
@@ -188,14 +191,14 @@ public class CouchbaseList implements CouchbaseStorable {
/**
* Verifies that only values of a certain and supported type
* can be stored.
*
* <p/>
* <p>If this is not the case, a {@link IllegalArgumentException} is
* thrown.</p>
*
* @param value the object to verify its type.
*/
private void verifyValueType(final Object value) {
if(value == null) {
if (value == null) {
return;
}
@@ -205,7 +208,7 @@ public class CouchbaseList implements CouchbaseStorable {
}
throw new IllegalArgumentException("Attribute of type "
+ clazz.getCanonicalName() + "can not be stored and must be converted.");
+ clazz.getCanonicalName() + "can not be stored and must be converted.");
}
/**
@@ -216,7 +219,7 @@ public class CouchbaseList implements CouchbaseStorable {
@Override
public String toString() {
return "CouchbaseList{" +
"payload=" + payload +
'}';
"payload=" + payload +
'}';
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -16,6 +16,9 @@
package org.springframework.data.couchbase.core.mapping;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -25,9 +28,6 @@ import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.TypeInformation;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
/**
* Default implementation of a {@link org.springframework.data.mapping.context.MappingContext} for Couchbase using
* {@link BasicCouchbasePersistentEntity} and {@link BasicCouchbasePersistentProperty} as primary abstractions.
@@ -35,8 +35,8 @@ import java.lang.reflect.Field;
* @author Michael Nitschinger
*/
public class CouchbaseMappingContext
extends AbstractMappingContext<BasicCouchbasePersistentEntity<?>, CouchbasePersistentProperty>
implements ApplicationContextAware {
extends AbstractMappingContext<BasicCouchbasePersistentEntity<?>, CouchbasePersistentProperty>
implements ApplicationContextAware {
/**
* Contains the application context to configure the application.
@@ -58,7 +58,7 @@ public class CouchbaseMappingContext
* Defaults to a strategy using the plain property name.
*
* @param fieldNamingStrategy the {@link FieldNamingStrategy} to be used to determine the field name if no manual
* mapping is applied.
* mapping is applied.
*/
public void setFieldNamingStrategy(final FieldNamingStrategy fieldNamingStrategy) {
this.fieldNamingStrategy = fieldNamingStrategy == null ? DEFAULT_NAMING_STRATEGY : fieldNamingStrategy;
@@ -91,7 +91,7 @@ public class CouchbaseMappingContext
*/
@Override
protected CouchbasePersistentProperty createPersistentProperty(final Field field, final PropertyDescriptor descriptor,
final BasicCouchbasePersistentEntity<?> owner, final SimpleTypeHolder simpleTypeHolder) {
final BasicCouchbasePersistentEntity<?> owner, final SimpleTypeHolder simpleTypeHolder) {
return new BasicCouchbasePersistentProperty(field, descriptor, owner, simpleTypeHolder, fieldNamingStrategy);
}
@@ -101,9 +101,9 @@ public class CouchbaseMappingContext
* @param applicationContext the application context to be assigned.
* @throws BeansException if the context can not be set properly.
*/
@Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
@Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -24,13 +24,13 @@ import org.springframework.data.mapping.PersistentEntity;
* @author Michael Nitschinger
*/
public interface CouchbasePersistentEntity<T> extends
PersistentEntity<T, CouchbasePersistentProperty> {
PersistentEntity<T, CouchbasePersistentProperty> {
/**
* Returns the expiry time for the document.
*
* @return the expiration time.
*/
int getExpiry();
int getExpiry();
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -27,7 +27,7 @@ public interface CouchbasePersistentProperty extends PersistentProperty<Couchbas
/**
* Returns the field name of the property.
*
* <p/>
* The field name can be different from the actual property name by using a custom annotation.
*/
String getFieldName();

View File

@@ -1,18 +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 org.springframework.data.mapping.model.SimpleTypeHolder;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import com.couchbase.client.java.document.RawJsonDocument;
import com.couchbase.client.java.document.json.JsonArray;
import org.springframework.data.mapping.model.SimpleTypeHolder;
public abstract class CouchbaseSimpleTypes {
static {
Set<Class<?>> simpleTypes = new HashSet<Class<?>>();
simpleTypes.add(CouchbaseDocument.class);
simpleTypes.add(CouchbaseList.class);
simpleTypes.add(RawJsonDocument.class);
simpleTypes.add(JsonArray.class);
COUCHBASE_SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes);
}

View File

@@ -1,8 +1,24 @@
/*
* 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;
/**
* Marker Interface to identify either a {@link CouchbaseDocument} or a {@link CouchbaseList}.
*
* <p/>
* This interface will be extended in the future to refactor the needed infrastructure into the common interface.
*
* @author Michael Nitschinger

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -16,9 +16,13 @@
package org.springframework.data.couchbase.core.mapping;
import org.springframework.data.annotation.Persistent;
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 java.lang.annotation.*;
import org.springframework.data.annotation.Persistent;
/**
* Identifies a domain object to be persisted to Couchbase.
@@ -28,9 +32,9 @@ import java.lang.annotation.*;
@Persistent
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@Target({ElementType.TYPE})
public @interface Document {
/**
* An optional expiry time for the document.
*/

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -18,6 +18,7 @@ package org.springframework.data.couchbase.core.mapping.event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.core.GenericTypeResolver;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
@@ -42,25 +43,28 @@ public class AbstractCouchbaseEventListener<E> implements ApplicationListener<Co
@SuppressWarnings("rawtypes")
public void onApplicationEvent(CouchbaseMappingEvent<?> event) {
E source = (E) event.getSource();
// Check for matching domain type and invoke callbacks
if (source != null && !domainClass.isAssignableFrom(source.getClass())) {
return;
}
E source = (E) event.getSource();
// Check for matching domain type and invoke callbacks
if (source != null && !domainClass.isAssignableFrom(source.getClass())) {
return;
}
if (event instanceof BeforeDeleteEvent) {
onBeforeDelete(event.getSource(), event.getDocument());
return;
} else if (event instanceof AfterDeleteEvent) {
}
else if (event instanceof AfterDeleteEvent) {
onAfterDelete(event.getSource(), event.getDocument());
return;
}
if (event instanceof BeforeConvertEvent) {
onBeforeConvert(source);
} else if (event instanceof BeforeSaveEvent) {
}
else if (event instanceof BeforeSaveEvent) {
onBeforeSave(source, event.getDocument());
} else if (event instanceof AfterSaveEvent) {
}
else if (event instanceof AfterSaveEvent) {
onAfterSave(source, event.getDocument());
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -18,6 +18,7 @@ package org.springframework.data.couchbase.core.mapping.event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
@@ -31,10 +32,14 @@ public class LoggingEventListener extends AbstractCouchbaseEventListener<Object>
private static final Logger LOGGER = LoggerFactory.getLogger(LoggingEventListener.class);
@Override
public void onBeforeDelete(Object source, CouchbaseDocument doc) { LOGGER.info("onBeforeDelete: {}, {}", source, doc); }
public void onBeforeDelete(Object source, CouchbaseDocument doc) {
LOGGER.info("onBeforeDelete: {}, {}", source, doc);
}
@Override
public void onAfterDelete(Object source, CouchbaseDocument doc) { LOGGER.info("onAfterDelete: {} {}", source, doc); }
public void onAfterDelete(Object source, CouchbaseDocument doc) {
LOGGER.info("onAfterDelete: {} {}", source, doc);
}
@Override
public void onAfterSave(Object source, CouchbaseDocument doc) {

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -16,14 +16,16 @@
package org.springframework.data.couchbase.core.mapping.event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.util.Assert;
import java.util.Set;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.util.Assert;
/**
* javax.validation dependant entities validator. When it is registered as Spring component its automatically invoked
@@ -49,10 +51,12 @@ public class ValidatingCouchbaseEventListener extends AbstractCouchbaseEventList
}
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
@SuppressWarnings({"rawtypes", "unchecked"})
public void onBeforeSave(Object source, CouchbaseDocument dbo) {
LOG.debug("Validating object: {}", source);
if (LOG.isDebugEnabled()) {
LOG.debug("Validating object: {}", source);
}
Set violations = validator.validate(source);
if (!violations.isEmpty()) {

View File

@@ -0,0 +1,26 @@
/*
* 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.
*/
/**
* This package contains the specific implementations and core classes for
* Spring Data Couchbase internals. It also contains Couchbase implementation
* to support the Spring Data template abstraction.
* <br/>
* The template provides lower level access to the underlying database and also serves as the foundation for
* repositories. Any time a repository is too high-level for you needs chances are good that the templates will serve
* you well.
*/
package org.springframework.data.couchbase.core;

View File

@@ -1,74 +0,0 @@
/*
* Copyright 2013 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 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;
/**
* Base class to encapsulate common configuration settings.
*
* @author Michael Nitschinger
*/
public abstract class AbstractMonitor {
private CouchbaseClient client;
private RestTemplate template = new RestTemplate();
protected AbstractMonitor(final CouchbaseClient client) {
this.client = client;
}
public CouchbaseClient getClient() {
return client;
}
protected RestTemplate getTemplate() {
return template;
}
protected String randomAvailableHostname() {
List<SocketAddress> available = (ArrayList<SocketAddress>) client.getAvailableServers();
Collections.shuffle(available);
return ((InetSocketAddress) available.get(0)).getHostName();
}
/**
* Fetches stats for all nodes.
*
* @return stats for each node
*/
protected Map<SocketAddress, Map<String, String>> getStats() {
return client.getStats();
}
/**
* Returns stats for an individual node.
*/
protected Map<String, String> getStats(SocketAddress node) {
return getStats().get(node);
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -16,28 +16,35 @@
package org.springframework.data.couchbase.monitor;
import com.couchbase.client.CouchbaseClient;
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;
import java.net.SocketAddress;
/**
* Exposes basic client information.
*
* @author Michael Nitschinger
* @author Simon Baslé
*/
@ManagedResource(description = "Client Information")
public class ClientInfo extends AbstractMonitor {
@ManagedResource(description = "Client Information")
public class ClientInfo {
public ClientInfo(final CouchbaseClient client) {
super(client);
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 (SocketAddress node : getStats().keySet()) {
for (InetAddress node : info.nodeList()) {
result.append(node.toString()).append(",");
}
return result.toString();
@@ -45,17 +52,9 @@ public class ClientInfo extends AbstractMonitor {
@ManagedAttribute(description = "Number of connected nodes")
public int getNumberOfNodes() {
return getStats().keySet().size();
return info.nodeCount();
}
@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();
}
//TODO obtain count of available nodes vs unavailable ones and expose it
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013 the original author or authors.
* 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
* 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,
@@ -16,23 +16,36 @@
package org.springframework.data.couchbase.monitor;
import com.couchbase.client.CouchbaseClient;
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 java.util.HashMap;
import org.springframework.web.client.RestTemplate;
/**
* Exposes basic cluster information.
*
* @author Michael Nitschinger
* @author Simon Baslé
*/
@ManagedResource(description = "Cluster Information")
public class ClusterInfo extends AbstractMonitor {
@ManagedResource(description = "Cluster Information")
public class ClusterInfo {
public ClusterInfo(final CouchbaseClient client) {
super(client);
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")
@@ -80,27 +93,34 @@ public class ClusterInfo extends AbstractMonitor {
* 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) {
}
else if (value instanceof Long) {
return (Long) value;
} else {
}
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 getTemplate().getForObject("http://"
+ randomAvailableHostname() + ":8091/pools/default", HashMap.class);
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");
return (HashMap<String, HashMap>) stats.get("storageTotals");
}
}

View File

@@ -0,0 +1,5 @@
/**
* This package contains all classes related to monitoring the Couchbase cluster,
* statistics that will be exposed as JMX beans.
*/
package org.springframework.data.couchbase.monitor;

View File

@@ -0,0 +1,7 @@
/**
* This package contains the Couchbase implementation to support the Spring Data repository abstraction.
* <br/>
* The goal of Spring Data repository abstraction is to significantly reduce the amount of boilerplate code required to
* implement data access layers for various persistence stores.
*/
package org.springframework.data.couchbase.repository;

View File

@@ -16,7 +16,8 @@
package org.springframework.data.couchbase.repository.query;
import com.couchbase.client.protocol.views.Query;
import com.couchbase.client.java.view.ViewQuery;
import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.RepositoryQuery;
@@ -39,21 +40,21 @@ public class ViewBasedCouchbaseQuery implements RepositoryQuery {
@Override
public Object execute(Object[] runtimeParams) {
Query query = null;
ViewQuery query = null;
for (Object param : runtimeParams) {
if (param instanceof Query) {
query = (Query) param;
if (param instanceof ViewQuery) {
query = (ViewQuery) param;
} else {
throw new IllegalStateException("Unknown query param: " + param);
}
}
if (query == null) {
query = new Query();
query = ViewQuery.from(designDocName(), viewName());
}
query.setReduce(false);
query.reduce(false);
return operations.findByView(designDocName(), viewName(), query, method.getEntityInformation().getJavaType());
return operations.findByView(query, method.getEntityInformation().getJavaType());
}
@Override

View File

@@ -16,20 +16,21 @@
package org.springframework.data.couchbase.repository.support;
import com.couchbase.client.protocol.views.ComplexKey;
import com.couchbase.client.protocol.views.Query;
import com.couchbase.client.protocol.views.ViewResponse;
import com.couchbase.client.protocol.views.ViewRow;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import com.couchbase.client.java.document.json.JsonArray;
import com.couchbase.client.java.view.ViewQuery;
import com.couchbase.client.java.view.ViewResult;
import com.couchbase.client.java.view.ViewRow;
import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.couchbase.core.view.View;
import org.springframework.data.couchbase.repository.CouchbaseRepository;
import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation;
import org.springframework.util.Assert;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* Repository base implementation for Couchbase.
*
@@ -130,30 +131,36 @@ public class SimpleCouchbaseRepository<T, ID extends Serializable> implements Co
@Override
public Iterable<T> findAll() {
final ResolvedView resolvedView = determineView();
return couchbaseOperations.findByView(resolvedView.getDesignDocument(), resolvedView.getViewName(), new Query().setReduce(false), entityInformation.getJavaType());
ViewQuery query = ViewQuery.from(resolvedView.getDesignDocument(), resolvedView.getViewName());
query.reduce(false);
return couchbaseOperations.findByView(query, entityInformation.getJavaType());
}
@Override
public Iterable<T> findAll(final Iterable<ID> ids) {
Query query = new Query();
query.setReduce(false);
query.setKeys(ComplexKey.of(ids));
final ResolvedView resolvedView = determineView();
return couchbaseOperations.findByView(resolvedView.getDesignDocument(), resolvedView.getViewName(), query, entityInformation.getJavaType());
ViewQuery query = ViewQuery.from(resolvedView.getDesignDocument(), resolvedView.getViewName());
query.reduce(false);
JsonArray keys = JsonArray.create();
for (ID id : ids) {
keys.add(id);
}
query.keys(keys);
return couchbaseOperations.findByView(query, entityInformation.getJavaType());
}
@Override
public long count() {
Query query = new Query();
query.setReduce(true);
final ResolvedView resolvedView = determineView();
ViewResponse response = couchbaseOperations.queryView(resolvedView.getDesignDocument(), resolvedView.getViewName(), query);
ViewQuery query = ViewQuery.from(resolvedView.getDesignDocument(), resolvedView.getViewName());
query.reduce(true);
ViewResult response = couchbaseOperations.queryView(query);
long count = 0;
for (ViewRow row : response) {
count += Long.parseLong(row.getValue());
count += Long.parseLong(String.valueOf(row.value()));
}
return count;
@@ -161,13 +168,13 @@ public class SimpleCouchbaseRepository<T, ID extends Serializable> implements Co
@Override
public void deleteAll() {
Query query = new Query();
query.setReduce(false);
final ResolvedView resolvedView = determineView();
ViewResponse response = couchbaseOperations.queryView(resolvedView.getDesignDocument(), resolvedView.getViewName(), query);
ViewQuery query = ViewQuery.from(resolvedView.getDesignDocument(), resolvedView.getViewName());
query.reduce(false);
ViewResult response = couchbaseOperations.queryView(query);
for (ViewRow row : response) {
couchbaseOperations.remove(row.getId());
couchbaseOperations.remove(row.id());
}
}