diff --git a/.gitignore b/.gitignore
index 1afaa0470..d5a59700c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,7 +3,11 @@ lib
classes
target
.settings
-*.xml
*.jar
.classpath
.project
+.idea
+*.iml
+*~
+.DS_Store
+nb-configuration.xml
diff --git a/.hgignore b/.hgignore
index 2a2888c74..7cd708362 100644
--- a/.hgignore
+++ b/.hgignore
@@ -4,4 +4,6 @@ lib/*
.project
.classpath
.settings
+.idea
+*.iml
target
diff --git a/.hgtags b/.hgtags
deleted file mode 100644
index b0945f803..000000000
--- a/.hgtags
+++ /dev/null
@@ -1,31 +0,0 @@
-4f5ddc6ec3c94b992bc52b452a1866d6e6bfabaa riak-java-client-1.0
-fd6be5ae145101f1dcca618899b2b7c312628e0a riak-client-0.9.0
-4f5ddc6ec3c94b992bc52b452a1866d6e6bfabaa riak-java-client-1.0
-0000000000000000000000000000000000000000 riak-java-client-1.0
-5e4c2dd39010438557a0c1c1273d2d9a20967c27 riak-client-0.9.1
-c7922ca30dbb89efbb824b68ccf292398016cfd2 riak-0.10
-3c07251b13bcd1d5198c70aad5e595f92457f352 riak-0.11.0
-1d48c9ab06da07545754347a9ebd3035a04ab1e5 riak-client-0.11.0
-3c07251b13bcd1d5198c70aad5e595f92457f352 riak-0.11.0
-0000000000000000000000000000000000000000 riak-0.11.0
-0000000000000000000000000000000000000000 riak-0.11.0
-e479cfffeff0d9d10857459816eaca132fb3328c riak-0.11.0
-89dbaa1ee6ea849034b5a350ce9ad90dc556db77 0.11
-89dbaa1ee6ea849034b5a350ce9ad90dc556db77 0.11
-0000000000000000000000000000000000000000 0.11
-e479cfffeff0d9d10857459816eaca132fb3328c riak-0.11.0
-0000000000000000000000000000000000000000 riak-0.11.0
-1d48c9ab06da07545754347a9ebd3035a04ab1e5 riak-client-0.11.0
-0000000000000000000000000000000000000000 riak-client-0.11.0
-0000000000000000000000000000000000000000 riak-client-0.11.0
-0000000000000000000000000000000000000000 riak-client-0.11.0
-0000000000000000000000000000000000000000 riak-client-0.11.0
-c4c548212af1e9c2b1221a38bdfbd2949719a486 riak-client-0.11.0
-0000000000000000000000000000000000000000 riak-0.11.0
-355cf1ddaf2fabe5829d99f63ecd0985f2cb2870 riak-0.11.0
-355cf1ddaf2fabe5829d99f63ecd0985f2cb2870 riak-0.11.0
-0000000000000000000000000000000000000000 riak-0.11.0
-c4c548212af1e9c2b1221a38bdfbd2949719a486 riak-client-0.11.0
-0000000000000000000000000000000000000000 riak-client-0.11.0
-0000000000000000000000000000000000000000 riak-client-0.11.0
-fb9aa46baf9891d3f221a25215043798050d6020 riak-client-0.11.0
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 000000000..79768ebc0
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,10 @@
+language: java
+script: mvn -Pitest -DargLine="-Dcom.basho.riak.search=true -Dcom.basho.riak.2i=true" clean verify
+notifications:
+ # Email notifications are disabled to not annoy anybody.
+ # See http://about.travis-ci.org/docs/user/build-configuration/ to learn more
+ # about configuring notification recipients and more.
+ webhooks: http://basho-engbot.herokuapp.com/travis?key=a8f477c8cdaabc8118d2a650281ff4a71eff875f
+ email: false
+services:
+ - riak
diff --git a/CHANGELOG b/CHANGELOG
deleted file mode 100644
index 56d83391d..000000000
--- a/CHANGELOG
+++ /dev/null
@@ -1,75 +0,0 @@
-0.14.1
--------------
-
-This version fixes a few bugs with the protocol buffers client (including one
-major crasher with setClientId). It fixes a bug setting max connections per
-host which limited the http client to 2 concurrent connections. It fixes a
-few encapsulation/thread safety issues. Some API elements have been deprecated
-(direct access to RiakObject(s) links and user meta collections, mutation of
-RiakLink) in preparation for removal in the next release. Unit and integration
-test coverage has been increased to ~70% in preparation for a refactor. Java 5
-is now supported.
-
-**Changes**:
-
-d8824e5 Add test to show byte[] value safely encapsulated in RiakObject
-0e7d255 Add tests to show RiakObjects internal collections are thread safe
-568cab8 Remove calls to deprecated methods from tests
-c1e23eb Remove references to sun Base64Encoder/Decoder
-79b3f5a Fix Javadoc typo and rename usermeta to userMetaData
-35e49c5 Return defensive copy of internal value byte array
-ad05279 Fix direct store of byte array
-46bb956 Fix sporadically failing vclock test
-86b1927 Fix encapsulation/safety issues from direct store of array arguments
-825e734 Fix concurrent mutation issue for RiakObject's links
-fc3172f Fix typo in README (link to DEVELOPERS.md)
-a11206f Merge branch 'bz1040_coverage' - bz 1040
-d88abac Add unit tests for pbc MapReduceBuilder - bz 1040
-eb3cea9 Remove redundant imports
-3eda593 Add integration and unit tests for the protobufs client - bz 1040
-bb82e66 Add test for pbc RiakLink - bz 1040
-96ac2aa Improve test coverage prior to refactor - bz 1040
-cd0104e fix infinite loop in PBC setClientId - bz 1038
-3897895 Fix always zero w and dw for PBC put request with RequestMeta
-6aaecd8 RiakObject.writeLink(): Fixed bug (using setRequestHeader()
- vs. addRequestHeader())
-d54dd19 RiakObject.writeToHttpMethod(): Factored link part out; circumventing
- problem with long http header lines (MochiWeb apparently has an 8K limit)
-1f452b8 Add private constructor to JSONEquals helper class
-6215c9f Change internal representation of M/R inputs to LinkedHashMap
-ebdb63d Fix tests that depend on order of keys in Map
-ff9748c Remove Java 6 only code and annotations
-af74421 Add max connections per host to match max connections
-2d72f31 updating maven dependency info on README
-
-
-0.14.0
--------------
-
-This version adds support for the riak protocol buffers interface.
-
-0.11
--------------
-
-This version no longer performs binary to String conversions automatically. All
-bucket data is surfaced as byte arrays. Clients will need to either perform
-their own string conversions or use the getBodyAsString() method added to the
-com.basho.riak.client.response.HttpResponse interface.
-
-0.10-SNAPSHOT
--------------
-
-This version breaks compatibility with Riak releases prior to the pre-0.10
-tag. Specifically, it no longer parses link walking results in prior releases.
-
-**Changes**:
-
-* Expect CRLF in multipart (link walking) results
- (http://bitbucket.org/basho/riak/changeset/df553e35cc92/)
-* RiakClient.getClientId() returns the raw client ID byte[4] rather than String
- to avoid charset encoding issues
-
-0.9.1
--------------
-
-First official release of riak-client.
\ No newline at end of file
diff --git a/DEVELOPERS.md b/DEVELOPERS.md
deleted file mode 100644
index 50e8802ed..000000000
--- a/DEVELOPERS.md
+++ /dev/null
@@ -1,130 +0,0 @@
-
-
-This document contains a technical overview of this project for development. For instructions on using this client, see the [README](/jonjlee/riak-java-client/wiki/Home).
-
-# Javadocs #
-
-The Javadocs contain detailed information about each class. They can be built using:
-
- mvn javadoc:javadoc
-
-Then open `target/site/apidocs/index.html`.
-
-# Overview #
-
-The most important classes are:
-
-* **ClientHelper**: executes HTTP methods
-
-* **RiakObject**: Java representations of a Riak object capable of serializing to an HTTP request
-
-* **--Response** classes: parse HTTP responses from Riak
-
-* **RiakClient**: calls ClientHelper and wraps responses in `--Response` classes
-
-# Classes #
-
-## Example ##
-
-A typical fetch for an object from the Riak HTTP interface is handled as follows:
-
-1. User calls `RiakClient.fetch(...)`
-
-2. Calls `ClientHelper.fetch(...)` which builds the `HttpMethod`
-
-3. Calls `ClientHelper.executeMethod(...)` which executes the method and fetches the response with `getResponseBodyAsString()`
-
-4. `RiakClient.fetch(...)` constructs a `FetchResponse` from the response
-
-5. `FetchResponse` parses the response and constructs a `RiakObject`
-
-The other operations follow the same basic flow: Client -> ClientHelper -> Response.
-
-## Executing HTTP Methods ##
-
-The heart of the client is `ClientHelper`. It builds and executes the HTTP methods corresponding to each operation. The `executeMethod()` method performs the HTTP operation using Commons HttpClient and retrieves the response.
-
-## Serialization/Parsing ##
-
-While the HTTP operations are performed by ClientHelper, the majority of the work actually consists of serializing objects and parsing the HTTP responses from Riak. Serialization of objects to send to Riak is performed by `RiakObject.writeToHttpMethod(...)`. Responses are parsed by the `--Response` classes which read the `HttpResponse` returned by `ClientHelper` and construct the appropriate domain objects.
-
-## Client Interface ##
-
-Users primarily use `RiakClient`/`RiakObject`. `RiakClient` is a simple adapter from `ClientHelper` to the `RiakClient` interface. It uses `ClientHelper` to execute the HTTP methods and wraps the resulting response with the proper `--Response`.
-
-# Development #
-
-## Unit Tests ##
-
-The unit tests exercise the code. The coverage for the unit tests can be obtained by running
-
- mvn cobertura:cobertura
-
-Then browse to
-
- riak-java-client/target/site/cobertura/index.html
-
-to see the coverage report. Coverage is currently ~66%. It should only go *up* from here. If you add code, add a test. If you fix a bug, add a test.
-
-## Integration Tests ##
-
-The integration tests perform each of the basic operations (store bucket schema, list bucket, store, fetch, modify, walk, map/reduce) against a running Riak instance. They can be run using:
-
- mvn -Pitest clean verify
-
-Riak must be running on `127.0.0.1:8098` with the HTTP interface located at `/riak`. Note that prior to Riak 0.9, the HTTP interface was located at `/raw` by default. The integration tests expect to find the protobuffers interface at '127.0.0.1:8087'
-
-## Code Format ##
-
-The files `eclipse-cleanup-profile.xml` and `eclipse-format-profile.xml` are provided to be used with Eclipse. Import them using Preferences > Java > Code Style > Clean Up and ... > Formatter. New code can be formatted like the current code using the Source > Clean Up... feature.
-
-# Releasing #
-
-## Sonatype Access ##
-
-Artifacts are deployed to the [Sonatype OSS Nexus repository](http://oss.sonatype.org/), which is synchronized with the Maven Central Repository. Therefore, in order to deploy you must have a Sonatype account with write permission to the repository. First, sign up for an account on the [Sonatype Nexus homepage](http://oss.sonatype.org/) (link on upper right of page). Next, send me a message with your account name to request access to the repository.
-
-Once you have the proper permissions, place the following blob inside `~/.m2/settings.xml`. You may need to create this file.
-
-
-
-
- sonatype-nexus-snapshots
- your-sonatype-username
- your-sonatype-password
-
-
- sonatype-nexus-staging
- your-sonatype-username
- your-sonatype-password
-
-
-
-
-
-## Deploying Snapshots ##
-
-At this point, SNAPSHOT artifacts can be deployed by simply running:
-
- mvn clean deploy
-
-## Deploying Releases ##
-
-Releases are more complicated because they must be properly signed. Follow the [instructions here](http://www.sonatype.com/people/2010/01/how-to-generate-pgp-signatures-with-maven/) to set up GPG locally. Once that is done, to release:
-
-1. Edit pom.xml to reflect the correct scm.connection and scm.developerConnection and check in.
-
-2. Run the Maven release:prepare goal until it succeeds
-
- # Note: this command pushes to bitbucket!
- mvn release:prepare -Dresume=true -Dusername= -Dpassword=
-
-3. Run
-
- gpg-agent --use-standard-socket --daemon 2>/dev/null
-
-4. In the same terminal, run
-
- mvn release:perform -Dgpg.passphrase= -Darguments="-Dgpg.passphrase="
-
-If the release succeeds, you should be able to log in to Sonatype and see the new artifact in the Staging section. Right click and choose "Close" to approve the artifact to be synchronized to Maven Central.
diff --git a/README.md b/README.md
index f4d0b288b..17c6ef72f 100644
--- a/README.md
+++ b/README.md
@@ -1,344 +1,166 @@
+## Riak Java client v2.0.1
+This branch of the Riak Java Client is for the new v2.0 client, to be used with
+ Riak 2.0.
-This document describes how to use this client to interact with Riak. See the [DEVELOPERS](https://github.com/basho/riak-java-client/blob/master/DEVELOPERS.md) document for a technical overview of the project.
+Previous versions:
-# Overview #
+[riak-client-1.4.4](https://github.com/basho/riak-java-client/tree/1.4.4) - For use with Riak 1.4.x
-This Java-based Riak client uses Commons HttpClient to perform HTTP requests. It provides:
+[riak-client-1.1.4](https://github.com/basho/riak-java-client/tree/1.1.4) - For use with < Riak 1.4.0
-* **HttpClient**-provided functionality such as connection pooling, timeouts, and retries, which is not provided by `HttpURLConnection`.
-
-* **Link** and **Map/Reduce** query building support.
-
-* **HTTP response data** returned directly to the client rather than via exceptions. While this slightly couples the domain model with the underlying HTTP model, it gives the benefit of allowing the full suite of HTTP to be used (in possibly unforseen ways) without requiring modifications to the client library. In reality, clients need to and do understand that each operation in fact translates to an HTTP operation. In any case, **PlainClient** provides the more traditional interface with domain objects and checked exceptions which can be used when appropriate.
-
-* **Stream handling** for GET requests.
-
-* **Exceptions** are unchecked and the **RiakExceptionHandler** interface allows all exceptions to be handled in a central location. This means the client does not need to wrap each operation in try/catch blocks.
-
-# Including in your project #
-
-To use `riak-client` in a [Maven](http://maven.apache.org/) project, add the following dependency to `pom.xml`:
-
-
- com.basho.riak
- riak-client
- 0.14.0
- pom
-
-
-To build and install from source, first install Apache Maven (http://maven.apache.org/download.html). With Maven installed, run:
-
- mvn clean install
-
-# Quick start #
-
-Connect to Riak:
-
- RiakClient riak = new RiakClient("http://localhost:8098/riak");
-
-Build an object:
-
- RiakObject o = new RiakObject("bucket", "key", "value");
-
-Store it:
-
- riak.store(o);
-
-Retrieve it:
-
- FetchResponse r = riak.fetch("bucket", "key");
- if (r.hasObject())
- o = r.getObject();
-
-Update it:
-
- o.setValue("foo");
- riak.store(o);
-
-Handling siblings:
-
- if (r.hasSiblings())
- Collection siblings = r.getSiblings();
-
-
-# Connecting #
-
-Connect to a Riak server by specifying the base URL of the Riak HTTP interface:
-
- RiakClient riak = new RiakClient("http://localhost:8098/riak");
-
-HttpClient parameters can be provided using a RiakConfig object:
-
- RiakConfig config = new RiakConfig("http://localhost:8098/riak");
-
- config.setTimeout(2000); // 2 second connection timeout
- config.setMaxConnections(50); // 50 concurrent connections
-
- RiakClient riak = new RiakClient(config);
-
-The HttpClient instance itself can also be given:
-
- MultiThreadedHttpConnectionManager m = new MultiThreadedHttpConnectionManager();
- m.getParams().setIntParameter(HttpConnectionManagerParams.MAX_TOTAL_CONNECTIONS, 50);
-
- HttpClient http = new HttpClient(m);
- http.getParams().setLongParameter(HttpClientParams.CONNECTION_MANAGER_TIMEOUT, 2000);
-
- RiakConfig config = new RiakConfig("http://localhost:8098/riak");
- config.setHttpClient(http);
-
- RiakClient riak = new RiakClient(config);
-
-As an alternative to `RiakClient`, the `PlainClient` interface can be used, which exposes a different interface that hides HTTP response information from results.
-
- PlainClient plain = PlainClient.getClient("http://localhost:8098/riak");
- PlainClient plain = PlainClient.getClient(new RiakConfig("http://localhost:8098/riak"));
-
-# Operations #
-
-Using RiakClient, you can manipulate Riak objects and buckets.
-
-## Store Objects ##
-
-First create the object type corresponding to the interface being used and fill in any metadata and value. Then, store the object and check the server response. The response contains the updated vclock, last modified date, and vtag of the object, if it was returned by the server.
-
- RiakObject o = new RiakObject("bucket", "key", "value");
- o.getUsermeta().put("custom-max-age", "60");
- o.setContentType("text/plain");
-
- StoreResponse r = riak.store(o);
- if (r.isSuccess()) obj.updateMeta(r);
-
-Or, with Plain:
-
- plain.store(new RiakObject("a", "b"));
-
-The request `w` and `dw` values (number of write or durable write responses required for success) can be specified using a RequestMeta object:
-
- client.store(obj, RequestMeta.writeParams(2 /* w-value */, 2 /* dw-value */));
-
-## Fetch Objects ##
-
-Simply request the object by bucket and key. The response contains the requested object.
-
- FetchResponse r = riak.fetch("bucket", "key");
- if (r.isSuccess())
- RiakObject o = r.getObject();
-
-With Plain:
-
- RiakObject o = plain.fetch("bucket", "key");
-
-The request `r` value (number of servers responding to a read request required for success) can be specified using a RequestMeta object:
-
- client.fetch("bucket", "key", RequestMeta.readParams(2 /* r-value */));
-
-## Fetch Object Metadata ##
-
-This works identically to fetching an object, except that the object's value will not necessarily be populated.
-
- client.fetchMeta("bucket", "key", /* optional */ RequestMeta.readParams(2));
-
-## Modify Objects ##
-
-To ensure that no conflicts are created when modifying an object, first fetch it, then update and store it.
-
- FetchResponse r = riak.fetch("bucket", "key");
- if (r.isSuccess()) {
- RiakObject o = r.getObject();
- o.setValue("foo");
- client.store(o);
- }
-
-With Plain:
-
- RiakObject o = plain.fetch("bucket", "key");
- o.setValue("foo");
- plain.store(o);
-
-## Delete Objects ##
-
-Simply execute the delete method. The `dw` value can be optionally specified as before with a RequestMeta object.
-
- client.delete("bucket", "key", RequestMeta.writeParams(null /* no w value */, 2 /* dw value */));
-
-## Handling Conflicts and Siblings ##
-
-The Riak HTTP interface is able to return conflicting versions of the same object, known as siblings.
-
- // ensure the that allow_mult bucket property is set to true, or Riak will not return siblings.
- FetchResponse r = riak.fetch("bucket", "key");
- if (r.isSuccess() && r.hasSiblings())
- Collection siblings = r.getSiblings();
-
-With Plain:
-
- Collection extends RiakObject> objects = plain.fetchAll("bucket", "key");
-
-## Streaming Objects ##
-
-To process an object as a stream, implement a StreamHandler and use the stream() method. The input stream of the HTTP response is given to the handler.
-
- client.stream("bucket", "key", new MyStreamHandler(), RequestMeta.readParams(1 /* r value */));
-
-RiakClient also contains a stream() method that returns a standard RiakObject with a value stream. The user is responsible for closing the response in this case.
-
- FetchResponse r;
- try {
- r = riak.stream("bucket", "key");
- if (r.isSuccess()) {
- InputStream valueStream = r.getObject().getValueStream();
-
- /* process valueStream */
- }
- } finally {
- if (r != null) {
- r.close();
- }
- }
-
-## Buckets Keys and Schema ##
-
-The bucket schema and a list the keys of all the objects in the bucket can be read using the listBucket() method and written using the setBucketSchema() method. The bucket schema is presented as a JSONObject and contains per-bucket information. For example, the `allow_mult` property allows Riak to return conflicting versions of the same object. See the Riak documentation for more information.
-
- BucketResponse r = riak.listBucket("bucket");
- if (r.isSuccess()) {
- BucketInfo info = r.getBucketInfo();
- Collection keys = info.getKeys(); // list of all object keys in this bucket
-
- // Update the schema and put
- info.setAllowMult(true);
- riak.setBucketSchema("bucket", info);
- }
-
-It is also possible to stream the keys in the bucket, which sends the `?keys=stream` query parameter to Riak.
-
- BucketResponse r;
- try {
- r = riak.streamBucket("bucket");
- if (r.isSuccess())
- for (String key : r.getBucketInfo().getKeys())
- // process key
- } finally {
- if (r != null)
- r.close();
- }
-
-
-With Plain:
-
- RiakBucketInfo info = plain.listBucket("bucket");
- info.getSchema().put("allow_mult", true);
- plain.setBucketSchema("bucket", info);
-
-## Links and Link Walking ##
-
-Links can be stored with each object. A link consists of the target object's bucket and key and a tag to identify the link.
-
- RiakObject o = new RiakObject("bucket", "key");
- o.getLinks().add(new RiakLink("bucket", "target-object", "link-tag"));
-
-Link walking is performed by calling walk() with a walk specification (see the Riak documentation and JavaDocs for RiakWalkSpec). A list of lists of objects is returned. Each list of objects represents all the objects returned in a single step of the walk.
-
- WalkResponse r = riak.walk("bucket", "key", "bucket,_,1");
- if (r.isSuccess()) {
- List extends List> steps = r.getSteps();
- for (List step : steps) {
- for (RiakObject o : step) {
- // process the object
- }
- }
- }
-
-Alternatively, the link walk can be built from a `RiakObject`.
-
- RiakObject o = new RiakObject(riak, "bucket", "key");
- WalkResponse r = o.walk("bucket").run();
-
-## Map/Reduce Queries ##
-
-Refer to the [Riak Javascript Map/Reduce documentation](http://bitbucket.org/basho/riak/src/tip/doc/js-mapreduce.org) for a detailed explanation of how map/reduce works in Riak over HTTP.
-
-First, build a Map/Reduce query by calling `mapReduceOverBucket()` or `mapReduceOverObjects()`. Then execute it by calling `submit()`.
-
- MapReduceResponse r = riak.mapReduceOverBucket("bucket")
- .link("bucket", "tag", false)
- .map(JavascriptFunction.named("Riak.mapValuesJson"), false)
- .reduce(new ErlangFunction("riak_mapreduce", "reduce_sort"), true)
- .submit();
- if (r.isSuccess()) {
- JSONArray results = r.getResults();
- // process the results array
- }
-
-A Map/Reduce query is built by chaining methods calls that to corresponding Riak Map/Reduce phase types.
-
-* `map(function, keep)`
-* `reduce(function, keep)`
-* `link(bucket, tag, keep)`
-
-The `keep` flag determines whether the results from that phase is returned by Riak.
-
-The supported `map()` and `reduce()` function types are
-
-* `ErlangFunction`: an Erlang function specified by a module and function
-* `JavascriptFunction`: a named or anonymous Javascript function
-* `LinkFunction`: a link specification consisting of a bucket name and tag
-
-For example:
-
- // Erlang function riak_mapreduce:reduce_sort
- new ErlangFunction("riak_mapreduce", "reduce_sort");
-
- // Built-in named Javascript function Riak.mapValuesJson
- JavascriptFunction.named("Riak.mapValuesJson");
-
- // Unnamed Javascript function
- JavascriptFunction.anon("function(v) { return [v.values[0].data]; }");
-
- // Follow links to bucket "b" tagged with tag "t"
- new LinkFunction("b", "t");
-
-If `submit()` succeeds, Riak returns the query result, which is a JSON array.
-
-
-# HTTP Request/Response Information #
-
-All of the above operations can also be called with a RequestMeta object to specify extra HTTP headers and query parameters:
-
- RequestMeta meta = new RequestMeta();
- meta.putHeader("X-Custom-Header", "value");
- meta.addHeader("custom-query-param", "param");
-
- client.fetch("bucket", "key", meta);
-
-The operations also return results that implement the HttpResponse interface, which exposes the HTTP status code, headers, body, and original HttpMethod used for the request. The entity stream, however is closed, so the stream() function should be used to stream objects.
-
- HttpResponse r = client.fetch("bucket", "key");
- Map httpHeaders = r.getHttpHeaders();
- String httpBody = r.getBody();
-
-# Exception Handling #
-
-RiakClient will usually throw the unchecked exceptions RiakIORuntimeException and RiakResponseRuntimeException if there is an error talking to the server or if the server returns a response that can't be parsed. However, a RiakExceptionHandler can be installed in the client to prevent them from throwing the exceptions. Instead, they are passed to the exception handler. This allows a user, for example, to consolidate processing for these exceptional cases in a single class and avoid inline try/catch blocks.
-
-In addition, the `ClientUtils.throwChecked()` method allows the unchecked exceptions to be easily converted to checked exceptions. Of course, in this case, methods using the `RiakClient` instance must be careful to declare the exception in their signatures, since the compiler will not produce the usual warnings.
-
- RiakClient c = new RiakClient("");
- c.setExceptionHandler(new RiakExceptionHandler() {
-
- public void handle(RiakResponseRuntimeException e) {
- // Log and ignore malformed responses. The operation in progress
- // will return an HttpResponse with 0 status code.
- LOG.warn("Received malformed server response", e);
- }
-
- public void handle(RiakIORuntimeException e) {
- // Convert to a checked exception, which should be declared in
- // the calling method signature
- ClientUtils.throwChecked(
- new IOException("Riak connection is down", e.getCause()));
- }
- });
+This client is published to Maven Central and can be included in your project by adding:
+
+```xml
+
+
+ com.basho.riak
+ riak-client
+ 2.0.1
+
+ ...
+
+```
+
+## Overview
+
+Version 2.0 of the Riak Java client is a completely new codebase. It relies on
+Netty4 in the core for handling network operations and all operations can
+be executed synchronously or asynchronously.
+
+## Getting started with the 2.0 client.
+
+The new client is designed to model a Riak cluster:
+
+
+
+The easiest way to get started with the client is using one of the static
+methods provided to instantiate and start the client:
+
+```java
+RiakClient client =
+ RiakClient.newClient("192.168.1.1","192.168.1.2","192.168.1.3");
+```
+
+The RiakClient object is thread safe and may be shared across multiple threads.
+
+For more complex configurations, you can instantiate a RiakCluster from the
+core packages and supply it to the RiakClient constructor.
+
+```java
+RiakNode.Builder builder = new RiakNode.Builder();
+builder.withMinConnections(10);
+builder.withMaxConnections(50);
+
+List addresses = new LinkedList();
+addresses.add("192.168.1.1");
+addresses.add("192.168.1.2");
+addresses.add("192.168.1.3");
+
+List nodes = RiakNode.Builder.buildNodes(builder, addresses);
+RiakCluster cluster = new RiakCluster.Builder(nodes).build();
+cluster.start();
+RiakClient client = new RiakClient(cluster)
+```
+
+Once you have a client, commands from the [com.basho.riak.client.api.commands.*](#riakcommand-subclasses)
+packages are built then executed by the client.
+
+Some basic examples of building and executing these commands is shown
+below.
+
+## Getting Data In
+
+```java
+Namespace ns = new Namespace("default", "my_bucket");
+Location location = new Location(ns, "my_key");
+RiakObject riakObject = new RiakObject();
+riakObject.setValue(BinaryValue.create("my_value"));
+StoreValue store = new StoreValue.Builder(riakObject)
+ .withLocation(location)
+ .withOption(Option.W, new Quorum(3)).build();
+client.execute(store);
+```
+
+## Getting Data Out
+
+```java
+Namespace ns = new Namespace("default","my_bucket");
+Location location = new Location(ns, "my_key");
+FetchValue fv = new FetchValue.Builder(location).build();
+FetchValue.Response response = client.execute(fv);
+RiakObject obj = response.getValue(RiakObject.class);
+```
+
+## Using 2.0 Data Types (Maps & Registers)
+
+A [bucket type](http://docs.basho.com/riak/latest/dev/advanced/bucket-types) must be created (in all local and remote clusters) before 2.0
+data types can be used. In the example below, it is assumed that the type
+"my_map_type" has been created and associated to the "my_map_bucket" prior
+to this code executing.
+
+Once a bucket has been associated with a type, all values stored in that bucket
+must belong to that data type.
+
+```java
+Namespace ns = new Namespace("my_map_type", "my_map_bucket");
+Location location = new Location(ns, "my_key");
+RegisterUpdate ru1 = new RegisterUpdate(BinaryValue.create("map_value_1"));
+RegisterUpdate ru2 = new RegisterUpdate(BinaryValue.create("map_value_2"));
+MapUpdate mu = new MapUpdate();
+mu.update("map_key_1", ru1);
+mu.update("map_key_2", ru2);
+UpdateMap update = new UpdateMap.Builder(location, mu).build();
+client.execute(update);
+```
+
+
+ *
+ * Note that the Riak Java client uses the Riak Protocol Buffers API exclusively.
+ *
+ * For more complex configurations you will instantiate one or more {@link com.basho.riak.client.core.RiakNode}s
+ * and build a {@link com.basho.riak.client.core.RiakCluster} to supply to the
+ * RiakClient constructor.
+ *
+ * Once you have a client, {@literal RiakCommand}s from the {@literal com.basho.riak.client.api.commands.*}
+ * packages are built then executed by the client:
+ *
+ * {@code
+ * Namespace ns = new Namespace("default","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);}
+ *
+ *
+ * You can also execute all {@literal RiakCommand}s asynchronously. A
+ * {@link RiakFuture} for the operation is immediately returned:
+ *
+ * @author Dave Rusek
+ * @author Brian Roach
+ * @since 2.0
+ */
+public class RiakClient
+{
+
+ private final RiakCluster cluster;
+
+ /**
+ * Create a new RiakClient to perform operations on the given cluster.
+ *
+ * The RiakClient provides a user API on top of the client core. Once
+ * instantiated, commands are submitted to it for execution on Riak.
+ *
+ * @param cluster the started RiakCluster to use.
+ */
+ public RiakClient(RiakCluster cluster)
+ {
+ this.cluster = cluster;
+ }
+
+ /**
+ * Static factory method to create a new client instance.
+ * This method produces a client that connects to 127.0.0.1 on the default
+ * protocol buffers port (8087).
+ *
+ * @return a new client instance.
+ * @throws UnknownHostException
+ */
+ public static RiakClient newClient() throws UnknownHostException
+ {
+ RiakNode.Builder builder = new RiakNode.Builder()
+ .withMinConnections(10);
+ RiakCluster cluster = new RiakCluster.Builder(builder.build()).build();
+ cluster.start();
+ return new RiakClient(cluster);
+
+ }
+
+ /**
+ * Static factory method to create a new client instance.
+ * This method produces a client connected to the supplied addresses on
+ * the supplied port.
+ * @param remoteAddresses a list of IP addresses or hostnames
+ * @param port the (protocol buffers) port to connect to on the supplied hosts.
+ * @return a new client instance
+ * @throws UnknownHostException if a supplied hostname cannot be resolved.
+ */
+ public static RiakClient newClient(int port, String... remoteAddresses) throws UnknownHostException
+ {
+ return newClient(port, Arrays.asList(remoteAddresses));
+ }
+
+ /**
+ * Static factory method to create a new client instance.
+ * This method produces a client connected to the supplied addresses on
+ * the default (protocol buffers) port (8087).
+ * @param remoteAddresses a list of IP addresses or hostnames
+ * @return a new client instance
+ * @throws UnknownHostException if a supplied hostname cannot be resolved.
+ */
+ public static RiakClient newClient(List remoteAddresses) throws UnknownHostException
+ {
+ return newClient(RiakNode.Builder.DEFAULT_REMOTE_PORT, remoteAddresses);
+ }
+
+ /**
+ * Static factory method to create a new client instance.
+ * This method produces a client connected to the supplied addresses on
+ * the default (protocol buffers) port (8087).
+ * @param remoteAddresses a list of IP addresses or hostnames
+ * @return a new client instance
+ * @throws UnknownHostException if a supplied hostname cannot be resolved.
+ */
+ public static RiakClient newClient(String... remoteAddresses) throws UnknownHostException
+ {
+ return newClient(RiakNode.Builder.DEFAULT_REMOTE_PORT, Arrays.asList(remoteAddresses));
+ }
+
+ /**
+ * Static factory method to create a new client instance.
+ * This method produces a client connected to the supplied addresses on
+ * the supplied port.
+ * @param remoteAddresses a list of IP addresses or hostnames
+ * @param port the (protocol buffers) port to connect to on the supplied hosts.
+ * @return a new client instance
+ * @throws UnknownHostException if a supplied hostname cannot be resolved.
+ */
+ public static RiakClient newClient(int port, List remoteAddresses) throws UnknownHostException
+ {
+ RiakNode.Builder builder = new RiakNode.Builder()
+ .withRemotePort(port)
+ .withMinConnections(10);
+ List nodes = RiakNode.Builder.buildNodes(builder, remoteAddresses);
+ RiakCluster cluster = new RiakCluster.Builder(nodes).build();
+ cluster.start();
+ return new RiakClient(cluster);
+ }
+
+ /**
+ * Static factory method to create a new client instance.
+ * This method produces a client connected to the supplied addresses.
+ * @param addresses one or more addresses to connect to.
+ * @return a new RiakClient instance.
+ * @throws java.net.UnknownHostException if a supplied hostname cannot be resolved.
+ */
+ public static RiakClient newClient(InetSocketAddress... addresses) throws UnknownHostException
+ {
+ RiakNode.Builder builder = new RiakNode.Builder().withMinConnections(10);
+ List nodes = new LinkedList();
+ for (InetSocketAddress addy : addresses)
+ {
+ builder.withRemoteAddress(addy.getHostString())
+ .withRemotePort(addy.getPort());
+ nodes.add(builder.build());
+ }
+ RiakCluster cluster = new RiakCluster.Builder(nodes).build();
+ cluster.start();
+ return new RiakClient(cluster);
+ }
+
+ /**
+ * Execute a RiakCommand synchronously.
+ *
+ * Calling this method causes the client to execute the provided RiakCommand synchronously.
+ * It will block until the operation completes then either return the response
+ * on success or throw an exception on failure.
+ *
+ *
+ * @param command
+ * The RiakCommand to execute.
+ * @param
+ * The RiakCommand's return type.
+ * @param The RiakCommand's query info type.
+ * @return a response from Riak.
+ * @throws ExecutionException if the command fails for any reason.
+ * @throws InterruptedException
+ */
+ public T execute(RiakCommand command) throws ExecutionException, InterruptedException
+ {
+ return command.execute(cluster);
+ }
+
+ /**
+ * Execute a RiakCommand asynchronously.
+ *
+ * Calling this method causes the client to execute the provided RiakCommand
+ * asynchronously. It will immediately return a RiakFuture that contains the
+ * running operation.
+ * @param RiakCommand's return type.
+ * @param The RiakCommand's query info type.
+ * @param command The RiakCommand to execute.
+ * @return a RiakFuture for the operation.
+ * @see RiakFuture
+ */
+ public RiakFuture executeAsync(RiakCommand command)
+ {
+ return command.executeAsync(cluster);
+ }
+
+ /**
+ * Shut down the client and the underlying RiakCluster.
+ *
+ * The underlying client core (RiakCluster) uses a number of threads as
+ * does Netty. Calling this method will shut down all those threads cleanly.
+ * Failure to do so may prevent your application from exiting.
+ *
+ * @return a future that will complete when shutdown
+ */
+ public Future shutdown()
+ {
+ return cluster.shutdown();
+ }
+
+ /**
+ * Get the RiakCluster being used by this client.
+ *
+ * Allows for adding/removing nodes, etc.
+ *
+ * @return The RiakCluster instance being used by this client.
+ */
+ public RiakCluster getRiakCluster()
+ {
+ return cluster;
+ }
+}
diff --git a/src/main/java/com/basho/riak/client/api/RiakCommand.java b/src/main/java/com/basho/riak/client/api/RiakCommand.java
new file mode 100644
index 000000000..2cf38f4ef
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/RiakCommand.java
@@ -0,0 +1,93 @@
+/*
+ * 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;
+
+import com.basho.riak.client.core.RiakCluster;
+import com.basho.riak.client.core.RiakFuture;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * The base class for all Riak Commands.
+ *
+ * All the commands the {@link RiakClient} can execute extend this class.
+ *
+ * @author Dave Rusek
+ * @author Brian Roach
+ * @param The response type
+ * @param The query info type
+ * @since 2.0
+ */
+public abstract class RiakCommand
+{
+ protected final T execute(RiakCluster cluster) throws ExecutionException, InterruptedException
+ {
+ RiakFuture future = executeAsync(cluster);
+ future.await();
+ return future.get();
+ }
+ protected abstract RiakFuture executeAsync(RiakCluster cluster);
+}
+
diff --git a/src/main/java/com/basho/riak/client/api/RiakException.java b/src/main/java/com/basho/riak/client/api/RiakException.java
new file mode 100644
index 000000000..e53e290d6
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/RiakException.java
@@ -0,0 +1,25 @@
+package com.basho.riak.client.api;
+
+public class RiakException extends Exception
+{
+ public RiakException()
+ {
+ super();
+ }
+
+ public RiakException(String message)
+ {
+ super(message);
+ }
+
+ public RiakException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+
+ public RiakException(Throwable cause)
+ {
+ super(cause);
+ }
+
+}
diff --git a/src/main/java/com/basho/riak/client/api/annotations/RiakBucketName.java b/src/main/java/com/basho/riak/client/api/annotations/RiakBucketName.java
new file mode 100644
index 000000000..7f3f4626c
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/annotations/RiakBucketName.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 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.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a field or getter/setter method pair in a class to serve as the bucket name.
+ *
+ * @author Brian Roach
+ * @since 2.0
+ */
+@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) public @interface RiakBucketName
+{
+
+}
diff --git a/src/main/java/com/basho/riak/client/api/annotations/RiakBucketType.java b/src/main/java/com/basho/riak/client/api/annotations/RiakBucketType.java
new file mode 100644
index 000000000..a8aace070
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/annotations/RiakBucketType.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 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.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a field or getter/setter method pair in a class to server as the bucket type.
+ *
+ * @author Brian Roach
+ * @since 2.0
+ */
+@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) public @interface RiakBucketType
+{
+
+}
diff --git a/src/main/java/com/basho/riak/client/api/annotations/RiakContentType.java b/src/main/java/com/basho/riak/client/api/annotations/RiakContentType.java
new file mode 100644
index 000000000..b63abd015
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/annotations/RiakContentType.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 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 WARANTIES 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.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+
+
+/**
+ * Annotates a field or getter/setter method pair in a class to serve as the content-type.
+ *
+ * The type must be a {@code String}.
+ *
+ *
+ * In most cases this is not needed as the {@link com.basho.riak.client.api.convert.Converter}
+ * will supply the appropriate content-type during serialization.
+ *
+ *
+ * @author Brian Roach
+ * @since 2.0
+ */
+@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) public @interface RiakContentType
+{
+
+}
diff --git a/src/main/java/com/basho/riak/client/api/annotations/RiakIndex.java b/src/main/java/com/basho/riak/client/api/annotations/RiakIndex.java
new file mode 100644
index 000000000..79170c94c
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/annotations/RiakIndex.java
@@ -0,0 +1,79 @@
+/*
+ * 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.api.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a field or getter/setter method pair in a class to serve as a RiakIndex.
+ *
+ * You do not need to specify the index type suffix ({@code _bin}/{@code _int}). It will be
+ * inferred from the type of the annotated field except when using {@code byte[]};
+ * in this case the full index name including the suffix must be supplied.
+ *
+ *
+ * For {@code _bin} indexes, {@code String} and {@code byte[]} types are supported.
+ * For {@code _int} indexes, {@code Long}, {@code BigInteger} and {@code byte[]}
+ * are supported. This can either be a single instance or a {@code Set}.
+ *
+ *
+ * Important Note: if there are multiple index keys for the object and the field
+ * is a single value rather than a {@code Set}, only a single index key will be
+ * set (the first returned).
+ *
+ *
+ * public class MyClass
+ * {
+ * {@literal @}RiakKey
+ * private String myKeyString;
+ *
+ * {@literal @}RiakIndex(name="email")
+ * private {@literal Set} emailAddress; // will be indexed in the email_bin index
+ *
+ * {@literal @}RiakIndex(name="age")
+ * private long age; // will be indexed in the age_int index
+ * }
+ *
+ * public class MyClass
+ * {
+ * private {@literal Set} categoryIds;
+ *
+ * {@literal @}RiakIndex(name="category_ids")
+ * public {@literal Set} getCategoryIds()
+ * {
+ * return categoryIds;
+ * }
+ *
+ * {@literal @}RiakIndex(name="category_ids")
+ * public void setCategoryIds({@literal Set} ids)
+ * {
+ * categoryIds = ids;
+ * }
+ * }
+ *
+ *
+ *
+ * @author Russell Brown
+ * @author Brian Roach
+ * @since 1.0
+*/
+@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) public @interface RiakIndex {
+ /**
+ * @return the index name
+ */
+ String name() default "";
+}
diff --git a/src/main/java/com/basho/riak/client/api/annotations/RiakKey.java b/src/main/java/com/basho/riak/client/api/annotations/RiakKey.java
new file mode 100644
index 000000000..cb0bda717
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/annotations/RiakKey.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 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.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a field or getter/setter method pair in a class to serve as the key.
+ *
+ * @author Russell Brown
+ * @author Brian Roach
+ * @since 1.0
+ */
+@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) public @interface RiakKey {
+
+}
diff --git a/src/main/java/com/basho/riak/client/api/annotations/RiakLastModified.java b/src/main/java/com/basho/riak/client/api/annotations/RiakLastModified.java
new file mode 100644
index 000000000..78b8c0707
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/annotations/RiakLastModified.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 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.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a field or setter method in a class to store last modified.
+ *
+ * This value is only populated when fetching an object from riak. Setting it when
+ * storing an object has no effect. A getter method is not supported; only a setter
+ * method may be annotated. The type must be {@literal long} or {@literal Long}.
+ *
+ *
+ *
+ * @author Brian Roach
+ * @since 2.0
+ */
+@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) public @interface RiakLastModified
+{
+
+}
diff --git a/src/main/java/com/basho/riak/client/api/annotations/RiakLinks.java b/src/main/java/com/basho/riak/client/api/annotations/RiakLinks.java
new file mode 100644
index 000000000..e2dadbbcd
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/annotations/RiakLinks.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 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.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a field or getter/setter method pair in a class to serve as a set of links.
+ *
+ *
+ *
+ * @author Russell Brown
+ * @author Brian Roach
+ * @since 1.0
+*/
+@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) public @interface RiakLinks {
+}
diff --git a/src/main/java/com/basho/riak/client/api/annotations/RiakTombstone.java b/src/main/java/com/basho/riak/client/api/annotations/RiakTombstone.java
new file mode 100644
index 000000000..18c03eb20
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/annotations/RiakTombstone.java
@@ -0,0 +1,57 @@
+/*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.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a field or getter/setter method pair in a class to serve as the tombstone indicator.
+ *
+ * This annotation is used to denote a boolean field or getter/setter pair that will be marked true
+ * if the object is a tombstone (deleted vector clock)
+ *
+ *
+ *
+ * @author Brian Roach
+ * @since 2.0
+ */
+
+@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) public @interface RiakTombstone {
+
+}
diff --git a/src/main/java/com/basho/riak/client/api/annotations/RiakUsermeta.java b/src/main/java/com/basho/riak/client/api/annotations/RiakUsermeta.java
new file mode 100644
index 000000000..123328eb9
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/annotations/RiakUsermeta.java
@@ -0,0 +1,83 @@
+/*
+ * 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.api.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a field or getter/setter method pair in a class to serve as containing user meta data for a Riak
+ * object.
+ *
+ * If you set the key value (to anything other than the empty string) then you
+ * can use the annotation to map a single key of user meta data to a field.
+ *
+ *
+ *
+ * @author Russel Brown
+ * @author Brian Roach
+ * @since 1.0
+ */
+@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) public @interface RiakUsermeta {
+ /**
+ * Use a lower case key. The riak HTTP API *will* lower case key names.
+ * @return the key for the user meta item
+ */
+ String key() default "";
+}
diff --git a/src/main/java/com/basho/riak/client/api/annotations/RiakVClock.java b/src/main/java/com/basho/riak/client/api/annotations/RiakVClock.java
new file mode 100644
index 000000000..4c8b43fad
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/annotations/RiakVClock.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 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.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a field or getter/setter method pair in a class to serve as the vector clock.
+ *
+ * This annotation can be used with either {@code byte[]} or {@code VClock} types.
+ *
+ * @author Brian Roach
+ * @since 1.4
+ */
+
+@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) public @interface RiakVClock {
+
+}
diff --git a/src/main/java/com/basho/riak/client/api/annotations/RiakVTag.java b/src/main/java/com/basho/riak/client/api/annotations/RiakVTag.java
new file mode 100644
index 000000000..5aea1cd52
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/annotations/RiakVTag.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 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.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a field or setter method in a class to store the VTag.
+ *
+ * This value is only populated when fetching an object from riak. Setting it when
+ * storing an object has no effect. A getter method is not supported; only a setter
+ * method may be annotated. The type must be {@literal String}.
+ *
+ *
+ *
+ * @author Brian Roach
+ * @since 2.0
+ */
+@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) public @interface RiakVTag
+{
+
+}
diff --git a/src/main/java/com/basho/riak/client/api/annotations/package-info.java b/src/main/java/com/basho/riak/client/api/annotations/package-info.java
new file mode 100644
index 000000000..b55b4964a
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/annotations/package-info.java
@@ -0,0 +1,51 @@
+/**
+ * Annotations to use for ORM.
+ *
+ *
Introduction
+ * The Riak Java client provides a full set of annotations to facilitate simply
+ * storing/retrieving your own domain object. All annotations can be applied to
+ * a field or a getter/setter pair of methods.
+ *
+ * In addition, the {@link com.basho.riak.client.api.convert.Converter}
+ * interface allows for serialization/deserialization for the data portion.
+ *
+ *
+ * By annotating your own domain object, you can simply pass an instance of it
+ * to {@link com.basho.riak.client.api.commands.kv.StoreValue}.
+ *
+ *
+ * When fetching data from Riak, the reverse is also true. The {@link com.basho.riak.client.api.commands.kv.FetchValue.Response}
+ * handles injecting your domain object with any of the annotated values.
+ *
+ *
+ * Raw types as well as Generic types are supported. The latter is done via Jackson's
+ * {@code TypeReferece} class.
+ *
+ *
OverView
+ * To store an object in Riak there's a minimum of four pieces of information
+ * required; a bucket type, a bucket name, a key, and a vector clock. For an
+ * annotated domain object, only three of these are required as in the absence of
+ * a bucket type, the default "default" type is supplied.
+ *
+ *
+ */
+package com.basho.riak.client.api.annotations;
diff --git a/src/main/java/com/basho/riak/client/api/cap/BasicVClock.java b/src/main/java/com/basho/riak/client/api/cap/BasicVClock.java
new file mode 100644
index 000000000..77bdfd648
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/cap/BasicVClock.java
@@ -0,0 +1,97 @@
+/*
+ * 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.cap;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+/**
+ * Encapsulates a Riak vector clock.
+ * @author Russel Brown
+ * @since 1.0
+ */
+public class BasicVClock implements VClock
+{
+ private final byte[] value;
+
+ /**
+ * Create a BasicVclock.
+ * @param value the vector clock bytes. NOTE: copies the value
+ * @throws IllegalArgumentException if value is null
+ */
+ public BasicVClock(final byte[] value) {
+ if (value == null) {
+ throw new IllegalArgumentException("VClock value cannot be null");
+ }
+ this.value = Arrays.copyOf(value, value.length);
+ }
+
+ /**
+ * Create a BasicVclock from utf8 String.
+ * @param vclock the vector clock.
+ * @throws IllegalArgumentException if {@code vclock} is null
+ */
+ public BasicVClock(String vclock)
+ {
+ if (vclock == null) {
+ throw new IllegalArgumentException("VClock value cannot be null");
+ }
+ this.value = vclock.getBytes(Charset.forName("UTF-8"));
+ }
+
+ @Override
+ public byte[] getBytes()
+ {
+ return Arrays.copyOf(value, value.length);
+ }
+
+ @Override
+ public String asString()
+ {
+ try
+ {
+ return new String(value, "UTF-8");
+ }
+ catch (UnsupportedEncodingException ex)
+ {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (o != null)
+ {
+ if (o instanceof VClock)
+ {
+ VClock other = (VClock)o;
+ return Arrays.equals(other.getBytes(), value);
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = 3;
+ hash = 67 * hash + Arrays.hashCode(this.value);
+ return hash;
+ }
+}
diff --git a/src/main/java/com/basho/riak/client/api/cap/ConflictResolver.java b/src/main/java/com/basho/riak/client/api/cap/ConflictResolver.java
new file mode 100644
index 000000000..566119aa3
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/cap/ConflictResolver.java
@@ -0,0 +1,40 @@
+/*
+ * 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.cap;
+
+import java.util.List;
+
+/**
+ * Interface used to resolve siblings.
+ *
+ * When you have multiple writers there may be multiple versions of the same
+ * object stored in Riak. When fetching all of these will be returned and you
+ * will need to resolve the conflict.
+ *
+ *
+ * To facilitate this, you can store an instance of the ConflictResolver
+ * in the {@link com.basho.riak.client.api.cap.ConflictResolverFactory} for a class.
+ * It will then be used by the {@link com.basho.riak.client.api.commands.kv.FetchValue.Response}
+ * to resolve a set a of siblings to a single object.
+ *
+ * @author Brian Roach
+ * @since 2.0
+ * @param The type of the objects to be resolved.
+ */
+public interface ConflictResolver
+{
+ T resolve(List objectList) throws UnresolvedConflictException;
+}
diff --git a/src/main/java/com/basho/riak/client/api/cap/ConflictResolverFactory.java b/src/main/java/com/basho/riak/client/api/cap/ConflictResolverFactory.java
new file mode 100644
index 000000000..6ede66272
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/cap/ConflictResolverFactory.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2014 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.cap;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import java.lang.reflect.Type;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Simple factory for ConflictResolver objects.
+ *
+ * When you have multiple writers there may be multiple versions of the same
+ * object stored in Riak. When fetching all of these will be returned and you
+ * will need to resolve the conflict.
+ *
+ *
+ * To facilitate this, you can store an instance of the {@link ConflictResolver}
+ * in this factory for a class. It will then be used by the {@link com.basho.riak.client.api.commands.kv.FetchValue.Response}
+ * to resolve a set a of siblings to a single object.
+ *
+ *
+ * @author Brian Roach
+ * @since 2.0
+ */
+public enum ConflictResolverFactory
+{
+ INSTANCE;
+
+ private final Map> resolverInstances =
+ new ConcurrentHashMap>();
+
+ /**
+ * Returns the instance of the ConflictResolverFactory.
+ * @return The ConflictResolverFactory
+ */
+ public static ConflictResolverFactory getInstance()
+ {
+ return INSTANCE;
+ }
+
+ public ConflictResolver getConflictResolver(Class clazz)
+ {
+ if (clazz == null)
+ {
+ throw new IllegalArgumentException("clazz cannot be null");
+ }
+ return getConflictResolver(clazz, null);
+ }
+
+ public ConflictResolver getConflictResolver(TypeReference typeReference)
+ {
+ if (typeReference == null)
+ {
+ throw new IllegalArgumentException("typeReference cannot be null");
+ }
+ return getConflictResolver(null, typeReference);
+ }
+
+
+ /**
+ * Return the ConflictResolver for the given class.
+ *
+ * If no ConflictResolver is registered for the provided class, an instance of the
+ * {@link com.basho.riak.client.cap.DefaultResolver} is returned.
+ *
+ * @param The type being resolved
+ * @param clazz the class of the type being resolved
+ * @return The conflict resolver for the type.
+ * @throws UnresolvedConflictException
+ */
+ @SuppressWarnings("unchecked")
+ private ConflictResolver getConflictResolver(Type type, TypeReference typeReference)
+ {
+
+ type = type != null ? type : typeReference.getType();
+
+ ConflictResolver resolver = (ConflictResolver) resolverInstances.get(type);
+ if (resolver == null)
+ {
+ // Cache this?
+ resolver = (ConflictResolver) new DefaultResolver();
+ }
+
+ return resolver;
+
+
+ }
+
+ /**
+ * Register a ConflictResolver.
+ *
+ * The instance provided will be used to resolve siblings for the given type.
+ *
+ *
+ * @param The type being resolved
+ * @param clazz the class of the type being resolved
+ * @param resolver an instance of a class implementing ConflictResolver.
+ */
+ public void registerConflictResolver(Class clazz, ConflictResolver resolver)
+ {
+ resolverInstances.put(clazz, resolver);
+ }
+
+ public void registerConflictResolver(TypeReference typeReference, ConflictResolver resolver)
+ {
+ resolverInstances.put(typeReference.getType(), resolver);
+ }
+
+
+ /**
+ * Unregister a ConflictResolver.
+ * @param The type being Resolved
+ * @param clazz the class of the type being resolved.
+ */
+ public void unregisterConflictResolver(Class clazz)
+ {
+ resolverInstances.remove(clazz);
+ }
+
+ public void unregisterConflictResolver(TypeReference typeReference)
+ {
+ resolverInstances.remove(typeReference.getType());
+ }
+}
diff --git a/src/main/java/com/basho/riak/client/api/cap/DefaultResolver.java b/src/main/java/com/basho/riak/client/api/cap/DefaultResolver.java
new file mode 100644
index 000000000..7015ed0c1
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/cap/DefaultResolver.java
@@ -0,0 +1,60 @@
+/*
+ * 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.cap;
+
+import java.util.List;
+
+/**
+ * A conflict resolver that doesn't resolve conflict.
+ *
+ * This is the default ConflictResolver used if there is not one registered
+ * in the {@link com.basho.riak.client.api.cap.ConflictResolverFactory} for the
+ * class of object being resolved.
+ *
+ *
+ * If it is presented with a
+ * collection of siblings with more than one entry it throws an Exception.
+ *
+ * @author Russell Brown
+ * @since 1.0
+ */
+public class DefaultResolver implements ConflictResolver
+{
+
+ /**
+ * Detects conflict but does not resolve it.
+ *
+ * @param siblings the list of siblings returned from Riak
+ * @return null or the single value in the collection
+ * @throws UnresolvedConflictException if {@code siblings} has > 1 entry.
+ */
+ @Override
+ public T resolve(List siblings) throws UnresolvedConflictException
+ {
+ if (siblings.size() > 1)
+ {
+ throw new UnresolvedConflictException("Siblings found", siblings);
+ }
+ else if (siblings.size() == 1)
+ {
+ return siblings.get(0);
+ }
+ else
+ {
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/com/basho/riak/client/api/cap/Quorum.java b/src/main/java/com/basho/riak/client/api/cap/Quorum.java
new file mode 100644
index 000000000..9cbe3d89f
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/cap/Quorum.java
@@ -0,0 +1,191 @@
+/*
+ * 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.cap;
+
+/**
+ * Encapsulates a r/w/dw/rw/pr/pw quorum.
+ *
+ *
Symbolic Consistency Names
+ * Riak 0.12 introduced “symbolic” consistency options for R and W
+ * that can be easier to use and understand. They are:
+ *
+ *
+ *
+ * all - All replicas must reply. This is the same as setting R or W equal to N.
+ *
+ *
+ * one - This is the same as sending 1 as the R or W value.
+ *
+ *
+ * quorum - A majority of the replicas must respond, that is, “half plus one”.
+ * For the default N value of 3, this calculates to 2.
+ *
+ *
+ * default - Uses whatever the per-bucket consistency property is for R or W,
+ * which may be any of the above values, or an integer.
+ *
+ *
+ *
+ * Static factory methods are provided as a convenience for using these.
+ *
+ * @author Brian Roach
+ * @since 2.0
+ * @see CAP Controls
+ */
+public final class Quorum {
+
+ public static final String ONE = "one";
+ public static final String QUORUM = "quorum";
+ public static final String ALL = "all";
+ public static final String DEFAULT = "default";
+
+ private final int i;
+
+ /**
+ * Construct an instance using an integer value.
+ *
+ * @param i the quorum value
+ */
+ public Quorum(int i) {
+ if (i < -5 || i == -1)
+ {
+ throw new IllegalArgumentException("Illegal value for quorum: " + i);
+ }
+ this.i = i;
+ }
+
+ /**
+ * Static factory method for a quorum of "one"
+ * @return a new Quorum with an integer value of -2
+ */
+ public static Quorum oneQuorum()
+ {
+ return new Quorum(-2);
+ }
+
+ /**
+ * Static factory method for a quorum of "quorum"
+ * @return a new Quorum with an integer value of -3
+ */
+ public static Quorum quorumQuorum()
+ {
+ return new Quorum(-3);
+ }
+
+ /**
+ * Static factory method for a quorum of "all"
+ * @return a new Quorum with an integer value of -4
+ */
+ public static Quorum allQuorum()
+ {
+ return new Quorum(-4);
+ }
+
+ /**
+ * Static factory method for a quorum of "default"
+ * @return a new Quorum with an integer value of -5
+ */
+ public static Quorum defaultQuorum()
+ {
+ return new Quorum(-5);
+ }
+
+ /**
+ * Determine if the quorum has a symbolic value.
+ * @return true if this Quorum represents a symbolic value, false if literal
+ * integer value
+ */
+ public boolean isSymbolic() {
+ switch(i)
+ {
+ case -2: // "one"
+ case -3: // "quorum"
+ case -4: // "all"
+ case -5: // "default"
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * The int value of the quorum. Call isSymbolic to determine if you should
+ * use this.
+ *
+ * @return the int value. Will be a negative number for symbolic values.
+ */
+ public int getIntValue() {
+ return i;
+ }
+
+ @Override
+ public String toString()
+ {
+ String quorum = null;
+ if (i > 0)
+ {
+ quorum = Integer.toString(i);
+ }
+ else
+ {
+
+ switch(i)
+ {
+ case -2:
+ quorum = ONE;
+ break;
+ case -3:
+ quorum = QUORUM;
+ break;
+ case -4:
+ quorum = ALL;
+ break;
+ case -5:
+ quorum = DEFAULT;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return quorum;
+ }
+
+ @Override public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + i;
+ return result;
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof Quorum)) {
+ return false;
+ }
+ Quorum other = (Quorum) obj;
+ if (i != other.i) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/com/basho/riak/client/api/cap/UnresolvedConflictException.java b/src/main/java/com/basho/riak/client/api/cap/UnresolvedConflictException.java
new file mode 100644
index 000000000..1467bbfec
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/cap/UnresolvedConflictException.java
@@ -0,0 +1,72 @@
+/*
+ * 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.cap;
+
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+/**
+ *
+ * Holds the Collection of siblings so they can be presented for further resolution attempts.
+ *
+ * @author Russell Brown
+ * @since 1.0
+ */
+public class UnresolvedConflictException extends ExecutionException {
+
+ /**
+ * eclipse generated id
+ */
+ private static final long serialVersionUID = -219858468775752064L;
+
+ private final List extends Object> siblings;
+
+ /**
+ * For when a list of siblings cannot be whittled down to one.
+ *
+ * @param cause
+ * the exception that broke the camels back
+ * @param message
+ * a String message
+ * @param siblings
+ * the list of siblings
+ */
+ public UnresolvedConflictException(Throwable cause, String message, List extends Object> siblings) {
+ super(message, cause);
+ this.siblings = siblings;
+ }
+
+ /**
+ * For when a collection of siblings cannot be whittled down to one.
+ *
+ * @param message
+ * a String message
+ * @param siblings
+ * the list of siblings
+ */
+ public UnresolvedConflictException(String message, List extends Object> siblings) {
+ super(message);
+ this.siblings = siblings;
+ }
+
+ /**
+ * Get the siblings that could not be resolved.
+ * @return the siblings
+ */
+ public List extends Object> getSiblings() {
+ return siblings;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/basho/riak/client/api/cap/VClock.java b/src/main/java/com/basho/riak/client/api/cap/VClock.java
new file mode 100644
index 000000000..1f10fb127
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/cap/VClock.java
@@ -0,0 +1,37 @@
+/*
+ * 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.cap;
+
+/**
+ * Access the opaque Riak vector clock as either a String or array of bytes.
+ *
+ * @author Russel Brown
+ * @since 1.0
+ */
+public interface VClock
+{
+ /**
+ * Get the bytes that make up this VClock.
+ * @return a copy of this vector clocks bytes
+ */
+ byte[] getBytes();
+
+ /**
+ * Get the string representation of this VClock.
+ * @return a UTF-8 String of this vector clocks bytes
+ */
+ String asString();
+}
diff --git a/src/main/java/com/basho/riak/client/api/cap/package-info.java b/src/main/java/com/basho/riak/client/api/cap/package-info.java
new file mode 100644
index 000000000..4100f40c2
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/cap/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Classes related to eventual consistency in Riak.
+ */
+
+package com.basho.riak.client.api.cap;
\ No newline at end of file
diff --git a/src/main/java/com/basho/riak/client/api/commands/CoreFutureAdapter.java b/src/main/java/com/basho/riak/client/api/commands/CoreFutureAdapter.java
new file mode 100644
index 000000000..ba46b67ca
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/commands/CoreFutureAdapter.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2014 Brian Roach .
+ *
+ * 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;
+
+import com.basho.riak.client.core.RiakFuture;
+import com.basho.riak.client.core.RiakFutureListener;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ *
+ * @author Brian Roach
+ * @since 2.0
+ * @param The core response type.
+ * @param The core query info type.
+ * @param The converted response type.
+ * @param The converted query info type.
+ */
+public abstract class CoreFutureAdapter extends ListenableFuture implements RiakFutureListener
+{
+ private final RiakFuture coreFuture;
+
+ public CoreFutureAdapter(RiakFuture coreFuture)
+ {
+ this.coreFuture = coreFuture;
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning)
+ {
+ return coreFuture.cancel(mayInterruptIfRunning);
+ }
+
+ @Override
+ public T2 get() throws InterruptedException, ExecutionException
+ {
+ return convertResponse(coreFuture.get());
+ }
+
+ @Override
+ public T2 get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
+ {
+ T response = coreFuture.get(timeout, unit);
+ return convertResponse(response);
+ }
+
+ @Override
+ public T2 getNow()
+ {
+ if (coreFuture.isDone())
+ {
+ return convertResponse(coreFuture.getNow());
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean isCancelled()
+ {
+ return coreFuture.isCancelled();
+ }
+
+ @Override
+ public boolean isDone()
+ {
+ return coreFuture.isDone();
+ }
+
+ @Override
+ public void await() throws InterruptedException
+ {
+ coreFuture.await();
+ }
+
+ @Override
+ public void await(long timeout, TimeUnit unit) throws InterruptedException
+ {
+ coreFuture.await(timeout, unit);
+ }
+
+ @Override
+ public boolean isSuccess()
+ {
+ return coreFuture.isSuccess();
+ }
+
+ @Override
+ public Throwable cause()
+ {
+ return coreFuture.cause();
+ }
+
+ @Override
+ public S2 getQueryInfo()
+ {
+ return convertQueryInfo(coreFuture.getQueryInfo());
+ }
+
+ @Override
+ public void handle(RiakFuture f)
+ {
+ notifyListeners();
+ }
+
+ protected abstract T2 convertResponse(T coreResponse);
+ protected abstract S2 convertQueryInfo(S coreQueryInfo);
+}
diff --git a/src/main/java/com/basho/riak/client/api/commands/ListenableFuture.java b/src/main/java/com/basho/riak/client/api/commands/ListenableFuture.java
new file mode 100644
index 000000000..91c088d8b
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/commands/ListenableFuture.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 Brian Roach .
+ *
+ * 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;
+
+import com.basho.riak.client.core.RiakFuture;
+import com.basho.riak.client.core.RiakFutureListener;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * The base class for RiakFutures returned to users.
+ * @author Brian Roach
+ * @since 2.0
+ */
+public abstract class ListenableFuture implements RiakFuture
+{
+ private final Set> listeners =
+ new HashSet>();
+ private final ReentrantLock listenersLock = new ReentrantLock();
+
+ @Override
+ public void addListener(RiakFutureListener listener)
+ {
+ listenersLock.lock();
+ try
+ {
+ if (isDone())
+ {
+ listener.handle(this);
+ }
+ else
+ {
+ listeners.add(listener);
+ }
+ }
+ finally
+ {
+ listenersLock.unlock();
+ }
+ }
+
+ @Override
+ public void removeListener(RiakFutureListener listener)
+ {
+ listenersLock.lock();
+ try
+ {
+ listeners.remove(listener);
+ }
+ finally
+ {
+ listenersLock.unlock();
+ }
+ }
+
+ protected void notifyListeners()
+ {
+ listenersLock.lock();
+ try
+ {
+ for (RiakFutureListener listener : listeners)
+ {
+ listener.handle(this);
+ }
+ }
+ finally
+ {
+ listenersLock.unlock();
+ }
+ }
+}
diff --git a/src/main/java/com/basho/riak/client/api/commands/RiakOption.java b/src/main/java/com/basho/riak/client/api/commands/RiakOption.java
new file mode 100644
index 000000000..6e70d62b5
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/commands/RiakOption.java
@@ -0,0 +1,42 @@
+/*
+ * 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;
+
+ /*
+ * @author Dave Rusek
+ * @since 2.0
+ */
+public class RiakOption
+{
+
+ private final String name;
+
+ public RiakOption(String name)
+ {
+ this.name = name;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ @Override
+ public String toString()
+ {
+ return name;
+ }
+}
diff --git a/src/main/java/com/basho/riak/client/api/commands/buckets/FetchBucketProperties.java b/src/main/java/com/basho/riak/client/api/commands/buckets/FetchBucketProperties.java
new file mode 100644
index 000000000..d9a3c6325
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/commands/buckets/FetchBucketProperties.java
@@ -0,0 +1,113 @@
+/*
+ * 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.buckets;
+
+import com.basho.riak.client.api.RiakCommand;
+import com.basho.riak.client.api.commands.CoreFutureAdapter;
+import com.basho.riak.client.core.RiakCluster;
+import com.basho.riak.client.core.RiakFuture;
+import com.basho.riak.client.core.operations.FetchBucketPropsOperation;
+import com.basho.riak.client.core.query.Namespace;
+
+
+/**
+ * Command used to fetch the properties of a bucket in Riak.
+ *
+ *
+ * Note that this simply returns the core response {@link com.basho.riak.client.core.operations.FetchBucketPropsOperation.Response}
+ *
+ *
+ * @author Dave Rusek
+ * @since 2.0
+ */
+public final class FetchBucketProperties extends RiakCommand
+{
+
+ private final Namespace namespace;
+
+ public FetchBucketProperties(Builder builder)
+ {
+ this.namespace = builder.namespace;
+ }
+
+ @Override
+ protected final RiakFuture executeAsync(RiakCluster cluster)
+ {
+ RiakFuture coreFuture =
+ cluster.execute(buildCoreOperation());
+
+ CoreFutureAdapter future =
+ new CoreFutureAdapter(coreFuture)
+ {
+ @Override
+ protected FetchBucketPropsOperation.Response convertResponse(FetchBucketPropsOperation.Response coreResponse)
+ {
+ return coreResponse;
+ }
+
+ @Override
+ protected Namespace convertQueryInfo(Namespace coreQueryInfo)
+ {
+ return coreQueryInfo;
+ }
+ };
+ coreFuture.addListener(future);
+ return future;
+ }
+
+ private FetchBucketPropsOperation buildCoreOperation()
+ {
+ return new FetchBucketPropsOperation.Builder(namespace).build();
+ }
+
+ /**
+ * Builder used to construct a FetchBucketPoperties command.
+ */
+ public static class Builder
+ {
+ private final Namespace namespace;
+
+ /**
+ * Construct a Builder for a FetchBucketProperties command.
+ * @param namespace The namespace for the bucket.
+ */
+ public Builder(Namespace namespace)
+ {
+ if (namespace == null)
+ {
+ throw new IllegalArgumentException("Namespace cannot be null");
+ }
+ this.namespace = namespace;
+ }
+
+ /**
+ * Construct a new FetchBucketProperties command.
+ * @return a new FetchBucketProperties command.
+ */
+ public FetchBucketProperties build()
+ {
+ return new FetchBucketProperties(this);
+ }
+ }
+
+}
diff --git a/src/main/java/com/basho/riak/client/api/commands/buckets/ListBuckets.java b/src/main/java/com/basho/riak/client/api/commands/buckets/ListBuckets.java
new file mode 100644
index 000000000..522e7052a
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/commands/buckets/ListBuckets.java
@@ -0,0 +1,205 @@
+/*
+ * 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.buckets;
+
+import com.basho.riak.client.api.RiakCommand;
+import com.basho.riak.client.api.commands.CoreFutureAdapter;
+import com.basho.riak.client.core.RiakCluster;
+import com.basho.riak.client.core.RiakFuture;
+import com.basho.riak.client.core.operations.ListBucketsOperation;
+import com.basho.riak.client.core.query.Namespace;
+import com.basho.riak.client.core.util.BinaryValue;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Command used to list the buckets contained in a bucket type.
+ *
+ *
+ *
+ */
+ public static class Response implements Iterable {
+
+ private final BinaryValue type;
+ private final List buckets;
+
+ public Response(BinaryValue type, List buckets)
+ {
+ this.type = type;
+ this.buckets = buckets;
+ }
+
+ @Override
+ public Iterator iterator()
+ {
+ return new Itr(buckets.iterator(), type);
+ }
+ }
+
+ private static class Itr implements Iterator
+ {
+ private final Iterator iterator;
+ private final BinaryValue type;
+
+ private Itr(Iterator iterator, BinaryValue type)
+ {
+ this.iterator = iterator;
+ this.type = type;
+ }
+
+ @Override
+ public boolean hasNext()
+ {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public Namespace next()
+ {
+ BinaryValue bucket = iterator.next();
+ return new Namespace(type, bucket);
+ }
+
+ @Override
+ public void remove()
+ {
+ iterator.remove();
+ }
+ }
+
+ /**
+ * Builder for a ListBuckets command.
+ */
+ public static class Builder
+ {
+ private int timeout;
+ private final BinaryValue type;
+
+ /**
+ * Construct a Builder for a ListBuckets command.
+ * @param type the bucket type.
+ */
+ public Builder(String type)
+ {
+ this.type = BinaryValue.create(type);
+ }
+
+ /**
+ * Construct a Builder for a ListBuckets command.
+ * @param type the bucket type.
+ */
+ public Builder(BinaryValue type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * 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)
+ {
+ this.timeout = timeout;
+ return this;
+ }
+
+ /**
+ * Construct a new ListBuckets command.
+ * @return a new ListBuckets command.
+ */
+ public ListBuckets build()
+ {
+ return new ListBuckets(this);
+ }
+ }
+
+}
diff --git a/src/main/java/com/basho/riak/client/api/commands/buckets/StoreBucketProperties.java b/src/main/java/com/basho/riak/client/api/commands/buckets/StoreBucketProperties.java
new file mode 100644
index 000000000..aa4631d64
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/commands/buckets/StoreBucketProperties.java
@@ -0,0 +1,593 @@
+/*
+ * 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.buckets;
+
+import com.basho.riak.client.api.RiakCommand;
+import com.basho.riak.client.api.commands.CoreFutureAdapter;
+import com.basho.riak.client.core.RiakCluster;
+import com.basho.riak.client.core.RiakFuture;
+import com.basho.riak.client.core.operations.StoreBucketPropsOperation;
+import com.basho.riak.client.core.query.Namespace;
+import com.basho.riak.client.core.query.functions.Function;
+
+
+/**
+ * Command used to store (modify) the properties of a bucket in Riak.
+ *
+ */
+package com.basho.riak.client.api.commands.buckets;
\ No newline at end of file
diff --git a/src/main/java/com/basho/riak/client/api/commands/datatypes/Context.java b/src/main/java/com/basho/riak/client/api/commands/datatypes/Context.java
new file mode 100644
index 000000000..02d3113d1
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/commands/datatypes/Context.java
@@ -0,0 +1,51 @@
+/*
+ * 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.datatypes;
+
+import com.basho.riak.client.core.util.BinaryValue;
+
+ /**
+ * Encapsulates a context returned from a datatype fetch command.
+ *
+ * When performing an update to a datatype, the Context from the
+ * previous fetch is used.
+ *
+ * @author Dave Rusek
+ * @since 2.0
+ */
+public class Context
+{
+
+ private final BinaryValue value;
+
+ /**
+ * Construct a new Context.
+ * @param value
+ */
+ public Context(BinaryValue value)
+ {
+ this.value = value;
+ }
+
+ /**
+ * Returns the context as a BinaryValue.
+ * @return the context.
+ */
+ public BinaryValue getValue()
+ {
+ return value;
+ }
+}
diff --git a/src/main/java/com/basho/riak/client/api/commands/datatypes/CounterUpdate.java b/src/main/java/com/basho/riak/client/api/commands/datatypes/CounterUpdate.java
new file mode 100644
index 000000000..77a06dbe0
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/commands/datatypes/CounterUpdate.java
@@ -0,0 +1,70 @@
+/*
+ * 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.datatypes;
+
+import com.basho.riak.client.core.query.crdt.ops.CounterOp;
+
+/**
+ * An update to a Riak counter datatype.
+ *
+ * When building a {@link UpdateCounter} or {@link UpdateMap} command
+ * this class is used to encapsulate the update to be performed on a
+ * Riak counter datatype.
+ *
+ * @author Dave Rusek
+ * @since 2.0
+ */
+public class CounterUpdate implements DatatypeUpdate
+{
+
+ private long delta = 0;
+
+ /**
+ * Constructs a CounterUpdate with a delta of 0 (zero).
+ */
+ public CounterUpdate()
+ {
+ }
+
+ /**
+ * Constructs a CounterUpdate with the supplied delta.
+ *
To decrease a counter supply a negative value.
+ * @param delta the value to add to the counter in Riak.
+ */
+ public CounterUpdate(long delta)
+ {
+ this.delta = delta;
+ }
+
+ /**
+ * Get the delta.
+ * @return the value contained in this CounterUpdate.
+ */
+ public long getDelta()
+ {
+ return delta;
+ }
+
+ /**
+ * Returns the core update.
+ * @return the update used by the client core.
+ */
+ @Override
+ public CounterOp getOp()
+ {
+ return new CounterOp(delta);
+ }
+}
diff --git a/src/main/java/com/basho/riak/client/api/commands/datatypes/DatatypeUpdate.java b/src/main/java/com/basho/riak/client/api/commands/datatypes/DatatypeUpdate.java
new file mode 100644
index 000000000..d271a6683
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/commands/datatypes/DatatypeUpdate.java
@@ -0,0 +1,29 @@
+/*
+ * 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.datatypes;
+
+import com.basho.riak.client.core.query.crdt.ops.CrdtOp;
+
+/**
+ * An object that represents an update to a Riak datatype.
+ * @author Dave Rusek
+ * @since 2.0
+ */
+public interface DatatypeUpdate
+{
+ public abstract CrdtOp getOp();
+
+}
diff --git a/src/main/java/com/basho/riak/client/api/commands/datatypes/FetchCounter.java b/src/main/java/com/basho/riak/client/api/commands/datatypes/FetchCounter.java
new file mode 100644
index 000000000..65e14d6d3
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/commands/datatypes/FetchCounter.java
@@ -0,0 +1,142 @@
+/*
+ * 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.datatypes;
+
+import com.basho.riak.client.api.commands.CoreFutureAdapter;
+import com.basho.riak.client.core.RiakCluster;
+import com.basho.riak.client.core.RiakFuture;
+import com.basho.riak.client.core.operations.DtFetchOperation;
+import com.basho.riak.client.core.query.Location;
+import com.basho.riak.client.core.query.crdt.types.RiakCounter;
+import com.basho.riak.client.core.query.crdt.types.RiakDatatype;
+
+ /**
+ * Command used to fetch a counter datatype from Riak.
+ *
+ *
+ *
+ * {@code
+ * Namespace ns = new Namespace("my_type", "my_bucket");
+ * Location loc = new Location(ns, "my_key");
+ * FetchCounter fc = new FetchCounter.Builder(loc).build();
+ * FetchCounter.Response resp = client.execute(fc);
+ * Long counter = resp.getDatatype().view();}
+ *
+ * @author Dave Rusek
+ * @author Brian Roach
+ * @since 2.0
+ */
+public final class FetchCounter extends FetchDatatype
+{
+ private FetchCounter(Builder builder)
+ {
+ super(builder);
+ }
+
+ @Override
+ protected final RiakFuture executeAsync(RiakCluster cluster)
+ {
+ RiakFuture coreFuture =
+ cluster.execute(buildCoreOperation());
+
+ CoreFutureAdapter future =
+ new CoreFutureAdapter(coreFuture) {
+
+ @Override
+ protected FetchCounter.Response convertResponse(DtFetchOperation.Response coreResponse)
+ {
+ RiakDatatype element = coreResponse.getCrdtElement();
+
+ Context context = null;
+ if (coreResponse.hasContext())
+ {
+ context = new Context(coreResponse.getContext());
+ }
+
+ RiakCounter datatype = extractDatatype(element);
+
+ return new Response(datatype, context);
+ }
+
+ @Override
+ protected Location convertQueryInfo(Location coreQueryInfo)
+ {
+ return coreQueryInfo;
+ }
+ };
+ coreFuture.addListener(future);
+ return future;
+ }
+
+ @Override
+ public RiakCounter extractDatatype(RiakDatatype element)
+ {
+ return element.getAsCounter();
+ }
+
+ /**
+ * Builder used to construct a FetchCounter command.
+ */
+ public static class Builder extends FetchDatatype.Builder
+ {
+
+ /**
+ * Construct a builder for a FetchCounter command.
+ * @param location the location of the counter in Riak.
+ */
+ public Builder(Location location)
+ {
+ super(location);
+ }
+
+ @Override
+ protected Builder self()
+ {
+ return this;
+ }
+
+ /**
+ * Build a FetchCounter command.
+ * @return a new FetchCounter command.
+ */
+ public FetchCounter build()
+ {
+ return new FetchCounter(this);
+ }
+ }
+
+ /**
+ * Response from a FetchCounter command.
+ *
+ * Encapsulates a RiakCounter returned from the command.
+ *
+ *
+ */
+ public static class Response extends FetchDatatype.Response
+ {
+ Response(RiakCounter counter, Context context)
+ {
+ super(counter, context);
+ }
+ }
+}
diff --git a/src/main/java/com/basho/riak/client/api/commands/datatypes/FetchDatatype.java b/src/main/java/com/basho/riak/client/api/commands/datatypes/FetchDatatype.java
new file mode 100644
index 000000000..abee25ad1
--- /dev/null
+++ b/src/main/java/com/basho/riak/client/api/commands/datatypes/FetchDatatype.java
@@ -0,0 +1,256 @@
+/*
+ * 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.datatypes;
+
+import com.basho.riak.client.api.RiakCommand;
+import com.basho.riak.client.api.cap.Quorum;
+import com.basho.riak.client.api.commands.RiakOption;
+import com.basho.riak.client.core.operations.DtFetchOperation;
+
+import com.basho.riak.client.core.query.Location;
+import com.basho.riak.client.core.query.crdt.types.RiakDatatype;
+
+import java.util.HashMap;
+import java.util.Map;
+
+ /**
+ * Base abstract class for all CRDT fetch commands.
+ * @author Dave Rusek
+ * @since 2.0
+ */
+public abstract class FetchDatatype extends RiakCommand
+{
+
+ private final Location location;
+ private final Map