com.basho.riak.client.api.commands.kv.FetchValue Maven / Gradle / Ivy
Show all versions of riak-client Show documentation
/*
* Copyright 2013 Basho Technologies Inc
*
* 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 com.basho.riak.client.api.commands.kv;
import com.basho.riak.client.api.GenericRiakCommand;
import com.basho.riak.client.api.cap.Quorum;
import com.basho.riak.client.api.cap.VClock;
import com.basho.riak.client.core.FutureOperation;
import com.basho.riak.client.core.RiakCluster;
import com.basho.riak.client.core.operations.FetchOperation;
import com.basho.riak.client.core.RiakFuture;
import com.basho.riak.client.api.commands.RiakOption;
import com.basho.riak.client.core.query.Location;
import java.util.HashMap;
import java.util.Map;
/**
* Command used to fetch a value from Riak.
*
*
* Fetching an object from Riak is a simple matter of supplying a {@link com.basho.riak.client.core.query.Location}
* and executing the FetchValue operation.
*
* {@code
* Namespace ns = new Namespace("my_type","my_bucket");
* Location loc = new Location(ns, "my_key");
* FetchValue fv = new FetchValue.Builder(loc).build();
* FetchValue.Response response = client.execute(fv);
* RiakObject obj = response.getValue(RiakObject.class);}
*
*
* All operations can called async as well.
*
* {@code
* ...
* RiakFuture future = client.executeAsync(fv);
* ...
* future.await();
* if (future.isSuccess())
* {
* ...
* }}
*
*
* ORM features are also provided when retrieving the results from the response.
* By default, JSON serialization / deserializtion is used. For example, if
* the value stored in Riak was JSON and mapped to your class {@code MyPojo}:
*
* {@code
* ...
* MyPojo mp = response.getValue(MyPojo.class);
* ...}
*
*
* @author Dave Rusek
* @since 2.0
* @see Response
*/
public final class FetchValue extends GenericRiakCommand.GenericRiakCommandWithSameInfo
{
private final Location location;
private final Map, Object> options = new HashMap<>();
FetchValue(Builder builder)
{
this.location = builder.location;
this.options.putAll(builder.options);
}
@Override
protected final RiakFuture executeAsync(RiakCluster cluster)
{
return super.executeAsync(cluster);
}
@Override
protected Response convertResponse(FutureOperation request,
FetchOperation.Response coreResponse)
{
return new Response.Builder().withNotFound(coreResponse.isNotFound())
.withUnchanged(coreResponse.isUnchanged())
.withValues(coreResponse.getObjectList())
.withLocation(location) // for ORM
.build();
}
@Override
protected FetchOperation buildCoreOperation()
{
FetchOperation.Builder builder = new FetchOperation.Builder(location);
for (Map.Entry, Object> opPair : options.entrySet())
{
RiakOption> option = opPair.getKey();
if (option == Option.R)
{
builder.withR(((Quorum) opPair.getValue()).getIntValue());
} else if (option == Option.DELETED_VCLOCK)
{
builder.withReturnDeletedVClock((Boolean) opPair.getValue());
} else if (option == Option.TIMEOUT)
{
builder.withTimeout((Integer) opPair.getValue());
} else if (option == Option.HEAD)
{
builder.withHeadOnly((Boolean) opPair.getValue());
} else if (option == Option.BASIC_QUORUM)
{
builder.withBasicQuorum((Boolean) opPair.getValue());
} else if (option == Option.IF_MODIFIED)
{
VClock clock = (VClock) opPair.getValue();
builder.withIfNotModified(clock.getBytes());
} else if (option == Option.N_VAL)
{
builder.withNVal((Integer) opPair.getValue());
} else if (option == Option.PR)
{
builder.withPr(((Quorum) opPair.getValue()).getIntValue());
} else if (option == Option.SLOPPY_QUORUM)
{
builder.withSloppyQuorum((Boolean) opPair.getValue());
} else if (option == Option.NOTFOUND_OK)
{
builder.withNotFoundOK((Boolean) opPair.getValue());
}
}
return builder.build();
}
/**
* A response from Riak containing results from a FetchValue command.
*
* The Response, unless marked not found or unchanged, will contain one or
* more objects returned from Riak (all siblings are returned if present).
*
*/
public static class Response extends KvResponseBase
{
private final boolean notFound;
private final boolean unchanged;
Response(Init> builder)
{
super(builder);
this.notFound = builder.notFound;
this.unchanged = builder.unchanged;
}
/**
* Determine if there was a value in Riak.
*
* If there was no value present at the supplied {@code Location} in
* Riak, this will be true.
*
*
* @return true if there was no value in Riak.
*/
public boolean isNotFound()
{
return notFound;
}
/**
* Determine if the value is unchanged.
*
* If the fetch request set {@link com.basho.riak.client.api.commands.kv.FetchValue.Option#IF_MODIFIED}
* this indicates if the value in Riak has been modified.
*
*
* @return true if the vector clock for the object in Riak matched the
* supplied vector clock, false otherwise.
*/
public boolean isUnchanged()
{
return unchanged;
}
protected static abstract class Init> extends KvResponseBase.Init
{
private boolean notFound;
private boolean unchanged;
T withUnchanged(boolean unchanged)
{
this.unchanged = unchanged;
return self();
}
T withNotFound(boolean notFound)
{
this.notFound = notFound;
return self();
}
}
static class Builder extends Init
{
@Override
protected Builder self()
{
return this;
}
@Override
Response build()
{
return new Response(this);
}
}
}
/**
* Options for controlling how Riak performs the fetch operation.
*
* These options can be supplied to the {@link FetchValue.Builder} to change
* how Riak performs the operation. These override the defaults provided
* by the bucket.
*
*
* @author Dave Rusek
* @since 2.0
* @see Replication Properties
*/
public static final class Option extends RiakOption
{
/**
* Read Quorum.
* How many replicas need to agree when fetching the object.
*/
public static final Option R = new Option<>("R");
/**
* Primary Read Quorum.
* How many primary replicas need to be available when retrieving the object.
*/
public static final Option PR = new Option<>("PR");
/**
* Basic Quorum.
* Whether to return early in some failure cases (eg. when r=1 and you get
* 2 errors and a success basic_quorum=true would return an error)
*/
public static final Option BASIC_QUORUM = new Option<>("BASIC_QUORUM");
/**
* Not Found OK.
* Whether to treat notfounds as successful reads for the purposes of R
*/
public static final Option NOTFOUND_OK = new Option<>("NOTFOUND_OK");
/**
* If Modified.
* When a vector clock is supplied with this option, only return the object
* if the vector clocks don't match.
*/
public static final Option IF_MODIFIED = new Option<>("IF_MODIFIED");
/**
* Head.
* return the object with the value(s) set as empty. This allows you to get the
* meta data without a potentially large value. Analogous to an HTTP HEAD request.
*/
public static final Option HEAD = new Option<>("HEAD");
/**
* Deleted VClock.
* By default single tombstones are not returned by a fetch operations. This
* will return a Tombstone if it is present.
*/
public static final Option DELETED_VCLOCK = new Option<>("DELETED_VCLOCK");
/**
* Timeout.
* Sets the server-side timeout for this operation. The default in Riak is 60 seconds.
*/
public static final Option TIMEOUT = new Option<>("TIMEOUT");
public static final Option SLOPPY_QUORUM = new Option<>("SLOPPY_QUORUM");
public static final Option N_VAL = new Option<>("N_VAL");
private Option(String name)
{
super(name);
}
}
/**
* Used to construct a FetchValue command.
*/
public static class Builder extends KvBuilderBase
{
/**
* Constructs a builder for a FetchValue operation using the supplied location.
* @param location the location of the object you want to fetch from Riak.
*/
public Builder(Location location)
{
super(location);
}
/**
* Add an optional setting for this command.
* This will be passed along with the request to Riak to tell it how
* to behave when servicing the request.
*
* @param option the option
* @param value the value for the option
* @return a reference to this object.
*/
public Builder withOption(Option option, U value)
{
addOption(option, value);
return this;
}
/**
* Set the Riak-side timeout value.
*
* By default, riak has a 60s timeout for operations. Setting
* this value will override that default for this operation.
*
* @param timeout the timeout in milliseconds to be sent to riak.
* @return a reference to this object.
*/
public Builder withTimeout(int timeout)
{
withOption(Option.TIMEOUT, timeout);
return this;
}
/**
* Build a {@link FetchValue} object
*
* @return a FetchValue command
*/
@Override
public FetchValue build()
{
return new FetchValue(this);
}
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + (location != null ? location.hashCode() : 0);;
result = prime * result + options.hashCode();
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof FetchValue))
{
return false;
}
final FetchValue other = (FetchValue) obj;
if (this.location != other.location && (this.location == null || !this.location.equals(other.location)))
{
return false;
}
if (this.options != other.options && (this.options == null || !this.options.equals(other.options)))
{
return false;
}
return true;
}
@Override
public String toString()
{
return String.format("{location: %s, options: %s}", location, options);
}
}