
com.basho.riak.client.bucket.DefaultBucket Maven / Gradle / Ivy
Show all versions of riak-client Show documentation
/*
* This file is provided to you 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 com.basho.riak.client.bucket;
import static com.basho.riak.client.convert.KeyUtil.getKey;
import java.io.IOException;
import java.util.Collection;
import com.basho.riak.client.DefaultRiakClient;
import com.basho.riak.client.DefaultRiakObject;
import com.basho.riak.client.IRiakObject;
import com.basho.riak.client.RiakException;
import com.basho.riak.client.builders.RiakObjectBuilder;
import com.basho.riak.client.cap.ClobberMutation;
import com.basho.riak.client.cap.DefaultResolver;
import com.basho.riak.client.cap.Mutation;
import com.basho.riak.client.cap.Quorum;
import com.basho.riak.client.cap.Retrier;
import com.basho.riak.client.cap.UnresolvedConflictException;
import com.basho.riak.client.convert.Converter;
import com.basho.riak.client.convert.JSONConverter;
import com.basho.riak.client.convert.NoKeySpecifedException;
import com.basho.riak.client.convert.PassThroughConverter;
import com.basho.riak.client.convert.RiakKey;
import com.basho.riak.client.http.util.Constants;
import com.basho.riak.client.operations.DeleteObject;
import com.basho.riak.client.operations.FetchObject;
import com.basho.riak.client.operations.MultiFetchObject;
import com.basho.riak.client.operations.RiakOperation;
import com.basho.riak.client.operations.StoreObject;
import com.basho.riak.client.query.functions.NamedErlangFunction;
import com.basho.riak.client.query.functions.NamedFunction;
import com.basho.riak.client.query.indexes.FetchIndex;
import com.basho.riak.client.query.indexes.RiakIndex;
import com.basho.riak.client.raw.RawClient;
import com.basho.riak.client.util.CharsetUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Default implementation of {@link Bucket} for creating {@link RiakOperation}s
* on k/v data and accessing {@link BucketProperties}.
*
*
* Obtain a {@link DefaultBucket} from {@link FetchBucket} or
* {@link WriteBucket} operations from
* {@link DefaultRiakClient#fetchBucket(String)},
* {@link DefaultRiakClient#createBucket(String)}
*
*
*
* final String bucketName = UUID.randomUUID().toString();
*
* Bucket b = client.createBucket(bucketName).execute();
* //store something
* IRiakObject o = b.store("k", "v").execute();
* //fetch it back
* IRiakObject fetched = b.fetch("k").execute();
* // now update that riak object
* b.store("k", "my new value").execute();
* //fetch it back again
* fetched = b.fetch("k").execute();
* //delete it
* b.delete("k").execute();
*
*
* All operations created by instances of this class are configured with the
* {@link Retrier} and {@link RawClient} passed at construction.
*
*
* @author russell
* @see DomainBucket
* @see RiakBucket
*/
public class DefaultBucket implements Bucket {
private final String name;
private final BucketProperties properties;
private final RawClient client;
private final Retrier retrier;
/**
* All {@link RiakOperation}s created by this instance will use the
* {@link RawClient} and {@link Retrier} provided here.
*
* @param name this bucket's name
* @param properties the {@link BucketProperties} for this bucket
* @param client a {@link RawClient} to use for all {@link RiakOperation}s
* @param retrier a {@link Retrier} to use for all {@link RiakOperation}s
*/
protected DefaultBucket(String name, final BucketProperties properties, final RawClient client, final Retrier retrier) {
this.name = name;
this.properties = properties;
this.client = client;
this.retrier = retrier;
}
// BUCKET PROPS
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.Bucket#getName()
*/
public String getName() {
return name;
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.BucketProperties#getAllowSiblings()
*/
public Boolean getAllowSiblings() {
return properties.getAllowSiblings();
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.BucketProperties#getLastWriteWins()
*/
public Boolean getLastWriteWins() {
return properties.getLastWriteWins();
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.BucketProperties#getNVal()
*/
public Integer getNVal() {
return properties.getNVal();
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.BucketProperties#getBackend()
*/
public String getBackend() {
return properties.getBackend();
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.BucketProperties#getSmallVClock()
*/
public Integer getSmallVClock() {
return properties.getSmallVClock();
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.BucketProperties#getBigVClock()
*/
public Integer getBigVClock() {
return properties.getBigVClock();
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.BucketProperties#getYoungVClock()
*/
public Long getYoungVClock() {
return properties.getYoungVClock();
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.BucketProperties#getOldVClock()
*/
public Long getOldVClock() {
return properties.getOldVClock();
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.BucketProperties#getPrecommitHooks()
*/
public Collection getPrecommitHooks() {
return properties.getPrecommitHooks();
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.BucketProperties#getPostcommitHooks()
*/
public Collection getPostcommitHooks() {
return properties.getPostcommitHooks();
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.BucketProperties#getR()
*/
public Quorum getR() {
return properties.getR();
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.BucketProperties#getW()
*/
public Quorum getW() {
return properties.getW();
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.BucketProperties#getRW()
*/
public Quorum getRW() {
return properties.getRW();
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.BucketProperties#getDW()
*/
public Quorum getDW() {
return properties.getDW();
}
/* (non-Javadoc)
* @see com.basho.riak.client.bucket.BucketProperties#getPR()
*/
public Quorum getPR() {
return properties.getPR();
}
/* (non-Javadoc)
* @see com.basho.riak.client.bucket.BucketProperties#getPW()
*/
public Quorum getPW() {
return properties.getPW();
}
/* (non-Javadoc)
* @see com.basho.riak.client.bucket.BucketProperties#isBasicQuorum()
*/
public Boolean getBasicQuorum() {
return properties.getBasicQuorum();
}
/* (non-Javadoc)
* @see com.basho.riak.client.bucket.BucketProperties#isNotFoundOK()
*/
public Boolean getNotFoundOK() {
return properties.getNotFoundOK();
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.BucketProperties#getChashKeyFunction()
*/
public NamedErlangFunction getChashKeyFunction() {
return properties.getChashKeyFunction();
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.BucketProperties#getLinkWalkFunction()
*/
public NamedErlangFunction getLinkWalkFunction() {
return properties.getLinkWalkFunction();
}
/* (non-Javadoc)
* @see com.basho.riak.client.bucket.BucketProperties#getSearch()
*/
public Boolean getSearch() {
return properties.getSearch();
}
/* (non-Javadoc)
* @see com.basho.riak.client.bucket.BucketProperties#isSearchEnabled()
*/
public boolean isSearchEnabled() {
return properties.isSearchEnabled();
}
// BUCKET
/**
* Iterate over the keys for this bucket (Expensive, are you sure?) Beware:
* at present all {@link RawClient#listKeys(String)} operations return a
* stream of keys. The stream is closed automatically when the iteratore is
* weakly reachable. Do not retain a reference to this {@link Iterable}
* after you have used it.
*
* @see RawClient#listKeys(String)
*/
public Iterable keys() throws RiakException {
try {
return client.listKeys(name);
} catch (IOException e) {
throw new RiakException(e);
}
}
/**
* Convenience method to create a RiakObject with a payload of
* application/octect-stream
*
* For example, to get a new {@link IRiakObject} into Riak.
* IRiakObject myNewObject = bucket.store("k", myByteArray)
* .w(2) // tunable CAP write quorum
* .returnBody(true) // return the IRiakObject from the store
* .execute(); // perform the operation.
*
*
*
* Creates a {@link StoreObject} operation configured with a
* {@link Mutation} that copies value
and
* {@link DefaultRiakObject#DEFAULT_CONTENT_TYPE} over the any existing
* value at key
or creates a new {@link DefaultRiakObject} with
* value
and {@link DefaultRiakObject#DEFAULT_CONTENT_TYPE}.
*
*
* The {@link StoreObject} is configured with the {@link DefaultResolver}
* which means the presence of siblings triggers a
* {@link UnresolvedConflictException}
*
*
* The {@link StoreObject} is configured with a {@link Converter} that
* simply returns what it is given (IE does no conversion).
*
*
* @param key
* the key to store the object under.
* @param value
* a byte[] of the objects value.
* @return a {@link StoreObject} configured to store value
at
* key
on execute()
.
* @see StoreObject
*/
public StoreObject store(final String key, final byte[] value) {
return new StoreObject(client, name, key, retrier).withMutator(new Mutation() {
public IRiakObject apply(IRiakObject original) {
if (original == null) {
return RiakObjectBuilder.newBuilder(name, key).withValue(value).withContentType(Constants.CTYPE_OCTET_STREAM).build();
} else {
original.setValue(value);
return original;
}
}
}).withResolver(new DefaultResolver()).withConverter(new PassThroughConverter());
}
/**
* Convenience methods will create an {@link IRiakObject} with
* value
as the data payload and
* text/plain:charset=utf-8
as the contentType
*
* For example, to get a new {@link IRiakObject} into Riak.
* IRiakObject myNewObject = bucket.store("k", "myValue")
* .w(2) // tunable CAP write quorum
* .returnBody(true) // return the IRiakObject from the store
* .execute(); // perform the operation.
*
*
*
* Creates a {@link StoreObject} operation configured with a
* {@link Mutation} that copies value
and
* {@link Constants#CTYPE_TEXT_UTF8} over the any existing
* value at key
or creates a new {@link DefaultRiakObject} with
* value
and {@link Constants#CTYPE_TEXT_UTF8}.
*
*
* The {@link StoreObject} is configured with the {@link DefaultResolver}
* which means the presence of siblings triggers a
* {@link UnresolvedConflictException}
*
*
* The {@link StoreObject} is configured with a {@link Converter} that
* simply returns what it is given (IE does no conversion).
*
*
* @param key
* the key to store the object under.
* @param value
* a String of the data to store
* @return a {@link StoreObject} configured to store value
at
* key
on execute()
.
* @see StoreObject
*/
public StoreObject store(final String key, final String value) {
final Mutation m = new Mutation() {
public IRiakObject apply(IRiakObject original) {
if (original == null) {
return RiakObjectBuilder.newBuilder(name, key).withValue(value).withContentType(Constants.CTYPE_TEXT_UTF8).build();
} else {
original.setValue(CharsetUtils.utf8StringToBytes(value));
original.setContentType(Constants.CTYPE_TEXT_UTF8);
return original;
}
}
};
return store(key, CharsetUtils.utf8StringToBytes(value)).withMutator(m);
}
/**
* Store an instance of T
in Riak. Depends on the
* {@link Converter} provided to {@link StoreObject} to convert
* o
from T
to {@link IRiakObject}.
*
* T
must have a field annotated with {@link RiakKey} as the
* Key to store this data under.
*
*
*
* Creates a {@link StoreObject} operation configured with the
* {@link JSONConverter} the {@link ClobberMutation} and
* {@link DefaultResolver}.
*
*
* @param
* the Type of o
* @param o
* the data to store
* @return a {@link StoreObject} configured to store o
at the
* {@link RiakKey} annotated key
on
* execute()
.
* @see StoreObject
* @see DomainBucket
*/
public StoreObject store(final T o) {
@SuppressWarnings("unchecked") Class clazz = (Class) o.getClass();
final String key = getKey(o);
if (key == null) {
throw new NoKeySpecifedException(o);
}
Converter converter = getDefaultConverter(clazz);
return new StoreObject(client, name, key, retrier)
.withConverter(converter)
.withMutator(new ClobberMutation(o))
.withResolver(new DefaultResolver());
}
private Converter getDefaultConverter(Class clazz) {
return getDefaultConverter(clazz, null);
}
@SuppressWarnings("unchecked") private Converter getDefaultConverter(Class clazz, String key) {
Converter converter;
if (IRiakObject.class.isAssignableFrom(clazz)) {
converter = (Converter) new PassThroughConverter();
} else {
if (key != null) {
converter = new JSONConverter(clazz, name, key);
} else {
converter = new JSONConverter(clazz, name);
}
}
return converter;
}
/**
* Store an instance of T
in Riak. Depends on the
* {@link Converter} provided to {@link StoreObject} to convert
* o
from T
to {@link IRiakObject}.
*
*
* Creates a {@link StoreObject} operation configured with the
* {@link JSONConverter} the {@link ClobberMutation} and
* {@link DefaultResolver}.
*
*
* @param
* the Type of o
* @param o
* the data to store
* @return a {@link StoreObject} configured to store o
at the
* {@link RiakKey} annotated key
on
* execute()
.
* @see StoreObject
* @see DomainBucket
*/
public StoreObject store(final String key, final T o) {
@SuppressWarnings("unchecked") final Class clazz = (Class) o.getClass();
Converter converter = getDefaultConverter(clazz, key);
return new StoreObject(client, name, key, retrier).
withConverter(converter)
.withMutator(new ClobberMutation(o)).withResolver(new DefaultResolver());
}
/**
* Creates a {@link FetchObject} operation that returns the data at
* o
's annotated {@link RiakKey} field as an instance of type
* T
on execute()
.
*
* Creates a {@link FetchObject} operation configured with the
* {@link JSONConverter} and
* {@link DefaultResolver}.
*
*
* @param
* the Type to return
* @param o
* an instance ot T
that has the key annotated with
* {@link RiakKey}
* @return a {@link FetchObject}
* @see FetchObject
*/
public FetchObject fetch(T o) {
@SuppressWarnings("unchecked") final Class clazz = (Class) o.getClass();
final String key = getKey(o);
if (key == null) {
throw new NoKeySpecifedException(o);
}
Converter converter = getDefaultConverter(clazz);
return new FetchObject(client, name, key, retrier)
.withConverter(converter)
.withResolver(new DefaultResolver());
}
/**
* Creates a {@link FetchObject} operation that returns the data at
* key
as an instance of type T
on
* execute()
.
*
*
* Creates a {@link FetchObject} operation configured with the
* {@link JSONConverter} and
* {@link DefaultResolver}.
*
*
* @param
* the Type to return
* @param key
* the key under which the data is stored
* @param type
* the Class of the type to return
* @return a {@link FetchObject}
* @see FetchObject
*/
public FetchObject fetch(final String key, final Class type) {
Converter converter = getDefaultConverter(type, key);
return new FetchObject(client, name, key, retrier)
.withConverter(converter)
.withResolver(new DefaultResolver());
}
/**
* Creates a {@link FetchObject} that returns the data at key
* as an {@link IRiakObject} on execute()
.
*
*
* Creates a {@link FetchObject} with the {@link DefaultResolver} and a {@link Converter}
* that does nothing to the {@link IRiakObject}.
*
*
* @param key the key
* @return a {@link FetchObject}
* @see FetchObject
*/
public FetchObject fetch(String key) {
return new FetchObject(client, name, key, retrier)
.withResolver(new DefaultResolver())
.withConverter(new PassThroughConverter());
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.Bucket#delete(java.lang.Object)
*/
public DeleteObject delete(T o) {
final String key = getKey(o);
if (key == null) {
throw new NoKeySpecifedException(o);
}
return new DeleteObject(client, name, key, retrier);
}
/*
* (non-Javadoc)
*
* @see com.basho.riak.newapi.bucket.Bucket#delete(java.lang.String)
*/
public DeleteObject delete(String key) {
return new DeleteObject(client, name, key, retrier);
}
/* (non-Javadoc)
* @see com.basho.riak.client.bucket.Bucket#fetchIndex(com.basho.riak.client.query.indexes.RiakIndex)
*/
public FetchIndex fetchIndex(RiakIndex index) {
return new FetchIndex(client, name, index, retrier);
}
/**
* (non-Javadoc)
* @see com.basho.riak.client.bucket.Bucket#multiFetch(java.lang.String[])
*/
public MultiFetchObject multiFetch(String[] keys)
{
return new MultiFetchObject(client, name, Arrays.asList(keys), retrier)
.withResolver(new DefaultResolver())
.withConverter(new PassThroughConverter());
}
/**
* (non-Javadoc)
* @see com.basho.riak.client.bucket.Bucket#multiFetch(java.util.List, java.lang.Class)
*/
public MultiFetchObject multiFetch(List keys, Class type)
{
Converter converter = getDefaultConverter(type, keys.get(0));
return new MultiFetchObject(client, name, keys, retrier)
.withConverter(converter)
.withResolver(new DefaultResolver());
}
/**
* (non-Javadoc)
* @see com.basho.riak.client.bucket.Bucket#multiFetch(java.util.List)
*/
public MultiFetchObject multiFetch(List objs)
{
T o = objs.get(0);
@SuppressWarnings("unchecked") final Class clazz = (Class) o.getClass();
List keyList = new ArrayList(objs.size());
for (T obj : objs)
{
String key = getKey(obj);
if (key == null) {
throw new NoKeySpecifedException(o);
}
keyList.add(key);
}
Converter converter = getDefaultConverter(clazz);
return new MultiFetchObject(client, name, keyList, retrier)
.withConverter(converter)
.withResolver(new DefaultResolver());
}
}