diff --git a/spring-data-riak/pom.xml b/spring-data-riak/pom.xml index fd9debb2b..daa112025 100644 --- a/spring-data-riak/pom.xml +++ b/spring-data-riak/pom.xml @@ -13,41 +13,8 @@ Spring Data Riak Support - - - org.springframework - spring-beans - - - org.springframework - spring-tx - - - org.springframework - spring-web - - - org.springframework - spring-test - - - - org.springframework.data - spring-data-keyvalue-core - - - - - org.codehaus.jackson - jackson-core-asl - - - org.codehaus.jackson - jackson-mapper-asl - - - + + + commons-logging + commons-logging + 1.1.1 + + + + org.springframework + spring-beans + + + org.springframework + spring-tx + + + org.springframework + spring-web + + + org.springframework + spring-test + + + + + org.codehaus.groovy + groovy-all + + + + + org.springframework.data + spring-data-keyvalue-core + + + + + org.codehaus.jackson + jackson-core-asl + + + org.codehaus.jackson + jackson-mapper-asl + + + javax.annotation jsr250-api @@ -101,18 +115,14 @@ activation + - org.mockito - mockito-all - test - - - - - org.codehaus.groovy - groovy-all + commons-cli + commons-cli + 1.2 + junit junit @@ -121,6 +131,11 @@ org.spockframework spock-spring + + org.mockito + mockito-all + test + diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/AbstractRiakTemplate.java b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/AbstractRiakTemplate.java index aeced402e..85b50f641 100644 --- a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/AbstractRiakTemplate.java +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/AbstractRiakTemplate.java @@ -18,17 +18,19 @@ package org.springframework.data.keyvalue.riak.core; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.codehaus.groovy.runtime.GStringImpl; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.ser.CustomSerializerFactory; import org.codehaus.jackson.map.ser.ToStringSerializer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.ConversionServiceFactory; import org.springframework.data.keyvalue.riak.DataStoreOperationException; import org.springframework.data.keyvalue.riak.convert.KeyValueStoreMetaData; +import org.springframework.data.keyvalue.riak.util.Ignore404sErrorHandler; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpInputMessage; import org.springframework.http.MediaType; @@ -39,6 +41,7 @@ import org.springframework.http.converter.json.MappingJacksonHttpMessageConverte import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; +import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestTemplate; import org.springframework.web.client.support.RestGatewaySupport; @@ -62,7 +65,7 @@ import java.util.regex.Pattern; * * @author J. Brisbin */ -public abstract class AbstractRiakTemplate extends RestGatewaySupport implements InitializingBean { +public abstract class AbstractRiakTemplate extends RestGatewaySupport implements InitializingBean, BeanClassLoaderAware { protected static final String RIAK_META_CLASSNAME = "X-Riak-Meta-ClassName"; protected static final String RIAK_VCLOCK = "X-Riak-Vclock"; @@ -75,16 +78,16 @@ public abstract class AbstractRiakTemplate extends RestGatewaySupport implements /** * Do we need to handle Groovy strings in the Jackson JSON processor? */ - protected static final boolean groovyPresent = ClassUtils.isPresent( + protected final boolean groovyPresent = ClassUtils.isPresent( "org.codehaus.groovy.runtime.GStringImpl", - RiakTemplate.class.getClassLoader()); + getClass().getClassLoader()); /** * For getting a java.util.Date from the Last-Modified header. */ protected static SimpleDateFormat httpDate = new SimpleDateFormat( "EEE, d MMM yyyy HH:mm:ss z"); - protected final Logger log = LoggerFactory.getLogger(getClass()); + protected final Log log = LogFactory.getLog(getClass()); /** * Client ID used by Riak to correlate updates. @@ -124,8 +127,14 @@ public abstract class AbstractRiakTemplate extends RestGatewaySupport implements * {@link java.util.concurrent.ExecutorService} to use for running asynchronous jobs. */ protected ExecutorService workerPool = Executors.newCachedThreadPool(); - + /** + * Default type to use when trying to deserialize objects and we can't otherwise tell what to + * do. + */ protected Class defaultType = String.class; + /** + * ClassLoader to use for saving/loading objects using the automatic converters. + */ protected ClassLoader classLoader = null; /** @@ -133,7 +142,6 @@ public abstract class AbstractRiakTemplate extends RestGatewaySupport implements */ public AbstractRiakTemplate() { setRestTemplate(new RestTemplate()); - bucketKeyResolvers.add(new SimpleBucketKeyResolver()); } /** @@ -144,7 +152,6 @@ public abstract class AbstractRiakTemplate extends RestGatewaySupport implements public AbstractRiakTemplate(ClientHttpRequestFactory requestFactory) { super(requestFactory); setRestTemplate(new RestTemplate()); - bucketKeyResolvers.add(new SimpleBucketKeyResolver()); } public ConversionService getConversionService() { @@ -200,6 +207,19 @@ public abstract class AbstractRiakTemplate extends RestGatewaySupport implements this.workerPool = workerPool; } + public void setIgnoreNotFound(boolean b) { + if (b) { + getRestTemplate().setErrorHandler(new Ignore404sErrorHandler()); + } else { + if (getRestTemplate().getErrorHandler() instanceof Ignore404sErrorHandler) { + getRestTemplate().setErrorHandler(new DefaultResponseErrorHandler()); + } + } + } + + public boolean getIgnoreNotFound() { + return (getRestTemplate().getErrorHandler() instanceof Ignore404sErrorHandler); + } /** * Get the default type to use if none can be inferred. @@ -219,21 +239,7 @@ public abstract class AbstractRiakTemplate extends RestGatewaySupport implements this.defaultType = defaultType; } - /** - * Get the {@link ClassLoader} to use when trying to load objects from the store. - * - * @return - */ - public ClassLoader getClassLoader() { - return classLoader; - } - - /** - * Set the {@link ClassLoader} to use when trying to load objects from the store. - * - * @param classLoader - */ - public void setClassLoader(ClassLoader classLoader) { + public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } @@ -285,6 +291,7 @@ public abstract class AbstractRiakTemplate extends RestGatewaySupport implements } } } + /*----------------- Utilities -----------------*/ @SuppressWarnings({"unchecked"}) @@ -296,26 +303,24 @@ public abstract class AbstractRiakTemplate extends RestGatewaySupport implements break; } } - BucketKeyPair bucketKeyPair; - if (null != resolver) { - bucketKeyPair = resolver.resolve(key); - if (null == bucketKeyPair.getBucket() && null != val) { - // No bucket specified, check for an annotation that specified bucket name. - Annotation meta = (val instanceof Class ? (Class) val : val.getClass()).getAnnotation( - org.springframework.data.keyvalue.riak.convert.KeyValueStoreMetaData.class); - if (null != meta) { - String bucket = ((KeyValueStoreMetaData) meta).bucket(); - if (null != bucket) { - return new SimpleBucketKeyPair(bucket, - bucketKeyPair.getKey()); - } + if (null == resolver) { + resolver = new SimpleBucketKeyResolver(); + } + + BucketKeyPair bucketKeyPair = resolver.resolve(key); + if (null == bucketKeyPair.getBucket() && null != val) { + // No bucket specified, check for an annotation that specified bucket name. + Annotation meta = (val instanceof Class ? (Class) val : val.getClass()).getAnnotation( + org.springframework.data.keyvalue.riak.convert.KeyValueStoreMetaData.class); + if (null != meta) { + String bucket = ((KeyValueStoreMetaData) meta).bucket(); + if (null != bucket) { + return new SimpleBucketKeyPair(bucket, + bucketKeyPair.getKey()); } } - return bucketKeyPair; } - throw new DataStoreOperationException(String.format( - "No resolvers available to resolve bucket/key pair from %s", - key)); + return bucketKeyPair; } protected MediaType extractMediaType(Object value) { diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/AsyncRiakTemplate.java b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/AsyncRiakTemplate.java index 1f2e41d9d..b96a82765 100644 --- a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/AsyncRiakTemplate.java +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/AsyncRiakTemplate.java @@ -18,8 +18,6 @@ package org.springframework.data.keyvalue.riak.core; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.data.keyvalue.riak.DataStoreOperationException; import org.springframework.data.keyvalue.riak.mapreduce.AsyncMapReduceOperations; @@ -73,8 +71,6 @@ import java.util.concurrent.Future; */ public class AsyncRiakTemplate extends AbstractRiakTemplate implements AsyncBucketKeyValueStoreOperations, AsyncMapReduceOperations { - protected final Logger log = LoggerFactory.getLogger(getClass()); - protected AsyncKeyValueStoreOperation defaultErrorHandler = new LoggingErrorHandler(); public AsyncRiakTemplate() { diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/RiakTemplate.java b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/RiakTemplate.java index 7164ef759..740fb96f2 100644 --- a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/RiakTemplate.java +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/RiakTemplate.java @@ -23,7 +23,6 @@ import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.data.keyvalue.riak.DataStoreOperationException; import org.springframework.data.keyvalue.riak.mapreduce.MapReduceJob; import org.springframework.data.keyvalue.riak.mapreduce.MapReduceOperations; -import org.springframework.data.keyvalue.riak.mapreduce.RiakMapReduceJob; import org.springframework.http.*; import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpRequestFactory; @@ -510,10 +509,6 @@ public class RiakTemplate extends AbstractRiakTemplate implements BucketKeyValue /*----------------- Map/Reduce Operations -----------------*/ - public RiakMapReduceJob createMapReduceJob() { - return new RiakMapReduceJob(this); - } - public Object execute(MapReduceJob job) { return execute(job, List.class); } diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/SimpleBucketKeyResolver.java b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/SimpleBucketKeyResolver.java index 118a7c890..5d5ea0419 100644 --- a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/SimpleBucketKeyResolver.java +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/SimpleBucketKeyResolver.java @@ -30,9 +30,9 @@ import java.util.regex.Pattern; @SuppressWarnings({"unchecked"}) public class SimpleBucketKeyResolver implements BucketKeyResolver { - private static final boolean groovyPresent = ClassUtils.isPresent( + private final boolean groovyPresent = ClassUtils.isPresent( "org.codehaus.groovy.runtime.GStringImpl", - RiakTemplate.class.getClassLoader()); + getClass().getClassLoader()); protected Pattern bucketColonKey = Pattern.compile("(.+):(.+)"); diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/io/RiakFile.java b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/io/RiakFile.java index eb697a8ca..1687172af 100644 --- a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/io/RiakFile.java +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/io/RiakFile.java @@ -18,8 +18,8 @@ package org.springframework.data.keyvalue.riak.core.io; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.data.keyvalue.riak.DataStoreOperationException; import org.springframework.data.keyvalue.riak.core.KeyValueStoreMetaData; import org.springframework.data.keyvalue.riak.core.RiakTemplate; @@ -43,7 +43,7 @@ import java.util.Map; public class RiakFile extends File { private static final long serialVersionUID = 1L; - private static final Logger log = LoggerFactory.getLogger(RiakFile.class); + protected final Log log = LogFactory.getLog(getClass()); private RiakTemplate riak; private B bucket; diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/io/RiakResource.java b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/io/RiakResource.java index a5e4b172b..7b34e0c8a 100644 --- a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/io/RiakResource.java +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/io/RiakResource.java @@ -18,8 +18,8 @@ package org.springframework.data.keyvalue.riak.core.io; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.data.keyvalue.riak.core.RiakTemplate; @@ -40,7 +40,7 @@ import java.net.URL; */ public class RiakResource extends UrlResource { - private static final Logger log = LoggerFactory.getLogger(RiakResource.class); + protected final Log log = LogFactory.getLog(getClass()); private RiakTemplate riak; private B bucket; diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/io/overview.html b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/io/overview.html new file mode 100644 index 000000000..e9d4adc0c --- /dev/null +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/io/overview.html @@ -0,0 +1,15 @@ + + +

+ Utilities for working with resources stored in Riak as standard java.io objects. Opening a RiakInputStream to a resource will allow code that doesn't + know anything about Key/Value datastores to access resources stored within them. +

+ +

+ Alternatively, writing data to a RiakOutputStream will + create a resources in Riak without exposing any of the underlying data access code to the + calling application. +

+ + \ No newline at end of file diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/overview.html b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/overview.html new file mode 100644 index 000000000..dc82cc087 --- /dev/null +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/overview.html @@ -0,0 +1,7 @@ + + +

+ Root package for the core utilities that make up the Riak data access library. +

+ + \ No newline at end of file diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/groovy/RiakBuilder.java b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/groovy/RiakBuilder.java index 13ca114cd..381465e7d 100644 --- a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/groovy/RiakBuilder.java +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/groovy/RiakBuilder.java @@ -20,8 +20,8 @@ package org.springframework.data.keyvalue.riak.groovy; import groovy.lang.Closure; import groovy.util.BuilderSupport; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.keyvalue.riak.DataStoreOperationException; import org.springframework.data.keyvalue.riak.core.AsyncRiakTemplate; @@ -60,7 +60,7 @@ public class RiakBuilder extends BuilderSupport { CALL, FOREACH, MAPREDUCE, QUERY, MAP, REDUCE, INPUTS, LANGUAGE, SOURCE, KEEP, ARG, COMPLETED, FAILED } - protected final Logger log = LoggerFactory.getLogger(getClass()); + protected final Log log = LogFactory.getLog(getClass()); @Autowired(required = false) protected AsyncRiakTemplate riak; @Autowired(required = false) diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/groovy/RiakMapReduceOperation.java b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/groovy/RiakMapReduceOperation.java index e00fc69d1..6690e94b1 100644 --- a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/groovy/RiakMapReduceOperation.java +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/groovy/RiakMapReduceOperation.java @@ -19,8 +19,8 @@ package org.springframework.data.keyvalue.riak.groovy; import groovy.lang.Closure; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.data.keyvalue.riak.core.AsyncKeyValueStoreOperation; import org.springframework.data.keyvalue.riak.core.AsyncRiakTemplate; import org.springframework.data.keyvalue.riak.core.KeyValueStoreMetaData; @@ -36,7 +36,7 @@ import java.util.concurrent.TimeUnit; */ public class RiakMapReduceOperation implements Callable { - protected final Logger log = LoggerFactory.getLogger(getClass()); + protected final Log log = LogFactory.getLog(getClass()); protected AsyncRiakTemplate riak; protected AsyncRiakMapReduceJob job; diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/groovy/RiakOperation.java b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/groovy/RiakOperation.java index acdf21308..0ce166410 100644 --- a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/groovy/RiakOperation.java +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/groovy/RiakOperation.java @@ -19,8 +19,8 @@ package org.springframework.data.keyvalue.riak.groovy; import groovy.lang.Closure; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.data.keyvalue.riak.DataStoreOperationException; import org.springframework.data.keyvalue.riak.core.AsyncKeyValueStoreOperation; import org.springframework.data.keyvalue.riak.core.AsyncRiakTemplate; @@ -42,7 +42,7 @@ public class RiakOperation implements Callable { static String COMPLETED = "completed"; static String FAILED = "failed"; - protected final Logger log = LoggerFactory.getLogger(getClass()); + protected final Log log = LogFactory.getLog(getClass()); protected AsyncRiakTemplate riak; protected Type type; diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/groovy/overview.html b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/groovy/overview.html new file mode 100644 index 000000000..d7856cbe9 --- /dev/null +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/groovy/overview.html @@ -0,0 +1,8 @@ + + +

+ Utilities for making Riak data access easier in Groovy. The RiakBuilder + provides a Groovy DSL for interacting with Riak. +

+ + \ No newline at end of file diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/mapreduce/AbstractRiakMapReduceJob.java b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/mapreduce/AbstractRiakMapReduceJob.java index 7de70e631..fc41e66db 100644 --- a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/mapreduce/AbstractRiakMapReduceJob.java +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/mapreduce/AbstractRiakMapReduceJob.java @@ -18,11 +18,11 @@ package org.springframework.data.keyvalue.riak.mapreduce; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.map.ObjectMapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.data.keyvalue.riak.core.BucketKeyPair; import java.io.IOException; @@ -40,7 +40,7 @@ import java.util.Map; @SuppressWarnings({"unchecked"}) public abstract class AbstractRiakMapReduceJob implements MapReduceJob { - protected final Logger log = LoggerFactory.getLogger(getClass()); + protected final Log log = LogFactory.getLog(getClass()); protected List inputs = new LinkedList(); protected List phases = new ArrayList(); diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/mapreduce/overview.html b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/mapreduce/overview.html new file mode 100644 index 000000000..e9362f9de --- /dev/null +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/mapreduce/overview.html @@ -0,0 +1,7 @@ + + +

+ Root package for +

+ + \ No newline at end of file diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/overview.html b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/overview.html new file mode 100644 index 000000000..4d1677397 --- /dev/null +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/overview.html @@ -0,0 +1,8 @@ + + +

+ Root package for integrating Riak with Spring + concepts. +

+ + \ No newline at end of file diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/util/Ignore404sErrorHandler.java b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/util/Ignore404sErrorHandler.java new file mode 100644 index 000000000..8e7bf5aac --- /dev/null +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/util/Ignore404sErrorHandler.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 by J. Brisbin + * Portions (c) 2011 by NPC International, Inc. or the + * original author(s). + * + * 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.keyvalue.riak.util; + +import org.springframework.http.HttpStatus; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.web.client.DefaultResponseErrorHandler; + +import java.io.IOException; + +/** + * @author J. Brisbin + */ +public class Ignore404sErrorHandler extends DefaultResponseErrorHandler { + + @Override + protected boolean hasError(HttpStatus statusCode) { + if (statusCode != HttpStatus.NOT_FOUND) { + return super.hasError(statusCode); + } else { + return false; + } + } + + @Override + public void handleError(ClientHttpResponse response) throws IOException { + // Ignore 404s entirely + if (response.getStatusCode() != HttpStatus.NOT_FOUND) { + super.handleError(response); + } + } + +} diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/util/RiakClassFileLoader.java b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/util/RiakClassFileLoader.java new file mode 100644 index 000000000..42ee23072 --- /dev/null +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/util/RiakClassFileLoader.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2011 by J. Brisbin + * Portions (c) 2011 by NPC International, Inc. or the + * original author(s). + * + * 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.keyvalue.riak.util; + +import org.apache.commons.cli.*; +import org.springframework.data.keyvalue.riak.core.RiakTemplate; + +import java.io.*; +import java.net.URLEncoder; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * @author J. Brisbin + */ +public class RiakClassFileLoader { + + static Options opts = new Options(); + + static { + opts.addOption("v", false, "Verbose output"); + opts.addOption("u", + true, + "URL to Riak (defaults to: 'http://localhost:8098/riak/{bucket}/{key}')"); + opts.addOption("b", true, "Bucket to load class files into"); + opts.addOption("k", true, "Key under which to store an individual class file"); + opts.addOption("j", true, "JAR file to load into Riak"); + opts.addOption("c", true, "Class file to load into Riak"); + opts.addOption("d", true, "Directory from which to load all JAR files into Riak"); + } + + public static void main(String[] args) { + Parser p = new BasicParser(); + CommandLine cl = null; + try { + cl = p.parse(opts, args); + } catch (ParseException e) { + System.err.println("Error parsing command line: " + e.getMessage()); + } + + if (null != cl) { + boolean verbose = cl.hasOption('v'); + RiakTemplate riak = new RiakTemplate(); + riak.getRestTemplate().setErrorHandler(new Ignore404sErrorHandler()); + if (cl.hasOption('u')) { + riak.setDefaultUri(cl.getOptionValue('u')); + } + try { + riak.afterPropertiesSet(); + } catch (Exception e) { + System.err.println("Error creating RiakTemplate: " + e.getMessage()); + } + String[] files = cl.getOptionValues('j'); + if (null != files) { + for (String file : files) { + if (verbose) { + System.out.println(String.format("Loading JAR file %s into Riak...", file)); + } + try { + File zfile = new File(file); + ZipInputStream zin = new ZipInputStream(new FileInputStream(zfile)); + ZipEntry entry; + while (null != (entry = zin.getNextEntry())) { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + byte[] buff = new byte[16384]; + for (int bytesRead = zin.read(buff); bytesRead > 0; bytesRead = zin.read(buff)) { + bout.write(buff, 0, bytesRead); + } + + if (entry.getName().endsWith(".class")) { + String name = entry.getName().replaceAll("/", "."); + name = URLEncoder.encode(name.substring(0, name.length() - 6), "UTF-8"); + String bucket; + if (cl.hasOption('b')) { + bucket = cl.getOptionValue('b'); + } else { + bucket = URLEncoder.encode(zfile.getCanonicalFile().getName(), "UTF-8"); + } + if (verbose) { + System.out.println(String.format("Uploading to %s/%s", bucket, name)); + } + + // Load these bytes into Riak + riak.setAsBytes(bucket, name, bout.toByteArray()); + } + } + } catch (FileNotFoundException e) { + System.err.println("Error reading JAR file: " + e.getMessage()); + } catch (IOException e) { + System.err.println("Error reading JAR file: " + e.getMessage()); + } + } + } + + String[] classFiles = cl.getOptionValues('c'); + if (null != classFiles) { + for (String classFile : classFiles) { + try { + FileInputStream fin = new FileInputStream(classFile); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + byte[] buff = new byte[16384]; + for (int bytesRead = fin.read(buff); bytesRead > 0; bytesRead = fin.read(buff)) { + bout.write(buff, 0, bytesRead); + } + + String name; + if (cl.hasOption('k')) { + name = cl.getOptionValue('k'); + } else { + throw new IllegalStateException( + "Must specify a Riak key in which to store the data if loading individual class files."); + } + String bucket; + if (cl.hasOption('b')) { + bucket = cl.getOptionValue('b'); + } else { + throw new IllegalStateException( + "Must specify a Riak bucket in which to store the data if loading individual class files."); + } + if (verbose) { + System.out.println(String.format("Uploading to %s/%s", bucket, name)); + } + + // Load these bytes into Riak + riak.setAsBytes(bucket, name, bout.toByteArray()); + + } catch (FileNotFoundException e) { + System.err.println("Error reading class file: " + e.getMessage()); + } catch (IOException e) { + System.err.println("Error reading class file: " + e.getMessage()); + } + } + } + } + } + +} diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/util/RiakClassLoader.java b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/util/RiakClassLoader.java new file mode 100644 index 000000000..bc1f8a82f --- /dev/null +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/util/RiakClassLoader.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2011 by J. Brisbin + * Portions (c) 2011 by NPC International, Inc. or the + * original author(s). + * + * 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.keyvalue.riak.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.data.keyvalue.riak.core.RiakTemplate; +import org.springframework.http.HttpInputMessage; +import org.springframework.http.HttpOutputMessage; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.converter.HttpMessageNotWritableException; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * @author J. Brisbin + */ +public class RiakClassLoader extends ClassLoader { + + protected final Log log = LogFactory.getLog(getClass()); + protected Set buckets = new LinkedHashSet(); + protected RiakTemplate riakTemplate; + protected String defaultBucket = null; + + public RiakClassLoader(ClassLoader classLoader, RiakTemplate riakTemplate) { + super(classLoader); + init(riakTemplate); + loadBucketsFromClassPath(); + } + + public RiakClassLoader(RiakTemplate riakTemplate) { + init(riakTemplate); + loadBucketsFromClassPath(); + } + + public Set getBuckets() { + return buckets; + } + + public void setBuckets(Set buckets) { + this.buckets = buckets; + } + + public RiakTemplate getRiakTemplate() { + return riakTemplate; + } + + public void setRiakTemplate(RiakTemplate riakTemplate) { + this.riakTemplate = riakTemplate; + } + + public String getDefaultBucket() { + return defaultBucket; + } + + public void setDefaultBucket(String defaultBucket) { + this.defaultBucket = defaultBucket; + } + + @Override + protected Class findClass(String s) throws ClassNotFoundException { + Class c; + try { + c = super.findClass(s); + if (log.isDebugEnabled()) { + log.debug(String.format("Found class '%s' locally defined.", s)); + } + } catch (Throwable t) { + // Class not defined in this ClassLoader yet + } + + Set buckets = new LinkedHashSet(this.buckets); + if (null != defaultBucket) { + buckets.add(defaultBucket); + } + for (String bucket : buckets) { + if (bucket.indexOf("/") < 0) { + try { + if (log.isDebugEnabled()) { + log.debug(String.format("Class '%s' not locally defined, trying Riak.", s)); + } + byte[] buff = riakTemplate.getAsBytes(URLEncoder.encode(bucket, "UTF-8"), s); + c = defineClass(s, buff, 0, buff.length); + if (null != c) { + return c; + } + } catch (ClassFormatError ignored) { + } catch (UnsupportedEncodingException e) { + log.error(e.getMessage(), e); + } + } + } + + // Nothing found + throw new ClassNotFoundException("Class not found: " + s); + } + + + protected void loadBucketsFromClassPath() { + String classPath = System.getProperty("java.class.path"); + String pathSep = System.getProperty("path.separator", ":"); + if (null != classPath) { + String[] paths = classPath.split(pathSep); + for (String p : paths) { + buckets.add(p); + } + } + } + + protected void init(RiakTemplate riakTemplate) { + this.riakTemplate = riakTemplate; + RestTemplate tmpl = this.riakTemplate.getRestTemplate(); + tmpl.getMessageConverters().add(0, new JavaSerializationMessageHandler()); + tmpl.setErrorHandler(new Ignore404sErrorHandler()); + } + + private class JavaSerializationMessageHandler implements HttpMessageConverter { + + public boolean canRead(Class clazz, MediaType mediaType) { + return MediaType.APPLICATION_OCTET_STREAM.equals(mediaType); + } + + public boolean canWrite(Class clazz, MediaType mediaType) { + return null != clazz; + } + + public List getSupportedMediaTypes() { + List types = new ArrayList(1); + types.add(MediaType.APPLICATION_OCTET_STREAM); + return types; + } + + public Object read(java.lang.Class clazz, HttpInputMessage inputMessage) throws + IOException, + HttpMessageNotReadableException { + ObjectInputStream oin = new ObjectInputStream(inputMessage.getBody()); + try { + Class c = (Class) oin.readObject(); + if (log.isDebugEnabled()) { + log.debug("Loaded class: " + c); + } + return c; + } catch (ClassNotFoundException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage) throws + IOException, + HttpMessageNotWritableException { + outputMessage.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM); + ObjectOutputStream oout = new ObjectOutputStream(outputMessage.getBody()); + oout.writeObject(o); + oout.flush(); + } + + } + +} diff --git a/spring-data-riak/src/test/classes/org/springframework/data/keyvalue/riak/core/ClassLoaderTest.class b/spring-data-riak/src/test/classes/org/springframework/data/keyvalue/riak/core/ClassLoaderTest.class new file mode 100644 index 000000000..5a576518a Binary files /dev/null and b/spring-data-riak/src/test/classes/org/springframework/data/keyvalue/riak/core/ClassLoaderTest.class differ diff --git a/spring-data-riak/src/test/classes/org/springframework/data/keyvalue/riak/core/ClassLoaderTest.java b/spring-data-riak/src/test/classes/org/springframework/data/keyvalue/riak/core/ClassLoaderTest.java new file mode 100644 index 000000000..b8cd0896d --- /dev/null +++ b/spring-data-riak/src/test/classes/org/springframework/data/keyvalue/riak/core/ClassLoaderTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2011 by J. Brisbin + * Portions (c) 2011 by NPC International, Inc. or the + * original author(s). + * + * 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.keyvalue.riak.core; + +/** + * @author J. Brisbin + */ +public class ClassLoaderTest { + + String name = "ClassLoaderTest"; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakClassLoaderSpec.groovy b/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakClassLoaderSpec.groovy new file mode 100644 index 000000000..43a410793 --- /dev/null +++ b/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakClassLoaderSpec.groovy @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2011 by J. Brisbin + * Portions (c) 2011 by NPC International, Inc. or the + * original author(s). + * + * 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.keyvalue.riak.core + +import org.springframework.data.keyvalue.riak.util.RiakClassFileLoader +import org.springframework.data.keyvalue.riak.util.RiakClassLoader +import spock.lang.Shared +import spock.lang.Specification + +/** + * @author J. Brisbin + */ +class RiakClassLoaderSpec extends Specification { + + @Shared RiakTemplate riakTemplate = new RiakTemplate() + + def setupSpec() { + RiakQosParameters qos = new RiakQosParameters() + qos.durableWriteThreshold = "all" + riakTemplate.defaultQosParameters = qos + riakTemplate.ignoreNotFound = true + riakTemplate.afterPropertiesSet() + } + + def "Test load class file into Riak"() { + + when: + def args = [ + "-c", "src/test/classes/org/springframework/data/keyvalue/riak/core/ClassLoaderTest.class", + "-b", "test", + "-k", "org.springframework.data.keyvalue.riak.core.ClassLoaderTest" + ].toArray(new String[6]) + RiakClassFileLoader.main(args) + + then: + true + + } + + def "Test find class previously loaded into Riak"() { + + given: + RiakClassLoader classLoader = new RiakClassLoader(riakTemplate) + classLoader.defaultBucket = "test" + + when: + def clazz = Class.forName("org.springframework.data.keyvalue.riak.core.ClassLoaderTest", false, classLoader) + def inst = clazz?.newInstance() + + then: + null != clazz + null != inst + inst.name == "ClassLoaderTest" + + } + +} diff --git a/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakKeyValueTemplateSpec.groovy b/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakKeyValueTemplateSpec.groovy index ae0d8cb58..25fcb48d6 100644 --- a/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakKeyValueTemplateSpec.groovy +++ b/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakKeyValueTemplateSpec.groovy @@ -17,38 +17,49 @@ */ package org.springframework.data.keyvalue.riak.core -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.context.ApplicationContext import org.springframework.data.keyvalue.riak.mapreduce.JavascriptMapReduceOperation import org.springframework.data.keyvalue.riak.mapreduce.MapReduceJob import org.springframework.data.keyvalue.riak.mapreduce.RiakMapReducePhase -import org.springframework.test.context.ContextConfiguration +import org.springframework.data.keyvalue.riak.util.Ignore404sErrorHandler import spock.lang.Shared import spock.lang.Specification /** * @author J. Brisbin */ -@ContextConfiguration(locations = "/org/springframework/data/RiakKeyValueTemplateTests.xml") class RiakKeyValueTemplateSpec extends Specification { - @Autowired - ApplicationContext appCtx - @Autowired - RiakKeyValueTemplate riak + @Shared RiakKeyValueTemplate riak = new RiakKeyValueTemplate() int run = 1 @Shared def riakBin = System.properties["bamboo.RIAK_BIN"] ?: "/usr/sbin/riak" @Shared def p def setupSpec() { - p = "$riakBin start".execute() - p.waitFor() - Thread.sleep(2000) + RiakQosParameters qos = new RiakQosParameters() + qos.setDurableWriteThreshold("all") + riak.setDefaultQosParameters(qos) + riak.getRestTemplate().setErrorHandler(new Ignore404sErrorHandler()) + + if (!riak.get("status", "")) { + p = "$riakBin start".execute() + p.waitFor() + shutdown = true + Thread.sleep(2000) + } + + riak.getBucketSchema("test", true).keys.each { + riak.delete("test", it) + } + riak.getBucketSchema(TestObject.name, true).keys.each { + riak.delete("test", it) + } } def cleanupSpec() { - p = "$riakBin stop".execute() - p.waitFor() + if (shutdown) { + p = "$riakBin stop".execute() + p.waitFor() + } } def "Test Map object"() { diff --git a/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakTemplateSpec.groovy b/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakTemplateSpec.groovy index 10e275ed0..e6988c764 100644 --- a/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakTemplateSpec.groovy +++ b/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakTemplateSpec.groovy @@ -17,24 +17,19 @@ */ package org.springframework.data.keyvalue.riak.core -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.context.ApplicationContext import org.springframework.data.keyvalue.riak.core.io.RiakFile import org.springframework.data.keyvalue.riak.mapreduce.JavascriptMapReduceOperation import org.springframework.data.keyvalue.riak.mapreduce.MapReduceJob +import org.springframework.data.keyvalue.riak.mapreduce.RiakMapReduceJob import org.springframework.data.keyvalue.riak.mapreduce.RiakMapReducePhase -import org.springframework.test.context.ContextConfiguration import spock.lang.Shared import spock.lang.Specification /** * @author J. Brisbin */ -@ContextConfiguration(locations = "/org/springframework/data/RiakTemplateTests.xml") class RiakTemplateSpec extends Specification { - @Autowired - ApplicationContext appCtx @Shared RiakTemplate riak = new RiakTemplate() int run = 1 @Shared def riakBin = System.properties["bamboo.RIAK_BIN"] ?: "/usr/sbin/riak" @@ -46,6 +41,7 @@ class RiakTemplateSpec extends Specification { RiakQosParameters qos = new RiakQosParameters() qos.setDurableWriteThreshold("all") riak.setDefaultQosParameters(qos) + riak.ignoreNotFound = true if (!riak.get("status", "")) { p = "$riakBin start".execute() @@ -242,7 +238,7 @@ class RiakTemplateSpec extends Specification { def "Test Map/Reduce returning Integer"() { given: - MapReduceJob job = riak.createMapReduceJob() + MapReduceJob job = new RiakMapReduceJob(riak) def uuid = UUID.randomUUID().toString() def mapJs = new JavascriptMapReduceOperation("function(v){ var uuid='$uuid'; ejsLog('/tmp/mapred.log', 'map input: '+JSON.stringify(v)); var o=Riak.mapValuesJson(v); return [1]; }") def mapPhase = new RiakMapReducePhase("map", "javascript", mapJs) @@ -266,7 +262,7 @@ class RiakTemplateSpec extends Specification { def "Test Map/Reduce returning List"() { given: - MapReduceJob job = riak.createMapReduceJob() + MapReduceJob job = new RiakMapReduceJob(riak) def uuid = UUID.randomUUID().toString() def mapJs = new JavascriptMapReduceOperation("function(v){ var uuid='$uuid'; ejsLog('/tmp/mapred.log', 'map input: '+JSON.stringify(v)); var o=Riak.mapValuesJson(v); return [1]; }") def mapPhase = new RiakMapReducePhase("map", "javascript", mapJs) diff --git a/spring-data-riak/src/test/resources/org/springframework/data/AsyncRiakTemplateTests.xml b/spring-data-riak/src/test/resources/org/springframework/data/AsyncRiakTemplateTests.xml index 86e08abe5..7fab17ab0 100644 --- a/spring-data-riak/src/test/resources/org/springframework/data/AsyncRiakTemplateTests.xml +++ b/spring-data-riak/src/test/resources/org/springframework/data/AsyncRiakTemplateTests.xml @@ -27,8 +27,8 @@ - diff --git a/spring-data-riak/src/test/resources/org/springframework/data/RiakKeyValueTemplateTests.xml b/spring-data-riak/src/test/resources/org/springframework/data/RiakKeyValueTemplateTests.xml deleted file mode 100644 index 43aafe1d5..000000000 --- a/spring-data-riak/src/test/resources/org/springframework/data/RiakKeyValueTemplateTests.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - diff --git a/spring-data-riak/src/test/resources/org/springframework/data/RiakTemplateTests.xml b/spring-data-riak/src/test/resources/org/springframework/data/RiakTemplateTests.xml deleted file mode 100644 index e8ebcfb28..000000000 --- a/spring-data-riak/src/test/resources/org/springframework/data/RiakTemplateTests.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - diff --git a/spring-data-riak/template.mf b/spring-data-riak/template.mf index 0b4985d35..a7eae64c5 100644 --- a/spring-data-riak/template.mf +++ b/spring-data-riak/template.mf @@ -19,7 +19,7 @@ Import-Template: org.springframework.data.persistence.*;version="[1.0.0, 2.0.0)", org.springframework.data.document.*;version="[1.0.0, 2.0.0)", org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional, - org.slf4j.*;version="[1.5.10, 2.0.0)", + org.apache.commons.logging.*;version="[1.1.1, 2.0.0)", org.w3c.dom.*;version="0", org.codehaus.jackson.*;version="[1.5.6, 1.5.6)", org.codehaus.jackson.map.*;version="[1.5.6, 1.5.6)", @@ -27,4 +27,5 @@ Import-Template: groovy.lang.*;version="[1.7.5, 2.0.0)", groovy.util.*;version="[1.7.5, 2.0.0)", javax.activation.*;version="[1.1, 2.0)", - javax.mail.*;version="[1.4.0, 2.0.0)", \ No newline at end of file + javax.mail.*;version="[1.4.0, 2.0.0)", + org.apache.commons.cli.*;version="[1.2, 2.0)", \ No newline at end of file