Merge branch 'master' of github.com:SpringSource/spring-data-keyvalue

This commit is contained in:
Costin Leau
2011-01-10 14:21:59 +02:00
28 changed files with 698 additions and 188 deletions

View File

@@ -13,41 +13,8 @@
<name>Spring Data Riak Support</name>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<!-- Spring Data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-keyvalue-core</artifactId>
</dependency>
<!-- Jackson JSON -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
</dependency>
<!-- Logging -->
<!-- Logging
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
@@ -86,7 +53,54 @@
</exclusions>
<scope>provided</scope>
</dependency>
-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<!-- Groovy -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
</dependency>
<!-- Spring Data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-keyvalue-core</artifactId>
</dependency>
<!-- Jackson JSON -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
</dependency>
<!-- APIs -->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
@@ -101,18 +115,14 @@
<artifactId>activation</artifactId>
</dependency>
<!-- Commons -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
<!-- Groovy -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.2</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
@@ -121,6 +131,11 @@
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

View File

@@ -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 <jon@jbrisbin.com>
*/
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 <code>java.util.Date</code> 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<String, Object>(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<String, Object>(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) {

View File

@@ -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<Throwable, Object> defaultErrorHandler = new LoggingErrorHandler();
public AsyncRiakTemplate() {

View File

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

View File

@@ -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("(.+):(.+)");

View File

@@ -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<B, K> 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;

View File

@@ -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<B, K> extends UrlResource {
private static final Logger log = LoggerFactory.getLogger(RiakResource.class);
protected final Log log = LogFactory.getLog(getClass());
private RiakTemplate riak;
private B bucket;

View File

@@ -0,0 +1,15 @@
<html>
<body>
<p>
Utilities for working with resources stored in Riak as standard java.io objects. Opening a <a
href="RiakInputStream.html">RiakInputStream</a> to a resource will allow code that doesn't
know anything about Key/Value datastores to access resources stored within them.
</p>
<p>
Alternatively, writing data to a <a href="RiakOutputStream.html">RiakOutputStream</a> will
create a resources in Riak without exposing any of the underlying data access code to the
calling application.
</p>
</body>
</html>

View File

@@ -0,0 +1,7 @@
<html>
<body>
<p>
Root package for the core utilities that make up the Riak data access library.
</p>
</body>
</html>

View File

@@ -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)

View File

@@ -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;

View File

@@ -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<T> 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;

View File

@@ -0,0 +1,8 @@
<html>
<body>
<p>
Utilities for making Riak data access easier in Groovy. The <a href="RiakBuilder.html">RiakBuilder</a>
provides a Groovy DSL for interacting with Riak.
</p>
</body>
</html>

View File

@@ -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<Object> inputs = new LinkedList<Object>();
protected List<MapReducePhase> phases = new ArrayList<MapReducePhase>();

View File

@@ -0,0 +1,7 @@
<html>
<body>
<p>
Root package for
</p>
</body>
</html>

View File

@@ -0,0 +1,8 @@
<html>
<body>
<p>
Root package for integrating <a href="http://www.basho.com/Riak.html">Riak</a> with Spring
concepts.
</p>
</body>
</html>

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2011 by J. Brisbin <jon@jbrisbin.com>
* 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 <jon@jbrisbin.com>
*/
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);
}
}
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright (c) 2011 by J. Brisbin <jon@jbrisbin.com>
* 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 <jon@jbrisbin.com>
*/
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());
}
}
}
}
}
}

View File

@@ -0,0 +1,186 @@
/*
* Copyright (c) 2011 by J. Brisbin <jon@jbrisbin.com>
* 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 <jon@jbrisbin.com>
*/
public class RiakClassLoader extends ClassLoader {
protected final Log log = LogFactory.getLog(getClass());
protected Set<String> buckets = new LinkedHashSet<String>();
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<String> getBuckets() {
return buckets;
}
public void setBuckets(Set<String> 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<String> buckets = new LinkedHashSet<String>(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<MediaType> getSupportedMediaTypes() {
List<MediaType> types = new ArrayList<MediaType>(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();
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2011 by J. Brisbin <jon@jbrisbin.com>
* 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 <jon@jbrisbin.com>
*/
public class ClassLoaderTest {
String name = "ClassLoaderTest";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2011 by J. Brisbin <jon@jbrisbin.com>
* 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 <jon@jbrisbin.com>
*/
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"
}
}

View File

@@ -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 <jon@jbrisbin.com>
*/
@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"() {

View File

@@ -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 <jon@jbrisbin.com>
*/
@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)

View File

@@ -27,8 +27,8 @@
<bean id="qos" class="org.springframework.data.keyvalue.riak.core.RiakQosParameters"
p:durableWriteThreshold="all"
p:writeThreshold="all"/>
<bean id="riakTemplate"
class="org.springframework.data.keyvalue.riak.core.AsyncRiakTemplate"
<bean id="riakTemplate" class="org.springframework.data.keyvalue.riak.core.AsyncRiakTemplate"
p:ignoreNotFound="true"
p:defaultQosParameters-ref="qos"/>
</beans>

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:/META-INF/spring/app-context.xml"/>
<bean id="qos" class="org.springframework.data.keyvalue.riak.core.RiakQosParameters"
p:durableWriteThreshold="all"
p:writeThreshold="all"/>
<bean id="riakTemplate"
class="org.springframework.data.keyvalue.riak.core.RiakKeyValueTemplate"
p:defaultQosParameters-ref="qos"/>
</beans>

View File

@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
~ Portions (c) 2010 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.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:/META-INF/spring/app-context.xml"/>
<bean id="qos" class="org.springframework.data.keyvalue.riak.core.RiakQosParameters"
p:durableWriteThreshold="all"
p:writeThreshold="all"/>
<bean id="riakTemplate"
class="org.springframework.data.keyvalue.riak.core.RiakTemplate"
p:defaultQosParameters-ref="qos"/>
</beans>

View File

@@ -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)",
javax.mail.*;version="[1.4.0, 2.0.0)",
org.apache.commons.cli.*;version="[1.2, 2.0)",