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 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> 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: + +![RJC model](http://brianroach.info/blog/wp-content/uploads/2013/10/RJC2.png) + +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); +``` + +

RiakCommand subclasses

+

Fetching, storing and deleting objects

+ +

Listing keys in a namespace

+ +

Secondary index (2i) commands

+ +

Fetching and storing datatypes (CRDTs)

+ +

Querying and modifying buckets

+ +

Search commands

+ +

Map-Reduce

+ diff --git a/THANKS b/THANKS deleted file mode 100644 index efdd9d950..000000000 --- a/THANKS +++ /dev/null @@ -1,9 +0,0 @@ -The following people have contributed to riak-java-client: - -Jonathan Lee -Benjamin Schmaus -Kevin Smith -Bryan Fink -Dr Kresten Krab -Russell Brown -Erik Soe Sorensen diff --git a/TODO b/TODO deleted file mode 100644 index ed253d69f..000000000 --- a/TODO +++ /dev/null @@ -1,2 +0,0 @@ -Add ?links=false param to listBucket -Stream raw siblings diff --git a/buildbot/Makefile b/buildbot/Makefile new file mode 100644 index 000000000..3d1cbb2bc --- /dev/null +++ b/buildbot/Makefile @@ -0,0 +1,43 @@ +RIAK_CONF = ${RIAK_DIR}/etc/riak.conf +ADVANCED_CONF = ${RIAK_DIR}/etc/advanced.config +# RIAK = ${RIAK_DIR}/bin/riak +RIAK_ADMIN = ${RIAK_DIR}/bin/riak-admin +CERTS_DIR = $(shell pwd)/../src/test/resources + +preconfigure: + echo "storage_backend = leveldb" >> ${RIAK_CONF} + echo "search = on" >> ${RIAK_CONF} + echo "listener.protobuf.internal = 127.0.0.1:8087" >> ${RIAK_CONF} + echo "ssl.keyfile = ${CERTS_DIR}/key.pem" >> ${RIAK_CONF} + echo "ssl.certfile = ${CERTS_DIR}/cert.pem" >> ${RIAK_CONF} + echo "ssl.cacertfile = ${CERTS_DIR}/cacert.pem" >> ${RIAK_CONF} + +configure: + ${RIAK_ADMIN} bucket-type create jvtest_yz_search '{"props":{}}' + ${RIAK_ADMIN} bucket-type create jvtest_test_type '{"props":{}}' + ${RIAK_ADMIN} bucket-type create jvtest_counters '{"props":{"datatype":"counter"}}' + ${RIAK_ADMIN} bucket-type create jvtest_sets '{"props":{"datatype":"set"}}' + ${RIAK_ADMIN} bucket-type create jvtest_maps '{"props":{"datatype":"map"}}' + ${RIAK_ADMIN} bucket-type activate jvtest_yz_search + ${RIAK_ADMIN} bucket-type activate jvtest_test_type + ${RIAK_ADMIN} bucket-type activate jvtest_counters + ${RIAK_ADMIN} bucket-type activate jvtest_sets + ${RIAK_ADMIN} bucket-type activate jvtest_maps + +compile: + @cd ..; mvn clean compile + +lint: + @echo "No lint task" + +test: test-normal test-security + +test-normal: + @cd ..; mvn -Pitest,default -DargLine="-Dcom.basho.riak.2i=true -Dcom.basho.riak.yokozuna=true -Dcom.basho.riak.buckettype=true -Dcom.basho.riak.crdt=true" verify + +test-security: + ${RIAK_ADMIN} security add-user tester password=tester + ${RIAK_ADMIN} security add-source tester 127.0.0.1/32 password + ${RIAK_ADMIN} security grant riak_kv.get,riak_kv.put,riak_kv.delete,riak_kv.index,riak_kv.list_keys,riak_kv.list_buckets,riak_core.get_bucket,riak_core.set_bucket,riak_core.get_bucket_type,riak_core.set_bucket_type,search.admin,search.query,riak_kv.mapreduce on any to tester + ${RIAK_ADMIN} security enable + @cd ..; mvn -Pitest -DargLine="-Dcom.basho.riak.security=true" verify diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml new file mode 100644 index 000000000..dc1e6d830 --- /dev/null +++ b/dependency-reduced-pom.xml @@ -0,0 +1,370 @@ + + + 4.0.0 + com.basho.riak + riak-client + Riak Client for Java + 2.0.1 + Java client for Riak 2.0 + https://github.com/basho/riak-java-client + + + Brian Roach + roach@basho.com + Basho Technologies, Inc + http://www.basho.com + + + David Rusek + drusek@basho.com + Basho Technologies, Inc + http://www.basho.com + + + Alex Moore + amoore@basho.com + Basho Technologies, Inc + http://www.basho.com + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + A business-friendly OSS license + + + + scm:git:ssh://git@github.com/basho/riak-java-client.git + scm:git:ssh://git@github.com/basho/riak-java-client.git + http://github.com/basho/riak-java-client + + + + + maven-shade-plugin + 2.1 + + + package + + shade + + + false + true + + + com.google.protobuf:protobuf-java + com.basho.riak.protobuf:riak-pb + + + + + com.google.protobuf + shaded.com.google.protobuf + + + com.basho.riak.protobuf + shaded.com.bash.riak.protobuf + + + + + + + + maven-release-plugin + 2.1 + + + org.jacoco + jacoco-maven-plugin + 0.6.3.201306030806 + + + jacoco-initialize + + prepare-agent + + + + jacoco-site + package + + report + + + + + + maven-compiler-plugin + 3.1 + + 1.6 + 1.6 + -Xlint:unchecked + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + 1.9 + + + test + + check + + + + + + org.codehaus.mojo.signature + java16 + 1.1 + + + + + maven-surefire-plugin + 2.14 + + + debug + true + yyyy-MM-dd HH:mm:ss + + + + + maven-javadoc-plugin + 2.9.1 + + + + com.basho.riak.client.javadoc.RiakThreadSafetyTaglet + + com.basho.riak + riak-client + 2.0.1 + + + + com.basho.riak.client.javadoc.JavadocFilter + + com.basho.riak + riak-client + 2.0.1 + + + + + + + + + + org.slf4j + slf4j-nop + 1.7.5 + test + + + + + test-debug-logging + + + org.slf4j + slf4j-simple + 1.7.5 + test + + + + + itest + + + + maven-failsafe-plugin + 2.16 + + + + integration-test + verify + + + + + + + + + jar-with-dependencies + + + + maven-assembly-plugin + 2.4 + + + make-assembly + package + + single + + + + + + src/main/assembly/jar-with-dependencies.xml + + + + + + + + + release + + + + maven-gpg-plugin + 1.4 + + + sign-artifacts + verify + + sign + + + + + + + + + + + com.sun + tools + 1.4.2 + system + /Library/Java/JavaVirtualMachines/jdk1.7.0_25.jdk/Contents/Home/jre/../lib/tools.jar + + + org.slf4j + slf4j-api + 1.7.2 + compile + + + org.mockito + mockito-core + 1.9.5 + test + + + objenesis + org.objenesis + + + + + org.powermock + powermock-api-mockito + 1.5 + test + + + mockito-all + org.mockito + + + powermock-api-support + org.powermock + + + + + org.powermock + powermock-module-junit4 + 1.5 + test + + + powermock-module-junit4-common + org.powermock + + + + + com.jayway.awaitility + awaitility + 1.3.5 + test + + + hamcrest-library + org.hamcrest + + + cglib-nodep + cglib + + + objenesis + org.objenesis + + + + + com.fasterxml.jackson.core + jackson-databind + 2.2.2 + compile + + + com.fasterxml.jackson.datatype + jackson-datatype-joda + 2.2.2 + compile + + + io.netty + netty-all + 4.0.17.Final + compile + + + org.slf4j + slf4j-nop + 1.7.5 + test + + + + + sonatype-nexus-staging + Nexus Release Repository + https://oss.sonatype.org/service/local/staging/deploy/maven2 + + + sonatype-nexus-snapshots + Sonatype Nexus Snapshots + https://oss.sonatype.org/content/repositories/snapshots + + + + UTF-8 + 1.5 + UTF-8 + + + diff --git a/eclipse-cleanup-profile.xml b/eclipse-cleanup-profile.xml deleted file mode 100644 index 4982e7377..000000000 --- a/eclipse-cleanup-profile.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/eclipse-format-profile.xml b/eclipse-format-profile.xml deleted file mode 100644 index 039fc8ac0..000000000 --- a/eclipse-format-profile.xml +++ /dev/null @@ -1,269 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/commons-codec-1.2.jar b/lib/commons-codec-1.2.jar deleted file mode 100644 index 67cb720f5..000000000 Binary files a/lib/commons-codec-1.2.jar and /dev/null differ diff --git a/lib/commons-httpclient-3.1.jar b/lib/commons-httpclient-3.1.jar deleted file mode 100644 index 7c59774ae..000000000 Binary files a/lib/commons-httpclient-3.1.jar and /dev/null differ diff --git a/lib/commons-logging-1.0.4.jar b/lib/commons-logging-1.0.4.jar deleted file mode 100644 index b73a80fab..000000000 Binary files a/lib/commons-logging-1.0.4.jar and /dev/null differ diff --git a/lib/junit-4.4.jar b/lib/junit-4.4.jar deleted file mode 100644 index 649b0b327..000000000 Binary files a/lib/junit-4.4.jar and /dev/null differ diff --git a/lib/mockito-all-1.8.0.jar b/lib/mockito-all-1.8.0.jar deleted file mode 100644 index 3b336bf76..000000000 Binary files a/lib/mockito-all-1.8.0.jar and /dev/null differ diff --git a/pom.xml b/pom.xml old mode 100644 new mode 100755 index 4a91f9454..166f3644d --- a/pom.xml +++ b/pom.xml @@ -1,190 +1,363 @@ - - - 4.0.0 - com.basho.riak - riak-client - jar - 0.14.2-SNAPSHOT - Riak Client for Java - HttpClient-based client for Riak - https://github.com/basho/riak-java-client - - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - - + + 4.0.0 + com.basho.riak + riak-client + jar + 2.0.1 + Riak Client for Java + Java client for Riak 2.0 + https://github.com/basho/riak-java-client + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + A business-friendly OSS license + + - Jonathan Lee - jonjlee@gmail.com + Brian Roach + roach@basho.com + Basho Technologies, Inc + http://www.basho.com - - Andy Gross - andy@basho.com - - - Dave Smith - dizzyd@basho.com + + David Rusek + drusek@basho.com + Basho Technologies, Inc + http://www.basho.com - - Russell Brown - russelldb@basho.com + + Alex Moore + amoore@basho.com + Basho Technologies, Inc + http://www.basho.com + + + scm:git:ssh://git@github.com/basho/riak-java-client.git + scm:git:ssh://git@github.com/basho/riak-java-client.git + http://github.com/basho/riak-java-client + - - http://github.org/basho/riak-java-client - scm:git:git://github.com/basho/riak-java-client - - - - 2.3.0 + UTF-8 - 2.4 + UTF-8 + 1.5 - - - - sonatype-nexus-snapshots - Sonatype Nexus Snapshots - http://oss.sonatype.org/content/repositories/snapshots - - - sonatype-nexus-staging - Nexus Release Repository - http://oss.sonatype.org/service/local/staging/deploy/maven2 - - - - - - - commons-httpclient - commons-httpclient - 3.1 - - + + + + sonatype-nexus-snapshots + Sonatype Nexus Snapshots + https://oss.sonatype.org/content/repositories/snapshots + + + sonatype-nexus-staging + Nexus Release Repository + https://oss.sonatype.org/service/local/staging/deploy/maven2 + + + + + + default + + true + + - com.google.protobuf - protobuf-java - 2.3.0 + org.slf4j + slf4j-nop + 1.7.5 + test - - - org.mockito - mockito-all - 1.8.0 - test - - - junit - junit - 4.4 - test - - - - - - - maven-compiler-plugin - - 1.5 - 1.5 - - - - org.codehaus.mojo - cobertura-maven-plugin - ${cobertura.version} - - - - org/json/*.class - com/basho/riak/pbc/RPB* - - - - - - - clean - - - - - - maven-release-plugin - 2.1 - - - - - - - itest - - - - - org.codehaus.mojo - failsafe-maven-plugin - 2.4.3-alpha-1 - - - - integration-test - verify - - - - - - - - - release - - - performRelease - true - - - - - - org.apache.maven.plugins - maven-gpg-plugin - - - sign-artifacts - verify - - sign - - - - - - - - - - - - - - org.codehaus.mojo - cobertura-maven-plugin - ${cobertura.version} - - - + + + + test-debug-logging + + + org.slf4j + slf4j-simple + 1.7.5 + test + + + + + itest + + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.16 + + + + integration-test + verify + + + + + + + + + jar-with-dependencies + + + bundle.fat + true + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.4 + + + src/main/assembly/jar-with-dependencies.xml + + + + + + + make-assembly + package + + single + + + + + + + + + release + + + performRelease + true + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.4 + + + sign-artifacts + verify + + sign + + + + + + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 2.1 + + + package + + shade + + + false + true + + + com.google.protobuf:protobuf-java + com.basho.riak.protobuf:riak-pb + + + + + com.google.protobuf + shaded.com.google.protobuf + + + com.basho.riak.protobuf + shaded.com.bash.riak.protobuf + + + + + + + + maven-release-plugin + 2.1 + + + org.jacoco + jacoco-maven-plugin + 0.6.3.201306030806 + + + jacoco-initialize + + prepare-agent + + + + jacoco-site + package + + report + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.6 + 1.6 + -Xlint:unchecked + + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + 1.9 + + + org.codehaus.mojo.signature + java16 + 1.1 + + + + + test + + check + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.14 + + + debug + true + yyyy-MM-dd HH:mm:ss + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + + com.basho.riak.client.javadoc.RiakThreadSafetyTaglet + + com.basho.riak + riak-client + 2.0.1 + + + + com.basho.riak.client.javadoc.JavadocFilter + + com.basho.riak + riak-client + 2.0.1 + + + + + + + + com.sun + tools + 1.4.2 + system + ${java.home}/../lib/tools.jar + + + org.slf4j + slf4j-api + 1.7.2 + + + org.mockito + mockito-core + 1.9.5 + test + + + org.powermock + powermock-api-mockito + ${powermock.version} + test + + + org.powermock + powermock-module-junit4 + ${powermock.version} + test + + + com.jayway.awaitility + awaitility + 1.3.5 + test + + + com.fasterxml.jackson.core + jackson-databind + 2.2.2 + + + com.fasterxml.jackson.datatype + jackson-datatype-joda + 2.2.2 + jar + + + io.netty + netty-all + 4.0.17.Final + + + com.basho.riak.protobuf + riak-pb + 2.0.0.16 + + diff --git a/src/integration/java/com/basho/riak/client/integ/StoreFetchDeleteTest.java b/src/integration/java/com/basho/riak/client/integ/StoreFetchDeleteTest.java new file mode 100644 index 000000000..098b940bb --- /dev/null +++ b/src/integration/java/com/basho/riak/client/integ/StoreFetchDeleteTest.java @@ -0,0 +1,134 @@ +/* + * 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.integ; + +import com.basho.riak.client.FetchMeta; +import com.basho.riak.client.StoreMeta; +import com.basho.riak.client.convert.Converter; +import com.basho.riak.client.convert.PassThroughConverter; +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.RiakNode; +import com.basho.riak.client.core.operations.DeleteOperation; +import com.basho.riak.client.core.operations.FetchOperation; +import com.basho.riak.client.core.operations.StoreOperation; +import com.basho.riak.client.query.RiakObject; +import com.basho.riak.client.util.BinaryValue; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.net.UnknownHostException; +import java.util.concurrent.ExecutionException; + +public class StoreFetchDeleteTest +{ + + private final Converter domainObjectConverter = new PassThroughConverter(); + private static RiakCluster cluster; + private BinaryValue bucket = BinaryValue.create("bucket"); + + @BeforeClass + public static void setup() throws UnknownHostException + { + RiakNode node = new RiakNode.Builder() + .withRemoteAddress("localhost") + .withRemotePort(8087) + .build(); + + cluster = RiakCluster.builder(node).build(); + cluster.start(); + } + + @AfterClass + public static void teardown() + { + cluster.stop(); + } + + @Test + public void testStoreFetchDelete() throws ExecutionException, InterruptedException + { + + RiakObject o = RiakObject.create(bucket.unsafeGetValue()).setValue("test value"); + StoreMeta storeMeta = new StoreMeta.Builder().returnBody(true).build(); + + StoreOperation store = + new StoreOperation(bucket, o) + .withConverter(domainObjectConverter) + .withStoreMeta(storeMeta); + + cluster.execute(store); + + RiakObject storeReturn = store.get(); + + BinaryValue returnedKey = BinaryValue.create(storeReturn.getBucketAsBytes()); + FetchOperation fetch = + new FetchOperation(bucket, returnedKey) + .withConverter(domainObjectConverter); + + cluster.execute(fetch); + + RiakObject fetchReturn = fetch.get(); + + DeleteOperation delete = new DeleteOperation(bucket, returnedKey); + + cluster.execute(delete); + + delete.get(); + + FetchOperation tombstoneFetch = + new FetchOperation(bucket, returnedKey) + .withConverter(domainObjectConverter); + + cluster.execute(tombstoneFetch); + + RiakObject tombstone = tombstoneFetch.get(); + + Assert.assertTrue(tombstone.isNotFound()); + + } + + @Test + public void testSiblings() throws ExecutionException, InterruptedException + { + + RiakObject o = RiakObject.create(bucket.unsafeGetValue()).setValue("test value"); + StoreMeta storeMeta = new StoreMeta.Builder().returnBody(true).build(); + + StoreOperation store1 = + new StoreOperation(bucket, o) + .withConverter(domainObjectConverter) + .withStoreMeta(storeMeta); + + cluster.execute(store1); + + RiakObject storeReturn1 = store1.get(); + + BinaryValue key = BinaryValue.create(storeReturn1.getKeyAsBytes()); + StoreOperation store2 = + new StoreOperation(bucket, key, o) + .withConverter(domainObjectConverter) + .withStoreMeta(storeMeta); + + cluster.execute(store2); + + RiakObject storeReturn2 = store2.get(); + + + } + +} diff --git a/src/main/assembly/jar-with-dependencies.xml b/src/main/assembly/jar-with-dependencies.xml new file mode 100644 index 000000000..4407782da --- /dev/null +++ b/src/main/assembly/jar-with-dependencies.xml @@ -0,0 +1,17 @@ + + jar-with-dependencies + + jar + + false + + + true + runtime + / + + junit:junit + + + + \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/RiakBucketInfo.java b/src/main/java/com/basho/riak/client/RiakBucketInfo.java deleted file mode 100644 index 30f6f4efd..000000000 --- a/src/main/java/com/basho/riak/client/RiakBucketInfo.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 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; - -import java.util.ArrayList; -import java.util.Collection; - -import org.json.JSONException; -import org.json.JSONObject; - -import com.basho.riak.client.util.Constants; - -/** - * Represents the metadata stored in a bucket including its schema and the list - * of keys contained in the bucket. - */ -public class RiakBucketInfo { - - private JSONObject schema; - private Collection keys; - - /** - * Returns the bucket's properties. - */ - public JSONObject getSchema() { - return schema; - } - - /** - * The object keys in this bucket. - */ - public Collection getKeys() { - return keys; - } - - /** - * Construct a bucket info to populate for a writeSchema request. - */ - public RiakBucketInfo() { - this(null, null); - } - - /** - * Construct a bucket info using the JSON data from a listBucket() response. - * - * @param schema - * The JSON object containing the bucket's schema - * @param keys - * The keys in the bucket - */ - public RiakBucketInfo(JSONObject schema, Collection keys) { - - if (schema != null) { - this.schema = schema; - } else { - this.schema = new JSONObject(); - } - if (keys != null) { - this.keys = keys; - } else { - this.keys = new ArrayList(); - } - } - - /** - * Allow siblings to be returned for an object. If false, last write wins. - */ - public void setAllowMult(boolean allowMult) { - try { - getSchema().put(Constants.FL_SCHEMA_ALLOW_MULT, allowMult); - } catch (JSONException unreached) { - throw new IllegalStateException("operation is valid json", unreached); - } - } - - public Boolean getAllowMult() { - return getSchema().optBoolean(Constants.FL_SCHEMA_ALLOW_MULT); - } - - /** - * Number of replicas per object in this bucket. - */ - public void setNVal(int n) { - try { - getSchema().put(Constants.FL_SCHEMA_NVAL, n); - } catch (JSONException unreached) { - throw new IllegalStateException("operation is valid json", unreached); - } - } - - public Integer getNVal() { - return getSchema().optInt(Constants.FL_SCHEMA_NVAL); - } - - /** - * Erlang module and name of the function to use to hash object keys. See - * Riak's documentation. - */ - public void setCHashFun(String mod, String fun) { - if (mod == null) { - mod = ""; - } - if (fun == null) { - fun = ""; - } - try { - JSONObject chashfun = new JSONObject(); - chashfun.put(Constants.FL_SCHEMA_CHASHFUN_MOD, mod); - chashfun.put(Constants.FL_SCHEMA_CHASHFUN_FUN, fun); - getSchema().put(Constants.FL_SCHEMA_CHASHFUN, chashfun); - } catch (JSONException unreached) { - throw new IllegalStateException("operation is valid json", unreached); - } - } - - /** - * The chash_keyfun property as {@literal :} - */ - public String getCHashFun() { - JSONObject chashfun = getSchema().optJSONObject(Constants.FL_SCHEMA_CHASHFUN); - if (chashfun == null) - return null; - String mod = chashfun.optString(Constants.FL_SCHEMA_CHASHFUN_MOD, ""); - String fun = chashfun.optString(Constants.FL_SCHEMA_CHASHFUN_FUN, ""); - return mod + ":" + fun; - } - - /** - * Erlang module and name of the function to use to walk object links. See - * Riak's documentation. - */ - public void setLinkFun(String mod, String fun) { - if (mod == null) { - mod = ""; - } - if (fun == null) { - fun = ""; - } - try { - JSONObject linkfun = new JSONObject(); - linkfun.put(Constants.FL_SCHEMA_LINKFUN_MOD, mod); - linkfun.put(Constants.FL_SCHEMA_LINKFUN_FUN, fun); - getSchema().put(Constants.FL_SCHEMA_LINKFUN, linkfun); - } catch (JSONException unreached) { - throw new IllegalStateException("operation is valid json", unreached); - } - } - - /** - * The linkfun property as {@literal :} - */ - public String getLinkFun() { - JSONObject linkfun = getSchema().optJSONObject(Constants.FL_SCHEMA_LINKFUN); - if (linkfun == null) - return null; - String mod = linkfun.optString(Constants.FL_SCHEMA_LINKFUN_MOD, ""); - String fun = linkfun.optString(Constants.FL_SCHEMA_LINKFUN_FUN, ""); - return mod + ":" + fun; - } -} diff --git a/src/main/java/com/basho/riak/client/RiakClient.java b/src/main/java/com/basho/riak/client/RiakClient.java deleted file mode 100644 index 6ee5ad790..000000000 --- a/src/main/java/com/basho/riak/client/RiakClient.java +++ /dev/null @@ -1,577 +0,0 @@ -/* - * 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; - -import java.io.IOException; -import java.util.Map; -import java.util.Set; - -import org.apache.commons.httpclient.HttpClient; -import org.json.JSONException; -import org.json.JSONObject; - -import com.basho.riak.client.request.MapReduceBuilder; -import com.basho.riak.client.request.RequestMeta; -import com.basho.riak.client.request.RiakWalkSpec; -import com.basho.riak.client.response.BucketResponse; -import com.basho.riak.client.response.FetchResponse; -import com.basho.riak.client.response.HttpResponse; -import com.basho.riak.client.response.MapReduceResponse; -import com.basho.riak.client.response.RiakExceptionHandler; -import com.basho.riak.client.response.RiakIORuntimeException; -import com.basho.riak.client.response.RiakResponseRuntimeException; -import com.basho.riak.client.response.StoreResponse; -import com.basho.riak.client.response.StreamHandler; -import com.basho.riak.client.response.WalkResponse; -import com.basho.riak.client.util.ClientHelper; -import com.basho.riak.client.util.ClientUtils; -import com.basho.riak.client.util.Constants; - -/** - * Primary interface for interacting with Riak via HTTP. - */ -public class RiakClient { - - private ClientHelper helper; - - public RiakConfig getConfig() { - return helper.getConfig(); - } - - public RiakClient(RiakConfig config) { - this(config, null); - } - - public RiakClient(RiakConfig config, String clientId) { - helper = new ClientHelper(config, clientId); - } - - public RiakClient(String url) { - this(new RiakConfig(url), null); - } - - public RiakClient(String url, String clientId) { - this(new RiakConfig(url), clientId); - } - - // Package protected constructor used for testing - RiakClient(ClientHelper helper) { - this.helper = helper; - } - - /** - * Set the properties for a Riak bucket. - * - * @param bucket - * The bucket name. - * @param bucketInfo - * Contains the schema to use for the bucket. Refer to the Riak - * documentation for a list of the recognized properties and the - * format of their values. - * @param meta - * Extra metadata to attach to the request such as HTTP headers - * and query parameters. - * - * @return {@link HttpResponse} containing HTTP response information. - * - * @throws IllegalArgumentException - * If the provided schema values cannot be serialized to send to - * Riak. - * @throws RiakIORuntimeException - * If an error occurs during communication with the Riak server. - */ - public HttpResponse setBucketSchema(String bucket, RiakBucketInfo bucketInfo, RequestMeta meta) { - JSONObject schema = null; - try { - schema = new JSONObject().put(Constants.FL_SCHEMA, bucketInfo.getSchema()); - } catch (JSONException unreached) { - throw new IllegalStateException("wrapping valid json should be valid", unreached); - } - - return helper.setBucketSchema(bucket, schema, meta); - } - - public HttpResponse setBucketSchema(String bucket, RiakBucketInfo bucketInfo) { - return setBucketSchema(bucket, bucketInfo, null); - } - - /** - * Return the properties for a Riak bucket without listing the keys in it. - * - * @param bucket - * The target bucket. - * @param meta - * Extra metadata to attach to the request such as HTTP headers - * and query parameters. - * - * @return {@link BucketResponse} containing HTTP response information and - * the parsed schema - * - * @throws RiakIORuntimeException - * If an error occurs during communication with the Riak server. - * @throws RiakResponseRuntimeException - * If the Riak server returns a malformed response. - */ - public BucketResponse getBucketSchema(String bucket, RequestMeta meta) { - HttpResponse r = helper.getBucketSchema(bucket, meta); - try { - return getBucketResponse(r); - } catch (JSONException e) { - try { - return new BucketResponse(helper.toss(new RiakResponseRuntimeException(r, e))); - } catch (Exception e1) { - throw new IllegalStateException( - "helper.toss() returns a unsuccessful result, so BucketResponse shouldn't try to parse it or throw"); - } - } catch (IOException e) { - try { - return new BucketResponse(helper.toss(new RiakIORuntimeException(e))); - } catch (Exception e1) { - throw new IllegalStateException( - "helper.toss() returns a unsuccessful result, so BucketResponse shouldn't try to read it or throw"); - } - } - } - - public BucketResponse getBucketSchema(String bucket) { - return getBucketSchema(bucket, null); - } - - /** - * Return the properties and keys for a Riak bucket. - * - * @param bucket - * The bucket to list. - * @param meta - * Extra metadata to attach to the request such as HTTP headers - * and query parameters. - * - * @return {@link BucketResponse} containing HTTP response information and - * the parsed schema and keys - * - * @throws RiakIORuntimeException - * If an error occurs during communication with the Riak server. - * @throws RiakResponseRuntimeException - * If the Riak server returns a malformed response. - */ - public BucketResponse listBucket(String bucket, RequestMeta meta) { - return listBucket(bucket, meta, false); - } - - public BucketResponse listBucket(String bucket) { - return listBucket(bucket, null); - } - - /** - * Same as {@link RiakClient#listBucket(String, RequestMeta)}, except - * streams the response, so the user must remember to call - * {@link BucketResponse#close()} on the return value. - */ - public BucketResponse streamBucket(String bucket, RequestMeta meta) { - return listBucket(bucket, meta, true); - } - - public BucketResponse streamBucket(String bucket) { - return streamBucket(bucket, null); - } - - BucketResponse listBucket(String bucket, RequestMeta meta, boolean streamResponse) { - HttpResponse r = helper.listBucket(bucket, meta, streamResponse); - try { - return getBucketResponse(r); - } catch (JSONException e) { - try { - return new BucketResponse(helper.toss(new RiakResponseRuntimeException(r, e))); - } catch (Exception e1) { - throw new IllegalStateException( - "helper.toss() returns a unsuccessful result, so BucketResponse shouldn't try to parse it or throw"); - } - } catch (IOException e) { - try { - return new BucketResponse(helper.toss(new RiakIORuntimeException(e))); - } catch (Exception e1) { - throw new IllegalStateException( - "helper.toss() returns a unsuccessful result, so BucketResponse shouldn't try to read it or throw"); - } - } - } - - /** - * Store a {@link RiakObject}. - * - * @param object - * The {@link RiakObject} to store. - * @param meta - * Extra metadata to attach to the request such as w and dw - * values for the request, HTTP headers, and other query - * parameters. See - * {@link RequestMeta#writeParams(Integer, Integer)}. - * - * @return A {@link StoreResponse} containing HTTP response information and - * any updated information returned by the server such as the - * vclock, last modified date. - * - * @throws RiakIORuntimeException - * If an error occurs during communication with the Riak server. - * @throws RiakResponseRuntimeException - * If the Riak server returns a malformed response. - */ - public StoreResponse store(RiakObject object, RequestMeta meta) { - if (meta == null) { - meta = new RequestMeta(); - } - if (meta.getQueryParam(Constants.QP_RETURN_BODY) == null) { - meta.setQueryParam(Constants.QP_RETURN_BODY, "true"); - } - - setAcceptHeader(meta); - HttpResponse r = helper.store(object, meta); - return new StoreResponse(new FetchResponse(r, this)); - } - - /** - * @param meta - */ - private void setAcceptHeader(RequestMeta meta) { - String accept = meta.getHeader(Constants.HDR_ACCEPT); - if (accept == null) { - meta.setHeader(Constants.HDR_ACCEPT, Constants.CTYPE_ANY + ", " + Constants.CTYPE_MULTIPART_MIXED); - } else { - meta.setHeader(Constants.HDR_ACCEPT, accept + ", " + Constants.CTYPE_MULTIPART_MIXED); - } - } - - public StoreResponse store(RiakObject object) { - return store(object, null); - } - - /** - * Fetch metadata (e.g. vclock, last modified, vtag) for the - * {@link RiakObject} stored at bucket and key. - * - * @param bucket - * The bucket containing the {@link RiakObject} to fetch. - * @param key - * The key of the {@link RiakObject} to fetch. - * @param meta - * Extra metadata to attach to the request such as an r- value - * for the request, HTTP headers, and other query parameters. See - * {@link RequestMeta#readParams(int)}. - * - * @return {@link FetchResponse} containing HTTP response information and a - * {@link RiakObject} containing only metadata and no value. - * - * @throws RiakIORuntimeException - * If an error occurs during communication with the Riak server. - * @throws RiakResponseRuntimeException - * If the Riak server returns a malformed response. - */ - public FetchResponse fetchMeta(String bucket, String key, RequestMeta meta) { - try { - return getFetchResponse(helper.fetchMeta(bucket, key, meta)); - } catch (RiakResponseRuntimeException e) { - return new FetchResponse(helper.toss(e), this); - } - } - - public FetchResponse fetchMeta(String bucket, String key) { - return fetchMeta(bucket, key, null); - } - - /** - * Fetch the {@link RiakObject} (which can include sibling objects) stored - * at bucket and key. - * - * @param bucket - * The bucket containing the {@link RiakObject} to fetch. - * @param key - * The key of the {@link RiakObject} to fetch. - * @param meta - * Extra metadata to attach to the request such as an r- value - * for the request, HTTP headers, and other query parameters. See - * {@link RequestMeta#readParams(int)}. - * - * @return {@link FetchResponse} containing HTTP response information and a - * {@link RiakObject} or sibling objects. - * - * @throws RiakIORuntimeException - * If an error occurs during communication with the Riak server. - * @throws RiakResponseRuntimeException - * If the Riak server returns a malformed response. - */ - public FetchResponse fetch(String bucket, String key, RequestMeta meta) { - return fetch(bucket, key, meta, false); - } - - public FetchResponse fetch(String bucket, String key) { - return fetch(bucket, key, null, false); - } - - /** - * Similar to fetch(), except the HTTP connection is left open for - * successful responses, and the Riak response is provided as a stream. - * The user must remember to call {@link FetchResponse#close()} on the - * return value. - * - * @param bucket - * The bucket containing the {@link RiakObject} to fetch. - * @param key - * The key of the {@link RiakObject} to fetch. - * @param meta - * Extra metadata to attach to the request such as an r- value - * for the request, HTTP headers, and other query parameters. See - * RequestMeta.readParams(). - * - * @return A streaming {@link FetchResponse} containing HTTP response - * information and the response stream. The HTTP connection must be - * closed manually by the user by calling - * {@link FetchResponse#close()}. - */ - public FetchResponse stream(String bucket, String key, RequestMeta meta) { - return fetch(bucket, key, meta, true); - } - - public FetchResponse stream(String bucket, String key) { - return fetch(bucket, key, null, true); - } - - FetchResponse fetch(String bucket, String key, RequestMeta meta, boolean streamResponse) { - if (meta == null) { - meta = new RequestMeta(); - } - - setAcceptHeader(meta); - HttpResponse r = helper.fetch(bucket, key, meta, streamResponse); - - try { - return getFetchResponse(r); - } catch (RiakResponseRuntimeException e) { - return new FetchResponse(helper.toss(e), this); - } - - } - - /** - * Fetch and process the object stored at bucket and - * key as a stream. - * - * @param bucket - * The bucket containing the {@link RiakObject} to fetch. - * @param key - * The key of the {@link RiakObject} to fetch. - * @param handler - * A {@link StreamHandler} to process the Riak response. - * @param meta - * Extra metadata to attach to the request such as an r- value - * for the request, HTTP headers, and other query parameters. See - * RequestMeta.readParams(). - * - * @return Result from calling handler.process() or true if handler is null. - * - * @throws IOException - * If an error occurs during communication with the Riak server. - * - * @see StreamHandler - */ - public boolean stream(String bucket, String key, StreamHandler handler, RequestMeta meta) throws IOException { - return helper.stream(bucket, key, handler, meta); - } - - /** - * Delete the object at bucket and key. - * - * @param bucket - * The bucket containing the object. - * @param key - * The key of the object - * @param meta - * Extra metadata to attach to the request such as w and dw - * values for the request, HTTP headers, and other query - * parameters. See - * {@link RequestMeta#writeParams(Integer, Integer)}. - * - * @return {@link HttpResponse} containing HTTP response information. - * - * @throws RiakIORuntimeException - * If an error occurs during communication with the Riak server. - */ - public HttpResponse delete(String bucket, String key, RequestMeta meta) { - return helper.delete(bucket, key, meta); - } - - public HttpResponse delete(String bucket, String key) { - return delete(bucket, key, null); - } - - /** - * Perform a map/reduce link walking operation and return the objects for - * which the "accumulate" flag is true. - * - * @param bucket - * The bucket of the "starting object" - * @param key - * The key of the "starting object" - * @param walkSpec - * A URL-path (omit beginning /) of the form - * bucket,tag-spec,accumulateFlag The - * tag-spec "_" matches all tags. - * accumulateFlag is either the String "1" or "0". - * @param meta - * Extra metadata to attach to the request such as HTTP headers - * or query parameters. - * - * @return {@link WalkResponse} containing HTTP response information and a - * List of Lists, where each sub-list - * corresponds to a walkSpec element that had - * accumulateFlag equal to 1. - * - * @throws RiakIORuntimeException - * If an error occurs during communication with the Riak server. - * @throws RiakResponseRuntimeException - * If the Riak server returns a malformed response. - * - * @see RiakWalkSpec - */ - public WalkResponse walk(String bucket, String key, String walkSpec, RequestMeta meta) { - HttpResponse r = helper.walk(bucket, key, walkSpec, meta); - - try { - return getWalkResponse(r); - } catch (RiakResponseRuntimeException e) { - return new WalkResponse(helper.toss(e), this); - } - } - - public WalkResponse walk(String bucket, String key, String walkSpec) { - return walk(bucket, key, walkSpec, null); - } - - public WalkResponse walk(String bucket, String key, RiakWalkSpec walkSpec) { - return walk(bucket, key, walkSpec.toString(), null); - } - - /** - * Execute a map reduce job on the Riak server. - * - * @param job - * JSON string representing the map reduce job to run, which can - * be created using {@link MapReduceBuilder} - * @param meta - * Extra metadata to attach to the request such as HTTP headers - * or query parameters. - * - * @return {@link MapReduceResponse} containing HTTP response information - * and the result of the map reduce job - * - * @throws RiakIORuntimeException - * If an error occurs during communication with the Riak server. - * @throws RiakResponseRuntimeException - * If the Riak server does not return a valid JSON array. - */ - public MapReduceResponse mapReduce(String job, RequestMeta meta) { - HttpResponse r = helper.mapReduce(job, meta); - try { - return getMapReduceResponse(r); - } catch (JSONException e) { - helper.toss(new RiakResponseRuntimeException(r, e)); - return null; - } - } - - public MapReduceResponse mapReduce(String job) { - return mapReduce(job, null); - } - - /** - * A convenience method for creating a MapReduceBuilder used for building a - * map reduce job to submission to this client - * - * @param bucket - * The bucket to perform the map reduce job over - * @return A {@link MapReduceBuilder} to build the map reduce job - */ - public MapReduceBuilder mapReduceOverBucket(String bucket) { - return new MapReduceBuilder(this).setBucket(bucket); - } - - /** - * Same as {@link RiakClient#mapReduceOverBucket(String)}, except over a set - * of objects instead of a bucket. - * - * @param objects - * A set of objects represented as a map of { bucket : [ list of - * keys in bucket ] } - */ - public MapReduceBuilder mapReduceOverObjects(Map> objects) { - return new MapReduceBuilder(this).setRiakObjects(objects); - } - - /** - * The installed exception handler or null if not installed - */ - public RiakExceptionHandler getExceptionHandler() { - return helper.getExceptionHandler(); - } - - /** - * If an exception handler is provided, then the Riak client will hand - * exceptions to the handler rather than throwing them. - * {@link ClientUtils#throwChecked(Throwable)} can be used to throw - * undeclared checked exceptions to effectively "convert" RiakClient's - * unchecked exceptions to checked exceptions. - */ - public void setExceptionHandler(RiakExceptionHandler exceptionHandler) { - helper.setExceptionHandler(exceptionHandler); - } - - /** - * Return the {@link HttpClient} used to make requests, which can be - * configured. - */ - public HttpClient getHttpClient() { - return helper.getHttpClient(); - } - - /** - * A 4-byte unique ID for this client. The ID is base 64 encoded and sent to - * Riak to generating the object vclock on store operations. Refer to the - * Riak documentation and - * http://lists.basho.com/pipermail/riak-users_lists.basho.com/2009- - * November/000153.html for information about the client ID. - */ - public byte[] getClientId() { - return helper.getClientId(); - } - - public void setClientId(String clientId) { - helper.setClientId(clientId); - } - - // Encapsulate response creation so it can be stubbed for testing - BucketResponse getBucketResponse(HttpResponse r) throws JSONException, IOException { - return new BucketResponse(r); - } - - FetchResponse getFetchResponse(HttpResponse r) throws RiakResponseRuntimeException { - return new FetchResponse(r, this); - } - - WalkResponse getWalkResponse(HttpResponse r) throws RiakResponseRuntimeException { - return new WalkResponse(r, this); - } - - MapReduceResponse getMapReduceResponse(HttpResponse r) throws JSONException { - return new MapReduceResponse(r); - } -} diff --git a/src/main/java/com/basho/riak/client/RiakConfig.java b/src/main/java/com/basho/riak/client/RiakConfig.java deleted file mode 100644 index 59f03f2b2..000000000 --- a/src/main/java/com/basho/riak/client/RiakConfig.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * 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; - -import java.net.URL; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpMethodRetryHandler; -import org.apache.commons.httpclient.params.HttpClientParams; -import org.apache.commons.httpclient.params.HttpConnectionManagerParams; - -/** - * Configuration settings for connecting to a Riak instance such as the base - * Riak URL and HttpClient settings. A pre-constructed HttpClient can also be - * provided. - */ -public class RiakConfig { - - public static Pattern BASE_URL_PATTERN = Pattern.compile("^((?:[^:]*://)?[^/]*)"); - - private String url = null; - private String baseUrl = null; - private String mapredPath = "/mapred"; - private HttpClient httpClient = null; - private Integer timeout = null; - private Integer maxConnections = null; - private HttpMethodRetryHandler retryHandler = null; - - public RiakConfig() {} - - public RiakConfig(String url) { - if (url == null || url.length() == 0) - throw new IllegalArgumentException(); - - this.setUrl(url); - } - - public RiakConfig(URL url) { - if (url == null) { - throw new IllegalArgumentException(); - } - - String protocol = url.getProtocol().toLowerCase(); - if(!protocol.equals("http") && !protocol.equals("https")) { - throw new IllegalArgumentException(); - } - - this.setUrl(url.toExternalForm()); - } - - public RiakConfig(String ip, String port, String prefix) { - if (prefix == null) { - prefix = ""; - } - - this.setUrl("http://" + ip + ":" + port + prefix); - } - - /** - * The base URL used by a client to construct object URLs - */ - public String getUrl() { - return url; - } - - /** - * Set the base URL that clients should use to construct object URLs (e.g. - * http://localhost:8098/riak). - */ - public void setUrl(String url) { - this.url = url.endsWith("/") ? url.substring(0, url.length() - 1) : url; - - Matcher m = BASE_URL_PATTERN.matcher(url); - if (m.find()) { - baseUrl = m.group(); - } else { - baseUrl = this.url; - } - } - - /** - * The full URL of Riak map reduce resource, which is calculated by - * combining the host and port from the Riak URL and the map reduce path. - */ - public String getMapReduceUrl() { - return baseUrl + mapredPath; - } - - /** - * The host and port of the Riak server, which is extracted from the - * specified Riak URL. - */ - public String getBaseUrl() { - return baseUrl; - } - - /** - * The path to the Riak map reduce resource, which defaults to /mapred - */ - public String getMapReducePath() { - return mapredPath; - } - - public void setMapReducePath(String path) { - if (!path.startsWith("/")) { - path = "/" + path; - } - if (path.endsWith("/")) { - path = path.substring(0, path.length() - 1); - } - mapredPath = path; - } - - /** - * The pre-constructed HttpClient for a client to use if one was provided - */ - public HttpClient getHttpClient() { - return httpClient; - } - - /** - * Provide a pre-constructed HttpClient for clients to use to connect to - * Riak - */ - public void setHttpClient(HttpClient httpClient) { - this.httpClient = httpClient; - } - - /** - * Value to set for the properties: - * {@link HttpClientParams#CONNECTION_MANAGER_TIMEOUT}, - * {@link HttpClientParams#SO_TIMEOUT}, - * {@link HttpConnectionManagerParams#CONNECTION_TIMEOUT} which sets the - * timeout milliseconds for retrieving an HTTP connection and data over the - * connection. Null for default. - */ - public void setTimeout(final Integer timeout) { - this.timeout = timeout; - } - - public Integer getTimeout() { - return timeout; - } - - /** - * Value to set for the HttpConnectionManagerParams.MAX_TOTAL_CONNECTIONS - * and the HttpConnectionManagerParams.MAX_HOST_CONNECTIONS properties: - * overall maximum number of connections used by the HttpClient and the - * maximum number of connections per host. The latter is set to overcome - * the default 2 connections per host in HttpClient. - */ - public void setMaxConnections(Integer maxConnections) { - this.maxConnections = maxConnections; - } - - public Integer getMaxConnections() { - return maxConnections; - } - - /** - * Value to set for the HttpClientParams.RETRY_HANDLER property: the default - * retry handler for requests. - * - * @see org.apache.commons.httpclient.DefaultHttpMethodRetryHandler - */ - public HttpMethodRetryHandler getRetryHandler() { - return retryHandler; - } - - public void setRetryHandler(HttpMethodRetryHandler retryHandler) { - this.retryHandler = retryHandler; - } -} diff --git a/src/main/java/com/basho/riak/client/RiakLink.java b/src/main/java/com/basho/riak/client/RiakLink.java deleted file mode 100644 index dff595561..000000000 --- a/src/main/java/com/basho/riak/client/RiakLink.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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; - -/** - * Represents a link to a Riak object. The target object is identified by its - * bucket and key and the link is classified by a tag. - */ -public class RiakLink { - - private String bucket; - private String key; - private String tag; - - public RiakLink(String bucket, String key, String tag) { - this.bucket = bucket; - this.key = key; - this.tag = tag; - } - - /** Copy constructor */ - public RiakLink(RiakLink link) { - bucket = link.bucket; - key = link.key; - tag = link.tag; - } - - /** - * Bucket of the target object being linked to. - */ - public String getBucket() { - return bucket; - } - - /** - * @deprecated will be removed in future versions to make RiakLink immutable. - * @param bucket - */ - @Deprecated - public void setBucket(String bucket) { - this.bucket = bucket; - } - - /** - * Key of the target object being linked to. - */ - public String getKey() { - return key; - } - - /** - * @deprecated will be removed in future versions to make RiakLink immutable. - * @param key - */ - @Deprecated - public void setKey(String key) { - this.key = key; - } - - /** - * Tag to classify the link. - */ - public String getTag() { - return tag; - } - - /** - * @deprecated will be removed in future versions to make RiakLink immutable. - * @param tag - */ - @Deprecated - public void setTag(String tag) { - this.tag = tag; - } - - @Override public boolean equals(Object obj) { - if (!(obj instanceof RiakLink)) - return false; - RiakLink other = (RiakLink) obj; - - boolean bucketEq = (bucket != null && bucket.equals(other.bucket)) || (bucket == null && other.bucket == null); - boolean keyEq = (key != null && key.equals(other.key)) || (key == null && other.key == null); - boolean tagEq = (tag != null && tag.equals(other.tag)) || (tag == null && other.tag == null); - return bucketEq && keyEq && tagEq; - } - - @Override public String toString() { - return new StringBuilder("[").append(bucket).append(",").append(key).append(",").append(tag).append("]").toString(); - } -} diff --git a/src/main/java/com/basho/riak/client/RiakObject.java b/src/main/java/com/basho/riak/client/RiakObject.java deleted file mode 100644 index 295218495..000000000 --- a/src/main/java/com/basho/riak/client/RiakObject.java +++ /dev/null @@ -1,893 +0,0 @@ -/* - * 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; - -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; - -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; -import org.apache.commons.httpclient.methods.EntityEnclosingMethod; -import org.apache.commons.httpclient.methods.InputStreamRequestEntity; -import org.apache.commons.httpclient.util.DateParseException; -import org.apache.commons.httpclient.util.DateUtil; - -import com.basho.riak.client.request.RequestMeta; -import com.basho.riak.client.request.RiakWalkSpec; -import com.basho.riak.client.response.FetchResponse; -import com.basho.riak.client.response.HttpResponse; -import com.basho.riak.client.response.RiakIORuntimeException; -import com.basho.riak.client.response.RiakResponseRuntimeException; -import com.basho.riak.client.response.StoreResponse; -import com.basho.riak.client.response.WalkResponse; -import com.basho.riak.client.util.Constants; - -/** - * A Riak object. - */ -public class RiakObject { - - private RiakClient riak; - private String bucket; - private String key; - private byte[] value; - private List links; - private Map userMetaData; - private String contentType; - private String vclock; - private String lastmod; - private String vtag; - private InputStream valueStream; - private Long valueStreamLength; - - /** - * Create an empty object. The content type defaults to - * application/octet-stream. - * - * @param riak - * Riak instance this object is associated with, which is used by - * the convenience methods in this class (e.g. - * {@link RiakObject#store()}). - * @param bucket - * The object's bucket - * @param key - * The object's key - * @param value - * The object's value - * @param contentType - * The object's content type which defaults to - * application/octet-stream if null. - * @param links - * Links to other objects - * @param userMetaData - * Custom metadata key-value pairs for this object - * @param vclock - * An opaque vclock assigned by Riak - * @param lastmod - * The last time this object was modified according to Riak - * @param vtag - * This object's entity tag assigned by Riak - */ - public RiakObject(RiakClient riak, String bucket, String key, byte[] value, String contentType, - List links, Map userMetaData, String vclock, String lastmod, String vtag) { - this.riak = riak; - this.bucket = bucket; - this.key = key; - this.vclock = vclock; - this.lastmod = lastmod; - this.vtag = vtag; - - safeSetValue(value); - this.contentType = contentType == null ? Constants.CTYPE_OCTET_STREAM : contentType; - safeSetLinks(links); - safeSetUsermetaData(userMetaData); - } - - public RiakObject(RiakClient riak, String bucket, String key) { - this(riak, bucket, key, null, null, null, null, null, null, null); - } - - public RiakObject(RiakClient riak, String bucket, String key, byte[] value) { - this(riak, bucket, key, value, null, null, null, null, null, null); - } - - public RiakObject(RiakClient riak, String bucket, String key, byte[] value, String contentType) { - this(riak, bucket, key, value, contentType, null, null, null, null, null); - } - - public RiakObject(RiakClient riak, String bucket, String key, byte[] value, String contentType, List links) { - this(riak, bucket, key, value, contentType, links, null, null, null, null); - } - - public RiakObject(RiakClient riak, String bucket, String key, byte[] value, String contentType, - List links, Map userMetaData) { - this(riak, bucket, key, value, contentType, links, userMetaData, null, null, null); - } - - public RiakObject(String bucket, String key) { - this(null, bucket, key, null, null, null, null, null, null, null); - } - - public RiakObject(String bucket, String key, byte[] value) { - this(null, bucket, key, value, null, null, null, null, null, null); - } - - public RiakObject(String bucket, String key, byte[] value, String contentType) { - this(null, bucket, key, value, contentType, null, null, null, null, null); - } - - public RiakObject(String bucket, String key, byte[] value, String contentType, List links) { - this(null, bucket, key, value, contentType, links, null, null, null, null); - } - - public RiakObject(String bucket, String key, byte[] value, String contentType, List links, - Map userMetaData) { - this(null, bucket, key, value, contentType, links, userMetaData, null, null, null); - } - - public RiakObject(String bucket, String key, byte[] value, String contentType, List links, - Map userMetaData, String vclock, String lastmod, String vtag) { - this(null, bucket, key, value, contentType, links, userMetaData, vclock, lastmod, vtag); - } - - /** - * A {@link RiakObject} can be loosely attached to the {@link RiakClient} - * from which retrieve it was retrieved. Calling convenience methods like - * {@link RiakObject#store()} will store this object use that client. - */ - public RiakClient getRiakClient() { - return riak; - } - - public RiakObject setRiakClient(RiakClient client) { - riak = client; - return this; - } - - /** - * Copy the metadata and value from object. The bucket and key - * are not copied. - * - * @param object - * The source object to copy from - */ - public void copyData(RiakObject object) { - if (object == null) - return; - - if (object.value != null) { - value = object.value.clone(); - } else { - value = null; - } - - valueStream = object.valueStream; - valueStreamLength = object.valueStreamLength; - - setLinks(object.links); - - userMetaData = new HashMap(); - if (object.userMetaData != null) { - userMetaData.putAll(object.userMetaData); - } - contentType = object.contentType; - vclock = object.vclock; - lastmod = object.lastmod; - vtag = object.vtag; - } - - /** - * Perform a shallow copy of the object - */ - void shallowCopy(RiakObject object) { - value = object.value; - this.links = object.links; - userMetaData = object.userMetaData; - contentType = object.contentType; - vclock = object.vclock; - lastmod = object.lastmod; - vtag = object.vtag; - valueStream = object.valueStream; - valueStreamLength = object.valueStreamLength; - } - - /** - * Update the object's metadata. This usually happens when Riak returns - * updated metadata from a store operation. - * - * @param response - * Response from a store operation containing an updated vclock, - * last modified date, and vtag - */ - public void updateMeta(StoreResponse response) { - if (response == null) { - vclock = null; - lastmod = null; - vtag = null; - } else { - vclock = response.getVclock(); - lastmod = response.getLastmod(); - vtag = response.getVtag(); - } - } - - /** - * Update the object's metadata from a fetch or fetchMeta operation - * - * @param response - * Response from a fetch or fetchMeta operation containing a - * vclock, last modified date, and vtag - */ - public void updateMeta(FetchResponse response) { - if (response == null || response.getObject() == null) { - vclock = null; - lastmod = null; - vtag = null; - } else { - vclock = response.getObject().getVclock(); - lastmod = response.getObject().getLastmod(); - vtag = response.getObject().getVtag(); - } - } - - /** - * The object's bucket - */ - public String getBucket() { - return bucket; - } - - /** - * The object's key - */ - public String getKey() { - return key; - } - - /** - * The object's value - */ - public String getValue() { - return (value == null ? null : new String(value)); - } - - public byte[] getValueAsBytes() { - return value == null ? value : value.clone(); - } - - public void setValue(String value) { - if (value != null) { - this.value = value.getBytes(); - } else { - this.value = null; - } - } - - public void setValue(byte[] value) { - safeSetValue(value); - } - - /** - * - * @param value - */ - private void safeSetValue(final byte[] value) { - if(value != null) { - this.value = value.clone(); - } else { - this.value = null; - } - } - - /** - * Set the object's value as a stream. A value set here is independent of - * and has precedent over any value set using setValue(): - * {@link RiakObject#writeToHttpMethod(HttpMethod)} will always write the - * value from getValueStream() if it is not null. Calling getValue() will - * always return values set via setValue(), and calling getValueStream() - * will always return the stream set via setValueStream. - * - * @param in - * Input stream representing the object's value - * @param len - * Length of the InputStream or null if unknown. If null, the - * value will be buffered in memory to determine its size before - * sending to the server. - */ - public void setValueStream(InputStream in, Long len) { - valueStream = in; - valueStreamLength = len; - } - - public void setValueStream(InputStream in) { - valueStream = in; - } - - public InputStream getValueStream() { - return valueStream; - } - - public void setValueStreamLength(Long len) { - valueStreamLength = len; - } - - public Long getValueStreamLength() { - return valueStreamLength; - } - - /** - * The object's links -- may be empty, but never be null. - * - * @see {@link RiakObject#addLink()}, {@link RiakObject#removeLink()}, {@link RiakObject#iterator()}, {@link RiakObject#hasLinks()} and , {@link RiakObject#numLinks()} - * - * @return the list of {@link RiakLink}s for this - * RiakObject - * @deprecated please use {@link RiakObject#iterableLinks())} to iterate over the - * collection of {@link RiakLink}s. Attempting to mutate the - * collection will result in UnsupportedOperationException in - * future versions. Use {@link RiakObject#addLink()} and {@link RiakObject#removeLink()} instead. - * Use {@link RiakObject#hasLinks()}, {@link RiakObject#numLinks()} and {@link RiakObject#hasLink(RiakLink)} - * to query state of links. - */ - @Deprecated - public List getLinks() { - return this.links; - } - - /** - * Makes a *deep* copy of links. - * - * Changes made to the original collection and its contents will not be reflected - * in this RiakObject's links. Use {@link RiakObject#addLink(RiakLink)}, - * {@link RiakObject#removeLink(RiakLink)} and {@link RiakObject#setLinks(List)} to alter the collection. - * @param links a List of {@link RiakLink} - */ - public void setLinks(List links) { - safeSetLinks(links); - } - - private void safeSetLinks(final List links) { - if (links == null) { - this.links = new CopyOnWriteArrayList(); - } else { - this.links = new CopyOnWriteArrayList(deepCopy(links)); - } - } - - /** - * Creates a new RiakLink for each RiakLink in links and adds it to a new List. - * - * @param links a List of {@link RiakLink}s - * @return a deep copy of List. - */ - private List deepCopy(List links) { - final ArrayList copyLinks = new ArrayList(); - - for(RiakLink link : links) { - copyLinks.add(new RiakLink(link)); - } - - return copyLinks; - } - - /** - * Add link to this RiakObject's links. - * @param link a {@link RiakLink} to add. - * @return this RiakObject. - */ - public RiakObject addLink(RiakLink link) { - if (link != null) { - links.add(link); - } - return this; - } - - /** - * Remove a {@link RiakLink} from this RiakObject. - * @param link the {@link RiakLink} to remove - * @return this RiakObject - */ - public RiakObject removeLink(final RiakLink link) { - this.links.remove(link); - return this; - } - - /** - * Does this RiakObject have any {@link RiakLink}s? - * @return true if there are links, false otherwise - */ - public boolean hasLinks() { - return !links.isEmpty(); - } - - /** - * How many {@link RiakLink}s does this RiakObject have? - * @return the number of {@link RiakLink}s this object has. - */ - public int numLinks() { - return links.size(); - } - - /** - * Checks if the collection of RiakLinks contains the one passed in. - * @param riakLink a RiakLink - * @return true if the RiakObject's link collection contains riakLink. - */ - public boolean hasLink(final RiakLink riakLink) { - return links.contains(riakLink); - } - - /** - * User-specified metadata for the object in the form of key-value pairs -- - * may be empty, but never be null. New key-value pairs can be added using - * addUsermeta() - * - * @deprecated Future versions will return an unmodifiable view of the user meta. Please use - * {@link RiakObject#addUsermeta(String, String)}, - * {@link RiakObject#removeUsermetaItem(String)}, - * {@link RiakObject#setUsermeta(Map)}, - * {@link RiakObject#hasUsermetaItem(String)}, - * {@link RiakObject#hasUsermeta()} and - * {@link RiakObject#getUsermetaItem(String)} to mutate and query the User meta collection - */ - @Deprecated - public Map getUsermeta() { - return userMetaData; - } - - /** - * Creates a copy of userMetaData. Changes made to the original collection will not be - * reflected in the RiakObject's state. - * @param userMetaData - */ - public void setUsermeta(final Map userMetaData) { - safeSetUsermetaData(userMetaData); - } - - private void safeSetUsermetaData(final Map userMetaData) { - if (userMetaData == null) { - this.userMetaData = new ConcurrentHashMap(); - } else { - this.userMetaData = new ConcurrentHashMap(userMetaData); - } - } - - /** - * Adds the key, value to the collection of user meta for this object. - * @param key - * @param value - * @return this RiakObject. - */ - public RiakObject addUsermetaItem(String key, String value) { - userMetaData.put(key, value); - return this; - } - - /** - * @return true if there are any user meta data set on this RiakObject. - */ - public boolean hasUsermeta() { - return !userMetaData.isEmpty(); - } - - /** - * @return how many user meta data items this RiakObject has. - */ - public int numUsermetaItems() { - return userMetaData.size(); - } - - /** - * @param key - * @return - */ - public boolean hasUsermetaItem(String key) { - return userMetaData.containsKey(key); - } - - /** - * Get an item of user meta data. - * @param key the user meta data item key - * @return The value for the given key or null. - */ - public String getUsermetaItem(String key) { - return userMetaData.get(key); - } - - /** - * @param key the key of the item to remove - */ - public void removeUsermetaItem(String key) { - userMetaData.remove(key); - } - - public Iterable usermetaKeys() { - return userMetaData.keySet(); - } - - /** - * The object's content type as a MIME type - */ - public String getContentType() { - return contentType; - } - - public void setContentType(String contentType) { - if (contentType != null) { - this.contentType = contentType; - } else { - this.contentType = Constants.CTYPE_OCTET_STREAM; - } - } - - /** - * The object's opaque vclock assigned by Riak - */ - public String getVclock() { - return vclock; - } - - /** - * The modification date of the object determined by Riak - */ - public String getLastmod() { - return lastmod; - } - - /** - * Convenience method to get the last modified header parsed into a Date - * object. Returns null if header is null, malformed, or cannot be parsed. - */ - public Date getLastmodAsDate() { - try { - return DateUtil.parseDate(lastmod); - } catch (DateParseException e) { - return null; - } - } - - /** - * An entity tag for the object assigned by Riak - */ - public String getVtag() { - return vtag; - } - - /** - * Convenience method for calling - * {@link RiakClient#store(RiakObject, RequestMeta)} followed by - * {@link RiakObject#updateMeta(StoreResponse)} - * - * @throws IllegalStateException - * if this object was not fetched from a Riak instance, so there - * is not associated server to store it with. - */ - public StoreResponse store(RequestMeta meta) { - return store(riak, meta); - } - - public StoreResponse store() { - return store(riak, null); - } - - /** - * Store this object to a different Riak instance. - * - * @param riak - * Riak instance to store this object to - * @param meta - * Same as {@link RiakClient#store(RiakObject, RequestMeta)} - * @throws IllegalStateException - * if this object was not fetched from a Riak instance, so there - * is not associated server to store it with. - */ - public StoreResponse store(RiakClient riak, RequestMeta meta) { - if (riak == null) - throw new IllegalStateException("Cannot store an object without a RiakClient"); - - StoreResponse r = riak.store(this, meta); - if (r.isSuccess()) { - this.updateMeta(r); - } - return r; - } - - /** - * Convenience method for calling {@link RiakClient#fetch(String, String)} - * followed by {@link RiakObject#copyData(RiakObject)} - * - * @param meta - * Same as {@link RiakClient#fetch(String, String, RequestMeta)} - * @throws IllegalStateException - * if this object was not fetched from a Riak instance, so there - * is not associated server to refetch it from. - */ - public FetchResponse fetch(RequestMeta meta) { - if (riak == null) - throw new IllegalStateException("Cannot fetch an object without a RiakClient"); - - FetchResponse r = riak.fetch(bucket, key, meta); - if (r.getObject() != null) { - RiakObject other = r.getObject(); - shallowCopy(other); - r.setObject(this); - } - return r; - } - - public FetchResponse fetch() { - return fetch(null); - } - - /** - * Convenience method for calling - * {@link RiakClient#fetchMeta(String, String, RequestMeta)} followed by - * {@link RiakObject#updateMeta(FetchResponse)} - * - * @throws IllegalStateException - * if this object was not fetched from a Riak instance, so there - * is not associated server to refetch meta from. - */ - public FetchResponse fetchMeta(RequestMeta meta) { - if (riak == null) - throw new IllegalStateException("Cannot fetch meta for an object without a RiakClient"); - - FetchResponse r = riak.fetchMeta(bucket, key, meta); - if (r.isSuccess()) { - this.updateMeta(r); - } - return r; - } - - public FetchResponse fetchMeta() { - return fetchMeta(null); - } - - /** - * Convenience method for calling - * {@link RiakClient#delete(String, String, RequestMeta)}. - * - * @throws IllegalStateException - * if this object was not fetched from a Riak instance, so there - * is not associated server to delete from. - */ - public HttpResponse delete(RequestMeta meta) { - if (riak == null) - throw new IllegalStateException("Cannot delete an object without a RiakClient"); - - return riak.delete(bucket, key, meta); - } - - public HttpResponse delete() { - return delete(null); - } - - /** - * Convenience methods for building a link walk specification starting from - * this object and calling - * {@link RiakClient#walk(String, String, RiakWalkSpec)} - * - * @param bucket - * The bucket to follow object links to - * @param tag - * The link tags to follow from this object - * @param keep - * Whether to keep the output from this link walking step. If not - * specified, then the output is only kept from the last step. - * @return A {@link LinkBuilder} object to continue building the walk query - * or to run it. - */ - public LinkBuilder walk(String bucket, String tag, boolean keep) { - return new LinkBuilder().walk(bucket, tag, keep); - } - - public LinkBuilder walk(String bucket, String tag) { - return new LinkBuilder().walk(bucket, tag); - } - - public LinkBuilder walk(String bucket, boolean keep) { - return new LinkBuilder().walk(bucket, keep); - } - - public LinkBuilder walk(String bucket) { - return new LinkBuilder().walk(bucket); - } - - public LinkBuilder walk() { - return new LinkBuilder().walk(); - } - - public LinkBuilder walk(boolean keep) { - return new LinkBuilder().walk(keep); - } - - /** - * Serializes this object to an existing {@link HttpMethod} which can be - * sent as an HTTP request. Specifically, sends the object's link, - * user-defined metadata and vclock as HTTP headers and the value as the - * body. Used by {@link RiakClient} to create PUT requests. - */ - public void writeToHttpMethod(HttpMethod httpMethod) { - // Serialize headers - String basePath = getBasePathFromHttpMethod(httpMethod); - writeLinks(httpMethod, basePath); - for (String name : userMetaData.keySet()) { - httpMethod.setRequestHeader(Constants.HDR_USERMETA_REQ_PREFIX + name, userMetaData.get(name)); - } - if (vclock != null) { - httpMethod.setRequestHeader(Constants.HDR_VCLOCK, vclock); - } - - // Serialize body - if (httpMethod instanceof EntityEnclosingMethod) { - EntityEnclosingMethod entityEnclosingMethod = (EntityEnclosingMethod) httpMethod; - - // Any value set using setValueAsStream() has precedent over value - // set using setValue() - if (valueStream != null) { - if (valueStreamLength != null && valueStreamLength >= 0) { - entityEnclosingMethod.setRequestEntity(new InputStreamRequestEntity(valueStream, valueStreamLength, - contentType)); - } else { - entityEnclosingMethod.setRequestEntity(new InputStreamRequestEntity(valueStream, contentType)); - } - } else if (value != null) { - entityEnclosingMethod.setRequestEntity(new ByteArrayRequestEntity(value, contentType)); - } else { - entityEnclosingMethod.setRequestEntity(new ByteArrayRequestEntity("".getBytes(), contentType)); - } - } - } - - private void writeLinks(HttpMethod httpMethod, String basePath) { - StringBuilder linkHeader = new StringBuilder(); - - for (RiakLink link : this.links) { - if (linkHeader.length() > 0) { - linkHeader.append(", "); - } - linkHeader.append("<"); - linkHeader.append(basePath); - linkHeader.append("/"); - linkHeader.append(link.getBucket()); - linkHeader.append("/"); - linkHeader.append(link.getKey()); - linkHeader.append(">; "); - linkHeader.append(Constants.LINK_TAG); - linkHeader.append("=\""); - linkHeader.append(link.getTag()); - linkHeader.append("\""); - - // To avoid (MochiWeb) problems with too long headers, flush if - // it grows too big: - if (linkHeader.length() > 2000) { - httpMethod.addRequestHeader(Constants.HDR_LINK, linkHeader.toString()); - linkHeader = new StringBuilder(); - } - } - if (linkHeader.length() > 0) { - httpMethod.addRequestHeader(Constants.HDR_LINK, linkHeader.toString()); - } - } - - String getBasePathFromHttpMethod(HttpMethod httpMethod) { - if (httpMethod == null || httpMethod.getPath() == null) - return ""; - - String path = httpMethod.getPath(); - int idx = path.length() - 1; - - // ignore any trailing slash - if (path.endsWith("/")) { - idx--; - } - - // trim off last two path components - idx = path.lastIndexOf('/', idx); - idx = path.lastIndexOf('/', idx - 1); - - if (idx <= 0) - return ""; - - return path.substring(0, idx); - } - - /** - * Created by links() as a convenient way to build up link walking queries - */ - public class LinkBuilder { - - private RiakWalkSpec walkSpec = new RiakWalkSpec(); - - public LinkBuilder walk() { - walkSpec.addStep(RiakWalkSpec.WILDCARD, RiakWalkSpec.WILDCARD); - return this; - } - - public LinkBuilder walk(boolean keep) { - walkSpec.addStep(RiakWalkSpec.WILDCARD, RiakWalkSpec.WILDCARD, keep); - return this; - } - - public LinkBuilder walk(String bucket) { - walkSpec.addStep(bucket, RiakWalkSpec.WILDCARD); - return this; - } - - public LinkBuilder walk(String bucket, boolean keep) { - walkSpec.addStep(bucket, RiakWalkSpec.WILDCARD, keep); - return this; - } - - public LinkBuilder walk(String bucket, String tag) { - walkSpec.addStep(bucket, tag); - return this; - } - - public LinkBuilder walk(String bucket, String tag, boolean keep) { - walkSpec.addStep(bucket, tag, keep); - return this; - } - - public String getWalkSpec() { - return walkSpec.toString(); - } - - /** - * Execute the link walking query by calling - * {@link RiakClient#walk(String, String, String, RequestMeta)}. - * - * @param meta - * Extra metadata to attach to the request such as HTTP - * headers or query parameters. - * @return See - * {@link RiakClient#walk(String, String, String, RequestMeta)}. - * - * @throws RiakIORuntimeException - * If an error occurs during communication with the Riak - * server. - * @throws RiakResponseRuntimeException - * If the Riak server returns a malformed response. - */ - public WalkResponse run(RequestMeta meta) { - if (riak == null) - throw new IllegalStateException("Cannot perform object link walk without a RiakClient"); - return riak.walk(bucket, key, getWalkSpec(), meta); - } - - public WalkResponse run() { - return run(null); - } - } - - /** - * A thread safe, snapshot Iterable view of the state of this RiakObject's {@link RiakLink}s at call time. - * Modifications are *NOT* supported. - * @return Iterable for this RiakObject's {@link RiakLink}s - */ - public Iterable iterableLinks() { - return new Iterable() { - public Iterator iterator() { - return links.iterator(); - } - }; - } -} \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/api/RiakClient.java b/src/main/java/com/basho/riak/client/api/RiakClient.java new file mode 100644 index 000000000..8815faa2a --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/RiakClient.java @@ -0,0 +1,338 @@ +/* + * 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 com.basho.riak.client.core.RiakNode; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + + +/** + * + * The client used to perform operations on Riak. + *

+ * The core of the Java client models a Riak cluster: + *

+ * + *

+ *

+ * The easiest way to get started with the client API is using one of the static + * methods provided to instantiate and start the client: + *

+ *
+ * {@code
+ * RiakClient client = 
+ *     RiakClient.newClient("192.168.1.1","192.168.1.2","192.168.1.3"); } 
+ * + * 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. + *

+ *
+ * {@code
+ * 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, {@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: + *

+ * {@code
+ * Namespace ns = new Namespace("default","my_bucket");
+ * Location loc = new Location(ns, "my_key");
+ * FetchValue fv = new FetchValue.Builder(loc).build();
+ * RiakFuture future = client.executeAsync(fv);
+ * future.await();
+ * if (future.isSuccess())
+ * {
+ *     FetchValue.Response response = future.getNow();
+ *     RiakObject obj = response.getValue(RiakObject.class);
+ *     ...
+ * }
+ * else
+ * {
+ *     Throwable error = future.cause();
+ *     ...
+ * }}
+ *

+ *

+ *

RiakCommand subclasses

+ *

Fetching, storing and deleting objects

+ *
    + *
  • {@link com.basho.riak.client.api.commands.kv.FetchValue}
  • + *
  • {@link com.basho.riak.client.api.commands.kv.MultiFetch}
  • + *
  • {@link com.basho.riak.client.api.commands.kv.StoreValue}
  • + *
  • {@link com.basho.riak.client.api.commands.kv.UpdateValue}
  • + *
  • {@link com.basho.riak.client.api.commands.kv.DeleteValue}
  • + *
+ *

Listing keys in a namespace

+ *
  • {@link com.basho.riak.client.api.commands.kv.ListKeys}
+ *

Secondary index (2i) commands

+ *
    + *
  • {@link com.basho.riak.client.api.commands.indexes.RawIndexQuery}
  • + *
  • {@link com.basho.riak.client.api.commands.indexes.BinIndexQuery}
  • + *
  • {@link com.basho.riak.client.api.commands.indexes.IntIndexQuery}
  • + *
  • {@link com.basho.riak.client.api.commands.indexes.BigIntIndexQuery}
  • + *
+ *

Fetching and storing datatypes (CRDTs)

+ *
    + *
  • {@link com.basho.riak.client.api.commands.datatypes.FetchCounter}
  • + *
  • {@link com.basho.riak.client.api.commands.datatypes.FetchSet}
  • + *
  • {@link com.basho.riak.client.api.commands.datatypes.FetchMap}
  • + *
  • {@link com.basho.riak.client.api.commands.datatypes.UpdateCounter}
  • + *
  • {@link com.basho.riak.client.api.commands.datatypes.UpdateSet}
  • + *
  • {@link com.basho.riak.client.api.commands.datatypes.UpdateMap}
  • + *
+ *

Querying and modifying buckets

+ *
    + *
  • {@link com.basho.riak.client.api.commands.buckets.FetchBucketProperties}
  • + *
  • {@link com.basho.riak.client.api.commands.buckets.StoreBucketProperties}
  • + *
  • {@link com.basho.riak.client.api.commands.buckets.ListBuckets}
  • + *
+ *

Search commands

+ *
    + *
  • {@link com.basho.riak.client.api.commands.search.Search}
  • + *
  • {@link com.basho.riak.client.api.commands.search.FetchIndex}
  • + *
  • {@link com.basho.riak.client.api.commands.search.StoreIndex}
  • + *
  • {@link com.basho.riak.client.api.commands.search.DeleteIndex}
  • + *
  • {@link com.basho.riak.client.api.commands.search.FetchSchema}
  • + *
  • {@link com.basho.riak.client.api.commands.search.StoreSchema}
  • +*
+*

Map-Reduce

+ *
    + *
  • {@link com.basho.riak.client.api.commands.mapreduce.BucketMapReduce}
  • + *
  • {@link com.basho.riak.client.api.commands.mapreduce.BucketKeyMapReduce}
  • + *
  • {@link com.basho.riak.client.api.commands.mapreduce.IndexMapReduce}
  • + *
  • {@link com.basho.riak.client.api.commands.mapreduce.SearchMapReduce}
  • + *
+ * @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. + *

Client Commands

+ *

Fetching, storing and deleting objects

+ *
    + *
  • {@link com.basho.riak.client.api.commands.kv.FetchValue}
  • + *
  • {@link com.basho.riak.client.api.commands.kv.MultiFetch}
  • + *
  • {@link com.basho.riak.client.api.commands.kv.StoreValue}
  • + *
  • {@link com.basho.riak.client.api.commands.kv.UpdateValue}
  • + *
  • {@link com.basho.riak.client.api.commands.kv.DeleteValue}
  • + *
+ *

Listing keys in a namespace

+ *
  • {@link com.basho.riak.client.api.commands.kv.ListKeys}
+ *

Secondary index (2i) commands

+ *
    + *
  • {@link com.basho.riak.client.api.commands.indexes.RawIndexQuery}
  • + *
  • {@link com.basho.riak.client.api.commands.indexes.BinIndexQuery}
  • + *
  • {@link com.basho.riak.client.api.commands.indexes.IntIndexQuery}
  • + *
  • {@link com.basho.riak.client.api.commands.indexes.BigIntIndexQuery}
  • + *
+ *

Fetching and storing datatypes (CRDTs)

+ *
    + *
  • {@link com.basho.riak.client.api.commands.datatypes.FetchCounter}
  • + *
  • {@link com.basho.riak.client.api.commands.datatypes.FetchSet}
  • + *
  • {@link com.basho.riak.client.api.commands.datatypes.FetchMap}
  • + *
  • {@link com.basho.riak.client.api.commands.datatypes.UpdateCounter}
  • + *
  • {@link com.basho.riak.client.api.commands.datatypes.UpdateSet}
  • + *
  • {@link com.basho.riak.client.api.commands.datatypes.UpdateMap}
  • + *
+ *

Querying and modifying buckets

+ *
    + *
  • {@link com.basho.riak.client.api.commands.buckets.FetchBucketProperties}
  • + *
  • {@link com.basho.riak.client.api.commands.buckets.StoreBucketProperties}
  • + *
  • {@link com.basho.riak.client.api.commands.buckets.ListBuckets}
  • + *
+ *

Search commands

+ *
    + *
  • {@link com.basho.riak.client.api.commands.search.Search}
  • + *
  • {@link com.basho.riak.client.api.commands.search.FetchIndex}
  • + *
  • {@link com.basho.riak.client.api.commands.search.StoreIndex}
  • + *
  • {@link com.basho.riak.client.api.commands.search.DeleteIndex}
  • + *
  • {@link com.basho.riak.client.api.commands.search.FetchSchema}
  • + *
  • {@link com.basho.riak.client.api.commands.search.StoreSchema}
  • +*
+*

Map-Reduce

+ *
    + *
  • {@link com.basho.riak.client.api.commands.mapreduce.BucketMapReduce}
  • + *
  • {@link com.basho.riak.client.api.commands.mapreduce.BucketKeyMapReduce}
  • + *
  • {@link com.basho.riak.client.api.commands.mapreduce.IndexMapReduce}
  • + *
  • {@link com.basho.riak.client.api.commands.mapreduce.SearchMapReduce}
  • + *
+ *

+ * @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. + *

+ * The type must be a {@code String}. + * + *

+ * class MyPojo
+ * {
+ *     {@literal @}RiakBucketName
+ *     String bucketName;
+ * }
+ * 
+ * class MyPojo
+ * {
+ *     private String bucketName;
+ * 
+ *     {@literal @}RiakBucketName
+ *     public void setBucketName(String bucketName)
+ *     {
+ *         this.bucketName = bucketName;
+ *     }
+ * 
+ *     {@literal @}RiakBucketName
+ *     public String getBucketName()
+ *     {
+ *         return bucketName;
+ *     }
+ * }
+ * 
+ *

+ * @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. + *

+ * The type must be a {@code String}. + * + *

+ * class MyPojo
+ * {
+ *     {@literal @}RiakBucketType
+ *     String bucketType;
+ * }
+ * 
+ * class MyPojo
+ * {
+ *     private String bucketType;
+ * 
+ *     {@literal @}RiakBucketType
+ *     public void setBucketType(String bucketType)
+ *     {
+ *         this.bucketType = bucketType;
+ *     }
+ * 
+ *     {@literal @}RiakBucketType
+ *     public String getBucketType()
+ *     {
+ *         return bucketType;
+ *     }
+ * }
+ * 
+ *

+ * @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. + * + *

+ * class MyPojo
+ * {
+ *     {@literal @}RiakContentType
+ *     String contentType;
+ * }
+ * 
+ * class MyPojo
+ * {
+ *     private String contentType;
+ * 
+ *     {@literal @}RiakContentType
+ *     public void setContentType(String contentType)
+ *     {
+ *         this.contentType = contentType;
+ *     }
+ * 
+ *     {@literal @}RiakContentType
+ *     public String getContentType()
+ *     {
+ *         return contentType;
+ *     }
+ * }
+ * 
+ *

+ * @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. + *

+ * The type must be a {@code String}. + *

+ *

+ * public class MyPojo 
+ * {
+ *     {@literal @}RiakKey 
+ *     public String key;
+ * }
+ * 
+ * public class AnotherPojo 
+ * {
+ *     private String key;
+ *     
+ *     {@literal @}RiakKey 
+ *     public String getKey() 
+ *     {
+ *         return key;
+ *     }
+ * 
+ *     {@literal @}RiakKey 
+ *     public void setKey(String key) 
+ *     {
+ *         this.key = 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}. + * + *

+ * class MyPojo
+ * {
+ *     {@literal @}RiakLastModified
+ *     Long lastModified;
+ * }
+ * 
+ * class MyPojo
+ * {
+ *     private Long lastModified;
+ * 
+ *     {@literal @}RiakLastModified
+ *     public void setLastModified(Long lastModified)
+ *     {
+ *         this.lastModified = lastModified;
+ *     }
+ * }
+ * 
+ * + * @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. + *

+ * The type must be {@literal Collection}. + *

+ * class MyPojo
+ * {
+ *     {@literal @}RiakLinks
+ *     {@literal Collection} riakLinks;
+ * }
+ * 
+ * class MyPojo
+ * {
+ *     private {@literal Collection} riakLinks;
+ * 
+ *     {@literal @}RiakLinks
+ *     public void setRiakLinks({@literal Collection} riakLinks)
+ *     {
+ *         this.riakLinks = riakLinks;
+ *     }
+ * 
+ *     {@literal @}RiakLinks
+ *     public {@literal Collection} getRiakLinks()
+ *     {
+ *         return riakLinks;
+ *     }
+ * }
+ * 
+ * 
+ * + * + * @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) + * + *

+ * public class MyPojo 
+ * {
+ *     {@literal @}RiakTombstone 
+ *     public boolean tombstone;
+ * }
+ * 
+ * public class AnotherPojo 
+ * {
+ *     private boolean tombstone;
+ *     
+ *     {@literal @}RiakTombstone 
+ *     public boolean getTombstone() 
+ *     {
+ *         return tombstone;
+ *     }
+ * 
+ *     {@literal @}RiakTombstone 
+ *     public void setTombstone(boolean tombstone) 
+ *     {
+ *         this.tombstone = tombstone;
+ *     }
+ * }
+ * 
+ * + * @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. + *

+ *

+ *

+ * public class MyClass 
+ * {
+ *     
+ *     {@literal @}RiakUsermeta
+ *     private {@literal Map} usermetaData;
+ *     
+ *     {@literal @}RiakUsermeta("usermeta-data-key1") 
+ *     private String usermetaDataItem1;
+ * }
+ * 
+ * public class MyClass
+ * {
+ *     private {@literal Map} usermetaData;
+ *     private String usermetaDataItem1;
+ * 
+ *     {@literal @}RiakUsermeta
+ *     public {@literal Map} getMeta()
+ *     {
+ *         return usermetaData;
+ *     }
+ * 
+ *     {@literal @}RiakUsermeta
+ *     public void setMeta({@literal Map} meta)
+ *     {
+ *         usermetaData = meta;
+ *     }
+ * 
+ *     {@literal @}RiakUsermeta("usermeta-data-key1") 
+ *     public String getSingleMeta()
+ *     {
+ *          return usermetaDataItem1;
+ *     }
+ * 
+ *     {@literal @}RiakUsermeta("usermeta-data-key1")
+ *     public void setSingleMeta(String meta)
+ *     {
+ *         usermetaDataItem1 = meta;
+ *     }
+ *  }
+ * 
+ *

+ * + * + * @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. + *

+ *
+ * public class MyPojo 
+ * {
+ *     {@literal @}RiakVClock 
+ *     public VClock vclock;
+ * }
+ * 
+ * public class AnotherPojo 
+ * {
+ *     private VClock vclock;
+ *     
+ *     {@literal @}RiakVClock 
+ *     public VClock getVClock() 
+ *     {
+ *         return vclock;
+ *     }
+ * 
+ *     {@literal @}RiakVClock 
+ *     public void setVClock(VClock vclock) 
+ *     {
+ *         this.vclock = vclock;
+ *     }
+ * }
+ * 
+ * @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}. + * + *

+ * class MyPojo
+ * {
+ *     {@literal @}RiakVTag
+ *     String vtag;
+ * }
+ * 
+ * class MyPojo
+ * {
+ *     private String vtag;
+ * 
+ *     {@literal @}RiakVTag
+ *     public void setVTag(String vtag)
+ *     {
+ *         this.vtag = vtag;
+ *     }
+ * }
+ * 
+ * + * @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. + *
+ * 
+ * public class AnnotatedPojo
+ * {
+ *     {@literal @}RiakBucketType
+ *     public String bucketType;
+ *     
+ *     {@literal @}RiakBucketName
+ *     public String bucketName;
+ *     
+ *     {@literal @}RiakKey
+ *     public String key; 
+ *     
+ *     {@literal @}RiakVClock
+ *     VClock clock;
+ * 
+ *     public String value;
+ * }
+ * 
+ * 
+ * + */ +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 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 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 siblings) { + super(message); + this.siblings = siblings; + } + + /** + * Get the siblings that could not be resolved. + * @return the siblings + */ + public List 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. + * + *

+ *

+ * {@code
+ * Namespace ns = new Namespace("my_type", "my_bucket");
+ * FetchBucketProperties fbp = new FetchBucketProperties.Builder(ns).build();
+ * FetchBucketPropsOperation.Response resp = client.execute(fbp);
+ * BucketProperties props = resp.getBucketProperties();}
+ * 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. + * + *

+ *

+ * {@code
+ * ListBuckets lb = new ListBuckets.Builder("my_type").build();
+ * ListBuckets.Response resp = client.execute(lb);
+ * for (Namespace ns : response)
+ * {
+ *     System.out.println(ns.getBucketName());
+ * }}
+ *

+ * @author Dave Rusek + * @since 2.0 + */ +public final class ListBuckets extends RiakCommand +{ + private final int timeout; + private final BinaryValue type; + + ListBuckets(Builder builder) + { + this.timeout = builder.timeout; + this.type = builder.type; + } + + @Override + protected RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + cluster.execute(buildCoreOperation()); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) + { + @Override + protected Response convertResponse(ListBucketsOperation.Response coreResponse) + { + return new Response(type, coreResponse.getBuckets()); + } + + @Override + protected BinaryValue convertQueryInfo(BinaryValue coreQueryInfo) + { + return coreQueryInfo; + } + }; + coreFuture.addListener(future); + return future; + } + + private ListBucketsOperation buildCoreOperation() + { + ListBucketsOperation.Builder builder = new ListBucketsOperation.Builder(); + if (timeout > 0) + { + builder.withTimeout(timeout); + } + return builder.build(); + } + + /** + * A response from a ListBuckets command. + *

This encapsulates an immutable list of bucket names + * and is Iterable: + *

+     * {@code
+     * for (Namespace ns : response)
+     * {
+     *     System.out.println(ns.getBucketName());
+     * }
+     * }
+     * 
+ *

+ */ + 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. + *

+ *

+ * {@code
+ * Namespace ns = new Namespace("my_type", "my_bucket");
+ * StoreBucketProperties sbp = 
+ *  new StoreBucketProperties.Builder(ns)
+ *      .withAllowMulti(true)
+ *      .build();
+ * client.execute(sbp);}
+ *

+ * @author Dave Rusek + * @since 2.0 + */ +public final class StoreBucketProperties extends RiakCommand +{ + + private final Namespace namespace; + private final Boolean allowMulti; + private final String backend; + private final Boolean basicQuorum; + private final Long bigVClock; + private final Function chashkeyFunction; + private final Boolean lastWriteWins; + private final Function linkWalkFunction; + private final Integer rw; + private final Integer dw; + private final Integer w; + private final Integer r; + private final Integer pr; + private final Integer pw; + private final Boolean notFoundOk; + private final Function preCommitHook; + private final Function postCommitHook; + private final Long oldVClock; + private final Long youngVClock; + private final Long smallVClock; + private final Integer nval; + private final Boolean legacySearch; + private final String searchIndex; + + StoreBucketProperties(Builder builder) + { + + this.namespace = builder.namespace; + this.allowMulti = builder.allowMulti; + this.backend = builder.backend; + this.bigVClock = builder.bigVClock; + this.chashkeyFunction = builder.chashkeyFunction; + this.lastWriteWins = builder.lastWriteWins; + this.basicQuorum = builder.basicQuorum; + this.linkWalkFunction = builder.linkWalkFunction; + this.rw = builder.rw; + this.dw = builder.dw; + this.w = builder.w; + this.pr = builder.pr; + this.pw = builder.pw; + this.r = builder.r; + this.notFoundOk = builder.notFoundOk; + this.preCommitHook = builder.preCommitHook; + this.postCommitHook = builder.postCommitHook; + this.oldVClock = builder.oldVClock; + this.youngVClock = builder.youngVClock; + this.smallVClock = builder.smallVClock; + this.nval = builder.nval; + this.legacySearch = builder.legacySearch; + this.searchIndex = builder.searchIndex; + + } + + @Override + protected final RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + cluster.execute(buildCoreOperation()); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) + { + @Override + protected Void convertResponse(Void coreResponse) + { + return coreResponse; + } + + @Override + protected Namespace convertQueryInfo(Namespace coreQueryInfo) + { + return coreQueryInfo; + } + }; + coreFuture.addListener(future); + return future; + } + + + private StoreBucketPropsOperation buildCoreOperation() + { + StoreBucketPropsOperation.Builder builder = + new StoreBucketPropsOperation.Builder(namespace); + + if (allowMulti != null) + { + builder.withAllowMulti(allowMulti); + } + + if (backend != null) + { + builder.withBackend(backend); + } + + if (basicQuorum != null) + { + builder.withBasicQuorum(basicQuorum); + } + + if (bigVClock != null) + { + builder.withBigVClock(bigVClock); + } + + if (chashkeyFunction != null) + { + builder.withChashkeyFunction(chashkeyFunction); + } + + if (lastWriteWins != null) + { + builder.withLastWriteWins(lastWriteWins); + } + + if (linkWalkFunction != null) + { + builder.withLinkwalkFunction(linkWalkFunction); + } + + if (rw != null) + { + builder.withRw(rw); + } + + if (dw != null) + { + builder.withDw(dw); + } + + if (w != null) + { + builder.withW(w); + } + + if (r != null) + { + builder.withR(r); + } + + if (pr != null) + { + builder.withPr(pr); + } + + if (pw != null) + { + builder.withPw(pw); + } + + if (notFoundOk != null) + { + builder.withNotFoundOk(notFoundOk); + } + + if (preCommitHook != null) + { + builder.withPrecommitHook(preCommitHook); + } + + if (postCommitHook != null) + { + builder.withPostcommitHook(postCommitHook); + } + + if (oldVClock != null) + { + builder.withOldVClock(oldVClock); + } + + if (youngVClock != null) + { + builder.withYoungVClock(youngVClock); + } + + if (smallVClock != null) + { + builder.withSmallVClock(smallVClock); + } + + if (nval != null) + { + builder.withNVal(nval); + } + + if (legacySearch != null) + { + builder.withLegacyRiakSearchEnabled(legacySearch); + } + + if (searchIndex != null) + { + builder.withSearchIndex(searchIndex); + } + + return builder.build(); + } + + public static class Builder + { + + private final Namespace namespace; + private Boolean allowMulti; + private String backend; + private Boolean basicQuorum; + private Long bigVClock; + private Function chashkeyFunction; + private Boolean lastWriteWins; + private Function linkWalkFunction; + private Integer rw; + private Integer dw; + private Integer w; + private Integer r; + private Integer pr; + private Integer pw; + private Boolean notFoundOk; + private Function preCommitHook; + private Function postCommitHook; + private Long oldVClock; + private Long youngVClock; + private Long smallVClock; + private Integer nval; + private Boolean legacySearch; + private String searchIndex; + + + public Builder(Namespace namespace) + { + if (namespace == null) + { + throw new IllegalArgumentException("Namespace cannot be null"); + } + this.namespace = namespace; + } + + /** + * Set the allow_multi value. + * + * @param allow whether to allow sibling objects to be created. + * @return a reference to this object. + */ + public Builder withAllowMulti(boolean allow) + { + this.allowMulti = allow; + return this; + } + + /** + * Set the backend used by this bucket. Only applies when using + * {@code riak_kv_multi_backend} in Riak. + * + * @param backend the name of the backend to use. + * @return a reference to this object. + */ + public Builder withBackend(String backend) + { + if (null == backend || backend.length() == 0) + { + throw new IllegalArgumentException("Backend can not be null or zero length"); + } + this.backend = backend; + return this; + } + + /** + * Set the basic_quorum value. + * + * The parameter controls whether a read request should return early in + * some fail cases. E.g. If a quorum of nodes has already returned + * notfound/error, don't wait around for the rest. + * + * @param use the basic_quorum value. + * @return a reference to this object. + */ + public Builder withBasicQuorum(boolean use) + { + this.basicQuorum = use; + return this; + } + + /** + * Set the big_vclock value. + * + * @param bigVClock a long representing a epoch time value. + * @return a reference to this object. + * @see Vector Clock Pruning + */ + public Builder withBigVClock(Long bigVClock) + { + this.bigVClock = bigVClock; + return this; + } + + /** + * Set the chash_keyfun value. + * + * @param func a Function representing the Erlang func to use. + * @return a reference to this object. + */ + public Builder withChashkeyFunction(Function func) + { + this.chashkeyFunction = func; + return this; + } + + /** + * Set the last_write_wins value. Unless you really know what you're + * doing, you probably do not want to set this to true. + * + * @param wins whether to ignore vector clocks when writing. + * @return a reference to this object. + */ + public Builder withLastWriteWins(boolean wins) + { + this.lastWriteWins = wins; + return this; + } + + /** + * Set the linkfun value. + * + * @param func a Function representing the Erlang func to use. + * @return a reference to this object. + */ + public Builder withLinkwalkFunction(Function func) + { + this.linkWalkFunction = func; + return this; + } + + /** + * Set the rw value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param rw the rw value as an integer. + * @return a reference to this object. + */ + public Builder withRw(int rw) + { + this.rw = rw; + return this; + } + + /** + * Set the dw value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param dw the dw value as an integer. + * @return a reference to this object. + */ + public Builder withDw(int dw) + { + this.dw = dw; + return this; + } + + /** + * Set the w value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param w the w value as an integer. + * @return a reference to this object. + */ + public Builder withW(int w) + { + this.w = w; + return this; + } + + /** + * Set the r value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param r the r value as an integer. + * @return a reference to this object. + */ + public Builder withR(int r) + { + this.r = r; + return this; + } + + /** + * Set the pr value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param pr the pr value as an integer. + * @return a reference to this object. + */ + public Builder withPr(int pr) + { + this.pr = pr; + return this; + } + + /** + * Set the pw value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param pw the pw value as an integer. + * @return a reference to this object. + */ + public Builder withPw(int pw) + { + this.pw = pw; + return this; + } + + /** + * Set the not_found_ok value. If true a vnode returning notfound for a + * key increments the r tally. False is higher consistency, true is + * higher availability. + * + * @param ok the not_found_ok value. + * @return a reference to this object. + */ + public Builder withNotFoundOk(boolean ok) + { + this.notFoundOk = ok; + return this; + } + + /** + * Add a pre-commit hook. The supplied Function must be an Erlang or + * Named JS function. + * + * @param hook the Function to add. + * @return a reference to this object. + * @see Using Commit Hooks + */ + public Builder withPrecommitHook(Function hook) + { + if (null == hook || !(!hook.isJavascript() || hook.isNamed())) + { + throw new IllegalArgumentException("Must be a named JS or Erlang function."); + } + this.preCommitHook = hook; + return this; + } + + /** + * Add a post-commit hook. The supplied Function must be an Erlang or + * Named JS function. + * + * @param hook the Function to add. + * @return a reference to this object. + * @see Using Commit Hooks + */ + public Builder withPostcommitHook(Function hook) + { + this.postCommitHook = hook; + return this; + } + + /** + * Set the old_vclock value. + * + * @param oldVClock an long representing a epoch time value. + * @return a reference to this object. + * @see Vector Clock Pruning + */ + public Builder withOldVClock(Long oldVClock) + { + this.oldVClock = oldVClock; + return this; + } + + /** + * Set the young_vclock value. + * + * @param youngVClock a long representing a epoch time value. + * @return a reference to this object. + * @see Vector Clock Pruning + */ + public Builder withYoungVClock(Long youngVClock) + { + this.youngVClock = youngVClock; + return this; + } + + /** + * Set the small_vclock value. + * + * @param smallVClock a long representing a epoch time value. + * @return a reference to this object. + * @see Vector Clock Pruning + */ + public Builder withSmallVClock(Long smallVClock) + { + this.smallVClock = smallVClock; + return this; + } + + /** + * Set the nVal. + * + * @param nVal the number of replicas. + * @return a reference to this object. + */ + public Builder withNVal(int nVal) + { + if (nVal <= 0) + { + throw new IllegalArgumentException("nVal must be >= 1"); + } + this.nval = nVal; + return this; + } + + /** + * Enable Legacy Riak Search. Setting this to true causes the search + * pre-commit hook to be added. + * + * Note this is only for legacy Riak (< v2.0) Search support. + * + * @param enable add/remove (true/false) the pre-commit hook for Legacy + * Riak Search. + * @return a reference to this object. + */ + public Builder withLegacyRiakSearchEnabled(boolean enable) + { + this.legacySearch = enable; + return this; + } + + /** + * Associate a Search Index. This only applies if Yokozuna is enabled in + * Riak v2.0. + * + * @param indexName The name of the search index to use. + * @return a reference to this object. + */ + public Builder withSearchIndex(String indexName) + { + if (null == indexName || indexName.length() == 0) + { + throw new IllegalArgumentException("Index name cannot be null or zero length"); + } + this.searchIndex = indexName; + return this; + } + + public StoreBucketProperties build() + { + return new StoreBucketProperties(this); + } + + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/buckets/package-info.java b/src/main/java/com/basho/riak/client/api/commands/buckets/package-info.java new file mode 100644 index 000000000..f2f603830 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/buckets/package-info.java @@ -0,0 +1,10 @@ +/** + * Commands used to query and modify buckets in Riak. + *

Querying and modifying buckets

+ *
    + *
  • {@link com.basho.riak.client.api.commands.buckets.FetchBucketProperties}
  • + *
  • {@link com.basho.riak.client.api.commands.buckets.StoreBucketProperties}
  • + *
  • {@link com.basho.riak.client.api.commands.buckets.ListBuckets}
  • + *
+ */ +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. + *

+     * {@code 
+     * ...
+     * RiakCounter counter = response.getDatatype();
+     * Long value = counter.view();
+     * }
+     * 
+ *

+ */ + 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, Object> options = new HashMap, Object>(); + + @SuppressWarnings("unchecked") + protected FetchDatatype(Builder builder) + { + this.location = builder.location; + this.options.putAll(builder.options); + } + + public FetchDatatype withOption(Option option, V value) + { + options.put(option, value); + return this; + } + + public abstract T extractDatatype(RiakDatatype element); + + protected final DtFetchOperation buildCoreOperation() + { + DtFetchOperation.Builder builder = + new DtFetchOperation.Builder(location); + + for (Map.Entry, Object> entry : options.entrySet()) + { + if (entry.getKey() == Option.R) + { + builder.withR(((Quorum) entry.getValue()).getIntValue()); + } + else if (entry.getKey() == Option.PR) + { + builder.withPr(((Quorum) entry.getValue()).getIntValue()); + } + else if (entry.getKey() == Option.BASIC_QUORUM) + { + builder.withBasicQuorum((Boolean) entry.getValue()); + } + else if (entry.getKey() == Option.NOTFOUND_OK) + { + builder.withNotFoundOK((Boolean) entry.getValue()); + } + else if (entry.getKey() == Option.TIMEOUT) + { + builder.withTimeout((Integer) entry.getValue()); + } + else if (entry.getKey() == Option.SLOPPY_QUORUM) + { + builder.withSloppyQuorum((Boolean) entry.getValue()); + } + else if (entry.getKey() == Option.N_VAL) + { + builder.withNVal((Integer) entry.getValue()); + } + else if (entry.getKey() == Option.INCLUDE_CONTEXT) + { + builder.includeContext((Boolean) entry.getValue()); + } + } + + + return builder.build(); + + } + + /** + * Tuning parameters for all datatype fetch commands. + * @author Dave Rusek + * @since 2.0 + */ + public static final class Option extends RiakOption { + + /** + * Read Quorum. + * How many replicas need to agree when fetching the object. + */ + public static final Option R = new Option("R"); + + /** + * Primary Read Quorum. + * How many primary replicas need to be available when retrieving the object. + */ + public static final Option PR = new Option("PR"); + + /** + * Basic Quorum. + * Whether to return early in some failure cases (eg. when r=1 and you get + * 2 errors and a success basic_quorum=true would return an error) + */ + public static final Option BASIC_QUORUM = new Option("BASIC_QUORUM"); + + /** + * Not Found OK. + * Whether to treat notfounds as successful reads for the purposes of R + */ + public static final Option NOTFOUND_OK = new Option("NOTFOUND_OK"); + + /** + * Timeout. + * Sets the server-side timeout for this operation. The default in Riak is 60 seconds. + */ + public static final Option TIMEOUT = new Option("TIMEOUT"); + public static final Option SLOPPY_QUORUM = new Option("SLOPPY_QUORUM"); + public static final Option N_VAL = new Option("N_VAL"); + + /** + * Whether to return a context. + * The default is true. Use this option if you're planning on only reading the datatype. + */ + public static final Option INCLUDE_CONTEXT = new Option("INCLUDE_CONTEXT"); + + public Option(String name) { + super(name); + } + } + + /** + * Base abstract builder for all datatype fetch command builders. + * @param + */ + protected static abstract class Builder> + { + + private final Location location; + private final Map, Object> options = new HashMap, Object>(); + + protected Builder(Location location) + { + if (location == null) + { + throw new IllegalArgumentException("Location cannot be null"); + } + this.location = location; + } + + + /** + * 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 T withTimeout(int timeout) + { + return withOption(Option.TIMEOUT, timeout); + } + + /** + * Add an optional setting for this command. + * This will be passed along with the request to Riak to tell it how + * to behave when servicing the request. + * + * @param option the option + * @param value the value for the option + * @return a reference to this object. + * @see Option + */ + public T withOption(Option option, U value) + { + this.options.put(option, value); + return self(); + } + + protected abstract T self(); + + } + + /** + * Base response for all CRDT fetch commands. + * @param + */ + public static class Response + { + + private final T datatype; + private final Context context; + + protected Response(T datatype, Context context) + { + this.datatype = datatype; + this.context = context; + } + + /** + * Get the datatype from this response. + * @return the fetched datatype. + */ + public T getDatatype() + { + return datatype; + } + + /** + * Check to see if a context is present in this response. + * @return true if a context is present, false otherwise. + */ + public boolean hasContext() + { + return context != null; + } + + /** + * Get the context from this response. + *

+ * The context is used when a subsequent update to the datatype + * is performed. + *

+ * @return the context if present, null otherwise. + */ + public Context getContext() + { + return context; + } + } + + + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/datatypes/FetchMap.java b/src/main/java/com/basho/riak/client/api/commands/datatypes/FetchMap.java new file mode 100644 index 000000000..5386175d8 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/datatypes/FetchMap.java @@ -0,0 +1,147 @@ +/* + * 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.RiakDatatype; +import com.basho.riak.client.core.query.crdt.types.RiakMap; + +/** + * 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");
+ * FetchMap fm = new FetchMap.Builder(loc).build();
+ * FetchMap.Response resp = client.execute(fm);
+ * RiakMap rMap = resp.getDatatype();
+ * Map> map = rMap.view();
+ * }
+ * 
+ *

+ * @author Dave Rusek + * @since 2.0 + */ +public final class FetchMap extends FetchDatatype +{ + private FetchMap(Builder builder) + { + super(builder); + } + + @Override + public RiakMap extractDatatype(RiakDatatype element) + { + return element.getAsMap(); + } + + @Override + protected final RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + cluster.execute(buildCoreOperation()); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) { + + @Override + protected FetchMap.Response convertResponse(DtFetchOperation.Response coreResponse) + { + RiakDatatype element = coreResponse.getCrdtElement(); + + Context context = null; + if (coreResponse.hasContext()) + { + context = new Context(coreResponse.getContext()); + } + + RiakMap datatype = extractDatatype(element); + + return new Response(datatype, context); + } + + @Override + protected Location convertQueryInfo(Location coreQueryInfo) + { + return coreQueryInfo; + } + }; + coreFuture.addListener(future); + return future; + } + + /** + * Builder used to construct a FetchMap command. + */ + public static class Builder extends FetchDatatype.Builder + { + + /** + * Construct a Builder for a FetchMap command. + * @param location the location of the map in Riak. + */ + public Builder(Location location) + { + super(location); + } + + @Override + protected Builder self() + { + return this; + } + + /** + * Construct a new FetchMap command. + * @return a new FetchMap command. + */ + public FetchMap build() + { + return new FetchMap(this); + } + + } + + /** + * Response from a FetchMap command. + *

+ * Encapsulates a RiakMap returned from the command. + *

+     * {@code
+     * ...
+     * RiakMap rMap = response.getDatatype();
+     * Map> map = rMap.view();
+     * 
+     * }
+     * 
+ *

+ */ + public static class Response extends FetchDatatype.Response + { + protected Response(RiakMap datatype, Context context) + { + super(datatype, context); + } + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/datatypes/FetchSet.java b/src/main/java/com/basho/riak/client/api/commands/datatypes/FetchSet.java new file mode 100644 index 000000000..ec8c2f5a6 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/datatypes/FetchSet.java @@ -0,0 +1,143 @@ +/* + * 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.RiakDatatype; +import com.basho.riak.client.core.query.crdt.types.RiakSet; + +/** + * Command used to fetch a set datatype from Riak. + * + *

+ *

+ * {@code
+ * Namespace ns = new Namespace("my_type", "my_bucket");
+ * Location loc = new Location(ns, "my_key");
+ * FetchSet fs = new FetchSet.Builder(loc).build();
+ * FetchSet.Response resp = client.execute(fs);
+ * RiakSet rSet = resp.getDatatype();
+ * Set set = rSet.view();
+ * }
+ * 
+ * @author Dave Rusek + * @since 2.0 + */ +public final class FetchSet extends FetchDatatype +{ + private FetchSet(Builder builder) + { + super(builder); + } + + @Override + protected final RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + cluster.execute(buildCoreOperation()); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) { + + @Override + protected FetchSet.Response convertResponse(DtFetchOperation.Response coreResponse) + { + RiakDatatype element = coreResponse.getCrdtElement(); + + Context context = null; + if (coreResponse.hasContext()) + { + context = new Context(coreResponse.getContext()); + } + + RiakSet datatype = extractDatatype(element); + + return new Response(datatype, context); + } + + @Override + protected Location convertQueryInfo(Location coreQueryInfo) + { + return coreQueryInfo; + } + }; + coreFuture.addListener(future); + return future; + } + + @Override + public RiakSet extractDatatype(RiakDatatype element) + { + return element.getAsSet(); + } + + /** + * Builder used to construct a FetchSet command. + */ + public static class Builder extends FetchDatatype.Builder + { + + /** + * Construct a builder for a FetchSet command. + * @param location the location of the set in Riak. + */ + public Builder(Location location) + { + super(location); + } + + @Override + protected Builder self() + { + return this; + } + + /** + * Construct a FetchSet command. + * @return a new FetchSet Command. + */ + public FetchSet build() + { + return new FetchSet(this); + } + } + + /** + * Response from a FetchSet command. + *

+ * Encapsulates a RiakSet returned from the FetchSet command. + *

+     * {@code
+     * ...
+     * RiakSet rSet = response.getDatatype();
+     * Set set = rSet.view();
+     * }
+     * 
+ *

+ */ + public static class Response extends FetchDatatype.Response + { + Response(RiakSet set, Context context) + { + super(set,context); + } + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/datatypes/FlagUpdate.java b/src/main/java/com/basho/riak/client/api/commands/datatypes/FlagUpdate.java new file mode 100644 index 000000000..4e422e8f3 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/datatypes/FlagUpdate.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.FlagOp; + +/** + * An update to a Riak flag datatype. + *

+ * When building an {@link UpdateMap} command + * this class is used to encapsulate the update to be performed on a + * Riak flag datatype contained in the map. It is used in conjunction with the + * {@link MapUpdate}. + *

+ * @author Dave Rusek + * @since 2.0 + */ +public class FlagUpdate implements DatatypeUpdate +{ + + private boolean flag = false; + + /** + * Construct a FlagUpdate. + * @param flag true to enable, false to disable. + */ + public FlagUpdate(boolean flag) + { + this.flag = flag; + } + + /** + * Construct a FlagUpdate with the default disabled (false) value. + */ + public FlagUpdate() + { + } + + /** + * Return the value of this FlagUpdate. + * @return true if enabled, false if disabled. + */ + public boolean isEnabled() + { + return flag; + } + + /** + * Returns the core update. + * @return the update used by the client core. + */ + @Override + public FlagOp getOp() + { + return new FlagOp(flag); + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/datatypes/MapUpdate.java b/src/main/java/com/basho/riak/client/api/commands/datatypes/MapUpdate.java new file mode 100644 index 000000000..013918338 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/datatypes/MapUpdate.java @@ -0,0 +1,202 @@ +/* + * 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.MapOp; +import com.basho.riak.client.core.util.BinaryValue; + +import java.util.HashSet; +import java.util.Set; + +/** + * An update to a Raik map datatype. + *

+ * When building an {@link UpdateMap} command + * this class is used to encapsulate the update to be performed on a + * Riak map datatype. It is a composition of other updates. + *

+ * + * @author Dave Rusek + * @since 2.0 + */ +public class MapUpdate implements DatatypeUpdate +{ + + private final Set removes = new HashSet(); + private final Set updates = new HashSet(); + + /** + * Construct an empty MapUpdate. + */ + public MapUpdate() + { + } + + /** + * Update the map in Riak by removing the counter mapped to the provided key. + * @param key the key the counter is mapped to. + * @return a reference to this object. + */ + public MapUpdate removeCounter(String key) + { + BinaryValue k = BinaryValue.create(key); + removes.add(new MapOp.MapField(MapOp.FieldType.COUNTER, k)); + return this; + } + + /** + * Update the map in Riak by removing the register mapped to the provided key. + * @param key the key the register is mapped to. + * @return a reference to this object. + */ + public MapUpdate removeRegister(String key) + { + BinaryValue k = BinaryValue.create(key); + removes.add(new MapOp.MapField(MapOp.FieldType.REGISTER, k)); + return this; + } + + /** + * Update the map in Riak by removing the flag mapped to the provided key. + * @param key the key the flag is mapped to. + * @return a reference to this object. + */ + public MapUpdate removeFlag(String key) + { + BinaryValue k = BinaryValue.create(key); + removes.add(new MapOp.MapField(MapOp.FieldType.FLAG, k)); + return this; + } + + /** + * Update the map in Riak by removing the set mapped to the provided key. + * @param key the key the set is mapped to. + * @return a reference to this object. + */ + public MapUpdate removeSet(String key) + { + BinaryValue k = BinaryValue.create(key); + removes.add(new MapOp.MapField(MapOp.FieldType.SET, k)); + return this; + } + + /** + * Update the map in Riak by removing the map mapped to the provided key. + * @param key the key the map is mapped to. + * @return a reference to this object. + */ + public MapUpdate removeMap(String key) + { + BinaryValue k = BinaryValue.create(key); + removes.add(new MapOp.MapField(MapOp.FieldType.MAP, k)); + return this; + } + + /** + * Update the map in Riak by adding/updating the map mapped to the provided key. + *

+ * If there is no map referenced by the key, it is created + *

+ * + * @param key the key the map is mapped to. + * @param builder the update to apply to the map the key is mapped to. If none exists it is created. + * @return a reference to this object. + */ + public MapUpdate update(String key, MapUpdate builder) + { + BinaryValue k = BinaryValue.create(key); + updates.add(new MapOp.MapUpdate(new MapOp.MapField(MapOp.FieldType.MAP, k), builder.getOp())); + return this; + } + + /** + * Update the map in Riak by adding/updating the set mapped to the provided key. + *

+ * If there is no set referenced by the key, it is created + *

+ * + * @param key the key the set is mapped to. + * @param builder the update to apply to the set the key is mapped to. If none exists it is created. + * @return a reference to this object. + */ + public MapUpdate update(String key, SetUpdate builder) + { + BinaryValue k = BinaryValue.create(key); + updates.add(new MapOp.MapUpdate(new MapOp.MapField(MapOp.FieldType.SET, k), builder.getOp())); + return this; + } + + /** + * Update the map in Riak by adding/updating the counter mapped to the provided key. + *

+ * If there is no counter referenced by the key, it is created + *

+ * + * @param key the key the set is mapped to. + * @param builder the update to apply to the counter the key is mapped to. If none exists it is created. + * @return a reference to this object. + */ + public MapUpdate update(String key, CounterUpdate builder) + { + BinaryValue k = BinaryValue.create(key); + updates.add(new MapOp.MapUpdate(new MapOp.MapField(MapOp.FieldType.COUNTER, k), builder.getOp())); + return this; + } + + /** + * Update the map in Riak by adding/updating the register mapped to the provided key. + *

+ * If there is no register referenced by the key, it is created + *

+ * + * @param key the key the register is mapped to. + * @param builder the update to apply to the register the key is mapped to. If none exists it is created. + * @return a reference to this object. + */ + public MapUpdate update(String key, RegisterUpdate builder) + { + BinaryValue k = BinaryValue.create(key); + updates.add(new MapOp.MapUpdate(new MapOp.MapField(MapOp.FieldType.REGISTER, k), builder.getOp())); + return this; + } + + /** + * Update the map in Riak by adding/updating the flag mapped to the provided key. + *

+ * If there is no flag referenced by the key, it is created + *

+ * + * @param key the key the register is mapped to. + * @param builder the update to apply to the flag the key is mapped to. If none exists it is created. + * @return a reference to this object. + */ + public MapUpdate update(String key, FlagUpdate builder) + { + BinaryValue k = BinaryValue.create(key); + updates.add(new MapOp.MapUpdate(new MapOp.MapField(MapOp.FieldType.FLAG, k), builder.getOp())); + return this; + } + + /** + * Returns the core update. + * @return the update used by the client core. + */ + @Override + public MapOp getOp() + { + return new MapOp(removes, updates); + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/datatypes/RegisterUpdate.java b/src/main/java/com/basho/riak/client/api/commands/datatypes/RegisterUpdate.java new file mode 100644 index 000000000..184c5b23d --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/datatypes/RegisterUpdate.java @@ -0,0 +1,92 @@ +/* + * 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.RegisterOp; +import com.basho.riak.client.core.util.BinaryValue; + +/** + * An update to a Riak register datatype. + *

+ * When building an {@link UpdateMap} command + * this class is used to encapsulate the update to be performed on a + * Riak register datatype contained in the map. It is used in conjunction with the + * {@link MapUpdate}. + *

+ * @author Dave Rusek + * @since 2.0 + */ +public class RegisterUpdate implements DatatypeUpdate +{ + + private final BinaryValue value; + + /** + * Construct a RegisterUpdate with the provided bytes. + * @param value the bytes representing the register. + */ + public RegisterUpdate(byte[] value) + { + this.value = BinaryValue.create(value); + } + + /** + * Construct a RegisterUpdate with the provided BinaryValue. + * @param value the BinaryValue representing the register. + */ + public RegisterUpdate(BinaryValue value) + { + this.value = value; + } + + /** + * Construct a RegisterUpdate with the provided String. + *

+ * Note the String is converted to bytes using the default Charset. + *

+ * @param value the String representing the register. + */ + public RegisterUpdate(String value) + { + this.value = BinaryValue.create(value); + } + + /** + * Get the register contained in this update. + * @return the register as a BinaryValue. + */ + public BinaryValue get() + { + return value; + } + + /** + * Returns the core update. + * @return the update used by the client core. + */ + @Override + public RegisterOp getOp() + { + return new RegisterOp(value); + } + + @Override + public String toString() + { + return value.toString(); + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/datatypes/SetUpdate.java b/src/main/java/com/basho/riak/client/api/commands/datatypes/SetUpdate.java new file mode 100644 index 000000000..4f769c93d --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/datatypes/SetUpdate.java @@ -0,0 +1,124 @@ +/* + * 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.SetOp; +import com.basho.riak.client.core.util.BinaryValue; + +import java.util.HashSet; +import java.util.Set; + +/** + * An update to a Riak set datatype. + *

+ * When building an {@link UpdateSet} or {@link UpdateMap} command + * this class is used to encapsulate the update to be performed on a + * Riak set datatype. + *

+ * @author Dave Rusek + * @since 2.0 + */ +public class SetUpdate implements DatatypeUpdate +{ + + private final Set adds = new HashSet(); + private final Set removes = new HashSet(); + + /** + * Constructs an empty SetUpdate. + */ + public SetUpdate() + { + } + + /** + * Add the provided value to the set in Riak. + * @param value the value to be added. + * @return a reference to this object. + */ + public SetUpdate add(BinaryValue value) + { + this.adds.add(value); + return this; + } + + /** + * Add the provided value to the set in Riak. + * @param value the value to be added. + * @return a reference to this object. + */ + public SetUpdate add(String value) + { + this.adds.add(BinaryValue.create(value)); + return this; + } + + /** + * Remove the provided value from the set in Riak. + * @param value the value to be removed. + * @return a reference to this object. + */ + public SetUpdate remove(BinaryValue value) + { + this.removes.add(value); + return this; + } + + /** + * Remove the provided value from the set in Riak. + * @param value the value to be removed. + * @return a reference to this object. + */ + public SetUpdate remove(String value) + { + this.removes.add(BinaryValue.create(value)); + return this; + } + + /** + * Get the set of additions contained in this update. + * @return the set of additions. + */ + public Set getAdds() + { + return adds; + } + + /** + * Get the set of removes contained in this update. + * @return the set of removes. + */ + public Set getRemoves() + { + return removes; + } + + /** + * Returns the core update. + * @return the update used by the client core. + */ + @Override + public SetOp getOp() + { + return new SetOp(adds, removes); + } + + @Override + public String toString() + { + return "Add: " + adds + " Remove: " + removes; + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/datatypes/UpdateCounter.java b/src/main/java/com/basho/riak/client/api/commands/datatypes/UpdateCounter.java new file mode 100644 index 000000000..062736fda --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/datatypes/UpdateCounter.java @@ -0,0 +1,176 @@ +/* + * 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.DtUpdateOperation; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.crdt.types.RiakCounter; +import com.basho.riak.client.core.query.crdt.types.RiakDatatype; +import com.basho.riak.client.core.util.BinaryValue; + +/** + * Command used to update or create a counter datatype in Riak. + * + *

+ * To update or create a counter in Riak you construct a {@link CounterUpdate} and use + * this command to send it to Riak. + *

+ * {@code 
+ * Namespace ns = new Namespace("my_type", "my_bucket");
+ * Location loc = new Location(ns, "my_key");
+ * CounterUpdate update = new CounterUpdate(10L);
+ * 
+ * UpdateCounter uc = new UpdateCounter.Builder(loc, update).withReturnDatatype(true).build();
+ * UpdateCounter.Response resp = client.execute(uc);
+ * RiakCounter counter = resp.getDatatype();
+ * 
+ * }
+ * 
+ *

+ * @author Dave Rusek + * @author Brian Roach + * @since 2.0 + */ +public class UpdateCounter extends UpdateDatatype +{ + private final CounterUpdate update; + + private UpdateCounter(Builder builder) + { + super(builder); + this.update = builder.update; + } + + @Override + protected RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + cluster.execute(buildCoreOperation(update)); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) + { + @Override + protected Response convertResponse(DtUpdateOperation.Response coreResponse) + { + RiakCounter counter = null; + if (coreResponse.hasCrdtElement()) + { + RiakDatatype element = coreResponse.getCrdtElement(); + counter = element.getAsCounter(); + } + BinaryValue returnedKey = coreResponse.hasGeneratedKey() + ? coreResponse.getGeneratedKey() + : null; + + Context returnedCtx = null; + if (coreResponse.hasContext()) + { + returnedCtx = new Context(coreResponse.getContext()); + } + + return new Response(returnedCtx, counter, returnedKey); + } + + @Override + protected Location convertQueryInfo(Location coreQueryInfo) + { + return coreQueryInfo; + } + + }; + coreFuture.addListener(future); + return future; + } + + /** + * A response from an UpdateCounter command. + */ + public static final class Response extends UpdateDatatype.Response + { + private Response(Context context, RiakCounter datatype, BinaryValue generatedKey) + { + super(context, datatype, generatedKey); + } + + } + + /** + * Builder used to construct an UpdateCounter command. + */ + public static final class Builder extends UpdateDatatype.Builder + { + private final CounterUpdate update; + + /** + * Construct a Builder for an UpdateCounter command. + * @param location the location of the counter in Riak. + * @param update the update to apply to the counter. + */ + public Builder(Location location, CounterUpdate update) + { + super(location); + if (update == null) + { + throw new IllegalArgumentException("Update cannot be null"); + } + this.update = update; + } + + /** + * Constructs a builder for an UpdateCounter command with only a Namespace. + *

+ * By providing only a Namespace with the update, Riak will create the + * counter, generate the key, + * and return it in the response. + *

+ * @param namespace the namespace to create the datatype. + * @param update the CounterUpdate to apply + * @see Response#getGeneratedKey() + */ + public Builder(Namespace namespace, CounterUpdate update) + { + super(namespace); + if (update == null) + { + throw new IllegalArgumentException("Update cannot be null"); + } + this.update = update; + } + + /** + * Construct the UpdateCounter command. + * @return a new UpdateCounter Command. + */ + @Override + public UpdateCounter build() + { + return new UpdateCounter(this); + } + + @Override + protected Builder self() + { + return this; + } + + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/datatypes/UpdateDatatype.java b/src/main/java/com/basho/riak/client/api/commands/datatypes/UpdateDatatype.java new file mode 100644 index 000000000..a37fe27cf --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/datatypes/UpdateDatatype.java @@ -0,0 +1,341 @@ +/* + * 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.DtUpdateOperation; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.crdt.types.RiakDatatype; +import com.basho.riak.client.core.util.BinaryValue; + +import java.util.HashMap; +import java.util.Map; + +/** + * Base abstract class used for all datatype updates. + * @author Dave Rusek + * @author Brian Roach + * @since 2.0 + */ +public abstract class UpdateDatatype extends RiakCommand +{ + + protected final Namespace namespace; + protected final BinaryValue key; + private final Context ctx; + private final Map, Object> options = new HashMap, Object>(); + + @SuppressWarnings("unchecked") + UpdateDatatype(Builder builder) + { + this.namespace = builder.namespace; + this.key = builder.key; + this.ctx = builder.ctx; + this.options.putAll(builder.options); + } + + protected final DtUpdateOperation buildCoreOperation(DatatypeUpdate update) + { + DtUpdateOperation.Builder builder; + + if (key != null) + { + Location loc = new Location(namespace, key); + builder = new DtUpdateOperation.Builder(loc); + } + else + { + builder = new DtUpdateOperation.Builder(namespace); + } + + if (ctx != null) + { + builder.withContext(ctx.getValue()); + } + + builder.withOp(update.getOp()); + + for (Map.Entry, Object> entry : options.entrySet()) + { + if (entry.getKey() == Option.DW) + { + builder.withDw(((Quorum) entry.getValue()).getIntValue()); + } + else if (entry.getKey() == Option.N_VAL) + { + builder.withNVal((Integer) entry.getValue()); + } + else if (entry.getKey() == Option.PW) + { + builder.withPw(((Quorum) entry.getValue()).getIntValue()); + } + else if (entry.getKey() == Option.RETURN_BODY) + { + builder.withReturnBody((Boolean) entry.getValue()); + } + else if (entry.getKey() == Option.SLOPPY_QUORUM) + { + builder.withSloppyQuorum((Boolean) entry.getValue()); + } + else if (entry.getKey() == Option.TIMEOUT) + { + builder.withTimeout((Integer) entry.getValue()); + } + else if (entry.getKey() == Option.W) + { + builder.withW(((Quorum) entry.getValue()).getIntValue()); + } + } + + return builder.build(); + + } + + /** + * Tuning parameters for all datatype fetch commands. + * @author Dave Rusek + * @since 2.0 + */ + public static final class Option extends RiakOption + { + + /** + * Durable Write Quorum. + * How many replicas to commit to durable storage before returning a successful response. + */ + public static final Option DW = new Option("DW"); + public static final Option N_VAL = new Option("N_VAL"); + /** + * Primary Write Quorum. + * How many primary nodes must be up when the write is attempted. + */ + public static final Option PW = new Option("PW"); + /** + * Return Body. + * Return the object stored in Riak. Note this will return all siblings. + */ + public static final Option RETURN_BODY = new Option("RETURN_BODY"); + public static final Option SLOPPY_QUORUM = new Option("SLOPPY_QUORUM"); + /** + * Timeout. + * Sets the server-side timeout for this operation. The default in Riak is 60 seconds. + */ + public static final Option TIMEOUT = new Option("TIMEOUT"); + /** + * Write Quorum. + * How many replicas to write to before returning a successful response. + */ + public static final Option W = new Option("W"); + + public Option(String name) + { + super(name); + } + } + + /** + * Base abstract builder for all datatype update builders. + */ + public static abstract class Builder> + { + private final Namespace namespace; + private BinaryValue key; + private Context ctx; + private Map, Object> options = new HashMap, Object>(); + + /** + * Constructs a builder for a datatype update. + * @param location the location of the datatype object in Riak. + */ + Builder(Location location) + { + if (location == null) + { + throw new IllegalArgumentException("Location cannot be null."); + } + this.namespace = location.getNamespace(); + this.key = location.getKey(); + } + + /** + * Constructs a builder for a datatype update with only a Namespace. + *

+ * By providing only a Namespace with the update, Riak will create the + * datatype object, generate the key, + * and return it in the response. + *

+ * @param namespace the namespace to create the datatype. + * @see Response#getGeneratedKey() + */ + Builder(Namespace namespace) + { + if (namespace == null) + { + throw new IllegalArgumentException("Namespace cannot be null."); + } + this.namespace = namespace; + } + + /** + * Include the context from a previous fetch. + *

+ * When updating a previously fetched set or map you generally + * want to include the context returned from that query with the update. + *

+ * @param context the Context from a previous fetch. + * @return a reference to this object. + */ + public T withContext(Context context) + { + if (context == null) + { + throw new IllegalArgumentException("Context cannot be null."); + } + this.ctx = context; + return self(); + } + + /** + * Add an optional setting for this command. + * This will be passed along with the request to Riak to tell it how + * to behave when servicing the request. + * + * @param option the option + * @param value the value for the option + * @return a reference to this object. + * @see Option + */ + public T withOption(Option option, U value) + { + this.options.put(option, value); + return self(); + } + + /** + * 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 T withTimeout(int timeout) + { + withOption(Option.TIMEOUT, timeout); + return self(); + } + + /** + * Return the updated datatype. + *

+ * By default the datatype update commands are "fire and forget" in that + * the modified datatype in Riak is not returned. Setting this to true + * returns the modified datatype in the response. + *

+ * @param returnDatatype true to return the modified datatype. + * @return a reference to this object. + */ + public T withReturnDatatype(boolean returnDatatype) + { + withOption(Option.RETURN_BODY, true); + return self(); + } + + protected abstract T self(); + protected abstract UpdateDatatype build(); + } + + /** + * Base abstract class used for all datatype update responses. + */ + public static abstract class Response + { + private final T datatype; + private final Context context; + private final BinaryValue generatedKey; + + Response(Context context, T datatype, BinaryValue generatedKey) + { + this.datatype = datatype; + this.context = context; + this.generatedKey = generatedKey; + } + + /** + * Check to see if this response includes a Context. + * @return true if Context is present, false otherwise. + */ + public boolean hasContext() + { + return context != null; + } + + /** + * Get the returned context. + * @return the Context, or null if not present. + */ + public Context getContext() + { + return context; + } + + /** + * Check to see if this resposne includes the updated datatype. + * @return true if datatype is present, false otherwise. + * @see Builder#withReturnDatatype(boolean) + */ + public boolean hasDatatype() + { + return datatype != null; + } + + /** + * Get the returned datatype. + * @return the updated datatype, or null if not present. + * @see Builder#withReturnDatatype(boolean) + */ + public T getDatatype() + { + return datatype; + } + + /** + * Check to see if the response includes a generated key. + *

This will only be true if the datatype update was sent with + * only a Namespace

+ * @return true if key is present, false otherwise. + */ + public boolean hasGeneratedKey() + { + return generatedKey != null; + } + + /** + * Get the returned generated key. + * @return the key, or null if not present. + * @see #hasGeneratedKey() + */ + public BinaryValue getGeneratedKey() + { + return generatedKey; + } + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/datatypes/UpdateMap.java b/src/main/java/com/basho/riak/client/api/commands/datatypes/UpdateMap.java new file mode 100644 index 000000000..2b8bc6f94 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/datatypes/UpdateMap.java @@ -0,0 +1,175 @@ +/* + * 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.DtUpdateOperation; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.crdt.types.RiakDatatype; +import com.basho.riak.client.core.query.crdt.types.RiakMap; +import com.basho.riak.client.core.util.BinaryValue; + +/** + * Command used to update or create a map datatype in Riak. + * + *

+ * To update or create a map in Riak you construct a {@link MapUpdate} and use + * this command to send it to Riak. + *

+ * {@code
+ * Namespace ns = new Namespace("my_type", "my_bucket");
+ * Location loc = new Location(ns, "my_key");
+ * 
+ * CounterUpdate cUpdate = new CounterUpdate(10L);
+ * MapUpdate update = new MapUpdate().update("my_key", cUpdate);
+ * 
+ * UpdateMap um = new UpdateMap.Builder(loc, update).withReturnDatatype(true).build();
+ * UpdateMap.Response resp = client.execute(um);
+ * RiakMap map = resp.getDatatype();
+ * 
+ * }
+ * 
+ *

+ * @author Brian Roach + * @since 2.0 + */ +public class UpdateMap extends UpdateDatatype +{ + private final MapUpdate update; + + private UpdateMap(Builder builder) + { + super(builder); + this.update = builder.update; + } + + @Override + protected RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + cluster.execute(buildCoreOperation(update)); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) + { + @Override + protected Response convertResponse(DtUpdateOperation.Response coreResponse) + { + RiakMap map = null; + if (coreResponse.hasCrdtElement()) + { + RiakDatatype element = coreResponse.getCrdtElement(); + map = element.getAsMap(); + } + BinaryValue returnedKey = coreResponse.hasGeneratedKey() + ? coreResponse.getGeneratedKey() + : null; + + Context returnedCtx = null; + if (coreResponse.hasContext()) + { + returnedCtx = new Context(coreResponse.getContext()); + } + + return new Response(returnedCtx, map, returnedKey); + } + + @Override + protected Location convertQueryInfo(Location coreQueryInfo) + { + return coreQueryInfo; + } + + }; + coreFuture.addListener(future); + return future; + } + + public static final class Response extends UpdateDatatype.Response + { + private Response(Context context, RiakMap datatype, BinaryValue generatedKey) + { + super(context, datatype, generatedKey); + } + + } + + /** + * Builder used to construct an UpdateMap command. + */ + public static final class Builder extends UpdateDatatype.Builder + { + private final MapUpdate update; + + /** + * Construct a Builder for an UpdateMap command. + * @param location the location of the map in Riak. + * @param update the update to apply to the map. + */ + public Builder(Location location, MapUpdate update) + { + super(location); + if (update == null) + { + throw new IllegalArgumentException("Update cannot be null"); + } + this.update = update; + } + + /** + * Constructs a builder for an UpdateMap command with only a Namespace. + *

+ * By providing only a Namespace with the update, Riak will create the + * map, generate the key, + * and return it in the response. + *

+ * @param namespace the namespace to create the datatype. + * @param update the update to apply + * @see Response#getGeneratedKey() + */ + public Builder(Namespace namespace, MapUpdate update) + { + super(namespace); + if (update == null) + { + throw new IllegalArgumentException("Update cannot be null"); + } + this.update = update; + } + + @Override + protected Builder self() + { + return this; + } + + /** + * Construct a new UpdateMap command. + * @return a new UpdateMap command. + */ + @Override + public UpdateMap build() + { + return new UpdateMap(this); + } + + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/datatypes/UpdateSet.java b/src/main/java/com/basho/riak/client/api/commands/datatypes/UpdateSet.java new file mode 100644 index 000000000..a2a6d1b75 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/datatypes/UpdateSet.java @@ -0,0 +1,174 @@ +/* + * 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.DtUpdateOperation; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.crdt.types.RiakDatatype; +import com.basho.riak.client.core.query.crdt.types.RiakSet; +import com.basho.riak.client.core.util.BinaryValue; + +/** + * Command used to update or create a set datatype in Riak. + * + *

+ * To update or create a set in Riak you construct a {@link SetUpdate} and use + * this command to send it to Riak. + *

+ * {@code 
+ * Namespace ns = new Namespace("my_type", "my_bucket");
+ * Location loc = new Location(ns, "my_key");
+ * SetUpdate update = new SetUpdate().add("some_new_value");
+ * 
+ * UpdateSet us = new UpdateSet.Builder(loc, update).withReturnDatatype(true).build();
+ * UpdateSet.Response resp = client.execute(us);
+ * RiakSet = resp.getDatatype();
+ * }
+ * 
+ *

+ * + * @author Dave Rusek + * @author Brian Roach + * @since 2.0 + */ +public class UpdateSet extends UpdateDatatype +{ + private final SetUpdate update; + + private UpdateSet(Builder builder) + { + super(builder); + this.update = builder.update; + } + + @Override + protected RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + cluster.execute(buildCoreOperation(update)); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) + { + @Override + protected Response convertResponse(DtUpdateOperation.Response coreResponse) + { + RiakSet set = null; + if (coreResponse.hasCrdtElement()) + { + RiakDatatype element = coreResponse.getCrdtElement(); + set = element.getAsSet(); + } + BinaryValue returnedKey = coreResponse.hasGeneratedKey() + ? coreResponse.getGeneratedKey() + : null; + Context returnedCtx = null; + if (coreResponse.hasContext()) + { + returnedCtx = new Context(coreResponse.getContext()); + } + return new Response(returnedCtx, set, returnedKey); + } + + @Override + protected Location convertQueryInfo(Location coreQueryInfo) + { + return coreQueryInfo; + } + + }; + coreFuture.addListener(future); + return future; + } + + /** + * Builder used to construct an UpdateSet command. + */ + public static class Builder extends UpdateDatatype.Builder + { + private final SetUpdate update; + + /** + * Construct a Builder for an UpdateSet command. + * @param location the location of the set in Riak. + * @param update the update to apply to the set. + */ + public Builder(Location location, SetUpdate update) + { + super(location); + if (update == null) + { + throw new IllegalArgumentException("Update cannot be null"); + } + this.update = update; + } + + /** + * Constructs a builder for an UpdateSet command with only a Namespace. + *

+ * By providing only a Namespace with the update, Riak will create the + * set, generate the key, + * and return it in the response. + *

+ * @param namespace the namespace to create the datatype. + * @param update the update to apply + * @see Response#getGeneratedKey() + */ + public Builder(Namespace namespace, SetUpdate update) + { + super(namespace); + if (update == null) + { + throw new IllegalArgumentException("Update cannot be null"); + } + this.update = update; + } + + /** + * Construct a new UpdateSet command. + * @return a new UpdateSet command. + */ + @Override + public UpdateSet build() + { + return new UpdateSet(this); + } + + @Override + protected Builder self() + { + return this; + } + + } + + /** + * A response from an UpdateSet command. + */ + public static class Response extends UpdateDatatype.Response + { + private Response(Context context, RiakSet datatype, BinaryValue generatedKey) + { + super(context, datatype, generatedKey); + } + + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/datatypes/package-info.java b/src/main/java/com/basho/riak/client/api/commands/datatypes/package-info.java new file mode 100644 index 000000000..2ff4beebc --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/datatypes/package-info.java @@ -0,0 +1,16 @@ +/** + * Commands for manipulating CRDTs in Riak. + *

Fetching datatypes

+ *
    + *
  • {@link com.basho.riak.client.api.commands.datatypes.FetchCounter}
  • + *
  • {@link com.basho.riak.client.api.commands.datatypes.FetchSet}
  • + *
  • {@link com.basho.riak.client.api.commands.datatypes.FetchMap}
  • + *
+ *

Creating / Updating datatypes

+ *
    + *
  • {@link com.basho.riak.client.api.commands.datatypes.UpdateCounter}
  • + *
  • {@link com.basho.riak.client.api.commands.datatypes.UpdateSet}
  • + *
  • {@link com.basho.riak.client.api.commands.datatypes.UpdateMap}
  • + *
+ */ +package com.basho.riak.client.api.commands.datatypes; \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/api/commands/indexes/BigIntIndexQuery.java b/src/main/java/com/basho/riak/client/api/commands/indexes/BigIntIndexQuery.java new file mode 100644 index 000000000..9de37957a --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/indexes/BigIntIndexQuery.java @@ -0,0 +1,212 @@ +/* + * 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.indexes; + +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.SecondaryIndexQueryOperation; +import com.basho.riak.client.api.commands.CoreFutureAdapter; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.util.BinaryValue; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +/** + * Performs a 2i query where the 2i index keys are numeric. + * + *

+ * A BigIntIndexQuery is used when you are using integers for your 2i keys. The + * parameters are provided as BigInteger values. Use this query if your + * 2i key values exceed that + * which can be stored in a (64 bit) long, + *

+ *
+ * {@code
+ * Namespace ns = new Namespace("my_type", "my_bucket");
+ * BigInteger key = new BigInteger(someReallyLongNumber);
+ * BigIntIndexQuery q = new BigIntIndexQuery.Builder(ns, "my_index", key).build();
+ * BigIntIndexQuery.Response resp = client.execute(q);}
+ * @author Brian Roach + * @since 2.0 + */ +public class BigIntIndexQuery extends SecondaryIndexQuery +{ + private final IndexConverter converter; + + @Override + protected IndexConverter getConverter() + { + return converter; + } + + protected BigIntIndexQuery(Init builder) + { + super(builder); + this.converter = new IndexConverter() + { + @Override + public BigInteger convert(BinaryValue input) + { + // Riak. US_ASCII string instead of integer + return new BigInteger(input.toStringUtf8()); + } + + @Override + public BinaryValue convert(BigInteger input) + { + return BinaryValue.createFromUtf8(input.toString()); + } + }; + } + + @Override + protected RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + executeCoreAsync(cluster); + + BigIntQueryFuture future = new BigIntQueryFuture(coreFuture); + coreFuture.addListener(future); + return future; + + } + + protected final class BigIntQueryFuture extends CoreFutureAdapter + { + public BigIntQueryFuture(RiakFuture coreFuture) + { + super(coreFuture); + } + + @Override + protected Response convertResponse(SecondaryIndexQueryOperation.Response coreResponse) + { + return new Response(namespace, coreResponse, converter); + } + + @Override + protected BigIntIndexQuery convertQueryInfo(SecondaryIndexQueryOperation.Query coreQueryInfo) + { + return BigIntIndexQuery.this; + } + } + + protected static abstract class Init> extends SecondaryIndexQuery.Init + { + + public Init(Namespace namespace, String indexName, S start, S end) + { + super(namespace, indexName + Type._INT, start, end); + } + + public Init(Namespace namespace, String indexName, S match) + { + super(namespace, indexName + Type._INT, match); + } + + @Override + public T withRegexTermFilter(String filter) + { + throw new IllegalArgumentException("Cannot use term filter with _int query"); + } + } + + /** + * Builder used to construct a BigIntIndexQuery. + */ + public static class Builder extends Init + { + + /** + * Construct a Builder for a BigIntIndexQuery with a range. + *

+ * Note that your index name should not include the Riak {@literal _int} or + * {@literal _bin} extension. + *

+ * @param namespace The namespace in Riak to query. + * @param indexName The name of the index in Riak. + * @param start the start of the 2i range. + * @param end the end of the 2i range. + */ + public Builder(Namespace namespace, String indexName, BigInteger start, BigInteger end) + { + super(namespace, indexName, start, end); + } + + /** + * Construct a Builder for a BigIntIndexQuery with a single 2i key. + *

+ * Note that your index name should not include the Riak {@literal _int} or + * {@literal _bin} extension. + *

+ * @param namespace The namespace in Riak to query. + * @param indexName The name of the index in Riak. + * @param match the 2i key. + */ + public Builder(Namespace namespace, String indexName, BigInteger match) + { + super(namespace, indexName, match); + } + + @Override + protected Builder self() + { + return this; + } + + /** + * Construct the query. + * @return a new BigIntIndexQuery. + */ + public BigIntIndexQuery build() + { + return new BigIntIndexQuery(this); + } + } + + public static class Response extends SecondaryIndexQuery.Response + { + protected Response(Namespace queryLocation, SecondaryIndexQueryOperation.Response coreResponse, IndexConverter converter) + { + super(queryLocation, coreResponse, converter); + } + + @Override + public List getEntries() + { + List convertedList = new ArrayList(); + for (SecondaryIndexQueryOperation.Response.Entry e : coreResponse.getEntryList()) + { + Location loc = getLocationFromCoreEntry(e); + Entry ce = new Entry(loc, e.getIndexKey(), converter); + convertedList.add(ce); + } + return convertedList; + } + + public class Entry extends SecondaryIndexQuery.Response.Entry + { + protected Entry(Location riakObjectLocation, BinaryValue indexKey, IndexConverter converter) + { + super(riakObjectLocation, indexKey, converter); + } + + } + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/indexes/BinIndexQuery.java b/src/main/java/com/basho/riak/client/api/commands/indexes/BinIndexQuery.java new file mode 100644 index 000000000..67d373824 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/indexes/BinIndexQuery.java @@ -0,0 +1,214 @@ +/* + * 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.commands.indexes; + +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.SecondaryIndexQueryOperation; +import com.basho.riak.client.api.commands.CoreFutureAdapter; +import com.basho.riak.client.api.commands.indexes.SecondaryIndexQuery.IndexConverter; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.util.BinaryValue; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +/** + * Performs a 2i query where the 2i index keys are strings. + * + *

+ * A RawIndexQuery is used when you are using strings for your 2i keys. The + * parameters are provided as String objects. + *

+ *
+ * {@code
+ * Namespace ns = new Namespace("my_type", "my_bucket");
+ * String key = "some_key";
+ * BinIndexQuery q = new BinIndexQuery.Builder(ns, "my_index", key).build();
+ * BinIndexQuery.Response resp = client.execute(q);}
+ * @author Brian Roach + * @since 2.0 + */ +public class BinIndexQuery extends SecondaryIndexQuery +{ + private final Charset charset; + private final IndexConverter converter; + + protected BinIndexQuery(Init builder) + { + super(builder); + this.charset = builder.charset; + this.converter = new IndexConverter() + { + @Override + public String convert(BinaryValue input) + { + return input.toString(charset); + } + + @Override + public BinaryValue convert(String input) + { + return BinaryValue.create(input, charset); + } + }; + } + + @Override + protected IndexConverter getConverter() + { + return converter; + } + + @Override + protected RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + executeCoreAsync(cluster); + + BinQueryFuture future = new BinQueryFuture(coreFuture); + coreFuture.addListener(future); + return future; + } + + protected final class BinQueryFuture extends CoreFutureAdapter + { + public BinQueryFuture(RiakFuture coreFuture) + { + super(coreFuture); + } + + @Override + protected Response convertResponse(SecondaryIndexQueryOperation.Response coreResponse) + { + return new Response(namespace, coreResponse, converter); + } + + @Override + protected BinIndexQuery convertQueryInfo(SecondaryIndexQueryOperation.Query coreQueryInfo) + { + return BinIndexQuery.this; + } + } + + protected static abstract class Init> extends SecondaryIndexQuery.Init + { + private Charset charset = Charset.defaultCharset(); + + public Init(Namespace namespace, String indexName, S start, S end) + { + super(namespace, indexName + Type._BIN, start, end); + } + + public Init(Namespace namespace, String indexName, S match) + { + super(namespace, indexName + Type._BIN, match); + } + + T withCharacterSet(Charset charset) + { + this.charset = charset; + return self(); + } + + } + + /** + * Builder used to construct a BinIndexQuery. + */ + public static class Builder extends Init + { + + /** + * Construct a Builder for a BinIndexQuery with a range. + *

+ * Note that your index name should not include the Riak {@literal _int} or + * {@literal _bin} extension. + *

+ * @param namespace The namespace in Riak to query. + * @param indexName The index name in Riak to query. + * @param start The start of the 2i range. + * @param end The end of the 2i range. + */ + public Builder(Namespace namespace, String indexName, String start, String end) + { + super(namespace, indexName, start, end); + } + + /** + * Construct a Builder for a BinIndexQuery with a single 2i key. + *

+ * Note that your index name should not include the Riak {@literal _int} or + * {@literal _bin} extension. + *

+ * @param namespace The namespace in Riak to query. + * @param indexName The name of the index in Riak. + * @param match the 2i key. + */ + public Builder(Namespace namespace, String indexName, String match) + { + super(namespace, indexName, match); + } + + @Override + protected Builder self() + { + return this; + } + + /** + * Construct the query. + * @return a new BinIndexQuery + */ + public BinIndexQuery build() + { + return new BinIndexQuery(this); + } + + } + + public static class Response extends SecondaryIndexQuery.Response + { + protected Response(Namespace queryLocation, SecondaryIndexQueryOperation.Response coreResponse, IndexConverter converter) + { + super(queryLocation, coreResponse, converter); + } + + @Override + public List getEntries() + { + List convertedList = new ArrayList(); + for (SecondaryIndexQueryOperation.Response.Entry e : coreResponse.getEntryList()) + { + Location loc = getLocationFromCoreEntry(e); + Entry ce = new Entry(loc, e.getIndexKey(), converter); + convertedList.add(ce); + } + return convertedList; + } + + public class Entry extends SecondaryIndexQuery.Response.Entry + { + protected Entry(Location riakObjectLocation, BinaryValue indexKey, IndexConverter converter) + { + super(riakObjectLocation, indexKey, converter); + } + + } + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/indexes/IntIndexQuery.java b/src/main/java/com/basho/riak/client/api/commands/indexes/IntIndexQuery.java new file mode 100644 index 000000000..6c614bbf3 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/indexes/IntIndexQuery.java @@ -0,0 +1,211 @@ +/* + * 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.commands.indexes; + +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.SecondaryIndexQueryOperation; +import com.basho.riak.client.api.commands.CoreFutureAdapter; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.util.BinaryValue; +import java.util.ArrayList; +import java.util.List; + +/** + * Performs a 2i query where the 2i index keys are numeric. + * + *

+ * A IntIndexQuery is used when you are using integers for your 2i keys. The + * parameters are provided as long values. + *

+ *
+ * {@code
+ * Namespace ns = new Namespace("my_type", "my_bucket");
+ * long key = 1234L;
+ * IntIndexQuery q = new IntIndexQuery.Builder(ns, "my_index", key).build();
+ * IntIndexQuery.Response resp = client.execute(q);}
+ * @author Brian Roach + * @since 2.0 + */ +public class IntIndexQuery extends SecondaryIndexQuery +{ + private final IndexConverter converter; + + @Override + protected IndexConverter getConverter() + { + return converter; + } + + protected IntIndexQuery(Init builder) + { + super(builder); + this.converter = new IndexConverter() + { + @Override + public Long convert(BinaryValue input) + { + // Riak. US_ASCII string instead of integer + return Long.valueOf(input.toStringUtf8()); + } + + @Override + public BinaryValue convert(Long input) + { + // Riak. US_ASCII string instead of integer + return BinaryValue.createFromUtf8(String.valueOf(input)); + } + + }; + } + + @Override + protected RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + executeCoreAsync(cluster); + + IntQueryFuture future = new IntQueryFuture(coreFuture); + coreFuture.addListener(future); + return future; + + } + + protected final class IntQueryFuture extends CoreFutureAdapter + { + public IntQueryFuture(RiakFuture coreFuture) + { + super(coreFuture); + } + + @Override + protected Response convertResponse(SecondaryIndexQueryOperation.Response coreResponse) + { + return new Response(namespace, coreResponse, converter); + } + + @Override + protected IntIndexQuery convertQueryInfo(SecondaryIndexQueryOperation.Query coreQueryInfo) + { + return IntIndexQuery.this; + } + } + + protected static abstract class Init> extends SecondaryIndexQuery.Init + { + + public Init(Namespace namespace, String indexName, S start, S end) + { + super(namespace, indexName + Type._INT, start, end); + } + + public Init(Namespace namespace, String indexName, S match) + { + super(namespace, indexName + Type._INT, match); + } + + @Override + public T withRegexTermFilter(String filter) + { + throw new IllegalArgumentException("Cannot use term filter with _int query"); + } + } + + /** + * Builder used to construct a IntIndexQuery. + */ + public static class Builder extends Init + { + + /** + * Construct a Builder for a IntIndexQuery with a range. + *

+ * Note that your index name should not include the Riak {@literal _int} or + * {@literal _bin} extension. + *

+ * @param namespace The namespace in Riak to query. + * @param indexName The name of the index in Riak. + * @param start the start of the 2i range. + * @param end the end of the 2i range. + */ + public Builder(Namespace namespace, String indexName, Long start, Long end) + { + super(namespace, indexName, start, end); + } + + /** + * Construct a Builder for a IntIndexQuery with a single 2i key. + *

+ * Note that your index name should not include the Riak {@literal _int} or + * {@literal _bin} extension. + *

+ * @param namespace The namespace in Riak to query. + * @param indexName The name of the index in Riak. + * @param match the 2i key. + */ + public Builder(Namespace namespace, String indexName, Long match) + { + super(namespace, indexName, match); + } + + @Override + protected Builder self() + { + return this; + } + + /** + * Construct the query. + * @return a new IntIndexQuery + */ + public IntIndexQuery build() + { + return new IntIndexQuery(this); + } + } + + public static class Response extends SecondaryIndexQuery.Response + { + protected Response(Namespace queryLocation, SecondaryIndexQueryOperation.Response coreResponse, IndexConverter converter) + { + super(queryLocation, coreResponse, converter); + } + + @Override + public List getEntries() + { + List convertedList = new ArrayList(); + for (SecondaryIndexQueryOperation.Response.Entry e : coreResponse.getEntryList()) + { + Location loc = getLocationFromCoreEntry(e); + Entry ce = new Entry(loc, e.getIndexKey(), converter); + convertedList.add(ce); + } + return convertedList; + } + + public class Entry extends SecondaryIndexQuery.Response.Entry + { + protected Entry(Location riakObjectLocation, BinaryValue indexKey, IndexConverter converter) + { + super(riakObjectLocation, indexKey, converter); + } + + } + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/indexes/RawIndexQuery.java b/src/main/java/com/basho/riak/client/api/commands/indexes/RawIndexQuery.java new file mode 100644 index 000000000..c732ca8f0 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/indexes/RawIndexQuery.java @@ -0,0 +1,186 @@ +/* + * 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.indexes; + +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.SecondaryIndexQueryOperation; +import com.basho.riak.client.api.commands.CoreFutureAdapter; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.util.BinaryValue; +import java.util.ArrayList; +import java.util.List; + +/** + * Performs a 2i query where the 2i index keys are raw bytes. + * + *

+ * A RawIndexQuery is used when you are using raw bytes for your 2i keys. The + * parameters are provided as BinaryValue objects. + *

+ *
+ * {@code
+ * byte[] bytes = new byte[] { 1,2,3,4};
+ * BinaryValue key = BinaryValue.create(bytes);
+ * Namespace ns = new Namespace("my_type", "my_bucket");
+ * RawIndexQuery q = new RawIndexQuery.Builder(ns, "my_index", Type._BIN, key).build();
+ * RawIndexquery.Response resp = client.execute(q);}
+ * @author Brian Roach + * @since 2.0 + */ +public class RawIndexQuery extends SecondaryIndexQuery +{ + private final IndexConverter converter; + + protected RawIndexQuery(Init builder) + { + super(builder); + this.converter = new IndexConverter() + { + @Override + public BinaryValue convert(BinaryValue input) + { + return input; + } + }; + } + + @Override + protected IndexConverter getConverter() + { + return converter; + } + + @Override + protected RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + executeCoreAsync(cluster); + + RawQueryFuture future = new RawQueryFuture(coreFuture); + coreFuture.addListener(future); + return future; + } + + protected final class RawQueryFuture extends CoreFutureAdapter + { + public RawQueryFuture(RiakFuture coreFuture) + { + super(coreFuture); + } + + @Override + protected Response convertResponse(SecondaryIndexQueryOperation.Response coreResponse) + { + return new Response(namespace, coreResponse, converter); + } + + @Override + protected RawIndexQuery convertQueryInfo(SecondaryIndexQueryOperation.Query coreQueryInfo) + { + return RawIndexQuery.this; + } + + } + + /** + * Builder used to construct a RawIndexQuery command. + */ + public static class Builder extends SecondaryIndexQuery.Init + { + + /** + * Construct a Builder for a RawIndexQuery with a range. + *

+ * Note that your index name should not include the Riak {@literal _int} or + * {@literal _bin} extension as these are supplied by the type. + *

+ * @param namespace The namespace in Riak to query. + * @param indexName The index name in Riak to query. + * @param type The Riak index type. + * @param start The start of the 2i range. + * @param end The end of the 2i range. + */ + public Builder(Namespace namespace, String indexName, Type type, BinaryValue start, BinaryValue end) + { + super(namespace, indexName + type, start, end); + } + + /** + * Construct a Builder for a RawIndexQuery with a single 2i key. + *

+ * Note that your index name should not include the Riak {@literal _int} or + * {@literal _bin} extension as these are supplied by the type. + *

+ * @param namespace The namespace in Riak to query. + * @param indexName The index name in Riak to query. + * @param type The Riak index type. + * @param match The 2i key to query. + */ + public Builder(Namespace namespace, String indexName, Type type, BinaryValue match) + { + super(namespace, indexName + type, match); + } + + @Override + protected Builder self() + { + return this; + } + + /** + * Construct the query. + * @return a new RawIndexQuery + */ + public RawIndexQuery build() + { + return new RawIndexQuery(this); + } + } + + public static class Response extends SecondaryIndexQuery.Response + { + protected Response(Namespace queryLocation, SecondaryIndexQueryOperation.Response coreResponse, IndexConverter converter) + { + super(queryLocation, coreResponse, converter); + } + + @Override + public List getEntries() + { + List convertedList = new ArrayList(); + for (SecondaryIndexQueryOperation.Response.Entry e : coreResponse.getEntryList()) + { + Location loc = getLocationFromCoreEntry(e); + Entry ce = new Entry(loc, e.getIndexKey(), converter); + convertedList.add(ce); + } + return convertedList; + } + + public class Entry extends SecondaryIndexQuery.Response.Entry + { + protected Entry(Location riakObjectLocation, BinaryValue indexKey, IndexConverter converter) + { + super(riakObjectLocation, indexKey, converter); + } + + } + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/indexes/SecondaryIndexQuery.java b/src/main/java/com/basho/riak/client/api/commands/indexes/SecondaryIndexQuery.java new file mode 100644 index 000000000..e73132386 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/indexes/SecondaryIndexQuery.java @@ -0,0 +1,466 @@ +/* + * 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.commands.indexes; + +import com.basho.riak.client.api.RiakCommand; +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.SecondaryIndexQueryOperation; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.util.BinaryValue; +import java.util.List; + +/** + * A Secondary Index Query. + *

+ * Serves as a base class for all 2i queries. + *

+ * @param the type being used for the query. + * + * @author Brian Roach + * @since 2.0 + */ +public abstract class SecondaryIndexQuery extends RiakCommand +{ + public enum Type + { + _INT("_int"), _BIN("_bin"), _BUCKET(""), _KEY(""); + + private String suffix; + + Type(String suffix) + { + this.suffix = suffix; + } + + @Override + public String toString() + { + return suffix; + } + } + + + protected final Namespace namespace; + protected final String indexName; + protected final BinaryValue continuation; + protected final T match; + protected final T start; + protected final T end; + protected final Integer maxResults; + protected final boolean returnTerms; + protected final boolean paginationSort; + protected final String termFilter; + protected Integer timeout; + + protected abstract IndexConverter getConverter(); + + protected SecondaryIndexQuery(Init builder) + { + this.namespace = builder.namespace; + this.indexName = builder.indexName; + this.continuation = builder.continuation; + this.match = builder.match; + this.start = builder.start; + this.end = builder.end; + this.maxResults = builder.maxResults; + this.returnTerms = builder.returnTerms; + this.paginationSort = builder.paginationSort; + this.termFilter = builder.termFilter; + this.timeout = builder.timeout; + } + + /** + * Get the location for this query. + * @return the location encompassing a bucket and bucket type. + */ + public Namespace getNamespace() + { + return namespace; + } + + /** + * Get the full index name for this query. + * @return the index name including Riak suffix. + */ + public String getIndexName() + { + return indexName; + } + + /** + * Get the match value supplied for this query. + * @return the single index key to match, or null if not present + */ + public T getMatchValue() + { + return match; + } + + /** + * Get the range start value for this query. + * @return the range start, or null if not present. + */ + public T getRangeStart() + { + return start; + } + + /** + * Get the range end value for this query. + * @return the range end value, or null if not present + */ + public T getRangeEnd() + { + return end; + } + + /** + * Get the max number of results for this query. + * @return the max number of results, or null if not present. + */ + public Integer getMaxResults() + { + return maxResults; + } + + /** + * Get whether this query will return both index keys and object keys. + * @return true if specified, false otherwise. + */ + public boolean getReturnKeyAndIndex() + { + return returnTerms; + } + + /** + * Get the pagination sort setting. + * @return true if set, false otherwise. + */ + public boolean getPaginationSort() + { + return paginationSort; + } + + /** + * Get the regex term filter for this query. + * @return the filter, or null if not set. + */ + public String getTermFilter() + { + return termFilter; + } + + /** + * Get the continuation supplied for this query. + * @return the continuation, or null if not set. + */ + public BinaryValue getContinuation() + { + return continuation; + } + + /** + * Get the timeout value for this query. + * @return the timeout value, or null if not set. + */ + public Integer getTimeout() + { + return timeout; + } + + protected final SecondaryIndexQueryOperation.Query createCoreQuery() + { + IndexConverter converter = getConverter(); + + SecondaryIndexQueryOperation.Query.Builder coreQueryBuilder = + new SecondaryIndexQueryOperation.Query.Builder(namespace, BinaryValue.create(indexName)) + .withContinuation(continuation) + .withReturnKeyAndIndex(returnTerms) + .withPaginationSort(paginationSort); + + if (termFilter != null) + { + coreQueryBuilder.withRegexTermFilter(BinaryValue.create(termFilter)); + } + + if (match != null) + { + coreQueryBuilder.withIndexKey(converter.convert(match)); + } + else + { + coreQueryBuilder.withRangeStart(converter.convert(start)) + .withRangeEnd(converter.convert(end)); + } + + if (maxResults != null) + { + coreQueryBuilder.withMaxResults(maxResults); + } + + if (timeout != null) + { + coreQueryBuilder.withTimeout(timeout); + } + + return coreQueryBuilder.build(); + } + + protected RiakFuture executeCoreAsync(RiakCluster cluster) + { + SecondaryIndexQueryOperation.Builder builder = + new SecondaryIndexQueryOperation.Builder(this.createCoreQuery()); + + return cluster.execute(builder.build()); + } + + protected interface IndexConverter + { + T convert(BinaryValue input); + BinaryValue convert(T input); + } + + public static abstract class Init> + { + private final Namespace namespace; + private final String indexName; + private volatile BinaryValue continuation; + private volatile S match; + private volatile S start; + private volatile S end; + private volatile Integer maxResults; + private volatile boolean returnTerms; + private volatile boolean paginationSort; + private volatile String termFilter; + private volatile Integer timeout; + + protected abstract T self(); + + /** + * Build a range query. + *

+ * Returns all objects in Riak that have an index value + * in the specified range. + *

+ * @param namespace the namespace for this query. + * @param indexName the indexname + * @param start the start index value + * @param end the end index value + */ + public Init(Namespace namespace, String indexName, S start, S end) + { + this.namespace = namespace; + this.indexName = indexName; + this.start = start; + this.end = end; + } + + /** + * Build a match query. + *

+ * Returns all objects in Riak that have an index value matching the + * one supplied. + *

+ * @param namespace the namespace for this query + * @param indexName the index name + * @param match the index value. + */ + public Init(Namespace namespace, String indexName, S match) + { + this.namespace = namespace; + this.indexName = indexName; + this.match = match; + } + + /** + * Set the continuation for this query. + *

+ * The continuation is returned by a previous paginated query. + *

+ * @param continuation + * @return a reference to this object. + */ + public T withContinuation(BinaryValue continuation) + { + this.continuation = continuation; + return self(); + } + + /** + * Set the maximum number of results returned by the query. + * @param maxResults the number of results. + * @return a reference to this object. + */ + public T withMaxResults(Integer maxResults) + { + this.maxResults = maxResults; + return self(); + } + + /** + * Set whether to return the index keys with the Riak object keys. + * Setting this to true will return both the index key and the Riak + * object's key. The default is false (only to return the Riak object keys). + * @param returnBoth true to return both index and object keys, false to return only object keys. + * @return a reference to this object. + */ + public T withKeyAndIndex(boolean returnBoth) + { + this.returnTerms = returnBoth; + return self(); + } + + /** + * Set whether to sort the results of a non-paginated 2i query. + *

+ * Setting this to true will sort the results in Riak before returning them. + *

+ *

+ * Note that this is not recommended for queries that could return a large + * result set; the overhead in Riak is substantial. + *

+ * + * @param orderByKey true to sort the results, false to return as-is. + * @return a reference to this object. + */ + public T withPaginationSort(boolean orderByKey) + { + this.paginationSort = orderByKey; + return self(); + } + + /** + * Set the regex to filter result terms by for this query. + * @param filter the regex to filter terms by. + * @return a reference to this object. + */ + public T withRegexTermFilter(String filter) + { + this.termFilter = filter; + return self(); + } + + /** + * Set the Riak-side timeout value. + *

+ * By default, riak has a 60s timeout for operations. Setting + * this value will override that default for both the + * fetch and store operation. + *

+ * @param timeout the timeout in milliseconds + * @return a reference to this object. + */ + public T withTimeout(int timeout) + { + this.timeout = timeout; + return self(); + } + } + + /** + * Base class for all 2i responses. + * @param The type contained in the resposne. + */ + public abstract static class Response + { + final IndexConverter converter; + final SecondaryIndexQueryOperation.Response coreResponse; + final Namespace queryLocation; + + protected Response(Namespace queryLocation, SecondaryIndexQueryOperation.Response coreResponse, IndexConverter converter) + { + this.coreResponse = coreResponse; + this.converter = converter; + this.queryLocation = queryLocation; + } + + /** + * Check if this response has a continuation. + * @return true if the response contains a continuation. + */ + public boolean hasContinuation() + { + return coreResponse.hasContinuation(); + } + + /** + * Get the continuation from this response. + * @return the continuation, or null if none is present. + */ + public BinaryValue getContinuation() + { + return coreResponse.getContinuation(); + } + + /** + * Check is this response contains any entries. + * @return true if entries are present, false otherwise. + */ + public boolean hasEntries() + { + return !coreResponse.getEntryList().isEmpty(); + } + + + protected final Location getLocationFromCoreEntry(SecondaryIndexQueryOperation.Response.Entry e) + { + Location loc = new Location(queryLocation, e.getObjectKey()); + return loc; + } + + protected abstract List getEntries(); + + public abstract static class Entry + { + private final Location RiakObjectLocation; + private final BinaryValue indexKey; + private final IndexConverter converter; + + protected Entry(Location riakObjectLocation, BinaryValue indexKey, IndexConverter converter) + { + this.RiakObjectLocation = riakObjectLocation; + this.indexKey = indexKey; + this.converter = converter; + } + + /** + * Get the location for this entry. + * @return the location for this object in Riak. + */ + public Location getRiakObjectLocation() + { + return RiakObjectLocation; + } + + /** + * Get this 2i key for this entry. + * Note this will only be present if the {@literal withKeyAndIndex(true)} + * method was used when constructing the query. + * @return The 2i key for this entry or null if not present. + */ + public T getIndexKey() + { + return converter.convert(indexKey); + } + + } + + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/indexes/package-info.java b/src/main/java/com/basho/riak/client/api/commands/indexes/package-info.java new file mode 100644 index 000000000..ad0e308de --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/indexes/package-info.java @@ -0,0 +1,11 @@ +/** + * Commands for performing secondary index (2i) queries in Riak. + *

Secondary index commands

+ *
    + *
  • {@link com.basho.riak.client.api.commands.indexes.RawIndexQuery}
  • + *
  • {@link com.basho.riak.client.api.commands.indexes.BinIndexQuery}
  • + *
  • {@link com.basho.riak.client.api.commands.indexes.IntIndexQuery}
  • + *
  • {@link com.basho.riak.client.api.commands.indexes.BigIntIndexQuery}
  • + *
+ */ +package com.basho.riak.client.api.commands.indexes; \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/api/commands/kv/DeleteValue.java b/src/main/java/com/basho/riak/client/api/commands/kv/DeleteValue.java new file mode 100644 index 000000000..46128963e --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/kv/DeleteValue.java @@ -0,0 +1,285 @@ +/* + * Copyright 2013 Basho Technologies Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.basho.riak.client.api.commands.kv; + +import com.basho.riak.client.api.RiakCommand; +import com.basho.riak.client.api.cap.Quorum; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.DeleteOperation; +import com.basho.riak.client.api.commands.CoreFutureAdapter; +import com.basho.riak.client.api.commands.RiakOption; +import com.basho.riak.client.core.query.Location; + +import java.util.HashMap; +import java.util.Map; + +/** + * Command used to delete a value from Riak. + * + *

+ * Deleting an object from Riak is a simple matter of supplying a {@link com.basho.riak.client.core.query.Location} + * and executing the operation. + *

+ *

+ * Note that this operation returns a {@code Void} type upon success. Any failure + * will be thrown as an exception when calling {@code DeleteValue} synchronously or + * when calling the future's get methods. + *

+ *
+ * {@code
+ * Namespace ns = new Namespace("my_type", "my_bucket");
+ * Location loc = new Location(ns, "my_key");
+ * DeleteValue dv = new DeleteValue.Builder(loc).build();
+ * client.execute(dv);}
+ *

+ *

+ * All operations can called async as well. + *

+ * {@code
+ * ...
+ * RiakFuture future = client.executeAsync(dv);
+ * ...
+ * future.await();
+ * if (future.isSuccess())
+ * { 
+ *     ... 
+ * }}
+ *

+ * + * @author Dave Rusek + * @since 2.0 + */ +public final class DeleteValue extends RiakCommand +{ + + private final Location location; + private final Map, Object> options = + new HashMap, Object>(); + private final VClock vClock; + + public DeleteValue(Builder builder) + { + this.location = builder.location; + this.options.putAll(builder.options); + this.vClock = builder.vClock; + } + + @Override + protected RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + cluster.execute(buildCoreOperation()); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) + { + @Override + protected Void convertResponse(Void coreResponse) + { + return coreResponse; + } + + @Override + protected Location convertQueryInfo(Location coreQueryInfo) + { + return coreQueryInfo; + } + }; + coreFuture.addListener(future); + return future; + } + + private DeleteOperation buildCoreOperation() + { + DeleteOperation.Builder builder = new DeleteOperation.Builder(location); + + if (vClock != null) + { + builder.withVclock(vClock); + } + + for (Map.Entry, Object> optPair : options.entrySet()) + { + + Option option = optPair.getKey(); + + if (option == Option.DW) + { + builder.withDw(((Quorum) optPair.getValue()).getIntValue()); + } + else if (option == Option.N_VAL) + { + builder.withNVal((Integer) optPair.getValue()); + } + else if (option == Option.PR) + { + builder.withPr(((Quorum) optPair.getValue()).getIntValue()); + } + else if (option == Option.R) + { + builder.withR(((Quorum) optPair.getValue()).getIntValue()); + } + else if (option == Option.PW) + { + builder.withPw(((Quorum) optPair.getValue()).getIntValue()); + } + else if (option == Option.RW) + { + builder.withRw(((Quorum) optPair.getValue()).getIntValue()); + } + else if (option == Option.SLOPPY_QUORUM) + { + builder.withSloppyQuorum((Boolean) optPair.getValue()); + } + else if (option == Option.TIMEOUT) + { + builder.withTimeout((Integer) optPair.getValue()); + } + else if (option == Option.W) + { + builder.withW(((Quorum) optPair.getValue()).getIntValue()); + } + } + + return builder.build(); + } + + public final static class Option extends RiakOption + { + + /** + * Read Write Quorum. + * Quorum for both operations (get and put) involved in deleting an object + */ + public static final Option RW = new Option("RW"); + /** + * Read Quorum. + * How many replicas need to agree when fetching the object. + */ + public static final Option R = new Option("R"); + /** + * Write Quorum. + * How many replicas to write to before returning a successful response. + */ + public static final Option W = new Option("W"); + /** + * Primary Read Quorum. + * How many primary replicas need to be available when retrieving the object. + */ + public static final Option PR = new Option("PR"); + /** + * Primary Write Quorum. + * How many primary nodes must be up when the write is attempted + */ + public static final Option PW = new Option("PW"); + /** + * Durable Write Quorum. + * How many replicas to commit to durable storage before returning a successful response. + */ + public static final Option DW = new Option("DW"); + /** + * Timeout. + * Sets the server-side timeout for this operation. The default is 60 seconds. + */ + public static final Option TIMEOUT = new Option("TIMEOUT"); + public static final Option SLOPPY_QUORUM = new Option("SLOPPY_QUORUM"); + public static final Option N_VAL = new Option("N_VAL"); + + private Option(String name) + { + super(name); + } + } + + /** + * Used to construct a DeleteValue command. + */ + public static class Builder + { + + private final Location location; + private final Map, Object> options = + new HashMap, Object>(); + private VClock vClock; + + /** + * Construct a Builder for a DeleteValue command. + * @param location the location of the object in Riak + */ + public Builder(Location location) + { + if (location == null) + { + throw new IllegalArgumentException("Location cannot be null"); + } + this.location = location; + } + + /** + * Specify the VClock to use when deleting the object from Riak. + * + * @param vClock the vclock + * @return this + */ + public Builder withVClock(VClock vClock) + { + this.vClock = vClock; + return this; + } + + /** + * Add a delete option. + * + * @param option the option + * @param value the value associated with the option + * @param the type required by the option + * @return a reference to this object + */ + public Builder withOption(Option option, T value) + { + options.put(option, value); + return this; + } + + /** + * Set the Riak-side timeout value. + *

+ * By default, riak has a 60s timeout for operations. Setting + * this value will override that default for this operation. + *

+ * @param timeout the timeout in milliseconds to be sent to riak. + * @return a reference to this object. + */ + public Builder withTimeout(int timeout) + { + withOption(Option.TIMEOUT, timeout); + return this; + } + + /** + * Construct a DeleteValue object. + * @return a new DeleteValue instance. + */ + public DeleteValue build() + { + return new DeleteValue(this); + } + + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/kv/FetchValue.java b/src/main/java/com/basho/riak/client/api/commands/kv/FetchValue.java new file mode 100644 index 000000000..1f4c058ac --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/kv/FetchValue.java @@ -0,0 +1,384 @@ +/* + * Copyright 2013 Basho Technologies Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.basho.riak.client.api.commands.kv; + +import com.basho.riak.client.api.cap.Quorum; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.operations.FetchOperation; +import com.basho.riak.client.api.RiakCommand; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.api.commands.CoreFutureAdapter; +import com.basho.riak.client.api.commands.RiakOption; +import com.basho.riak.client.core.query.Location; + +import java.util.HashMap; +import java.util.Map; + + +/** + * Command used to fetch a value from Riak. + * + *

+ * Fetching an object from Riak is a simple matter of supplying a {@link com.basho.riak.client.core.query.Location} + * and executing the FetchValue operation. + *

+ * {@code
+ * Namespace ns = new Namespace("my_type","my_bucket");
+ * Location loc = new Location(ns, "my_key");
+ * FetchValue fv = new FetchValue.Builder(loc).build();
+ * FetchValue.Response response = client.execute(fv);
+ * RiakObject obj = response.getValue(RiakObject.class);}
+ *

+ *

+ * All operations can called async as well. + *

+ * {@code
+ * ...
+ * RiakFuture future = client.executeAsync(fv);
+ * ...
+ * future.await();
+ * if (future.isSuccess())
+ * { 
+ *     ... 
+ * }}
+ *

+ *

+ * ORM features are also provided when retrieving the results from the response. + * By default, JSON serialization / deserializtion is used. For example, if + * the value stored in Riak was JSON and mapped to your class {@code MyPojo}: + *

+ * {@code
+ * ...
+ * MyPojo mp = response.getValue(MyPojo.class);
+ * ...}
+ *

+ * + * @author Dave Rusek + * @since 2.0 + * @see Response + */ +public final class FetchValue extends RiakCommand +{ + + private final Location location; + private final Map, Object> options = + new HashMap, Object>(); + + FetchValue(Builder builder) + { + this.location = builder.location; + this.options.putAll(builder.options); + } + + @Override + protected final RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + cluster.execute(buildCoreOperation()); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) + { + @Override + protected Response convertResponse(FetchOperation.Response coreResponse) + { + return new Response.Builder().withNotFound(coreResponse.isNotFound()) + .withUnchanged(coreResponse.isUnchanged()) + .withValues(coreResponse.getObjectList()) + .withLocation(location) // for ORM + .build(); + } + + @Override + protected Location convertQueryInfo(Location coreQueryInfo) + { + return coreQueryInfo; + } + }; + coreFuture.addListener(future); + return future; + + } + + private FetchOperation buildCoreOperation() + { + FetchOperation.Builder builder = new FetchOperation.Builder(location); + + for (Map.Entry, Object> opPair : options.entrySet()) + { + + RiakOption option = opPair.getKey(); + + if (option == Option.R) + { + builder.withR(((Quorum) opPair.getValue()).getIntValue()); + } else if (option == Option.DELETED_VCLOCK) + { + builder.withReturnDeletedVClock((Boolean) opPair.getValue()); + } else if (option == Option.TIMEOUT) + { + builder.withTimeout((Integer) opPair.getValue()); + } else if (option == Option.HEAD) + { + builder.withHeadOnly((Boolean) opPair.getValue()); + } else if (option == Option.BASIC_QUORUM) + { + builder.withBasicQuorum((Boolean) opPair.getValue()); + } else if (option == Option.IF_MODIFIED) + { + VClock clock = (VClock) opPair.getValue(); + builder.withIfNotModified(clock.getBytes()); + } else if (option == Option.N_VAL) + { + builder.withNVal((Integer) opPair.getValue()); + } else if (option == Option.PR) + { + builder.withPr(((Quorum) opPair.getValue()).getIntValue()); + } else if (option == Option.SLOPPY_QUORUM) + { + builder.withSloppyQuorum((Boolean) opPair.getValue()); + } else if (option == Option.NOTFOUND_OK) + { + builder.withNotFoundOK((Boolean) opPair.getValue()); + } + + } + + return builder.build(); + } + + /** + * A response from Riak containing results from a FetchValue command. + *

+ * The Response, unless marked not found or unchanged, will contain one or + * more objects returned from Riak (all siblings are returned if present). + *

+ */ + public static class Response extends KvResponseBase + { + private final boolean notFound; + private final boolean unchanged; + + Response(Init builder) + { + super(builder); + this.notFound = builder.notFound; + this.unchanged = builder.unchanged; + } + + /** + * Determine if there was a value in Riak. + *

+ * If there was no value present at the supplied {@code Location} in + * Riak, this will be true. + *

+ * + * @return true if there was no value in Riak. + */ + public boolean isNotFound() + { + return notFound; + } + + /** + * Determine if the value is unchanged. + *

+ * If the fetch request set {@link com.basho.riak.client.api.commands.kv.FetchValue.Option#IF_MODIFIED} + * this indicates if the value in Riak has been modified. + *

+ * + * @return true if the vector clock for the object in Riak matched the + * supplied vector clock, false otherwise. + */ + public boolean isUnchanged() + { + return unchanged; + } + + /** + * @ExcludeFromJavadoc + */ + protected static abstract class Init> extends KvResponseBase.Init + { + private boolean notFound; + private boolean unchanged; + + T withUnchanged(boolean unchanged) + { + this.unchanged = unchanged; + return self(); + } + + T withNotFound(boolean notFound) + { + this.notFound = notFound; + return self(); + } + } + + static class Builder extends Init + { + + @Override + protected Builder self() + { + return this; + } + + @Override + Response build() + { + return new Response(this); + } + + } + + } + + /** + * Options for controlling how Riak performs the fetch operation. + *

+ * These options can be supplied to the {@link FetchValue.Builder} to change + * how Riak performs the operation. These override the defaults provided + * by the bucket. + *

+ * + * @author Dave Rusek + * @since 2.0 + * @see Replication Properties + */ + public static final class Option extends RiakOption + { + + /** + * Read Quorum. + * How many replicas need to agree when fetching the object. + */ + public static final Option R = new Option("R"); + /** + * Primary Read Quorum. + * How many primary replicas need to be available when retrieving the object. + */ + public static final Option PR = new Option("PR"); + /** + * Basic Quorum. + * Whether to return early in some failure cases (eg. when r=1 and you get + * 2 errors and a success basic_quorum=true would return an error) + */ + public static final Option BASIC_QUORUM = new Option("BASIC_QUORUM"); + /** + * Not Found OK. + * Whether to treat notfounds as successful reads for the purposes of R + */ + public static final Option NOTFOUND_OK = new Option("NOTFOUND_OK"); + /** + * If Modified. + * When a vector clock is supplied with this option, only return the object + * if the vector clocks don't match. + */ + public static final Option IF_MODIFIED = new Option("IF_MODIFIED"); + /** + * Head. + * return the object with the value(s) set as empty. This allows you to get the + * meta data without a potentially large value. Analogous to an HTTP HEAD request. + */ + public static final Option HEAD = new Option("HEAD"); + /** + * Deleted VClock. + * By default single tombstones are not returned by a fetch operations. This + * will return a Tombstone if it is present. + */ + public static final Option DELETED_VCLOCK = new Option("DELETED_VCLOCK"); + /** + * Timeout. + * Sets the server-side timeout for this operation. The default in Riak is 60 seconds. + */ + public static final Option TIMEOUT = new Option("TIMEOUT"); + public static final Option SLOPPY_QUORUM = new Option("SLOPPY_QUORUM"); + public static final Option N_VAL = new Option("N_VAL"); + + private Option(String name) + { + super(name); + } + + } + + /** + * Used to construct a FetchValue command. + */ + public static class Builder + { + + private final Location location; + private final Map, Object> options = + new HashMap, Object>(); + + /** + * Constructs a builder for a FetchValue operation using the supplied location. + * @param location the location of the object you want to fetch from Riak. + */ + public Builder(Location location) + { + if (location == null) + { + throw new IllegalArgumentException("Location cannot be null"); + } + this.location = location; + } + + /** + * Add an optional setting for this command. + * This will be passed along with the request to Riak to tell it how + * to behave when servicing the request. + * + * @param option the option + * @param value the value for the option + * @return a reference to this object. + */ + public Builder withOption(Option option, U value) + { + options.put(option, value); + return this; + } + + /** + * Set the Riak-side timeout value. + *

+ * By default, riak has a 60s timeout for operations. Setting + * this value will override that default for this operation. + *

+ * @param timeout the timeout in milliseconds to be sent to riak. + * @return a reference to this object. + */ + public Builder withTimeout(int timeout) + { + withOption(Option.TIMEOUT, timeout); + return this; + } + + /** + * Build a {@link FetchValue} object + * + * @return a FetchValue command + */ + public FetchValue build() + { + return new FetchValue(this); + } + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/kv/KvResponseBase.java b/src/main/java/com/basho/riak/client/api/commands/kv/KvResponseBase.java new file mode 100644 index 000000000..c18c42ffd --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/kv/KvResponseBase.java @@ -0,0 +1,329 @@ +/* + * 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.kv; + +import com.basho.riak.client.api.cap.ConflictResolver; +import com.basho.riak.client.api.cap.ConflictResolverFactory; +import com.basho.riak.client.api.cap.UnresolvedConflictException; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.api.convert.Converter; +import com.basho.riak.client.api.convert.ConverterFactory; +import com.basho.riak.client.api.convert.reflection.AnnotationUtil; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.RiakObject; +import com.fasterxml.jackson.core.type.TypeReference; +import java.util.ArrayList; +import java.util.List; + +/** + * Base abstract class that KV responses extend. + * + * @author Brian Roach + * @since 2.0 + */ +abstract class KvResponseBase +{ + private final Location location; + private final List values; + + + protected KvResponseBase(Init builder) + { + this.location = builder.location; + this.values = builder.values; + } + + /** + * Determine if this response contains any returned values. + * @return true if values are present, false otherwise. + */ + public boolean hasValues() + { + return !values.isEmpty(); + } + + /** + * Return the number of values contained in this response. + *

+ * If siblings are present at the {@code Location}, all values + * are returned. + *

+ * @return the number of values in this response. + */ + public int getNumberOfValues() + { + return values.size(); + } + + /** + * Get all the objects returned in this response. + *

+ * If siblings were present in Riak for the object you were fetching, + * this method will return all of them to you. + *

+ * @return a list of values as RiakObjects + */ + public List getValues() + { + return values; + } + + /** + * Get the vector clock returned with this response. + *

+ * When storing/retrieving core Java types ({@code HashMap}, + * {@code ArrayList},{@code String}, etc) or non-annotated POJOs + * this method allows you to retrieve the vector clock. + *

+ * @return The vector clock or null if one is not present. + */ + public VClock getVectorClock() + { + if (hasValues()) + { + return values.get(0).getVClock(); + } + else + { + return null; + } + } + + /** + * Get all the objects returned in this response. + *

+ * If siblings were present in Riak for the object you were fetching, + * this method will return all of them to you. + *

+ *

+ * The values will be converted to the supplied class using the + * {@link com.basho.riak.client.api.convert.Converter} returned from the {@link com.basho.riak.client.api.convert.ConverterFactory}. + * By default this will be the {@link com.basho.riak.client.api.convert.JSONConverter}, + * or no conversion at all if you pass in {@code RiakObject.class}. + *

+ * @param clazz the class to be converted to + * @return a list of values, converted to the supplied class. + * @see ConverterFactory + * @see Converter + */ + public List getValues(Class clazz) + { + Converter converter = ConverterFactory.getInstance().getConverter(clazz); + return convertValues(converter); + } + + /** + * Get all the objects returned in this response. + *

+ * If siblings were present in Riak for the object you were fetching, + * this method will return all of them to you. + *

+ *

+ * The values will be converted to an object using the supplied + * {@link com.basho.riak.client.api.convert.Converter} rather than one + * registered with the {@link com.basho.riak.client.api.convert.ConverterFactory}. + *

+ * @param converter The converter to use. + * @return a list of values, converted to the supplied class. + * @see Converter + */ + public List getValues(Converter converter) + { + return convertValues(converter); + } + + /** + * Get a single, resolved object from this response. + *

+ * The values will be converted to objects using the supplied + * {@link com.basho.riak.client.api.convert.Converter} rather than one registered + * with the {@link com.basho.riak.client.api.convert.ConverterFactory}. + *

+ *

If there are multiple + * values present (siblings), they will then be resolved using the supplied + * {@link com.basho.riak.client.api.cap.ConflictResolver} rather than one + * registered with the {@link com.basho.riak.client.api.cap.ConflictResolverFactory}. + *

+ * @param converter The converter to use. + * @param resolver The conflict resolver to use. + * @return the single, resolved value. + * @throws UnresolvedConflictException if the resolver fails to resolve siblings. + * @see Converter + * @see ConflictResolver + */ + public T getValue(Converter converter, ConflictResolver resolver) throws UnresolvedConflictException + { + List convertedValues = convertValues(converter); + T resolved = resolver.resolve(convertedValues); + + if (hasValues() && resolved != null) + { + VClock vclock = values.get(0).getVClock(); + AnnotationUtil.setVClock(resolved, vclock); + } + + return resolved; + } + + /** + * Get a single, resolved object from this response. + *

+ * The values will be converted to the supplied class using the + * {@link com.basho.riak.client.api.convert.Converter} returned from the {@link com.basho.riak.client.api.convert.ConverterFactory}. + * By default this will be the {@link com.basho.riak.client.api.convert.JSONConverter}, + * or no conversion at all if you pass in {@code RiakObject.class}. If there are multiple + * values present (siblings), they will then be resolved using the + * {@link com.basho.riak.client.api.cap.ConflictResolver} returned by the {@link com.basho.riak.client.api.cap.ConflictResolverFactory}. + *

+ * @param clazz the class to be converted to. + * @return the single, resolved value converted to the supplied class. + * @throws UnresolvedConflictException + * @see ConverterFactory + * @see Converter + * @see ConflictResolverFactory + * @see ConflictResolver + */ + public T getValue(Class clazz) throws UnresolvedConflictException + { + Converter converter = ConverterFactory.getInstance().getConverter(clazz); + List convertedValues = convertValues(converter); + + ConflictResolver resolver = + ConflictResolverFactory.getInstance().getConflictResolver(clazz); + + T resolved = resolver.resolve(convertedValues); + + if (hasValues() && resolved != null) + { + VClock vclock = values.get(0).getVClock(); + AnnotationUtil.setVClock(resolved, vclock); + } + + return resolved; + } + + /** + * Get a single, resolved object from this response. + *

+ * The values will be converted to the supplied class using the + * {@link com.basho.riak.client.api.convert.Converter} returned from the {@link com.basho.riak.client.api.convert.ConverterFactory}. + * By default this will be the {@link com.basho.riak.client.api.convert.JSONConverter}, + * or no conversion at all if you pass in {@code RiakObject.class}. If there are multiple + * values present (siblings), they will then be resolved using the + * {@link com.basho.riak.client.api.cap.ConflictResolver} returned by the {@link com.basho.riak.client.api.cap.ConflictResolverFactory}. + *

+ *

+ * This version should only be used if you're converting to a parameterized + * generic domain object. For example: + *

+     * {@literal TypeReference>} tr = new {@literal TypeReference>}(){};
+     * {@literal MyPojo} myPojo = response.getValue(tr);
+     * 
+ *

+ * @param typeReference The TypeReference of the class to be converted to. + * @return the single, resolved value converted to the supplied class. + * @throws UnresolvedConflictException + * @see ConverterFactory + * @see Converter + * @see ConflictResolverFactory + * @see ConflictResolver + */ + public T getValue(TypeReference typeReference) throws UnresolvedConflictException + { + Converter converter = ConverterFactory.getInstance().getConverter(typeReference); + List convertedValues = convertValues(converter); + + ConflictResolver resolver = + ConflictResolverFactory.getInstance().getConflictResolver(typeReference); + + T resolved = resolver.resolve(convertedValues); + if (hasValues() && resolved != null) + { + VClock vclock = values.get(0).getVClock(); + AnnotationUtil.setVClock(resolved, vclock); + } + + return resolved; + } + + /** + * Get the objects returned in this response. + *

+ * If siblings were present in Riak for the object you were fetching, + * this method will return all of them to you. + *

+ *

+ * The values will be converted to the supplied class using the + * {@link com.basho.riak.client.api.convert.Converter} returned from the {@link com.basho.riak.client.api.convert.ConverterFactory}. + * By default this will be the {@link com.basho.riak.client.api.convert.JSONConverter}, + * or no conversion at all if you pass in a TypeReference for {@code RiakObject.class}. + *

+ *

+ * This version should only be used if you're converting to a parameterized + * generic domain object. For example: + *

+     * {@literal TypeReference>} tr = new {@literal TypeReference>}(){};
+     * {@literal List>} list = response.getValues(tr);
+     * 
+ *

+ * @param typeReference the TypeReference for the class to be converted to + * @return a list of values, converted to the supplied class. + * @see ConverterFactory + * @see Converter + */ + public List getValues(TypeReference typeReference) + { + Converter converter = ConverterFactory.getInstance().getConverter(typeReference); + return convertValues(converter); + } + + private List convertValues(Converter converter) + { + List convertedValues = new ArrayList(values.size()); + for (RiakObject ro : values) + { + convertedValues.add(converter.toDomain(ro, location)); + } + + return convertedValues; + } + + + /** + * @ExcludeFromJavadoc + */ + protected static abstract class Init> + { + private Location location; + private List values = new ArrayList(); + + protected abstract T self(); + abstract KvResponseBase build(); + + T withLocation(Location location) + { + this.location = location; + return self(); + } + + T withValues(List values) + { + this.values.addAll(values); + return self(); + } + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/kv/ListKeys.java b/src/main/java/com/basho/riak/client/api/commands/kv/ListKeys.java new file mode 100644 index 000000000..45fed9dda --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/kv/ListKeys.java @@ -0,0 +1,198 @@ +/* + * Copyright 2013 Basho Technologies Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.basho.riak.client.api.commands.kv; + +import com.basho.riak.client.api.RiakCommand; +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.ListKeysOperation; +import com.basho.riak.client.api.commands.CoreFutureAdapter; +import com.basho.riak.client.core.query.Location; +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 keys in a bucket. + * + *

+ * This command is used to retrieve a list of all keys in a bucket. The response + * is iterable and contains a list of Locations. + *

+ * {@code
+ * Namespace ns = new Namespace("my_type", "my_bucket");
+ * ListKeys lk = new ListKeys.Builder(ns).build();
+ * ListKeys.Response response = client.execute(lk);
+ * for (Location l : response)
+ * {
+ *     System.out.println(l.getKeyAsString());
+ * }}
+ *

+ *

+ * This is a very expensive operation and is not recommended for use on a production system + *

+ * + * @author Dave Rusek + * @since 2.0 + */ +public final class ListKeys extends RiakCommand +{ + + private final Namespace namespace; + private final int timeout; + + ListKeys(Builder builder) + { + this.namespace = builder.namespace; + this.timeout = builder.timeout; + } + + @Override + protected final RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + cluster.execute(buildCoreOperation()); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) + { + @Override + protected Response convertResponse(ListKeysOperation.Response coreResponse) + { + return new Response(namespace, coreResponse.getKeys()); + } + + @Override + protected Namespace convertQueryInfo(Namespace coreQueryInfo) + { + return coreQueryInfo; + } + }; + coreFuture.addListener(future); + return future; + } + + private ListKeysOperation buildCoreOperation() + { + ListKeysOperation.Builder builder = new ListKeysOperation.Builder(namespace); + + if (timeout > 0) + { + builder.withTimeout(timeout); + } + + return builder.build(); + } + + public static class Response implements Iterable + { + + private final Namespace namespace; + private final List keys; + + public Response(Namespace namespace, List keys) + { + this.namespace = namespace; + this.keys = keys; + } + + @Override + public Iterator iterator() + { + return new Itr(namespace, keys.iterator()); + } + } + + private static class Itr implements Iterator + { + private final Iterator iterator; + private final Namespace namespace; + + private Itr(Namespace namespace, Iterator iterator) + { + this.iterator = iterator; + this.namespace = namespace; + } + + @Override + public boolean hasNext() + { + return iterator.hasNext(); + } + + @Override + public Location next() + { + BinaryValue key = iterator.next(); + return new Location(namespace, key); + } + + @Override + public void remove() + { + iterator.remove(); + } + } + + /** + * Used to construct a ListKeys command. + */ + public static class Builder + { + private final Namespace namespace; + private int timeout; + + /** + * Constructs a Builder for a ListKeys command. + * @param namespace the namespace from which to list keys. + */ + public Builder(Namespace namespace) + { + if (namespace == null) + { + throw new IllegalArgumentException("Namespace cannot be null"); + } + this.namespace = namespace; + } + + /** + * 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 the ListKeys command. + * @return A ListKeys command. + */ + public ListKeys build() + { + return new ListKeys(this); + } + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/kv/MultiFetch.java b/src/main/java/com/basho/riak/client/api/commands/kv/MultiFetch.java new file mode 100644 index 000000000..5e8659f8d --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/kv/MultiFetch.java @@ -0,0 +1,439 @@ +/* + * Copyright 2013 Basho Technologies Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.basho.riak.client.api.commands.kv; + +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.api.RiakCommand; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.RiakFutureListener; +import com.basho.riak.client.api.commands.ListenableFuture; +import com.basho.riak.client.api.commands.kv.FetchValue.Option; +import com.basho.riak.client.core.query.Location; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; + + +import static java.util.Collections.unmodifiableList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Command used to fetch multiple values from Riak. + * + *

+ * Riak itself does not support pipelining of requests. MutliFetch addresses this issue by using a thread to + * parallelize and manage a set of async fetch operations for a given set of keys. + *

+ *

+ * The result of executing this command is a {@code List} of {@link RiakFuture} objects, each one representing a single + * fetch operation. The returned {@code RiakFuture} that contains that list completes + * when all the FetchValue operations contained have finished. + *

+ *

+ * {@code
+ * MultiFetch multifetch = ...;
+ * MultiFetch.Response response = client.execute(multifetch);
+ * List myResults = new ArrayList();
+ * for (RiakFuture f : response)
+ * {
+ *     try
+ *     {
+ *          FetchValue.Response response = f.get();
+ *          myResults.add(response.getValue(MyPojo.class));
+ *     }
+ *     catch (ExecutionException e)
+ *     {
+ *         // log error, etc.
+ *     }
+ * }}
+ *

+ *

+ * The maximum number of concurrent requests defaults to 10. This can be changed + * when constructing the operation. + *

+ *

+ * Be aware that because requests are being parallelized performance is also + * dependent on the client's underlying connection pool. If there are no connections + * available performance will suffer initially as connections will need to be established + * or worse they could time out. + *

+ * + * @author Dave Rusek + * @since 2.0 + */ +public final class MultiFetch extends RiakCommand> +{ + public static final int DEFAULT_MAX_IN_FLIGHT = 10; + + private final ArrayList locations = new ArrayList(); + private final Map, Object> options = new HashMap, Object>(); + private final int maxInFlight; + + private MultiFetch(Builder builder) + { + this.locations.addAll(builder.keys); + this.options.putAll(builder.options); + this.maxInFlight = builder.maxInFlight; + } + + @Override + protected RiakFuture> executeAsync(final RiakCluster cluster) + { + List fetchOperations = buildFetchOperations(); + MultiFetchFuture future = new MultiFetchFuture(locations); + + Submitter submitter = new Submitter(fetchOperations, maxInFlight, + cluster, future); + + Thread t = new Thread(submitter); + t.setDaemon(true); + t.start(); + return future; + } + + @SuppressWarnings("unchecked") + private List buildFetchOperations() + { + List fetchValueOperations = + new LinkedList(); + + for (Location location : locations) + { + FetchValue.Builder builder = new FetchValue.Builder(location); + + for (Option option : options.keySet()) + { + builder.withOption((Option) option, options.get(option)); + } + + fetchValueOperations.add(builder.build()); + } + + return fetchValueOperations; + + } + + /** + * Used to construct a MutiFetch command. + */ + public static class Builder + { + private ArrayList keys = new ArrayList(); + private Map, Object> options = new HashMap, Object>(); + private int maxInFlight = DEFAULT_MAX_IN_FLIGHT; + + /** + * Add a location to the list of locations to retrieve as part of + * this multifetch operation. + * + * @param location the location to add. + * @return this + */ + public Builder addLocation(Location location) + { + keys.add(location); + return this; + } + + /** + * Add a list of Locations to the list of locations to retrieve as part of + * this multifetch operation. + * + * @param location a list of Locations + * @return a reference to this object + */ + public Builder addLocations(Location... location) + { + keys.addAll(Arrays.asList(location)); + return this; + } + + /** + * Add a set of keys to the list of Locations to retrieve as part of + * this multifetch operation. + * + * @param location an Iterable set of Locations. + * @return a reference to this object + */ + public Builder addLocations(Iterable location) + { + for (Location loc : location) + { + keys.add(loc); + } + return this; + } + + /** + * Set the maximum number of requests to be in progress simultaneously. + *

+ * As noted, Riak does not actually have "MultiFetch" functionality. This + * operation simulates it by sending multiple fetch requests. This + * parameter controls how many outstanding requests are allowed simultaneously. + *

+ * @param maxInFlight the max number of outstanding requests. + * @return a reference to this object. + */ + public Builder withMaxInFlight(int maxInFlight) + { + this.maxInFlight = maxInFlight; + return this; + } + + /** + * A {@link Option} to use with each fetch operation. + * + * @param option an option + * @param value the option's associated value + * @param the type of the option's value + * @return a reference to this object. + */ + public Builder withOption(Option option, U value) + { + this.options.put(option, value); + return this; + } + + /** + * Set the Riak-side timeout value. + *

+ * By default, riak has a 60s timeout for operations. Setting + * this value will override that default for each fetch. + *

+ * @param timeout the timeout in milliseconds to be sent to riak. + * @return a reference to this object. + */ + public Builder withTimeout(int timeout) + { + withOption(Option.TIMEOUT, timeout); + return this; + } + + /** + * Build a {@link MultiFetch} operation from this builder + * + * @return an initialized {@link MultiFetch} operation + */ + public MultiFetch build() + { + return new MultiFetch(this); + } + + } + + /** + * The response from Raik for a MultiFetch command. + * + */ + public static final class Response implements Iterable> + { + + private final List> responses; + + Response(List> responses) + { + this.responses = responses; + } + + @Override + public Iterator> iterator() + { + return unmodifiableList(responses).iterator(); + } + + public List> getResponses() + { + return responses; + } + + } + + private class Submitter implements Runnable, RiakFutureListener + { + private final List operations; + private final Semaphore inFlight; + private final AtomicInteger received = new AtomicInteger(); + private final RiakCluster cluster; + private final MultiFetchFuture multiFuture; + + public Submitter(List operations, int maxInFlight, + RiakCluster cluster, MultiFetchFuture multiFuture) + { + this.operations = operations; + this.cluster = cluster; + this.multiFuture = multiFuture; + inFlight = new Semaphore(maxInFlight); + } + + @Override + public void run() + { + for (FetchValue fv : operations) + { + try + { + inFlight.acquire(); + } + catch (InterruptedException ex) + { + multiFuture.setFailed(ex); + break; + } + + RiakFuture future = + fv.executeAsync(cluster); + future.addListener(this); + } + } + + @Override + public void handle(RiakFuture f) + { + multiFuture.addFetchFuture(f); + inFlight.release(); + int completed = received.incrementAndGet(); + if (completed == operations.size()) + { + multiFuture.setCompleted(); + } + } + + } + + private class MultiFetchFuture extends ListenableFuture> + { + private final CountDownLatch latch = new CountDownLatch(1); + private final List locations; + private final List> futures; + private volatile Throwable exception; + + private MultiFetchFuture(List locations) + { + this.locations = locations; + futures = + Collections.synchronizedList(new LinkedList>()); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) + { + return false; + } + + @Override + public Response get() throws InterruptedException + { + latch.await(); + return new Response(futures); + } + + @Override + public Response get(long timeout, TimeUnit unit) throws InterruptedException + { + latch.await(timeout, unit); + if (isDone()) + { + return new Response(futures); + } + else + { + return null; + } + } + + @Override + public Response getNow() + { + if (isDone()) + { + return new Response(futures); + } + else + { + return null; + } + } + + @Override + public boolean isCancelled() + { + return false; + } + + @Override + public boolean isDone() + { + return latch.getCount() != 1; + } + + @Override + public void await() throws InterruptedException + { + latch.await(); + } + + @Override + public void await(long timeout, TimeUnit unit) throws InterruptedException + { + latch.await(timeout, unit); + } + + @Override + public boolean isSuccess() + { + return isDone() && exception == null; + } + + @Override + public List getQueryInfo() + { + return locations; + } + + @Override + public Throwable cause() + { + return exception; + } + + private void addFetchFuture(RiakFuture future) + { + futures.add(future); + } + + private void setCompleted() + { + latch.countDown(); + notifyListeners(); + } + + private void setFailed(Throwable t) + { + this.exception = t; + latch.countDown(); + notifyListeners(); + } + + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/kv/StoreValue.java b/src/main/java/com/basho/riak/client/api/commands/kv/StoreValue.java new file mode 100644 index 000000000..98c28a996 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/kv/StoreValue.java @@ -0,0 +1,480 @@ +/* + * Copyright 2013 Basho Technologies Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.basho.riak.client.api.commands.kv; + +import com.basho.riak.client.api.cap.Quorum; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.api.convert.Converter; +import com.basho.riak.client.api.convert.Converter.OrmExtracted; +import com.basho.riak.client.api.convert.ConverterFactory; +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.operations.StoreOperation; +import com.basho.riak.client.api.RiakCommand; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.api.commands.CoreFutureAdapter; +import com.basho.riak.client.api.commands.RiakOption; +import com.basho.riak.client.core.util.BinaryValue; + +import java.util.HashMap; +import java.util.Map; + +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.fasterxml.jackson.core.type.TypeReference; + +/** + * Command used to store a value in Riak. + * + *

+ * To store a value in Riak you supply a {@link com.basho.riak.client.core.query.Location} + * and an object to store. The object may be an instance of {@link com.basho.riak.client.core.query.RiakObject} + * or your own POJO. In the case of a POJO the default serialization uses the Jackson JSON library + * to store your object as JSON in Riak. + *

+ * {@code
+ * Namespace ns = new Namespace("my_type","my_bucket");
+ * Location loc = new Location(ns, "my_key");
+ * RiakObject ro = new RiakObject();
+ * ro.setValue(BinaryValue.create("This is my value"));
+ * StoreValue sv = 
+ *      new StoreValue.Builder(ro).withLocation(loc).build();
+ * StoreValue.Response response = client.execute(sv);}
+ *

+ *

+ * All operations can called async as well. + *

+ * {@code
+ * ...
+ * RiakFuture future = client.executeAsync(sv);
+ * ...
+ * future.await();
+ * if (future.isSuccess())
+ * { 
+ *     ... 
+ * }}
+ *

+ * @author Dave Rusek + * @since 2.0 + */ +public final class StoreValue extends RiakCommand +{ + private final Namespace namespace; + private final BinaryValue key; + private final Map, Object> options = + new HashMap, Object>(); + private final Object value; + private final TypeReference typeReference; + private final VClock vclock; + + StoreValue(Builder builder) + { + this.options.putAll(builder.options); + this.namespace = builder.namespace; + this.key = builder.key; + this.value = builder.value; + this.typeReference = builder.typeReference; + this.vclock = builder.vclock; + } + + + @SuppressWarnings("unchecked") + @Override + protected RiakFuture executeAsync(RiakCluster cluster) + { + Converter converter; + + if (typeReference == null) + { + converter = ConverterFactory.getInstance().getConverter(value.getClass()); + } + else + { + converter = ConverterFactory.getInstance().getConverter(typeReference); + } + + final OrmExtracted orm = converter.fromDomain(value, namespace, key); + + // If there's no vector clock in the object, use one possibly given via + // the builder. + if (orm.getRiakObject().getVClock() == null) + { + orm.getRiakObject().setVClock(vclock); + } + + RiakFuture coreFuture = + cluster.execute(buildCoreOperation(orm)); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) + { + @Override + protected Response convertResponse(StoreOperation.Response coreResponse) + { + Namespace ns = orm.getNamespace(); + BinaryValue key = orm.getKey(); + if (coreResponse.hasGeneratedKey()) + { + key = coreResponse.getGeneratedKey(); + } + + Location loc = new Location(ns, key); + + return new Response.Builder() + .withValues(coreResponse.getObjectList()) + .withGeneratedKey(coreResponse.getGeneratedKey()) + .withLocation(loc) // for ORM + .build(); + } + + @Override + protected Location convertQueryInfo(Location coreQueryInfo) + { + return coreQueryInfo; + } + + }; + coreFuture.addListener(future); + return future; + } + + private StoreOperation buildCoreOperation(OrmExtracted orm) + { + StoreOperation.Builder builder; + + if (orm.hasKey()) + { + Location loc = new Location(orm.getNamespace(), orm.getKey()); + builder = new StoreOperation.Builder(loc); + } + else + { + builder = new StoreOperation.Builder(orm.getNamespace()); + } + + builder.withContent(orm.getRiakObject()); + + for (Map.Entry, Object> opPair : options.entrySet()) + { + + RiakOption option = opPair.getKey(); + + if (option == Option.TIMEOUT) + { + builder.withTimeout((Integer) opPair.getValue()); + } + else if (option == Option.RETURN_HEAD) + { + builder.withReturnHead((Boolean) opPair.getValue()); + } + else if (option == Option.ASIS) + { + builder.withAsis((Boolean) opPair.getValue()); + } + else if (option == Option.DW) + { + builder.withDw(((Quorum) opPair.getValue()).getIntValue()); + } + else if (option == Option.IF_NONE_MATCH) + { + builder.withIfNoneMatch((Boolean) opPair.getValue()); + } + else if (option == Option.IF_NOT_MODIFIED) + { + builder.withIfNotModified((Boolean) opPair.getValue()); + } + else if (option == Option.N_VAL) + { + builder.withNVal((Integer) opPair.getValue()); + } + else if (option == Option.PW) + { + builder.withPw(((Quorum) opPair.getValue()).getIntValue()); + } + else if (option == Option.SLOPPY_QUORUM) + { + builder.withSloppyQuorum((Boolean) opPair.getValue()); + } + else if (option == Option.W) + { + builder.withW(((Quorum) opPair.getValue()).getIntValue()); + } + else if (option == Option.RETURN_BODY) + { + builder.withReturnBody((Boolean) opPair.getValue()); + } + + } + + return builder.build(); + } + + /** + * Options For controlling how Riak performs the store operation. + *

+ * These options can be supplied to the {@link StoreValue.Builder} to change + * how Riak performs the operation. These override the defaults provided + * by the bucket. + *

+ * @author Dave Rusek + * @since 2.0 + * @see Replication Properties + */ + public final static class Option extends RiakOption + { + + /** + * Write Quorum. + * How many replicas to write to before returning a successful response. + */ + public static final Option W = new Option("W"); + /** + * Durable Write Quorum. + * How many replicas to commit to durable storage before returning a successful response. + */ + public static final Option DW = new Option("DW"); + /** + * Primary Write Quorum. + * How many primary nodes must be up when the write is attempted. + */ + public static final Option PW = new Option("PW"); + /** + * If Not Modified. + * Update the value only if the vclock in the supplied object matches the one in the database. + */ + public static final Option IF_NOT_MODIFIED = new Option("IF_NOT_MODIFIED"); + /** + * If None Match. + * Store the value only if this bucket/key combination are not already defined. + */ + public static final Option IF_NONE_MATCH = new Option("IF_NONE_MATCH"); + /** + * Return Body. + * Return the object stored in Riak. Note this will return all siblings. + */ + public static final Option RETURN_BODY = new Option("RETURN_BODY"); + /** + * Return Head. + * Like {@link #RETURN_BODY} except that the value(s) in the object are blank to + * avoid returning potentially large value(s). + */ + public static final Option RETURN_HEAD = new Option("RETURN_HEAD"); + /** + * Timeout. + * Sets the server-side timeout for this operation. The default in Riak is 60 seconds. + */ + public static final Option TIMEOUT = new Option("TIMEOUT"); + + public static final Option ASIS = new Option("ASIS"); + public static final Option SLOPPY_QUORUM = new Option("SLOPPY_QUORUM"); + public static final Option N_VAL = new Option("N_VAL"); + + + private Option(String name) + { + super(name); + } + } + + + public static class Response extends KvResponseBase + { + private final BinaryValue generatedKey; + + Response(Init builder) + { + super(builder); + this.generatedKey = builder.generatedKey; + } + + public boolean hasGeneratedKey() + { + return generatedKey != null; + } + + public BinaryValue getGeneratedKey() + { + return generatedKey; + } + + /** + * @ExcludeFromJavadoc + */ + protected static abstract class Init> extends KvResponseBase.Init + { + private BinaryValue generatedKey; + + T withGeneratedKey(BinaryValue generatedKey) + { + this.generatedKey = generatedKey; + return self(); + } + } + + static class Builder extends Init + { + + @Override + protected Builder self() + { + return this; + } + + @Override + Response build() + { + return new Response(this); + } + } + } + + /** + * Used to construct a StoreValue command. + */ + public static class Builder + { + + private final Map, Object> options = + new HashMap, Object>(); + private final Object value; + private Namespace namespace; + private BinaryValue key; + private TypeReference typeReference; + private VClock vclock; + + + + /** + * Construct a Builder for a StoreValue command. + *

+ * Prior to storing the object, it's raw type (class) will + * be used to retrieve a converter from the {@link com.basho.riak.client.api.convert.ConverterFactory}. + * For anything other than a RiakObject this is the {@link com.basho.riak.client.api.convert.JSONConverter} + * by default. + *

+ * @param value The object to be stored in Riak. + */ + public Builder(Object value) + { + this.value = value; + } + + /** + * Construct a Builder for a StoreValue command. + *

+ * Prior to storing the object, the supplied TypeReference will + * be used to retrieve a converter from the {@link com.basho.riak.client.api.convert.ConverterFactory}. + * For anything other than a RiakObject this is the {@link com.basho.riak.client.api.convert.JSONConverter} + * by default. + *

+ * @param value The object to be stored in Riak. + * @param typeReference the TypeReference for the object. + */ + public Builder(Object value, TypeReference typeReference) + { + this.value = value; + this.typeReference = typeReference; + } + + /** + * Set the location to store the object. + *

+ * When storing a RiakObject or a POJO that does not have annotations for + * the bucket and key, a {@link com.basho.riak.client.core.query.Location} + * must be provided. + *

+ * @param location the location to store the object in Riak. + * @return a reference to this object. + */ + public Builder withLocation(Location location) + { + this.namespace = location.getNamespace(); + this.key = location.getKey(); + return this; + } + + /** + * Set the namespace to store the object. + *

+ * When storing a POJO that does not have an annotation for the + * bucket type and bucket, a {@link com.basho.riak.client.core.query.Namespace} + * must be supplied. + *

+ * @param namespace The namespaec to store the object. + * @return a reference to this object. + */ + public Builder withNamespace(Namespace namespace) + { + this.namespace = namespace; + return this; + } + + /** + * Set the Riak-side timeout value. + *

+ * By default, riak has a 60s timeout for operations. Setting + * this value will override that default for this operation. + *

+ * @param timeout the timeout in milliseconds to be sent to riak. + * @return a reference to this object. + */ + public Builder withTimeout(int timeout) + { + withOption(Option.TIMEOUT, timeout); + return this; + } + + /** + * Add an optional setting for this command. + * This will be passed along with the request to Riak to tell it how + * to behave when servicing the request. + * + * @param option the option + * @param value the value for the option + * @return a reference to this object. + */ + public Builder withOption(Option option, T value) + { + options.put(option, value); + return this; + } + + /** + * Set the vector clock. + *

+ * When storing core Java types ({@code HashMap}, + * {@code ArrayList},{@code String}, etc) or non-annotated POJOs this + * method allows you to specify the vector clock retrieved from a + * prior fetch operation. + *

+ * @param vclock The vector clock to send to Riak. + * @return a reference to this object. + */ + public Builder withVectorClock(VClock vclock) + { + this.vclock = vclock; + return this; + } + + /** + * Construct the StoreValue command. + * @return the new StoreValue command. + */ + public StoreValue build() + { + return new StoreValue(this); + } + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/kv/UpdateValue.java b/src/main/java/com/basho/riak/client/api/commands/kv/UpdateValue.java new file mode 100644 index 000000000..4f53ebc0c --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/kv/UpdateValue.java @@ -0,0 +1,599 @@ +/* + * Copyright 2013 Basho Technologies Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.basho.riak.client.api.commands.kv; + +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.api.RiakCommand; +import com.basho.riak.client.api.cap.UnresolvedConflictException; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.api.convert.ConversionException; +import com.basho.riak.client.api.convert.reflection.AnnotationUtil; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.RiakFutureListener; +import com.basho.riak.client.api.commands.ListenableFuture; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.RiakObject; +import com.fasterxml.jackson.core.type.TypeReference; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * Perform an full cycle update of a Riak value: fetch, resolve, modify, store. + * + *

+ * The UpdateValue command completely encapsulates the typical read/modify/write + * cycle used with data in Riak. + *

+ *

The object specified by the given {@link com.basho.riak.client.core.query.Location} + * will be fetched from Riak and have the {@link com.basho.riak.client.api.cap.ConflictResolver} stored in + * the {@link com.basho.riak.client.api.cap.ConflictResolverFactory} applied. The resolved + * object is then passed to your {@link com.basho.riak.client.api.commands.kv.UpdateValue.Update} + * and the result stored back into Riak. + *

+ *

+ * To create the mutation you wish to perform, you extend the + * {@link com.basho.riak.client.api.commands.kv.UpdateValue.Update} class: + *

+ *
+ * {@code
+ * class AppendUpdate extends UpdateValue.Update
+ * {
+ *     private final String update;
+ * 
+ *     public AppendUpdate(String update)
+ *     {
+ *         this.update = update;
+ *     }
+ * 
+ *     {@literal @Override}
+ *     public MyPojo apply(MyPojo original)
+ *     {
+ *         if (original == null)
+ *         {
+ *             original = new MyPojo();
+ *         }
+ * 
+ *         original.value += update;
+ *         return original;
+ *     }
+ * }
+ * 
+ * Namespace ns = new Namespace("my_type", "my_bucket");
+ * Location loc = new Location(ns, "my_key");
+ * AppendUpdate update = new AppendUpdate("append this string");
+ * 
+ * UpdateValue uv = 
+ *     new UpdateValue.Builder(loc).withUpdate(update).build();
+ * 
+ * UpdateValue.Response response = client.execute(uv);}
+ * @author Dave Rusek + * @since 2.0 + */ +public final class UpdateValue extends RiakCommand +{ + private final Location location; + private final Update update; + private final TypeReference typeReference; + private final Map, Object> fetchOptions = + new HashMap, Object>(); + private final Map, Object> storeOptions = + new HashMap, Object>(); + + UpdateValue(Builder builder) + { + this.location = builder.location; + this.update = builder.update; + this.typeReference = builder.typeReference; + this.fetchOptions.putAll(builder.fetchOptions); + this.storeOptions.putAll(builder.storeOptions); + } + + @SuppressWarnings("unchecked") + @Override + protected RiakFuture executeAsync(final RiakCluster cluster) + { + final UpdateValueFuture updateFuture = new UpdateValueFuture(location); + + FetchValue.Builder fetchBuilder = new FetchValue.Builder(location); + for (Map.Entry, Object> optPair : fetchOptions.entrySet()) + { + fetchBuilder.withOption((FetchValue.Option) optPair.getKey(), optPair.getValue()); + } + + RiakFuture fetchFuture = + fetchBuilder.build().executeAsync(cluster); + + // Anonymous listener that will do the work + RiakFutureListener fetchListener = + new RiakFutureListener() + { + @Override + public void handle(RiakFuture f) + { + if (f.isSuccess()) + { + FetchValue.Response fetchResponse; + try + { + fetchResponse = f.get(); + Object resolved = null; + VClock vclock = null; + + if (!fetchResponse.isNotFound()) + { + if (typeReference == null) + { + // Steal the type from the Update. Yes, Really. + ParameterizedType pType = (ParameterizedType)update.getClass().getGenericSuperclass(); + Type t = pType.getActualTypeArguments()[0]; + if (t instanceof ParameterizedType) + { + t = ((ParameterizedType)t).getRawType(); + } + + resolved = fetchResponse.getValue((Class) t); + } + else + { + resolved = fetchResponse.getValue(typeReference); + } + + // We get the vclock so we can inject it into the updated object. + // This is so the end user doesn't have to worry about vclocks + // in the Update. + vclock = fetchResponse.getVectorClock(); + } + + Object updated = ((Update)update).apply(resolved); + + if (update.isModified()) + { + AnnotationUtil.setVClock(updated, vclock); + + StoreValue.Builder store = + new StoreValue.Builder(updated, typeReference) + .withLocation(location) + .withVectorClock(vclock); + + for (Map.Entry, Object> optPair : storeOptions.entrySet()) + { + store.withOption((StoreValue.Option) optPair.getKey(), optPair.getValue()); + } + RiakFuture storeFuture = + store.build().executeAsync(cluster); + storeFuture.addListener(updateFuture); + } + else + { + Response updateResponse = new Response.Builder() + .withLocation(f.getQueryInfo()) + .withUpdated(false) + .build(); + updateFuture.setResponse(updateResponse); + } + + + } + catch (InterruptedException ex) + { + updateFuture.setException(ex); + } + catch (UnresolvedConflictException ex) + { + updateFuture.setException(ex); + } + catch (ConversionException ex) + { + updateFuture.setException(ex); + } + catch (ExecutionException ex) + { + updateFuture.setException(ex); + } + } + else + { + updateFuture.setException(f.cause().getCause()); + } + } + }; + + fetchFuture.addListener(fetchListener); + return updateFuture; + } + + /** + * + */ + public static class Response extends KvResponseBase + { + private final boolean wasUpdated; + + Response(Init builder) + { + super(builder); + this.wasUpdated = builder.wasUpdated; + } + + /** + * Determine if an update occurred. + *

+ * The supplied {@code Update} indicates if a modification was made. If + * no modification was made, no store operation is performed and this + * will return false. + *

+ * @return true if the supplied {@code Update} modified the retrieved object, + * false otherwise. + */ + public boolean wasUpdated() + { + return wasUpdated; + } + + /** + * @ExcludeFromJavadoc + */ + protected static abstract class Init> extends KvResponseBase.Init + { + private boolean wasUpdated; + + T withUpdated(boolean updated) + { + this.wasUpdated = updated; + return self(); + } + } + + static class Builder extends Init + { + @Override + protected Builder self() + { + return this; + } + + @Override + Response build() + { + return new Response(this); + } + } + + } + + /** + * An update on a Riak object + * + * @param + */ + public abstract static class Update + { + + private boolean modified = true; + + /** + * Modify the input value and return the modification. It is OK to + * modify the input value in-place and return it. + * + * @param original the resolved value + * @return a modified value + */ + public abstract T apply(T original); + + /** + * Set the modification status of this update, defaults to {@code true} + * + * @param modified true if modified + */ + protected void setModified(boolean modified) + { + this.modified = modified; + } + + /** + * true if this Update has modified the input value and requires a store, + * defaults to {@code true} + * + * @return true if modified + */ + public boolean isModified() + { + return modified; + } + + } + + /** + * Used to construct an UpdateValue command. + */ + public static class Builder + { + private final Location location; + private Update update; + private TypeReference typeReference; + private final Map, Object> fetchOptions = + new HashMap, Object>(); + private final Map, Object> storeOptions = + new HashMap, Object>(); + + /** + * Construct a Builder for an UpdateValue command. + * @param location the location of the object in Riak to update. + */ + public Builder(Location location) + { + this.location = location; + } + + /** + * Add an option for the fetch phase of the update. + * + * @param option the option + * @param value the option's value + * @param the type of the option's value + * @return this + */ + public Builder withFetchOption(FetchValue.Option option, U value) + { + fetchOptions.put(option, value); + return this; + } + + /** + * Add an option for the store phase of the update. + * + * @param option the option + * @param value the option's value + * @param the type of the option's value + * @return this + */ + public Builder withStoreOption(StoreValue.Option option, U value) + { + storeOptions.put(option, value); + return this; + } + + /** + * Supply the Update. + *

+ * During the update operation, the fetched value needs to be converted + * before being passed to the {@code ConflictResolver} and the {@code Update} + * method. + *

+ *

+ * Supplying only an {@code Update} means the raw type of {@code T} + * will be used to retrieve the {@code Converter} and {@code ConflictResolver} + * to be used. + *

+ * @param update The {@code Update} instance + * @return a reference to this object. + * @see com.basho.riak.client.api.convert.Converter + * @see com.basho.riak.client.api.convert.ConverterFactory + * @see com.basho.riak.client.api.cap.ConflictResolver + * @see com.basho.riak.client.api.cap.ConflictResolverFactory + */ + public Builder withUpdate(Update update) + { + this.update = update; + return this; + } + + /** + * Supply the Update with a TypeReference. + *

+ * During the update operation, the fetched value needs to be converted + * before being passed to the {@code ConflictResolver} and the {@code Update} + * method. If your domain object is a parameterized type you will need to supply + * a {@code TypeReference} so the appropriate {@code ConflictResolver} + * and {@code Converter} can be found. + *

+ * @param update The {@code Update} instance + * @param typeReference the {@code TypeReference} for the class used for conversion. + * @return a reference to this object. + * @see com.basho.riak.client.api.convert.Converter + * @see com.basho.riak.client.api.convert.ConverterFactory + * @see com.basho.riak.client.api.cap.ConflictResolver + * @see com.basho.riak.client.api.cap.ConflictResolverFactory + */ + public Builder withUpdate(Update update, TypeReference typeReference) + { + this.update = update; + this.typeReference = typeReference; + return this; + } + + /** + * Set the Riak-side timeout value. + *

+ * By default, riak has a 60s timeout for operations. Setting + * this value will override that default for both the + * fetch and store operation. + *

+ * @param timeout the timeout in milliseconds to be sent to riak. + * @return a reference to this object. + */ + public Builder withTimeout(int timeout) + { + withFetchOption(FetchValue.Option.TIMEOUT, timeout); + withStoreOption(StoreValue.Option.TIMEOUT, timeout); + return this; + } + + /** + * Construct the UpdateValue command. + * @return a new UpdateValue command. + */ + public UpdateValue build() + { + return new UpdateValue(this); + } + } + + private class UpdateValueFuture extends ListenableFuture + implements RiakFutureListener + { + private final Location location; + private final CountDownLatch latch = new CountDownLatch(1); + private volatile Throwable exception; + private volatile Response updateResponse; + + private UpdateValueFuture(Location location) + { + this.location = location; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) + { + return false; + } + + @Override + public Response get() throws InterruptedException, ExecutionException + { + latch.await(); + + if (exception != null) + { + throw new ExecutionException(exception); + } + else + { + return updateResponse; + } + } + + @Override + public Response get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException + { + boolean succeed = latch.await(timeout, unit); + + if (!succeed) + { + throw new TimeoutException(); + } + else if (exception != null) + { + throw new ExecutionException(exception); + } + else + { + return updateResponse; + } + } + + @Override + public Response getNow() + { + return updateResponse; + } + + @Override + public boolean isCancelled() + { + return false; + } + + @Override + public boolean isDone() + { + return latch.getCount() != 1; + } + + @Override + public void await() throws InterruptedException + { + latch.await(); + } + + @Override + public void await(long timeout, TimeUnit unit) throws InterruptedException + { + latch.await(timeout, unit); + } + + @Override + public boolean isSuccess() + { + return isDone() && exception == null; + } + + @Override + public Throwable cause() + { + return exception; + } + + private void setResponse(Response response) + { + this.updateResponse = response; + latch.countDown(); + notifyListeners(); + } + + private void setException(Throwable t) + { + this.exception = t; + latch.countDown(); + notifyListeners(); + } + + @Override + public void handle(RiakFuture f) + { + if (f.isSuccess()) + { + StoreValue.Response storeResponse; + try + { + storeResponse = f.get(); + Response response = new Response.Builder() + .withValues(storeResponse.getValues(RiakObject.class)) + .withLocation(f.getQueryInfo()) + .withUpdated(true) + .build(); + setResponse(response); + + } + catch (InterruptedException ex) + { + setException(ex); + } + catch (ExecutionException ex) + { + setException(ex); + } + } + else + { + setException(f.cause().getCause()); + } + } + + @Override + public Location getQueryInfo() + { + return location; + } + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/kv/package-info.java b/src/main/java/com/basho/riak/client/api/commands/kv/package-info.java new file mode 100644 index 000000000..ba9f8a6f9 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/kv/package-info.java @@ -0,0 +1,14 @@ +/** + * Commands for storing, fetching, and deleting objects. + *

Fetching, storing and deleting objects.

+ *
    + *
  • {@link com.basho.riak.client.api.commands.kv.FetchValue}
  • + *
  • {@link com.basho.riak.client.api.commands.kv.MultiFetch}
  • + *
  • {@link com.basho.riak.client.api.commands.kv.StoreValue}
  • + *
  • {@link com.basho.riak.client.api.commands.kv.UpdateValue}
  • + *
  • {@link com.basho.riak.client.api.commands.kv.DeleteValue}
  • + *
+ *

Listing Keys in a namespace.

+ *
  • {@link com.basho.riak.client.api.commands.kv.ListKeys}
+ * */ +package com.basho.riak.client.api.commands.kv; \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketInput.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketInput.java new file mode 100644 index 000000000..34594c689 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketInput.java @@ -0,0 +1,35 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.api.commands.mapreduce.filters.KeyFilter; + +import java.util.Collection; + +public class BucketInput implements MapReduceInput +{ + + private final Namespace namespace; + private final Collection filters; + + public BucketInput(Namespace namespace, Collection filters) + { + this.namespace = namespace; + this.filters = filters; + } + + public Namespace getNamespace() + { + return namespace; + } + + public boolean hasFilters() + { + return filters != null && !filters.isEmpty(); + } + + public Collection getFilters() + { + return filters; + } + +} \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketInputSerializer.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketInputSerializer.java new file mode 100644 index 000000000..fd1b7d884 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketInputSerializer.java @@ -0,0 +1,48 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.core.query.Namespace; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +public class BucketInputSerializer extends JsonSerializer +{ + @Override + public void serialize(BucketInput input, JsonGenerator jg, SerializerProvider sp) throws IOException + { + if (input.hasFilters()) + { + jg.writeStartObject(); + if (!input.getNamespace().getBucketType().toStringUtf8().equals(Namespace.DEFAULT_BUCKET_TYPE)) + { + jg.writeArrayFieldStart("bucket"); + jg.writeString(input.getNamespace().getBucketTypeAsString()); + jg.writeString(input.getNamespace().getBucketNameAsString()); + jg.writeEndArray(); + } + else + { + jg.writeObjectField("bucket", input.getNamespace().getBucketNameAsString()); + } + + jg.writeObjectField("key_filters", input.getFilters()); + + jg.writeEndObject(); + } + else if (!input.getNamespace().getBucketType().toStringUtf8().equals(Namespace.DEFAULT_BUCKET_TYPE)) + { + jg.writeStartArray(); + jg.writeString(input.getNamespace().getBucketTypeAsString()); + jg.writeString(input.getNamespace().getBucketNameAsString()); + jg.writeEndArray(); + } + else + { + jg.writeString(input.getNamespace().getBucketNameAsString()); + } + + + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketKeyInput.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketKeyInput.java new file mode 100644 index 000000000..a10fd27de --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketKeyInput.java @@ -0,0 +1,49 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.core.query.Location; + +import java.util.Collection; + +public class BucketKeyInput implements MapReduceInput +{ + + private final Collection inputs; + + public BucketKeyInput(Collection inputs) + { + this.inputs = inputs; + } + + public Collection getInputs() + { + return inputs; + } + + static class IndividualInput + { + public final Location location; + public final String keyData; + + IndividualInput(Location location, String keyData) + { + this.location = location; + if (keyData == null) + { + throw new IllegalArgumentException("keyData cannot be null."); + } + this.keyData = keyData; + } + + IndividualInput(Location location) + { + this.location = location; + this.keyData = ""; + } + + public boolean hasKeyData() + { + return keyData != null; + } + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketKeyInputSerializer.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketKeyInputSerializer.java new file mode 100644 index 000000000..0e1bfc61f --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketKeyInputSerializer.java @@ -0,0 +1,45 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +public class BucketKeyInputSerializer extends JsonSerializer +{ + + @Override + public void serialize(BucketKeyInput input, JsonGenerator jg, SerializerProvider sp) throws IOException + { + + jg.writeStartArray(); + + for (BucketKeyInput.IndividualInput i : input.getInputs()) + { + + jg.writeStartArray(); + + Location loc = i.location; + jg.writeString(loc.getNamespace().getBucketNameAsString()); + jg.writeString(loc.getKeyAsString()); + jg.writeObject(i.keyData); + + // TODO: Remove this when bug in Riak is fixed. + // There's a bug in Riak where if you explicitly specify + // "default" with the 4 argument version of input, it + // blows up. + if (!loc.getNamespace().getBucketTypeAsString().equals(Namespace.DEFAULT_BUCKET_TYPE)) + { + jg.writeString(loc.getNamespace().getBucketTypeAsString()); + } + jg.writeEndArray(); + + } + + jg.writeEndArray(); + + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketKeyMapReduce.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketKeyMapReduce.java new file mode 100644 index 000000000..84be3c249 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketKeyMapReduce.java @@ -0,0 +1,60 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.core.query.Location; + +import java.util.ArrayList; +import java.util.List; + +/** + * Command used to perform a map reduce operation over a specific set of keys in a bucket. + * @author Dave Rusek + * @since 2.0 + */ +public class BucketKeyMapReduce extends MapReduce +{ + + public BucketKeyMapReduce(BucketKeyInput input, Builder builder) + { + super(input, builder); + } + + /** + * Builder for a BucketKeyMapReduce command. + */ + public static class Builder extends MapReduce.Builder + { + + private List input = + new ArrayList(); + + @Override + protected Builder self() + { + return this; + } + + public Builder withLocation(Location location) + { + input.add(new BucketKeyInput.IndividualInput(location)); + return this; + } + + public Builder withLocation(Location location, String keyData) + { + input.add(new BucketKeyInput.IndividualInput(location, keyData)); + return this; + } + + public BucketKeyMapReduce build() + { + if (input == null) + { + throw new IllegalStateException("At least one location must be specified"); + } + + return new BucketKeyMapReduce(new BucketKeyInput(input), this); + } + + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketMapReduce.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketMapReduce.java new file mode 100644 index 000000000..2074608ff --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/BucketMapReduce.java @@ -0,0 +1,59 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.api.commands.mapreduce.filters.KeyFilter; + +import java.util.ArrayList; +import java.util.List; + +/** + * Command used to perform a Map Reduce operation over a bucket in Riak. + * @author Dave Rusek + * @since 2.0 + */ +public class BucketMapReduce extends MapReduce +{ + + protected BucketMapReduce(BucketInput input, Builder builder) + { + super(input, builder); + } + + /** + * Builder for a BucketMapReduce command. + */ + public static class Builder extends MapReduce.Builder + { + + private Namespace namespace; + private final List filters = new ArrayList(); + + @Override + protected Builder self() + { + return this; + } + + public Builder withNamespace(Namespace namespace) + { + this.namespace = namespace; + return this; + } + + public Builder withKeyFilter(KeyFilter filter) + { + filters.add(filter); + return this; + } + + public BucketMapReduce build() + { + if (namespace == null) + { + throw new IllegalStateException("A Namespace must be specified"); + } + + return new BucketMapReduce(new BucketInput(namespace, filters), this); + } + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/FunctionPhase.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/FunctionPhase.java new file mode 100644 index 000000000..6c1ebed3e --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/FunctionPhase.java @@ -0,0 +1,52 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.core.query.functions.Function; + +class FunctionPhase extends MapReducePhase +{ + final PhaseFunction phaseFunction; + private final Object arg; // TODO object? you sure? + + /** + * Create a Phase containing a function + * + * @param phaseFunction the {@link Function} + * @param arg an argument that will be passed to the phase verbatim + * (Object#toString) + * @param keepResult if the result should be returned or merely provide input for + * the next phase. + */ + public FunctionPhase(PhaseType type, Function phaseFunction, Object arg, boolean keepResult) + { + super(type); + this.phaseFunction = new PhaseFunction(phaseFunction, keepResult); + this.arg = arg; + } + + /** + * @return the phaseFunction + */ + public Function getFunction() + { + return phaseFunction.getFunction(); + } + + /** + * @return the keep + */ + public Boolean isKeep() + { + return phaseFunction.isKeep(); + } + + /** + * @return the arg + */ + public Object getArg() + { + return arg; + } + + + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/FunctionPhaseSerializer.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/FunctionPhaseSerializer.java new file mode 100644 index 000000000..a22aeb536 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/FunctionPhaseSerializer.java @@ -0,0 +1,61 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.core.query.functions.Function; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +class FunctionPhaseSerializer extends JsonSerializer +{ + + private void writePhaseFunction(JsonGenerator jg, FunctionPhase phase) throws IOException + { + Function function = phase.getFunction(); + + jg.writeStartObject(); + + jg.writeStringField("language", function.isJavascript() ? "javascript" : "erlang"); + + if (function.isJavascript()) + { + if (function.isNamed()) + { + jg.writeStringField("name", function.getName()); + } else if (function.isStored()) + { + jg.writeStringField("bucket", function.getBucket()); + jg.writeStringField("key", function.getKey()); + } else if (function.isAnonymous()) + { + jg.writeStringField("source", function.getSource()); + } else + { + throw new IllegalStateException("Cannot determine function type"); + } + } else if (!function.isJavascript()) + { + jg.writeStringField("module", function.getModule()); + jg.writeStringField("function", function.getFunction()); + } + + jg.writeBooleanField("keep", phase.isKeep()); + jg.writeObjectField("arg", phase.getArg()); + + jg.writeEndObject(); + } + + @Override + public void serialize(FunctionPhase phase, JsonGenerator jg, SerializerProvider sp) throws IOException + { + + jg.writeStartObject(); + + jg.writeFieldName(phase.getType().toString()); + writePhaseFunction(jg, phase); + + jg.writeEndObject(); + + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/IndexInput.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/IndexInput.java new file mode 100644 index 000000000..9875cd2b7 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/IndexInput.java @@ -0,0 +1,77 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.core.query.Namespace; + +public class IndexInput implements MapReduceInput +{ + private final Namespace namespace; + private final String index; + private final IndexCriteria criteria; + + public IndexInput(Namespace namespace, String index, IndexCriteria criteria) + { + this.namespace = namespace; + this.index = index; + this.criteria = criteria; + } + + public Namespace getNamespace() + { + return namespace; + } + + public String getIndex() + { + return index; + } + + public IndexCriteria getCriteria() + { + return criteria; + } + + static interface IndexCriteria + { + } + + static class RangeCriteria implements IndexCriteria + { + + private final V begin; + private final V end; + + public RangeCriteria(V begin, V end) + { + this.begin = begin; + this.end = end; + } + + public V getBegin() + { + return begin; + } + + public V getEnd() + { + return end; + } + + } + + static class MatchCriteria implements IndexCriteria + { + + private final V value; + + public MatchCriteria(V value) + { + this.value = value; + } + + public V getValue() + { + return value; + } + + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/IndexInputSerializer.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/IndexInputSerializer.java new file mode 100644 index 000000000..fc0902260 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/IndexInputSerializer.java @@ -0,0 +1,44 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.core.query.Namespace; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +public class IndexInputSerializer extends JsonSerializer +{ + @Override + public void serialize(IndexInput input, JsonGenerator jg, SerializerProvider sp) throws IOException + { + jg.writeStartObject(); + + // Riak bug ... explicitly specifying "default" as the type breaks things + if (!input.getNamespace().getBucketTypeAsString().equals(Namespace.DEFAULT_BUCKET_TYPE)) + { + jg.writeArrayFieldStart("bucket"); + jg.writeString(input.getNamespace().getBucketTypeAsString()); + jg.writeString(input.getNamespace().getBucketNameAsString()); + jg.writeEndArray(); + } + else + { + jg.writeStringField("bucket", input.getNamespace().getBucketNameAsString()); + } + + jg.writeStringField("index", input.getIndex()); + IndexInput.IndexCriteria criteria = input.getCriteria(); + if (criteria instanceof IndexInput.MatchCriteria) + { + IndexInput.MatchCriteria match = (IndexInput.MatchCriteria) criteria; + jg.writeObjectField("key", match.getValue()); + } else if (criteria instanceof IndexInput.RangeCriteria) + { + IndexInput.RangeCriteria range = (IndexInput.RangeCriteria) criteria; + jg.writeObjectField("start", range.getBegin()); + jg.writeObjectField("end", range.getEnd()); + } + jg.writeEndObject(); + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/IndexMapReduce.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/IndexMapReduce.java new file mode 100644 index 000000000..7d3424dae --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/IndexMapReduce.java @@ -0,0 +1,93 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.util.BinaryValue; + +/** + * Command used to performa a map reduce operation using a secondary index (2i) as input. + * @author Dave Rusek + * @since 2.0 + */ +public class IndexMapReduce extends MapReduce +{ + protected IndexMapReduce(IndexInput input, Builder builder) + { + super(input, builder); + } + + /** + * Builder for a IndexMapReduce command. + */ + public static class Builder extends MapReduce.Builder + { + + private Namespace namespace; + private String index; + private IndexInput.IndexCriteria criteria; + + @Override + protected Builder self() + { + return this; + } + + public Builder withNamespace(Namespace namespace) + { + this.namespace = namespace; + return this; + } + + public Builder withIndex(String index) + { + this.index = index; + return this; + } + + public Builder withRange(final long start, final long end) + { + this.criteria = new IndexInput.RangeCriteria(start, end); + return this; + } + + public Builder withRange(final BinaryValue start, final BinaryValue end) + { + this.criteria = new IndexInput.RangeCriteria(start, end); + return this; + } + + public Builder withMatchValue(final long value) + { + this.criteria = new IndexInput.MatchCriteria(value); + return this; + } + + public Builder withMatchValue(final BinaryValue value) + { + this.criteria = new IndexInput.MatchCriteria(value); + return this; + } + + public IndexMapReduce build() + { + + if (namespace == null) + { + throw new IllegalStateException("A namespace must be specified"); + } + + if (index == null) + { + throw new IllegalStateException("An index must be specified"); + } + + if (criteria == null) + { + throw new IllegalStateException("An index search criteria must be specified"); + } + + return new IndexMapReduce(new IndexInput(namespace, index, criteria), this); + } + + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/LinkPhase.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/LinkPhase.java new file mode 100644 index 000000000..ef83a5fd6 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/LinkPhase.java @@ -0,0 +1,97 @@ +/* + * 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.commands.mapreduce; + +/** + * {@link MapReducePhase} implementation that models a Link Phase + * + * @author russell + * @see MapReduce + * @see MapPhase + * @see ReducePhase + */ +class LinkPhase extends MapReducePhase +{ + + private final String bucket; + private final String tag; + private final Boolean keep; + + /** + * Create a Link Phase that points to bucket / tag + * . + * + * @param bucket the bucket at the end of the link (or "_" or "" for wildcard) + * @param tag the tag (or ("_", or "" for wildcard) + * @param keep to keep the result of this phase and return it at the end of + * the operation + */ + public LinkPhase(String bucket, String tag, Boolean keep) + { + super(PhaseType.LINK); + this.bucket = bucket; + this.tag = tag; + this.keep = keep; + } + + /** + * Create a Link Phase that points to bucket / tag + * keep will be false + * + * @param bucket the bucket at the end of the link (or "_" or "" for wildcard) + * @param tag the tag (or ("_", or "" for wildcard) + */ + public LinkPhase(String bucket, String tag) + { + this(bucket, tag, null); + } + + /** + * The bucket for this link phase + * + * @return the bucket + */ + public String getBucket() + { + return bucket; + } + + /** + * The tag for this link phase + * + * @return the tag + */ + public String getTag() + { + return tag; + } + + /** + * Keep the result or just use as input to the next phase? + * + * @return whether the result is kept or just passed to the next phase. + */ + public Boolean isKeep() + { + return keep; + } + + /* (non-Javadoc) + * @see com.basho.riak.newapi.query.MapReducePhase#getType() + */ + public PhaseType getType() + { + return PhaseType.LINK; + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/LinkPhaseSerializer.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/LinkPhaseSerializer.java new file mode 100644 index 000000000..d70e52c9d --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/LinkPhaseSerializer.java @@ -0,0 +1,24 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +class LinkPhaseSerializer extends JsonSerializer +{ + @Override + public void serialize(LinkPhase phase, JsonGenerator jg, SerializerProvider sp) throws IOException + { + jg.writeStartObject(); + jg.writeFieldName(phase.getType().toString()); + + jg.writeStartObject(); + jg.writeStringField("bucket", phase.getBucket()); + jg.writeStringField("tag", phase.getTag()); + jg.writeEndObject(); + + jg.writeEndObject(); + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/MapPhase.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/MapPhase.java new file mode 100644 index 000000000..8753a236a --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/MapPhase.java @@ -0,0 +1,74 @@ +/* + * 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.commands.mapreduce; + +import com.basho.riak.client.core.query.functions.Function; + +/** + * A Map Phase of a Map/Reduce job spec. + * + * @author russell + */ +class MapPhase extends FunctionPhase +{ + + /** + * Create a MapPhase + * + * @param phaseFunction the {@link Function} + * @param arg an argument that will be passed to the phase verbatim + * (Object#toString) + * @param keepResult if the result should be returned or merely provide input for + * the next phase. + */ + public MapPhase(Function phaseFunction, Object arg, boolean keepResult) + { + super(PhaseType.MAP, phaseFunction, arg, keepResult); + } + + /** + * Create a MapPhase + * + * @param phaseFunction the {@link Function} + * @param arg an argument that will be passed to the phase verbatim + * (Object#toString) + */ + public MapPhase(Function phaseFunction, Object arg) + { + this(phaseFunction, arg, false); + } + + /** + * Create a MapPhase + * + * @param phaseFunction the {@link Function} + */ + public MapPhase(Function phaseFunction) + { + this(phaseFunction, null, false); + } + + /** + * Create a MapPhase + * + * @param phaseFunction the {@link Function} + * @param keep if the result should be returned or merely provide input for + * the next phase. + */ + public MapPhase(Function phaseFunction, boolean keep) + { + this(phaseFunction, null, keep); + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/MapReduce.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/MapReduce.java new file mode 100644 index 000000000..091c69d11 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/MapReduce.java @@ -0,0 +1,392 @@ +/* + * 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.commands.mapreduce; + +import com.basho.riak.client.api.RiakCommand; +import com.basho.riak.client.api.RiakException; +import com.basho.riak.client.api.convert.ConversionException; +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.MapReduceOperation; +import com.basho.riak.client.api.commands.CoreFutureAdapter; +import com.basho.riak.client.core.query.functions.Function; +import com.basho.riak.client.core.util.BinaryValue; +import com.fasterxml.jackson.core.JsonEncoding; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.*; + +/** + * Base abstract class for all MapReduce commands. + *

See Map/Reduce for details.

+ * @author Dave Rusek + * @since 2.0 + */ +public abstract class MapReduce extends RiakCommand +{ + private final MapReduceSpec spec; + + @SuppressWarnings("unchecked") + protected MapReduce(MapReduceInput input, Builder builder) + { + this.spec = new MapReduceSpec(input, builder.phases, builder.timeout); + } + + @Override + protected RiakFuture executeAsync(RiakCluster cluster) + { + + BinaryValue jobSpec; + try + { + String spec = writeSpec(); + //System.out.println(spec); + jobSpec = BinaryValue.create(spec); + } catch (RiakException e) + { + throw new RuntimeException(e); + } + + MapReduceOperation operation = new MapReduceOperation.Builder(jobSpec).build(); + + final RiakFuture coreFuture = cluster.execute(operation); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) + { + @Override + protected Response convertResponse(MapReduceOperation.Response coreResponse) + { + return new Response(coreResponse.getResults()); + } + + @Override + protected BinaryValue convertQueryInfo(BinaryValue coreQueryInfo) + { + return coreQueryInfo; + } + }; + + coreFuture.addListener(future); + + return future; + + } + + /** + * Creates the JSON string of the M/R job for submitting to the client + *

+ * Uses Jackson to write out the JSON string. I'm not very happy with this method, it is a candidate for change. + *

+ * TODO re-evaluate this method, look for something smaller and more elegant. + * + * @return a String of JSON + * @throws RiakException if, for some reason, we can't create a JSON string. + */ + String writeSpec() throws RiakException + { + + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try + { + JsonGenerator jg = new JsonFactory().createGenerator(out, JsonEncoding.UTF8); + + ObjectMapper objectMapper = new ObjectMapper(); + SimpleModule specModule = new SimpleModule("SpecModule", Version.unknownVersion()); + specModule.addSerializer(LinkPhase.class, new LinkPhaseSerializer()); + specModule.addSerializer(FunctionPhase.class, new FunctionPhaseSerializer()); + specModule.addSerializer(BucketInput.class, new BucketInputSerializer()); + specModule.addSerializer(SearchInput.class, new SearchInputSerializer()); + specModule.addSerializer(BucketKeyInput.class, new BucketKeyInputSerializer()); + specModule.addSerializer(IndexInput.class, new IndexInputSerializer()); + objectMapper.registerModule(specModule); + + jg.setCodec(objectMapper); + + List phases = spec.getPhases(); + phases.get(phases.size() - 1).setKeep(true); + jg.writeObject(spec); + + jg.flush(); + + return out.toString("UTF8"); + + } catch (IOException e) + { + throw new RiakException(e); + } + } + + /** + * Base abstract class for all MapReduce command builders. + */ + protected static abstract class Builder> + { + + protected final List phases = new LinkedList(); + protected Long timeout; + + /** + * Set the operations timeout + * + * @param timeout + * @return this + */ + public T timeout(long timeout) + { + this.timeout = timeout; + return self(); + } + + /** + * Add {@link MapPhase} to the query + * + * @param phaseFunction the {@link Function} + * @param keep keep the results and return them with the query results? + * @return a reference to this object. + */ + public T withMapPhase(Function phaseFunction, boolean keep) + { + synchronized (phases) + { + phases.add(new MapPhase(phaseFunction, keep)); + } + + return self(); + } + + /** + * Add a MapPhase + * + * @param phaseFunction the {@link Function} + * @param arg an argument that will be passed to the phase verbatim (Object#toString) + * @param keep if the result should be returned or merely provide input for the next phase. + * @return a reference to this object. + */ + public T withMapPhase(Function phaseFunction, Object arg, boolean keep) + { + synchronized (phases) + { + phases.add(new MapPhase(phaseFunction, arg, keep)); + } + + return self(); + } + + /** + * Add a MapPhase + * + * @param phaseFunction the {@link Function} + * @param arg an argument that will be passed to the phase verbatim (Object#toString) + * @return a reference to this object. + */ + public T withMapPhase(Function phaseFunction, Object arg) + { + synchronized (phases) + { + phases.add(new MapPhase(phaseFunction, arg)); + } + + return self(); + } + + /** + * Add a MapPhase + * + * @param phaseFunction the {@link Function} + * @return a reference to this object. + */ + public T withMapPhase(Function phaseFunction) + { + synchronized (phases) + { + phases.add(new MapPhase(phaseFunction)); + } + + return self(); + } + + /** + * Add {@link ReducePhase} to the query + * + * @param phaseFunction the {@link Function} + * @param keep keep the results and return them with the query results? + * @return a reference to this object. + */ + public T withReducePhase(Function phaseFunction, boolean keep) + { + synchronized (phases) + { + phases.add(new ReducePhase(phaseFunction, keep)); + } + + return self(); + } + + /** + * Add a {@link ReducePhase} + * + * @param phaseFunction the {@link Function} + * @param arg an argument that will be passed to the phase verbatim (Object#toString) + * @param keep if the result should be returned or merely provide input for the next phase. + * @return a reference to this object. + */ + public T withReducePhase(Function phaseFunction, Object arg, boolean keep) + { + synchronized (phases) + { + phases.add(new ReducePhase(phaseFunction, arg, keep)); + } + + return self(); + } + + /** + * Add a {@link ReducePhase} + * + * @param phaseFunction the {@link Function} + * @param arg an argument that will be passed to the phase verbatim + * @return a reference to this object. + */ + public T withReducePhase(Function phaseFunction, Object arg) + { + synchronized (phases) + { + phases.add(new ReducePhase(phaseFunction, arg)); + } + + return self(); + } + + /** + * Add a {@link ReducePhase} + * + * @param phaseFunction + * @return a reference to this object. + */ + public T withReducePhase(Function phaseFunction) + { + synchronized (phases) + { + phases.add(new ReducePhase(phaseFunction)); + } + + return self(); + } + + /** + * Add a Link Phase that points to bucket / tag . + * + * @param bucket the bucket at the end of the link (or "_" or "" for wildcard) + * @param tag the tag (or ("_", or "" for wildcard) + * @param keep to keep the result of this phase and return it at the end of the operation + * @return a reference to this object. + */ + public T withLinkPhase(String bucket, String tag, boolean keep) + { + synchronized (phases) + { + phases.add(new LinkPhase(bucket, tag, keep)); + } + + return self(); + } + + /** + * Create a Link Phase that points to bucket / tag keep will be + * false + * + * @param bucket the bucket at the end of the link (or "_" or "" for wildcard) + * @param tag the tag (or ("_", or "" for wildcard) + * @return a reference to this object. + */ + public T withLinkPhase(String bucket, String tag) + { + synchronized (phases) + { + phases.add(new LinkPhase(bucket, tag)); + } + + return self(); + } + + protected abstract T self(); + + } + + /** + * Response from a Map Reduce command. + */ + public static class Response + { + + private final Map results; + + public Response(Map results) + { + this.results = results; + } + + public boolean hasResultForPhase(int i) + { + return results.containsKey(i); + } + + public ArrayNode getResultForPhase(int i) + { + return results.get(i); + } + + public ArrayNode getResultsFromAllPhases() + { + return flattenResults(); + } + + public Collection getResultsFromAllPhases(Class resultType) + { + ArrayNode flat = flattenResults(); + ObjectMapper mapper = new ObjectMapper(); + try + { + return mapper.readValue(flat.toString(), mapper.getTypeFactory().constructCollectionType(Collection.class, resultType)); + } + catch (IOException ex) + { + throw new ConversionException("Could not convert Mapreduce response",ex); + } + } + + private ArrayNode flattenResults() + { + final JsonNodeFactory factory = JsonNodeFactory.instance; + ArrayNode flatArray = factory.arrayNode(); + for (Map.Entry entry : results.entrySet()) + { + flatArray.addAll(entry.getValue()); + } + return flatArray; + } + + } +} \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/MapReduceInput.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/MapReduceInput.java new file mode 100644 index 000000000..897edfd85 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/MapReduceInput.java @@ -0,0 +1,5 @@ +package com.basho.riak.client.api.commands.mapreduce; + +public interface MapReduceInput +{ +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/MapReducePhase.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/MapReducePhase.java new file mode 100644 index 000000000..add52b880 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/MapReducePhase.java @@ -0,0 +1,80 @@ +/* + * 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.commands.mapreduce; + +/** + * Shared common interface for Map/Reduce phase definitions. + * + * @author russell + */ +public abstract class MapReducePhase +{ + + /** + * Possible phase typess. + */ + public enum PhaseType + { + LINK("link"), MAP("map"), REDUCE("reduce"); + + private final String phaseName; + + private PhaseType(String phaseName) + { + this.phaseName = phaseName; + } + + public String toString() + { + return this.phaseName; + } + } + + private final PhaseType type; + private boolean keep; + + protected MapReducePhase(PhaseType type) + { + this.type = type; + } + + /** + * Is this phase's output to be returned or only passed as input to the next phase. + * + * @return true if the results are returned, false otherwise. + */ + Boolean isKeep() { + return keep; + } + + /** + * set whether this should be kept + * + * @param keep + */ + void setKeep(boolean keep) + { + this.keep = keep; + } + + /** + * The PhaseType of this {@link MapReducePhase} implementation. + * + * @return a PhaseType. + */ + PhaseType getType() + { + return type; + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/MapReduceSpec.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/MapReduceSpec.java new file mode 100644 index 000000000..5f2255b66 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/MapReduceSpec.java @@ -0,0 +1,38 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +@JsonInclude(JsonInclude.Include.NON_NULL) +class MapReduceSpec +{ + + private final MapReduceInput inputs; + @JsonProperty(value = "query") + private final List phases; + private final Long timeout; + + MapReduceSpec(MapReduceInput inputs, List phases, Long timeout) + { + this.inputs = inputs; + this.phases = phases; + this.timeout = timeout; + } + + public List getPhases() + { + return phases; + } + + public Long getTimeout() + { + return timeout; + } + + public MapReduceInput getInputs() + { + return inputs; + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/PhaseFunction.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/PhaseFunction.java new file mode 100644 index 000000000..b17631411 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/PhaseFunction.java @@ -0,0 +1,27 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.core.query.functions.Function; + +class PhaseFunction +{ + + private final Function function; + private final boolean keep; + + public PhaseFunction(Function function, boolean keep) + { + this.function = function; + this.keep = keep; + } + + public Function getFunction() + { + return function; + } + + public boolean isKeep() + { + return keep; + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/PhaseFunctionSerializer.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/PhaseFunctionSerializer.java new file mode 100644 index 000000000..da1ddd78d --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/PhaseFunctionSerializer.java @@ -0,0 +1,47 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.core.query.functions.Function; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +class PhaseFunctionSerializer extends JsonSerializer +{ + @Override + public void serialize(PhaseFunction phaseFunction, JsonGenerator jg, SerializerProvider sp) throws IOException + { + Function function = phaseFunction.getFunction(); + + jg.writeStartObject(); + + jg.writeStringField("language", function.isJavascript() ? "javascript" : "erlang"); + + if (function.isJavascript()) + { + if (function.isNamed()) + { + jg.writeStringField("name", function.getName()); + } else if (function.isStored()) + { + jg.writeStringField("bucket", function.getBucket()); + jg.writeStringField("key", function.getKey()); + } else if (function.isAnonymous()) + { + jg.writeStringField("source", function.getSource()); + } else + { + throw new IllegalStateException("Cannot determine function type"); + } + } else if (!function.isJavascript()) + { + jg.writeStringField("module", function.getModule()); + jg.writeStringField("function", function.getFunction()); + } + + jg.writeBooleanField("keep", phaseFunction.isKeep()); + + jg.writeEndObject(); + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/ReducePhase.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/ReducePhase.java new file mode 100644 index 000000000..1d87c3ce7 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/ReducePhase.java @@ -0,0 +1,64 @@ +/* + * 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.commands.mapreduce; + +import com.basho.riak.client.core.query.functions.Function; + +/** + * A reduce phase of a MapReduce job spec. Just a tag class. + * + * @author russell + */ +class ReducePhase extends FunctionPhase +{ + + /** + * Create a Reduce Phase + * + * @param phaseFunction + * @param arg + * @param keepResult + */ + public ReducePhase(Function phaseFunction, Object arg, boolean keepResult) + { + super(PhaseType.REDUCE, phaseFunction, arg, keepResult); + } + + /** + * @param phaseFunction + * @param arg + */ + public ReducePhase(Function phaseFunction, Object arg) + { + this(phaseFunction, arg, false); + } + + /** + * @param phaseFunction + * @param keep + */ + public ReducePhase(Function phaseFunction, boolean keep) + { + this(phaseFunction, null, keep); + } + + /** + * @param phaseFunction + */ + public ReducePhase(Function phaseFunction) + { + this(phaseFunction, null, false); + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/SearchInput.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/SearchInput.java new file mode 100644 index 000000000..1f4db521a --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/SearchInput.java @@ -0,0 +1,24 @@ +package com.basho.riak.client.api.commands.mapreduce; + +public class SearchInput implements MapReduceInput +{ + private final String index; + private final String search; + + public SearchInput(String index, String search) + { + this.index = index; + this.search = search; + } + + public String getIndex() + { + return index; + } + + public String getSearch() + { + return search; + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/SearchInputSerializer.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/SearchInputSerializer.java new file mode 100644 index 000000000..16ef4595e --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/SearchInputSerializer.java @@ -0,0 +1,24 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +public class SearchInputSerializer extends JsonSerializer +{ + @Override + public void serialize(SearchInput input, JsonGenerator jg, SerializerProvider sp) throws IOException + { + jg.writeStartObject(); + jg.writeObjectField("module", "yokozuna"); + jg.writeObjectField("function", "mapred_search"); + + jg.writeArrayFieldStart("arg"); + jg.writeString(input.getIndex()); + jg.writeString(input.getSearch()); + jg.writeEndArray(); + jg.writeEndObject(); + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/SearchMapReduce.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/SearchMapReduce.java new file mode 100644 index 000000000..ae2119d33 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/SearchMapReduce.java @@ -0,0 +1,58 @@ +package com.basho.riak.client.api.commands.mapreduce; + +/** + * Command used to perform a map reduce operation with a search query as input. + * @author Dave Rusek + * @since 2.0 + */ +public class SearchMapReduce extends MapReduce +{ + protected SearchMapReduce(SearchInput input, Builder builder) + { + super(input, builder); + } + + /** + * Builder for a SearchMapReduce command. + */ + public static class Builder extends MapReduce.Builder + { + + private String index; + private String query; + + @Override + protected Builder self() + { + return this; + } + + public Builder withIndex(String index) + { + this.index = index; + return this; + } + + public Builder withQuery(String query) + { + this.query = query; + return this; + } + + public SearchMapReduce build() + { + if (index == null) + { + throw new IllegalStateException("An index must be specified"); + } + + if (query == null) + { + throw new IllegalStateException("A query must be specified"); + } + + return new SearchMapReduce(new SearchInput(index, query), this); + } + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/BetweenFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/BetweenFilter.java new file mode 100644 index 000000000..9b7c97011 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/BetweenFilter.java @@ -0,0 +1,48 @@ +/* + * 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.commands.mapreduce.filters; + +/** + * Key filter that matches keys in a range delimited by from - + * to + * + * @author russell + * + */ +public class BetweenFilter extends KeyFilter +{ + + private static final String NAME = "between"; + + private final T from; + private final T to; + + public BetweenFilter(T from, T to) + { + super(NAME); + this.from = from; + this.to = to; + } + + public T getFrom() + { + return from; + } + + public T getTo() + { + return to; + } + +} diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/EndsWithFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/EndsWithFilter.java similarity index 51% rename from src/main/java/com/basho/riak/client/mapreduce/filter/EndsWithFilter.java rename to src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/EndsWithFilter.java index d64ac9f74..d0a9d22bd 100644 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/EndsWithFilter.java +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/EndsWithFilter.java @@ -11,20 +11,31 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.basho.riak.client.mapreduce.filter; +package com.basho.riak.client.api.commands.mapreduce.filters; -import org.json.JSONArray; -public class EndsWithFilter implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.FILTER; - private JSONArray args = new JSONArray(); - - public EndsWithFilter(String endsWith) { - args.put("ends_with"); - args.put(endsWith); - } +/** + * A filter that matches keys whose name ends with the configured String argument + * @author russell + * + */ +public class EndsWithFilter extends KeyFilter +{ + + private static final String NAME = "ends_with"; + private final String pattern; - public JSONArray toJson() { - return args; + /** + * Filter in keys that end with endsWith + * @param pattern + */ + public EndsWithFilter(String pattern) { + super(NAME); + this.pattern = pattern; } + + public String getPattern() + { + return pattern; + } } diff --git a/src/main/java/com/basho/riak/client/response/WithBodyResponse.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/EqualToFilter.java similarity index 57% rename from src/main/java/com/basho/riak/client/response/WithBodyResponse.java rename to src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/EqualToFilter.java index fc20e1f4f..0f9e972d5 100644 --- a/src/main/java/com/basho/riak/client/response/WithBodyResponse.java +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/EqualToFilter.java @@ -11,26 +11,31 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.basho.riak.client.response; +package com.basho.riak.client.api.commands.mapreduce.filters; -import java.util.Collection; - -import com.basho.riak.client.RiakObject; /** - * A unified interface for responses that may return one, or more RiakObjects - * - * @see {@link FetchResponse}, {@link StoreResponse} + * Filter in keys that equal the configured value * @author russell - * + * */ -public interface WithBodyResponse extends HttpResponse { - - public boolean hasObject(); +public class EqualToFilter extends KeyFilter +{ - public RiakObject getObject(); + private final static String NAME = "eq"; + private final T value; - public boolean hasSiblings(); + /** + * Filter in keys whose name is equalTo + * @param value + */ + public EqualToFilter(T value) { + super(NAME); + this.value = value; + } - public Collection getSiblings(); + public T getValue() + { + return value; + } } diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/ToLowerFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/FloatToStringFilter.java similarity index 63% rename from src/main/java/com/basho/riak/client/mapreduce/filter/ToLowerFilter.java rename to src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/FloatToStringFilter.java index eef0e0f4c..18d4da232 100644 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/ToLowerFilter.java +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/FloatToStringFilter.java @@ -11,16 +11,20 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.basho.riak.client.mapreduce.filter; +package com.basho.riak.client.api.commands.mapreduce.filters; -import org.json.JSONArray; +/** + * Transforms the input key from a float to a string. + * + * @author russell + */ +public class FloatToStringFilter extends KeyFilter +{ + + private static final String NAME = "float_to_string"; -public class ToLowerFilter implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.TRANSFORM; - - public JSONArray toJson() { - JSONArray filter = new JSONArray(); - filter.put("to_lower"); - return filter; - } + public FloatToStringFilter() + { + super(NAME); + } } diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/GreaterThanFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/GreaterThanFilter.java new file mode 100644 index 000000000..20a427e17 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/GreaterThanFilter.java @@ -0,0 +1,42 @@ +/* + * 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.commands.mapreduce.filters; + +/** + * Filters in keys that are greater than the configured value. + * + * @author russell + */ +public class GreaterThanFilter extends KeyFilter +{ + + private static final String NAME = "greater_than"; + private final T value; + + /** + * @param value + */ + public GreaterThanFilter(T value) + { + super(NAME); + this.value = value; + + + } + + public T getValue() + { + return value; + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/GreaterThanOrEqualFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/GreaterThanOrEqualFilter.java new file mode 100644 index 000000000..010f5bb53 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/GreaterThanOrEqualFilter.java @@ -0,0 +1,39 @@ +/* + * 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.commands.mapreduce.filters; + +/** + * Filter in keys that are >= to the configured value + * + * @author russell + */ +public class GreaterThanOrEqualFilter extends KeyFilter +{ + private static final String NAME = "greater_than_eq"; + private final T value; + + /** + * @param value + */ + public GreaterThanOrEqualFilter(T value) + { + super(NAME); + this.value = value; + } + + public T getValue() + { + return value; + } +} diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/MapReduceFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/IntToStringFilter.java similarity index 66% rename from src/main/java/com/basho/riak/client/mapreduce/filter/MapReduceFilter.java rename to src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/IntToStringFilter.java index b2329a687..462bea58a 100644 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/MapReduceFilter.java +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/IntToStringFilter.java @@ -11,21 +11,21 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.basho.riak.client.mapreduce.filter; +package com.basho.riak.client.api.commands.mapreduce.filters; -import org.json.JSONArray; -/* - * Interface for filter functions used for - * key filtering +/** + * Transforms a key from an int to a string. + * + * @author russell */ -public interface MapReduceFilter { +public class IntToStringFilter extends KeyFilter +{ + + private static final String NAME = "int_to_string"; - public static enum Types { - LOGICAL, - TRANSFORM, - FILTER - } - - public JSONArray toJson(); + public IntToStringFilter() + { + super(NAME); + } } \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/KeyFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/KeyFilter.java new file mode 100644 index 000000000..8595880a2 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/KeyFilter.java @@ -0,0 +1,38 @@ +/* + * 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.commands.mapreduce.filters; + + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * Top level interface for Key Filters. + * + * @author russell + */ +@JsonFormat(shape = JsonFormat.Shape.ARRAY) +public abstract class KeyFilter +{ + private final String name; + + protected KeyFilter(String name) + { + this.name = name; + } + + public String getName() + { + return name; + } +} diff --git a/src/main/java/com/basho/riak/pbc/mapreduce/MapReduceFunction.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/KeyTransformFilter.java similarity index 68% rename from src/main/java/com/basho/riak/pbc/mapreduce/MapReduceFunction.java rename to src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/KeyTransformFilter.java index aa933973b..e78c66f46 100644 --- a/src/main/java/com/basho/riak/pbc/mapreduce/MapReduceFunction.java +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/KeyTransformFilter.java @@ -11,20 +11,13 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.basho.riak.pbc.mapreduce; - -import org.json.JSONObject; +package com.basho.riak.client.api.commands.mapreduce.filters; /** - * Interface for describing functions used in - * map/reduce jobs + * Marker interface for the type of filter that, uh, transforms a key from one thing to, uh, another + * @author russell + * */ -public interface MapReduceFunction { - - public static enum Types { - ANONYMOUS, - NAMED - } - - public JSONObject toJson(); +public interface KeyTransformFilter { + } diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LessThanFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LessThanFilter.java new file mode 100644 index 000000000..3777f6649 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LessThanFilter.java @@ -0,0 +1,42 @@ +/* + * 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.commands.mapreduce.filters; + + +/** + * Filter in keys that are less than the configured value + * + * @author russell + */ +public class LessThanFilter extends KeyFilter +{ + + private static final String NAME = "less_than"; + + private final T value; + + /** + * @param value + */ + public LessThanFilter(T value) + { + super(NAME); + this.value = value; + } + + public T getValue() + { + return value; + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LessThanOrEqualFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LessThanOrEqualFilter.java new file mode 100644 index 000000000..25e8660a8 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LessThanOrEqualFilter.java @@ -0,0 +1,40 @@ +/* + * 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.commands.mapreduce.filters; + + +/** + * Filter in keys that =< than the configured value. + * + * @author russell + */ +public class LessThanOrEqualFilter extends KeyFilter +{ + private static final String NAME = "less_than_eq"; + private final T value; + + /** + * @param value + */ + public LessThanOrEqualFilter(T value) + { + super(NAME); + this.value = value; + } + + public T getValue() + { + return value; + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LogicalAndFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LogicalAndFilter.java new file mode 100644 index 000000000..b26fdbb1c --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LogicalAndFilter.java @@ -0,0 +1,35 @@ +/* + * 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.commands.mapreduce.filters; + + +/** + * Group a bunch of filters together and filter in keys that match all + * + * @author russell + */ +public class LogicalAndFilter extends LogicalFilter +{ + + private static final String NAME = "and"; + + /** + * @param filters + */ + public LogicalAndFilter(KeyFilter... filters) + { + super(NAME, filters); + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LogicalFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LogicalFilter.java new file mode 100644 index 000000000..5b87f4e25 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LogicalFilter.java @@ -0,0 +1,42 @@ +/* + * 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.commands.mapreduce.filters; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import java.util.Arrays; +import java.util.LinkedList; + +/** + * Shared code for the set of "logical" filters. + * + * @author russell + */ +@JsonSerialize(using = LogicalFilterSerializer.class) +public abstract class LogicalFilter extends KeyFilter +{ + private final LinkedList filters = new LinkedList(); + + protected LogicalFilter(String name, KeyFilter... filters) + { + super(name); + this.filters.addAll(Arrays.asList(filters)); + } + + public LinkedList getFilters() + { + return filters; + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LogicalFilterSerializer.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LogicalFilterSerializer.java new file mode 100644 index 000000000..9edacc4f0 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LogicalFilterSerializer.java @@ -0,0 +1,24 @@ +package com.basho.riak.client.api.commands.mapreduce.filters; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +class LogicalFilterSerializer extends JsonSerializer +{ + @Override + public void serialize(LogicalFilter value, JsonGenerator jgen, SerializerProvider provider) throws IOException + { + jgen.writeStartArray(); + jgen.writeString(value.getName()); + for (KeyFilter filter : value.getFilters()) + { + jgen.writeStartArray(); + jgen.writeObject(filter); + jgen.writeEndArray(); + } + jgen.writeEndArray(); + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LogicalNotFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LogicalNotFilter.java new file mode 100644 index 000000000..be5960b16 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LogicalNotFilter.java @@ -0,0 +1,34 @@ +/* + * 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.commands.mapreduce.filters; + + +/** + * Filter in keys that aren't caught by the group + * + * @author russell + */ +public class LogicalNotFilter extends LogicalFilter +{ + private static final String NAME = "not"; + + /** + * @param filters + */ + public LogicalNotFilter(KeyFilter... filters) + { + super(NAME, filters); + } + +} diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/IntToStringFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LogicalOrFilter.java similarity index 62% rename from src/main/java/com/basho/riak/client/mapreduce/filter/IntToStringFilter.java rename to src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LogicalOrFilter.java index ecc01d6f1..303b9566e 100644 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/IntToStringFilter.java +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/LogicalOrFilter.java @@ -11,16 +11,24 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.basho.riak.client.mapreduce.filter; +package com.basho.riak.client.api.commands.mapreduce.filters; -import org.json.JSONArray; +/** + * Filter in keys that match any of the filters specified + * + * @author russell + */ +public class LogicalOrFilter extends LogicalFilter +{ + + private static final String NAME = "or"; + + /** + * @param filters + */ + public LogicalOrFilter(KeyFilter... filters) + { + super(NAME, filters); + } -public class IntToStringFilter implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.TRANSFORM; - - public JSONArray toJson() { - JSONArray filter = new JSONArray(); - filter.put("int_to_string"); - return filter; - } } diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/MatchFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/MatchFilter.java new file mode 100644 index 000000000..fa1dad376 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/MatchFilter.java @@ -0,0 +1,41 @@ +/* + * 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.commands.mapreduce.filters; + + +/** + * Filter in keys that match the regular expression argument + * + * @author russell + */ +public class MatchFilter extends KeyFilter +{ + + private static final String NAME = "matches"; + private final String value; + + /** + * @param value a Reg Exp + */ + public MatchFilter(String value) + { + super(NAME); + this.value = value; + } + + public String getValue() + { + return value; + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/NotEqualToFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/NotEqualToFilter.java new file mode 100644 index 000000000..48723fb7c --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/NotEqualToFilter.java @@ -0,0 +1,40 @@ +/* + * 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.commands.mapreduce.filters; + + +/** + * Filetr in keys that don't equal the configured argument + * + * @author russell + */ +public class NotEqualToFilter extends KeyFilter +{ + private final static String NAME = "neq"; + private final T value; + + /** + * @param value + */ + public NotEqualToFilter(T value) + { + super(NAME); + this.value = value; + } + + public T getValue() + { + return value; + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/SetMemberFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/SetMemberFilter.java new file mode 100644 index 000000000..d606ebfa8 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/SetMemberFilter.java @@ -0,0 +1,58 @@ +/* + * 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.commands.mapreduce.filters; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * Filter in keys that are a member of the provided set + * + * @author russell + */ +@JsonSerialize(using = SetMemberSerializer.class) +public class SetMemberFilter extends KeyFilter +{ + + private static final String NAME = "set_member"; + private final Set set = new HashSet(); + + /** + * Creates a set from a String var arg + * + * @param set + */ + public SetMemberFilter(T... set) + { + super(NAME); + this.set.addAll(Arrays.asList(set)); + } + + /** + * @param set + */ + public SetMemberFilter(Set set) + { + super(NAME); + this.set.addAll(set); + } + + public Set getSet() + { + return set; + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/SetMemberSerializer.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/SetMemberSerializer.java new file mode 100644 index 000000000..83f2d32dd --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/SetMemberSerializer.java @@ -0,0 +1,22 @@ +package com.basho.riak.client.api.commands.mapreduce.filters; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +class SetMemberSerializer extends JsonSerializer> +{ + @Override + public void serialize(SetMemberFilter value, JsonGenerator jgen, SerializerProvider provider) throws IOException + { + jgen.writeStartArray(); + jgen.writeString(value.getName()); + for (Object member : value.getSet()) + { + jgen.writeObject(member); + } + jgen.writeEndArray(); + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/SimilarToFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/SimilarToFilter.java new file mode 100644 index 000000000..751bc5650 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/SimilarToFilter.java @@ -0,0 +1,48 @@ +/* + * 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.commands.mapreduce.filters; + + +/** + * Filter in keys that are within a specified Levenshtein edit distance of the provided string + * + * @author russell + */ +public class SimilarToFilter extends KeyFilter +{ + private static final String NAME = "similar_to"; + private final String value; + private final int distance; + + /** + * @param value the string to compare to + * @param maxEditDistance + */ + public SimilarToFilter(String value, int maxEditDistance) + { + super(NAME); + this.value = value; + this.distance = maxEditDistance; + } + + public String getValue() + { + return value; + } + + public int getDistance() + { + return distance; + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/StartsWithFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/StartsWithFilter.java new file mode 100644 index 000000000..ecfe85958 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/StartsWithFilter.java @@ -0,0 +1,41 @@ +/* + * 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.commands.mapreduce.filters; + + +/** + * Filter in keys that start with the provided argument + * + * @author russell + */ +public class StartsWithFilter extends KeyFilter +{ + + private static final String NAME = "starts_with"; + private final String value; + + /** + * @param value + */ + public StartsWithFilter(String value) + { + super(NAME); + this.value = value; + } + + public String getValue() + { + return value; + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/StringToFloatFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/StringToFloatFilter.java new file mode 100644 index 000000000..e8eae2835 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/StringToFloatFilter.java @@ -0,0 +1,31 @@ +/* + * 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.commands.mapreduce.filters; + + +/** + * Transform the key from a string to a float + * + * @author russell + */ +public class StringToFloatFilter extends KeyFilter +{ + + private static final String NAME = "string_to_float"; + + public StringToFloatFilter() + { + super(NAME); + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/StringToIntFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/StringToIntFilter.java new file mode 100644 index 000000000..c3ce41582 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/StringToIntFilter.java @@ -0,0 +1,31 @@ +/* + * 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.commands.mapreduce.filters; + + +/** + * Transform the key from a String to an int + * + * @author russell + */ +public class StringToIntFilter extends KeyFilter +{ + + private static final String NAME = "string_to_int"; + + public StringToIntFilter() + { + super(NAME); + } +} diff --git a/src/main/java/com/basho/riak/client/mapreduce/MapReduceFunction.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/ToLowerFilter.java similarity index 68% rename from src/main/java/com/basho/riak/client/mapreduce/MapReduceFunction.java rename to src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/ToLowerFilter.java index a1b7954bf..91ecf81ee 100644 --- a/src/main/java/com/basho/riak/client/mapreduce/MapReduceFunction.java +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/ToLowerFilter.java @@ -11,20 +11,21 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.basho.riak.client.mapreduce; +package com.basho.riak.client.api.commands.mapreduce.filters; -import org.json.JSONObject; /** - * Interface for describing functions used in - * map/reduce jobs + * Transform filter that lower cases the key. + * + * @author russell */ -public interface MapReduceFunction { - - public static enum Types { - ANONYMOUS, - NAMED - } - - public JSONObject toJson(); +public class ToLowerFilter extends KeyFilter +{ + + private static final String NAME = "to_lower"; + + public ToLowerFilter() + { + super(NAME); + } } diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/ToUpperFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/ToUpperFilter.java similarity index 63% rename from src/main/java/com/basho/riak/client/mapreduce/filter/ToUpperFilter.java rename to src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/ToUpperFilter.java index df3ad224e..e1395a3f0 100644 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/ToUpperFilter.java +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/ToUpperFilter.java @@ -11,16 +11,21 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.basho.riak.client.mapreduce.filter; +package com.basho.riak.client.api.commands.mapreduce.filters; -import org.json.JSONArray; -public class ToUpperFilter implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.TRANSFORM; - - public JSONArray toJson() { - JSONArray filter = new JSONArray(); - filter.put("to_upper"); - return filter; - } +/** + * Transform filter that uppercases the key + * + * @author russell + */ +public class ToUpperFilter extends KeyFilter +{ + + private static final String NAME = "to_upper"; + + public ToUpperFilter() + { + super(NAME); + } } diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/TokenizeFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/TokenizeFilter.java new file mode 100644 index 000000000..57f073a94 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/TokenizeFilter.java @@ -0,0 +1,50 @@ +/* + * 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.commands.mapreduce.filters; + + +/** + * Filter in keys that match the tokenNum token of the key when tokenized by separator + * + * @author russell + */ +public class TokenizeFilter extends KeyFilter implements KeyTransformFilter +{ + + private static final String NAME = "tokenize"; + private final String separator; + private final int tokens; + + /** + * @param separator + * @param tokens + */ + public TokenizeFilter(String separator, int tokens) + { + super(NAME); + this.separator = separator; + this.tokens = tokens; + } + + public String getSeparator() + { + return separator; + } + + public int getTokens() + { + return tokens; + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/UrlDecodeFilter.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/UrlDecodeFilter.java new file mode 100644 index 000000000..b3245b22a --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/UrlDecodeFilter.java @@ -0,0 +1,30 @@ +/* + * 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.commands.mapreduce.filters; + +/** + * Transform filter that urldecodes the key + * + * @author russell + */ +public class UrlDecodeFilter extends KeyFilter implements KeyTransformFilter +{ + + private static final String NAME = "urldecode"; + + public UrlDecodeFilter() + { + super(NAME); + } +} diff --git a/src/main/java/com/basho/riak/client/plain/RiakIOException.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/package-info.java similarity index 60% rename from src/main/java/com/basho/riak/client/plain/RiakIOException.java rename to src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/package-info.java index abdf1e5ed..956ae5360 100644 --- a/src/main/java/com/basho/riak/client/plain/RiakIOException.java +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/filters/package-info.java @@ -11,17 +11,15 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.basho.riak.client.plain; - -import com.basho.riak.client.response.RiakIORuntimeException; - /** - * A checked wrapper for {@link RiakIORuntimeException}. - */ -public class RiakIOException extends Exception { - private static final long serialVersionUID = 2179229841757644538L; + * The set of key filters that can be used in a {@link com.basho.riak.client.api.commands.mapreduce.MapReduce} + * - public RiakIOException(RiakIORuntimeException e) { - super(e.getMessage(), e.getCause()); - } -} + *

+ * Please see the basho wiki on + * Map/Reduce and + * key Filters for more details. + *

+ * + */ +package com.basho.riak.client.api.commands.mapreduce.filters; \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/api/commands/mapreduce/package-info.java b/src/main/java/com/basho/riak/client/api/commands/mapreduce/package-info.java new file mode 100644 index 000000000..9fa38cd65 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/mapreduce/package-info.java @@ -0,0 +1,11 @@ +/** + * Commands for Map-Reduce in Riak. + *

Map-Reduce

+ *
    + *
  • {@link com.basho.riak.client.api.commands.mapreduce.BucketMapReduce}
  • + *
  • {@link com.basho.riak.client.api.commands.mapreduce.BucketKeyMapReduce}
  • + *
  • {@link com.basho.riak.client.api.commands.mapreduce.IndexMapReduce}
  • + *
  • {@link com.basho.riak.client.api.commands.mapreduce.SearchMapReduce}
  • + *
+ */ +package com.basho.riak.client.api.commands.mapreduce; \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/api/commands/package-info.java b/src/main/java/com/basho/riak/client/api/commands/package-info.java new file mode 100644 index 000000000..7282fc944 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/package-info.java @@ -0,0 +1,4 @@ +/** + * Support classes for commands. + */ +package com.basho.riak.client.api.commands; \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/api/commands/search/DeleteIndex.java b/src/main/java/com/basho/riak/client/api/commands/search/DeleteIndex.java new file mode 100644 index 000000000..81e7c0565 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/search/DeleteIndex.java @@ -0,0 +1,73 @@ +package com.basho.riak.client.api.commands.search; + +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.YzDeleteIndexOperation; + + +/** + * Command used to delete a search index in Riak. + * @author Dave Rusek + * @since 2.0 + */ +public final class DeleteIndex extends RiakCommand +{ + private final String index; + + DeleteIndex(Builder builder) + { + this.index = builder.index; + } + + @Override + protected final RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + cluster.execute(buildCoreOperation()); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) + { + + @Override + protected Void convertResponse(Void coreResponse) + { + return coreResponse; + } + + @Override + protected String convertQueryInfo(String coreQueryInfo) + { + return coreQueryInfo; + } + }; + coreFuture.addListener(future); + return future; + } + + private YzDeleteIndexOperation buildCoreOperation() + { + return new YzDeleteIndexOperation.Builder(index).build(); + } + + /** + * Builder for a DeleteIndex command. + */ + public static class Builder + { + + private final String index; + + public Builder(String index) + { + this.index = index; + } + + public DeleteIndex build() + { + return new DeleteIndex(this); + } + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/search/FetchIndex.java b/src/main/java/com/basho/riak/client/api/commands/search/FetchIndex.java new file mode 100644 index 000000000..db15130a8 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/search/FetchIndex.java @@ -0,0 +1,87 @@ +/* + * 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.search; + +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.YzFetchIndexOperation; + + +/** + * Command used to fetch a search index from Riak. + * @author Dave Rusek + * @since 2.0 + */ +public class FetchIndex extends RiakCommand +{ + private final String index; + + FetchIndex(Builder builder) + { + this.index = builder.index; + } + + @Override + protected final RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + cluster.execute(buildCoreOperation()); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) + { + @Override + protected YzFetchIndexOperation.Response convertResponse(YzFetchIndexOperation.Response coreResponse) + { + return coreResponse; + } + + @Override + protected String convertQueryInfo(String coreQueryInfo) + { + return coreQueryInfo; + } + }; + coreFuture.addListener(future); + return future; + } + + private YzFetchIndexOperation buildCoreOperation() + { + return new YzFetchIndexOperation.Builder().withIndexName(index).build(); + } + + /** + * Builder for a FetchIndex command. + */ + public static class Builder + { + private final String index; + + public Builder(String index) + { + this.index = index; + } + + public FetchIndex build() + { + return new FetchIndex(this); + } + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/search/FetchSchema.java b/src/main/java/com/basho/riak/client/api/commands/search/FetchSchema.java new file mode 100644 index 000000000..b9d090efc --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/search/FetchSchema.java @@ -0,0 +1,87 @@ +/* + * 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.search; + +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.YzGetSchemaOperation; + + +/** + * Command used to fetch a search schema from Riak. + * @author Dave Rusek + * @since 2.0 + */ +public final class FetchSchema extends RiakCommand +{ + private final String schema; + + FetchSchema(Builder builder) + { + this.schema = builder.schema; + } + + @Override + protected RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + cluster.execute(buildCoreOperation()); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) + { + @Override + protected YzGetSchemaOperation.Response convertResponse(YzGetSchemaOperation.Response coreResponse) + { + return coreResponse; + } + + @Override + protected String convertQueryInfo(String coreQueryInfo) + { + return coreQueryInfo; + } + }; + coreFuture.addListener(future); + return future; + } + + private YzGetSchemaOperation buildCoreOperation() + { + return new YzGetSchemaOperation.Builder(schema).build(); + } + + /** + * Builder for a FetchSchema command. + */ + public static class Builder + { + private final String schema; + + public Builder(String schema) + { + this.schema = schema; + } + + public FetchSchema build() + { + return new FetchSchema(this); + } + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/search/Search.java b/src/main/java/com/basho/riak/client/api/commands/search/Search.java new file mode 100644 index 000000000..89547763e --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/search/Search.java @@ -0,0 +1,258 @@ +/* + * 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.search; + +import com.basho.riak.client.api.RiakCommand; +import com.basho.riak.client.api.commands.CoreFutureAdapter; +import com.basho.riak.client.api.commands.RiakOption; +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.SearchOperation; +import com.basho.riak.client.core.util.BinaryValue; + +import java.util.*; + +/** + * Command used to perform a serach in Riak. + * @author Dave Rusek + * @since 2.0 + */ +public final class Search extends RiakCommand +{ + + public static enum Presort + { + KEY("key"), SCORE("score"); + + final String sortStr; + + Presort(String sortStr) + { + this.sortStr = sortStr; + } + } + + private final String index; + private final String query; + private final int start; + private final int rows; + private final Presort presort; + private final String filterQuery; + private final String sortField; + private final List returnFields; + private final Map, Object> options = + new HashMap, Object>(); + + public Search(Builder builder) + { + this.index = builder.index; + this.query = builder.query; + this.start = builder.start; + this.rows = builder.rows; + this.presort = builder.presort; + this.filterQuery = builder.filterQuery; + this.sortField = builder.sortField; + this.returnFields = builder.returnFields; + this.options.putAll(builder.options); + } + + + @Override + protected RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + cluster.execute(buildCoreOperation()); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) + { + @Override + protected SearchOperation.Response convertResponse(SearchOperation.Response coreResponse) + { + return coreResponse; + } + + @Override + protected BinaryValue convertQueryInfo(BinaryValue coreQueryInfo) + { + return coreQueryInfo; + } + }; + coreFuture.addListener(future); + return future; + } + + private SearchOperation buildCoreOperation() + { + SearchOperation.Builder builder = new SearchOperation.Builder(BinaryValue.create(index), query); + + for (Map.Entry, Object> option : options.entrySet()) + { + + if (option.getKey() == Option.DEFAULT_FIELD) + { + builder.withDefaultField((String) option.getValue()); + } + else if (option.getKey() == Option.DEFAULT_OPERATION) + { + Option.Operation op = (Option.Operation) option.getValue(); + builder.withDefaultOperation(op.opStr); + } + + } + + if (start >= 0) + { + builder.withStart(start); + } + + if (rows > 0) + { + builder.withNumRows(rows); + } + + if (presort != null) + { + builder.withPresort(presort.sortStr); + } + + if (filterQuery != null) + { + builder.withFilterQuery(filterQuery); + } + + if (sortField != null) + { + builder.withSortField(sortField); + } + + if (returnFields != null) + { + builder.withReturnFields(returnFields); + } + + return builder.build(); + } + + /* + * @author Dave Rusek + * @since 2.0 + */ + public static final class Option extends RiakOption + { + + public static enum Operation + { + AND("and"), OR("or"); + + final String opStr; + + Operation(String opStr) + { + this.opStr = opStr; + } + } + + public static Option DEFAULT_OPERATION = new Option("DEFAULT_OPERATION"); + public static Option DEFAULT_FIELD = new Option("DEFAULT_FIELD"); + + private Option(String name) + { + super(name); + } + } + + /** + * Builder for a Search command. + */ + public static class Builder + { + private final String index; + private final String query; + private int start; + private int rows; + private Presort presort; + private String filterQuery; + private String sortField; + private List returnFields; + private Map, Object> options = new HashMap, Object>(); + + public Builder(String index, String query) + { + this.index = index; + this.query = query; + } + + public Builder withPresort(Presort presort) + { + this.presort = presort; + return this; + } + + public Builder withStart(int start) + { + this.start = start; + return this; + } + + public Builder withRows(int rows) + { + this.rows = rows; + return this; + } + + public Builder withOption(Option option, T value) + { + options.put(option, value); + return this; + } + + public Builder filter(String query) + { + this.filterQuery = query; + return this; + } + + public Builder sort(String field) + { + this.sortField = field; + return this; + } + + public Builder returnFields(Iterable fields) + { + this.returnFields = new ArrayList(); + for (String field : fields) + { + returnFields.add(field); + } + return this; + } + + public Builder returnFields(String... fields) + { + this.returnFields(Arrays.asList(fields)); + return this; + } + + public Search build() + { + return new Search(this); + } + + } + +} diff --git a/src/main/java/com/basho/riak/client/api/commands/search/StoreIndex.java b/src/main/java/com/basho/riak/client/api/commands/search/StoreIndex.java new file mode 100644 index 000000000..1e581e048 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/search/StoreIndex.java @@ -0,0 +1,72 @@ +package com.basho.riak.client.api.commands.search; + +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.YzPutIndexOperation; +import com.basho.riak.client.core.query.search.YokozunaIndex; + + +/** + * Command used to store a search index in Riak. + * @author Dave Rusek + * @since 2.0 + */ +public final class StoreIndex extends RiakCommand +{ + private final YokozunaIndex index; + + StoreIndex(Builder builder) + { + this.index = builder.index; + } + + @Override + protected RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + cluster.execute(buildCoreOperation()); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) + { + @Override + protected Void convertResponse(Void coreResponse) + { + return coreResponse; + } + + @Override + protected YokozunaIndex convertQueryInfo(YokozunaIndex coreQueryInfo) + { + return coreQueryInfo; + } + }; + coreFuture.addListener(future); + return future; + } + + private final YzPutIndexOperation buildCoreOperation() + { + return new YzPutIndexOperation.Builder(index).build(); + } + + /** + * Builder for a StoreIndex command. + */ + public static class Builder + { + private final YokozunaIndex index; + + public Builder(YokozunaIndex index) + { + this.index = index; + } + + public StoreIndex build() + { + return new StoreIndex(this); + } + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/search/StoreSchema.java b/src/main/java/com/basho/riak/client/api/commands/search/StoreSchema.java new file mode 100644 index 000000000..72c650e73 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/search/StoreSchema.java @@ -0,0 +1,72 @@ +package com.basho.riak.client.api.commands.search; + +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.YzPutSchemaOperation; +import com.basho.riak.client.core.query.search.YokozunaSchema; + + +/** + * Command used to store a search schema in Riak. + * @author Dave Rusek + * @since 2.0 + */ +public final class StoreSchema extends RiakCommand +{ + private final YokozunaSchema schema; + + StoreSchema(Builder builder) + { + this.schema = builder.schema; + } + + @Override + protected RiakFuture executeAsync(RiakCluster cluster) + { + RiakFuture coreFuture = + cluster.execute(buildCoreOperation()); + + CoreFutureAdapter future = + new CoreFutureAdapter(coreFuture) + { + @Override + protected Void convertResponse(Void coreResponse) + { + return coreResponse; + } + + @Override + protected YokozunaSchema convertQueryInfo(YokozunaSchema coreQueryInfo) + { + return coreQueryInfo; + } + }; + coreFuture.addListener(future); + return future; + } + + private YzPutSchemaOperation buildCoreOperation() + { + return new YzPutSchemaOperation.Builder(schema).build(); + } + + /** + * Builder for a StoreSchema command. + */ + public static class Builder + { + private final YokozunaSchema schema; + + public Builder(YokozunaSchema schema) + { + this.schema = schema; + } + + public StoreSchema build() + { + return new StoreSchema(this); + } + } +} diff --git a/src/main/java/com/basho/riak/client/api/commands/search/package-info.java b/src/main/java/com/basho/riak/client/api/commands/search/package-info.java new file mode 100644 index 000000000..c4cc13851 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/commands/search/package-info.java @@ -0,0 +1,13 @@ +/** + * Commands used for Riak search. + *

Search commands

+ *
    + *
  • {@link com.basho.riak.client.api.commands.search.Search}
  • + *
  • {@link com.basho.riak.client.api.commands.search.FetchIndex}
  • + *
  • {@link com.basho.riak.client.api.commands.search.StoreIndex}
  • + *
  • {@link com.basho.riak.client.api.commands.search.DeleteIndex}
  • + *
  • {@link com.basho.riak.client.api.commands.search.FetchSchema}
  • + *
  • {@link com.basho.riak.client.api.commands.search.StoreSchema}
  • +*
+ */ +package com.basho.riak.client.api.commands.search; \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/api/convert/ConversionException.java b/src/main/java/com/basho/riak/client/api/convert/ConversionException.java new file mode 100644 index 000000000..06f5da1d6 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/ConversionException.java @@ -0,0 +1,45 @@ +/* + * 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.convert; + +import com.basho.riak.client.core.query.RiakObject; + +/** + * Wraps any exception thrown while converting to/from {@link RiakObject} and your domain types. + * @author russell + * + */ +public class ConversionException extends RuntimeException { + + /** + * eclipse generated id + */ + private static final long serialVersionUID = 2116948528090219193L; + + public ConversionException() { + super(); + } + + public ConversionException(String message) { + super(message); + } + + public ConversionException(Throwable cause) { + super(cause); + } + + public ConversionException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/api/convert/Converter.java b/src/main/java/com/basho/riak/client/api/convert/Converter.java new file mode 100644 index 000000000..0cc571987 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/Converter.java @@ -0,0 +1,246 @@ +/* + * 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.convert; + +import com.basho.riak.client.api.convert.reflection.AnnotationUtil; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; +import java.lang.reflect.Constructor; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +/** + * The Converter acts as a bridge between the core and the user level API, specifically + * for ORM. + *

+ * Subclasses will override the {@link #fromDomain(java.lang.Object) } and + * {@link #fromDomain(java.lang.Object) } methods to convert the value portion + * of a {@code RiakObject} to a domain object. + *

+ * + * @author Brian Roach + * @param the type to convert to + * @since 2.0 + */ +public abstract class Converter +{ + protected final Type type; + + public Converter(Type type) + { + this.type = type; + } + + @SuppressWarnings("unchecked") + protected final T newDomainInstance() + { + try + { + Class rawType = type instanceof Class + ? (Class) type + : (Class) ((ParameterizedType) type).getRawType(); + Constructor constructor = rawType.getConstructor(); + return (T) constructor.newInstance(); + } + catch (Exception ex) + { + throw new ConversionException(ex); + } + } + + /** + * Converts from a RiakObject to a domain object. + * + * @param obj the RiakObject to be converted + * @param location The location of this RiakObject in Riak + * @return an instance of the domain type T + */ + public T toDomain(RiakObject obj, Location location) + { + T domainObject; + if (obj.isDeleted()) + { + domainObject = newDomainInstance(); + } + else + { + domainObject = toDomain(obj.getValue(), obj.getContentType()); + + AnnotationUtil.populateIndexes(obj.getIndexes(), domainObject); + AnnotationUtil.populateLinks(obj.getLinks(), domainObject); + AnnotationUtil.populateUsermeta(obj.getUserMeta(), domainObject); + AnnotationUtil.setContentType(domainObject, obj.getContentType()); + AnnotationUtil.setVTag(domainObject, obj.getVTag()); + } + + AnnotationUtil.setKey(domainObject, location.getKey()); + AnnotationUtil.setBucketName(domainObject, location.getNamespace().getBucketName()); + AnnotationUtil.setBucketType(domainObject, location.getNamespace().getBucketType()); + + AnnotationUtil.setVClock(domainObject, obj.getVClock()); + AnnotationUtil.setTombstone(domainObject, obj.isDeleted()); + AnnotationUtil.setLastModified(domainObject, obj.getLastModified()); + + return domainObject; + + } + + /** + * Convert the value portion of a RiakObject to a domain object. + *

+ * Implementations override this method to convert the value contained in + * a {@code RiakObject} to an instance of a domain object. + *

+ * @param value the value portion of a RiakObject to convert to a domain object + * @param contentType The content type of the RiakObject + * @return a new instance of the domain object + */ + public abstract T toDomain(BinaryValue value, String contentType) throws ConversionException; + + /** + * Convert from a domain object to a RiakObject. + *

+ * The domain object itself may be completely annotated with everything + * required to produce a RiakObject except for the value portion. + * This will prefer annotated + * items over the {@code Location} passed in. + *

+ * @param domainObject a domain object to be stored in Riak. + * @param namespace the namespace in Riak + * @param key the key for the object + * @return data to be stored in Riak. + */ + public OrmExtracted fromDomain(T domainObject, Namespace namespace, BinaryValue key) + { + BinaryValue bucketName = namespace != null ? namespace.getBucketName() : null; + BinaryValue bucketType = namespace != null ? namespace.getBucketType() : null; + + key = AnnotationUtil.getKey(domainObject, key); + bucketName = AnnotationUtil.getBucketName(domainObject, bucketName); + bucketType = AnnotationUtil.getBucketType(domainObject, bucketType); + + if (bucketName == null) + { + throw new ConversionException("Bucket name not provided via namespace or domain object"); + } + + VClock vclock = AnnotationUtil.getVClock(domainObject); + String contentType = + AnnotationUtil.getContentType(domainObject, RiakObject.DEFAULT_CONTENT_TYPE); + + RiakObject riakObject = new RiakObject(); + + AnnotationUtil.getUsermetaData(riakObject.getUserMeta(), domainObject); + AnnotationUtil.getIndexes(riakObject.getIndexes(), domainObject); + AnnotationUtil.getLinks(riakObject.getLinks(), domainObject); + + ContentAndType cAndT = fromDomain(domainObject); + contentType = cAndT.contentType != null ? cAndT.contentType : contentType; + + riakObject.setContentType(contentType) + .setValue(cAndT.content) + .setVClock(vclock); + + // We allow an annotated object to omit @BucketType and get the default + Namespace ns; + if (bucketType == null) + { + ns = new Namespace(bucketName); + } + else + { + ns = new Namespace(bucketType, bucketName); + } + + OrmExtracted extracted = new OrmExtracted(riakObject, ns, key); + return extracted; + } + + /** + * Provide the value portion of a RiakObject from the domain object. + *

+ * Implementations override this method to provide the value portion of the + * RiakObject to be stored from the supplied domain object. + *

+ * @param domainObject the domain object. + * @return A BinaryValue to be stored in Riak + */ + public abstract ContentAndType fromDomain(T domainObject) throws ConversionException; + + /** + * Encapsulation of ORM data extracted from a domain object. + *

+ * This allows for user-defined POJOs to encapsulate everything required to + * query Riak. + *

+ * + */ + public static class OrmExtracted + { + private final RiakObject riakObject; + private final Namespace namespace; + private final BinaryValue key; + + public OrmExtracted(RiakObject riakObject, Namespace namespace, BinaryValue key) + { + this.riakObject = riakObject; + this.namespace = namespace; + this.key = key; + } + + /** + * @return the riakObject + */ + public RiakObject getRiakObject() + { + return riakObject; + } + + /** + * @return the namespace + */ + public Namespace getNamespace() + { + return namespace; + } + + public boolean hasKey() + { + return key != null; + } + + public BinaryValue getKey() + { + return key; + } + + } + + protected class ContentAndType + { + private final BinaryValue content; + private final String contentType; + + public ContentAndType(BinaryValue content, String contentType) + { + this.content = content; + this.contentType = contentType; + } + } +} diff --git a/src/main/java/com/basho/riak/client/api/convert/ConverterFactory.java b/src/main/java/com/basho/riak/client/api/convert/ConverterFactory.java new file mode 100644 index 000000000..7a552b9e6 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/ConverterFactory.java @@ -0,0 +1,169 @@ +/* + * 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.convert; + +import com.basho.riak.client.core.query.RiakObject; +import com.fasterxml.jackson.core.type.TypeReference; +import java.lang.reflect.Type; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Holds instances of converters to be used for serialization / deserialization + * of objects stored and fetched from Riak. + *

+ * When storing and retrieving your own domain objects to/from Riak, they + * need to be serialized / deserialized. By default the {@link JSONConverter} + * is provided. This uses the Jackson JSON library to translate your object to/from + * JSON. In many cases you will never need to create or register your own + * converter with the ConverterFactory. + *

+ *

+ * In the case you do need custom conversion, you would extend {@link Converter} + * and then register it with the ConverterFactory for your classes. + *

+ * + * @author Brian Roach + * @since 2.0 + */ +public enum ConverterFactory +{ + INSTANCE; + + private final Map> converterInstances = + new ConcurrentHashMap>(){{ + put(new TypeReference(){}.getType(), new PassThroughConverter()); + put(RiakObject.class, new PassThroughConverter()); + put (new TypeReference(){}.getType(), new StringConverter()); + put(String.class, new StringConverter()); + }}; + + + + /** + * Get the instance of the ConverterFactory. + * @return the ConverterFactory + */ + public static ConverterFactory getInstance() + { + return INSTANCE; + } + + /** + * Returns a Converter instance for the supplied class. + *

+ * If no converter is registered, the default {@link JSONConverter} is returned. + *

+ * @param The type for the converter + * @param type The type used to look up the converter + * @return an instance of the converter for this class + */ + public Converter getConverter(Type type) + { + return getConverter(type, null); + } + + /** + * Returns a Converter instance for the supplied class. + *

+ * If no converter is registered, the default {@link JSONConverter} is returned. + *

+ * @param The type for the converter + * @param typeReference the TypeReference for the class being converted. + * @return an instance of the converter for this class + */ + public Converter getConverter(TypeReference typeReference) + { + return getConverter(null, typeReference); + } + + @SuppressWarnings("unchecked") + private Converter getConverter(Type type, TypeReference typeReference) + { + type = type != null ? type : typeReference.getType(); + + Converter converter; + + converter = (Converter) converterInstances.get(type); + if (converter == null) + { + if (typeReference != null) + { + // Should we cache this? + converter = new JSONConverter(typeReference); + } + else + { + converter = new JSONConverter(type); + } + } + + return converter; + + } + + + /** + * Register a converter for the supplied class. + *

+ * This instance be re-used for every conversion. + *

+ * @param The type being converted + * @param clazz the class for this converter. + * @param converter an instance of Converter + */ + public void registerConverterForClass(Class clazz, Converter converter) + { + converterInstances.put((Type)clazz, converter); + } + + /** + * Register a converter for the supplied class. + *

+ * This instance be re-used for every conversion. + *

+ * @param The type being converted + * @param typeReference the TypeReference for the class being converted. + * @param converter an instance of Converter + */ + public void registerConverterForClass(TypeReference typeReference, Converter converter) + { + Type t = typeReference.getType(); + converterInstances.put(t, converter); + } + + /** + * Unregister a converter. + * @param The type + * @param clazz the class being converted + */ + public void unregisterConverterForClass(Class clazz) + { + converterInstances.remove((Type)clazz); + } + + /** + * Unregister a converter. + * @param The type + * @param typeReference the TypeReference for the class being converted. + */ + public void unregisterConverterForClass(TypeReference typeReference) + { + Type t = typeReference.getType(); + converterInstances.remove(t); + } +} diff --git a/src/main/java/com/basho/riak/client/api/convert/JSONConverter.java b/src/main/java/com/basho/riak/client/api/convert/JSONConverter.java new file mode 100644 index 000000000..574b4b05c --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/JSONConverter.java @@ -0,0 +1,126 @@ +/* + * 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.convert; + + +import java.io.IOException; + +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.datatype.joda.JodaModule; + +import com.basho.riak.client.core.util.BinaryValue; +import com.fasterxml.jackson.core.type.TypeReference; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +/** + * The default Converter used when storing and fetching domain objects from Riak. + *

+ * This uses the Jackson JSON library to serialize / deserialize objects to JSON. + * The reulsting JSON is then stored in Riak. + *

+ * @author Brian Roach + * @param type to convert to/from + * + */ +public class JSONConverter extends Converter { + + // Object mapper per domain class is expensive, a singleton (and ThreadSafe) will do. + private static final ObjectMapper OBJECT_MAPPER= new ObjectMapper(); + private final TypeReference typeReference; + static { + OBJECT_MAPPER.registerModule(new RiakJacksonModule()); + OBJECT_MAPPER.registerModule(new JodaModule()); + } + + /** + * Create a JSONConverter for creating instances of type from + * JSON and instances of {@literal RiakObject} with a JSON payload from + * instances of clazz + * + * @param type + */ + public JSONConverter(Type type) { + super(type); + typeReference = null; + } + + public JSONConverter(TypeReference typeReference) + { + super(typeReference.getType()); + this.typeReference = typeReference; + } + + /** + * Returns the {@link ObjectMapper} being used. + * This is a convenience method to allow changing its behavior. + * @return The Jackson ObjectMapper + */ + public static ObjectMapper getObjectMapper() { + return OBJECT_MAPPER; + } + + /** + * Convenient method to register a Jackson module into the singleton Object mapper used by domain objects. + * @param jacksonModule Module to register. + */ + public static void registerJacksonModule(final Module jacksonModule) { + OBJECT_MAPPER.registerModule(jacksonModule); + } + + @SuppressWarnings("unchecked") + @Override + public T toDomain(BinaryValue value, String contentType) + { + try + { + if (typeReference != null) + { + return OBJECT_MAPPER.readValue(value.unsafeGetValue(), typeReference); + } + else + { + Class rawType = type instanceof Class + ? (Class) type + : (Class) ((ParameterizedType) type).getRawType(); + return OBJECT_MAPPER.readValue(value.unsafeGetValue(), (Class) rawType); + + } + + } + catch (IOException ex) + { + throw new ConversionException(ex); + } + } + + @Override + public ContentAndType fromDomain(T domainObject) + { + try + { + return new ContentAndType(BinaryValue.unsafeCreate(OBJECT_MAPPER.writeValueAsBytes(domainObject)), + "application/json"); + } + catch (JsonProcessingException ex) + { + throw new ConversionException(ex); + } + } + +} diff --git a/src/main/java/com/basho/riak/client/api/convert/PassThroughConverter.java b/src/main/java/com/basho/riak/client/api/convert/PassThroughConverter.java new file mode 100644 index 000000000..5a95c28d6 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/PassThroughConverter.java @@ -0,0 +1,64 @@ +/* + * 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.convert; + +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; + + +/** + * For working with {@link RiakObject} rather than domain types. + * + * @author Russel Brown + * @since 1.0 + */ +public final class PassThroughConverter extends Converter +{ + public PassThroughConverter() + { + super(RiakObject.class); + } + + @Override + public RiakObject toDomain(RiakObject obj, Location location) + { + return obj; + } + + @Override + public Converter.OrmExtracted fromDomain(RiakObject domainObject, Namespace namespace, BinaryValue key) + { + return new Converter.OrmExtracted(domainObject, namespace, key); + } + + + + @Override + public RiakObject toDomain(BinaryValue value, String contentType) + { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public ContentAndType fromDomain(RiakObject domainObject) + { + throw new UnsupportedOperationException("Not supported"); + } + +} diff --git a/src/main/java/com/basho/riak/client/api/convert/RiakBeanSerializerModifier.java b/src/main/java/com/basho/riak/client/api/convert/RiakBeanSerializerModifier.java new file mode 100644 index 000000000..0a9c7a4cd --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/RiakBeanSerializerModifier.java @@ -0,0 +1,135 @@ +/* + * 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.convert; + +import com.basho.riak.client.api.annotations.RiakBucketName; +import com.basho.riak.client.api.annotations.RiakBucketType; +import com.basho.riak.client.api.annotations.RiakContentType; +import com.basho.riak.client.api.annotations.RiakKey; +import com.basho.riak.client.api.annotations.RiakLinks; +import com.basho.riak.client.api.annotations.RiakUsermeta; +import com.basho.riak.client.api.annotations.RiakVClock; +import com.basho.riak.client.api.annotations.RiakIndex; +import com.basho.riak.client.api.annotations.RiakLastModified; +import com.basho.riak.client.api.annotations.RiakTombstone; +import com.basho.riak.client.api.annotations.RiakVTag; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; + +/** + * {@link BeanSerializerModifier} that drops {@link RiakKey} and + * {@link RiakUsermeta} annotated fields from the the set of fields that Jackson + * will serialize + * + * @author russell + * + */ +public class RiakBeanSerializerModifier extends BeanSerializerModifier +{ + + private static final RiakBeanSerializerModifier INSTANCE = new RiakBeanSerializerModifier(); + + /** + * + */ + private RiakBeanSerializerModifier() + { + } + + public static RiakBeanSerializerModifier getInstance() + { + return INSTANCE; + } + + /* + * (non-Javadoc) + * + * @see + * org.codehaus.jackson.map.ser.BeanSerializerModifier#changeProperties( + * org.codehaus.jackson.map.SerializationConfig, + * org.codehaus.jackson.map.introspect.BasicBeanDescription, java.util.List) + */ + @Override + public List changeProperties(SerializationConfig config, + BeanDescription beanDesc, + List beanProperties) + { + final List keptProperties = new ArrayList(); + for (BeanPropertyWriter w : beanProperties) + { + if (keepProperty(w)) + { + keptProperties.add(w); + } + } + + return keptProperties; + } + + static final List> RIAK_ANNOTATIONS = new ArrayList>(); + + static + { + RIAK_ANNOTATIONS.add(RiakKey.class); + RIAK_ANNOTATIONS.add(RiakBucketName.class); + RIAK_ANNOTATIONS.add(RiakBucketType.class); + RIAK_ANNOTATIONS.add(RiakContentType.class); + RIAK_ANNOTATIONS.add(RiakVTag.class); + RIAK_ANNOTATIONS.add(RiakLastModified.class); + RIAK_ANNOTATIONS.add(RiakUsermeta.class); + RIAK_ANNOTATIONS.add(RiakLinks.class); + RIAK_ANNOTATIONS.add(RiakIndex.class); + RIAK_ANNOTATIONS.add(RiakVClock.class); + RIAK_ANNOTATIONS.add(RiakTombstone.class); + } + + /** + * Checks if the property has any of the Riak annotations on it or the + * Jackson JsonProperty annotation. + * + * If a Riak annotation is present without the Jackson JsonProperty + * annotation, this will return false. + * + * If a property has been annotated with both the Jackson JsonProperty + * annotation and a Riak annotation, the Jackson annotation takes precedent + * and this will return true. + * + * @param beanPropertyWriter a {@link BeanPropertyWriter} to check for Riak* + * annotations + * @return true if the property is not Riak annotated or is Jackson + * JsonProperty annotated, false otherwise + */ + private boolean keepProperty(BeanPropertyWriter beanPropertyWriter) + { + if (beanPropertyWriter.getAnnotation(JsonProperty.class) != null) + { + return true; + } + for (Class annotation : RIAK_ANNOTATIONS) + { + if (beanPropertyWriter.getAnnotation(annotation) != null) + { + return false; + } + } + return true; + } +} diff --git a/src/main/java/com/basho/riak/client/api/convert/RiakJacksonModule.java b/src/main/java/com/basho/riak/client/api/convert/RiakJacksonModule.java new file mode 100644 index 000000000..46f144b61 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/RiakJacksonModule.java @@ -0,0 +1,60 @@ +/* + * 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.convert; + +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.Module; + +/** + * A Jackson module that customizes Jackson's object + * mapper so we can handle Riak annotations like {@literal @RiakKey}, + * {@literal @RiakUsermeta}, etc correctly. + * + * Adds a {@link RiakBeanSerializerModifier} that removes any {@literal @RiakXXX} annotated + * fields from the JSON output (since they will be persisted as object meta data + * and not as part of the object). Explicitly adding an additional {@literal @JsonProperty} + * annotation overrides this exclusion. + * + * @author russell + * + */ +public class RiakJacksonModule extends Module +{ + + private static final String NAME = "RiakJacksonModule"; + private static final Version VERSION = Version.unknownVersion(); + + public RiakJacksonModule() + { + } + + @Override + public String getModuleName() + { + return NAME; + } + + @Override + public Version version() + { + return VERSION; + } + + @Override + public void setupModule(SetupContext context) + { + context.addBeanSerializerModifier(RiakBeanSerializerModifier.getInstance()); + } + +} diff --git a/src/main/java/com/basho/riak/client/api/convert/StringConverter.java b/src/main/java/com/basho/riak/client/api/convert/StringConverter.java new file mode 100644 index 000000000..7e4e1a2e9 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/StringConverter.java @@ -0,0 +1,71 @@ +/* + * 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.convert; + +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; + +/** + * Converter that passes Strings through unmodified. + * + * Worth noting is that when using String directly with StoreValue + * there's no way to determine the character set. This converter uses the + * default character set for conversion. + * + * @author Brian Roach + * @since 2.0 + */ +public class StringConverter extends Converter +{ + + public StringConverter() + { + super(String.class); + } + + @Override + public String toDomain(RiakObject obj, Location location) + { + return obj.getValue().toString(); + } + + @Override + public Converter.OrmExtracted fromDomain(String domainObject, Namespace namespace, BinaryValue key) + { + RiakObject obj = new RiakObject() + .setValue(BinaryValue.create(domainObject)) + .setContentType("text/plain"); + return new Converter.OrmExtracted(obj, namespace, key); + } + + @Override + public String toDomain(BinaryValue value, String contentType) throws ConversionException + { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ContentAndType fromDomain(String domainObject) throws ConversionException + { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + + +} diff --git a/src/main/java/com/basho/riak/client/api/convert/reflection/AnnotationCache.java b/src/main/java/com/basho/riak/client/api/convert/reflection/AnnotationCache.java new file mode 100644 index 000000000..d550f18ea --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/reflection/AnnotationCache.java @@ -0,0 +1,58 @@ +/* + * 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.convert.reflection; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; + +/** + * TODO: consider class reloading and re-scanning + * @author russell + * + */ +public class AnnotationCache { + @SuppressWarnings("rawtypes") private final ConcurrentHashMap> cache = new ConcurrentHashMap>(); + + /** + * @param clazz the class to be scanned and cached. + * @return an AnnotationInfo instance. + */ + public AnnotationInfo get(Class clazz) { + + Future scanner = cache.get(clazz); + + if (scanner == null) { + FutureTask scannerTask = new FutureTask(new AnnotationScanner(clazz)); + + scanner = cache.putIfAbsent(clazz, scannerTask); + if (scanner == null) { + scanner = scannerTask; + scannerTask.run(); + } + } + + try { + return scanner.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } catch (ExecutionException e) { + cache.remove(clazz); + throw new RuntimeException(e.getCause()); + } + } + +} diff --git a/src/main/java/com/basho/riak/client/api/convert/reflection/AnnotationHelper.java b/src/main/java/com/basho/riak/client/api/convert/reflection/AnnotationHelper.java new file mode 100644 index 000000000..a08616712 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/reflection/AnnotationHelper.java @@ -0,0 +1,161 @@ +/* + * 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.convert.reflection; + +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.query.UserMetadata.RiakUserMetadata; +import com.basho.riak.client.core.query.indexes.RiakIndexes; +import com.basho.riak.client.core.query.links.RiakLinks; +import com.basho.riak.client.core.util.BinaryValue; + +/** + * Singleton that wraps a cache of Class -> AnnotatioInfo and provides + * convenience methods for getting and setting Riak annotated field values + * + * @author russell + * + */ +public class AnnotationHelper { + + private static final AnnotationHelper INSTANCE = new AnnotationHelper(); + private final AnnotationCache annotationCache = new AnnotationCache(); + + private AnnotationHelper() {} + + public static AnnotationHelper getInstance() { + return INSTANCE; + } + + public BinaryValue getRiakKey(T obj) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + return annotationInfo.getRiakKey(obj); + } + + public T setRiakKey(T obj, BinaryValue key) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + annotationInfo.setRiakKey(obj, key); + + return obj; + } + + public BinaryValue getRiakBucketName(T obj) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + return annotationInfo.getRiakBucketName(obj); + } + + public T setRiakBucketName(T obj, BinaryValue bucketName) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + annotationInfo.setRiakBucketName(obj, bucketName); + return obj; + } + + public BinaryValue getRiakBucketType(T obj) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + return annotationInfo.getRiakBucketType(obj); + } + + public T setRiakBucketType(T obj, BinaryValue bucketType) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + annotationInfo.setRiakBucketType(obj, bucketType); + return obj; + } + + public boolean hasRiakVClockAnnotation(T obj) + { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + return annotationInfo.hasRiakVClock(); + } + + public T setRiakVClock(T obj, VClock vclock) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + annotationInfo.setRiakVClock(obj, vclock); + + return obj; + } + + public VClock getRiakVClock(T obj) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + return annotationInfo.getRiakVClock(obj); + } + + public T setRiakTombstone(T obj, boolean isTombstone) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + annotationInfo.setRiakTombstone(obj, isTombstone); + + return obj; + } + + public Boolean getRiakTombstone(T obj) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + Boolean tombstone = annotationInfo.getRiakTombstone(obj); + + return tombstone; + } + + public T setRiakContentType(T obj, String contentType) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + annotationInfo.setRiakContentType(obj, contentType); + return obj; + } + + public String getRiakContentType(T obj) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + return annotationInfo.getRiakContentType(obj); + } + + public T setRiakLastModified(T obj, Long lastModified) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + annotationInfo.setRiakLastModified(obj, lastModified); + return obj; + } + + public T setRiakVTag(T obj, String vtag) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + annotationInfo.setRiakVTag(obj, vtag); + return obj; + } + + public RiakUserMetadata getUsermetaData(RiakUserMetadata container, T obj) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + return annotationInfo.getUsermetaData(container, obj); + } + + public T setUsermetaData(RiakUserMetadata usermetaData, T obj) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + annotationInfo.setUsermetaData(usermetaData, obj); + return obj; + } + + public RiakIndexes getIndexes(RiakIndexes container, T obj) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + return annotationInfo.getIndexes(container, obj); + } + + public T setIndexes(RiakIndexes indexes, T obj) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + annotationInfo.setIndexes(indexes, obj); + return obj; + } + + public RiakLinks getLinks(RiakLinks container, T obj) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + return annotationInfo.getLinks(container, obj); + } + + public T setLinks(RiakLinks links, T obj) { + final AnnotationInfo annotationInfo = annotationCache.get(obj.getClass()); + annotationInfo.setLinks(links, obj); + return obj; + } +} diff --git a/src/main/java/com/basho/riak/client/api/convert/reflection/AnnotationInfo.java b/src/main/java/com/basho/riak/client/api/convert/reflection/AnnotationInfo.java new file mode 100644 index 000000000..fa30a2f18 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/reflection/AnnotationInfo.java @@ -0,0 +1,1522 @@ +/* + * 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.convert.reflection; + +import static com.basho.riak.client.api.convert.reflection.ClassUtil.getFieldValue; +import static com.basho.riak.client.api.convert.reflection.ClassUtil.setFieldValue; +import static com.basho.riak.client.api.convert.reflection.ClassUtil.getMethodValue; +import static com.basho.riak.client.api.convert.reflection.ClassUtil.setMethodValue; + +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.basho.riak.client.core.query.links.RiakLink; +import com.basho.riak.client.api.cap.BasicVClock; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.query.UserMetadata.RiakUserMetadata; +import com.basho.riak.client.core.query.indexes.BigIntIndex; +import com.basho.riak.client.core.query.indexes.IndexType; +import com.basho.riak.client.core.query.indexes.LongIntIndex; +import com.basho.riak.client.core.query.indexes.RawIndex; +import com.basho.riak.client.core.query.indexes.RiakIndexes; +import com.basho.riak.client.core.query.indexes.StringBinIndex; +import com.basho.riak.client.core.query.links.RiakLinks; +import com.basho.riak.client.core.util.BinaryValue; +import java.lang.reflect.Method; +import java.math.BigInteger; +import java.util.HashSet; +import java.util.LinkedList; + +/** + * Class that containsKeyKey the Riak annotated fields for an annotated class + * + * @author russell + * + */ +public class AnnotationInfo +{ + + private final Field riakKeyField; + private final Method riakKeySetter; + private final Method riakKeyGetter; + private final List usermetaFields; + private final List usermetaMethods; + private final List indexFields; + private final List indexMethods; + private final Field riakLinksField; + private final Method riakLinksGetter; + private final Method riakLinksSetter; + private final Field riakVClockField; + private final Method riakVClockSetter; + private final Method riakVClockGetter; + private final Field riakTombstoneField; + private final Method riakTombstoneSetter; + private final Method riakTombstoneGetter; + private final Field riakContentTypeField; + private final Method riakContentTypeGetter; + private final Method riakContentTypeSetter; + private final Field riakLastModifiedField; + private final Method riakLastModifiedSetter; + private final Field riakVTagField; + private final Method riakVTagSetter; + private final Field riakBucketNameField; + private final Method riakBucketNameSetter; + private final Method riakBucketNameGetter; + private final Field riakBucketTypeField; + private final Method riakBucketTypeSetter; + private final Method riakBucketTypeGetter; + + private AnnotationInfo(Builder builder) + { + this.riakKeyField = builder.riakKeyField; + this.riakKeyGetter = builder.riakKeyGetter; + this.riakKeySetter = builder.riakKeySetter; + this.riakLinksField = builder.riakLinksField; + this.riakLinksGetter = builder.riakLinksGetter; + this.riakLinksSetter = builder.riakLinksSetter; + this.riakVClockField = builder.riakVClockField; + this.riakVClockGetter = builder.riakVClockGetter; + this.riakVClockSetter = builder.riakVClockSetter; + this.riakTombstoneField = builder.riakTombstoneField; + this.riakTombstoneGetter = builder.riakTombstoneGetter; + this.riakTombstoneSetter = builder.riakTombstoneSetter; + this.usermetaFields = builder.usermetaFields; + this.usermetaMethods = builder.usermetaMethods; + this.indexFields = builder.indexFields; + this.indexMethods = builder.indexMethods; + this.riakContentTypeField = builder.riakContentTypeField; + this.riakContentTypeGetter = builder.riakContentTypeGetter; + this.riakContentTypeSetter = builder.riakContentTypeSetter; + this.riakLastModifiedField = builder.riakLastModifiedField; + this.riakLastModifiedSetter = builder.riakLastModified; + this.riakVTagField = builder.riakVTagField; + this.riakVTagSetter = builder.riakVTagSetter; + this.riakBucketNameField = builder.riakBucketNameField; + this.riakBucketNameGetter = builder.riakBucketNameGetter; + this.riakBucketNameSetter = builder.riakBucketNameSetter; + this.riakBucketTypeField = builder.riakBucketTypeField; + this.riakBucketTypeGetter = builder.riakBucketTypeGetter; + this.riakBucketTypeSetter = builder.riakBucketTypeSetter; + } + + /** + * Returns the key. + *

+ * The @RiakKey annotation allows for any type to be used. this method will + * call the object's toString() method to return a String. + *

+ * + * @param the type of the domain object + * @param obj the domain object + * @return the String representation of the key + */ + public BinaryValue getRiakKey(T obj) + { + // TODO: charset + BinaryValue key = null; + if (riakKeyGetter != null) + { + if (riakKeyGetter.getReturnType().isArray()) + { + Object o = getMethodValue(riakKeyGetter, obj); + if (o != null) + { + key = BinaryValue.create((byte[]) o); + } + } + else + { + Object o = getMethodValue(riakKeyGetter, obj); + if (o != null) + { + key = BinaryValue.create((String) o); + } + } + } + else if (riakKeyField != null) + { + if (riakKeyField.getType().equals(String.class)) + { + Object o = getFieldValue(riakKeyField, obj); + if (o != null) + { + key = BinaryValue.create((String) o); + } + } + else + { + Object o = getFieldValue(riakKeyField, obj); + if (o != null) + { + key = BinaryValue.create((byte[]) o); + } + } + } + + return key; + } + + // TODO: charset + public void setRiakKey(T obj, BinaryValue key) + { + if (riakKeySetter != null) + { + if (riakKeySetter.getParameterTypes()[0].isArray()) + { + setMethodValue(riakKeySetter, obj, key.unsafeGetValue()); + } + else + { + setMethodValue(riakKeySetter, obj, key.toString()); + } + } + else if (riakKeyField != null) + { + if (riakKeyField.getType().equals(String.class)) + { + setFieldValue(riakKeyField, obj, key.toString()); + } + else + { + setFieldValue(riakKeyField, obj, key.unsafeGetValue()); + } + } + + } + + // TODO: charset in annotation + public BinaryValue getRiakBucketName(T obj) + { + BinaryValue bucketName = null; + + if (riakBucketNameGetter != null) + { + if (riakBucketNameGetter.getReturnType().isArray()) + { + Object o = getMethodValue(riakBucketNameGetter, obj); + if (o != null) + { + bucketName = BinaryValue.create((byte[]) o); + } + } + else + { + Object o = getMethodValue(riakBucketNameGetter, obj); + if (o != null) + { + bucketName = BinaryValue.create((String) o); + } + } + } + else if (riakBucketNameField != null) + { + if (riakBucketNameField.getType().equals(String.class)) + { + Object o = getFieldValue(riakBucketNameField, obj); + if (o != null) + { + bucketName = BinaryValue.create((String) o); + } + } + else + { + Object o = getFieldValue(riakBucketNameField, obj); + if (o != null) + { + bucketName = BinaryValue.create((byte[]) o); + } + } + } + + return bucketName; + } + + // TODO: charset in annotation + public void setRiakBucketName(T obj, BinaryValue bucketName) + { + if (riakBucketNameSetter != null) + { + if (riakBucketNameSetter.getParameterTypes()[0].isArray()) + { + setMethodValue(riakBucketNameSetter, obj, bucketName.unsafeGetValue()); + } + else + { + setMethodValue(riakBucketNameSetter, obj, bucketName.toString()); + } + } + else if (riakBucketNameField != null) + { + if (riakBucketNameField.getType().equals(String.class)) + { + setFieldValue(riakBucketNameField, obj, bucketName.toString()); + } + else + { + setFieldValue(riakBucketNameField, obj, bucketName.unsafeGetValue()); + } + } + } + + // TODO: charset in annotation + public BinaryValue getRiakBucketType(T obj) + { + BinaryValue bucketType = null; + + if (riakBucketTypeGetter != null) + { + if (riakBucketTypeGetter.getReturnType().isArray()) + { + Object o = getMethodValue(riakBucketTypeGetter, obj); + if (o != null) + { + bucketType = BinaryValue.create((byte[]) o); + } + } + else + { + Object o = getMethodValue(riakBucketTypeGetter, obj); + if (o != null) + { + bucketType = BinaryValue.create((String) o); + } + } + } + else if (riakBucketTypeField != null) + { + if (riakBucketTypeField.getType().equals(String.class)) + { + Object o = getFieldValue(riakBucketTypeField, obj); + if (o != null) + { + bucketType = BinaryValue.create((String) o); + } + } + else + { + Object o = getFieldValue(riakBucketTypeField, obj); + if (o != null) + { + bucketType = BinaryValue.create((byte[]) o); + } + } + } + return bucketType; + } + + // TODO: charset in annotation + public void setRiakBucketType(T obj, BinaryValue bucketType) + { + if (riakBucketTypeSetter != null) + { + if (riakBucketTypeSetter.getParameterTypes()[0].isArray()) + { + setMethodValue(riakBucketTypeSetter, obj, bucketType.unsafeGetValue()); + } + else + { + setMethodValue(riakBucketTypeSetter, obj, bucketType.toString()); + } + } + else if (riakBucketTypeField != null) + { + if (riakBucketTypeField.getType().equals(String.class)) + { + setFieldValue(riakBucketTypeField, obj, bucketType.toString()); + } + else + { + setFieldValue(riakBucketTypeField, obj, bucketType.unsafeGetValue()); + } + } + } + + public boolean hasRiakVClock() + { + return riakVClockField != null || riakVClockSetter != null; + } + + public VClock getRiakVClock(T obj) + { + + VClock vclock = null; + + // We allow the annotated field to be either an actual VClock, or + // a byte array. + if (riakVClockGetter != null) + { + if (riakVClockGetter.getReturnType().isArray()) + { + vclock = new BasicVClock((byte[]) getMethodValue(riakVClockGetter, obj)); + } + else + { + vclock = (VClock) getMethodValue(riakVClockGetter, obj); + } + } + else if (riakVClockField != null) + { + if (riakVClockField.getType().isAssignableFrom(VClock.class)) + { + vclock = (VClock) getFieldValue(riakVClockField, obj); + } + else + { + vclock = new BasicVClock((byte[]) getFieldValue(riakVClockField, obj)); + } + } + + return vclock; + } + + public void setRiakVClock(T obj, VClock vclock) + { + + // We allow the annotated field to be either an actual VClock, or + // a byte array. This is enforced in the AnnotationScanner + if (riakVClockSetter != null) + { + Class pType = riakVClockSetter.getParameterTypes()[0]; + if (pType.isArray()) + { + setMethodValue(riakVClockSetter, obj, vclock.getBytes()); + } + else + { + setMethodValue(riakVClockSetter, obj, vclock); + } + } + else if (riakVClockField != null) + { + + if (riakVClockField.getType().isAssignableFrom(VClock.class)) + { + setFieldValue(riakVClockField, obj, vclock); + } + else + { + setFieldValue(riakVClockField, obj, vclock.getBytes()); + } + } + } + + public Boolean getRiakTombstone(T obj) + { + Boolean tombstone = null; + if (riakTombstoneGetter != null) + { + tombstone = (Boolean) getMethodValue(riakTombstoneGetter, obj); + } + else if (riakTombstoneField != null) + { + tombstone = (Boolean) getFieldValue(riakTombstoneField, obj); + } + + return tombstone; + } + + public void setRiakTombstone(T obj, Boolean isDeleted) + { + + if (riakTombstoneSetter != null) + { + setMethodValue(riakTombstoneSetter, obj, isDeleted); + } + else if (riakTombstoneField != null) + { + setFieldValue(riakTombstoneField, obj, isDeleted); + } + } + + public String getRiakContentType(T obj) + { + String contentType = null; + if (riakContentTypeGetter != null) + { + contentType = (String) getMethodValue(riakContentTypeGetter, obj); + } + else if (riakContentTypeField != null) + { + contentType = (String) getFieldValue(riakContentTypeField, obj); + } + return contentType; + } + + public void setRiakContentType(T obj, String contentType) + { + if (riakContentTypeSetter != null) + { + setMethodValue(riakContentTypeSetter, obj, contentType); + } + else if (riakContentTypeField != null) + { + setFieldValue(riakContentTypeField, obj, contentType); + } + } + + public void setRiakLastModified(T obj, Long lastModified) + { + if (riakLastModifiedSetter != null) + { + setMethodValue(riakLastModifiedSetter, obj, lastModified); + } + else if (riakLastModifiedField != null) + { + setFieldValue(riakLastModifiedField, obj, lastModified); + } + } + + public void setRiakVTag(T obj, String vtag) + { + if (riakVTagSetter != null) + { + setMethodValue(riakVTagSetter, obj, vtag); + } + else if (riakVTagField != null) + { + setFieldValue(riakVTagField, obj, vtag); + } + } + + public RiakUserMetadata getUsermetaData(RiakUserMetadata container, T obj) + { + for (UsermetaField uf : usermetaFields) + { + switch(uf.getFieldType()) + { + case MAP: + @SuppressWarnings("unchecked") + Map map = (Map) getFieldValue(uf.getField(), obj); + if (map != null) + { + container.put(map); + } + break; + case STRING: + Object o = getFieldValue(uf.getField(), obj); + String val = o == null ? null : o.toString(); + String key = uf.getUsermetaDataKey(); + // null is not a user meta datum + if (o != null) + { + container.put(key, val); + } + break; + default: + break; + } + } + + for (UsermetaMethod um : usermetaMethods) + { + switch(um.getMethodType()) + { + case MAP_GETTER: + @SuppressWarnings("unchecked") + Map map = (Map) getMethodValue(um.getMethod(), obj); + if (map != null) + { + container.put(map); + } + break; + case STRING_GETTER: + Object o = getMethodValue(um.getMethod(), obj); + String val = o == null ? null : o.toString(); + String key = um.getUsermetaDataKey(); + if (o != null) + { + container.put(key, val); + } + break; + default: + break; + } + } + + return container; + } + + /** + * Populates an @RiakUsermeta annotated domain object with the User metadata. + * @param + * @param userMetadata + * @param obj + */ + public void setUsermetaData(RiakUserMetadata userMetadata, T obj) + { + Field mapField = null; + for (UsermetaField uf : usermetaFields) + { + switch(uf.getFieldType()) + { + case STRING: + if (userMetadata.containsKey(uf.getUsermetaDataKey())) + { + setFieldValue(uf.getField(), obj, userMetadata.get(uf.getUsermetaDataKey())); + userMetadata.remove(uf.getUsermetaDataKey()); + } + break; + case MAP: + mapField = uf.getField(); + break; + default: + break; + } + } + + Method mapSetter = null; + for (UsermetaMethod um : usermetaMethods) + { + switch(um.getMethodType()) + { + case STRING_SETTER: + if (userMetadata.containsKey(um.getUsermetaDataKey())) + { + setMethodValue(um.getMethod(), obj, userMetadata.get(um.getUsermetaDataKey())); + userMetadata.remove(um.getUsermetaDataKey()); + } + break; + case MAP_SETTER: + mapSetter = um.getMethod(); + break; + default: + break; + } + } + + + if (mapSetter != null || mapField != null) + { + Map mapCopy = new HashMap(userMetadata.size()); + for (Map.Entry entry : userMetadata.getUserMetadata()) + { + mapCopy.put(entry.getKey().toString(), entry.getValue().toString()); + } + + if (mapSetter != null) + { + setMethodValue(mapSetter, obj, mapCopy); + } + + if (mapField != null) + { + setFieldValue(mapField, obj, mapCopy); + } + } + } + + /** + * @param The domain object type + * @param obj the domain object + * @return a {@link RiakIndexes} made of the values of the RiakIndex + * annotated fields and methods. + */ + @SuppressWarnings("unchecked") + public RiakIndexes getIndexes(RiakIndexes container, T obj) + { + for (RiakIndexField f : indexFields) + { + final Object val = getFieldValue(f.getField(), obj); + switch(f.getFieldType()) + { + case SET_LONG: + case LONG: + // We want to create the index regardless + LongIntIndex longIndex = container.getIndex(LongIntIndex.named(f.getIndexName())); + if (val != null) + { + if (f.getFieldType() == RiakIndexField.FieldType.SET_LONG) + { + longIndex.add((Set) val); + } + else + { + longIndex.add((Long) val); + } + } + break; + case SET_STRING: + case STRING: + // We want to create the index regardless + StringBinIndex stringBinIndex = container.getIndex(StringBinIndex.named(f.getIndexName())); + if (val != null) + { + if (f.getFieldType() == RiakIndexField.FieldType.SET_STRING) + { + stringBinIndex.add((Set) val); + } + else + { + stringBinIndex.add((String) val); + } + } + break; + case SET_BIG_INT: + case BIG_INT: + BigIntIndex bigIntIndex = container.getIndex(BigIntIndex.named(f.getIndexName())); + if (val != null) + { + if (f.getFieldType() == RiakIndexField.FieldType.SET_BIG_INT) + { + bigIntIndex.add((Set) val); + } + else + { + bigIntIndex.add((BigInteger) val); + } + } + break; + case SET_RAW: + case RAW: + { + // We want to create the index regardless + IndexType iType = IndexType.typeFromFullname(f.getIndexName()); + RawIndex rawIndex = container.getIndex(RawIndex.named(f.getIndexName(), iType)); + if (val != null) + { + if (f.getFieldType() == RiakIndexField.FieldType.SET_RAW) + { + for (byte[] bytes : (Set) val) + { + rawIndex.add(BinaryValue.create(bytes)); + } + } + else + { + rawIndex.add(BinaryValue.create((byte[])val)); + } + } + } + default: + break; + } + + } + + for (RiakIndexMethod m : indexMethods) + { + Object val; + + switch (m.getMethodType()) + { + case SET_LONG_GETTER: + case LONG_GETTER: + val = getMethodValue(m.getMethod(), obj); + // We want to create the index regardless + LongIntIndex index = container.getIndex(LongIntIndex.named(m.getIndexName())); + if (val != null) + { + if (m.getMethodType() == RiakIndexMethod.MethodType.SET_LONG_GETTER) + { + index.add((Set) val); + } + else + { + index.add((Long) val); + } + } + break; + case SET_STRING_GETTER: + case STRING_GETTER: + val = getMethodValue(m.getMethod(), obj); + // We want to create the index regardless + StringBinIndex stringBinIndex = container.getIndex(StringBinIndex.named(m.getIndexName())); + if (val != null) + { + if (m.getMethodType() == RiakIndexMethod.MethodType.SET_STRING_GETTER) + { + stringBinIndex.add((Set) val); + } + else + { + stringBinIndex.add((String) val); + } + } + break; + case SET_BIG_INT_GETTER: + case BIG_INT_GETTER: + val = getMethodValue(m.getMethod(), obj); + // We want to create the index regardless + BigIntIndex bigIntIndex = container.getIndex(BigIntIndex.named(m.getIndexName())); + if (val != null) + { + if (m.getMethodType() == RiakIndexMethod.MethodType.SET_BIG_INT_GETTER) + { + bigIntIndex.add((Set) val); + } + else + { + bigIntIndex.add((BigInteger) val); + } + } + break; + case SET_RAW_GETTER: + case RAW_GETTER: + val = getMethodValue(m.getMethod(), obj); + IndexType iType = IndexType.typeFromFullname(m.getIndexName()); + RawIndex rawIndex = container.getIndex(RawIndex.named(m.getIndexName(), iType)); + if (val != null) + { + if (m.getMethodType() == RiakIndexMethod.MethodType.SET_RAW_GETTER) + { + for (byte[] bytes : (Set) val) + { + rawIndex.add(BinaryValue.create(bytes)); + } + } + else + { + rawIndex.add(BinaryValue.create((byte[]) val)); + } + } + default: + break; + } + } + + return container; + } + + /** + * @param + * @param indexes the RiakIndexes to copy to the domain object + * @param obj the domain object to set indexes on + */ + public void setIndexes(RiakIndexes indexes, T obj) + { + // copy the index values to the correct fields + for (RiakIndexField f : indexFields) + { + Set val = null; + switch(f.getFieldType()) + { + case SET_LONG: + case LONG: + LongIntIndex longIndex = indexes.getIndex(LongIntIndex.named(f.getIndexName())); + val = longIndex.values(); + break; + case SET_STRING: + case STRING: + StringBinIndex stringIndex = indexes.getIndex(StringBinIndex.named(f.getIndexName())); + val = stringIndex.values(); + break; + case SET_BIG_INT: + case BIG_INT: + BigIntIndex bigIntIndex = indexes.getIndex(BigIntIndex.named(f.getIndexName())); + val = bigIntIndex.values(); + break; + case SET_RAW: + case RAW: + IndexType iType = IndexType.typeFromFullname(f.getIndexName()); + RawIndex rawIndex = indexes.getIndex((RawIndex.named(f.getIndexName(), iType))); + // Convert from BinaryValue to bytes + Set byteSet = new HashSet(); + for (BinaryValue bv : rawIndex.values()) + { + byteSet.add(bv.unsafeGetValue()); + } + val = byteSet; + break; + default: + break; + } + + if (val != null) + { + if (f.getFieldType() == RiakIndexField.FieldType.LONG || + f.getFieldType() == RiakIndexField.FieldType.STRING || + f.getFieldType() == RiakIndexField.FieldType.BIG_INT || + f.getFieldType() == RiakIndexField.FieldType.RAW) + { + if (!val.isEmpty()) + { + setFieldValue(f.getField(), obj, val.iterator().next()); // take the first value + } + } + else + { + setFieldValue(f.getField(), obj, val); + } + } + } + + for (RiakIndexMethod m : indexMethods) + { + Set val = null; + + switch(m.getMethodType()) + { + case SET_LONG_SETTER: + case LONG_SETTER: + LongIntIndex longIndex = indexes.getIndex(LongIntIndex.named(m.getIndexName())); + val = longIndex.values(); + break; + case SET_STRING_SETTER: + case STRING_SETTER: + StringBinIndex stringIndex = indexes.getIndex(StringBinIndex.named(m.getIndexName())); + val = stringIndex.values(); + break; + case SET_BIG_INT_SETTER: + case BIG_INT_SETTER: + BigIntIndex bigIntIndex = indexes.getIndex(BigIntIndex.named(m.getIndexName())); + val = bigIntIndex.values(); + break; + case SET_RAW_SETTER: + case RAW_SETTER: + IndexType iType = IndexType.typeFromFullname(m.getIndexName()); + RawIndex rawIndex = indexes.getIndex(RawIndex.named(m.getIndexName(), iType)); + // Convert from BinaryValue to bytes + Set byteSet = new HashSet(); + for (BinaryValue bv : rawIndex.values()) + { + byteSet.add(bv.unsafeGetValue()); + } + val = byteSet; + break; + default: + break; + } + + if (val != null) + { + if (m.getMethodType() == RiakIndexMethod.MethodType.LONG_SETTER || + m.getMethodType() == RiakIndexMethod.MethodType.STRING_SETTER || + m.getMethodType() == RiakIndexMethod.MethodType.BIG_INT_SETTER || + m.getMethodType() == RiakIndexMethod.MethodType.RAW_SETTER) + { + if (!val.isEmpty()) + { + setMethodValue(m.getMethod(), obj, val.iterator().next()); // take the first value + } + } + else + { + setMethodValue(m.getMethod(), obj, val); + } + + } + } + } + + @SuppressWarnings("unchecked") + public RiakLinks getLinks(RiakLinks container, T obj) + { + Object o = null; + if (riakLinksGetter != null) + { + o = getMethodValue(riakLinksGetter, obj); + } + else if (riakLinksField != null) + { + o = getFieldValue(riakLinksField, obj); + } + + if (o != null) + { + container.addLinks((Collection) o); + } + + return container; + } + + public void setLinks(RiakLinks links, T obj) + { + if (riakLinksSetter != null) + { + setMethodValue(riakLinksSetter, obj, links.getLinks()); + } + else if (riakLinksField != null) + { + setFieldValue(riakLinksField, obj, links.getLinks()); + } + + } + + public static class Builder + { + + private Field riakKeyField; + private Method riakKeySetter; + private Method riakKeyGetter; + private Field riakLinksField; + private Method riakLinksGetter; + private Method riakLinksSetter; + private Field riakVClockField; + private Method riakVClockSetter; + private Method riakVClockGetter; + private Field riakTombstoneField; + private Method riakTombstoneSetter; + private Method riakTombstoneGetter; + private Field riakContentTypeField; + private Method riakContentTypeGetter; + private Method riakContentTypeSetter; + private Field riakLastModifiedField; + private Method riakLastModified; + private Field riakVTagField; + private Method riakVTagSetter; + private Field riakBucketNameField; + private Method riakBucketNameSetter; + private Method riakBucketNameGetter; + private Field riakBucketTypeField; + private Method riakBucketTypeSetter; + private Method riakBucketTypeGetter; + private final List usermetaFields; + private final List usermetaMethods; + private final List indexFields; + private final List indexMethods; + + + /** + * Constructs a builder for a new AnnotationInfo + */ + public Builder() + { + } + + + { + usermetaFields = new LinkedList(); + usermetaMethods = new LinkedList(); + indexFields = new LinkedList(); + indexMethods = new LinkedList(); + } + + /** + * Set the @RiakKey annotated field. + * + * @param f the annotated field + * @return a reference to this object + */ + public Builder withRiakKeyField(Field f) + { + validateStringOrByteField(f, "@RiakKey"); + this.riakKeyField = ClassUtil.checkAndFixAccess(f); + return this; + } + + /** + * Set the @RiakKey annotated getter. + * + * @param m the annotated method + * @return a reference to this object + */ + public Builder withRiakKeyGetter(Method m) + { + validateStringOrByteMethod(m, "@RiakKey"); + this.riakKeyGetter = ClassUtil.checkAndFixAccess(m); + return this; + } + + /** + * Set the @RiakKey annotated setter. + * + * @param m the annotated setter + * @return a reference to this object + */ + public Builder withRiakKeySetter(Method m) + { + validateStringOrByteMethod(m, "@RiakKey"); + this.riakKeySetter = ClassUtil.checkAndFixAccess(m); + return this; + } + + /** + * Set the @RiakLinks annotated field. + * + * @param f the annotated field + * @return a reference to this object + */ + public Builder withRiakLinksField(Field f) + { + validateRiakLinksField(f); + this.riakLinksField = ClassUtil.checkAndFixAccess(f); + return this; + } + + /** + * Set the @RiakLinks annotated getter. + * + * @param m the annotated method + * @return a reference to this object + */ + public Builder withRiakLinksGetter(Method m) + { + validateRiakLinksMethod(m); + this.riakLinksGetter = ClassUtil.checkAndFixAccess(m); + return this; + } + + /** + * Set the @RiakLinks annotated setter. + * + * @param m the annotated method + * @return a reference to this object + * + */ + public Builder withRiakLinksSetter(Method m) + { + validateRiakLinksMethod(m); + this.riakLinksSetter = ClassUtil.checkAndFixAccess(m); + return this; + } + + /** + * Add a @RiakUsermeta annotated field. + * + * @param f the annotated field. + * @return a reference to this object. + */ + public Builder addRiakUsermetaField(Field f) + { + this.usermetaFields.add(new UsermetaField(ClassUtil.checkAndFixAccess(f))); + return this; + } + + /** + * Add a @RiakUsermeta annotated method + * + * @param m the annotated method + * @return a reference to this object + */ + public Builder addRiakUsermetaMethod(Method m) + { + this.usermetaMethods.add(new UsermetaMethod(ClassUtil.checkAndFixAccess(m))); + return this; + } + + /** + * Add a @RiakIndex annotated method. + * + * @param m the annotated method + * @return a reference to this object + */ + public Builder addRiakIndexMethod(Method m) + { + this.indexMethods.add(new RiakIndexMethod(ClassUtil.checkAndFixAccess(m))); + return this; + } + + /** + * Add a @RiakIndex annotated field. + * + * @param f the annotated field + * @return a reference to this object + */ + public Builder addRiakIndexField(Field f) + { + this.indexFields.add(new RiakIndexField(ClassUtil.checkAndFixAccess(f))); + return this; + } + + /** + * Set the @RiakVClock annotated field. + * + * @param f the annotated field + * @return a reference to this object + */ + public Builder withRiakVClockField(Field f) + { + validateVClockField(f); + this.riakVClockField = ClassUtil.checkAndFixAccess(f); + return this; + } + + /** + * Set the @RiakVClock annotated setter method. + * + * @param m the annotated method. + * @return a reference to this object + */ + public Builder withRiakVClockSetter(Method m) + { + validateVClockMethod(m); + this.riakVClockSetter = ClassUtil.checkAndFixAccess(m); + return this; + } + + /** + * Set the @RiakVClock annotated getter method. + * + * @param m the annotated method + * @return a reference to this object. + */ + public Builder withRiakVClockGetter(Method m) + { + validateVClockMethod(m); + this.riakVClockGetter = ClassUtil.checkAndFixAccess(m); + return this; + } + + public Builder withRiakTombstoneField(Field f) + { + validateTombstoneField(f); + this.riakTombstoneField = ClassUtil.checkAndFixAccess(f); + return this; + } + + public Builder withRiakTombstoneSetter(Method m) + { + validateTombstoneMethod(m); + this.riakTombstoneSetter = ClassUtil.checkAndFixAccess(m); + return this; + } + + public Builder withRiakTombstoneGetter(Method m) + { + validateTombstoneMethod(m); + this.riakTombstoneGetter = ClassUtil.checkAndFixAccess(m); + return this; + } + + public Builder withRiakContentTypeField(Field f) + { + validateContentTypeField(f); + this.riakContentTypeField = ClassUtil.checkAndFixAccess(f); + return this; + } + + public Builder withRiakContentTypeSetter(Method m) + { + validateContentTypeMethod(m); + this.riakContentTypeSetter = ClassUtil.checkAndFixAccess(m); + return this; + } + + public Builder withRiakContentTypeGetter(Method m) + { + validateContentTypeMethod(m); + this.riakContentTypeGetter = ClassUtil.checkAndFixAccess(m); + return this; + } + + public Builder withRiakLastModifiedField(Field f) + { + validateLastModifiedField(f); + this.riakLastModifiedField = ClassUtil.checkAndFixAccess(f); + return this; + } + + public Builder withRiakLastModifiedSetter(Method m) + { + validateLastModifiedMethod(m); + this.riakLastModified = ClassUtil.checkAndFixAccess(m); + return this; + } + + public Builder withRiakVTagField(Field f) + { + validateVTagField(f); + this.riakVTagField = ClassUtil.checkAndFixAccess(f); + return this; + } + + public Builder withRiakVTagSetter(Method m) + { + validateVTagMethod(m); + this.riakVTagSetter = ClassUtil.checkAndFixAccess(m); + return this; + } + + public Builder withRiakBucketNameField(Field f) + { + validateStringOrByteField(f, "@RiakBucketName"); + this.riakBucketNameField = ClassUtil.checkAndFixAccess(f); + return this; + } + + public Builder withRiakBucketNameSetter(Method m) + { + validateStringOrByteMethod(m, "@RiakBucketName"); + this.riakBucketNameSetter = ClassUtil.checkAndFixAccess(m); + return this; + } + + public Builder withRiakBucketNameGetter(Method m) + { + validateStringOrByteMethod(m, "@RiakBucketName"); + this.riakBucketNameGetter = ClassUtil.checkAndFixAccess(m); + return this; + } + + public Builder withRiakBucketTypeField(Field f) + { + validateStringOrByteField(f, "@RiakBucketType"); + this.riakBucketTypeField = ClassUtil.checkAndFixAccess(f); + return this; + } + + public Builder withRiakBucketTypeSetter(Method m) + { + validateStringOrByteMethod(m, "@RiakBucketType"); + this.riakBucketTypeSetter = ClassUtil.checkAndFixAccess(m); + return this; + } + + public Builder withRiakBucketTypeGetter(Method m) + { + validateStringOrByteMethod(m, "@RiakBucketType"); + this.riakBucketTypeGetter = ClassUtil.checkAndFixAccess(m); + return this; + } + + + public AnnotationInfo build() + { + return new AnnotationInfo(this); + } + + private void validateRiakLinksField(Field riakLinksField) + { + + if (Collection.class.isAssignableFrom(riakLinksField.getType())) + { + Type t = riakLinksField.getGenericType(); + if (t instanceof ParameterizedType) + { + ParameterizedType type = (ParameterizedType) t; + if (type.getRawType().equals(Collection.class)) + { + + Type[] genericParams = type.getActualTypeArguments(); + if (genericParams.length == 1 && genericParams[0].equals(RiakLink.class)) + { + return; + } + } + } + } + else if (riakLinksField.getType().equals(RiakLinks.class)) + { + return; + } + throw new IllegalArgumentException("@RiakLinks field must be Collection"); + } + + private void validateRiakLinksMethod(Method m) + { + if (m.getParameterTypes().length == 1) + { + // it's a setter, check the arg type + Type[] genericParameterTypes = m.getGenericParameterTypes(); + Type t = genericParameterTypes[0]; + if (t instanceof ParameterizedType) + { + ParameterizedType pType = (ParameterizedType) t; + if (pType.getRawType().equals(Collection.class)) + { + Class genericType = (Class) pType.getActualTypeArguments()[0]; + if (RiakLink.class.equals(genericType)) + { + return; + } + } + } + + throw new IllegalArgumentException("@RiakLinks setter must take Collection"); + } + else if (m.getParameterTypes().length == 0) + { + // it's a getter, check return type + Type t = m.getGenericReturnType(); + if (t instanceof ParameterizedType) + { + ParameterizedType pType = (ParameterizedType) t; + if (pType.getRawType().equals(Collection.class)) + { + Class genericType = (Class) pType.getActualTypeArguments()[0]; + if (RiakLink.class.equals(genericType)) + { + return; + } + } + } + throw new IllegalArgumentException("@RiakLinks getter must return Collection"); + + } + } + + private void validateVClockField(Field f) + { + if (!(f.getType().isArray() && f.getType().getComponentType().equals(byte.class)) + && !f.getType().isAssignableFrom(VClock.class)) + { + throw new IllegalArgumentException("@RiakVClock field must be a VClock or byte[]"); + } + } + + private void validateVClockMethod(Method m) + { + if (m.getParameterTypes().length == 1) + { + // It's a setter + Class pType = m.getParameterTypes()[0]; + if (!(pType.isArray() && pType.getComponentType().equals(byte.class)) + && !pType.isAssignableFrom(VClock.class)) + { + throw new IllegalArgumentException("@RiakVClock setter must take VClock or byte[]"); + } + } + else if (m.getParameterTypes().length == 0) + { + Class rType = m.getReturnType(); + if (!(rType.isArray() && rType.getComponentType().equals(byte.class)) + && !rType.isAssignableFrom(VClock.class)) + { + throw new IllegalArgumentException("@RiakVClock getter must return VClock or byte[]"); + } + } + + } + + private void validateTombstoneField(Field f) + { + if (!f.getType().equals(Boolean.class) && !f.getType().equals(boolean.class)) + { + throw new IllegalArgumentException("@RiakTombstone field must be Boolean or boolean"); + } + } + + private void validateTombstoneMethod(Method m) + { + if (m.getParameterTypes().length == 1) + { + Class pType = m.getParameterTypes()[0]; + if (!pType.equals(Boolean.class) && !pType.equals(boolean.class)) + { + throw new IllegalArgumentException("@RiakTombstone setter must take boolean or Boolean"); + } + } + else if (m.getParameterTypes().length == 0) + { + Class rType = m.getReturnType(); + if (!rType.equals(Boolean.class) && !rType.equals(boolean.class)) + { + throw new IllegalArgumentException("@RiakTombstone getter must return boolean or Boolean"); + } + } + } + + private void validateContentTypeField(Field f) + { + if (!f.getType().equals(String.class)) + { + throw new IllegalArgumentException("@RiakContentType field must be a String."); + } + } + + private void validateContentTypeMethod(Method m) + { + if (m.getParameterTypes().length == 1) + { + if (!String.class.equals(m.getParameterTypes()[0])) + { + throw new IllegalArgumentException("@RiakContentType setter must take a String."); + } + } + else if (m.getParameterTypes().length == 0) + { + if (!m.getReturnType().equals(String.class)) + { + throw new IllegalArgumentException("@RiakContentType getter must return a String."); + } + } + } + + private void validateLastModifiedField(Field f) + { + if (!f.getType().equals(Long.class) && !f.getType().equals(long.class)) + { + throw new IllegalArgumentException("@RiakLastModified field must be a Long or long."); + } + } + + private void validateLastModifiedMethod(Method m) + { + if (m.getParameterTypes().length == 0) + { + throw new IllegalArgumentException("@RiakLastModified can only be applied to a setter method."); + } + else if (m.getParameterTypes().length == 1) + { + Class pType = m.getParameterTypes()[0]; + if (!pType.equals(Long.class) && !pType.equals(long.class)) + { + throw new IllegalArgumentException("@RiakLastModified setter must take a long or Long."); + } + } + } + + private void validateVTagField(Field f) + { + if (!f.getType().equals(String.class)) + { + throw new IllegalArgumentException("@RiakVTag field must be a String."); + } + } + + private void validateVTagMethod(Method m) + { + if (m.getParameterTypes().length == 0) + { + throw new IllegalArgumentException("@RiakVTag can only be applied to a setter method."); + } + else if (m.getParameterTypes().length == 1) + { + Class pType = m.getParameterTypes()[0]; + if (!pType.equals(String.class)) + { + throw new IllegalArgumentException("@RiakVTag setter must take a String."); + } + } + } + + private void validateStringOrByteField(Field f, String annotation) + { + if (!(f.getType().isArray() && f.getType().getComponentType().equals(byte.class)) + && !f.getType().isAssignableFrom(String.class)) + { + throw new IllegalArgumentException(annotation + " field must be a String or byte[]."); + } + } + + private void validateStringOrByteMethod(Method m, String annotation) + { + if (m.getParameterTypes().length == 1) + { + // It's a setter + Class pType = m.getParameterTypes()[0]; + if (!(pType.isArray() && pType.getComponentType().equals(byte.class)) + && !pType.isAssignableFrom(String.class)) + { + throw new IllegalArgumentException(annotation + " setter must take String or byte[]"); + } + } + else if (m.getParameterTypes().length == 0) + { + Class rType = m.getReturnType(); + if (!(rType.isArray() && rType.getComponentType().equals(byte.class)) + && !rType.isAssignableFrom(String.class)) + { + throw new IllegalArgumentException(annotation + " getter must return String or byte[]"); + } + } + } + } +} diff --git a/src/main/java/com/basho/riak/client/api/convert/reflection/AnnotationScanner.java b/src/main/java/com/basho/riak/client/api/convert/reflection/AnnotationScanner.java new file mode 100644 index 000000000..01e8e3110 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/reflection/AnnotationScanner.java @@ -0,0 +1,243 @@ +/* + * 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.convert.reflection; + +import com.basho.riak.client.api.annotations.RiakBucketName; +import com.basho.riak.client.api.annotations.RiakBucketType; +import com.basho.riak.client.api.annotations.RiakContentType; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.concurrent.Callable; + +import com.basho.riak.client.api.annotations.RiakIndex; +import com.basho.riak.client.api.annotations.RiakKey; +import com.basho.riak.client.api.annotations.RiakLastModified; +import com.basho.riak.client.api.annotations.RiakLinks; +import com.basho.riak.client.api.annotations.RiakTombstone; +import com.basho.riak.client.api.annotations.RiakUsermeta; +import com.basho.riak.client.api.annotations.RiakVClock; +import com.basho.riak.client.api.annotations.RiakVTag; +import java.util.LinkedList; + +/** + * A {@link Callable} that loops over a classes fields and pulls out the fields + * for {@link RiakUsermeta} and {@link RiakKey} + * + * @author russell + * + */ +public class AnnotationScanner implements Callable +{ + + @SuppressWarnings("rawtypes") + private final Class classToScan; + + @SuppressWarnings("rawtypes") + public AnnotationScanner(Class clazz) + { + this.classToScan = clazz; + } + + @Override + public AnnotationInfo call() throws Exception + { + + AnnotationInfo.Builder builder = new AnnotationInfo.Builder(); + + // This allows us to start at the top and walk down so that + // annotations are overridden in subsclasses + LinkedList classList = new LinkedList(); + Class currentClass = classToScan; + while (currentClass != Object.class) + { + classList.addFirst(currentClass); + currentClass = currentClass.getSuperclass(); + } + + for (Class c : classList) + { + + final Field[] fields = c.getDeclaredFields(); + + for (Field field : fields) + { + + if (field.isAnnotationPresent(RiakKey.class)) + { + builder.withRiakKeyField(field); + } + else if (field.isAnnotationPresent(RiakVClock.class)) + { + builder.withRiakVClockField(field); + } + else if (field.isAnnotationPresent(RiakTombstone.class)) + { + builder.withRiakTombstoneField(field); + } + else if (field.isAnnotationPresent(RiakUsermeta.class)) + { + builder.addRiakUsermetaField(field); + } + else if (field.isAnnotationPresent(RiakIndex.class)) + { + builder.addRiakIndexField(field); + } + else if (field.isAnnotationPresent(RiakLinks.class)) + { + builder.withRiakLinksField(field); + } + else if (field.isAnnotationPresent(RiakContentType.class)) + { + builder.withRiakContentTypeField(field); + } + else if (field.isAnnotationPresent(RiakLastModified.class)) + { + builder.withRiakLastModifiedField(field); + } + else if (field.isAnnotationPresent(RiakVTag.class)) + { + builder.withRiakVTagField(field); + } + else if (field.isAnnotationPresent(RiakBucketName.class)) + { + builder.withRiakBucketNameField(field); + } + else if (field.isAnnotationPresent(RiakBucketType.class)) + { + builder.withRiakBucketTypeField(field); + } + } + + final Method[] methods = c.getDeclaredMethods(); + for (Method method : methods) + { + + if (method.isAnnotationPresent(RiakKey.class)) + { + if (isSetter(method)) + { + builder.withRiakKeySetter(method); + } + else + { + builder.withRiakKeyGetter(method); + } + } + else if (method.isAnnotationPresent(RiakBucketName.class)) + { + if (isSetter(method)) + { + builder.withRiakBucketNameSetter(method); + } + else + { + builder.withRiakBucketNameGetter(method); + } + } + else if (method.isAnnotationPresent(RiakBucketType.class)) + { + if (isSetter(method)) + { + builder.withRiakBucketTypeSetter(method); + } + else + { + builder.withRiakBucketTypeGetter(method); + } + } + else if (method.isAnnotationPresent(RiakLinks.class)) + { + if (isSetter(method)) + { + builder.withRiakLinksSetter(method); + } + else + { + builder.withRiakLinksGetter(method); + } + } + else if (method.isAnnotationPresent(RiakVClock.class)) + { + if (isSetter(method)) + { + builder.withRiakVClockSetter(method); + } + else + { + builder.withRiakVClockGetter(method); + } + } + else if (method.isAnnotationPresent(RiakTombstone.class)) + { + if (isSetter(method)) + { + builder.withRiakTombstoneSetter(method); + } + else + { + builder.withRiakTombstoneGetter(method); + } + } + else if (method.isAnnotationPresent(RiakContentType.class)) + { + if (isSetter(method)) + { + builder.withRiakContentTypeSetter(method); + } + else + { + builder.withRiakContentTypeGetter(method); + } + } + else if (method.isAnnotationPresent(RiakVTag.class)) + { + if (isSetter(method)) + { + builder.withRiakVTagSetter(method); + } + else + { + throw new IllegalArgumentException("@RiakVTag annotated getter not supported"); + } + } + else if (method.isAnnotationPresent(RiakLastModified.class)) + { + if (isSetter(method)) + { + builder.withRiakLastModifiedSetter(method); + } + else + { + throw new IllegalArgumentException("@RiakLastModified annotated getter not supported"); + } + } + else if (method.isAnnotationPresent(RiakIndex.class)) + { + builder.addRiakIndexMethod(method); + } + else if (method.isAnnotationPresent(RiakUsermeta.class)) + { + builder.addRiakUsermetaMethod(method); + } + } + } + + return builder.build(); + } + + private boolean isSetter(Method m) + { + return m.getReturnType().equals(Void.TYPE); + } +} diff --git a/src/main/java/com/basho/riak/client/api/convert/reflection/AnnotationUtil.java b/src/main/java/com/basho/riak/client/api/convert/reflection/AnnotationUtil.java new file mode 100644 index 000000000..64f952630 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/reflection/AnnotationUtil.java @@ -0,0 +1,310 @@ +/* + * 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.convert.reflection; + +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.api.convert.ConversionException; +import com.basho.riak.client.core.query.UserMetadata.RiakUserMetadata; +import com.basho.riak.client.core.query.indexes.RiakIndexes; +import com.basho.riak.client.core.query.links.RiakLinks; +import com.basho.riak.client.core.util.BinaryValue; + +/** + * Static utility methods used to get/set annotated fields/methods. + * + * @author Brian Roach + * @since 2.0 + */ +public class AnnotationUtil +{ + private AnnotationUtil() {} + + /** + * Attempts to inject key as the value of the {@literal @RiakKey} + * annotated member of domainObject + * + * @param the type of domainObject + * @param domainObject the object to inject the key into + * @param key the key to inject + * @return domainObject with {@literal @RiakKey} annotated member + * set to key + * @throws ConversionException if there is a {@literal @RiakKey} annotated member + * but it cannot be set to the value of key + */ + public static T setKey(T domainObject, BinaryValue key) throws ConversionException + { + T obj = AnnotationHelper.getInstance().setRiakKey(domainObject, key); + return obj; + } + + /** + * Attempts to get a key from domainObject by looking for a + * {@literal @RiakKey} annotated member. If non-present it simply returns + * defaultKey + * + * @param the type of domainObject + * @param domainObject the object to search for a key + * @param defaultKey the pass through value that will get returned if no key + * found on domainObject + * @return either the value found on domainObject;s + * {@literal @RiakKey} member or defaultkey + */ + public static BinaryValue getKey(T domainObject, BinaryValue defaultKey) + { + BinaryValue key = getKey(domainObject); + if (key == null) + { + key = defaultKey; + } + return key; + } + + /** + * Attempts to get a key from domainObject by looking for a + * {@literal @RiakKey} annotated member. If non-present it simply returns + * null + * + * @param the type of domainObject + * @param domainObject the object to search for a key + * @return either the value found on domainObject;s + * {@literal @RiakKey} member or null + */ + public static BinaryValue getKey(T domainObject) + { + return AnnotationHelper.getInstance().getRiakKey(domainObject); + } + + public static T setBucketName(T domainObject, BinaryValue bucketName) + { + return AnnotationHelper.getInstance().setRiakBucketName(domainObject, bucketName); + } + + public static BinaryValue getBucketName(T domainObject, BinaryValue defaultBucketName) + { + BinaryValue bucketName = getBucketName(domainObject); + return bucketName != null ? bucketName : defaultBucketName; + } + + public static BinaryValue getBucketName(T domainObject) + { + return AnnotationHelper.getInstance().getRiakBucketName(domainObject); + } + + public static T setBucketType(T domainObject, BinaryValue bucketType) + { + return AnnotationHelper.getInstance().setRiakBucketType(domainObject, bucketType); + } + + public static BinaryValue getBucketType(T domainObject, BinaryValue defaultBucketType) + { + BinaryValue bucketType = getBucketType(domainObject); + return bucketType != null ? bucketType : defaultBucketType; + } + + public static BinaryValue getBucketType(T domainObject) + { + return AnnotationHelper.getInstance().getRiakBucketType(domainObject); + } + + public static boolean hasVClockAnnotation(T domainObject) + { + return AnnotationHelper.getInstance().hasRiakVClockAnnotation(domainObject); + } + + /** + * Attempts to inject vclock as the value of the + * {@literal @RiakVClock} annotated member of domainObject + * + * @param the type of domainObject + * @param domainObject the object to inject the key into + * @param vclock the vclock to inject + * @return domainObject with {@literal @RiakVClock} annotated member + * set to vclock + * @throws ConversionException if there is a {@literal @RiakVClock} annotated + * member but it cannot be set to the value of vclock + */ + public static T setVClock(T domainObject, VClock vclock) throws ConversionException + { + T obj = AnnotationHelper.getInstance().setRiakVClock(domainObject, vclock); + return obj; + } + + public static VClock getVClock(T domainObject, VClock defaultVClock) + { + VClock vclock = getVClock(domainObject); + return vclock != null ? vclock : defaultVClock; + } + + /** + * Attempts to get a vector clock from domainObject by looking + * for a {@literal @RiakVClock} annotated member. If non-present it simply + * returns null + * + * @param the type of domainObject + * @param domainObject the object to search for a key + * @return either the value found on domainObject;s + * {@literal @RiakVClock} member or null + */ + public static VClock getVClock(T domainObject) + { + return AnnotationHelper.getInstance().getRiakVClock(domainObject); + } + + /** + * Attempts to inject isTombstone as the value of the + * {@literal @RiakTombstone} annotated member of domainObject + * + * @param the type of domainObject + * @param domainObject the object to inject the key into + * @param isTombstone the boolean to inject + * @return domainObject with {@literal @RiakTombstone} annotated + * member set to isTombstone + * @throws ConversionException if there is a {@literal @RiakTombstone} annotated + * member but it cannot be set to the value of isTombstone + */ + public static T setTombstone(T domainObject, boolean isTombstone) throws ConversionException + { + T obj = AnnotationHelper.getInstance().setRiakTombstone(domainObject, isTombstone); + return obj; + } + + /** + * Attempts to get boolean from domainObject by looking for a + * {@literal @RiakTombstone} annotated member. If non-present it simply returns + * null + * + * @param the type of domainObject + * @param domainObject the object to search for a key + * @return either the value found on domainObject's + * {@literal @RiakTombstone} member or null + */ + public static Boolean getTombstone(T domainObject) + { + return AnnotationHelper.getInstance().getRiakTombstone(domainObject); + } + + /** + * Attempts to get all the riak indexes from a domain object by looking for + * a {@literal @RiakIndexes} annotated member. + *

+ * If no indexes are present, an empty RiakIndexes is returned.

+ * + * @param the type of the domain object + * @param domainObject the domain object + * @return a RiakIndexes + */ + public static RiakIndexes getIndexes(RiakIndexes container, T domainObject) + { + return AnnotationHelper.getInstance().getIndexes(container, domainObject); + } + + /** + * Attempts to populate a domain object with the contents of the supplied + * RiakIndexes by looking for a {@literal @RiakIndex} annotated member + * + * @param the type of the domain object + * @param indexes a populated RiakIndexes object. + * @param domainObject the domain object + * @return the domain object. + */ + public static T populateIndexes(RiakIndexes indexes, T domainObject) + { + return AnnotationHelper.getInstance().setIndexes(indexes, domainObject); + } + + /** + * Attempts to get the the Riak links from a domain object by looking for a + * {@literal @RiakLinks} annotated member. + * + * @param the domain object type + * @param container the RiakLinks container + * @param domainObject the domain object + * @return a Collection of RiakLink objects. + */ + public static RiakLinks getLinks(RiakLinks container, T domainObject) + { + return AnnotationHelper.getInstance().getLinks(container, domainObject); + } + + /** + * Attempts to populate a domain object with riak links by looking for a + * {@literal @RiakLinks} annotated member. + * + * @param the type of the domain object + * @param links a collection of RiakLink objects + * @param domainObject the domain object + * @return the domain object + */ + public static T populateLinks(RiakLinks links, T domainObject) + { + return AnnotationHelper.getInstance().setLinks(links, domainObject); + } + + /** + * Attempts to get the riak user metadata from a domain object by looking + * for a {@literal @RiakUsermeta} annotated field or getter method. + * + * @param the type of the domain object + * @param metaContainer the RiakUserMetadata container + * @param domainObject the domain object + * @return a Map containing the user metadata. + */ + public static RiakUserMetadata getUsermetaData(RiakUserMetadata metaContainer, T domainObject) + { + return AnnotationHelper.getInstance().getUsermetaData(metaContainer, domainObject); + } + + /** + * Attempts to populate a domain object with user metadata by looking for a + * {@literal @RiakUsermeta} annotated member. + * + * @param the type of the domain object + * @param usermetaData a Map of user metadata. + * @param domainObject the domain object. + * @return the domain object. + */ + public static T populateUsermeta(RiakUserMetadata usermetaData, T domainObject) + { + return AnnotationHelper.getInstance().setUsermetaData(usermetaData, domainObject); + } + + public static String getContentType(T domainObject, String defaultContentType) + { + String type = getContentType(domainObject); + return type != null ? type : defaultContentType; + } + + public static String getContentType(T domainObject) + { + return AnnotationHelper.getInstance().getRiakContentType(domainObject); + } + + public static T setContentType(T domainObject, String contentType) + { + return AnnotationHelper.getInstance().setRiakContentType(domainObject, contentType); + } + + public static T setVTag(T domainObject, String vtag) + { + return AnnotationHelper.getInstance().setRiakVTag(domainObject, vtag); + } + + public static T setLastModified(T domainObject, Long lastModified) + { + return AnnotationHelper.getInstance().setRiakLastModified(domainObject, lastModified); + } + +} diff --git a/src/main/java/com/basho/riak/client/api/convert/reflection/ClassUtil.java b/src/main/java/com/basho/riak/client/api/convert/reflection/ClassUtil.java new file mode 100644 index 000000000..95288bcf5 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/reflection/ClassUtil.java @@ -0,0 +1,90 @@ +/* + * 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.convert.reflection; + +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +/** + * Reflection/class utilities. + * + * @author russell + * @author Brian Roach + * @since 1.0 + */ +public final class ClassUtil { + + private ClassUtil() {} + + /** + * Make the {@link Member} accessible if possible, throw + * {@link IllegalArgumentException} is not. + * + * @param member the member to check + * @return the member + * @throws IllegalArgumentException if cannot set accessibility + */ + public static T checkAndFixAccess(T member) { + com.fasterxml.jackson.databind.util.ClassUtil.checkAndFixAccess(member); + return member; + } + + public static void setFieldValue(Field field, Object obj, Object value) { + try { + field.set(obj, value); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Unable to set Riak annotated field value", e); + } + } + + public static Object getFieldValue(Field f, T obj) { + Object value = null; + try { + value = f.get(obj); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Unable to get Riak annotated field value", e); + } + + return value; + } + + public static Object getMethodValue(Method m, T obj) { + Object value = null; + try { + value = m.invoke(obj); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Unable to get Riak annotated method value", e); + } catch (IllegalArgumentException e) { + throw new IllegalStateException("Unable to get Riak annotated method value", e); + } catch (InvocationTargetException e) { + throw new IllegalStateException("Unable to get Riak annotated method value", e); + } + + return value; + } + + public static void setMethodValue(Method m, T obj, Object value) { + try { + m.invoke(obj, value); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Unable to set Riak annotated method value", e); + } catch (IllegalArgumentException e) { + throw new IllegalStateException("Unable to set Riak annotated method value", e); + } catch (InvocationTargetException e) { + throw new IllegalStateException("Unable to set Riak annotated method value", e); + } + } + +} diff --git a/src/main/java/com/basho/riak/client/api/convert/reflection/RiakIndexField.java b/src/main/java/com/basho/riak/client/api/convert/reflection/RiakIndexField.java new file mode 100644 index 000000000..38f5bfe3f --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/reflection/RiakIndexField.java @@ -0,0 +1,130 @@ +/* + * 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.convert.reflection; + +import java.lang.reflect.Field; + +import com.basho.riak.client.api.annotations.RiakIndex; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.math.BigInteger; +import java.util.Set; + +/** + * @author russell + * + */ +public class RiakIndexField { + + public enum FieldType { LONG, SET_LONG, STRING, SET_STRING, RAW, SET_RAW, BIG_INT, SET_BIG_INT } + + private final Field field; + private final String indexName; + private final FieldType type; + + /** + * The field that is to be wrapped + * + * @param field + */ + public RiakIndexField(final Field field) { + type = validateAndGetType(field); + this.field = field; + this.indexName = field.getAnnotation(RiakIndex.class).name(); + + if (indexName.isEmpty()) + { + throw new IllegalArgumentException("@RiakIndex must have 'name' parameter"); + } + else if ((type == FieldType.RAW || type == FieldType.SET_RAW) && + (!indexName.endsWith("_int") && !indexName.endsWith("_bin"))) + { + throw new IllegalArgumentException("@RiakIndex annotated byte[] must declare full indexname"); + } + } + + /** + * @return the field + */ + public Field getField() { + return field; + } + + /** + * @return the indexName + */ + public String getIndexName() { + return indexName; + } + + public FieldType getFieldType() { + return type; + } + + private FieldType validateAndGetType(Field f) { + + if (f != null) + { + Type t = f.getGenericType(); + if (t instanceof ParameterizedType) + { + ParameterizedType pType = (ParameterizedType)t; + if (pType.getRawType().equals(Set.class)) + { + Class genericType = (Class)pType.getActualTypeArguments()[0]; + if (String.class.equals(genericType)) + { + return FieldType.SET_STRING; + } + else if (Long.class.equals(genericType)) + { + return FieldType.SET_LONG; + } + else if (BigInteger.class.equals(genericType)) + { + return FieldType.SET_BIG_INT; + } + else if (genericType.isArray() && genericType.getComponentType().equals(byte.class)) + { + return FieldType.SET_RAW; + } + } + } + else + { + Class c = f.getType(); + + if (c.equals(String.class)) + { + return FieldType.STRING; + } + else if (c.equals(Long.class) || c.equals(long.class)) + { + return FieldType.LONG; + } + else if (c.equals(BigInteger.class)) + { + return FieldType.BIG_INT; + } + else if (c.isArray() && c.getComponentType().equals(byte.class)) + { + return FieldType.RAW; + } + } + throw new IllegalArgumentException("@RiakIndex must be a single or Set<> of Long, BigInteger, or String: " + + f); + } + throw new IllegalArgumentException("Field can not be null."); + } +} diff --git a/src/main/java/com/basho/riak/client/api/convert/reflection/RiakIndexMethod.java b/src/main/java/com/basho/riak/client/api/convert/reflection/RiakIndexMethod.java new file mode 100644 index 000000000..0523ca33b --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/reflection/RiakIndexMethod.java @@ -0,0 +1,196 @@ +/* + * 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.convert.reflection; + +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Set; + +import com.basho.riak.client.api.annotations.RiakIndex; +import java.math.BigInteger; + +/** + * + * @author gmedina + */ +public class RiakIndexMethod +{ + public enum MethodType { LONG_SETTER, LONG_GETTER, SET_LONG_GETTER, SET_LONG_SETTER, + STRING_GETTER, STRING_SETTER, SET_STRING_GETTER, SET_STRING_SETTER, + RAW_SETTER, RAW_GETTER, SET_RAW_SETTER, SET_RAW_GETTER, + BIG_INT_SETTER, BIG_INT_GETTER, SET_BIG_INT_GETTER, SET_BIG_INT_SETTER + } + + private final Method method; + private final String indexName; + private final MethodType methodType; + + /** + * The method that is to be wrapped + * + * @param method + */ + public RiakIndexMethod(final Method method) + { + methodType = validateAndGetReturnType(method); + + this.method = method; + this.indexName = method.getAnnotation(RiakIndex.class).name(); + + if (indexName.isEmpty()) + { + throw new IllegalArgumentException("@RiakIndex must have 'name' parameter"); + } + else if ( (methodType == MethodType.RAW_GETTER || methodType == MethodType.RAW_SETTER || + methodType == MethodType.SET_RAW_GETTER || methodType == MethodType.SET_RAW_SETTER) + && (!indexName.endsWith("_bin") && !indexName.endsWith("_int")) ) + { + throw new IllegalArgumentException("@RiakIndex annotated byte[] setter/getter must declare full indexname"); + } + } + + /** + * @return the method + */ + public Method getMethod() + { + return method; + } + + /** + * @return the indexName + */ + public String getIndexName() + { + return indexName; + } + + /** + * @return the type + */ + public MethodType getMethodType() + { + return methodType; + } + + private MethodType validateAndGetReturnType(Method m) + { + if (m != null) + { + if (m.getReturnType().equals(Void.TYPE)) + { + // It's a setter + Type[] genericParameterTypes = m.getGenericParameterTypes(); + Type t = genericParameterTypes[0]; + if (t instanceof ParameterizedType) + { + ParameterizedType pType = (ParameterizedType)t; + if (pType.getRawType().equals(Set.class)) + { + Class genericType = (Class)pType.getActualTypeArguments()[0]; + if (String.class.equals(genericType)) + { + return MethodType.SET_STRING_SETTER; + } + else if (Long.class.equals(genericType)) + { + return MethodType.SET_LONG_SETTER; + } + else if (BigInteger.class.equals(genericType)) + { + return MethodType.SET_BIG_INT_SETTER; + } + else if (genericType.isArray() && genericType.getComponentType().equals(byte.class)) + { + return MethodType.SET_RAW_SETTER; + } + } + } + else + { + Class c = m.getParameterTypes()[0]; + if (c.equals(String.class)) + { + return MethodType.STRING_SETTER; + } + else if (c.equals(Long.class) || c.equals(long.class)) + { + return MethodType.LONG_SETTER; + } + else if (c.equals(BigInteger.class)) + { + return MethodType.BIG_INT_SETTER; + } + else if (c.isArray() && c.getComponentType().equals(byte.class)) + { + return MethodType.RAW_SETTER; + } + } + throw new IllegalArgumentException("@RiakIndex setter must take a single or Set<> of String, Long, or byte[]: " + m); + } + else + { + // It's a getter + Type t = m.getGenericReturnType(); + if (t instanceof ParameterizedType) + { + ParameterizedType pType = (ParameterizedType)t; + if (pType.getRawType().equals(Set.class)) + { + Class genericType = (Class)pType.getActualTypeArguments()[0]; + if (String.class.equals(genericType)) + { + return MethodType.SET_STRING_GETTER; + } + else if (Long.class.equals(genericType)) + { + return MethodType.SET_LONG_GETTER; + } + else if (BigInteger.class.equals(genericType)) + { + return MethodType.SET_BIG_INT_GETTER; + } + else if (genericType.isArray() && genericType.getComponentType().equals(byte.class)) + { + return MethodType.SET_RAW_GETTER; + } + } + } + else + { + Class c = m.getReturnType(); + if (c.equals(String.class)) + { + return MethodType.STRING_GETTER; + } + else if (c.equals(Long.class) || c.equals(long.class)) + { + return MethodType.LONG_GETTER; + } + else if (c.equals(BigInteger.class)) + { + return MethodType.BIG_INT_SETTER; + } + else if (c.isArray() && c.getComponentType().equals(byte.class)) + { + return MethodType.RAW_GETTER; + } + } + throw new IllegalArgumentException("@RiakIndex getter must return a single or Set<> of String, Long, or byte[]: " +m ); + } + } + throw new IllegalArgumentException("Method can not be null."); + } +} diff --git a/src/main/java/com/basho/riak/client/api/convert/reflection/UsermetaField.java b/src/main/java/com/basho/riak/client/api/convert/reflection/UsermetaField.java new file mode 100644 index 000000000..d2aa91136 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/reflection/UsermetaField.java @@ -0,0 +1,109 @@ +/* + * 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.convert.reflection; + +import com.basho.riak.client.api.annotations.RiakUsermeta; +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Map; + +/** + * Convenience wrapper for a String field that is annotated with + * {@link RiakUsermeta} + * + * @author Russell Brown + * @author Brian Roach + */ +public class UsermetaField { + + public enum FieldType { STRING, MAP } + + private final Field field; + private final String usermetaDataKey; + private final FieldType fieldType; + + /** + * The field that is to be wrapped + * + * @param field + */ + public UsermetaField(final Field field) { + this.fieldType = validateAndGetType(field); + this.field = field; + this.usermetaDataKey = field.getAnnotation(RiakUsermeta.class).key(); + + if (fieldType == FieldType.STRING && "".equals(usermetaDataKey)) + { + throw new IllegalArgumentException("@RiakUsermeta annotated String must include key: " + field); + } + + } + + /** + * @return the field + */ + public Field getField() { + return field; + } + + /** + * + * @return the field type + */ + public FieldType getFieldType() { + return fieldType; + } + + /** + * @return the usermetaDataKey + */ + public String getUsermetaDataKey() { + return usermetaDataKey; + } + + private FieldType validateAndGetType(Field f) + { + if (f != null) + { + Type t = f.getGenericType(); + if (t instanceof ParameterizedType) + { + ParameterizedType pType = (ParameterizedType)t; + if (pType.getRawType().equals(Map.class)) + { + Type genericTypes[] = pType.getActualTypeArguments(); + if (String.class.equals(genericTypes[0]) && String.class.equals(genericTypes[1])) + { + return FieldType.MAP; + } + } + + } + else + { + if (f.getType().equals(String.class)) + { + return FieldType.STRING; + } + } + + throw new IllegalArgumentException("@RiakUsermeta must be a Map or single String: " + + f); + } + throw new IllegalArgumentException("Field can not be null."); + + } + +} diff --git a/src/main/java/com/basho/riak/client/api/convert/reflection/UsermetaMethod.java b/src/main/java/com/basho/riak/client/api/convert/reflection/UsermetaMethod.java new file mode 100644 index 000000000..9b765f830 --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/convert/reflection/UsermetaMethod.java @@ -0,0 +1,137 @@ +/* + * 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.convert.reflection; + +import com.basho.riak.client.api.annotations.RiakUsermeta; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Map; + +/** + * + * @author Brian Roach + */ +public class UsermetaMethod +{ + public enum MethodType { STRING_GETTER, STRING_SETTER, MAP_GETTER, MAP_SETTER } + + private final Method method; + private final String usermetaDataKey; + private final MethodType methodType; + + public UsermetaMethod(Method method) + { + this.methodType = validateAndGetReturnType(method); + this.method = method; + this.usermetaDataKey = method.getAnnotation(RiakUsermeta.class).key(); + + if ((methodType == MethodType.STRING_GETTER || methodType == MethodType.STRING_SETTER) && + "".equals(usermetaDataKey)) + { + throw new IllegalArgumentException("@RiakUsermeta annotated method must include key: " + + method); + } + } + + /** + * @return the method + */ + public Method getMethod() + { + return method; + } + + /** + * @return the usermetaDataKey + */ + public String getUsermetaDataKey() + { + return usermetaDataKey; + } + + /** + * Get the return type. + * @return the return type for this method. + */ + public MethodType getMethodType() + { + return methodType; + } + + private MethodType validateAndGetReturnType(Method m) + { + if (m != null) + { + if (m.getReturnType().equals(Void.TYPE)) // it's a setter + { + Type[] genericParameterTypes = m.getGenericParameterTypes(); + Type t = genericParameterTypes[0]; + if (t instanceof ParameterizedType) + { + ParameterizedType pType = (ParameterizedType)t; + if (pType.getRawType().equals(Map.class)) + { + Type genericTypes[] = pType.getActualTypeArguments(); + if (String.class.equals(genericTypes[0]) && String.class.equals(genericTypes[1])) + { + return MethodType.MAP_SETTER; + } + } + } + else + { + t = m.getParameterTypes()[0]; + if (t.equals(String.class)) + { + return MethodType.STRING_SETTER; + } + } + throw new IllegalArgumentException("@RiakUsermeta setter must take a Map or String: " + m); + } + else // it's a getter + { + Type t = m.getGenericReturnType(); + if (t instanceof ParameterizedType) + { + ParameterizedType pType = (ParameterizedType)t; + if (pType.getRawType().equals(Map.class)) + { + Type genericTypes[] = pType.getActualTypeArguments(); + if (String.class.equals(genericTypes[0]) && String.class.equals(genericTypes[1])) + { + return MethodType.MAP_GETTER; + } + } + + } + else + { + if (m.getReturnType().equals(String.class)) + { + return MethodType.STRING_GETTER; + } + } + + throw new IllegalArgumentException("@RiakUsermeta getter must return a Map or String: " + m); + } + } + throw new IllegalArgumentException("Method can not be null."); + + } + +} diff --git a/src/main/java/com/basho/riak/client/api/doc-files/client-image.png b/src/main/java/com/basho/riak/client/api/doc-files/client-image.png new file mode 100644 index 000000000..0a849eca0 Binary files /dev/null and b/src/main/java/com/basho/riak/client/api/doc-files/client-image.png differ diff --git a/src/main/java/com/basho/riak/client/api/package-info.java b/src/main/java/com/basho/riak/client/api/package-info.java new file mode 100644 index 000000000..8db38483d --- /dev/null +++ b/src/main/java/com/basho/riak/client/api/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the main RiakClient used to query Riak. + */ +package com.basho.riak.client.api; \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/core/ConnectionFailedException.java b/src/main/java/com/basho/riak/client/core/ConnectionFailedException.java new file mode 100644 index 000000000..a39e45b45 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/ConnectionFailedException.java @@ -0,0 +1,36 @@ +/* + * 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.core; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public class ConnectionFailedException extends Exception +{ + private static final long serialVersionUID = -382975266386787678L; + + public ConnectionFailedException(Throwable cause) + { + super(cause); + } + + public ConnectionFailedException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/src/main/java/com/basho/riak/client/core/DefaultNodeManager.java b/src/main/java/com/basho/riak/client/core/DefaultNodeManager.java new file mode 100644 index 000000000..41f76fc2f --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/DefaultNodeManager.java @@ -0,0 +1,210 @@ +/* + * 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.core; + +import com.basho.riak.client.core.RiakNode.State; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * The default {@link NodeManager} used by {@link RiakCluster} if none is + * specified. + * + * This NodeManager round-robins through a list of {@link RiakNode}s and attempts + * to execute the operation passed to it. If a node reports that it is + * health checking it is removed from the list until it sends an update that it + * is again running. If the selected node cannot accept the operation because all + * connections are in use or it unable to make a new connection, the next node in + * the list is tried until either the operation is accepted or all nodes have + * been tried. If no nodes are able to accept the operation its setException() + * method is called with a {@link NoNodesAvailableException}. + * + * @author Brian Roach + * @since 2.0 + */ +public class DefaultNodeManager implements NodeManager, NodeStateListener +{ + private final ArrayList healthy = new ArrayList(); + private final ArrayList unhealthy = new ArrayList(); + private final AtomicInteger index = new AtomicInteger(); + private final Logger logger = LoggerFactory.getLogger(DefaultNodeManager.class); + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + + @Override + public void init(List nodes) + { + try + { + lock.writeLock().lock(); + healthy.addAll(nodes); + } + finally + { + lock.writeLock().unlock(); + } + } + + @Override + public void executeOnNode(FutureOperation operation, RiakNode previousNode) + { + try + { + lock.readLock().lock(); + boolean executed = false; + if (healthy.size() > 1) + { + int startIndex = index.getAndIncrement(); + int currentIndex = startIndex; + + do + { + if (healthy.get(Math.abs(currentIndex % healthy.size())).execute(operation)) + { + executed = true; + break; + } + currentIndex++; + } + while (Math.abs(currentIndex % healthy.size()) != Math.abs(startIndex % healthy.size())); + } + else if (healthy.size() == 1) + { + executed = healthy.get(0).execute(operation); + } + + if (!executed) + { + operation.setException(new NoNodesAvailableException()); + } + } + finally + { + lock.readLock().unlock(); + } + } + + @Override + public void nodeStateChanged(RiakNode node, State state) + { + switch (state) + { + case RUNNING: + try + { + lock.writeLock().lock(); + if (unhealthy.remove(node)) + { + healthy.add(node); + logger.info("NodeManager moved node to healthy list; {}:{}", + node.getRemoteAddress(), node.getPort()); + } + } + finally + { + lock.writeLock().unlock(); + } + break; + case HEALTH_CHECKING: + try + { + lock.writeLock().lock(); + if (healthy.remove(node)) + { + unhealthy.add(node); + logger.info("NodeManager moved node to unhealthy list; {}:{}", + node.getRemoteAddress(), node.getPort()); + } + } + finally + { + lock.writeLock().unlock(); + } + break; + case SHUTTING_DOWN: + case SHUTDOWN: + boolean removed = false; + try + { + lock.writeLock().lock(); + removed = healthy.remove(node); + if (!removed) + { + unhealthy.remove(node); + } + } + finally + { + lock.writeLock().unlock(); + } + if (removed) + { + logger.info("NodeManager removed node due to it shutting down; {}:{}", + node.getRemoteAddress(), node.getPort()); + } + break; + default: + break; + } + } + + @Override + public void addNode(RiakNode newNode) + { + try + { + lock.writeLock().lock(); + healthy.add(newNode); + } + finally + { + lock.writeLock().unlock(); + } + + } + + @Override + public boolean removeNode(RiakNode node) + { + boolean removed; + try + { + lock.writeLock().lock(); + removed = healthy.remove(node); + if (!removed) + { + removed = unhealthy.remove(node); + } + } + finally + { + lock.writeLock().unlock(); + } + + if (removed) + { + node.removeStateListener(this); + node.shutdown(); + logger.info("NodeManager removed and shutdown node; {}:{}", + node.getRemoteAddress(), node.getPort()); + } + return removed; + } +} diff --git a/src/main/java/com/basho/riak/client/core/FutureOperation.java b/src/main/java/com/basho/riak/client/core/FutureOperation.java new file mode 100644 index 000000000..87c38a37b --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/FutureOperation.java @@ -0,0 +1,344 @@ +/* + * 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.core; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @author Brian Roach + * @param The type the operation returns + * @param The protocol type returned + * @param Query info type + * @since 2.0 + */ +public abstract class FutureOperation implements RiakFuture +{ + + private enum State + { + CREATED, WRITTEN, RETRY, COMPLETE, CANCELLED + } + + private final Logger logger = LoggerFactory.getLogger(FutureOperation.class); + private final CountDownLatch latch = new CountDownLatch(1); + private volatile OperationRetrier retrier; + private volatile int remainingTries = 1; + private volatile List rawResponse = new LinkedList(); + private volatile Throwable exception; + private volatile T converted; + private volatile State state = State.CREATED; + private volatile RiakNode lastNode; + + private final ReentrantLock listenersLock = new ReentrantLock(); + private final HashSet> listeners = + new HashSet>(); + private volatile boolean listenersFired = false; + + @Override + public void addListener(RiakFutureListener listener) + { + + boolean fireNow = false; + listenersLock.lock(); + try + { + if (listenersFired) + { + fireNow = true; + } + else + { + listeners.add(listener); + } + } + finally + { + listenersLock.unlock(); + } + + // the future has already been completed, fire on caller's thread + if (fireNow) + { + listener.handle(this); + } + + } + + @Override + public void removeListener(RiakFutureListener listener) + { + listenersLock.lock(); + try + { + if (!listenersFired) + { + listeners.remove(listener); + } // else, we don't care, they've already been fired + } + finally + { + listenersLock.unlock(); + } + } + + private void fireListeners() + { + + boolean fireNow = false; + listenersLock.lock(); + try + { + if (!listenersFired) + { + fireNow = true; + listenersFired = true; + } + } + finally + { + listenersLock.unlock(); + } + + if (fireNow) + { + for (RiakFutureListener listener : listeners) + { + listener.handle(this); + } + } + + } + + final synchronized void setRetrier(OperationRetrier retrier, int numTries) + { + stateCheck(State.CREATED); + this.retrier = retrier; + this.remainingTries = numTries; + } + + final RiakNode getLastNode() + { + return lastNode; + } + + final void setLastNode(RiakNode node) + { + this.lastNode = node; + } + + // Exposed for testing. + public synchronized final void setResponse(RiakMessage rawResponse) + { + stateCheck(State.CREATED, State.WRITTEN, State.RETRY); + U decodedMessage = decode(rawResponse); + this.rawResponse.add(decodedMessage); + exception = null; + if (done(decodedMessage)) + { + remainingTries--; + if (retrier != null) + { + retrier.operationComplete(this, remainingTries); + } + state = State.COMPLETE; + latch.countDown(); + fireListeners(); + } + } + + /** + * Detect when the streaming operation is finished + * + * @param message raw message + * @return returns true if this is the last message in the streaming operation + */ + protected boolean done(U message) + { + return true; + } + + synchronized final void setException(Throwable t) + { + stateCheck(State.CREATED, State.WRITTEN, State.RETRY); + this.exception = t; + + remainingTries--; + if (remainingTries == 0) + { + state = State.COMPLETE; + latch.countDown(); + fireListeners(); + } + else + { + state = State.RETRY; + } + + if (retrier != null) + { + retrier.operationFailed(this, remainingTries); + } + + } + + public synchronized final Object channelMessage() + { + Object message = createChannelMessage(); + state = State.WRITTEN; + return message; + } + + @Override + public final boolean cancel(boolean mayInterruptIfRunning) + { + return false; + } + + @Override + public final boolean isCancelled() + { + return state == State.CANCELLED; + } + + @Override + public final boolean isDone() + { + return state == State.COMPLETE; + } + + @Override + public final boolean isSuccess() + { + return (isDone() && exception == null); + } + + @Override + public final Throwable cause() + { + if (isSuccess()) + { + return null; + } + else + { + return exception; + } + } + + @Override + public final T get() throws InterruptedException, ExecutionException + { + latch.await(); + + if (exception != null) + { + throw new ExecutionException(exception); + } + else if(null == converted) + { + converted = convert(rawResponse); + + } + + return converted; + } + + @Override + public final T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException + { + boolean succeed = latch.await(timeout, unit); + + if (!succeed) + { + throw new TimeoutException(); + } + else if (exception != null) + { + throw new ExecutionException(exception); + } + else if (null == converted) + { + converted = convert(rawResponse); + } + + return converted; + } + + @Override + public final T getNow() + { + if (latch.getCount() < 1) + { + if (null == converted) + { + converted = convert(rawResponse); + } + + return converted; + } + else + { + return null; + } + } + + @Override + public final void await() throws InterruptedException + { + latch.await(); + } + + @Override + public final void await(long timeout, TimeUnit unit) throws InterruptedException + { + latch.await(timeout, unit); + } + + + private void stateCheck(State... allowedStates) + { + if (Arrays.binarySearch(allowedStates, state) < 0) + { + logger.debug("IllegalStateException; required: {} current: {} ", + Arrays.toString(allowedStates), state); + throw new IllegalStateException("required: " + + Arrays.toString(allowedStates) + + " current: " + state); + } + } + + abstract protected T convert(List rawResponse); + + abstract protected RiakMessage createChannelMessage(); + + abstract protected U decode(RiakMessage rawMessage); + + @Override + abstract public S getQueryInfo(); + +} diff --git a/src/main/java/com/basho/riak/client/core/HealthCheckFactory.java b/src/main/java/com/basho/riak/client/core/HealthCheckFactory.java new file mode 100644 index 000000000..6eae41db2 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/HealthCheckFactory.java @@ -0,0 +1,34 @@ +/* + * 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.core; + +import com.basho.riak.client.core.netty.HealthCheckDecoder; + +/** + * Provides a simple factory method for HealhCheckDecoders. + *

+ * Because of the stateful nature of the codec used for performing health checks, + * a new instance needs to be created each time. When configuring a RiakNode, a + * HealthCheckFactory is suppled for this purpose. + *

+ * @author Brian Roach + * @since 2.0 + */ +public interface HealthCheckFactory +{ + HealthCheckDecoder makeDecoder(); +} diff --git a/src/main/java/com/basho/riak/pbc/RiakError.java b/src/main/java/com/basho/riak/client/core/NoNodesAvailableException.java similarity index 56% rename from src/main/java/com/basho/riak/pbc/RiakError.java rename to src/main/java/com/basho/riak/client/core/NoNodesAvailableException.java index cb28c5d5a..a3f6fcce4 100644 --- a/src/main/java/com/basho/riak/pbc/RiakError.java +++ b/src/main/java/com/basho/riak/client/core/NoNodesAvailableException.java @@ -1,32 +1,26 @@ -/** - * This file is part of riak-java-pb-client - * - * Copyright (c) 2010 by Trifork +/* + * 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 + * + * 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.pbc; - -import java.io.IOException; - -import com.basho.riak.pbc.RPB.RpbErrorResp; - -@SuppressWarnings("serial") -public class RiakError extends IOException { - - public RiakError(RpbErrorResp err) { - super(err.getErrmsg().toStringUtf8()); - } + */ +package com.basho.riak.client.core; +/** + * + * @author Brian Roach + * @since 2.0 + */ +public class NoNodesAvailableException extends Exception +{ + } diff --git a/src/main/java/com/basho/riak/client/core/NodeManager.java b/src/main/java/com/basho/riak/client/core/NodeManager.java new file mode 100644 index 000000000..f175536df --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/NodeManager.java @@ -0,0 +1,31 @@ +/* + * 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.core; + +import java.util.List; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public interface NodeManager extends NodeStateListener +{ + void init(List nodes); + void executeOnNode(FutureOperation operation, RiakNode previousNode); + void addNode(RiakNode newNode); + boolean removeNode(RiakNode node); +} diff --git a/src/main/java/com/basho/riak/client/core/NodeStateListener.java b/src/main/java/com/basho/riak/client/core/NodeStateListener.java new file mode 100644 index 000000000..d299ef2f9 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/NodeStateListener.java @@ -0,0 +1,26 @@ +/* + * 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.core; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public interface NodeStateListener +{ + void nodeStateChanged(RiakNode node, RiakNode.State state); +} diff --git a/src/main/java/com/basho/riak/client/core/OperationRetrier.java b/src/main/java/com/basho/riak/client/core/OperationRetrier.java new file mode 100644 index 000000000..becfcdb9c --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/OperationRetrier.java @@ -0,0 +1,27 @@ +/* + * 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.core; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public interface OperationRetrier +{ + public void operationFailed(FutureOperation operation, int remainingRetries); + public void operationComplete(FutureOperation operation, int remainingRetries); +} diff --git a/src/main/java/com/basho/riak/client/core/RiakCluster.java b/src/main/java/com/basho/riak/client/core/RiakCluster.java new file mode 100644 index 000000000..5635bdb94 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/RiakCluster.java @@ -0,0 +1,559 @@ +/* + * 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.core; + + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * A modeled Riak Cluster. + * + *

+ * This class represents a Riak Cluster upon which operations are executed. + * Instances are created using the {@link Builder} + *

+ * + * @author Brian Roach + * @since 2.0 + */ +public class RiakCluster implements OperationRetrier, NodeStateListener +{ + enum State { CREATED, RUNNING, SHUTTING_DOWN, SHUTDOWN } + private final Logger logger = LoggerFactory.getLogger(RiakCluster.class); + private final int executionAttempts; + private final NodeManager nodeManager; + private final AtomicInteger inFlightCount = new AtomicInteger(); + private final ScheduledExecutorService executor; + private final Bootstrap bootstrap; + private final List nodeList; + private final ReentrantReadWriteLock nodeListLock = new ReentrantReadWriteLock(); + private final LinkedBlockingQueue retryQueue = + new LinkedBlockingQueue(); + private final List stateListeners = + Collections.synchronizedList(new LinkedList()); + + + private volatile ScheduledFuture shutdownFuture; + private volatile ScheduledFuture retrierFuture; + + private volatile State state; + private final CountDownLatch shutdownLatch = new CountDownLatch(1); + + private RiakCluster(Builder builder) throws UnknownHostException + { + this.executionAttempts = builder.executionAttempts; + + if (null == builder.nodeManager) + { + nodeManager = new DefaultNodeManager(); + } + else + { + this.nodeManager = builder.nodeManager; + } + + if (builder.bootstrap != null) + { + this.bootstrap = builder.bootstrap.clone(); + } + else + { + this.bootstrap = new Bootstrap() + .group(new NioEventLoopGroup()) + .channel(NioSocketChannel.class); + } + + if (builder.executor != null) + { + executor = builder.executor; + } + else + { + // We still need an executor if none was provided. + executor = new ScheduledThreadPoolExecutor(2); + } + + nodeList = new ArrayList(builder.riakNodes.size()); + for (RiakNode node : builder.riakNodes) + { + node.setExecutor(executor); + node.setBootstrap(bootstrap); + node.addStateListener(nodeManager); + nodeList.add(node); + } + + // Pass a *copy* of the list to the NodeManager + nodeManager.init(new ArrayList(nodeList)); + state = State.CREATED; + } + + private void stateCheck(State... allowedStates) + { + if (Arrays.binarySearch(allowedStates, state) < 0) + { + logger.debug("IllegalStateException; required: {} current: {} ", + Arrays.toString(allowedStates), state); + throw new IllegalStateException("required: " + + Arrays.toString(allowedStates) + + " current: " + state ); + } + } + + public synchronized void start() + { + stateCheck(State.CREATED); + + // Completely unneeded *right now* but operating on a copy + // of the nodeList defensively prevents a deadlock occuring + // if a callback were to try and modify the list. + for (RiakNode node : getNodes()) + { + node.start(); + } + + retrierFuture = executor.schedule(new RetryTask(), 0, TimeUnit.SECONDS); + logger.info("RiakCluster is starting."); + state = State.RUNNING; + } + + public synchronized Future shutdown() + { + stateCheck(State.RUNNING); + logger.info("RiakCluster is shutting down."); + state = State.SHUTTING_DOWN; + + // Wait for all in-progress operations to drain + // then shut down nodes. + shutdownFuture = executor.scheduleWithFixedDelay(new ShutdownTask(), + 500, 500, + TimeUnit.MILLISECONDS); + + return new Future() { + @Override + public boolean cancel(boolean mayInterruptIfRunning) + { + return false; + } + @Override + public Boolean get() throws InterruptedException + { + shutdownLatch.await(); + return true; + } + @Override + public Boolean get(long timeout, TimeUnit unit) throws InterruptedException + { + return shutdownLatch.await(timeout, unit); + } + @Override + public boolean isCancelled() + { + return false; + } + @Override + public boolean isDone() + { + return shutdownLatch.getCount() <= 0; + } + + }; + + } + + public RiakFuture execute(FutureOperation operation) + { + stateCheck(State.RUNNING); + operation.setRetrier(this, executionAttempts); + inFlightCount.incrementAndGet(); + this.execute(operation, null); + return operation; + } + + private void execute(FutureOperation operation, RiakNode previousNode) + { + nodeManager.executeOnNode(operation, previousNode); + } + + /** + * Adds a {@link RiakNode} to this cluster. + * The node can not have been started nor have its Bootstrap or Executor + * asSet. + * @param node the RiakNode to add + * @throws java.net.UnknownHostException if the RiakNode's hostname cannot be resolved + * @throws IllegalArgumentException if the node's Bootstrap or Executor are already asSet. + */ + public void addNode(RiakNode node) throws UnknownHostException + { + stateCheck(State.CREATED, State.RUNNING); + node.setExecutor(executor); + node.setBootstrap(bootstrap); + + try + { + nodeListLock.writeLock().lock(); + nodeList.add(node); + for (NodeStateListener listener : stateListeners) + { + node.addStateListener(listener); + } + } + finally + { + nodeListLock.writeLock().unlock(); + } + + nodeManager.addNode(node); + } + + /** + * Removes the provided node from the cluster. + * @param node + * @return true if the node was in the cluster, false otherwise. + */ + public boolean removeNode(RiakNode node) + { + stateCheck(State.CREATED, State.RUNNING); + boolean removed = false; + try + { + nodeListLock.writeLock().lock(); + removed = nodeList.remove(node); + for (NodeStateListener listener : stateListeners) + { + node.removeStateListener(listener); + } + } + finally + { + nodeListLock.writeLock().unlock(); + } + nodeManager.removeNode(node); + return removed; + } + + /** + * Returns a copy of the list of nodes in this cluster. + * @return A copy of the list of RiakNodes + */ + public List getNodes() + { + stateCheck(State.CREATED, State.RUNNING, State.SHUTTING_DOWN); + try + { + nodeListLock.readLock().lock(); + return new ArrayList(nodeList); + } + finally + { + nodeListLock.readLock().unlock(); + } + + } + + int inFlightCount() + { + return inFlightCount.get(); + } + + @Override + public void nodeStateChanged(RiakNode node, RiakNode.State state) + { + // We only listen for state changes after telling all the nodes + // to shutdown. + if (state == RiakNode.State.SHUTDOWN) + { + logger.debug("Node state changed to shutdown; {}:{}", node.getRemoteAddress(), node.getPort()); + try + { + nodeListLock.writeLock().lock(); + nodeList.remove(node); + logger.debug("Active nodes remaining: {}", nodeList.size()); + + if (nodeList.isEmpty()) + { + this.state = State.SHUTDOWN; + executor.shutdown(); + bootstrap.group().shutdownGracefully(); + logger.debug("RiakCluster shut down bootstrap"); + logger.info("RiakCluster has shut down"); + shutdownLatch.countDown(); + } + } + finally + { + nodeListLock.writeLock().unlock(); + } + } + } + + @Override + public void operationFailed(FutureOperation operation, int remainingRetries) + { + logger.debug("operation failed; remaining retries: {}", remainingRetries); + if (remainingRetries > 0) + { + retryQueue.add(operation); + } + else + { + inFlightCount.decrementAndGet(); + } + } + + @Override + public void operationComplete(FutureOperation operation, int remainingRetries) + { + inFlightCount.decrementAndGet(); + logger.debug("operation complete; remaining retries: {}", remainingRetries); + } + + private void retryOperation() throws InterruptedException + { + FutureOperation operation = retryQueue.take(); + execute(operation, operation.getLastNode()); + } + + /** + * Register a NodeStateListener. + *

+ * Any state change by any of the nodes in the cluster will be sent to + * the registered NodeStateListener. + *

+ *

When registering, the current state of all the nodes is sent to the + * listener. + *

+ * @param listener The NodeStateListener to register. + */ + public void registerNodeStateListener(NodeStateListener listener) + { + stateCheck(State.CREATED, State.RUNNING, State.SHUTTING_DOWN); + try + { + stateListeners.add(listener); + nodeListLock.readLock().lock(); + for (RiakNode node : nodeList) + { + node.addStateListener(listener); + listener.nodeStateChanged(node, node.getNodeState()); + } + } + finally + { + nodeListLock.readLock().unlock(); + } + } + + /** + * Remove a NodeStateListener. + *

+ * The supplied NodeStateListener will be unregistered and no longer + * receive state updates. + *

+ * @param listener The NodeStateListener to unregister. + */ + public void removeNodeStateListener(NodeStateListener listener) + { + stateCheck(State.CREATED, State.RUNNING, State.SHUTTING_DOWN); + try + { + stateListeners.remove(listener); + nodeListLock.readLock().lock(); + for (RiakNode node : nodeList) + { + node.removeStateListener(listener); + } + } + finally + { + nodeListLock.readLock().unlock(); + } + } + + private class RetryTask implements Runnable + { + @Override + public void run() + { + while (!Thread.interrupted()) + { + try + { + retryOperation(); + } + catch (InterruptedException ex) + { + break; + } + } + + logger.info("Retrier shutting down."); + } + + } + + private class ShutdownTask implements Runnable + { + @Override + public void run() + { + if (inFlightCount.get() == 0) + { + logger.info("All operations have completed"); + + retrierFuture.cancel(true); + + // Copying the list avoids any potential deadlocks on the callbacks. + for (RiakNode node : getNodes()) + { + node.addStateListener(RiakCluster.this); + logger.debug("calling shutdown on node {}:{}", node.getRemoteAddress(), node.getPort()); + node.shutdown(); + } + + shutdownFuture.cancel(false); + } + } + + } + + + public static Builder builder(List nodes) + { + return new Builder(nodes); + } + + public static Builder builder(RiakNode node) + { + return new Builder(node); + } + + /** + * Builder used to create {@link RiakCluster} instances. + */ + public static class Builder + { + public final static int DEFAULT_EXECUTION_ATTEMPTS = 3; + + private final List riakNodes; + + private int executionAttempts = DEFAULT_EXECUTION_ATTEMPTS; + private NodeManager nodeManager; + private ScheduledExecutorService executor; + private Bootstrap bootstrap; + + /** + * Instantiate a Builder containing the supplied {@link RiakNode}s + * @param riakNodes - a List of unstarted RiakNode objects + */ + public Builder(List riakNodes) + { + this.riakNodes = new ArrayList(riakNodes); + } + + /** + * Instantiate a Builder containing a single {@link RiakNode} + * @param node + */ + public Builder(RiakNode node) + { + this.riakNodes = new ArrayList(1); + this.riakNodes.add(node); + } + + /** + * Sets the number of times the {@link RiakCluster} will attempt an + * operation before returning it as failed. + * @param numberOfAttempts + * @return this + */ + public Builder withExecutionAttempts(int numberOfAttempts) + { + this.executionAttempts = numberOfAttempts; + return this; + } + + /** + * Sets the {@link NodeManager} for this {@link RiakCluster} + * + * If none is provided the {@link DefaultNodeManager} will be used + * @param nodeManager + * @return this + */ + public Builder withNodeManager(NodeManager nodeManager) + { + this.nodeManager = nodeManager; + return this; + } + + /** + * Sets the Threadpool for this cluster. + * + * This threadpool is passed down to the {@link RiakNode}s. + * At the very least it needs to have + * two threads available. It is not necessary to supply your own as the + * {@link RiakCluster} will instantiate one upon construction if this is + * not asSet. + * @param executor + * @return this + */ + public Builder withExecutor(ScheduledExecutorService executor) + { + this.executor = executor; + return this; + } + + /** + * The Netty {@link Bootstrap} this cluster will use. + * + * This Bootstrap is passed down to the {@link RiakNode}s. + * It is not necessary to supply your + * own as the {@link RiakCluster} will instantiate one upon construction + * if this is not asSet. + * @param bootstrap + * @return this + */ + public Builder withBootstrap(Bootstrap bootstrap) + { + this.bootstrap = bootstrap; + return this; + } + + /** + * Instantiates the {@link RiakCluster} + * @return a new RiakCluster + * @throws UnknownHostException if a node fails to start due to a DNS lookup + */ + public RiakCluster build() throws UnknownHostException + { + return new RiakCluster(this); + } + + } +} diff --git a/src/main/java/com/basho/riak/client/core/RiakFuture.java b/src/main/java/com/basho/riak/client/core/RiakFuture.java new file mode 100644 index 000000000..4ce8e0ab0 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/RiakFuture.java @@ -0,0 +1,200 @@ +/* + * 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.core; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * The result of an asynchronous Riak operation. + *

+ * All Riak operations are asynchronous. It means when you execute an operation on + * the cluster it will return immediately with no guarantee that the requested + * operation has been completed at the end of the call. Instead, you will be returned + * a {@code RiakFuture} instance which gives you the information about the result + * or status of the operation. + *

+ *

+ * A {@code RiakFuture} is either uncompleted or completed. When an operation begins, a new future + * object is created. The new future is uncompleted initially - it is neither + * succeeded, failed, nor canceled because the operation is not finished yet. + * If the operation is finished either successfully, with failure, or by cancellation, + * the future is marked as completed with more specific information, such as the + * cause of the failure. Please note that even failure and cancellation belong to the completed state. + *

+ *                                      +---------------------------+
+ *                                      | Completed successfully    |
+ *                                      +---------------------------+
+ *                                 +---->      isDone() = true      |
+ * +--------------------------+    |    |   isSuccess() = true      |
+ * |        Uncompleted       |    |    +===========================+
+ * +--------------------------+    |    | Completed with failure    |
+ * |      isDone() = false    |    |    +---------------------------+
+ * |   isSuccess() = false    |----+---->      isDone() = true      |
+ * | isCancelled() = false    |    |    |   isSuccess() = false     |
+ * |       cause() = null     |    |    |       cause() = non-null  |
+ * +--------------------------+    |    +===========================+
+ *                                 |    | Completed by cancellation |
+ *                                 |    +---------------------------+
+ *                                 +---->      isDone() = true      |
+ *                                      | isCancelled() = true      |
+ *                                      +---------------------------+
+ * 
+ *

+ *

+ * The typical use pattern is to call {@literal await()}, check {@literal isSuccess()} + * then call {@literal getNow()} or {@literal cause()}

+ * @author Brian Roach + * @param the (response) return type + * @param The query info type + * @since 2.0 + */ +public interface RiakFuture extends Future +{ + /** + * Not supported due to limitations of the Riak API. + *

+ * At present time there is no way to cancel an operation sent to Riak. + * This method will never succeed and always return false. + *

+ * @param mayInterruptIfRunning + * @return always false. + */ + @Override + boolean cancel(boolean mayInterruptIfRunning); + + /** + * Waits for this RiakFuture to be completed if necessary and returns the response if available. + * @return The response from the operation. + * @throws InterruptedException if the current thread was interrupted + * while waiting + * @throws ExecutionException if the computation threw an + * exception + */ + @Override + V get() throws InterruptedException, ExecutionException; + + /** + * Waits if necessary for at most the given time for the computation + * to complete, and then retrieves its result, if available. + *

+ * Note that the timeout value here is how long you are willing to wait + * for this RiakFuture to complete. If you wish to set a timeout on the command + * itself, use the timeout() method provided in the command's associated builder. + *

+ * @param timeout the amount of time to wait before returning. + * @param unit the unit of time. + * @return the response, or null if not completed or failed. + * @throws InterruptedException if the current thread was interrupted + * while waiting. + * @throws ExecutionException if the computation threw an + * exception. + * @throws TimeoutException if the wait timed out. + */ + @Override + V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; + @Override + boolean isCancelled(); + @Override + boolean isDone(); + /** + * Waits for this RiakFuture to be completed. + *

+ * Upon returning, the operation has completed. Checking isSuccess() tells + * you if it did so successfully. + *

+ * @throws InterruptedException if the current thread was interrupted + * while waiting + * @see #isSuccess() + */ + void await() throws InterruptedException; + /** + * Waits for this RiakFuture to be completed for a set amount of time. + *

Note that the timeout value here is how long you are willing to wait + * for this RiakFuture to complete. Upon return you can check {@literal isDone()} + * to see if the future has completed yet or not. The operation is still in + * progress if that returns false. + *

+ *

+ * If you wish to set a timeout on the command itself, use the timeout() method + * provided in the command's associated builder. + *

+ * + * @param timeout the amount of time to wait before returning. + * @param unit the unit of time. + * @throws InterruptedException if the current thread was interrupted + * while waiting + * @see #isDone() + * @see #isSuccess() + */ + void await(long timeout, TimeUnit unit) throws InterruptedException; + /** + * Determine if the operation was successful. + *

+ * In the case of true, get() will then return the response. If false, + * cause() will then return non-null. + * @return true if completed and successful, false otherwise. + * @see #cause() + */ + + /** + * Return the result without blocking or throwing an exception. + * If the future is not yet done or has failed this will return null. + * As it is possible that a null value is used to mark the future as successful + * you also need to check if the future is really done with {@link #isDone()} + * and not rely on the returned null value. + * @return The response, or {@literal null} if the future is not yet completed or failed. + * @see #isDone() + * @see #isSuccess() + */ + V getNow(); + /** + * Determine if the operation completed successfully. + * @return true if the operation succeeded, false if not yet completed or failed. + */ + boolean isSuccess(); + /** + * Return information about the operation and why it failed. + *

+ * Note this will return {@literal null} if the operation completed + * successfully. + *

+ * @return The exception thrown during the operation, or null if not set. + * @see #isSuccess() + */ + Throwable cause(); + /** + * Returns information related to the operation performed. + *

+ * Useful in async operations when you want to know what operation this + * future refers to. + *

+ * @return Information pertaining to the operation attached to this future. + */ + T getQueryInfo(); + /** + * Add a listener to this RiakFuture. + * @param listener a FiakFutureListener that will be notified when this RiakFuture completes. + */ + void addListener(RiakFutureListener listener); + /** + * Remove a listener from this RiakFuture. + * @param listener The listener to remove. + */ + void removeListener(RiakFutureListener listener); +} diff --git a/src/main/java/com/basho/riak/client/core/RiakFutureListener.java b/src/main/java/com/basho/riak/client/core/RiakFutureListener.java new file mode 100644 index 000000000..d79164ce5 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/RiakFutureListener.java @@ -0,0 +1,22 @@ +/* + * 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.core; + +public interface RiakFutureListener { + + void handle(RiakFuture f); + +} diff --git a/src/main/java/com/basho/riak/client/core/RiakMessage.java b/src/main/java/com/basho/riak/client/core/RiakMessage.java new file mode 100644 index 000000000..d24e3f526 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/RiakMessage.java @@ -0,0 +1,43 @@ +/* + * 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.core; + +/** + * Encapsulates the raw bytes sent to or received from Riak. + * @author Brian Roach + * @since 2.0 + */ +public final class RiakMessage +{ + private final byte code; + private final byte[] data; + + public RiakMessage(byte code, byte[] data) + { + this.code = code; + this.data = data; + } + + public byte getCode() + { + return code; + } + + public byte[] getData() + { + return data; + } +} diff --git a/src/main/java/com/basho/riak/client/core/RiakNode.java b/src/main/java/com/basho/riak/client/core/RiakNode.java new file mode 100644 index 000000000..1fe6fe5e8 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/RiakNode.java @@ -0,0 +1,1468 @@ +/* + * 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.core; + +import com.basho.riak.client.core.netty.HealthCheckDecoder; +import com.basho.riak.client.core.netty.PingHealthCheck; +import com.basho.riak.client.core.netty.RiakChannelInitializer; +import com.basho.riak.client.core.netty.RiakResponseException; +import com.basho.riak.client.core.netty.RiakSecurityDecoder; +import com.basho.riak.client.core.util.Constants; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelOption; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.util.concurrent.DefaultPromise; +import io.netty.util.concurrent.Promise; +import java.io.IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.security.KeyStore; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicLong; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.TrustManagerFactory; + +/** + * @author Brian Roach + * @since 2.0 + */ +public class RiakNode implements RiakResponseListener +{ + public enum State + { + CREATED, RUNNING, HEALTH_CHECKING, SHUTTING_DOWN, SHUTDOWN; + } + + private final Logger logger = LoggerFactory.getLogger(RiakNode.class); + + private final LinkedBlockingDeque available = + new LinkedBlockingDeque(); + private final ConcurrentLinkedQueue recentlyClosed = + new ConcurrentLinkedQueue(); + private final List stateListeners = + Collections.synchronizedList(new LinkedList()); + private final Map inProgressMap = + new ConcurrentHashMap(); + + private final Sync permits; + private final String remoteAddress; + private final int port; + private final String username; + private final String password; + private final KeyStore trustStore; + private final AtomicLong consecutiveFailedOperations = new AtomicLong(0); + private final AtomicLong consecutiveFailedConnectionAttempts = new AtomicLong(0); + + private volatile Bootstrap bootstrap; + private volatile boolean ownsBootstrap; + private volatile ScheduledExecutorService executor; + private volatile boolean ownsExecutor; + private volatile State state; + private volatile ScheduledFuture idleReaperFuture; + private volatile ScheduledFuture healthMonitorFuture; + private volatile int minConnections; + private volatile long idleTimeoutInNanos; + private volatile int connectionTimeout; + private volatile boolean blockOnMaxConnections; + + private HealthCheckFactory healthCheckFactory; + + private final ChannelFutureListener writeListener = + new ChannelFutureListener() + { + @Override + public void operationComplete(ChannelFuture future) throws Exception + { + // If there's a write failure, we yank the operation, close + // the channel, and set the exception. Returning the closed + // channel to the pool discards it and records a disconnect + // for the health check. + if (!future.isSuccess()) + { + logger.error("Write failed on RiakNode {}:{} id: {}; cause: {}", + remoteAddress, port, future.channel().hashCode(), + future.cause()); + FutureOperation inProgress = inProgressMap.remove(future.channel()); + if (inProgress != null) + { + future.channel().close(); + returnConnection(future.channel()); // to release permit + recentlyClosed.add(new ChannelWithIdleTime(future.channel())); + inProgress.setException(future.cause()); + } + } + else + { + // On a successful write we add the in-progress close listener + // and let it handle a disco during an op. + future.channel().closeFuture().addListener(inProgressCloseListener); + } + } + + }; + + private final ChannelFutureListener inAvailableCloseListener = + new ChannelFutureListener() + { + @Override + public void operationComplete(ChannelFuture future) throws Exception + { + // Rather than having to do an O(n) search here, we just leave + // the channel in available. Because it's closed it'll be discarded + // the next time it's pulled from the pool. + // We record the disco for the health check. + recentlyClosed.add(new ChannelWithIdleTime(future.channel())); + logger.error("inAvailable channel closed; id:{} {}:{}", + future.channel().hashCode(), remoteAddress, port); + } + }; + + private final ChannelFutureListener inProgressCloseListener = + new ChannelFutureListener() + { + @Override + public void operationComplete(ChannelFuture future) throws Exception + { + FutureOperation inProgress = inProgressMap.remove(future.channel()); + logger.error("Channel closed while operation in progress; id:{} {}:{}", + future.channel().hashCode(), remoteAddress, port); + if (inProgress != null) + { + returnConnection(future.channel()); // to release permit + recentlyClosed.add(new ChannelWithIdleTime(future.channel())); + + // Netty seems to not bother telling you *why* the connection + // was closed. + if (future.cause() != null) + { + inProgress.setException(future.cause()); + } + else + { + inProgress.setException(new Exception("Connection closed unexpectantly")); + } + } + + } + }; + + + private final CountDownLatch shutdownLatch = new CountDownLatch(1); + + private RiakNode(Builder builder) throws UnknownHostException + { + this.executor = builder.executor; + this.connectionTimeout = builder.connectionTimeout; + this.idleTimeoutInNanos = TimeUnit.NANOSECONDS.convert(builder.idleTimeout, TimeUnit.MILLISECONDS); + this.minConnections = builder.minConnections; + this.port = builder.port; + this.remoteAddress = builder.remoteAddress; + this.blockOnMaxConnections = builder.blockOnMaxConnections; + this.username = builder.username; + this.password = builder.password; + this.trustStore = builder.trustStore; + this.healthCheckFactory = builder.healthCheckFactory; + + if (builder.bootstrap != null) + { + this.bootstrap = builder.bootstrap.clone(); + } + + if (builder.maxConnections < 1) + { + permits = new Sync(Integer.MAX_VALUE); + } + else + { + permits = new Sync(builder.maxConnections); + } + + + this.state = State.CREATED; + } + + private void stateCheck(State... allowedStates) + { + if (Arrays.binarySearch(allowedStates, state) < 0) + { + logger.debug("IllegalStateException; RiakNode: {}:{} required: {} current: {} ", + remoteAddress, port, Arrays.toString(allowedStates), state); + throw new IllegalStateException("required: " + + Arrays.toString(allowedStates) + + " current: " + state); + } + } + + /** + * exposed for testing only + * + * @return number of inprogress tasks + */ + int getNumInProgress() + { + return inProgressMap.size(); + } + + public synchronized RiakNode start() + { + stateCheck(State.CREATED); + + if (executor == null) + { + executor = Executors.newSingleThreadScheduledExecutor(); + ownsExecutor = true; + } + + if (bootstrap == null) + { + bootstrap = new Bootstrap() + .group(new NioEventLoopGroup()) + .channel(NioSocketChannel.class); + ownsBootstrap = true; + } + + bootstrap.handler(new RiakChannelInitializer(this)) + .remoteAddress(new InetSocketAddress(remoteAddress, port)); + + if (connectionTimeout > 0) + { + bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeout); + } + + if (minConnections > 0) + { + List minChannels = new LinkedList(); + for (int i = 0; i < minConnections; i++) + { + Channel channel; + try + { + channel = doGetConnection(); + minChannels.add(channel); + } + catch (ConnectionFailedException ex) + { + // no-op, we don't care right now + } + } + + for (Channel c : minChannels) + { + available.offerFirst(new ChannelWithIdleTime(c)); + c.closeFuture().addListener(inAvailableCloseListener); + } + } + + idleReaperFuture = executor.scheduleWithFixedDelay(new IdleReaper(), 1, 5, TimeUnit.SECONDS); + healthMonitorFuture = executor.scheduleWithFixedDelay(new HealthMonitorTask(), 1000, 1000, TimeUnit.MILLISECONDS); + + state = State.RUNNING; + logger.info("RiakNode started; {}:{}", remoteAddress, port); + notifyStateListeners(); + return this; + } + + public synchronized Future shutdown() + { + stateCheck(State.RUNNING, State.HEALTH_CHECKING); + state = State.SHUTTING_DOWN; + logger.info("RiakNode shutting down; {}:{}", remoteAddress, port); + notifyStateListeners(); + idleReaperFuture.cancel(true); + healthMonitorFuture.cancel(true); + ChannelWithIdleTime cwi = available.poll(); + while (cwi != null) + { + Channel c = cwi.getChannel(); + closeConnection(c); + cwi = available.poll(); + } + + executor.schedule(new ShutdownTask(), 0, TimeUnit.SECONDS); + + return new Future() { + @Override + public boolean cancel(boolean mayInterruptIfRunning) + { + return false; + } + @Override + public Boolean get() throws InterruptedException + { + shutdownLatch.await(); + return true; + } + @Override + public Boolean get(long timeout, TimeUnit unit) throws InterruptedException + { + return shutdownLatch.await(timeout, unit); + } + @Override + public boolean isCancelled() + { + return false; + } + @Override + public boolean isDone() + { + return shutdownLatch.getCount() <= 0; + } + + }; + + } + + /** + * Sets the Netty {@link Bootstrap} for this Node's connections. + * {@link Bootstrap#clone()} is called to clone the bootstrap. + * + * @param bootstrap - the Netty Bootstrap to use + * @return a reference to this RiakNode + * @throws IllegalArgumentException if it was already set via the builder. + * @throws IllegalStateException if the node has already been started. + * @see Builder#withBootstrap(io.netty.bootstrap.Bootstrap) + */ + public RiakNode setBootstrap(Bootstrap bootstrap) + { + stateCheck(State.CREATED); + if (this.bootstrap != null) + { + throw new IllegalArgumentException("Bootstrap already set"); + } + + this.bootstrap = bootstrap.clone(); + return this; + } + + /** + * Sets the {@link ScheduledExecutorService} for this Node and its pool(s). + * + * @param executor - the ScheduledExecutorService to use. + * @return a reference to this RiakNode + * @throws IllegalArgumentException if it was already set via the builder. + * @throws IllegalStateException if the node has already been started. + * @see Builder#withExecutor(java.util.concurrent.ScheduledExecutorService) + */ + public RiakNode setExecutor(ScheduledExecutorService executor) + { + stateCheck(State.CREATED); + if (this.executor != null) + { + throw new IllegalArgumentException("Executor already set"); + } + this.executor = executor; + return this; + } + + /** + * Sets the maximum number of connections allowed. + * + * @param maxConnections the maxConnections to set. + * @return a reference to this RiakNode. + * @see Builder#withMaxConnections(int) + */ + public RiakNode setMaxConnections(int maxConnections) + { + stateCheck(State.CREATED, State.RUNNING, State.HEALTH_CHECKING); + if (maxConnections >= getMinConnections()) + { + permits.setMaxPermits(maxConnections); + } + else + { + throw new IllegalArgumentException("Max connections less than min connections"); + } + // TODO: reap delta? + return this; + } + + /** + * Returns the maximum number of connections allowed. + * + * @return the maxConnections + * @see Builder#withMaxConnections(int) + */ + public int getMaxConnections() + { + stateCheck(State.CREATED, State.RUNNING, State.HEALTH_CHECKING); + return permits.getMaxPermits(); + } + + /** + * Sets the minimum number of active connections to be maintained. + * + * @param minConnections the minConnections to set + * @return a reference to this RiakNode + * @see Builder#withMinConnections(int) + */ + public RiakNode setMinConnections(int minConnections) + { + stateCheck(State.CREATED, State.RUNNING, State.HEALTH_CHECKING); + if (minConnections <= getMaxConnections()) + { + this.minConnections = minConnections; + } + else + { + throw new IllegalArgumentException("Min connections greater than max connections"); + } + // TODO: Start / reap delta? + return this; + } + + /** + * Returns the current minimum number of active connections to be maintained. + * + * @return the minConnections + * @see Builder#withMinConnections(int) + */ + public int getMinConnections() + { + stateCheck(State.CREATED, State.RUNNING, State.HEALTH_CHECKING); + return minConnections; + } + + /** + * Set whether to block when all connections are in use. + * @param block true to block. + * @see Builder#withBlockOnMaxConnections(boolean) + */ + public void setBlockOnMaxConnections(boolean block) + { + this.blockOnMaxConnections = block; + } + + /** + * Returns if this node is set to block when all connections are in use. + * @return true if set to block, false otherwise. + * @see Builder#withBlockOnMaxConnections(boolean) + */ + public boolean getBlockOnMaxConnections() + { + return blockOnMaxConnections; + } + + /** + * Sets the connection idle timeout for connections. + * + * @param idleTimeoutInMillis the idleTimeout to set + * @return a reference to this RiakNode + * @see Builder#withIdleTimeout(int) + */ + public RiakNode setIdleTimeout(int idleTimeoutInMillis) + { + stateCheck(State.CREATED, State.RUNNING, State.HEALTH_CHECKING); + this.idleTimeoutInNanos = TimeUnit.NANOSECONDS.convert(idleTimeoutInMillis, TimeUnit.MILLISECONDS); + return this; + } + + /** + * Returns the connection idle timeout for connections in milliseconds. + * + * @return the idleTimeout in milliseconds + * @see Builder#withIdleTimeout(int) + */ + public int getIdleTimeout() + { + stateCheck(State.CREATED, State.RUNNING, State.HEALTH_CHECKING); + return (int) TimeUnit.MILLISECONDS.convert(idleTimeoutInNanos, TimeUnit.NANOSECONDS); + } + + /** + * Sets the connection timeout for new connections. + * + * @param connectionTimeoutInMillis the connectionTimeout to set + * @return a reference to this RiakNode + * @see Builder#withConnectionTimeout(int) + */ + public RiakNode setConnectionTimeout(int connectionTimeoutInMillis) + { + stateCheck(State.CREATED, State.RUNNING, State.HEALTH_CHECKING); + this.connectionTimeout = connectionTimeoutInMillis; + bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeout); + return this; + } + + /** + * Returns the connection timeout in milliseconds. + * + * @return the connectionTimeout + * @see Builder#withConnectionTimeout(int) + */ + public int getConnectionTimeout() + { + stateCheck(State.CREATED, State.RUNNING, State.HEALTH_CHECKING); + return connectionTimeout; + } + + /** + * Returns the number of permits currently available. + * The number of available permits indicates how many additional + * connections can be made without blocking. + * + * @return the number of available permits. + * @see Builder#withMaxConnections(int) + */ + public int availablePermits() + { + stateCheck(State.CREATED, State.RUNNING, State.HEALTH_CHECKING); + return permits.availablePermits(); + } + + public void addStateListener(NodeStateListener listener) + { + stateListeners.add(listener); + } + + public boolean removeStateListener(NodeStateListener listener) + { + return stateListeners.remove(listener); + } + + private void notifyStateListeners() + { + synchronized (stateListeners) + { + for (Iterator it = stateListeners.iterator(); it.hasNext(); ) + { + NodeStateListener listener = it.next(); + listener.nodeStateChanged(this, state); + } + } + } + + /** + * Submits the operation to be executed on this node. + * + * @param operation The operation to perform + * @return {@code true} if this operation was accepted, {@code false} if there + * were no available connections. + * @throws IllegalStateException if this node is not in the {@code RUNNING} or {@code HEALTH_CHECKING} state + * @throws IllegalArgumentException if the protocol required for the operation is not supported by this node + */ + public boolean execute(FutureOperation operation) + { + stateCheck(State.RUNNING, State.HEALTH_CHECKING); + + operation.setLastNode(this); + Channel channel = getConnection(); + if (channel != null) + { + inProgressMap.put(channel, operation); + ChannelFuture writeFuture = channel.writeAndFlush(operation); + writeFuture.addListener(writeListener); + logger.debug("Operation being executed on RiakNode {}:{}", remoteAddress, port); + return true; + } + else + { + logger.debug("Operation not being executed Riaknode {}:{}; no connections available", + remoteAddress, port); + return false; + } + } + + // ConnectionPool Stuff + + /** + * Get a Netty channel from the pool. + *

+ * The first thing this method does is attempt to acquire a permit from the + * Semaphore that controls the pool's behavior. Depending on whether + * {@code blockOnMaxConnections} is set, this will either block until one + * becomes available or return null. + *

+ *

+ * Once a permit has been acquired, a channel from the pool or a newly + * created one will be returned. If an attempt to create a new connection + * fails, null will then be returned. + *

+ * @return a connected channel or {@code null} + * @see Builder#withBlockOnMaxConnections(boolean) + */ + private Channel getConnection() + { + stateCheck(State.RUNNING, State.HEALTH_CHECKING); + boolean acquired = false; + if (blockOnMaxConnections) + { + try + { + if (!permits.tryAcquire()) + { + logger.info("All connections in use for {}; had to wait for one.", + remoteAddress); + permits.acquire(); + } + acquired = true; + } + catch (InterruptedException ex) + { + // no-op, don't care + } + } + else + { + acquired = permits.tryAcquire(); + } + + Channel channel = null; + if (acquired) + { + try + { + channel = doGetConnection(); + channel.closeFuture().removeListener(inAvailableCloseListener); + } + catch (ConnectionFailedException ex) + { + permits.release(); + } + } + return channel; + } + + private Channel doGetConnection() throws ConnectionFailedException + { + ChannelWithIdleTime cwi; + while ((cwi = available.poll()) != null) + { + Channel channel = cwi.getChannel(); + // If the channel from available is closed, try again. This will result in + // the caller always getting a connection or an exception. If closed + // the channel is simply discarded so this also acts as a purge + // for dead channels during a health check. + if (channel.isOpen()) + { + return channel; + } + } + + ChannelFuture f = bootstrap.connect(); + + try + { + f.await(); + } + catch (InterruptedException ex) + { + logger.error("Thread interrupted waiting for new connection to be made; {}", + remoteAddress); + Thread.currentThread().interrupt(); + throw new ConnectionFailedException(ex); + } + + if (!f.isSuccess()) + { + logger.error("Connection attempt failed: {}:{}; {}", + remoteAddress, port, f.cause()); + consecutiveFailedConnectionAttempts.incrementAndGet(); + throw new ConnectionFailedException(f.cause()); + } + + consecutiveFailedConnectionAttempts.set(0); + Channel c = f.channel(); + + if (trustStore != null) + { + SSLContext context; + try + { + context = SSLContext.getInstance("TLS"); + TrustManagerFactory tmf = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(trustStore); + + context.init(null, tmf.getTrustManagers(), null); + + } + catch (Exception ex) + { + c.close(); + logger.error("Failure configuring SSL; {}:{} {}", remoteAddress, port, ex); + throw new ConnectionFailedException(ex); + } + + SSLEngine engine = context.createSSLEngine(); + + Set protocols = new HashSet(Arrays.asList(engine.getSupportedProtocols())); + + if (protocols.contains("TLSv1.2")) + { + engine.setEnabledProtocols(new String[] {"TLSv1.2"}); + logger.debug("Using TLSv1.2"); + } + else if (protocols.contains("TLSv1.1")) + { + engine.setEnabledProtocols(new String[] {"TLSv1.1"}); + logger.debug("Using TLSv1.1"); + } + + engine.setUseClientMode(true); + RiakSecurityDecoder decoder = new RiakSecurityDecoder(engine, username, password); + c.pipeline().addFirst(decoder); + + try + { + DefaultPromise promise = decoder.getPromise(); + promise.await(); + + if (promise.isSuccess()) + { + logger.debug("Auth succeeded; {}:{}", remoteAddress, port); + } + else + { + c.close(); + logger.error("Failure during Auth; {}:{} {}",remoteAddress, port, promise.cause()); + throw new ConnectionFailedException(promise.cause()); + } + + + } + catch (InterruptedException e) + { + c.close(); + logger.error("Thread interrupted during Auth; {}:{}", + remoteAddress, port); + Thread.currentThread().interrupt(); + throw new ConnectionFailedException(e); + } + + } + + return c; + + } + + /** + * Return a Netty channel. + * + * @param c The Netty channel to return to the pool + */ + private void returnConnection(Channel c) + { + switch (state) + { + case SHUTTING_DOWN: + case SHUTDOWN: + closeConnection(c); + break; + case RUNNING: + case HEALTH_CHECKING: + default: + if (inProgressMap.containsKey(c)) + { + logger.error("Channel returned to pool while still in use. id: {}", + c.hashCode()); + } + else + { + if (c.isOpen()) + { + logger.debug("Channel id:{} returned to pool", c.hashCode()); + c.closeFuture().removeListener(inProgressCloseListener); + c.closeFuture().addListener(inAvailableCloseListener); + available.offerFirst(new ChannelWithIdleTime(c)); + } + else + { + logger.debug("Closed channel id:{} returned to pool; discarding", c.hashCode()); + } + logger.debug("Released pool permit"); + permits.release(); + } + } + } + + private void closeConnection(Channel c) + { + // If we are explicitly closing the connection we don't want to hear + // about it. + c.closeFuture().removeListener(inProgressCloseListener); + c.closeFuture().removeListener(inAvailableCloseListener); + c.close(); + } + + + // End ConnectionPool stuff + + @Override + public void onSuccess(Channel channel, final RiakMessage response) + { + logger.debug("Operation onSuccess() channel: id:{} {}:{}", channel.hashCode(), + remoteAddress, port); + consecutiveFailedOperations.set(0); + final FutureOperation inProgress = inProgressMap.get(channel); + + // Especially with a streaming op, the close listener may trigger causing + // a race. This check guards that. + if (inProgress != null) + { + inProgress.setResponse(response); + + if (inProgress.isDone()) + { + inProgressMap.remove(channel); + returnConnection(channel); // return permit + } + } + } + + @Override + public void onRiakErrorResponse(Channel channel, RiakResponseException ex) + { + logger.debug("Riak replied with error; {}:{}", ex.getCode(), ex.getMessage()); + final FutureOperation inProgress = inProgressMap.remove(channel); + consecutiveFailedOperations.incrementAndGet(); + if (inProgress != null) + { + inProgress.setException(ex); + returnConnection(channel); // release permit + } + } + + @Override + public void onException(Channel channel, final Throwable t) + { + logger.error("Operation onException() channel: id:{} {}:{} {}", + channel.hashCode(), remoteAddress, port, t); + + final FutureOperation inProgress = inProgressMap.remove(channel); + // There are fail cases where multiple exceptions are thrown from + // the pipeline. In that case we'll get an exception from the + // handler but will not have an entry in inProgress because it's + // already been handled. + if (inProgress != null) + { + inProgress.setException(t); + returnConnection(channel); // release permit + } + } + + /** + * Returns the {@code remoteAddress} for this RiakNode + * + * @return The IP address or FQDN as a {@code String} + */ + public String getRemoteAddress() + { + return remoteAddress; + } + + /** + * returns the remote port for this RiakNode + * + * @return the port number + */ + public int getPort() + { + return port; + } + + /** + * Returns the current state of this node. + * + * @return The state + */ + public State getNodeState() + { + return this.state; + } + + private class ChannelWithIdleTime + { + private Channel channel; + private long idleStart; + + public ChannelWithIdleTime(Channel channel) + { + this.channel = channel; + idleStart = System.nanoTime(); + } + + public Channel getChannel() + { + return channel; + } + + public long getIdleStart() + { + return idleStart; + } + } + + private class Sync extends Semaphore + { + private static final long serialVersionUID = -5118488872281021072L; + private volatile int maxPermits; + + public Sync(int numPermits) + { + super(numPermits); + this.maxPermits = numPermits; + } + + public Sync(int numPermits, boolean fair) + { + super(numPermits, fair); + this.maxPermits = numPermits; + } + + public int getMaxPermits() + { + return maxPermits; + } + + // Synchronized because we're (potentially) changing this.maxPermits + synchronized void setMaxPermits(int maxPermits) + { + int diff = maxPermits - this.maxPermits; + + if (diff == 0) + { + return; + } + else if (diff > 0) + { + release(diff); + } + else if (diff < 0) + { + reducePermits(diff); + } + + this.maxPermits = maxPermits; + } + + } + + private class IdleReaper implements Runnable + { + @Override + public void run() + { + reapIdleConnections(); + } + } + + private void reapIdleConnections() + { + // with all the concurrency there's really no reason to keep + // checking the sizes. This is really just a "best guess" + int currentNum = inProgressMap.size() + available.size(); + if (currentNum > minConnections) + { + // Note this will not throw a ConncurrentModificationException + // and if hasNext() returns true you are guaranteed that + // the next() will return a value (even if it has already + // been removed from the Deque between those calls). + Iterator i = available.descendingIterator(); + while (i.hasNext() && currentNum > minConnections) + { + ChannelWithIdleTime cwi = i.next(); + if (cwi.getIdleStart() + idleTimeoutInNanos < System.nanoTime()) + { + boolean removed = available.remove(cwi); + if (removed) + { + Channel c = cwi.getChannel(); + logger.debug("Idle channel closed; {}:{}", remoteAddress, port); + closeConnection(c); + currentNum--; + } + } + else + { + // Since we are descending and this is a LIFO, + // if the current connection hasn't been idle beyond + // the threshold, there's no reason to descend further + break; + } + } + } + } + + // TODO: Revisit if we ever support multiple protocols or change protocols. + // As-is the parameters work well for protocol buffers. + /** + * Task to see if a criteria should trigger a health check. + *

+ * We keep a list of connections that triggered the closeListener. We also + * track the number of consecutive failed connection attempts, and the + * number of consecutive error responses from Riak. + *

+ */ + private class HealthMonitorTask implements Runnable + { + @Override + public void run() + { + // Purge recentlyClosed past a certain age + // sliding window should be larger than the + // frequency of this task + long current = System.nanoTime(); + long window = 3000000000L; // 3 seconds + for (ChannelWithIdleTime cwi = recentlyClosed.peek(); + cwi != null && current - cwi.getIdleStart() > window; + cwi = recentlyClosed.peek()) + { + recentlyClosed.poll(); + } + + // If we more than 5 recently closed in 3 seconds, more than 1 consecutive failed + // connection attempts, more than 5 consecutive error responses from Riak, + // or we failed a healthcheck + if ((state == State.RUNNING && + (recentlyClosed.size() > 5 || + consecutiveFailedConnectionAttempts.get() > 1 || + consecutiveFailedOperations.get() > 5) + ) || + state == State.HEALTH_CHECKING) + { + checkHealth(); + } + } + } + + private void checkHealth() + { + try + { + HealthCheckDecoder healthCheck = healthCheckFactory.makeDecoder(); + RiakFuture future = healthCheck.getFuture(); + // See: doGetConnection() - this will purge closed + // connections from the available queue and either + // return/create a new one (meaning the node is up) or throw + // an exception if a connection can't be made. + Channel c = doGetConnection(); + logger.debug("Healthcheck channel: {} isOpen: {} handlers:{}", c.hashCode(), c.isOpen(), c.pipeline().names()); + + + + // If the channel closes between when we got it and now, the pipeline is emptied. If the handlers + // aren't there we fail the healthcheck + + try + { + if (c.pipeline().names().contains(Constants.SSL_HANDLER)) + { + c.pipeline().addAfter(Constants.SSL_HANDLER, Constants.HEALTHCHECK_CODEC, healthCheck); + } + else + { + c.pipeline().addBefore(Constants.MESSAGE_CODEC, Constants.HEALTHCHECK_CODEC, healthCheck); + } + + logger.debug("healthCheck added to pipeline."); + + //future.await(5, TimeUnit.SECONDS); + future.await(); + if (future.isSuccess()) + { + healthCheckSucceeded(); + } + else + { + healthCheckFailed(future.cause()); + } + + } + catch (InterruptedException ex) + { + logger.error("Thread interrupted performing healthcheck."); + } + catch (NoSuchElementException e) + { + healthCheckFailed(new IOException("Channel closed during health check")); + } + finally + { + closeConnection(c); + } + } + catch (ConnectionFailedException ex) + { + healthCheckFailed(ex); + } + catch (IllegalStateException e) + { + // no-op; there's a race condition where the bootstrap is shutting down + // right when a healthcheck occurs and netty will throw this + logger.debug("Illegal state exception during healthcheck."); + logger.debug("Stack: {}", e); + } + catch (RuntimeException e) + { + logger.error("Runtime exception during healthcheck: {}",e); + } + + } + + private void healthCheckFailed(Throwable cause) + { + if (state == State.RUNNING) + { + logger.error("RiakNode failed healthcheck operation; health checking; {}:{} {}", + remoteAddress, port, cause); + state = State.HEALTH_CHECKING; + notifyStateListeners(); + } + else + { + logger.error("RiakNode failed healthcheck operation; {}:{} {}", + remoteAddress, port, cause); + } + } + + private void healthCheckSucceeded() + { + if (state == State.HEALTH_CHECKING) + { + logger.info("RiakNode recovered; {}:{}", remoteAddress, port); + state = State.RUNNING; + notifyStateListeners(); + } + } + + private class ShutdownTask implements Runnable + { + @Override + public void run() + { + if (inProgressMap.isEmpty()) + { + state = State.SHUTDOWN; + notifyStateListeners(); + if (ownsExecutor) + { + executor.shutdown(); + } + if (ownsBootstrap) + { + bootstrap.group().shutdownGracefully(); + } + logger.debug("RiakNode shut down {}:{}", remoteAddress, port); + shutdownLatch.countDown(); + } + } + } + + /** + * Builder used to construct a RiakNode. + */ + public static class Builder + { + /** + * The default remote address to be used if not specified: {@value #DEFAULT_REMOTE_ADDRESS} + * + * @see #withRemoteAddress(java.lang.String) + */ + public final static String DEFAULT_REMOTE_ADDRESS = "127.0.0.1"; + /** + * The default port number to be used if not specified: {@value #DEFAULT_REMOTE_PORT} + * + * @see #withRemotePort(int) + */ + public final static int DEFAULT_REMOTE_PORT = 8087; + /** + * The default minimum number of connections to maintain if not specified: {@value #DEFAULT_MIN_CONNECTIONS} + * + * @see #withMinConnections(int) + */ + public final static int DEFAULT_MIN_CONNECTIONS = 1; + /** + * The default maximum number of connections allowed if not specified: {@value #DEFAULT_MAX_CONNECTIONS} + * + * @see #withMaxConnections(int) + */ + public final static int DEFAULT_MAX_CONNECTIONS = 0; + /** + * The default idle timeout in milliseconds for connections if not specified: {@value #DEFAULT_IDLE_TIMEOUT} + * + * @see #withIdleTimeout(int) + */ + public final static int DEFAULT_IDLE_TIMEOUT = 1000; + /** + * The default connection timeout in milliseconds if not specified: {@value #DEFAULT_CONNECTION_TIMEOUT} + * + * @see #withConnectionTimeout(int) + */ + public final static int DEFAULT_CONNECTION_TIMEOUT = 0; + + /** + * The default HealthCheckFactory. + *

+ * By default this is the {@link PingHealthCheck} + *

+ * @see HealthCheckFactory + * @see HealthCheckDecoder + */ + public final static HealthCheckFactory DEFAULT_HEALTHCHECK_FACTORY = new PingHealthCheck(); + + private int port = DEFAULT_REMOTE_PORT; + private String remoteAddress = DEFAULT_REMOTE_ADDRESS; + private int minConnections = DEFAULT_MIN_CONNECTIONS; + private int maxConnections = DEFAULT_MAX_CONNECTIONS; + private int idleTimeout = DEFAULT_IDLE_TIMEOUT; + private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT; + private HealthCheckFactory healthCheckFactory = DEFAULT_HEALTHCHECK_FACTORY; + private Bootstrap bootstrap; + private ScheduledExecutorService executor; + private boolean blockOnMaxConnections; + private String username; + private String password; + private KeyStore trustStore; + + + /** + * Default constructor. Returns a new builder for a RiakNode with + * default values set. + */ + public Builder() + { + + } + + /** + * Sets the remote address for this RiakNode. + * + * @param remoteAddress Can either be a FQDN or IP address + * @return this + * @see #DEFAULT_REMOTE_ADDRESS + */ + public Builder withRemoteAddress(String remoteAddress) + { + this.remoteAddress = remoteAddress; + return this; + } + + /** + * Specifies the remote port for this RiakNode. + * + * @param port - the port + * @return this + * @see #DEFAULT_REMOTE_PORT + */ + public Builder withRemotePort(int port) + { + this.port = port; + return this; + } + + /** + * Set the minimum number of active connections to maintain. + * These connections are exempt from the idle timeout. + * + * @param minConnections - number of connections to maintain. + * @return this + * @see #DEFAULT_MIN_CONNECTIONS + */ + public Builder withMinConnections(int minConnections) + { + if (maxConnections == DEFAULT_MAX_CONNECTIONS || minConnections <= maxConnections) + { + this.minConnections = minConnections; + } + else + { + throw new IllegalArgumentException("Min connections greater than max connections"); + } + return this; + } + + /** + * Set the maximum number of connections allowed. + * A value of 0 sets this to unlimited. + * + * @param maxConnections - maximum number of connections to allow + * @return this + * @see #DEFAULT_MAX_CONNECTIONS + */ + public Builder withMaxConnections(int maxConnections) + { + if (maxConnections >= minConnections) + { + this.maxConnections = maxConnections; + } + else + { + throw new IllegalArgumentException("Max connections less than min connections"); + } + return this; + } + + /** + * Set the idle timeout used to reap inactive connections. + * Any connection that has been idle for this amount of time + * becomes eligible to be closed and discarded unless {@code minConnections} + * has been set via {@link #withMinConnections(int) } + * + * @param idleTimeoutInMillis - idle timeout in milliseconds + * @return this + * @see #DEFAULT_IDLE_TIMEOUT + */ + public Builder withIdleTimeout(int idleTimeoutInMillis) + { + this.idleTimeout = idleTimeoutInMillis; + return this; + } + + /** + * Set the connection timeout used when making new connections + * + * @param connectionTimeoutInMillis + * @return this + * @see #DEFAULT_CONNECTION_TIMEOUT + */ + public Builder withConnectionTimeout(int connectionTimeoutInMillis) + { + this.connectionTimeout = connectionTimeoutInMillis; + return this; + } + + /** + * Provides an executor for this node to use for internal maintenance tasks. + * If not provided one will be created via + * {@link Executors#newSingleThreadScheduledExecutor()} + * + * @param executor the ScheduledExecutorService to use. + * @return this + */ + public Builder withExecutor(ScheduledExecutorService executor) + { + this.executor = executor; + return this; + } + + /** + * Provides a Netty Bootstrap for this node to use. + * If not provided one + * will be created with its own {@code NioEventLoopGroup}. + * + * @param bootstrap + * @return this + */ + public Builder withBootstrap(Bootstrap bootstrap) + { + this.bootstrap = bootstrap; + return this; + } + + /** + * Set whether to block if all connections are in use. + *

+ * If a maximum number of connections is specified and all those + * connections are in use, the default + * behavior when an operation is submitted to a node is to + * fail-fast and return. Setting this to true will cause the + * call to block (fair-scheduled, FIFO) until a connection becomes + * available. + *

+ * @param block whether to block when an operation is submitted and + * all connections are in use. + * @return this + */ + public Builder withBlockOnMaxConnections(boolean block) + { + this.blockOnMaxConnections = block; + return this; + } + + /** + * Set the credentials for Riak security and authentication. + *

+ * Riak supports authentication and authorization features. + * These credentials will be used for all connections. + *

+ *

+ * Note this requires Riak to have been configured with security enabled. + *

+ * + * @param username the riak user name. + * @param password the password for this user. + * @param trustStore A Java KeyStore loaded with the CA certificate required for TLS/SSL + * @return a reference to this object. + */ + public Builder withAuth(String username, String password, KeyStore trustStore) + { + this.username = username; + this.password = password; + this.trustStore = trustStore; + return this; + } + + /** + * Set the HealthCheckFactory used to determine if this RiakNode is healthy. + *

+ * If not set the {@link PingHealthCheck} is used. + *

+ * @param factory a HealthCheckFactory instance that produces HealthCheckDecoders + * @return a reference to this object. + * @see HealthCheckDecoder + */ + public Builder withHealthCheck(HealthCheckFactory factory) + { + this.healthCheckFactory = factory; + return this; + } + + /** + * Builds a RiakNode. + * If a Netty {@code Bootstrap} and/or a {@code ScheduledExecutorService} has not been provided they + * will be created. + * + * @return a new Riaknode + * @throws UnknownHostException if the DNS lookup fails for the supplied hostname + */ + public RiakNode build() throws UnknownHostException + { + return new RiakNode(this); + } + + + /** + * Build a set of RiakNodes. + * The provided builder will be used to construct a set of RiakNodes + * using the supplied addresses. + * + * @param builder a configured builder + * @param remoteAddresses a list of IP addresses or FQDN + * @return a list of constructed RiakNodes + * @throws UnknownHostException if a supplied FQDN can not be resolved. + */ + public static List buildNodes(Builder builder, List remoteAddresses) + throws UnknownHostException + { + List nodes = new ArrayList(remoteAddresses.size()); + for (String remoteAddress : remoteAddresses) + { + builder.withRemoteAddress(remoteAddress); + nodes.add(builder.build()); + } + return nodes; + } + } +} diff --git a/src/main/java/com/basho/riak/client/core/RiakResponseListener.java b/src/main/java/com/basho/riak/client/core/RiakResponseListener.java new file mode 100644 index 000000000..05c2f8a03 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/RiakResponseListener.java @@ -0,0 +1,31 @@ +/* + * 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.core; + +import com.basho.riak.client.core.netty.RiakResponseException; +import io.netty.channel.Channel; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public interface RiakResponseListener +{ + public void onSuccess(Channel channel, RiakMessage response); + public void onRiakErrorResponse(Channel channel, RiakResponseException response); + public void onException(Channel channel, Throwable t); +} diff --git a/src/main/java/com/basho/riak/client/core/converters/BucketPropertiesConverter.java b/src/main/java/com/basho/riak/client/core/converters/BucketPropertiesConverter.java new file mode 100644 index 000000000..07e30fec7 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/converters/BucketPropertiesConverter.java @@ -0,0 +1,244 @@ +/* + * Copyright 2013 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.core.converters; + +import com.basho.riak.client.core.operations.Operations; +import com.basho.riak.client.core.query.BucketProperties; +import com.basho.riak.client.core.query.functions.Function; +import com.basho.riak.protobuf.RiakPB; +import com.google.protobuf.ByteString; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Brian Roach + */ +public class BucketPropertiesConverter +{ + private BucketPropertiesConverter() {} + + public static BucketProperties convert(RiakPB.RpbBucketProps pbProps) + { + BucketProperties.Builder builder = new BucketProperties.Builder() + .withNVal(pbProps.getNVal()) + .withAllowMulti(pbProps.getAllowMult()) + .withLastWriteWins(pbProps.getLastWriteWins()) + .withOldVClock(Operations.getUnsignedIntValue(pbProps.getOldVclock())) + .withYoungVClock(Operations.getUnsignedIntValue(pbProps.getYoungVclock())) + .withBigVClock(Operations.getUnsignedIntValue(pbProps.getBigVclock())) + .withSmallVClock(Operations.getUnsignedIntValue(pbProps.getSmallVclock())) + .withPr(pbProps.getPr()) + .withR(pbProps.getR()) + .withW(pbProps.getW()) + .withPw(pbProps.getPw()) + .withDw(pbProps.getDw()) + .withRw(pbProps.getRw()) + .withBasicQuorum(pbProps.getBasicQuorum()) + .withNotFoundOk(pbProps.getNotfoundOk()) + .withLegacyRiakSearchEnabled(pbProps.getSearch()) + .withChashkeyFunction( + new Function.Builder() + .withModule(pbProps.getChashKeyfun().getModule().toStringUtf8()) + .withFunction(pbProps.getChashKeyfun().getFunction().toStringUtf8()) + .build()); + + if (pbProps.hasLinkfun()) + { + builder.withLinkwalkFunction( + new Function.Builder() + .withModule(pbProps.getLinkfun().getModule().toStringUtf8()) + .withFunction(pbProps.getLinkfun().getFunction().toStringUtf8()) + .build()); + } + + if (pbProps.hasHasPrecommit()) + { + for (Function f : parseHooks(pbProps.getPrecommitList())) + { + builder.withPrecommitHook(f); + } + } + + if (pbProps.hasHasPostcommit()) + { + for (Function f : parseHooks(pbProps.getPostcommitList())) + { + builder.withPostcommitHook(f); + } + } + + if (pbProps.hasSearchIndex()) + { + builder.withSearchIndex(pbProps.getSearchIndex().toStringUtf8()); + } + + if (pbProps.hasBackend()) + { + builder.withBackend(pbProps.getBackend().toStringUtf8()); + } + + return builder.build(); + } + + public static RiakPB.RpbBucketProps convert(BucketProperties bucketProperties) + { + RiakPB.RpbBucketProps.Builder propsBuilder = + RiakPB.RpbBucketProps.newBuilder(); + + if (bucketProperties.hasNVal()) + { + propsBuilder.setNVal(bucketProperties.getNVal()); + } + if (bucketProperties.hasAllowMulti()) + { + propsBuilder.setAllowMult(bucketProperties.getAllowMulti()); + } + if (bucketProperties.hasLastWriteWins()) + { + propsBuilder.setLastWriteWins(bucketProperties.getLastWriteWins()); + } + if (bucketProperties.hasPrecommitHooks()) + { + propsBuilder.addAllPrecommit(convertHooks(bucketProperties.getPrecommitHooks())); + } + if (bucketProperties.hasPostcommitHooks()) + { + propsBuilder.addAllPostcommit(convertHooks(bucketProperties.getPostcommitHooks())); + } + if (bucketProperties.hasChashKeyFunction()) + { + propsBuilder.setChashKeyfun(convertModFun(bucketProperties.getChashKeyFunction())); + } + if (bucketProperties.hasLinkwalkFunction()) + { + propsBuilder.setLinkfun(convertModFun(bucketProperties.getLinkwalkFunction())); + } + if (bucketProperties.hasOldVClock()) + { + propsBuilder.setOldVclock(bucketProperties.getOldVClock().intValue()); + } + if (bucketProperties.hasYoungVClock()) + { + propsBuilder.setYoungVclock(bucketProperties.getYoungVClock().intValue()); + } + if (bucketProperties.hasBigVClock()) + { + propsBuilder.setBigVclock(bucketProperties.getBigVClock().intValue()); + } + if (bucketProperties.hasSmallVClock()) + { + propsBuilder.setSmallVclock(bucketProperties.getSmallVClock().intValue()); + } + if (bucketProperties.hasPr()) + { + propsBuilder.setPr(bucketProperties.getPr().getIntValue()); + } + if (bucketProperties.hasR()) + { + propsBuilder.setR(bucketProperties.getR().getIntValue()); + } + if (bucketProperties.hasW()) + { + propsBuilder.setW(bucketProperties.getW().getIntValue()); + } + if (bucketProperties.hasPw()) + { + propsBuilder.setPw(bucketProperties.getPw().getIntValue()); + } + if (bucketProperties.hasDw()) + { + propsBuilder.setDw(bucketProperties.getDw().getIntValue()); + } + if (bucketProperties.hasRw()) + { + propsBuilder.setRw(bucketProperties.getRw().getIntValue()); + } + if (bucketProperties.hasBasicQuorum()) + { + propsBuilder.setBasicQuorum(bucketProperties.getBasicQuorum()); + } + if (bucketProperties.hasNotFoundOk()) + { + propsBuilder.setNotfoundOk(bucketProperties.getNotFoundOk()); + } + if (bucketProperties.hasBackend()) + { + propsBuilder.setBackend(ByteString.copyFromUtf8(bucketProperties.getBackend())); + } + if (bucketProperties.hasLegacyRiakSearchEnabled()) + { + propsBuilder.setSearch(bucketProperties.getLegacyRiakSearchEnabled()); + } + if (bucketProperties.hasSearchIndex()) + { + propsBuilder.setSearchIndex(ByteString.copyFromUtf8(bucketProperties.getSearchIndex())); + } + + return propsBuilder.build(); + + } + + + private static List parseHooks(List hooks) { + List list = new ArrayList(hooks.size()); + for ( RiakPB.RpbCommitHook hook : hooks) { + if (hook.hasName()) { + Function f = + new Function.Builder() + .withName(hook.getName().toStringUtf8()) + .build(); + list.add(f); + } else { + Function f = new Function.Builder() + .withModule(hook.getModfun().getModule().toStringUtf8()) + .withFunction(hook.getModfun().getFunction().toStringUtf8()) + .build(); + list.add(f); + } + } + return list; + } + + private static RiakPB.RpbModFun convertModFun(Function f) + { + return RiakPB.RpbModFun.newBuilder() + .setModule(ByteString.copyFromUtf8(f.getModule())) + .setFunction(ByteString.copyFromUtf8(f.getFunction())) + .build(); + } + + private static List convertHooks(List hookList) { + List pbHookList = new ArrayList(hookList.size()); + RiakPB.RpbCommitHook.Builder builder = RiakPB.RpbCommitHook.newBuilder(); + RiakPB.RpbModFun.Builder mfBuilder = RiakPB.RpbModFun.newBuilder(); + for (Function hook : hookList) { + if (hook.isJavascript()) { + builder.setName(ByteString.copyFromUtf8(hook.getName())); + } else { + mfBuilder.setModule(ByteString.copyFromUtf8(hook.getModule())); + mfBuilder.setFunction(ByteString.copyFromUtf8(hook.getFunction())); + builder.setModfun(mfBuilder); + } + + pbHookList.add(builder.build()); + builder.clear(); + mfBuilder.clear(); + } + return pbHookList; + } +} diff --git a/src/main/java/com/basho/riak/client/core/converters/CrdtResponseConverter.java b/src/main/java/com/basho/riak/client/core/converters/CrdtResponseConverter.java new file mode 100644 index 000000000..1a8c5046a --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/converters/CrdtResponseConverter.java @@ -0,0 +1,122 @@ +/* + * 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.core.converters; + +import com.basho.riak.client.core.query.crdt.types.RiakFlag; +import com.basho.riak.client.core.query.crdt.types.RiakRegister; +import com.basho.riak.client.core.query.crdt.types.RiakMap; +import com.basho.riak.client.core.query.crdt.types.RiakCounter; +import com.basho.riak.client.core.query.crdt.types.RiakDatatype; +import com.basho.riak.client.core.query.crdt.types.RiakSet; +import com.basho.riak.client.core.util.BinaryValue; +import com.basho.riak.protobuf.RiakDtPB; +import com.google.protobuf.ByteString; + +import java.util.ArrayList; +import java.util.List; + +public class CrdtResponseConverter +{ + private RiakDatatype parseSet(List setValues) + { + List entries = new ArrayList(setValues.size()); + for (ByteString bstring : setValues) + { + entries.add(BinaryValue.unsafeCreate(bstring.toByteArray())); + } + return new RiakSet(entries); + } + + private RiakDatatype parseMap(List mapEntries) + { + List entries = new ArrayList(mapEntries.size()); + for (RiakDtPB.MapEntry entry : mapEntries) + { + + RiakDtPB.MapField field = entry.getField(); + + RiakDatatype element; + switch (field.getType()) + { + case COUNTER: + element = new RiakCounter(entry.getCounterValue()); + break; + case FLAG: + element = new RiakFlag(entry.getFlagValue()); + break; + case MAP: + element = parseMap(entry.getMapValueList()); + break; + case REGISTER: + element = new RiakRegister(BinaryValue.unsafeCreate(entry.getRegisterValue().toByteArray())); + break; + case SET: + element = parseSet(entry.getSetValueList()); + break; + default: + throw new IllegalStateException("Expecting a datatype in map entry but none found"); + } + + BinaryValue key = BinaryValue.unsafeCreate(entry.getField().getName().toByteArray()); + entries.add(new RiakMap.MapEntry(key, element)); + } + + return new RiakMap(entries); + } + + public RiakDatatype convert(RiakDtPB.DtUpdateResp response) + { + + RiakDatatype element = null; + + if (response.hasCounterValue()) + { + element = new RiakCounter(response.getCounterValue()); + } + else if (response.getSetValueCount() > 0) + { + element = parseSet(response.getSetValueList()); + } + else if (response.getMapValueCount() > 0) + { + element = parseMap(response.getMapValueList()); + } + + return element; + + } + + public RiakDatatype convert(RiakDtPB.DtFetchResp response) + { + RiakDatatype element; + switch (response.getType()) + { + case COUNTER: + element = new RiakCounter(response.getValue().getCounterValue()); + break; + case MAP: + element = parseMap(response.getValue().getMapValueList()); + break; + case SET: + element = parseSet(response.getValue().getSetValueList()); + break; + default: + throw new IllegalStateException("No known datatype returned"); + } + + return element; + } +} diff --git a/src/main/java/com/basho/riak/client/core/converters/RiakObjectConverter.java b/src/main/java/com/basho/riak/client/core/converters/RiakObjectConverter.java new file mode 100644 index 000000000..2ca3ca8ec --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/converters/RiakObjectConverter.java @@ -0,0 +1,200 @@ +/* + * 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.core.converters; + +import com.basho.riak.client.api.cap.BasicVClock; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.query.UserMetadata.RiakUserMetadata; +import com.basho.riak.client.core.query.indexes.IndexType; +import com.basho.riak.client.core.query.indexes.RawIndex; +import com.basho.riak.client.core.query.indexes.RiakIndex; +import com.basho.riak.client.core.query.indexes.RiakIndexes; +import com.basho.riak.client.core.query.links.RiakLink; +import com.basho.riak.client.core.query.links.RiakLinks; +import com.basho.riak.client.core.util.BinaryValue; +import com.basho.riak.protobuf.RiakKvPB; +import com.basho.riak.protobuf.RiakKvPB.RpbContent; +import com.basho.riak.protobuf.RiakPB; +import com.google.protobuf.ByteString; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility class for converting to/from RiakKvPB.RpbContent and RiakObject + * + * + * @author Brian Roach + * @since 2.0 + */ +public class RiakObjectConverter +{ + private final static Logger logger = LoggerFactory.getLogger(RiakObjectConverter.class); + + private RiakObjectConverter() {} + + public static List convert(List contentList, ByteString contentVClock) + { + List roList = new LinkedList(); + BasicVClock vclock = new BasicVClock(contentVClock.toByteArray()); + + for (RpbContent content : contentList) + { + RiakObject ro = new RiakObject(); + + ro.setVClock(vclock); + + if (content.hasDeleted()) + { + ro.setDeleted(content.getDeleted()); + } + + if (content.hasContentType()) + { + ro.setContentType(content.getContentType().toStringUtf8()); + } + + if (content.hasCharset()) + { + ro.setCharset(content.getCharset().toStringUtf8()); + } + + if (content.hasLastMod()) + { + int lastMod = content.getLastMod(); + int lastModUsec = content.getLastModUsecs(); + ro.setLastModified((lastMod * 1000L) + (lastModUsec / 1000L)); + } + + if (content.hasValue() && !content.getValue().isEmpty()) + { + ro.setValue(BinaryValue.unsafeCreate(content.getValue().toByteArray())); + } + + if (content.hasVtag()) + { + ro.setVTag(content.getVtag().toStringUtf8()); + } + + if (content.getLinksCount() > 0) + { + List pbLinkList = content.getLinksList(); + RiakLinks riakLinks = ro.getLinks(); + + for (RiakKvPB.RpbLink pbLink : pbLinkList) + { + RiakLink link = new RiakLink(pbLink.getBucket().toStringUtf8(), + pbLink.getKey().toStringUtf8(), + pbLink.getTag().toStringUtf8()); + riakLinks.addLink(link); + } + } + + if (content.getIndexesCount() > 0) + { + RiakIndexes indexes = ro.getIndexes(); + for (RiakPB.RpbPair p : content.getIndexesList()) + { + String name = p.getKey().toStringUtf8(); + try + { + IndexType type = IndexType.typeFromFullname(name); + indexes.getIndex(RawIndex.named(name, type)) + .add(BinaryValue.unsafeCreate(p.getValue().toByteArray())); + } + catch (IllegalArgumentException e) + { + logger.error("Unknown index type during conversion: {};{}", name, e); + } + } + } + + if (content.getUsermetaCount() > 0) + { + RiakUserMetadata userMeta = ro.getUserMeta(); + for (int j = 0; j < content.getUsermetaCount(); j++) + { + RiakPB.RpbPair pair = content.getUsermeta(j); + userMeta.put(BinaryValue.unsafeCreate(pair.getKey().toByteArray()), + BinaryValue.unsafeCreate(pair.getValue().toByteArray())); + } + } + + roList.add(ro); + } + + return roList; + } + + public static RpbContent convert(RiakObject ro) + { + RpbContent.Builder builder = RpbContent.newBuilder(); + + builder.setContentType(ByteString.copyFromUtf8(ro.getContentType())); + + if (ro.hasCharset()) + { + builder.setCharset(ByteString.copyFromUtf8(ro.getCharset())); + } + + if (ro.hasValue()) + { + builder.setValue(ByteString.copyFrom(ro.getValue().unsafeGetValue())); + } + + if (ro.hasLinks()) + { + for (RiakLink link : ro.getLinks()) + { + builder.addLinks( + RiakKvPB.RpbLink.newBuilder() + .setBucket(ByteString.copyFrom(link.getBucketAsBytes().unsafeGetValue())) + .setTag(ByteString.copyFrom(link.getTagAsBytes().unsafeGetValue())) + .setKey(ByteString.copyFrom(link.getKeyAsBytes().unsafeGetValue()))); + } + } + + if (ro.hasIndexes()) + { + for (RiakIndex index : ro.getIndexes()) + { + for (BinaryValue value : index.rawValues()) + { + RiakPB.RpbPair.Builder pair = RiakPB.RpbPair.newBuilder(); + pair.setKey(ByteString.copyFrom(index.getFullname().getBytes())); + pair.setValue(ByteString.copyFrom(value.unsafeGetValue())); + builder.addIndexes(pair); + } + } + } + + if (ro.hasUserMeta()) + { + for (Map.Entry entry + : ro.getUserMeta().getUserMetadata()) + { + RiakPB.RpbPair.Builder pair = RiakPB.RpbPair.newBuilder(); + pair.setKey(ByteString.copyFrom(entry.getKey().unsafeGetValue())); + pair.setValue(ByteString.copyFrom(entry.getValue().unsafeGetValue())); + builder.addUsermeta(pair); + } + } + + return builder.build(); + } +} diff --git a/src/main/java/com/basho/riak/client/core/converters/RiakResponseConverter.java b/src/main/java/com/basho/riak/client/core/converters/RiakResponseConverter.java new file mode 100644 index 000000000..5ec63485f --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/converters/RiakResponseConverter.java @@ -0,0 +1,30 @@ +/* + * 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.core.converters; + +import com.basho.riak.protobuf.RiakKvPB; + +import java.util.concurrent.ExecutionException; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public interface RiakResponseConverter +{ + T convert(U message) throws ExecutionException; +} diff --git a/src/main/java/com/basho/riak/client/core/netty/HealthCheckDecoder.java b/src/main/java/com/basho/riak/client/core/netty/HealthCheckDecoder.java new file mode 100644 index 000000000..d68f35cff --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/netty/HealthCheckDecoder.java @@ -0,0 +1,261 @@ +/* + * 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.core.netty; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.RiakFutureListener; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakPB; +import com.google.protobuf.InvalidProtocolBufferException; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Brian Roach + */ +public abstract class HealthCheckDecoder extends ByteToMessageDecoder +{ + private final Logger logger = LoggerFactory.getLogger(HealthCheckDecoder.class); + private final HealthCheckFuture future = new HealthCheckFuture(); + + protected abstract FutureOperation buildOperation(); + + @Override + protected void decode(ChannelHandlerContext chc, ByteBuf in, List list) throws Exception + { + // Make sure we have 4 bytes + if (in.readableBytes() >= 4) + { + in.markReaderIndex(); + int length = in.readInt(); + + // See if we have the full frame. + if (in.readableBytes() < length) + { + in.resetReaderIndex(); + } + else + { + byte code = in.readByte(); + byte[] protobuf = new byte[length - 1]; + in.readBytes(protobuf); + + chc.channel().pipeline().remove(this); + if (code == RiakMessageCodes.MSG_ErrorResp) + { + logger.debug("Received MSG_ErrorResp reply to healthcheck"); + future.setException((riakErrorToException(protobuf))); + } + else + { + logger.debug("Healthcheck op successful; returned code {}", code); + future.setMessage(new RiakMessage(code,protobuf)); + } + } + } + } + + private void init(ChannelHandlerContext ctx) throws InterruptedException + { + ChannelFuture writeAndFlush = ctx.channel().writeAndFlush(buildOperation().channelMessage()); + } + + @Override + public void handlerAdded(ChannelHandlerContext ctx) throws Exception + { + logger.debug("HealthCheckDecoder Handler Added"); + if (ctx.channel().isActive()) + { + init(ctx); + } + else + { + future.setException(new IOException("HealthCheckDecoder added to inactive channel")); + } + } + + @Override + public void channelActive(final ChannelHandlerContext ctx) throws Exception { + logger.debug("HealthCheckDecoder Channel Active"); + init(ctx); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception + { + logger.debug("Healthcheck channel went inactive"); + future.setException(new IOException("Channel closed while performing health check op.")); + ctx.fireChannelInactive(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) + throws Exception + { + future.setException(new IOException("Exception in channel while performing health check op.", cause)); + } + + public RiakFuture getFuture() { + return future; + } + + private RiakResponseException riakErrorToException(byte[] protobuf) + { + try + { + RiakPB.RpbErrorResp error = RiakPB.RpbErrorResp.parseFrom(protobuf); + return new RiakResponseException(error.getErrcode(), error.getErrmsg().toStringUtf8()); + } + catch (InvalidProtocolBufferException ex) + { + return null; + } + } + + public static class HealthCheckFuture implements RiakFuture + { + + private final CountDownLatch latch = new CountDownLatch(1); + private volatile Throwable exception; + private volatile RiakMessage message; + + @Override + public boolean cancel(boolean mayInterruptIfRunning) + { + return false; + } + + @Override + public boolean isCancelled() + { + return false; + } + + @Override + public boolean isDone() + { + return latch.getCount() != 1; + } + + @Override + public RiakMessage get() throws InterruptedException, ExecutionException + { + latch.await(); + + if (exception != null) + { + throw new ExecutionException(exception); + } + else + { + return message; + } + } + + @Override + public RiakMessage get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException + { + boolean succeed = latch.await(timeout, unit); + if (!succeed) + { + throw new TimeoutException(); + } + else if (exception != null) + { + throw new ExecutionException(exception); + } + else + { + return message; + } + } + + public void setException(Throwable e) + { + exception = e; + latch.countDown(); + } + + public void setMessage(RiakMessage m) + { + message = m; + latch.countDown(); + } + + @Override + public void await() throws InterruptedException + { + latch.await(); + } + + @Override + public void await(long timeout, TimeUnit unit) throws InterruptedException + { + latch.await(timeout, unit); + } + + @Override + public RiakMessage getNow() + { + return message; + } + + @Override + public boolean isSuccess() + { + return isDone() && exception == null; + } + + @Override + public Throwable cause() + { + return exception; + } + + @Override + public Void getQueryInfo() + { + return null; + } + + @Override + public void addListener(RiakFutureListener listener) + { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public void removeListener(RiakFutureListener listener) + { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + } +} \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/core/netty/PingHealthCheck.java b/src/main/java/com/basho/riak/client/core/netty/PingHealthCheck.java new file mode 100644 index 000000000..e9e0140ff --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/netty/PingHealthCheck.java @@ -0,0 +1,43 @@ +/* + * 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.core.netty; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.HealthCheckFactory; +import com.basho.riak.client.core.operations.PingOperation; + +/** + * HealthCheck that sends a Ping operation to Riak. + * @author Brian Roach + * @since 2.0 + */ +public class PingHealthCheck extends HealthCheckDecoder implements HealthCheckFactory +{ + + @Override + protected FutureOperation buildOperation() + { + return new PingOperation(); + } + + @Override + public HealthCheckDecoder makeDecoder() + { + return new PingHealthCheck(); + } + +} diff --git a/src/main/java/com/basho/riak/client/core/netty/RiakChannelInitializer.java b/src/main/java/com/basho/riak/client/core/netty/RiakChannelInitializer.java new file mode 100644 index 000000000..728946699 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/netty/RiakChannelInitializer.java @@ -0,0 +1,47 @@ +/* + * 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.core.netty; + +import com.basho.riak.client.core.RiakResponseListener; +import com.basho.riak.client.core.util.Constants; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public class RiakChannelInitializer extends ChannelInitializer +{ + private final RiakResponseListener listener; + public RiakChannelInitializer(RiakResponseListener listener) + { + super(); + this.listener = listener; + } + + @Override + public void initChannel(SocketChannel ch) throws Exception + { + ChannelPipeline p = ch.pipeline(); + p.addLast(Constants.MESSAGE_CODEC, new RiakMessageCodec()); + p.addLast(Constants.OPERATION_ENCODER, new RiakOperationEncoder()); + p.addLast(Constants.RESPONSE_HANDLER, new RiakResponseHandler(listener)); + } + +} diff --git a/src/main/java/com/basho/riak/client/core/netty/RiakMessageCodec.java b/src/main/java/com/basho/riak/client/core/netty/RiakMessageCodec.java new file mode 100644 index 000000000..2ae37b35d --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/netty/RiakMessageCodec.java @@ -0,0 +1,65 @@ +/* + * 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.core.netty; + +import com.basho.riak.client.core.RiakMessage; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageCodec; +import java.util.List; + +/** + * + * @author Brian Roach + */ +public class RiakMessageCodec extends ByteToMessageCodec +{ + @Override + protected void encode(ChannelHandlerContext ctx, RiakMessage msg, ByteBuf out) throws Exception + { + int length = msg.getData().length + 1; + out.writeInt(length); + out.writeByte(msg.getCode()); + out.writeBytes(msg.getData()); + } + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception + { + // Make sure we have 4 bytes + if (in.readableBytes() >= 4) + { + in.markReaderIndex(); + int length = in.readInt(); + + // See if we have the full frame. + if (in.readableBytes() < length) + { + in.resetReaderIndex(); + return; + } + else + { + byte code = in.readByte(); + byte[] array = new byte[length - 1]; + in.readBytes(array); + out.add(new RiakMessage(code,array)); + } + + } + } + +} diff --git a/src/main/java/com/basho/riak/client/core/netty/RiakOperationEncoder.java b/src/main/java/com/basho/riak/client/core/netty/RiakOperationEncoder.java new file mode 100644 index 000000000..619c98b1d --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/netty/RiakOperationEncoder.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.core.netty; + +import com.basho.riak.client.core.FutureOperation; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageEncoder; +import java.util.List; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public class RiakOperationEncoder extends MessageToMessageEncoder +{ + + @Override + protected void encode(ChannelHandlerContext ctx, FutureOperation operation, List out) throws Exception + { + out.add(operation.channelMessage()); + } + +} diff --git a/src/main/java/com/basho/riak/client/core/netty/RiakResponseException.java b/src/main/java/com/basho/riak/client/core/netty/RiakResponseException.java new file mode 100644 index 000000000..b95bef2f9 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/netty/RiakResponseException.java @@ -0,0 +1,38 @@ +/* + * 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.core.netty; + +/** + * + * @author Brian Roach + */ +public class RiakResponseException extends Exception +{ + private static final long serialVersionUID = 2843706291883485752L; + private final int code; + + public RiakResponseException(int code, String message) + { + super(message); + this.code = code; + } + + public int getCode() + { + return code; + } + +} diff --git a/src/main/java/com/basho/riak/client/core/netty/RiakResponseHandler.java b/src/main/java/com/basho/riak/client/core/netty/RiakResponseHandler.java new file mode 100644 index 000000000..b13e93d9c --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/netty/RiakResponseHandler.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.core.netty; + +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.RiakResponseListener; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakPB; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public class RiakResponseHandler extends ChannelInboundHandlerAdapter +{ + + private RiakResponseListener listener; + private final Logger logger = LoggerFactory.getLogger(RiakResponseHandler.class); + + public RiakResponseHandler(RiakResponseListener listener) + { + super(); + this.listener = listener; + } + + @Override + public void channelRead(ChannelHandlerContext chc, Object message) throws Exception + { + RiakMessage riakMessage = (RiakMessage) message; + if (riakMessage.getCode() == RiakMessageCodes.MSG_ErrorResp) + { + RiakPB.RpbErrorResp error = RiakPB.RpbErrorResp.parseFrom(riakMessage.getData()); + + listener.onRiakErrorResponse(chc.channel(), + new RiakResponseException(error.getErrcode(), + error.getErrmsg().toStringUtf8())); + } + else + { + listener.onSuccess(chc.channel(), riakMessage); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) + throws Exception + { + // On any exception in the pipeline we explitly close the context here + // so the channel doesn't get reused by the ConnectionPool. + listener.onException(ctx.channel(), cause); + ctx.close(); + } + +} diff --git a/src/main/java/com/basho/riak/client/core/netty/RiakSecurityDecoder.java b/src/main/java/com/basho/riak/client/core/netty/RiakSecurityDecoder.java new file mode 100644 index 000000000..069900081 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/netty/RiakSecurityDecoder.java @@ -0,0 +1,231 @@ +/* + * 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.core.netty; + +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.util.Constants; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakPB; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.ssl.SslHandler; +import io.netty.util.concurrent.DefaultPromise; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import javax.net.ssl.SSLEngine; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Brian Roach + */ +public class RiakSecurityDecoder extends ByteToMessageDecoder + +{ + private final CountDownLatch promiseLatch = new CountDownLatch(1); + private final SSLEngine sslEngine; + private final String username; + private final String password; + private final Logger logger = LoggerFactory.getLogger(RiakSecurityDecoder.class); + private volatile DefaultPromise promise; + + private enum State { TLS_START, TLS_WAIT, SSL_WAIT, AUTH_WAIT } + + private volatile State state = State.TLS_START; + + public RiakSecurityDecoder(SSLEngine engine, String username, String password) + { + this.sslEngine = engine; + this.username = username; + this.password = password; + } + + @Override + protected void decode(ChannelHandlerContext chc, ByteBuf in, List out) throws Exception + { + // Make sure we have 4 bytes + if (in.readableBytes() >= 4) + { + in.markReaderIndex(); + int length = in.readInt(); + + // See if we have the full frame. + if (in.readableBytes() < length) + { + in.resetReaderIndex(); + } + else + { + byte code = in.readByte(); + byte[] protobuf = new byte[length - 1]; + in.readBytes(protobuf); + + switch(state) + { + case TLS_WAIT: + switch(code) + { + case RiakMessageCodes.MSG_StartTls: + logger.debug("Received MSG_RpbStartTls reply"); + // change state + this.state = State.SSL_WAIT; + // insert SSLHandler + SslHandler sslHandler = new SslHandler(sslEngine); + // get promise + Future hsFuture = sslHandler.handshakeFuture(); + // register callback + hsFuture.addListener(new SslListener()); + // Add handler + chc.channel().pipeline().addFirst(Constants.SSL_HANDLER, sslHandler); + break; + case RiakMessageCodes.MSG_ErrorResp: + logger.debug("Received MSG_ErrorResp reply to startTls"); + promise.tryFailure((riakErrorToException(protobuf))); + break; + default: + promise.tryFailure(new RiakResponseException(0, + "Invalid return code during StartTLS; " + code)); + } + break; + case AUTH_WAIT: + chc.channel().pipeline().remove(this); + switch(code) + { + case RiakMessageCodes.MSG_AuthResp: + logger.debug("Received MSG_RpbAuthResp reply"); + promise.trySuccess(null); + break; + case RiakMessageCodes.MSG_ErrorResp: + logger.debug("Received MSG_ErrorResp reply to auth"); + promise.tryFailure(riakErrorToException(protobuf)); + break; + default: + promise.tryFailure(new RiakResponseException(0, + "Invalid return code during Auth; " + code)); + } + break; + default: + // WTF? + logger.error("Received message while not in TLS_WAIT or AUTH_WAIT"); + promise.tryFailure(new IllegalStateException("Received message while not in TLS_WAIT or AUTH_WAIT")); + } + } + } + } + + private RiakResponseException riakErrorToException(byte[] protobuf) + { + try + { + RiakPB.RpbErrorResp error = RiakPB.RpbErrorResp.parseFrom(protobuf); + return new RiakResponseException(error.getErrcode(), error.getErrmsg().toStringUtf8()); + } + catch (InvalidProtocolBufferException ex) + { + return null; + } + } + + private void init(ChannelHandlerContext ctx) + { + state = State.TLS_WAIT; + promise = new DefaultPromise(ctx.executor()); + promiseLatch.countDown(); + ctx.channel().writeAndFlush(new RiakMessage(RiakMessageCodes.MSG_StartTls, + new byte[0])); + + } + + @Override + public void handlerAdded(ChannelHandlerContext ctx) throws Exception + { + logger.debug("MyStartTlsDecoder Handler Added"); + if (ctx.channel().isActive()) + { + init(ctx); + } + } + + @Override + public void channelActive(final ChannelHandlerContext ctx) throws Exception { + logger.debug("MyStartTlsDecoder Channel Active"); + init(ctx); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception + { + promise.tryFailure(new IOException("Channel closed during auth")); + ctx.fireChannelInactive(); + + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) + throws Exception + { + if (cause.getCause() instanceof javax.net.ssl.SSLHandshakeException) + { + // consume + } + else + { + ctx.fireExceptionCaught(cause); + } + } + + public DefaultPromise getPromise() throws InterruptedException { + promiseLatch.await(); + return promise; + } + + private class SslListener implements GenericFutureListener> + { + @Override + public void operationComplete(Future future) throws Exception + { + if (future.isSuccess()) + { + Channel c = future.getNow(); + state = State.AUTH_WAIT; + RiakPB.RpbAuthReq authReq = + RiakPB.RpbAuthReq.newBuilder() + .setUser(ByteString.copyFromUtf8(username)) + .setPassword(ByteString.copyFromUtf8(password)) + .build(); + c.writeAndFlush(new RiakMessage(RiakMessageCodes.MSG_AuthReq, + authReq.toByteArray())); + + } + else + { + promise.tryFailure(future.cause()); + } + } + } + + + +} diff --git a/src/main/java/com/basho/riak/client/core/netty/StoreValueHealthCheck.java b/src/main/java/com/basho/riak/client/core/netty/StoreValueHealthCheck.java new file mode 100644 index 000000000..64d802877 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/netty/StoreValueHealthCheck.java @@ -0,0 +1,64 @@ +/* + * 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.core.netty; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.HealthCheckFactory; +import com.basho.riak.client.core.operations.StoreOperation; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; + +/** + * A health check that attempts to store a value in Riak. + *

+ * Important note when using this HealthCheckDecoder: + *

+ *

+ * If security is enabled in Riak, the user the client is configured + * for must have access rights to the supplied location. + *

+ * @author Brian Roach + * @since 2.0 + */ +public class StoreValueHealthCheck extends HealthCheckDecoder implements HealthCheckFactory +{ + private final Location location; + private final BinaryValue value; + + + public StoreValueHealthCheck(Location location, BinaryValue value) + { + this.location = location; + this.value = value; + } + + @Override + protected FutureOperation buildOperation() + { + RiakObject ro = new RiakObject().setValue(value); + StoreOperation op = new StoreOperation.Builder(location).withContent(ro).build(); + return op; + } + + @Override + public HealthCheckDecoder makeDecoder() + { + return new StoreValueHealthCheck(location, value); + } + +} diff --git a/src/main/java/com/basho/riak/client/core/operations/DeleteOperation.java b/src/main/java/com/basho/riak/client/core/operations/DeleteOperation.java new file mode 100644 index 000000000..61b1350c5 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/DeleteOperation.java @@ -0,0 +1,234 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakKvPB; +import com.google.protobuf.ByteString; + +import java.util.List; + +import static com.basho.riak.client.core.operations.Operations.checkMessageType; +import com.basho.riak.client.core.query.Location; + +/** + * An operation to delete a riak object + * + * @author David Rusek + * @since 2.0 + */ +public class DeleteOperation extends FutureOperation +{ + + private final RiakKvPB.RpbDelReq.Builder reqBuilder; + private final Location location; + + private DeleteOperation(Builder builder) + { + this.reqBuilder = builder.reqBuilder; + this.location = builder.location; + } + + @Override + protected Void convert(List rawResponse) + { + return null; + } + + @Override + protected Void decode(RiakMessage rawResponse) + { + checkMessageType(rawResponse, RiakMessageCodes.MSG_DelResp); + return null; + } + + @Override + protected RiakMessage createChannelMessage() + { + return new RiakMessage(RiakMessageCodes.MSG_DelReq, reqBuilder.build().toByteArray()); + } + + @Override + public Location getQueryInfo() + { + return location; + } + + public static class Builder + { + private final Location location; + private final RiakKvPB.RpbDelReq.Builder reqBuilder = RiakKvPB.RpbDelReq.newBuilder(); + + /** + * Construct a builder for a DeleteOperation. + * @param location Location of the object in Riak to delete + */ + public Builder(Location location) + { + if (location == null) + { + throw new IllegalArgumentException("Location can not be null"); + } + + reqBuilder.setBucket(ByteString.copyFrom(location.getNamespace().getBucketName().unsafeGetValue())); + reqBuilder.setKey(ByteString.copyFrom(location.getKey().unsafeGetValue())); + reqBuilder.setType(ByteString.copyFrom(location.getNamespace().getBucketType().unsafeGetValue())); + this.location = location; + } + + /** + * Set the R value for this FetchOperation. + * If not asSet the bucket default is used. + * + * @param r the R value. + * @return a reference to this object. + */ + public Builder withR(int r) + { + reqBuilder.setR(r); + return this; + } + + /** + * Set the PR value for this query. + * If not asSet the bucket default is used. + * + * @param pr the PR value. + * @return a reference to this object. + */ + public Builder withPr(int pr) + { + reqBuilder.setPr(pr); + return this; + } + + /** + * Set the W value for this query. + * If not asSet the bucket default is used. + * + * @param w the W value. + * @return a reference to this object. + */ + public Builder withW(int w) + { + reqBuilder.setW(w); + return this; + } + + /** + * Set the DW value for this query. + * If not asSet the bucket default is used. + * + * @param dw the DW value. + * @return a reference to this object. + */ + public Builder withDw(int dw) + { + reqBuilder.setDw(dw); + return this; + } + + /** + * Set the PW value for this query. + * If not asSet the bucket default is used. + * + * @param pw the PW value. + * @return a reference to this object. + */ + public Builder withPw(int pw) + { + reqBuilder.setPw(pw); + return this; + } + + /** + * Set the RW value for this query. + * If not asSet the bucket default is used. + * + * @param rw the RW value. + * @return a reference to this object. + */ + public Builder withRw(int rw) + { + reqBuilder.setRw(rw); + return this; + } + + /** + * Set the Vclock to be used for this query. + * If not asSet siblings may be created depending on bucket properties. + * + * @param vclock the last fetched vclock. + * @return a reference to this object. + */ + public Builder withVclock(VClock vclock) + { + reqBuilder.setVclock(ByteString.copyFrom(vclock.getBytes())); + return this; + } + + /** + * Set a timeout for this operation. + * + * @param timeout a timeout in milliseconds. + * @return a reference to this object. + */ + public Builder withTimeout(int timeout) + { + reqBuilder.setTimeout(timeout); + return this; + } + + /** + * Set the n_val for this operation. + *

+ * Do not use this unless you understand the ramifications + *

+ * + * @param nval the n_val value + * @return a reference to this object. + */ + public Builder withNVal(int nval) + { + reqBuilder.setNVal(nval); + return this; + } + + /** + * Set whether to use sloppy_quorum. + *

+ * Do not use this unless you understand the ramifications + *

+ * + * @param sloppyQuorum true to use sloppy_quorum + * @return a reference to this object. + */ + public Builder withSloppyQuorum(boolean sloppyQuorum) + { + reqBuilder.setSloppyQuorum(sloppyQuorum); + return this; + } + + public DeleteOperation build() + { + return new DeleteOperation(this); + } + + } +} diff --git a/src/main/java/com/basho/riak/client/core/operations/DtFetchOperation.java b/src/main/java/com/basho/riak/client/core/operations/DtFetchOperation.java new file mode 100644 index 000000000..0cd46a6f6 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/DtFetchOperation.java @@ -0,0 +1,319 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.converters.CrdtResponseConverter; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.crdt.types.RiakDatatype; +import com.basho.riak.client.core.util.BinaryValue; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakDtPB; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import java.util.List; + +public class DtFetchOperation extends FutureOperation +{ + private final Location location; + private final RiakDtPB.DtFetchReq.Builder reqBuilder; + + private DtFetchOperation(Builder builder) + { + this.reqBuilder = builder.reqBuilder; + this.location = builder.location; + } + + @Override + protected Response convert(List rawResponse) + { + if (rawResponse.size() != 1) + { + throw new IllegalStateException("Expecting exactly one response, instead received " + rawResponse.size()); + } + + RiakDtPB.DtFetchResp response = rawResponse.iterator().next(); + + CrdtResponseConverter converter = new CrdtResponseConverter(); + RiakDatatype element = converter.convert(response); + + Response.Builder responseBuilder = new Response.Builder() + .withCrdtElement(element); + + if (response.hasContext()) + { + BinaryValue ctxWrapper = BinaryValue.create(response.getContext().toByteArray()); + responseBuilder.withContext(ctxWrapper); + } + + return responseBuilder.build(); + + } + + @Override + protected RiakMessage createChannelMessage() + { + return new RiakMessage(RiakMessageCodes.MSG_DtFetchReq, reqBuilder.build().toByteArray()); + } + + @Override + protected RiakDtPB.DtFetchResp decode(RiakMessage rawMessage) + { + Operations.checkMessageType(rawMessage, RiakMessageCodes.MSG_DtFetchResp); + try + { + return RiakDtPB.DtFetchResp.parseFrom(rawMessage.getData()); + } + catch (InvalidProtocolBufferException ex) + { + throw new IllegalArgumentException("Invalid message received", ex); + } + } + + @Override + public Location getQueryInfo() + { + return location; + } + + public static class Builder + { + private final RiakDtPB.DtFetchReq.Builder reqBuilder = RiakDtPB.DtFetchReq.newBuilder(); + private final Location location; + + /** + * Construct a Builder for a DtFetchOperaiton + * @param location the location of the object in Riak. + */ + public Builder(Location location) + { + if (location == null) + { + throw new IllegalArgumentException("Location can not be null"); + } + + reqBuilder.setBucket(ByteString.copyFrom(location.getNamespace().getBucketName().unsafeGetValue())); + reqBuilder.setKey(ByteString.copyFrom(location.getKey().unsafeGetValue())); + reqBuilder.setType(ByteString.copyFrom(location.getNamespace().getBucketType().unsafeGetValue())); + this.location = location; + } + + /** + * Set whether a context should be returned. + * Defaults to true. + * + * @param context return context + * @return a reference to this object. + */ + public Builder includeContext(boolean context) + { + reqBuilder.setIncludeContext(context); + return this; + } + + /** + * Set the R value for this FetchOperation. + * If not asSet the bucket default is used. + * + * @param r the R value. + * @return a reference to this object. + */ + public Builder withR(int r) + { + reqBuilder.setR(r); + return this; + } + + /** + * Set the PR value for this query. + * If not asSet the bucket default is used. + * + * @param pr the PR value. + * @return a reference to this object. + */ + public Builder withPr(int pr) + { + reqBuilder.setPr(pr); + return this; + } + + /** + * Set the not_found_ok value. + *

+ * If true a vnode returning notfound for a key increments the r tally. + * False is higher consistency, true is higher availability. + *

+ *

+ * If not asSet the bucket default is used. + *

+ * + * @param notFoundOK the not_found_ok value. + * @return a reference to this object. + */ + public Builder withNotFoundOK(boolean notFoundOK) + { + reqBuilder.setNotfoundOk(notFoundOK); + return this; + } + + /** + * Set the basic_quorum value. + *

+ * The parameter controls whether a read request should return early in + * some fail cases. + * E.g. If a quorum of nodes has already + * returned notfound/error, don't wait around for the rest. + *

+ * + * @param useBasicQuorum the basic_quorum value. + * @return a reference to this object. + */ + public Builder withBasicQuorum(boolean useBasicQuorum) + { + reqBuilder.setBasicQuorum(useBasicQuorum); + return this; + } + + /** + * Set a timeout for this operation. + * + * @param timeout a timeout in milliseconds. + * @return a reference to this object. + */ + public Builder withTimeout(int timeout) + { + reqBuilder.setTimeout(timeout); + return this; + } + + /** + * Set the n_val for this operation. + *

+ * Do not use this unless you understand the ramifications + *

+ * + * @param nval the n_val value + * @return a reference to this object. + */ + public Builder withNVal(int nval) + { + reqBuilder.setNVal(nval); + return this; + } + + /** + * Set whether to use sloppy_quorum. + *

+ * Do not use this unless you understand the ramifications + *

+ * + * @param sloppyQuorum true to use sloppy_quorum + * @return a reference to this object. + */ + public Builder withSloppyQuorum(boolean sloppyQuorum) + { + reqBuilder.setSloppyQuorum(sloppyQuorum); + return this; + } + + public DtFetchOperation build() + { + return new DtFetchOperation(this); + } + + } + + public static class Response + { + private final BinaryValue context; + private final RiakDatatype crdtElement; + + protected Response(Init builder) + { + this.context = builder.context; + this.crdtElement = builder.crdtElement; + } + + public boolean hasContext() + { + return context != null; + } + + public BinaryValue getContext() + { + return context; + } + + public boolean hasCrdtElement() + { + return crdtElement != null; + } + + public RiakDatatype getCrdtElement() + { + return crdtElement; + } + + protected static abstract class Init> + { + private BinaryValue context; + private RiakDatatype crdtElement; + + protected abstract T self(); + protected abstract Response build(); + + T withContext(BinaryValue context) + { + if (context != null) + { + if (context.length() == 0) + { + throw new IllegalArgumentException("Context cannot be null or zero length"); + } + else + { + this.context = context; + } + } + return self(); + } + + T withCrdtElement(RiakDatatype crdtElement) + { + this.crdtElement = crdtElement; + return self(); + } + } + + + private static class Builder extends Init + { + @Override + protected Builder self() + { + return this; + } + + @Override + protected Response build() + { + return new Response(this); + } + } + } +} diff --git a/src/main/java/com/basho/riak/client/core/operations/DtUpdateOperation.java b/src/main/java/com/basho/riak/client/core/operations/DtUpdateOperation.java new file mode 100644 index 000000000..ecc767cbc --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/DtUpdateOperation.java @@ -0,0 +1,489 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.query.crdt.ops.CounterOp; +import com.basho.riak.client.core.query.crdt.ops.RegisterOp; +import com.basho.riak.client.core.query.crdt.ops.MapOp; +import com.basho.riak.client.core.query.crdt.ops.CrdtOp; +import com.basho.riak.client.core.query.crdt.ops.FlagOp; +import com.basho.riak.client.core.query.crdt.ops.SetOp; +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.converters.CrdtResponseConverter; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.crdt.types.RiakDatatype; +import com.basho.riak.client.core.util.BinaryValue; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakDtPB; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import java.util.List; + +public class DtUpdateOperation extends FutureOperation +{ + private final Location location; + private final RiakDtPB.DtUpdateReq.Builder reqBuilder; + + private DtUpdateOperation(Builder builder) + { + this.reqBuilder = builder.reqBuilder; + this.location = builder.location; + } + + @Override + protected Response convert(List rawResponse) + { + if (rawResponse.size() != 1) + { + throw new IllegalStateException("Expecting exactly one response, instead received " + rawResponse.size()); + } + + RiakDtPB.DtUpdateResp response = rawResponse.iterator().next(); + CrdtResponseConverter converter = new CrdtResponseConverter(); + RiakDatatype element = converter.convert(response); + + Response.Builder responseBuilder = + new Response.Builder().withCrdtElement(element); + + if (response.hasKey()) + { + BinaryValue key = BinaryValue.unsafeCreate(response.getKey().toByteArray()); + responseBuilder.withGeneratedKey(key); + } + + if (response.hasContext()) + { + BinaryValue context = BinaryValue.unsafeCreate(response.getContext().toByteArray()); + responseBuilder.withContext(context); + } + + return responseBuilder.build(); + + } + + @Override + protected RiakMessage createChannelMessage() + { + return new RiakMessage(RiakMessageCodes.MSG_DtUpdateReq, reqBuilder.build().toByteArray()); + } + + @Override + protected RiakDtPB.DtUpdateResp decode(RiakMessage rawMessage) + { + Operations.checkMessageType(rawMessage, RiakMessageCodes.MSG_DtUpdateResp); + try + { + RiakDtPB.DtUpdateResp resp = RiakDtPB.DtUpdateResp.parseFrom(rawMessage.getData()); + return resp; + } + catch (InvalidProtocolBufferException ex) + { + throw new IllegalArgumentException("Invalid message received", ex); + } + } + + @Override + public Location getQueryInfo() + { + return location; + } + + public static class Builder + { + private final RiakDtPB.DtUpdateReq.Builder reqBuilder = RiakDtPB.DtUpdateReq.newBuilder(); + private final Location location; + private boolean removeOpPresent = false; + + /** + * Construct a builder for a DtUpdateOperation. + * @param location The location of the object in Riak. + */ + public Builder(Location location) + { + if (location == null) + { + throw new IllegalArgumentException("Location cannot be null"); + } + else if (location.getNamespace().getBucketTypeAsString().equals(Namespace.DEFAULT_BUCKET_TYPE)) + { + throw new IllegalArgumentException("Default bucket type does not accept CRDTs"); + } + + reqBuilder.setBucket(ByteString.copyFrom(location.getNamespace().getBucketName().unsafeGetValue())); + reqBuilder.setType(ByteString.copyFrom(location.getNamespace().getBucketType().unsafeGetValue())); + reqBuilder.setKey(ByteString.copyFrom(location.getKey().unsafeGetValue())); + + this.location = location; + } + + public Builder(Namespace namespace) + { + if (namespace == null) + { + throw new IllegalArgumentException("Namespace cannot be null"); + } + else if (namespace.getBucketTypeAsString().equals(Namespace.DEFAULT_BUCKET_TYPE)) + { + throw new IllegalArgumentException("Default bucket type does not accept CRDTs"); + } + + // This is simply for the returned query info + Location loc = new Location(namespace, "RIAK_GENERATED"); + + reqBuilder.setBucket(ByteString.copyFrom(loc.getNamespace().getBucketName().unsafeGetValue())); + reqBuilder.setType(ByteString.copyFrom(loc.getNamespace().getBucketType().unsafeGetValue())); + + this.location = loc; + + } + + /** + * Set the context for this operation. + * + * @param ctx a context from a previous fetch operation. + * @return a reference to this object. + */ + public Builder withContext(BinaryValue ctx) + { + if (null == ctx) + { + throw new IllegalArgumentException("Context cannot be null."); + } + reqBuilder.setContext(ByteString.copyFrom(ctx.unsafeGetValue())); + return this; + } + + /** + * Set the W value for this DtUpdateOperation. + * If not asSet the bucket default is used. + * + * @param w the W value. + * @return a reference to this object. + */ + public Builder withW(int w) + { + reqBuilder.setW(w); + return this; + } + + /** + * Set the DW value for this DtUpdateOperation. + * If not asSet the bucket default is used. + * + * @param dw the DW value. + * @return a reference to this object. + */ + public Builder withDw(int dw) + { + reqBuilder.setDw(dw); + return this; + } + + /** + * Set the PW value for this DtUpdateOperation. + * If not asSet the bucket default is used. + * + * @param pw the PW value. + * @return a reference to this object. + */ + public Builder withPw(int pw) + { + reqBuilder.setPw(pw); + return this; + } + + /** + * Return the object after storing (including any siblings). + * + * @param returnBody true to return the object. + * @return a reference to this object. + */ + public Builder withReturnBody(boolean returnBody) + { + reqBuilder.setReturnBody(returnBody); + return this; + } + + + /** + * Set a timeout for this operation. + * + * @param timeout a timeout in milliseconds. + * @return a reference to this object. + */ + public Builder withTimeout(int timeout) + { + reqBuilder.setTimeout(timeout); + return this; + } + + /** + * Set the n_val value for this operation. + *

+ * Do not use this unless you understand the ramifications + *

+ * + * @param nval the n_val value + * @return a reference to this object. + */ + public Builder withNVal(int nval) + { + reqBuilder.setNVal(nval); + return this; + } + + /** + * Set whether to use sloppy_quorum. + *

+ * Do not use this unless you understand the ramifications + *

+ * + * @param sloppyQuorum true to use sloppy_quorum + * @return a reference to this object. + */ + public Builder withSloppyQuorum(boolean sloppyQuorum) + { + reqBuilder.setSloppyQuorum(sloppyQuorum); + return this; + } + + public DtUpdateOperation build() + { + if (removeOpPresent && !reqBuilder.hasContext()) + { + throw new IllegalStateException("Remove operations cannot be performed without a context."); + } + + return new DtUpdateOperation(this); + } + + RiakDtPB.CounterOp getCounterOp(CounterOp op) + { + return RiakDtPB.CounterOp.newBuilder() + .setIncrement(op.getIncrement()) + .build(); + } + + RiakDtPB.SetOp getSetOp(SetOp op) + { + RiakDtPB.SetOp.Builder setOpBuilder = RiakDtPB.SetOp.newBuilder(); + + for (BinaryValue element : op.getAdds()) + { + setOpBuilder.addAdds(ByteString.copyFrom(element.unsafeGetValue())); + } + + for (BinaryValue element : op.getRemoves()) + { + setOpBuilder.addRemoves(ByteString.copyFrom(element.unsafeGetValue())); + } + + if (setOpBuilder.getRemovesCount() > 0) + { + removeOpPresent = true; + } + return setOpBuilder.build(); + } + + RiakDtPB.MapUpdate.FlagOp getFlagOp(FlagOp op) + { + return op.getEnabled() + ? RiakDtPB.MapUpdate.FlagOp.ENABLE + : RiakDtPB.MapUpdate.FlagOp.DISABLE; + } + + ByteString getRegisterOp(RegisterOp op) + { + return ByteString.copyFrom(op.getValue().unsafeGetValue()); + } + + RiakDtPB.MapField getMapField(MapOp.MapField field) + { + RiakDtPB.MapField.Builder mapFieldBuilder = RiakDtPB.MapField.newBuilder(); + + switch (field.type) + { + case SET: + mapFieldBuilder.setType(RiakDtPB.MapField.MapFieldType.SET); + break; + case REGISTER: + mapFieldBuilder.setType(RiakDtPB.MapField.MapFieldType.REGISTER); + break; + case MAP: + mapFieldBuilder.setType(RiakDtPB.MapField.MapFieldType.MAP); + break; + case FLAG: + mapFieldBuilder.setType(RiakDtPB.MapField.MapFieldType.FLAG); + break; + case COUNTER: + mapFieldBuilder.setType(RiakDtPB.MapField.MapFieldType.COUNTER); + break; + default: + } + mapFieldBuilder.setName(ByteString.copyFrom(field.key.unsafeGetValue())); + return mapFieldBuilder.build(); + } + + RiakDtPB.MapOp getMapOp(MapOp op) + { + RiakDtPB.MapOp.Builder mapOpBuilder = RiakDtPB.MapOp.newBuilder(); + + for (MapOp.MapField field : op.getRemoves()) + { + mapOpBuilder.addRemoves(getMapField(field)); + } + + if (mapOpBuilder.getRemovesCount() > 0) + { + removeOpPresent = true; + } + + for (MapOp.MapUpdate update : op.getUpdates()) + { + RiakDtPB.MapUpdate.Builder mapUpdateBuilder = + RiakDtPB.MapUpdate.newBuilder(); + + switch (update.field.type) + { + case COUNTER: + mapUpdateBuilder.setCounterOp(getCounterOp((CounterOp) update.op)); + break; + case FLAG: + mapUpdateBuilder.setFlagOp(getFlagOp((FlagOp) update.op)); + break; + case MAP: + mapUpdateBuilder.setMapOp(getMapOp((MapOp) update.op)); + break; + case REGISTER: + mapUpdateBuilder.setRegisterOp(getRegisterOp((RegisterOp) update.op)); + break; + case SET: + mapUpdateBuilder.setSetOp(getSetOp((SetOp) update.op)); + break; + default: + throw new IllegalStateException("Unknow datatype encountered"); + } + + mapUpdateBuilder.setField(getMapField(update.field)); + mapOpBuilder.addUpdates(mapUpdateBuilder); + } + + return mapOpBuilder.build(); + + } + + /** + * Add an update op to this operation + * + * @param op the update + * @return this + */ + public Builder withOp(CrdtOp op) + { + if (op instanceof CounterOp) + { + withOp((CounterOp) op); + } + else if (op instanceof MapOp) + { + withOp((MapOp) op); + } + else if (op instanceof SetOp) + { + withOp((SetOp) op); + } + + return this; + } + + private Builder withOp(CounterOp op) + { + reqBuilder.setOp(RiakDtPB.DtOp.newBuilder() + .setCounterOp(getCounterOp(op))); + + return this; + } + + private Builder withOp(MapOp op) + { + reqBuilder.setOp(RiakDtPB.DtOp.newBuilder() + .setMapOp(getMapOp(op))); + return this; + } + + private Builder withOp(SetOp op) + { + + reqBuilder.setOp(RiakDtPB.DtOp.newBuilder() + .setSetOp(getSetOp(op))); + + return this; + } + + } + + public static class Response extends DtFetchOperation.Response + { + private final BinaryValue generatedKey; + + private Response(Init builder) + { + super(builder); + this.generatedKey = builder.generatedKey; + } + + public boolean hasGeneratedKey() + { + return generatedKey != null; + } + + public BinaryValue getGeneratedKey() + { + return generatedKey; + } + + protected static abstract class Init> extends DtFetchOperation.Response.Init + { + private BinaryValue generatedKey; + + T withGeneratedKey(BinaryValue generatedKey) + { + this.generatedKey = generatedKey; + return self(); + } + } + + static class Builder extends Init + { + @Override + protected Builder self() + { + return this; + } + + @Override + protected Response build() + { + return new Response(this); + } + + } + } + +} diff --git a/src/main/java/com/basho/riak/client/core/operations/FetchBucketPropsOperation.java b/src/main/java/com/basho/riak/client/core/operations/FetchBucketPropsOperation.java new file mode 100644 index 000000000..dff694e43 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/FetchBucketPropsOperation.java @@ -0,0 +1,127 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.converters.BucketPropertiesConverter; +import com.basho.riak.client.core.query.BucketProperties; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakPB; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.List; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public class FetchBucketPropsOperation extends FutureOperation +{ + private final RiakPB.RpbGetBucketReq.Builder reqBuilder; + private final Namespace namespace; + + public FetchBucketPropsOperation(Builder builder) + { + this.reqBuilder = builder.reqBuilder; + this.namespace = builder.namespace; + } + + @Override + protected Response convert(List rawResponse) + { + // This isn't streaming, there will only be one response. + RiakPB.RpbBucketProps pbProps = rawResponse.get(0).getProps(); + return new Response(BucketPropertiesConverter.convert(pbProps)); + } + + @Override + protected RiakMessage createChannelMessage() + { + RiakPB.RpbGetBucketReq req = reqBuilder.build(); + return new RiakMessage(RiakMessageCodes.MSG_GetBucketReq, req.toByteArray()); + } + + @Override + protected RiakPB.RpbGetBucketResp decode(RiakMessage rawMessage) + { + Operations.checkMessageType(rawMessage, RiakMessageCodes.MSG_GetBucketResp); + try + { + return RiakPB.RpbGetBucketResp.parseFrom(rawMessage.getData()); + } + catch (InvalidProtocolBufferException ex) + { + throw new IllegalArgumentException("Invalid message received", ex); + } + } + + @Override + public Namespace getQueryInfo() + { + return namespace; + } + + public static class Builder + { + private final RiakPB.RpbGetBucketReq.Builder reqBuilder = + RiakPB.RpbGetBucketReq.newBuilder(); + private final Namespace namespace; + + /** + * Construct a builder for a FetchBucketPropsOperation. + * @param namespace The namespace for the bucket. + */ + public Builder(Namespace namespace) + { + if (namespace == null) + { + throw new IllegalArgumentException("Namespace cannot be null"); + } + reqBuilder.setBucket(ByteString.copyFrom(namespace.getBucketName().unsafeGetValue())); + reqBuilder.setType(ByteString.copyFrom(namespace.getBucketType().unsafeGetValue())); + this.namespace = namespace; + } + + public FetchBucketPropsOperation build() + { + return new FetchBucketPropsOperation(this); + } + } + + /** + * Response from Fetching a bucket's properties. + */ + public static class Response + { + private final BucketProperties props; + private Response(BucketProperties props) + { + this.props = props; + } + + /** + * Returns the fetched BucketProperties. + * @return the BucketProperties. + */ + public BucketProperties getBucketProperties() + { + return props; + } + } +} diff --git a/src/main/java/com/basho/riak/client/core/operations/FetchOperation.java b/src/main/java/com/basho/riak/client/core/operations/FetchOperation.java new file mode 100644 index 000000000..09e940de4 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/FetchOperation.java @@ -0,0 +1,393 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.api.cap.BasicVClock; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.converters.RiakObjectConverter; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakKvPB; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.LinkedList; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * An operation used to fetch an object from Riak. + * + * @author Brian Roach + * @since 2.0 + */ +public class FetchOperation extends FutureOperation +{ + private final RiakKvPB.RpbGetReq.Builder reqBuilder; + Location location; + + private final Logger logger = LoggerFactory.getLogger(FetchOperation.class); + + private FetchOperation(Builder builder) + { + this.reqBuilder = builder.reqBuilder; + this.location = builder.location; + } + + @Override + protected RiakKvPB.RpbGetResp decode(RiakMessage message) + { + Operations.checkMessageType(message, RiakMessageCodes.MSG_GetResp); + + try + { + byte[] data = message.getData(); + + if (data.length == 0) // not found + { + return null; + } + + return RiakKvPB.RpbGetResp.parseFrom(data); + } + catch (InvalidProtocolBufferException e) + { + logger.error("Invalid message received; {}", e); + throw new IllegalArgumentException("Invalid message received", e); + } + } + + @Override + protected FetchOperation.Response convert(List responses) + { + // This is not a streaming op, there will only be one response + if (responses.size() > 1) + { + logger.error("Received {} responses when only one was expected.", responses.size()); + } + + RiakKvPB.RpbGetResp response = responses.get(0); + + FetchOperation.Response.Builder responseBuilder = + new FetchOperation.Response.Builder(); + + // If the response is null ... it means not found. Riak only sends + // a message code and zero bytes when that's the case. (See: decode() ) + // Because that makes sense! + if (null == response) + { + responseBuilder.withNotFound(true); + } + else + { + // To unify the behavior of having just a tombstone vs. siblings + // that include a tombstone, we create an empty object and mark + // it deleted + if (response.getContentCount() == 0) + { + RiakObject ro = new RiakObject() + .setDeleted(true) + .setVClock(new BasicVClock(response.getVclock().toByteArray())); + + responseBuilder.addObject(ro); + } + else + { + responseBuilder.addObjects(RiakObjectConverter.convert(response.getContentList(), response.getVclock())); + } + + responseBuilder.withUnchanged(response.hasUnchanged() ? response.getUnchanged() : false); + + } + + return responseBuilder.build(); + } + + @Override + protected RiakMessage createChannelMessage() + { + RiakKvPB.RpbGetReq req = reqBuilder.build(); + return new RiakMessage(RiakMessageCodes.MSG_GetReq, req.toByteArray()); + } + + @Override + public Location getQueryInfo() + { + return location; + } + + public static class Builder + { + private final RiakKvPB.RpbGetReq.Builder reqBuilder = + RiakKvPB.RpbGetReq.newBuilder(); + private final Location location; + + /** + * Construct a FetchOperation that will retrieve an object from Riak stored + * at the provided Location. + * @param location the location of the object in Riak to fetch. + */ + public Builder(Location location) + { + if (location == null) + { + throw new IllegalArgumentException("Location can not be null."); + } + + reqBuilder.setKey(ByteString.copyFrom(location.getKey().unsafeGetValue())); + reqBuilder.setBucket(ByteString.copyFrom(location.getNamespace().getBucketName().unsafeGetValue())); + reqBuilder.setType(ByteString.copyFrom(location.getNamespace().getBucketType().unsafeGetValue())); + this.location = location; + + } + + /** + * Set the R value for this FetchOperation. + * If not asSet the bucket default is used. + * @param r the R value. + * @return a reference to this object. + */ + public Builder withR(int r) + { + reqBuilder.setR(r); + return this; + } + + /** + * Set the PR value for this query. + * If not asSet the bucket default is used. + * @param pr the PR value. + * @return a reference to this object. + */ + public Builder withPr(int pr) + { + reqBuilder.setPr(pr); + return this; + } + + /** + * Set the not_found_ok value. + *

+ * If true a vnode returning notfound for a key increments the r tally. + * False is higher consistency, true is higher availability. + *

+ *

+ * If not asSet the bucket default is used. + *

+ * @param notFoundOk the not_found_ok value. + * @return a reference to this object. + */ + public Builder withNotFoundOK(boolean notFoundOk) + { + reqBuilder.setNotfoundOk(notFoundOk); + return this; + } + + /** + * Set the basic_quorum value. + *

+ * The parameter controls whether a read request should return early in + * some fail cases. + * E.g. If a quorum of nodes has already + * returned notfound/error, don't wait around for the rest. + *

+ * @param useBasicQuorum the basic_quorum value. + * @return a reference to this object. + */ + public Builder withBasicQuorum(boolean useBasicQuorum) + { + reqBuilder.setBasicQuorum(useBasicQuorum); + return this; + } + + /** + * Set whether to return tombstones. + * @param returnDeletedVClock true to return tombstones, false otherwise. + * @return a reference to this object. + */ + public Builder withReturnDeletedVClock(boolean returnDeletedVClock) + { + reqBuilder.setDeletedvclock(returnDeletedVClock); + return this; + } + + /** + * Return only the metadata. + *

+ * Causes Riak to only return the metadata for the object. The value + * will be asSet to null. + * @param headOnly true to return only metadata. + * @return a reference to this object. + */ + public Builder withHeadOnly(boolean headOnly) + { + reqBuilder.setHead(headOnly); + return this; + } + + /** + * Do not return the object if the supplied vclock matches. + * @param vclock the vclock to match on + * @return a refrence to this object. + */ + public Builder withIfNotModified(byte[] vclock) + { + reqBuilder.setIfModified(ByteString.copyFrom(vclock)); + return this; + } + + /** + * Set a timeout for this operation. + * @param timeout a timeout in milliseconds. + * @return a reference to this object. + */ + public Builder withTimeout(int timeout) + { + reqBuilder.setTimeout(timeout); + return this; + } + + /** + * Set the n_val for this operation. + *

+ * Do not use this unless you understand the ramifications + *

+ * @param nval the n_val value + * @return a reference to this object. + */ + public Builder withNVal(int nval) + { + reqBuilder.setNVal(nval); + return this; + } + + /** + * Set whether to use sloppy_quorum. + *

+ * Do not use this unless you understand the ramifications + *

+ * @param sloppyQuorum true to use sloppy_quorum + * @return a reference to this object. + */ + public Builder withSloppyQuorum(boolean sloppyQuorum) + { + reqBuilder.setSloppyQuorum(sloppyQuorum); + return this; + } + + public FetchOperation build() + { + return new FetchOperation(this); + } + + + } + + protected static abstract class KvResponseBase + { + private final List objectList; + + protected KvResponseBase(Init builder) + { + this.objectList = builder.objectList; + } + + public List getObjectList() + { + return objectList; + } + + protected static abstract class Init> + { + private final List objectList = + new LinkedList(); + protected abstract T self(); + protected abstract KvResponseBase build(); + + T addObject(RiakObject object) + { + objectList.add(object); + return self(); + } + + T addObjects(List objects) + { + objectList.addAll(objects); + return self(); + } + } + } + + + public static class Response extends KvResponseBase + { + private final boolean notFound; + private final boolean unchanged; + + private Response(Init builder) + { + super(builder); + this.notFound = builder.notFound; + this.unchanged = builder.unchanged; + } + + public boolean isNotFound() + { + return notFound; + } + + public boolean isUnchanged() + { + return unchanged; + } + + protected static abstract class Init> extends KvResponseBase.Init + { + private boolean notFound; + private boolean unchanged; + + T withNotFound(boolean notFound) + { + this.notFound = notFound; + return self(); + } + + T withUnchanged(boolean unchanged) + { + this.unchanged = unchanged; + return self(); + } + } + + static class Builder extends Init + { + @Override + protected Builder self() + { + return this; + } + + @Override + protected Response build() + { + return new Response(this); + } + } + } +} diff --git a/src/main/java/com/basho/riak/client/core/operations/ListBucketsOperation.java b/src/main/java/com/basho/riak/client/core/operations/ListBucketsOperation.java new file mode 100644 index 000000000..2baf3585e --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/ListBucketsOperation.java @@ -0,0 +1,161 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.util.BinaryValue; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakKvPB; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import java.util.ArrayList; +import java.util.List; + +public class ListBucketsOperation extends FutureOperation +{ + private final RiakKvPB.RpbListBucketsReq.Builder reqBuilder; + private final BinaryValue bucketType; + + private ListBucketsOperation(Builder builder) + { + this.reqBuilder = builder.reqBuilder; + this.bucketType = builder.bucketType; + } + + @Override + protected boolean done(RiakKvPB.RpbListBucketsResp message) + { + return message.getDone(); + } + + @Override + protected ListBucketsOperation.Response convert(List rawResponse) + { + List buckets = new ArrayList(rawResponse.size()); + for (RiakKvPB.RpbListBucketsResp resp : rawResponse) + { + for (ByteString bucket : resp.getBucketsList()) + { + buckets.add(BinaryValue.unsafeCreate(bucket.toByteArray())); + } + } + return new Response(bucketType, buckets); + } + + @Override + protected RiakMessage createChannelMessage() + { + return new RiakMessage(RiakMessageCodes.MSG_ListBucketsReq, reqBuilder.build().toByteArray()); + } + + @Override + protected RiakKvPB.RpbListBucketsResp decode(RiakMessage rawMessage) + { + try + { + Operations.checkMessageType(rawMessage, RiakMessageCodes.MSG_ListBucketsResp); + return RiakKvPB.RpbListBucketsResp.parseFrom(rawMessage.getData()); + } + catch (InvalidProtocolBufferException e) + { + throw new IllegalArgumentException("Invalid message received", e); + } + + } + + @Override + public BinaryValue getQueryInfo() + { + return bucketType; + } + + public static class Builder + { + private final RiakKvPB.RpbListBucketsReq.Builder reqBuilder = + RiakKvPB.RpbListBucketsReq.newBuilder().setStream(true); + private BinaryValue bucketType = BinaryValue.create(Namespace.DEFAULT_BUCKET_TYPE); + + /** + * Create a Builder for a ListBucketsOperation. + */ + public Builder() + {} + + /** + * Provide a timeout for this operation. + * @param timeout value in milliseconds + * @return a reference to this object + */ + public Builder withTimeout(int timeout) + { + if (timeout <= 0) + { + throw new IllegalArgumentException("Timeout can not be zero or less."); + } + reqBuilder.setTimeout(timeout); + return this; + } + + /** + * Set the bucket type. + * If unset {@link Namespace#DEFAULT_BUCKET_TYPE} is used. + * @param bucketType the bucket type to use + * @return A reference to this object. + */ + public Builder withBucketType(BinaryValue bucketType) + { + if (null == bucketType || bucketType.length() == 0) + { + throw new IllegalArgumentException("Bucket type can not be null or zero length"); + } + reqBuilder.setType(ByteString.copyFrom(bucketType.unsafeGetValue())); + this.bucketType = bucketType; + return this; + } + + public ListBucketsOperation build() + { + return new ListBucketsOperation(this); + } + + } + + public static class Response + { + private final BinaryValue bucketType; + private final List buckets; + + Response(BinaryValue bucketType, List buckets) + { + this.bucketType = bucketType; + this.buckets = buckets; + } + + public BinaryValue getBucketType() + { + return bucketType; + } + + public List getBuckets() + { + return buckets; + } + } + +} diff --git a/src/main/java/com/basho/riak/client/core/operations/ListKeysOperation.java b/src/main/java/com/basho/riak/client/core/operations/ListKeysOperation.java new file mode 100644 index 000000000..d59642d5b --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/ListKeysOperation.java @@ -0,0 +1,161 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.util.BinaryValue; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakKvPB; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import java.util.ArrayList; +import java.util.List; + +public class ListKeysOperation extends FutureOperation +{ + private final Namespace namespace; + private final RiakKvPB.RpbListKeysReq.Builder reqBuilder; + + private ListKeysOperation(Builder builder) + { + this.reqBuilder = builder.reqBuilder; + this.namespace = builder.namespace; + } + + @Override + protected Response convert(List rawResponse) + { + Response.Builder builder = new Response.Builder(); + for (RiakKvPB.RpbListKeysResp resp : rawResponse) + { + for (ByteString bucket : resp.getKeysList()) + { + builder.addKey(BinaryValue.unsafeCreate(bucket.toByteArray())); + } + } + return builder.build(); + } + + @Override + protected RiakMessage createChannelMessage() + { + return new RiakMessage(RiakMessageCodes.MSG_ListKeysReq, reqBuilder.build().toByteArray()); + } + + @Override + protected RiakKvPB.RpbListKeysResp decode(RiakMessage rawMessage) + { + try + { + Operations.checkMessageType(rawMessage, RiakMessageCodes.MSG_ListKeysResp); + return RiakKvPB.RpbListKeysResp.parseFrom(rawMessage.getData()); + } + catch (InvalidProtocolBufferException e) + { + throw new IllegalArgumentException("Invalid message received", e); + } + } + + @Override + protected boolean done(RiakKvPB.RpbListKeysResp message) + { + return message.getDone(); + } + + @Override + public Namespace getQueryInfo() + { + return namespace; + } + + public static class Builder + { + private final RiakKvPB.RpbListKeysReq.Builder reqBuilder = + RiakKvPB.RpbListKeysReq.newBuilder(); + private final Namespace namespace; + + /** + * Construct a builder for a ListKeysOperaiton. + * @param namespace The namespace in Riak. + */ + public Builder(Namespace namespace) + { + if (namespace == null) + { + throw new IllegalArgumentException("Namespace cannot be null"); + } + reqBuilder.setBucket(ByteString.copyFrom(namespace.getBucketName().unsafeGetValue())); + reqBuilder.setType(ByteString.copyFrom(namespace.getBucketType().unsafeGetValue())); + this.namespace = namespace; + } + + public Builder withTimeout(int timeout) + { + if (timeout <= 0) + { + throw new IllegalArgumentException("Timeout can not be zero or less"); + } + reqBuilder.setTimeout(timeout); + return this; + } + + public ListKeysOperation build() + { + return new ListKeysOperation(this); + } + } + + public static class Response + { + private final List keys; + private Response(Builder builder) + { + this.keys = builder.keys; + } + + public List getKeys() + { + return keys; + } + + static class Builder + { + private List keys = new ArrayList(); + + Builder addKeys(List keys) + { + this.keys.addAll(keys); + return this; + } + + Builder addKey(BinaryValue key) + { + this.keys.add(key); + return this; + } + + Response build() + { + return new Response(this); + } + + } + + } +} diff --git a/src/main/java/com/basho/riak/client/core/operations/MapReduceOperation.java b/src/main/java/com/basho/riak/client/core/operations/MapReduceOperation.java new file mode 100644 index 000000000..0ee1fb182 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/MapReduceOperation.java @@ -0,0 +1,188 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.util.BinaryValue; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakKvPB; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import java.io.IOException; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A Map/Reduce Operation on Riak. No error checking is done on the content type of the content itself + * with the exception to making sure they are provided. + */ +public class MapReduceOperation extends FutureOperation +{ + private final RiakKvPB.RpbMapRedReq.Builder reqBuilder; + private final BinaryValue mapReduce; + private final Logger logger = LoggerFactory.getLogger(MapReduceOperation.class); + + private MapReduceOperation(Builder builder) + { + this.reqBuilder = builder.reqBuilder; + this.mapReduce = builder.mapReduce; + } + + @Override + protected Response convert(List rawResponse) + { + // Riak streams the result back. Each message from Riak contains a int + // that tells you what phase the result is from. The result from a phase + // can span multiple messages. Each result chunk is a JSON array. + + final JsonNodeFactory factory = JsonNodeFactory.instance; + final ObjectMapper mapper = new ObjectMapper(); + final Map resultMap = new LinkedHashMap(); + + int phase = 0; + + for (RiakKvPB.RpbMapRedResp response : rawResponse) + { + if (response.hasPhase()) + { + phase = response.getPhase(); + } + if (response.hasResponse()) + { + ArrayNode jsonArray; + if (resultMap.containsKey(phase)) + { + jsonArray = resultMap.get(phase); + } + else + { + jsonArray = factory.arrayNode(); + resultMap.put(phase, jsonArray); + } + + JsonNode responseJson; + try + { + responseJson = mapper.readTree(response.getResponse().toStringUtf8()); + } + catch (IOException ex) + { + logger.error("Mapreduce job returned non-JSON; {}",response.getResponse().toStringUtf8()); + throw new RuntimeException("Non-JSON response from MR job", ex); + } + + if (responseJson.isArray()) + { + jsonArray.addAll((ArrayNode)responseJson); + } + else + { + logger.error("Mapreduce job returned JSON that wasn't an array; {}", response.getResponse().toStringUtf8()); + } + } + } + return new Response(resultMap); + } + + @Override + protected RiakMessage createChannelMessage() + { + RiakKvPB.RpbMapRedReq request = reqBuilder.build(); + return new RiakMessage(RiakMessageCodes.MSG_MapRedReq, request.toByteArray()); + } + + @Override + protected RiakKvPB.RpbMapRedResp decode(RiakMessage rawMessage) + { + Operations.checkMessageType(rawMessage, RiakMessageCodes.MSG_MapRedResp); + try + { + return RiakKvPB.RpbMapRedResp.parseFrom(rawMessage.getData()); + } + catch (InvalidProtocolBufferException e) + { + throw new IllegalArgumentException(e); + } + } + + @Override + protected boolean done(RiakKvPB.RpbMapRedResp message) + { + return message.getDone(); + } + + @Override + public BinaryValue getQueryInfo() + { + return mapReduce; + } + + public static class Builder + { + private final RiakKvPB.RpbMapRedReq.Builder reqBuilder = + RiakKvPB.RpbMapRedReq.newBuilder(); + private final BinaryValue mapReduce; + + /** + * Create a MapReduce operation builder with the given function. + * + * @param mapReduce a mapReduce query. + */ + public Builder(BinaryValue mapReduce) + { + if ((null == mapReduce) || mapReduce.length() == 0) + { + throw new IllegalArgumentException("MapReduce can not be null or empty."); + } + + + reqBuilder.setRequest(ByteString.copyFrom(mapReduce.unsafeGetValue())) + .setContentType(ByteString.copyFromUtf8("application/json")); + this.mapReduce = mapReduce; + + } + + public MapReduceOperation build() + { + return new MapReduceOperation(this); + } + } + + public static class Response + { + private final Map resultMap; + + Response(Map results) + { + this.resultMap = results; + } + + public Map getResults() + { + return resultMap; + } + + } +} diff --git a/src/main/java/com/basho/riak/client/core/operations/Operations.java b/src/main/java/com/basho/riak/client/core/operations/Operations.java new file mode 100644 index 000000000..8aff1116b --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/Operations.java @@ -0,0 +1,45 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.RiakMessage; + + +public class Operations +{ + + public static void checkMessageType(RiakMessage msg, byte expected) + { + byte pbMessageCode = msg.getCode(); + if (pbMessageCode != expected) + { + throw new IllegalStateException("Wrong response; expected " + + expected + + " received " + pbMessageCode); + } + + } + + /** + * Convert a Java signed int to unsigned. + * Returns the unsigned value as a (signed) long. + * @param i a java signed int + * @return a long containing the converted value. + */ + public static long getUnsignedIntValue(int i) { + return i & 0x00000000ffffffffL; + } +} diff --git a/src/main/java/com/basho/riak/client/core/operations/PingOperation.java b/src/main/java/com/basho/riak/client/core/operations/PingOperation.java new file mode 100644 index 000000000..3f1f3685d --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/PingOperation.java @@ -0,0 +1,57 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.protobuf.RiakMessageCodes; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Brian Roach + */ +public class PingOperation extends FutureOperation +{ + private final Logger logger = LoggerFactory.getLogger(PingOperation.class); + + @Override + protected Void convert(List rawResponse) + { + return null; + } + + @Override + protected RiakMessage createChannelMessage() + { + return new RiakMessage(RiakMessageCodes.MSG_PingReq, new byte[0]); + } + + @Override + protected Void decode(RiakMessage rawMessage) + { + return null; + } + + @Override + public Void getQueryInfo() + { + return null; + } +} diff --git a/src/main/java/com/basho/riak/client/core/operations/ResetBucketPropsOperation.java b/src/main/java/com/basho/riak/client/core/operations/ResetBucketPropsOperation.java new file mode 100644 index 000000000..6c4fec32d --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/ResetBucketPropsOperation.java @@ -0,0 +1,96 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakPB; +import com.google.protobuf.ByteString; +import java.util.List; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public class ResetBucketPropsOperation extends FutureOperation +{ + private final RiakPB.RpbResetBucketReq.Builder reqBuilder; + private final Namespace namespace; + + private ResetBucketPropsOperation(Builder builder) + { + this.reqBuilder = builder.reqBuilder; + this.namespace = builder.namespace; + } + + @Override + protected Void convert(List rawResponse) + { + return null; + } + + @Override + protected RiakMessage createChannelMessage() + { + RiakPB.RpbResetBucketReq req = + reqBuilder.build(); + + return new RiakMessage(RiakMessageCodes.MSG_ResetBucketReq, req.toByteArray()); + } + + @Override + protected Void decode(RiakMessage rawMessage) + { + Operations.checkMessageType(rawMessage, RiakMessageCodes.MSG_ResetBucketResp); + return null; + } + + @Override + public Namespace getQueryInfo() + { + return namespace; + } + + public static class Builder + { + private final RiakPB.RpbResetBucketReq.Builder reqBuilder = + RiakPB.RpbResetBucketReq.newBuilder(); + private final Namespace namespace; + + /** + * Construct a builder for a ResetBucketPropsOperation. + * @param namespace The namespace in Riak. + */ + public Builder(Namespace namespace) + { + if (namespace == null) + { + throw new IllegalArgumentException("Namespace cannot be null"); + } + reqBuilder.setBucket(ByteString.copyFrom(namespace.getBucketName().unsafeGetValue())); + reqBuilder.setType(ByteString.copyFrom(namespace.getBucketType().unsafeGetValue())); + this.namespace = namespace; + } + + public ResetBucketPropsOperation build() + { + return new ResetBucketPropsOperation(this); + } + } +} diff --git a/src/main/java/com/basho/riak/client/core/operations/SearchOperation.java b/src/main/java/com/basho/riak/client/core/operations/SearchOperation.java new file mode 100644 index 000000000..e7ed22ae4 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/SearchOperation.java @@ -0,0 +1,320 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.util.BinaryValue; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakPB.RpbPair; +import com.basho.riak.protobuf.RiakSearchPB; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * A Riak Search or Yokozuna query operation. + *

+ * Due to the nature of both Riak Search and Yokozuna, all Strings must be + * UTF-8 encoded. + *

+ *

+ * This operation will fail if search is not enabled or the index does not exist. + *

+ * + * @author Brian Roach + * @since 2.0 + */ +public class SearchOperation extends FutureOperation +{ + private final String queryString; + private final BinaryValue indexName; + private final RiakSearchPB.RpbSearchQueryReq.Builder reqBuilder; + + private SearchOperation(Builder builder) + { + this.reqBuilder = builder.reqBuilder; + this.queryString = builder.queryString; + this.indexName = builder.indexName; + } + + @Override + protected SearchOperation.Response convert(List rawResponse) + { + // This isn't a streaming op, there will only be one protobuf + RiakSearchPB.RpbSearchQueryResp resp = rawResponse.get(0); + List>> docList = new LinkedList>>(); + for (RiakSearchPB.RpbSearchDoc pbDoc : resp.getDocsList()) + { + Map> map = new HashMap>(); + for (RpbPair pair : pbDoc.getFieldsList()) + { + String key = pair.getKey().toStringUtf8(); + List list = map.get(key); + if (null == list) + { + list = new LinkedList(); + map.put(key, list); + } + list.add(pair.getValue().toStringUtf8()); + } + docList.add(map); + } + return new Response(docList, resp.getMaxScore(), resp.getNumFound()); + + } + + @Override + protected RiakMessage createChannelMessage() + { + RiakSearchPB.RpbSearchQueryReq req = reqBuilder.build(); + return new RiakMessage(RiakMessageCodes.MSG_SearchQueryReq, req.toByteArray()); + } + + @Override + protected RiakSearchPB.RpbSearchQueryResp decode(RiakMessage rawMessage) + { + Operations.checkMessageType(rawMessage, RiakMessageCodes.MSG_SearchQueryResp); + try + { + return RiakSearchPB.RpbSearchQueryResp.parseFrom(rawMessage.getData()); + } + catch (InvalidProtocolBufferException ex) + { + throw new IllegalArgumentException("Invalid message received", ex); + } + } + + @Override + public BinaryValue getQueryInfo() + { + return BinaryValue.create(indexName.toString() +": " +queryString); + } + + public static class Builder + { + + private final BinaryValue indexName; + private final String queryString; + private final RiakSearchPB.RpbSearchQueryReq.Builder reqBuilder = + RiakSearchPB.RpbSearchQueryReq.newBuilder(); + + public Builder(BinaryValue indexName, String queryString) + { + if (null == indexName || indexName.length() == 0) + { + throw new IllegalArgumentException("Index name cannot be null or zero length"); + } + if (null == queryString || queryString.length() == 0) + { + throw new IllegalArgumentException("Query string cannot be null or zero length"); + } + + this.indexName = indexName; + this.queryString = queryString; + reqBuilder.setIndex(ByteString.copyFrom(indexName.unsafeGetValue())); + reqBuilder.setQ(ByteString.copyFromUtf8(queryString)); + } + + /** + * Specify the maximum number of results to return. + * Riak defaults to 10 if this is not asSet. + * + * @param rows the maximum number of results to return. + * @return a reference to this object. + */ + public Builder withNumRows(int rows) + { + if (rows <= 0) + { + throw new IllegalArgumentException("Rows must be >= 1"); + } + reqBuilder.setRows(rows); + return this; + } + + /** + * Specify the starting result of the query. + * Useful for pagination. The default is 0. + * + * @param start the index of the starting result. + * @return a reference to this object. + */ + public Builder withStart(int start) + { + if (start < 0) + { + throw new IllegalArgumentException("Start must be >= 0"); + } + reqBuilder.setStart(start); + return this; + } + + /** + * Sort the results on the specified field name. + * Default is “none”, which causes the results to be sorted in descending order by score. + * + * @param fieldName the fieldname to sort the results on. + * @return a reference to this object. + */ + public Builder withSortField(String fieldName) + { + stringCheck(fieldName); + reqBuilder.setSort(ByteString.copyFromUtf8(fieldName)); + return this; + } + + /** + * Filters the search by an additional query scoped to inline fields. + * + * @param filterQuery the filter query. + * @return a reference to this object. + */ + public Builder withFilterQuery(String filterQuery) + { + stringCheck(filterQuery); + reqBuilder.setFilter(ByteString.copyFromUtf8(filterQuery)); + return this; + } + + /** + * Use the provided field as the default. + * Overrides the “default_field” setting in the schema file. + * + * @param fieldName the name of the field. + * @return a reference to this object. + */ + public Builder withDefaultField(String fieldName) + { + stringCheck(fieldName); + reqBuilder.setDf(ByteString.copyFromUtf8(fieldName)); + return this; + } + + /** + * Set the default operation. + * Allowed settings are either “and” or “or”. + * Overrides the “default_op” setting in the schema file. + * + * @param op A string containing either "and" or "or". + * @return a reference to this object. + */ + public Builder withDefaultOperation(String op) + { + stringCheck(op); + reqBuilder.setOp(ByteString.copyFromUtf8(op)); + return this; + } + + /** + * Only return the provided fields. + * Filters the results to only contain the provided fields. + * + * @param fields a list of field names. + * @return a reference to this object. + */ + public Builder withReturnFields(List fields) + { + for (String f : fields) + { + stringCheck(f); + reqBuilder.addFl(ByteString.copyFromUtf8(f)); + } + return this; + } + + /** + * Sorts all of the results by bucket key, or the search score, before the given rows are chosen. + * This is useful when paginating to ensure the results are returned in a consistent order. + * + * @param presort a String containing either "key" or "score". + * @return a reference to this object. + */ + public Builder withPresort(String presort) + { + stringCheck(presort); + reqBuilder.setPresort(ByteString.copyFromUtf8(presort)); + return this; + } + + private void stringCheck(String arg) + { + if (null == arg || arg.length() == 0) + { + throw new IllegalArgumentException("Argument cannot be null or zero length"); + } + } + + public SearchOperation build() + { + return new SearchOperation(this); + } + } + + public static class Response implements Iterable + { + private final List>> results; + private final float maxScore; + private final int numResults; + + Response(List>> results, float maxScore, int numResults) + { + this.results = results; + this.maxScore = maxScore; + this.numResults = numResults; + } + + @Override + public Iterator>> iterator() + { + return results.iterator(); + } + + /** + * Returns the max score from the search query. + * @return the max score. + */ + public float getMaxScore() + { + return maxScore; + } + + /** + * Returns the number of results from the search query. + * @return the number of results. + */ + public int numResults() + { + return numResults; + } + + /** + * Returns the entire list of results from the search query. + * @return a list containing all the result sets. + */ + public List>> getAllResults() + { + return results; + } + + } + +} diff --git a/src/main/java/com/basho/riak/client/core/operations/SecondaryIndexQueryOperation.java b/src/main/java/com/basho/riak/client/core/operations/SecondaryIndexQueryOperation.java new file mode 100644 index 000000000..f19961a6c --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/SecondaryIndexQueryOperation.java @@ -0,0 +1,599 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.util.BinaryValue; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakKvPB; +import com.basho.riak.protobuf.RiakPB.RpbPair; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Brian Roach + * @author Alex Moore + * @since 2.0 + */ +public class SecondaryIndexQueryOperation extends FutureOperation +{ + private final RiakKvPB.RpbIndexReq pbReq; + private final Query query; + + private SecondaryIndexQueryOperation(Builder builder) + { + // Yo dawg, we don't ever not want to use streaming. + builder.pbReqBuilder.setStream(true); + this.query = builder.query; + this.pbReq = builder.pbReqBuilder.build(); + } + + @Override + protected SecondaryIndexQueryOperation.Response convert(List rawResponse) + { + SecondaryIndexQueryOperation.Response.Builder responseBuilder = + new SecondaryIndexQueryOperation.Response.Builder(); + + for (RiakKvPB.RpbIndexResp pbEntry : rawResponse) + { + /** + * The 2i API is inconsistent on the Riak side. If it's not + * a range query, return_terms is ignored it only returns the + * list of object keys and you have to have + * preserved the index key if you want to return it to the user + * with the results. + * + * Also, the $key index queries just ignore return_terms altogether. + */ + + if (pbReq.getReturnTerms() && !query.indexName.toString().equalsIgnoreCase("$key")) + { + if (pbReq.hasRangeMin()) + { + for (RpbPair pair : pbEntry.getResultsList()) + { + responseBuilder.addEntry(new Response.Entry(BinaryValue.unsafeCreate(pair.getKey().toByteArray()), + BinaryValue.unsafeCreate(pair.getValue().toByteArray()))); + } + } + else + { + for (ByteString objKey : pbEntry.getKeysList()) + { + responseBuilder.addEntry(new Response.Entry(BinaryValue.unsafeCreate(pbReq.getKey().toByteArray()), + BinaryValue.unsafeCreate(objKey.toByteArray()))); + } + } + } + else + { + /** + * If return_terms wasn't specified only the object keys are returned + */ + for (ByteString objKey : pbEntry.getKeysList()) + { + responseBuilder.addEntry(new Response.Entry(BinaryValue.unsafeCreate(objKey.toByteArray()))); + } + } + + if (pbEntry.hasContinuation()) + { + responseBuilder.withContinuation(BinaryValue.unsafeCreate(pbEntry.getContinuation().toByteArray())); + } + } + return responseBuilder.build(); + } + + @Override + protected RiakMessage createChannelMessage() + { + return new RiakMessage(RiakMessageCodes.MSG_IndexReq, pbReq.toByteArray()); + } + + @Override + protected RiakKvPB.RpbIndexResp decode(RiakMessage rawMessage) + { + try + { + Operations.checkMessageType(rawMessage, RiakMessageCodes.MSG_IndexResp); + return RiakKvPB.RpbIndexResp.parseFrom(rawMessage.getData()); + } + catch (InvalidProtocolBufferException e) + { + throw new IllegalArgumentException("Invalid message received", e); + } + } + + @Override + protected boolean done(RiakKvPB.RpbIndexResp msg) + { + return msg.getDone(); + } + + @Override + public Query getQueryInfo() + { + return query; + } + + + /** + * Builder that constructs a QueryOperation. + */ + public static class Builder + { + private final RiakKvPB.RpbIndexReq.Builder pbReqBuilder = RiakKvPB.RpbIndexReq.newBuilder(); + private final Query query; + + /** + * Constructs a builder for a QueryOperation. + * The index name must be the complete name with the _int or _bin suffix. + * @param query A 2i query. + */ + public Builder(Query query) + { + if (query == null) + { + throw new IllegalArgumentException("Query cannot be null."); + } + + this.query = query; + + pbReqBuilder.setBucket(ByteString.copyFrom(query.namespace.getBucketName().unsafeGetValue())) + .setType(ByteString.copyFrom(query.namespace.getBucketType().unsafeGetValue())) + .setIndex(ByteString.copyFrom(query.indexName.unsafeGetValue())) + .setReturnTerms(query.returnKeyAndIndex); + + if (query.indexKey != null) + { + pbReqBuilder.setKey(ByteString.copyFrom(query.indexKey.unsafeGetValue())) + .setQtype(RiakKvPB.RpbIndexReq.IndexQueryType.eq); + } + else + { + pbReqBuilder.setRangeMin(ByteString.copyFrom(query.rangeStart.unsafeGetValue())) + .setRangeMax(ByteString.copyFrom(query.rangeEnd.unsafeGetValue())) + .setQtype(RiakKvPB.RpbIndexReq.IndexQueryType.range); + } + + if (query.maxResults != null) + { + pbReqBuilder.setMaxResults(query.maxResults); + } + + if (query.continuation != null) + { + pbReqBuilder.setContinuation(ByteString.copyFrom(query.continuation.unsafeGetValue())); + } + + if (query.paginationSort != null) + { + pbReqBuilder.setPaginationSort(query.paginationSort); + } + + if (query.termFilter != null) + { + pbReqBuilder.setTermRegex(ByteString.copyFrom(query.termFilter.unsafeGetValue())); + } + + if (query.timeout != null) + { + pbReqBuilder.setTimeout(query.timeout); + } + } + + /** + * Construct a new QueryOperation. + * @return a QueryOperation + */ + public SecondaryIndexQueryOperation build() + { + return new SecondaryIndexQueryOperation(this); + } + } + + public static class Query + { + private final Namespace namespace; + private final BinaryValue indexName; + private final BinaryValue indexKey; + private final BinaryValue rangeStart; + private final BinaryValue rangeEnd; + private final boolean returnKeyAndIndex; + private final Integer maxResults; + private final BinaryValue continuation; + private final Boolean paginationSort; + private final BinaryValue termFilter; + private final Integer timeout; + + private Query(Builder builder) + { + this.indexName = builder.indexName; + this.indexKey = builder.indexKey; + this.rangeStart = builder.rangeStart; + this.rangeEnd = builder.rangeEnd; + this.returnKeyAndIndex = builder.returnKeyAndIndex; + this.maxResults = builder.maxResults; + this.continuation = builder.continuation; + this.paginationSort = builder.paginationSort; + this.termFilter = builder.termFilter; + this.namespace = builder.namespace; + this.timeout = builder.timeout; + } + + /** + * Return the location for the Query. + * @return the location. + */ + public Namespace getNamespace() + { + return namespace; + } + + /** + * @return the indexName + */ + public BinaryValue getIndexName() + { + return indexName; + } + + /** + * @return the indexKey + */ + public BinaryValue getIndexKey() + { + return indexKey; + } + + /** + * @return the rangeStart + */ + public BinaryValue getRangeStart() + { + return rangeStart; + } + + /** + * @return the rangeEnd + */ + public BinaryValue getRangeEnd() + { + return rangeEnd; + } + + /** + * @return the returnKeyAndIndex + */ + public boolean isReturnKeyAndIndex() + { + return returnKeyAndIndex; + } + + /** + * @return the maxResults + */ + public int getMaxResults() + { + return maxResults; + } + + /** + * @return the continuation + */ + public BinaryValue getContinuation() + { + return continuation; + } + + /** + * @return the paginationSort + */ + public boolean isPaginationSort() + { + return paginationSort; + } + + /** + * @return the termFilter + */ + public BinaryValue getTermFilter() + { + return termFilter; + } + + /** + * @return the timeout value, or null if not set. + */ + public Integer getTimeout() + { + return timeout; + } + + + public static class Builder + { + private final Namespace namespace; + private final BinaryValue indexName; + private BinaryValue indexKey; + private BinaryValue rangeStart; + private BinaryValue rangeEnd; + private boolean returnKeyAndIndex; + private Integer maxResults; + private BinaryValue continuation; + private Boolean paginationSort; + private BinaryValue termFilter; + private Integer timeout; + + /** + * Constructs a builder for a (2i) Query. + * The index name must be the complete name with the _int or _bin suffix. + * @param namespace the namespace for this Query + * @param indexName the name of the index (including suffix). + */ + public Builder(Namespace namespace, BinaryValue indexName) + { + if (namespace == null) + { + throw new IllegalArgumentException("Namespace cannot be null"); + } + else if (null == indexName || indexName.length() == 0) + { + throw new IllegalArgumentException("Index name cannot be null or zero length"); + } + this.indexName = indexName; + this.namespace = namespace; + } + + /** + * Set a single secondary index key to use for query. + * If querying a _int index the bytes must be the UTF-8 text + * representation of an integer (Yes, really). + * @param key the secondary index key. + * @return a reference to this object. + */ + public Builder withIndexKey(BinaryValue key) + { + this.indexKey = key; + return this; + } + + /** + * Set the start value for a range query. + * If querying a _int index the bytes must be the UTF-8 text + * representation of an integer (Yes, really). + * @param startingIndex the starting index for a range query. + * @return a reference to this object. + */ + public Builder withRangeStart(BinaryValue startingIndex) + { + this.rangeStart = startingIndex; + return this; + } + + /** + * Set the ending value for a range query. + * If querying a _int index the bytes must be the UTF-8 text + * representation of an integer (Yes, really). + * @param endIndex the ending index for a range query. + * @return a reference to this object. + */ + public Builder withRangeEnd(BinaryValue endIndex) + { + this.rangeEnd = endIndex; + return this; + } + + /** + * Set whether to return the index keys with the Riak object keys. + * Setting this to true will return both the index key and the Riak + * object's key. The default is false (only to return the Riak object keys). + * @param returnBoth true to return both index and object keys, false to return only object keys. + * @return a reference to this object. + */ + public Builder withReturnKeyAndIndex(boolean returnBoth) + { + this.returnKeyAndIndex = returnBoth; + return this; + } + + /** + * Set the maximum number of results returned by the query. + * @param maxResults the number of results. + * @return a reference to this object. + */ + public Builder withMaxResults(int maxResults) + { + this.maxResults = maxResults; + return this; + } + + /** + * Set the continuation for this query. + * @param continuation the continuation. + * @return a reference to this object. + */ + public Builder withContinuation(BinaryValue continuation) + { + this.continuation = continuation; + return this; + } + + /** + * Set whether to sort the results of a non-paginated 2i query. + *

+ * Setting this to true will sort the results in Riak before returning them. + *

+ *

+ * Note that this is not recommended for queries that could return a large + * result set; the overhead in Riak is substantial. + *

+ * + * @param orderByKey true to sort the results, false to return as-is. + * @return a reference to this object. + */ + public Builder withPaginationSort(boolean orderByKey) + { + this.paginationSort = orderByKey; + return this; + } + + /** + * Set the regex to filter result terms by for this query. + * @param filter the regex to filter terms by. + * @return a reference to this object. + */ + public Builder withRegexTermFilter(BinaryValue filter) + { + this.termFilter = filter; + return this; + } + + /** + * Set the timeout for the query. + *

+ * Sets the server-side timeout value for this query. + *

+ * @param timeout + * @return a reference to this object. + */ + public Builder withTimeout(int timeout) + { + this.timeout = timeout; + return this; + } + + public Query build() + { + // sanity checks + if ( rangeStart == null && rangeEnd == null && indexKey == null) + { + throw new IllegalArgumentException("An index key or range must be supplied"); + } + else if ( (rangeStart != null && rangeEnd == null) || + (rangeEnd != null && rangeStart == null ) ) + { + throw new IllegalArgumentException("When specifying ranges both start and end must be Set"); + } + else if (rangeStart != null && indexKey != null) + { + throw new IllegalArgumentException("Cannot specify single index key and range"); + } + else if (maxResults != null && (paginationSort != null && !paginationSort)) + { + throw new IllegalArgumentException("Cannot set paginationSort=false while setting maxResults"); + } + else if (termFilter != null && indexName.toStringUtf8().endsWith("_int")) + { + throw new IllegalArgumentException("Cannot use term regular expression in integer query"); + } + + return new Query(this); + } + } + } + + public static class Response + { + private final BinaryValue continuation; + private final List entryList; + + private Response(Builder builder) + { + this.continuation = builder.continuation; + this.entryList = builder.entryList; + } + + public boolean hasContinuation() + { + return continuation != null; + } + + public BinaryValue getContinuation() + { + return continuation; + } + + public List getEntryList() + { + return entryList; + } + + public static class Entry + { + private final BinaryValue indexKey; + private final BinaryValue objectKey; + + Entry(BinaryValue objectKey) + { + this(null, objectKey); + } + + Entry(BinaryValue indexKey, BinaryValue objectKey) + { + this.indexKey = indexKey; + this.objectKey = objectKey; + } + + public boolean hasIndexKey() + { + return indexKey != null; + } + + public BinaryValue getIndexKey() + { + return indexKey; + } + + public BinaryValue getObjectKey() + { + return objectKey; + } + } + + static class Builder + { + private BinaryValue continuation; + private List entryList = + new ArrayList(); + + Builder withContinuation(BinaryValue continuation) + { + this.continuation = continuation; + return this; + } + + Builder addEntry(Response.Entry entry) + { + entryList.add(entry); + return this; + } + + Response build() + { + return new Response(this); + } + } + + } + +} diff --git a/src/main/java/com/basho/riak/client/core/operations/StoreBucketPropsOperation.java b/src/main/java/com/basho/riak/client/core/operations/StoreBucketPropsOperation.java new file mode 100644 index 000000000..9f35c32f6 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/StoreBucketPropsOperation.java @@ -0,0 +1,468 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.functions.Function; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakPB; +import com.google.protobuf.ByteString; +import java.util.List; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +//TODO: return some sort of "success" instead of Void +public class StoreBucketPropsOperation extends FutureOperation +{ + private final Namespace namespace; + private final RiakPB.RpbSetBucketReq.Builder reqBuilder; + + private StoreBucketPropsOperation(Builder builder) + { + this.reqBuilder = builder.reqBuilder; + this.namespace = builder.namespace; + } + + @Override + protected Void convert(List rawResponse) + { + return null; + } + + @Override + protected RiakMessage createChannelMessage() + { + RiakPB.RpbSetBucketReq req = reqBuilder.build(); + return new RiakMessage(RiakMessageCodes.MSG_SetBucketReq, req.toByteArray()); + } + + @Override + protected Void decode(RiakMessage rawMessage) + { + Operations.checkMessageType(rawMessage, RiakMessageCodes.MSG_SetBucketResp); + return null; + } + + @Override + public Namespace getQueryInfo() + { + return namespace; + } + + static abstract class PropsBuilder> + { + + protected final RiakPB.RpbBucketProps.Builder propsBuilder + = RiakPB.RpbBucketProps.newBuilder(); + + protected abstract T self(); + + /** + * Set the allow_multi value. + * + * @param allow whether to allow sibling objects to be created. + * @return a reference to this object. + */ + public T withAllowMulti(boolean allow) + { + propsBuilder.setAllowMult(allow); + return self(); + } + + /** + * Set the backend used by this bucket. Only applies when using + * {@code riak_kv_multi_backend} in Riak. + * + * @param backend the name of the backend to use. + * @return a reference to this object. + */ + public T withBackend(String backend) + { + if (null == backend || backend.length() == 0) + { + throw new IllegalArgumentException("Backend can not be null or zero length"); + } + propsBuilder.setBackend(ByteString.copyFromUtf8(backend)); + return self(); + } + + /** + * Set the basic_quorum value. + * + * The parameter controls whether a read request should return early in + * some fail cases. E.g. If a quorum of nodes has already returned + * notfound/error, don't wait around for the rest. + * + * @param use the basic_quorum value. + * @return a reference to this object. + */ + public T withBasicQuorum(boolean use) + { + propsBuilder.setBasicQuorum(use); + return self(); + } + + /** + * Set the big_vclock value. + * + * @param bigVClock a long representing a epoch time value. + * @return a reference to this object. + * @see Vector Clock Pruning + */ + public T withBigVClock(Long bigVClock) + { + propsBuilder.setBigVclock(bigVClock.intValue()); + return self(); + } + + /** + * Set the chash_keyfun value. + * + * @param func a Function representing the Erlang func to use. + * @return a reference to this object. + */ + public T withChashkeyFunction(Function func) + { + verifyErlangFunc(func); + propsBuilder.setChashKeyfun(convertModFun(func)); + return self(); + } + + /** + * Set the last_write_wins value. Unless you really know what you're + * doing, you probably do not want to set this to true. + * + * @param wins whether to ignore vector clocks when writing. + * @return a reference to this object. + */ + public T withLastWriteWins(boolean wins) + { + propsBuilder.setLastWriteWins(wins); + return self(); + } + + /** + * Set the linkfun value. + * + * @param func a Function representing the Erlang func to use. + * @return a reference to this object. + */ + public T withLinkwalkFunction(Function func) + { + verifyErlangFunc(func); + propsBuilder.setLinkfun(convertModFun(func)); + return self(); + } + + /** + * Set the rw value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param rw the rw value as an integer. + * @return a reference to this object. + */ + public T withRw(int rw) + { + propsBuilder.setRw(rw); + return self(); + } + + /** + * Set the dw value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param dw the dw value as an integer. + * @return a reference to this object. + */ + public T withDw(int dw) + { + propsBuilder.setDw(dw); + return self(); + } + + /** + * Set the w value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param w the w value as an integer. + * @return a reference to this object. + */ + public T withW(int w) + { + propsBuilder.setW(w); + return self(); + } + + /** + * Set the r value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param r the r value as an integer. + * @return a reference to this object. + */ + public T withR(int r) + { + propsBuilder.setR(r); + return self(); + } + + /** + * Set the pr value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param pr the pr value as an integer. + * @return a reference to this object. + */ + public T withPr(int pr) + { + propsBuilder.setPr(pr); + return self(); + } + + /** + * Set the pw value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param pw the pw value as an integer. + * @return a reference to this object. + */ + public T withPw(int pw) + { + propsBuilder.setPw(pw); + return self(); + } + + /** + * Set the not_found_ok value. If true a vnode returning notfound for a + * key increments the r tally. False is higher consistency, true is + * higher availability. + * + * @param ok the not_found_ok value. + * @return a reference to this object. + */ + public T withNotFoundOk(boolean ok) + { + propsBuilder.setNotfoundOk(ok); + return self(); + } + + /** + * Add a pre-commit hook. The supplied Function must be an Erlang or + * Named JS function. + * + * @param hook the Function to add. + * @return a reference to this object. + * @see Using + * Commit Hooks + */ + public T withPrecommitHook(Function hook) + { + if (null == hook || !(!hook.isJavascript() || hook.isNamed())) + { + throw new IllegalArgumentException("Must be a named JS or Erlang function."); + } + + propsBuilder.addPrecommit(convertHook(hook)); + return self(); + } + + /** + * Add a post-commit hook. The supplied Function must be an Erlang or + * Named JS function. + * + * @param hook the Function to add. + * @return a reference to this object. + * @see Using + * Commit Hooks + */ + public T withPostcommitHook(Function hook) + { + verifyErlangFunc(hook); + propsBuilder.addPostcommit(convertHook(hook)); + return self(); + } + + /** + * Set the old_vclock value. + * + * @param oldVClock an long representing a epoch time value. + * @return a reference to this object. + * @see Vector + * Clock Pruning + */ + public T withOldVClock(Long oldVClock) + { + propsBuilder.setOldVclock(oldVClock.intValue()); + return self(); + } + + /** + * Set the young_vclock value. + * + * @param youngVClock a long representing a epoch time value. + * @return a reference to this object. + * @see Vector + * Clock Pruning + */ + public T withYoungVClock(Long youngVClock) + { + propsBuilder.setYoungVclock(youngVClock.intValue()); + return self(); + } + + /** + * Set the small_vclock value. + * + * @param smallVClock a long representing a epoch time value. + * @return a reference to this object. + * @see Vector + * Clock Pruning + */ + public T withSmallVClock(Long smallVClock) + { + propsBuilder.setSmallVclock(smallVClock.intValue()); + return self(); + } + + /** + * Set the nVal. + * + * @param nVal the number of replicas. + * @return a reference to this object. + */ + public T withNVal(int nVal) + { + if (nVal <= 0) + { + throw new IllegalArgumentException("nVal must be >= 1"); + } + propsBuilder.setNVal(nVal); + return self(); + } + + /** + * Enable Legacy Riak Search. Setting this to true causes the search + * pre-commit hook to be added. + * + * Note this is only for legacy Riak (< v2.0) Search support. + * + * @param enable add/remove (true/false) the pre-commit hook for Legacy + * Riak Search. + * @return a reference to this object. + */ + public T withLegacyRiakSearchEnabled(boolean enable) + { + propsBuilder.setSearch(enable); + return self(); + } + + /** + * Associate a Search Index. This only applies if Yokozuna is enabled in + * Riak v2.0. + * + * @param indexName The name of the search index to use. + * @return a reference to this object. + */ + public T withSearchIndex(String indexName) + { + if (null == indexName || indexName.length() == 0) + { + throw new IllegalArgumentException("Index name cannot be null or zero length"); + } + propsBuilder.setSearchIndex(ByteString.copyFromUtf8(indexName)); + return self(); + } + + private void verifyErlangFunc(Function f) + { + if (null == f || f.isJavascript()) + { + throw new IllegalArgumentException("Must be an Erlang Function."); + } + } + + private RiakPB.RpbModFun convertModFun(Function f) + { + return RiakPB.RpbModFun.newBuilder() + .setModule(ByteString.copyFromUtf8(f.getModule())) + .setFunction(ByteString.copyFromUtf8(f.getFunction())) + .build(); + } + + private RiakPB.RpbCommitHook convertHook(Function hook) + { + RiakPB.RpbCommitHook.Builder builder = RiakPB.RpbCommitHook.newBuilder(); + RiakPB.RpbModFun.Builder mfBuilder = RiakPB.RpbModFun.newBuilder(); + + if (hook.isJavascript()) + { + builder.setName(ByteString.copyFromUtf8(hook.getName())); + } + else + { + mfBuilder.setModule(ByteString.copyFromUtf8(hook.getModule())); + mfBuilder.setFunction(ByteString.copyFromUtf8(hook.getFunction())); + builder.setModfun(mfBuilder); + } + + return builder.build(); + } + } + + public static class Builder extends PropsBuilder + { + private final RiakPB.RpbSetBucketReq.Builder reqBuilder + = RiakPB.RpbSetBucketReq.newBuilder(); + private final Namespace namespace; + + /** + * Constructs a builder for a StoreBucketPropsOperation. + * @param namespace The namespace in Riak. + */ + public Builder(Namespace namespace) + { + if (namespace == null) + { + throw new IllegalArgumentException("Namespace cannot be null"); + } + reqBuilder.setBucket(ByteString.copyFrom(namespace.getBucketName().unsafeGetValue())); + reqBuilder.setType(ByteString.copyFrom(namespace.getBucketType().unsafeGetValue())); + this.namespace = namespace; + } + + @Override + protected Builder self() + { + return this; + } + + public StoreBucketPropsOperation build() + { + reqBuilder.setProps(propsBuilder); + return new StoreBucketPropsOperation(this); + } + } +} diff --git a/src/main/java/com/basho/riak/client/core/operations/StoreOperation.java b/src/main/java/com/basho/riak/client/core/operations/StoreOperation.java new file mode 100644 index 000000000..b9b6b2dcb --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/StoreOperation.java @@ -0,0 +1,379 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.converters.RiakObjectConverter; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakKvPB; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import java.util.List; + +import static com.basho.riak.client.core.operations.Operations.checkMessageType; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * An operation to store a riak object + */ +public class StoreOperation extends FutureOperation +{ + private final Logger logger = LoggerFactory.getLogger(StoreOperation.class); + private final RiakKvPB.RpbPutReq.Builder reqBuilder; + private final Location location; + + private StoreOperation(Builder builder) + { + this.reqBuilder = builder.reqBuilder; + this.location = builder.location; + } + + @Override + protected Response convert(List responses) + { + // There should only be one response message from Riak. + if (responses.size() != 1) + { + throw new IllegalStateException("RpbPutReq expects one response, " + responses.size() + " were received"); + } + + RiakKvPB.RpbPutResp response = responses.get(0); + + StoreOperation.Response.Builder responseBuilder = + new StoreOperation.Response.Builder(); + + // This only exists if no key was specified in the put request + if (response.hasKey()) + { + responseBuilder.withGeneratedKey(BinaryValue.unsafeCreate(response.getKey().toByteArray())); + } + + // Is there a case where this isn't true? Can a delete interleve? + if (response.getContentCount() > 0) + { + responseBuilder.addObjects(RiakObjectConverter.convert(response.getContentList(), response.getVclock())); + } + + return responseBuilder.build(); + } + + @Override + protected RiakKvPB.RpbPutResp decode(RiakMessage rawMessage) + { + checkMessageType(rawMessage, RiakMessageCodes.MSG_PutResp); + try + { + return RiakKvPB.RpbPutResp.parseFrom(rawMessage.getData()); + } + catch (InvalidProtocolBufferException e) + { + logger.error("Invalid message received; {}", e); + throw new IllegalArgumentException(e); + } + } + + @Override + protected RiakMessage createChannelMessage() + { + RiakKvPB.RpbPutReq req = reqBuilder.build(); + return new RiakMessage(RiakMessageCodes.MSG_PutReq, req.toByteArray()); + } + + @Override + public Location getQueryInfo() + { + return location; + } + + public static class Builder + { + private final RiakKvPB.RpbPutReq.Builder reqBuilder = RiakKvPB.RpbPutReq.newBuilder(); + private final Location location; + + /** + * Constructs a builder for a StoreOperation + * @param location The location in Riak at which to store. + */ + public Builder(Location location) + { + if (location == null) + { + throw new IllegalArgumentException("Location cannot be null"); + } + + reqBuilder.setType(ByteString.copyFrom(location.getNamespace().getBucketType().unsafeGetValue())); + reqBuilder.setBucket(ByteString.copyFrom(location.getNamespace().getBucketName().unsafeGetValue())); + reqBuilder.setKey(ByteString.copyFrom(location.getKey().unsafeGetValue())); + + this.location = location; + + } + + /** + * Constructs a builder for a StoreOperation. + *

+ * Supplying only a Namespace causes the key to be generated by Riak. + *

+ * @param namespace the namespace in Riak. + */ + public Builder(Namespace namespace) + { + if (namespace == null) + { + throw new IllegalArgumentException("Namespace cannot be null"); + } + reqBuilder.setType(ByteString.copyFrom(namespace.getBucketType().unsafeGetValue())); + reqBuilder.setBucket(ByteString.copyFrom(namespace.getBucketName().unsafeGetValue())); + + this.location = new Location(namespace, "RIAK_GENERATED"); + + } + + + public Builder withContent(RiakObject content) + { + if (null == content) + { + throw new IllegalArgumentException("Object cannot be null."); + } + + reqBuilder.setContent(RiakObjectConverter.convert(content)); + + if (content.getVClock() != null) + { + reqBuilder.setVclock(ByteString.copyFrom(content.getVClock().getBytes())); + } + return this; + } + + /** + * Set the W value for this StoreOperation. + * If not asSet the bucket default is used. + * @param w the W value. + * @return a reference to this object. + */ + public Builder withW(int w) + { + reqBuilder.setW(w); + return this; + } + + /** + * Set the DW value for this StoreOperation. + * If not asSet the bucket default is used. + * @param dw the DW value. + * @return a reference to this object. + */ + public Builder withDw(int dw) + { + reqBuilder.setDw(dw); + return this; + } + + /** + * Set the PW value for this StoreOperation. + * If not asSet the bucket default is used. + * @param pw the PW value. + * @return a reference to this object. + */ + public Builder withPw(int pw) + { + reqBuilder.setPw(pw); + return this; + } + + /** + * Return the object after storing (including any siblings). + * @param returnBody true to return the object. + * @return a reference to this object. + */ + public Builder withReturnBody(boolean returnBody) + { + reqBuilder.setReturnBody(returnBody); + return this; + } + + /** + * Return the metadata after storing the value. + *

+ * Causes Riak to only return the metadata for the object. The value + * will be asSet to null. + * @param returnHead true to return only metadata. + * @return a reference to this object. + */ + public Builder withReturnHead(boolean returnHead) + { + reqBuilder.setReturnHead(returnHead); + return this; + } + + /** + * Set the if_not_modified flag for this StoreOperation. + *

+ * Setting this to true means to update the value only if the + * vclock in the supplied object matches the one in the database. + *

+ *

+ * Be aware there are several cases where this may not actually happen. + * Use of this feature is discouraged. + *

+ * @param ifNotModified + * @return a reference to this object. + */ + public Builder withIfNotModified(boolean ifNotModified) + { + reqBuilder.setIfNotModified(ifNotModified); + return this; + } + + /** + * Set the if_none_match flag value for this StoreOperation. + *

+ * Setting this to true means store the value only if this + * bucket/key combination are not already defined. + *

+ * Be aware that there are several cases where + * this may not actually happen. Use of this option is discouraged. + *

+ * + * @param ifNoneMatch the if_non-match value. + * @return a reference to this object. + */ + public Builder withIfNoneMatch(boolean ifNoneMatch) + { + reqBuilder.setIfNoneMatch(ifNoneMatch); + return this; + } + + /** + * Set the asis value for this operation. + *

+ * Do not use this unless you understand the ramifications + *

+ * @param asis the asis value + * @return a reference to this object. + */ + public Builder withAsis(boolean asis) + { + reqBuilder.setAsis(asis); + return this; + } + + /** + * Set a timeout for this operation. + * @param timeout a timeout in milliseconds. + * @return a reference to this object. + */ + public Builder withTimeout(int timeout) + { + reqBuilder.setTimeout(timeout); + return this; + } + + /** + * Set the n_val value for this operation. + *

+ * Do not use this unless you understand the ramifications + *

+ * @param nval the n_val value + * @return a reference to this object. + */ + public Builder withNVal(int nval) + { + reqBuilder.setNVal(nval); + return this; + } + + /** + * Set whether to use sloppy_quorum. + *

+ * Do not use this unless you understand the ramifications + *

+ * @param sloppyQuorum true to use sloppy_quorum + * @return a reference to this object. + */ + public Builder withSloppyQuorum(boolean sloppyQuorum) + { + reqBuilder.setSloppyQuorum(sloppyQuorum); + return this; + } + + public StoreOperation build() + { + return new StoreOperation(this); + } + + } + + /** + * Response returned from a StoreOperation + */ + public static class Response extends FetchOperation.KvResponseBase + { + private final BinaryValue generatedKey; + + private Response(Init builder) + { + super(builder); + this.generatedKey = builder.generatedKey; + } + + public boolean hasGeneratedKey() + { + return generatedKey != null; + } + + public BinaryValue getGeneratedKey() + { + return generatedKey; + } + + protected static abstract class Init> extends FetchOperation.KvResponseBase.Init + { + private BinaryValue generatedKey; + + T withGeneratedKey(BinaryValue key) + { + this.generatedKey = key; + return self(); + } + + } + + static class Builder extends Init + { + @Override + protected Builder self() + { + return this; + } + + @Override + protected Response build() + { + return new Response(this); + } + } + } +} diff --git a/src/main/java/com/basho/riak/client/core/operations/YzDeleteIndexOperation.java b/src/main/java/com/basho/riak/client/core/operations/YzDeleteIndexOperation.java new file mode 100644 index 000000000..a3af69ccc --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/YzDeleteIndexOperation.java @@ -0,0 +1,88 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakYokozunaPB; +import com.google.protobuf.ByteString; +import java.util.List; + +/** + * + * @author Brian Roach + */ +public class YzDeleteIndexOperation extends FutureOperation +{ + private final RiakYokozunaPB.RpbYokozunaIndexDeleteReq.Builder reqBuilder; + private final String indexName; + + private YzDeleteIndexOperation(Builder builder) + { + this.reqBuilder = builder.reqBuilder; + this.indexName = builder.indexName; + } + + @Override + protected Void convert(List rawResponse) + { + return null; + } + + @Override + protected RiakMessage createChannelMessage() + { + RiakYokozunaPB.RpbYokozunaIndexDeleteReq req = reqBuilder.build(); + return new RiakMessage(RiakMessageCodes.MSG_YokozunaIndexDeleteReq, req.toByteArray()); + + } + + @Override + protected Void decode(RiakMessage rawMessage) + { + Operations.checkMessageType(rawMessage, RiakMessageCodes.MSG_DelResp); + return null; + } + + @Override + public String getQueryInfo() + { + return indexName; + } + + public static class Builder + { + private RiakYokozunaPB.RpbYokozunaIndexDeleteReq.Builder reqBuilder = + RiakYokozunaPB.RpbYokozunaIndexDeleteReq.newBuilder(); + private final String indexName; + + public Builder(String indexName) + { + if (null == indexName || indexName.length() == 0) + { + throw new IllegalArgumentException("Index name cannot be null or zero length"); + } + reqBuilder.setName(ByteString.copyFromUtf8(indexName)); + this.indexName = indexName; + } + + public YzDeleteIndexOperation build() + { + return new YzDeleteIndexOperation(this); + } + } +} diff --git a/src/main/java/com/basho/riak/client/core/operations/YzFetchIndexOperation.java b/src/main/java/com/basho/riak/client/core/operations/YzFetchIndexOperation.java new file mode 100644 index 000000000..285338316 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/YzFetchIndexOperation.java @@ -0,0 +1,127 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.query.search.YokozunaIndex; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakYokozunaPB; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.ArrayList; +import java.util.List; +/** + * + * @author Brian Roach + */ +public class YzFetchIndexOperation extends FutureOperation +{ + private final RiakYokozunaPB.RpbYokozunaIndexGetReq.Builder reqBuilder; + private final String indexName; + + private YzFetchIndexOperation(Builder builder) + { + this.reqBuilder = builder.reqBuilder; + this.indexName = builder.indexName; + } + + @Override + protected Response convert(List rawResponse) + { + // This isn't a streaming op, so there's only one protobuf in the list + RiakYokozunaPB.RpbYokozunaIndexGetResp response = rawResponse.get(0); + List indexList = new ArrayList(response.getIndexCount()); + + for (RiakYokozunaPB.RpbYokozunaIndex pbIndex : response.getIndexList()) + { + indexList.add(new YokozunaIndex(pbIndex.getName().toStringUtf8(), + pbIndex.getSchema().toStringUtf8())); + } + + return new Response(indexList); + } + + @Override + protected RiakMessage createChannelMessage() + { + RiakYokozunaPB.RpbYokozunaIndexGetReq req = reqBuilder.build(); + return new RiakMessage(RiakMessageCodes.MSG_YokozunaIndexGetReq, req.toByteArray()); + + } + + @Override + protected RiakYokozunaPB.RpbYokozunaIndexGetResp decode(RiakMessage rawMessage) + { + Operations.checkMessageType(rawMessage, RiakMessageCodes.MSG_YokozunaIndexGetResp); + try + { + return RiakYokozunaPB.RpbYokozunaIndexGetResp.parseFrom(rawMessage.getData()); + } + catch (InvalidProtocolBufferException ex) + { + throw new IllegalArgumentException("Invalid message received", ex); + } + + } + + @Override + public String getQueryInfo() + { + return indexName; + } + + public static class Builder + { + private final RiakYokozunaPB.RpbYokozunaIndexGetReq.Builder reqBuilder = + RiakYokozunaPB.RpbYokozunaIndexGetReq.newBuilder(); + private String indexName = "All Indexes"; + + public Builder() + {} + + public Builder withIndexName(String indexName) + { + if (null == indexName || indexName.length() == 0) + { + throw new IllegalArgumentException("Index name cannot be null or zero length"); + } + reqBuilder.setName(ByteString.copyFromUtf8(indexName)); + this.indexName = indexName; + return this; + } + + public YzFetchIndexOperation build() + { + return new YzFetchIndexOperation(this); + } + } + + public static class Response + { + private final List indexList; + + Response(List indexList) + { + this.indexList = indexList; + } + + public List getIndexes() + { + return this.indexList; + } + } +} diff --git a/src/main/java/com/basho/riak/client/core/operations/YzGetSchemaOperation.java b/src/main/java/com/basho/riak/client/core/operations/YzGetSchemaOperation.java new file mode 100644 index 000000000..57eb53118 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/YzGetSchemaOperation.java @@ -0,0 +1,118 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.query.search.YokozunaSchema; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakYokozunaPB; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.List; + +/** + * + * @author Brian Roach + */ +public class YzGetSchemaOperation extends FutureOperation +{ + private final RiakYokozunaPB.RpbYokozunaSchemaGetReq.Builder reqBuilder; + private final String schemaName; + + private YzGetSchemaOperation(Builder builder) + { + this.reqBuilder = builder.reqBuilder; + this.schemaName = builder.schemaName; + } + + @Override + protected YzGetSchemaOperation.Response convert(List rawResponse) + { + // This isn't a streaming op, so there's only one protobuf in the list + RiakYokozunaPB.RpbYokozunaSchemaGetResp response = rawResponse.get(0); + + return new Response(new YokozunaSchema(response.getSchema().getName().toStringUtf8(), + response.getSchema().getContent().toStringUtf8())); + + } + + @Override + protected RiakMessage createChannelMessage() + { + RiakYokozunaPB.RpbYokozunaSchemaGetReq req = reqBuilder.build(); + return new RiakMessage(RiakMessageCodes.MSG_YokozunaSchemaGetReq, req.toByteArray()); + + } + + @Override + protected RiakYokozunaPB.RpbYokozunaSchemaGetResp decode(RiakMessage rawMessage) + { + Operations.checkMessageType(rawMessage, RiakMessageCodes.MSG_YokozunaSchemaGetResp); + try + { + return RiakYokozunaPB.RpbYokozunaSchemaGetResp.parseFrom(rawMessage.getData()); + } + catch (InvalidProtocolBufferException ex) + { + throw new IllegalArgumentException("Invalid message received", ex); + } + + } + + @Override + public String getQueryInfo() + { + return schemaName; + } + + public static class Builder + { + private final RiakYokozunaPB.RpbYokozunaSchemaGetReq.Builder reqBuilder = + RiakYokozunaPB.RpbYokozunaSchemaGetReq.newBuilder(); + private final String schemaName; + + public Builder(String schemaName) + { + if (null == schemaName || schemaName.length() == 0) + { + throw new IllegalArgumentException("Schema name cannot be null or zero length"); + } + reqBuilder.setName(ByteString.copyFromUtf8(schemaName)); + this.schemaName = schemaName; + } + + public YzGetSchemaOperation build() + { + return new YzGetSchemaOperation(this); + } + } + + public static class Response + { + private final YokozunaSchema schema; + + Response(YokozunaSchema schema) + { + this.schema = schema; + } + + public YokozunaSchema getSchema() + { + return schema; + } + } +} diff --git a/src/main/java/com/basho/riak/client/core/operations/YzPutIndexOperation.java b/src/main/java/com/basho/riak/client/core/operations/YzPutIndexOperation.java new file mode 100644 index 000000000..2f049d019 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/YzPutIndexOperation.java @@ -0,0 +1,100 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.query.search.YokozunaIndex; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakYokozunaPB; +import com.google.protobuf.ByteString; +import java.util.List; + +/** + * + * @author Brian Roach + */ +public class YzPutIndexOperation extends FutureOperation +{ + private final RiakYokozunaPB.RpbYokozunaIndexPutReq.Builder reqBuilder; + private final YokozunaIndex index; + + private YzPutIndexOperation(Builder builder) + { + this.reqBuilder = builder.reqBuilder; + this.index = builder.index; + } + + @Override + protected Void convert(List rawResponse) + { + return null; + } + + @Override + protected RiakMessage createChannelMessage() + { + RiakYokozunaPB.RpbYokozunaIndexPutReq req = reqBuilder.build(); + return new RiakMessage(RiakMessageCodes.MSG_YokozunaIndexPutReq, req.toByteArray()); + + } + + @Override + protected Void decode(RiakMessage rawMessage) + { + Operations.checkMessageType(rawMessage, RiakMessageCodes.MSG_PutResp); + return null; + } + + @Override + public YokozunaIndex getQueryInfo() + { + return index; + } + + public static class Builder + { + private final RiakYokozunaPB.RpbYokozunaIndexPutReq.Builder reqBuilder = + RiakYokozunaPB.RpbYokozunaIndexPutReq.newBuilder(); + private final YokozunaIndex index; + + public Builder(YokozunaIndex index) + { + if (null == index) + { + throw new IllegalArgumentException("Index can not be null"); + } + + RiakYokozunaPB.RpbYokozunaIndex.Builder indexBuilder = + RiakYokozunaPB.RpbYokozunaIndex.newBuilder(); + + indexBuilder.setName(ByteString.copyFromUtf8(index.getName())); + // A null schema is valid; the default will be used + if (index.getSchema() != null) + { + indexBuilder.setSchema(ByteString.copyFromUtf8(index.getSchema())); + } + + reqBuilder.setIndex(indexBuilder); + this.index = index; + } + + public YzPutIndexOperation build() + { + return new YzPutIndexOperation(this); + } + } +} diff --git a/src/main/java/com/basho/riak/client/core/operations/YzPutSchemaOperation.java b/src/main/java/com/basho/riak/client/core/operations/YzPutSchemaOperation.java new file mode 100644 index 000000000..c19f9efd4 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/operations/YzPutSchemaOperation.java @@ -0,0 +1,89 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.query.search.YokozunaSchema; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakYokozunaPB; +import com.google.protobuf.ByteString; +import java.util.List; + +/** + * + * @author Brian Roach + */ +public class YzPutSchemaOperation extends FutureOperation +{ + private final RiakYokozunaPB.RpbYokozunaSchemaPutReq.Builder reqBuilder; + private final YokozunaSchema schema; + + private YzPutSchemaOperation(Builder builder) + { + this.reqBuilder = builder.reqBuilder; + this.schema = builder.schema; + } + + @Override + protected Void convert(List rawResponse) + { + return null; + } + + @Override + protected RiakMessage createChannelMessage() + { + RiakYokozunaPB.RpbYokozunaSchemaPutReq req = reqBuilder.build(); + return new RiakMessage(RiakMessageCodes.MSG_YokozunaSchemaPutReq, req.toByteArray()); + } + + @Override + protected Void decode(RiakMessage rawMessage) + { + Operations.checkMessageType(rawMessage, RiakMessageCodes.MSG_PutResp); + return null; + } + + @Override + public YokozunaSchema getQueryInfo() + { + return schema; + } + + public static class Builder + { + private final RiakYokozunaPB.RpbYokozunaSchemaPutReq.Builder reqBuilder = + RiakYokozunaPB.RpbYokozunaSchemaPutReq.newBuilder(); + private final YokozunaSchema schema; + + public Builder(YokozunaSchema schema) + { + RiakYokozunaPB.RpbYokozunaSchema.Builder schemaBuilder = + RiakYokozunaPB.RpbYokozunaSchema.newBuilder(); + + schemaBuilder.setName(ByteString.copyFromUtf8(schema.getName())); + schemaBuilder.setContent(ByteString.copyFromUtf8(schema.getContent())); + reqBuilder.setSchema(schemaBuilder); + this.schema = schema; + } + + public YzPutSchemaOperation build() + { + return new YzPutSchemaOperation(this); + } + } +} diff --git a/src/main/java/com/basho/riak/client/core/query/BucketProperties.java b/src/main/java/com/basho/riak/client/core/query/BucketProperties.java new file mode 100644 index 000000000..56903a971 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/BucketProperties.java @@ -0,0 +1,1078 @@ +/* + * Copyright 2013 Basho Technoilogies 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.core.query; + +import com.basho.riak.client.api.cap.Quorum; +import com.basho.riak.client.core.query.functions.Function; +import java.util.LinkedList; +import java.util.List; + +/** + * Bucket properties used for buckets and bucket types. + * + * Note that when instantiating a new instance there are no default values. + * Calling a getter for a specific property will return null if it has not been + * explicitly set. Unset properties are ignored when writing to a bucket or + * bucket type in Riak. + * + * @author Brian Roach + * @since 2.0 + */ +public class BucketProperties +{ + + private final Function linkwalkFunction; + private final Function chashKeyFunction; + private final Quorum rw; + private final Quorum dw; + private final Quorum w; + private final Quorum r; + private final Quorum pr; + private final Quorum pw; + private final Boolean notFoundOk; + private final Boolean basicQuorum; + private final List precommitHooks; + private final List postcommitHooks; + private final Long oldVClock; + private final Long youngVClock; + private final Long bigVClock; + private final Long smallVClock; + private final String backend; + private final Integer nVal; + private final Boolean lastWriteWins; + private final Boolean allowSiblings; + private final Boolean search; + private final String yokozunaIndex; + + private BucketProperties(Builder builder) + { + this.allowSiblings = builder.allowSiblings; + this.backend = builder.backend; + this.basicQuorum = builder.basicQuorum; + this.bigVClock = builder.bigVClock; + this.chashKeyFunction = builder.chashKeyFunction; + this.dw = builder.dw; + this.lastWriteWins = builder.lastWriteWins; + this.linkwalkFunction = builder.linkwalkFunction; + this.nVal = builder.nVal; + this.notFoundOk = builder.notFoundOk; + this.oldVClock = builder.oldVClock; + this.postcommitHooks = builder.postcommitHooks; + this.pr = builder.pr; + this.precommitHooks = builder.precommitHooks; + this.pw = builder.pw; + this.r = builder.r; + this.rw = builder.rw; + this.search = builder.search; + this.smallVClock = builder.smallVClock; + this.w = builder.w; + this.yokozunaIndex = builder.yokozunaIndex; + this.youngVClock = builder.youngVClock; + } + + /** + * Determine if a linkfun value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasLinkwalkFunction() + { + return linkwalkFunction != null; + } + + /** + * Get the linkfun value. + * + * @return the link walking function for the bucket, or null. + */ + public Function getLinkwalkFunction() + { + return linkwalkFunction; + } + + /** + * Determine if a chash_keyfun value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasChashKeyFunction() + { + return chashKeyFunction != null; + } + + /** + * Get the chash_keyfun value. + * + * @return the key hashing function for the bucket, or null. + */ + public Function getChashKeyFunction() + { + return chashKeyFunction; + } + + /** + * Determine if an rw value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasRw() + { + return rw != null; + } + + /** + * Get the rw value. + * + * @return the rw value as a Quorum, or null if not set. + */ + public Quorum getRw() + { + return rw; + } + + /** + * Determine if a dw value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasDw() + { + return rw != null; + } + + /** + * Get the dw value. + * + * @return the dw value as a Quorum, or null if not set. + */ + public Quorum getDw() + { + return dw; + } + + /** + * Determine if a w value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasW() + { + return w != null; + } + + /** + * Get the w value. + * + * @return the w value as a Quorum, or null if not set. + */ + public Quorum getW() + { + return w; + } + + /** + * Determine if an r value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasR() + { + return r != null; + } + + /** + * Get the r value. + * + * @return the r value as a Quorum, or null if not set. + */ + public Quorum getR() + { + return this.r; + } + + /** + * Determine if a pr value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasPr() + { + return pr != null; + } + + /** + * Get the pr value. + * + * @return the pr value as a Quorum, or null. + */ + public Quorum getPr() + { + return pr; + } + + /** + * Determine if a pw value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasPw() + { + return pw != null; + } + + /** + * Set the pw value. + * + * @return the pw value as a Quorum, or null. + */ + public Quorum getPw() + { + return pw; + } + + /** + * Determine if a not_found_ok value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasNotFoundOk() + { + return notFoundOk != null; + } + + /** + * Get the not_found_ok value. + * + * @return the not_found_ok value or null if not set. + * @see BucketProperties.Builder#withNotFoundOk(boolean) + */ + public Boolean getNotFoundOk() + { + return notFoundOk; + } + + /** + * Determine if a basic_quorum value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasBasicQuorum() + { + return basicQuorum != null; + } + + /** + * Get the basic_quorum value. + * + * @return the basic_quorum value, or null if not set. + * @see BucketProperties.Builder#withBasicQuorum(boolean) + */ + public Boolean getBasicQuorum() + { + return basicQuorum; + } + + /** + * Determine if any pre-commit hooks have been added. + * + * @return true if set, false otherwise. + */ + public boolean hasPrecommitHooks() + { + return !precommitHooks.isEmpty(); + } + + /** + * Get the list of pre-commit hooks. + * + * @return a List containing the pre-commit hooks. + * @see + * BucketProperties.Builder#withPrecommitHook(com.basho.riak.client.core.query.functions.Function) + */ + public List getPrecommitHooks() + { + return precommitHooks; + } + + /** + * Determine if any post-commit hooks have been added. + * + * @return true if set, false otherwise. + */ + public boolean hasPostcommitHooks() + { + return !postcommitHooks.isEmpty(); + } + + /** + * Get the list of post-commit hooks. + * + * @return a List containing the post-commit hooks + * @see + * BucketProperties.Builder#withPostcommitHook(com.basho.riak.client.core.query.functions.Function) + */ + public List getPostcommitHooks() + { + return postcommitHooks; + } + + /** + * Determine if an old_vclock value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasOldVClock() + { + return oldVClock != null; + } + + /** + * Get the old_vclock value. + * + * @return the old_vclock value as a long or null if not set. + * @see BucketProperties.Builder#withOldVClock(long) + */ + public Long getOldVClock() + { + return oldVClock; + } + + /** + * Determine if an young_vclock value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasYoungVClock() + { + return youngVClock != null; + } + + /** + * Get the young_vclock value. + * + * @return the young_vclock value as an long or null if not set. + * @see BucketProperties.Builder#withYoungVClock(long) + */ + public Long getYoungVClock() + { + return youngVClock; + } + + /** + * Determine if an big_vclock value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasBigVClock() + { + return bigVClock != null; + } + + /** + * Get the big_vclock value. + * + * @return the big_vclock value as a long or null if not set. + * @see BucketProperties.Builder#withBigVClock(long) + */ + public Long getBigVClock() + { + return bigVClock; + } + + /** + * Determine if an small_vclock value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasSmallVClock() + { + return smallVClock != null; + } + + /** + * Get the small_vclock value. + * + * @return the small_vclock value as a long or null if not set. + * @see BucketProperties.Builder#withSmallVClock(long) + */ + public Long getSmallVClock() + { + return smallVClock; + } + + /** + * Determine if an backend value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasBackend() + { + return backend != null; + } + + /** + * Get the backend. Only applies when using {@code riak_kv_multi_backend} in + * Riak. + * + * @return the name of the backend or null if not set. + */ + public String getBackend() + { + return backend; + } + + /** + * Determine if an nVal value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasNVal() + { + return nVal != null; + } + + /** + * Get the nVal. + * + * @return the nVal value or null if not set. + */ + public Integer getNVal() + { + return nVal; + } + + /** + * Determine if a last_write_wins value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasLastWriteWins() + { + return lastWriteWins != null; + } + + /** + * Get the last_write_wins value. + * + * @return the last_write_wins value or null if not set. + */ + public Boolean getLastWriteWins() + { + return lastWriteWins; + } + + /** + * Determine if an allow_multi value has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasAllowMulti() + { + return allowSiblings != null; + } + + /** + * Get the allow_multi value. + * + * @return the allow_multi value or null if not set. + */ + public Boolean getAllowMulti() + { + return allowSiblings; + } + + /** + * Determine if legacy Riak Search has been enabled. + * + * @return true if set, false otherwise. + */ + public boolean hasLegacyRiakSearchEnabled() + { + return search != null; + } + + /** + * Determine if legacy Riak Search is enabled. + * + * @return true if Riak Search is enabled, false otherwise. + */ + public Boolean getLegacyRiakSearchEnabled() + { + return search; + } + + /** + * Determine if a Yokozuna Index has been set. + * + * @return true if set, false otherwise. + */ + public boolean hasSearchIndex() + { + return yokozunaIndex != null; + } + + /** + * Get the associated Yokozuna Index. + * + * @return the Yokozuna index name or null if not set. + */ + public String getSearchIndex() + { + return yokozunaIndex; + } + + @Override + public String toString() + { + return String.format("DefaultBucketProperties [allowSiblings=%s, " + + "lastWriteWins=%s, nVal=%s, backend=%s, " + + "capProps=[rw=%s,dw=%s,w=%s,r=%s,pr=%s,pw=%s]," + + "vclockProps=[oldVClock=%s, youngVClock=%s, bigVClock=%s, smallVClock=%s]," + + "precommitHooks=%s, postcommitHooks=%s, " + + ", chashKeyFunction=%s, linkWalkFunction=%s, search=%s," + + "yokozunaIndex=%s]", + allowSiblings, lastWriteWins, nVal, backend, rw, dw, + w, r, pr, pw, oldVClock, youngVClock, bigVClock, smallVClock, + precommitHooks, postcommitHooks, + chashKeyFunction, linkwalkFunction, search, + yokozunaIndex); + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((null == linkwalkFunction) ? 0 : linkwalkFunction.hashCode()); + result = prime * result + ((null == chashKeyFunction) ? 0 : chashKeyFunction.hashCode()); + result = prime * result + ((null == rw) ? 0 : rw.hashCode()); + result = prime * result + ((null == dw) ? 0 : dw.hashCode()); + result = prime * result + ((null == w) ? 0 : w.hashCode()); + result = prime * result + ((null == r) ? 0 : r.hashCode()); + result = prime * result + ((null == pr) ? 0 : pr.hashCode()); + result = prime * result + ((null == pw) ? 0 : pw.hashCode()); + result = prime * result + ((null == notFoundOk) ? 0 : notFoundOk.hashCode()); + result = prime * result + ((null == basicQuorum) ? 0 : basicQuorum.hashCode()); + result = prime * result + precommitHooks.hashCode(); + result = prime * result + postcommitHooks.hashCode(); + result = prime * result + (null == oldVClock ? 0 : oldVClock.hashCode()); + result = prime * result + (null == youngVClock ? 0 : youngVClock.hashCode()); + result = prime * result + (null == bigVClock ? 0 : bigVClock.hashCode()); + result = prime * result + (null == smallVClock ? 0 : smallVClock.hashCode()); + result = prime * result + (null == backend ? 0 : backend.hashCode()); + result = prime * result + (null == nVal ? 0 : nVal.hashCode()); + result = prime * result + (null == lastWriteWins ? 0 : lastWriteWins.hashCode()); + result = prime * result + (null == allowSiblings ? 0 : allowSiblings.hashCode()); + result = prime * result + (null == yokozunaIndex ? 0 : yokozunaIndex.hashCode()); + result = prime * result + (null == search ? 0 : search.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (null == obj) + { + return false; + } + if (!(obj instanceof BucketProperties)) + { + return false; + } + + BucketProperties other = (BucketProperties) obj; + if ((linkwalkFunction == other.linkwalkFunction + || (linkwalkFunction != null && linkwalkFunction.equals(other.linkwalkFunction))) + && (chashKeyFunction == other.chashKeyFunction + || (chashKeyFunction != null && chashKeyFunction.equals(other.chashKeyFunction))) + && (rw == other.rw || (rw != null && rw.equals(other.rw))) + && (dw == other.dw || (dw != null && dw.equals(other.rw))) + && (w == other.w || (w != null && w.equals(other.w))) + && (r == other.r || (r != null && r.equals(other.r))) + && (pr == other.pr || (pr != null && pr.equals(other.pr))) + && (pw == other.pw || (pw != null && pw.equals(other.pw))) + && (notFoundOk == other.notFoundOk + || (notFoundOk != null && notFoundOk.equals(other.notFoundOk))) + && (basicQuorum == other.basicQuorum + || (basicQuorum != null && basicQuorum.equals(other.basicQuorum))) + && (precommitHooks.equals(other.precommitHooks)) + && (postcommitHooks.equals(other.postcommitHooks)) + && (oldVClock == other.oldVClock + || (oldVClock != null && oldVClock.equals(other.oldVClock))) + && (youngVClock == other.youngVClock + || (youngVClock != null && youngVClock.equals(other.youngVClock))) + && (bigVClock == other.bigVClock + || (bigVClock != null && bigVClock.equals(other.bigVClock))) + && (smallVClock == other.smallVClock + || (smallVClock != null && smallVClock.equals(other.smallVClock))) + && ((null == backend && other.backend == null) || backend.equals(other.backend)) + && (nVal == other.nVal || (nVal != null && nVal.equals(other.nVal))) + && (lastWriteWins == other.lastWriteWins + || (lastWriteWins != null && lastWriteWins.equals(other.lastWriteWins))) + && (allowSiblings == other.allowSiblings + || (allowSiblings != null && allowSiblings.equals(other.allowSiblings))) + && ((null == yokozunaIndex && null == other.yokozunaIndex) + || yokozunaIndex.equals(other.yokozunaIndex)) + && (search == other.search || (search != null && search.equals(other.search)))) + { + return true; + } + else + { + return false; + } + } + + public static class Builder + { + + private Function linkwalkFunction; + private Function chashKeyFunction; + private Quorum rw; + private Quorum dw; + private Quorum w; + private Quorum r; + private Quorum pr; + private Quorum pw; + private Boolean notFoundOk; + private Boolean basicQuorum; + private final List precommitHooks + = new LinkedList(); + private final List postcommitHooks + = new LinkedList(); + private Long oldVClock; + private Long youngVClock; + private Long bigVClock; + private Long smallVClock; + private String backend; + private Integer nVal; + private Boolean lastWriteWins; + private Boolean allowSiblings; + private Boolean search; + private String yokozunaIndex; + + public Builder() + { + } + + /** + * Set the allow_multi value. + * + * @param allow whether to allow sibling objects to be created. + * @return a reference to this object. + */ + public Builder withAllowMulti(boolean allow) + { + this.allowSiblings = allow; + return this; + } + + /** + * Set the backend used by this bucket. Only applies when using + * {@code riak_kv_multi_backend} in Riak. + * + * @param backend the name of the backend to use. + * @return a reference to this object. + */ + public Builder withBackend(String backend) + { + if (null == backend || backend.length() == 0) + { + throw new IllegalArgumentException("Backend can not be null or zero length"); + } + this.backend = backend; + return this; + } + + /** + * Set the basic_quorum value. + * + * The parameter controls whether a read request should return early in + * some fail cases. E.g. If a quorum of nodes has already returned + * notfound/error, don't wait around for the rest. + * + * @param use the basic_quorum value. + * @return a reference to this object. + */ + public Builder withBasicQuorum(boolean use) + { + this.basicQuorum = use; + return this; + } + + /** + * Set the big_vclock value. + * + * @param bigVClock a long representing a epoch time value. + * @return a reference to this object. + * @see Vector + * Clock Pruning + */ + public Builder withBigVClock(long bigVClock) + { + this.bigVClock = bigVClock; + return this; + } + + /** + * Set the chash_keyfun value. + * + * @param func a Function representing the Erlang func to use. + * @return a reference to this object. + */ + public Builder withChashkeyFunction(Function func) + { + verifyErlangFunc(func); + this.chashKeyFunction = func; + return this; + } + + /** + * Set the last_write_wins value. Unless you really know what you're + * doing, you probably do not want to set this to true. + * + * @param wins whether to ignore vector clocks when writing. + * @return a reference to this object. + */ + public Builder withLastWriteWins(boolean wins) + { + this.lastWriteWins = wins; + return this; + } + + /** + * Set the linkfun value. + * + * @param func a Function representing the Erlang func to use. + * @return a reference to this object. + */ + public Builder withLinkwalkFunction(Function func) + { + verifyErlangFunc(func); + this.linkwalkFunction = func; + return this; + } + + /** + * Set the rw value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param rw the rw value as an integer. + * @return a reference to this object. + */ + public Builder withRw(int rw) + { + this.rw = new Quorum(rw); + return this; + } + + /** + * Set the rw value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param rw the rw value as a Quorum. + * @return a reference to this object. + */ + public Builder withRw(Quorum rw) + { + this.rw = rw; + return this; + } + + /** + * Set the dw value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param dw the dw value as an integer. + * @return a reference to this object. + */ + public Builder withDw(int dw) + { + this.dw = new Quorum(dw); + return this; + } + + /** + * Set the w value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param w the w value as an integer. + * @return a reference to this object. + */ + public Builder withW(int w) + { + this.w = new Quorum(w); + return this; + } + + /** + * Set the w value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param w the w value as a Quorum. + * @return a reference to this object. + */ + public Builder withW(Quorum w) + { + this.w = w; + return this; + } + + /** + * Set the r value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param r the r value as an integer. + * @return a reference to this object. + */ + public Builder withR(int r) + { + this.r = new Quorum(r); + return this; + } + + /** + * Set the r value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param r the r value as a Quorum. + * @return a reference to this object. + */ + public Builder withR(Quorum r) + { + this.r = r; + return this; + } + + /** + * Set the pr value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param pr the pr value as an integer. + * @return a reference to this object. + */ + public Builder withPr(int pr) + { + this.pr = new Quorum(pr); + return this; + } + + /** + * Set the pr value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param pr the pr value as a Quorum. + * @return a reference to this object. + */ + public Builder withPr(Quorum pr) + { + this.pr = pr; + return this; + } + + /** + * Set the pw value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param pw the pw value as an integer. + * @return a reference to this object. + */ + public Builder withPw(int pw) + { + this.pw = new Quorum(pw); + return this; + } + + /** + * Set the pw value. Individual requests (or buckets in a bucket type) + * can override this. + * + * @param pw the pw value as a Quorum. + * @return a reference to this object. + */ + public Builder withPw(Quorum pw) + { + this.pw = pw; + return this; + } + + /** + * Set the not_found_ok value. If true a vnode returning notfound for a + * key increments the r tally. False is higher consistency, true is + * higher availability. + * + * @param ok the not_found_ok value. + * @return a reference to this object. + */ + public Builder withNotFoundOk(boolean ok) + { + this.notFoundOk = ok; + return this; + } + + /** + * Add a pre-commit hook. The supplied Function must be an Erlang or + * Named JS function. + * + * @param hook the Function to add. + * @return a reference to this object. + * @see Using + * Commit Hooks + */ + public Builder withPrecommitHook(Function hook) + { + if (null == hook || !(!hook.isJavascript() || hook.isNamed())) + { + throw new IllegalArgumentException("Must be a named JS or Erlang function."); + } + + precommitHooks.add(hook); + return this; + } + + /** + * Add a post-commit hook. The supplied Function must be an Erlang or + * Named JS function. + * + * @param hook the Function to add. + * @return a reference to this object. + * @see Using + * Commit Hooks + */ + public Builder withPostcommitHook(Function hook) + { + verifyErlangFunc(hook); + postcommitHooks.add(hook); + return this; + } + + /** + * Set the old_vclock value. + * + * @param oldVClock an long representing a epoch time value. + * @return a reference to this object. + * @see Vector + * Clock Pruning + */ + public Builder withOldVClock(long oldVClock) + { + this.oldVClock = oldVClock; + return this; + } + + /** + * Set the young_vclock value. + * + * @param youngVClock a long representing a epoch time value. + * @return a reference to this object. + * @see Vector + * Clock Pruning + */ + public Builder withYoungVClock(long youngVClock) + { + this.youngVClock = youngVClock; + return this; + } + + /** + * Set the small_vclock value. + * + * @param smallVClock a long representing a epoch time value. + * @return a reference to this object. + * @see Vector + * Clock Pruning + */ + public Builder withSmallVClock(long smallVClock) + { + this.smallVClock = smallVClock; + return this; + } + + /** + * Set the nVal. + * + * @param nVal the number of replicas. + * @return a reference to this object. + */ + public Builder withNVal(int nVal) + { + if (nVal <= 0) + { + throw new IllegalArgumentException("nVal must be >= 1"); + } + this.nVal = nVal; + return this; + } + + /** + * Enable Legacy Riak Search. Setting this to true causes the search pre-commit + * hook to be added. + * + * Note this is only for legacy Riak (< v2.0) Search support. + * + * @param enable add/remove (true/false) the pre-commit hook for Riak + * Search. + * @return a reference to this object. + */ + public Builder withLegacyRiakSearchEnabled(boolean enable) + { + search = enable; + return this; + } + + /** + * Associate a Search Index. This only applies if Yokozuna is enabled + * in Riak. + * + * @param indexName The name of the Yokozuna Index to use. + * @return a reference to this object. + */ + public Builder withSearchIndex(String indexName) + { + if (null == indexName || indexName.length() == 0) + { + throw new IllegalArgumentException("Index name cannot be null or zero length"); + } + this.yokozunaIndex = indexName; + return this; + } + + public BucketProperties build() + { + return new BucketProperties(this); + } + + private void verifyErlangFunc(Function f) + { + if (null == f || f.isJavascript()) + { + throw new IllegalArgumentException("Must be an Erlang Function."); + } + } + + } + +} diff --git a/src/main/java/com/basho/riak/client/core/query/Location.java b/src/main/java/com/basho/riak/client/core/query/Location.java new file mode 100644 index 000000000..bff11c8cd --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/Location.java @@ -0,0 +1,185 @@ +/* + * 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.core.query; + +import com.basho.riak.client.core.util.BinaryValue; +import java.nio.charset.Charset; + +/** + * Encapsulates a key and Namespace. + *

+ * Many client operations locate a piece of information in Riak via a + * bucket type, bucket name, and a key. This class encapsulates these + * three items by combining a {@link Namespace} with a key and is + * used with most client operations. + *

+ *

+ * Riak itself is character set agnostic; everything is stored as bytes. The + * convenience methods in this class rely on either the default Charset or a + * supplied one to convert Strings to a byte[]. + *

+ * + * @author Brian Roach + * @since 2.0 + */ +public final class Location +{ + + private final Namespace namespace; + private final BinaryValue key; + + /** + * Construct a new Location with the provided Namespace and key. + * @param namespace The namespace for this location. + * @param key The key for this location. + */ + public Location(Namespace namespace, BinaryValue key) + { + if (namespace == null) + { + throw new IllegalArgumentException("Namespace cannot be null"); + } + else if (key == null || key.length() == 0) + { + throw new IllegalArgumentException("Key cannot be null or zero length"); + } + this.namespace = namespace; + this.key = key; + } + + /** + * Construct a new Location with the provided Namespace and key. + *

+ * The supplied string is converted to bytes using the supplied charset. + *

+ * @param namespace The namespace for this location. + * @param key The key for this location. + * @param charset the charset for the key + */ + public Location(Namespace namespace, String key, Charset charset) + { + if (key == null || key.length() == 0) + { + throw new IllegalArgumentException("Key cannot be null or zero length"); + } + else if (namespace == null) + { + throw new IllegalArgumentException("Namespace cannot be null"); + } + else if (charset == null) + { + throw new IllegalArgumentException("Charset cannot be null"); + } + this.namespace = namespace; + this.key = BinaryValue.create(key, charset); + } + + /** + * Construct a new Location with the provided namespace and key. + *

+ * The supplied string is converted to bytes using the default charset. + *

+ * @param namespace The namespace for this location. + * @param key The key for this location. + */ + public Location(Namespace namespace, String key) + { + this(namespace, key, Charset.defaultCharset()); + } + + /** + * Returns the key for this location. + * @return the Riak Key. + */ + public BinaryValue getKey() + { + return key; + } + + /** + * Get the key for this location as a String. + *

+ * The default character set is used. + *

+ * @return the key for this location as a String + */ + public String getKeyAsString() + { + return key.toString(); + } + + /** + * Get the key for this location as a String. + *

+ * The supplied character set is used. + *

+ * @param charset The Charset used to convert to a String. + * @return the key for this Location as a String. + */ + public String getKeyAsString(Charset charset) + { + return key.toString(charset); + } + + /** + * Return the Namespace for this location. + * @return the namespace. + */ + public Namespace getNamespace() + { + return namespace; + } + + @Override + public int hashCode() + { + int hash = 7; + hash = 79 * hash + (this.namespace != null ? this.namespace.hashCode() : 0); + hash = 79 * hash + (this.key != null ? this.key.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final Location other = (Location) obj; + if (this.namespace != other.namespace && (this.namespace == null || !this.namespace.equals(other.namespace))) + { + return false; + } + if (this.key != other.key && (this.key == null || !this.key.equals(other.key))) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "{namespace: " + namespace + ", key: " + key + "}"; + } + +} diff --git a/src/main/java/com/basho/riak/client/core/query/Namespace.java b/src/main/java/com/basho/riak/client/core/query/Namespace.java new file mode 100644 index 000000000..e12b47f6d --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/Namespace.java @@ -0,0 +1,278 @@ +/* + * 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.core.query; + +import com.basho.riak.client.core.util.BinaryValue; +import java.nio.charset.Charset; + +/** + * Encapsulates a Riak bucket type and bucket name. + *

+ * Riak 2.0 introduced bucket types, which form a namespace in Riak when combined with + * a bucket name. This class encapsulates those two items for use with operations. + *

+ *

+ * Buckets in Riak are automatically created for a type if they do not yet exist. + * Bucket types, on the other hand, are not. Anything other than the {@literal default} + * bucket type must be explicitly created using the {@literal riak_admin} command line tool. + *

+ *

+ * Bucket types can only be in UTF-8. Bucket names have no restrictions. + *

+ *

+ * Buckets in the default bucket type can not hold CRDTs (e.g. Maps, Counters, Sets, etc). + *

+ * @author Brian Roach + * @since 2.0 + */ +public class Namespace +{ + + + /** + * The default bucket type in Riak. + * The Riak default bucket type. + * The type is {@value #DEFAULT_BUCKET_TYPE} + */ + public static final String DEFAULT_BUCKET_TYPE = "default"; + private static final BinaryValue DEFAULT_TYPE = BinaryValue.createFromUtf8(DEFAULT_BUCKET_TYPE); + + private final BinaryValue type; + private final BinaryValue bucket; + + /** + * Construct a new Namespace with the provided bucket type and name. + * @param bucketType The bucket type in Riak. This must be UTF-8 encoded. + * @param bucketName The bucket in Riak. + */ + public Namespace(BinaryValue bucketType, BinaryValue bucketName) + { + if (null == bucketName || bucketName.length() == 0) + { + throw new IllegalArgumentException("Bucket name cannot be null or zero length."); + } + else if (null == bucketType || bucketType.length() == 0) + { + throw new IllegalArgumentException("Bucket type cannot be null or zero length."); + } + this.bucket = bucketName; + this.type = bucketType; + } + + /** + * Construct a new Namespace with the provided bucket type and name. + *

+ * The supplied bucket type will be converted to bytes using UTF-8. + *

+ *

+ * The supplied bucket name is converted to bytes using the supplied charset. + *

+ * @param bucketType The bucket type in Riak. This must be a valid UTF-8 string. + * @param bucketName The name of the bucket in Riak. + * @param charset the charset used to convert the bucket name to bytes. + */ + public Namespace(String bucketType, String bucketName, Charset charset) + { + if (bucketName == null || bucketName.isEmpty()) + { + throw new IllegalArgumentException("Bucket name cannot be null or zero length"); + } + else if (bucketType == null || bucketType.isEmpty()) + { + throw new IllegalArgumentException("Bucket type cannot be null or zero length"); + } + else if (charset == null) + { + throw new IllegalArgumentException("Charset cannot be null"); + } + + this.bucket = BinaryValue.create(bucketName, charset); + this.type = BinaryValue.createFromUtf8(bucketType); + } + + /** + * Construct a new Namespace with the provided bucket type and name. + *

+ * The bucket type will be converted to bytes using UTF-8. + *

+ *

+ * The supplied bucketName is converted to bytes using the default charset. + *

+ * @param bucketType The bucket type in Riak. + * @param bucketName The bucket in Riak + */ + public Namespace(String bucketType, String bucketName) + { + this(bucketType, bucketName, Charset.defaultCharset()); + } + + /** + * Construct a new Namespace with the provided bucket name and the default bucket type. + *

+ * The Namespace will use the default bucket type and the bucket + * name provided. + *

+ * @param bucketName the bucket name in Riak. + * @see #DEFAULT_BUCKET_TYPE + */ + public Namespace(BinaryValue bucketName) + { + this(DEFAULT_TYPE, bucketName); + } + + /** + * Construct a Namespace with the provided bucket name and the default bucket type. + *

+ * The Namespace will use the default bucket type and the bucket + * name provided. + *

+ * The supplied bucket name will be converted to bytes using the supplied charset. + *

+ *

+ * @param bucketName the name of the bucket. + * @param charset the charset to use to convert the string to bytes. + * @see #DEFAULT_BUCKET_TYPE + */ + public Namespace(String bucketName, Charset charset) + { + this(DEFAULT_BUCKET_TYPE, bucketName, charset); + } + + /** + * Construct a Namespace with the provided bucket name and the default bucket type. + *

+ * The Namespace will use the default bucket type and the bucket + * name provided. + *

+ * The supplied bucket name will be converted to bytes using the default charset. + *

+ *

+ * @param bucketName the name of the bucket. + * @see #DEFAULT_BUCKET_TYPE + */ + public Namespace(String bucketName) + { + this(bucketName, Charset.defaultCharset()); + } + + /** + * Returns the bucket type for this Namespace. + * @return the Riak bucket type. + */ + public BinaryValue getBucketType() + { + return type; + } + + /** + * Get the bucket type for this Namespace as a String. + *

+ * The default Charset is used to convert to a String. + *

+ * @return The bucket type for this Namespace as a String. + */ + public String getBucketTypeAsString() + { + return type.toString(); + } + + /** + * Get the bucket type for this Namespace as a String. + *

+ * The supplied Charset is used to convert to a String. + *

+ * @param charset The Charset used to convert to a String. + * @return The bucket type for this Namespace as a String. + */ + public String getBucketTypeAsString(Charset charset) + { + return type.toString(charset); + } + + /** + * Returns the bucket name for this Namespace. + * @return the Riak bucket name. + */ + public BinaryValue getBucketName() + { + return bucket; + } + + /** + * Get the bucket name for this Namespace as a String. + *

+ * The default Charset is used. + *

+ * @return the key for this Location as a String. + */ + public String getBucketNameAsString() + { + return bucket.toString(); + } + + /** + * Get the bucket name for this Namespace as a String. + *

+ * The supplied Charset is used. + *

+ * @param charset the Charset used to convert to a String. + * @return the key for this Location as a String. + */ + public String getBucketNameAsString(Charset charset) + { + return bucket.toString(charset); + } + + @Override + public int hashCode() + { + int hash = 3; + hash = 37 * hash + (this.type != null ? this.type.hashCode() : 0); + hash = 37 * hash + (this.bucket != null ? this.bucket.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final Namespace other = (Namespace) obj; + if (this.type != other.type && (this.type == null || !this.type.equals(other.type))) + { + return false; + } + if (this.bucket != other.bucket && (this.bucket == null || !this.bucket.equals(other.bucket))) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "{type: " + type + ", bucket: " + bucket + "}"; + } + +} diff --git a/src/main/java/com/basho/riak/client/core/query/RiakObject.java b/src/main/java/com/basho/riak/client/core/query/RiakObject.java new file mode 100644 index 000000000..bce97044a --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/RiakObject.java @@ -0,0 +1,386 @@ +/* + * 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.core.query; + +import com.basho.riak.client.api.annotations.RiakVClock; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.query.UserMetadata.RiakUserMetadata; +import com.basho.riak.client.core.query.indexes.RiakIndexes; +import com.basho.riak.client.core.query.links.RiakLinks; +import com.basho.riak.client.core.util.BinaryValue; +import com.basho.riak.client.core.util.CharsetUtils; +import java.nio.charset.Charset; + +/** + * Represents the data and metadata stored in Riak + *

+ * While the client APIs provide methods to store/retrieve data to/from Riak using your own + * Beans/POJOs, this class is used as the core data type to and from which those types are + * converted. + *

+ *

+ *

Working with Metadata
+ * An object in Riak has a few types of metadata that can be attached; Secondary + * Indexes, Links, and User Metadata. Each of these has its own container in + * {@code RiakObject} and methods are provided to access them. + *

+ *

+ * @riak.threadsafety RiakObject is designed to be thread safe. All methods + * which update the object do so via a thread safe mechanism. The only caveat is + * that if you use any of the methods prefixed with "unsafe" you need to + * understand the ramifications as noted in their Javadoc. + *

+ * @author Brian Roach + * @since 2.0 + */ +public final class RiakObject +{ + /** + * The default content type assigned when storing in Riak if one is not + * provided. {@value #DEFAULT_CONTENT_TYPE} + * @see RiakObject#setContentType(java.lang.String) + */ + public final static String DEFAULT_CONTENT_TYPE = "application/octet-stream"; + + // Mutable types. + // Worth noting here is that changes to the contents of this + // are not guaranteed to be seen outside of a single thread. We never + // expose it directly outside the RiakObject except via "unsafe" methods + private volatile BinaryValue value; + + // Mutable collections + private volatile RiakIndexes riakIndexes; + private volatile RiakLinks links; + private volatile RiakUserMetadata userMeta; + + // All immutable types + private volatile String contentType = DEFAULT_CONTENT_TYPE; + private volatile String vtag; + private volatile boolean isDeleted; + private volatile boolean isModified; + + // This is annotated to that the UpdateValue command + // can inject a vclock. + @RiakVClock + private volatile VClock vclock; + + private volatile long lastModified; + + /** + * Constructs a new, empty RiakObject. + */ + public RiakObject() + { + + } + + // Methods For dealing with Value + + /** + * Returns whether or not this RiakObject has a value + * @return true if the value has been asSet, false otherwise + */ + public boolean hasValue() + { + return value != null; + } + + /** + * Returns the value of this RiakObject. + * + * @return the value of this RiakObject + */ + public BinaryValue getValue() + { + return value; + } + + /** + * Set the value for this RiakObject + *

+ * Note that if {@link RiakObject#setContentType(java.lang.String)} is not + * called a value of {@value #DEFAULT_CONTENT_TYPE} is used. + *

+ * @riak.threadsafety Unsafe modifications to the supplied BinaryValue's + * underlying byte[] would be a bad thing. + * @param value the value to be stored in Riak. + * @return a reference to this object. + * @throws IllegalArgumentException if {@code value} is zero length. + */ + public RiakObject setValue(BinaryValue value) + { + if (value != null && value.length() == 0) + { + throw new IllegalArgumentException("value can not be zero length"); + } + this.value = value; + return this; + } + + /** + * Get the vector clock for this RiakObject. + * @return The vector clock, or null if not set. + */ + public VClock getVClock() + { + return vclock; + } + + /** + * Set the vector clock for this RiakObject. + * @param vclock a vector clock. + * @return a reference to this object. + */ + public RiakObject setVClock(VClock vclock) + { + this.vclock = vclock; + return this; + } + + /** + * Returns the version tag (if it is one of a asSet of siblings) for this RiakObject + * + * @return the vtag if present, otherwise {@code null} + */ + public String getVTag() + { + return vtag; + } + + /** + * Set the version tag for this RiakObject + * + * @param vtag a {@code String} representing the VTag for this RiakObject + * @return a reference to this object + * @throws IllegalArgumentException if {@code vtag} is zero length + */ + public RiakObject setVTag(String vtag) + { + if (vtag != null && vtag.isEmpty()) + { + throw new IllegalArgumentException("vtag can not be zero length"); + } + this.vtag = vtag; + return this; + } + + /** + * Returns the last modified time of this RiakObject. + *

+ * The timestamp is returned as a {@code long} (Unix) epoch time. + *

+ * @return The last modified time or {@code 0} if it has not been asSet. + * @see RiakObject#setLastModified(long) + */ + public long getLastModified() + { + return lastModified; + } + + /** + * A {@code long} timestamp of milliseconds since the epoch to asSet as + * the last modified date on this RiakObject. + * + * @param lastModified + * @return a reference to this object + */ + public RiakObject setLastModified(long lastModified) + { + this.lastModified = lastModified; + return this; + } + + /** + * Returns the content type of the data payload (value) of this RiakObject + *

+ * Due to Riak's HTTP API this is represented as a string suitable for + * a HTTP {@code Content-Type} header. + *

+ * @return a {@code String} representing the content type of this object's value + * @see RiakObject#setVClock(com.basho.riak.client.api.cap.VClock) + */ + public String getContentType() + { + return contentType; + } + + /** + * Set the content type of the data payload (value) of the this RaikObject + *

+ * Due to Riak's HTTP API this is represented as a string suitable for + * a HTTP {@code Content-Type} header. + *

+ * @param contentType a {@code String} representing the content type of this object's value + * @return a reference to this object + * @see RiakObject#setValue(com.basho.riak.client.core.util.BinaryValue) + */ + public RiakObject setContentType(String contentType) + { + this.contentType = contentType; + return this; + } + + /** + * Determine if there is a character asSet present in the content-type. + * @return true if a charset is present, false otherwise. + */ + public boolean hasCharset() + { + return CharsetUtils.hasCharset(contentType); + } + + /** + * Get the character asSet for this RiakObject's content (value) + *

+ * Due to Riak's HTTP API this is represented as a string suitable for + * a HTTP {@code Content-Type} header. + *

+ * @return The character asSet {@code String} + * @see RiakObject#setCharset(java.lang.String) + * @see RiakObject#setValue(com.basho.riak.client.core.util.BinaryValue) + */ + public String getCharset() + { + return CharsetUtils.getDeclaredCharset(contentType); + } + + /** + * Set the character asSet for this object's content (value). + *

+ * Due to Riak's HTTP API this is represented as a string suitable for + * a HTTP {@code Content-Type} header. + *

+ * @param charset the {@link Charset} to be asSet + * @return a reference to this object + * @see RiakObject#setValue(com.basho.riak.client.core.util.BinaryValue) + */ + public RiakObject setCharset(String charset) + { + this.contentType = CharsetUtils.addCharset(charset, contentType); + return this; + } + + // Indexes + + /** + * Returns whether this RiakObject has secondary indexes (2i) + * @return {@code true} if indexes are present, {@code false} otherwise + * @see Riak Secondary Indexes + */ + public boolean hasIndexes() + { + return (riakIndexes != null && !riakIndexes.isEmpty()); + } + + /** + * Returns the Secondary Indexes (2i) for this RiakObject + *

+ * Any/all indexes for this {@code RiakObject} are encapsulated in the returned {@code RiakIndexes} object. + *

+ * @riak.threadsafety The returned RiakIndexes object encapsulates any/all + * indexes for this RiakObject}, is mutable, and thread safe. Any changes + * are directly applied to this RaikObject. + * @return the {@link RiakIndexes} that encapsulates any/all indexes for this RiakObject + * @see Riak Secondary Indexes + */ + public synchronized RiakIndexes getIndexes() + { + // Lazy initialization of the internal container. + if (null == riakIndexes) + { + riakIndexes = new RiakIndexes(); + } + return riakIndexes; + } + + // Links + /** + * Returns whether this RiakObject containsKeyKey links + * @return {@code true} if links are present, {@code false} otherwise + */ + public boolean hasLinks() + { + return (links != null && !links.isEmpty()); + } + + /** + * Returns the RiakLinks for this RiakObject + * @riak.threadsafety The returned RiakLinks object encapsulates any/all + * links for this RaikObject, is mutable, and thread safe. Any changes + * are directly applied to this RaikObject. + * @return the {@link RiakIndexes} that encapsulates all/any links for this {@code RiakObject} + */ + public synchronized RiakLinks getLinks() + { + // Lazy initialization of comtainer + if (null == links) + { + links = new RiakLinks(); + } + + return links; + } + + // User Meta + + /** + * Returns if there are any User Meta entries for this RiakObject + * @return {@code true} if user meta entries are present, {@code false} otherwise. + */ + public boolean hasUserMeta() + { + return userMeta != null && !userMeta.isEmpty(); + } + + /** + * Returns the User Meta for this RiakObject + * @riak.threadsafety The returned RiakUserMetadata} encapsulates any/all + * user meta entries for this RaikObject, is mutable, and thread safe. Any changes + * are directly applied to this RaikObject. + * @return the {@code RiakUserMetadata} that containsKeyKey any/all User Meta entries for this {@code RiakObject} + */ + public synchronized RiakUserMetadata getUserMeta() + { + // Lazy initialization of container. + if (null == userMeta) + { + userMeta = new RiakUserMetadata(); + } + + return userMeta; + } + + /** + * Marks this RiakObject as being a tombstone in Riak + * + * @param isDeleted + * @return a reference to this object + */ + public RiakObject setDeleted(boolean isDeleted) + { + this.isDeleted = isDeleted; + return this; + } + + /** + * Returns whether or not this RiakObject is marked as being deleted (a tombstone) + * @return [{@code true} is this {@code RiakObject} is a tombstone, {@code false} otherwise + */ + public boolean isDeleted() + { + return isDeleted; + } +} diff --git a/src/main/java/com/basho/riak/client/core/query/UserMetadata/RiakUserMetadata.java b/src/main/java/com/basho/riak/client/core/query/UserMetadata/RiakUserMetadata.java new file mode 100644 index 000000000..ce5979f18 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/UserMetadata/RiakUserMetadata.java @@ -0,0 +1,238 @@ +/* + * 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.core.query.UserMetadata; + +import com.basho.riak.client.core.util.BinaryValue; +import java.nio.charset.Charset; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A threadsafe container for user metadata. + *

+ * Arbitrary user metadata can be attached to an object in Riak. These are simply key/value + * pairs meaningful outside of the actual value of the object. This container + * allows the user to manipulate that data. + *

+ *

+ * Data in Riak is character asSet agnostic; it's simply raw bytes. Methods are provided here + * to either use your default character asSet or supply a specific one to convert to and + * from {@code String}s. + *

+ * + * @author Brian Roach + * @since 2.0 + * @see com.basho.riak.client.core.query.RiakObject#getUserMeta() + */ +public class RiakUserMetadata +{ + private final ConcurrentHashMap meta = + new ConcurrentHashMap(); + + /** + * Determine if usermeta is present. + * @return {@code true} if there are no entries, {@code false} otherwise. + */ + public boolean isEmpty() + { + return meta.isEmpty(); + } + + /** + * Determine if a specific usermeta entry is present. + *

+ * This method uses the default {@code Charset} to convert the supplied key. + *

+ * @param key the metadata key + * @return {@code true} if the entry is present, {@code false} otherwise. + */ + public boolean containsKey(String key) + { + return containsKey(key, Charset.defaultCharset()); + } + + /** + * Determine if a specific usermeta entry is present. + *

+ * This method uses the supplied {@code Charset} to convert the supplied key. + *

+ * @param key the metadata key + * @return {@code true} if the entry is present, {@code false} otherwise. + */ + public boolean containsKey(String key, Charset charset) + { + return meta.containsKey(BinaryValue.unsafeCreate(key.getBytes())); + } + + /** + * Get a user metadata entry. + *

+ * This method and its {@link RiakUserMetadata#put(java.lang.String, java.lang.String) } + * counterpart use the default {@code Charset} to convert the {@code String}s. + *

+ * @param key the key for the user metadata entry as a {@code String} encoded using the default {@code Charset} + * @return the value for the entry converted to a {@code String} using the default {@code Charset} + */ + public String get(String key) + { + return get(key, Charset.defaultCharset()); + } + + /** + * Get a user metadata entry. + *

+ * This method and its {@link RiakUserMetadata#put(java.lang.String, java.lang.String, java.nio.charset.Charset) } + * counterpart use the supplied {@code Charset} to convert the {@code String}s. + *

+ * @param key the key for the user metadata entry as a {@code String} encoded using the supplied {@code Charset} + * @return the value for the entry converted to a {@code String} using the supplied {@code Charset} + */ + public String get(String key, Charset charset) + { + BinaryValue wrappedKey = BinaryValue.unsafeCreate(key.getBytes(charset)); + BinaryValue value = meta.get(wrappedKey); + if (value != null) + { + return value.toString(charset); + } + else + { + return null; + } + + } + + /** + * Get a user metadata entry. + *

+ * This method and its {@link RiakUserMetadata#put(com.basho.riak.client.core.util.BinaryValue, com.basho.riak.client.core.util.BinaryValue)} + * allow access to the raw bytes. + *

+ * @param key the key for the user metadata entry + * @return the value for the entry + */ + public BinaryValue get(BinaryValue key) + { + return meta.get(key); + } + + /** + * Get the user metadata entries + *

+ * This method allows access to the user metadata entries directly as raw bytes. The + * {@code Set} is an unmodifiable view of all the entries. + *

+ * @return an unmodifiable view of all the entries. + */ + public Set> getUserMetadata() + { + return Collections.unmodifiableSet(meta.entrySet()); + } + + /** + * Set a user metadata entry. + *

+ * This method and its {@link RiakUserMetadata#get(java.lang.String) } + * counterpart use the default {@code Charset} to convert the {@code String}s. + *

+ * @param key the key for the user metadata entry as a {@code String} encoded using the default {@code Charset} + * @param value the value for the entry as a {@code String} encoded using the default {@code Charset} + */ + public void put(String key, String value) + { + put(key, value, Charset.defaultCharset()); + } + + /** + * Set a user metadata entry. + *

+ * This method and its {@link RiakUserMetadata#get(java.lang.String, java.nio.charset.Charset) } + * counterpart use the supplied {@code Charset} to convert the {@code String}s. + *

+ * @param key the key for the user metadata entry as a {@code String} encoded using the supplied {@code Charset} + * @param value the value for the entry as a {@code String} encoded using the supplied {@code Charset} + */ + public void put(String key, String value, Charset charset) + { + BinaryValue wrappedKey = BinaryValue.unsafeCreate(key.getBytes(charset)); + BinaryValue wrappedValue = BinaryValue.unsafeCreate(value.getBytes(charset)); + meta.put(wrappedKey, wrappedValue); + } + + /** + * Set a user metadata entry using raw bytes. + *

+ * This method and its {@link RiakUserMetadata#get(com.basho.riak.client.core.util.BinaryValue)} + * counterpart all access to the user metadata raw bytes + *

+ * @param key the key for the user metadata entry + * @param value the value for the entry + */ + public void put(BinaryValue key, BinaryValue value) + { + meta.put(key, value); + } + + public void remove(BinaryValue key) + { + meta.remove(key); + } + + public void remove(String key, Charset charset) + { + BinaryValue wrappedKey = BinaryValue.unsafeCreate(key.getBytes(charset)); + remove(wrappedKey); + } + + public void remove (String key) + { + remove(key, Charset.defaultCharset()); + } + + // TODO: deal with charset. Should add to annotation + public RiakUserMetadata put(Map metaMap) + { + for (Map.Entry e : metaMap.entrySet()) + { + BinaryValue wrappedKey = BinaryValue.unsafeCreate(e.getKey().getBytes()); + BinaryValue wrappedValue = BinaryValue.unsafeCreate(e.getValue().getBytes()); + meta.put(wrappedKey, wrappedValue); + } + return this; + } + + /** + * Clear all user metadata entries. + */ + public void clear() + { + meta.clear(); + } + + /** + * Get the number of user metadata entries. + * @return the number of entries + */ + public int size() + { + return meta.size(); + } + + + +} diff --git a/src/main/java/com/basho/riak/client/core/query/crdt/ops/CounterOp.java b/src/main/java/com/basho/riak/client/core/query/crdt/ops/CounterOp.java new file mode 100644 index 000000000..4c05c6e0d --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/crdt/ops/CounterOp.java @@ -0,0 +1,39 @@ +/* + * 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.core.query.crdt.ops; + +public class CounterOp implements CrdtOp +{ + private final long increment; + + public CounterOp(long increment) + { + this.increment = increment; + } + + public long getIncrement() + { + return increment; + } + + + + @Override + public String toString() + { + return "{increment: " + increment + "}"; + } +} diff --git a/src/main/java/com/basho/riak/client/core/query/crdt/ops/CrdtOp.java b/src/main/java/com/basho/riak/client/core/query/crdt/ops/CrdtOp.java new file mode 100644 index 000000000..99766542b --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/crdt/ops/CrdtOp.java @@ -0,0 +1,21 @@ +/* + * 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.core.query.crdt.ops; + +public interface CrdtOp +{ + +} diff --git a/src/main/java/com/basho/riak/client/core/query/crdt/ops/FlagOp.java b/src/main/java/com/basho/riak/client/core/query/crdt/ops/FlagOp.java new file mode 100644 index 000000000..f9b51aaf6 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/crdt/ops/FlagOp.java @@ -0,0 +1,32 @@ +/* + * 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.core.query.crdt.ops; + +public class FlagOp implements CrdtOp +{ + + private final boolean enabled; + + public FlagOp(boolean enabled) + { + this.enabled = enabled; + } + + public boolean getEnabled() + { + return enabled; + } +} diff --git a/src/main/java/com/basho/riak/client/core/query/crdt/ops/MapOp.java b/src/main/java/com/basho/riak/client/core/query/crdt/ops/MapOp.java new file mode 100644 index 000000000..843ee7dd0 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/crdt/ops/MapOp.java @@ -0,0 +1,122 @@ +/* + * 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.core.query.crdt.ops; + +import com.basho.riak.client.core.util.BinaryValue; + +import java.util.HashSet; +import java.util.Set; + +import static java.util.Collections.unmodifiableSet; + +public class MapOp implements CrdtOp +{ + + public enum FieldType + { + SET, COUNTER, MAP, REGISTER, FLAG + } + + public static class MapField + { + public final FieldType type; + public final BinaryValue key; + + public MapField(FieldType type, BinaryValue key) + { + this.type = type; + this.key = key; + } + } + + public static class MapUpdate + { + public final MapField field; + public final CrdtOp op; + + public MapUpdate(MapField field, CrdtOp op) + { + this.field = field; + this.op = op; + } + } + + private final Set removes; + private final Set updates; + + public MapOp() + { + removes = new HashSet(); + updates = new HashSet(); + } + + public MapOp(Set removes, Set updates) + { + this.removes = removes; + this.updates = updates; + } + + private MapOp update(BinaryValue key, CrdtOp op, FieldType type) + { + MapField field = new MapField(type, key); + MapUpdate update = new MapUpdate(field, op); + updates.add(update); + return this; + } + + public MapOp update(BinaryValue key, SetOp op) + { + return update(key, op, FieldType.SET); + } + + public MapOp update(BinaryValue key, CounterOp op) + { + return update(key, op, FieldType.COUNTER); + } + + public MapOp update(BinaryValue key, MapOp op) + { + return update(key, op, FieldType.MAP); + } + + public MapOp update(BinaryValue key, RegisterOp op) + { + return update(key, op, FieldType.REGISTER); + } + + public MapOp update(BinaryValue key, FlagOp op) + { + return update(key, op, FieldType.FLAG); + } + + + public MapOp remove(BinaryValue key, FieldType type) + { + MapField field = new MapField(type, key); + removes.add(field); + return this; + } + + public Set getRemoves() + { + return unmodifiableSet(removes); + } + + public Set getUpdates() + { + return unmodifiableSet(updates); + } +} diff --git a/src/main/java/com/basho/riak/client/core/query/crdt/ops/RegisterOp.java b/src/main/java/com/basho/riak/client/core/query/crdt/ops/RegisterOp.java new file mode 100644 index 000000000..98e1b67a3 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/crdt/ops/RegisterOp.java @@ -0,0 +1,34 @@ +/* + * 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.core.query.crdt.ops; + +import com.basho.riak.client.core.util.BinaryValue; + +public class RegisterOp implements CrdtOp +{ + + private final BinaryValue value; + + public RegisterOp(BinaryValue value) + { + this.value = value; + } + + public BinaryValue getValue() + { + return value; + } +} diff --git a/src/main/java/com/basho/riak/client/core/query/crdt/ops/SetOp.java b/src/main/java/com/basho/riak/client/core/query/crdt/ops/SetOp.java new file mode 100644 index 000000000..bfaf11172 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/crdt/ops/SetOp.java @@ -0,0 +1,64 @@ +/* + * 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.core.query.crdt.ops; + +import com.basho.riak.client.core.util.BinaryValue; + +import java.util.HashSet; +import java.util.Set; + +public class SetOp implements CrdtOp +{ + + private final Set adds = new HashSet(); + private final Set removes = new HashSet(); + + public SetOp(Set adds, Set removes) + { + this.adds.addAll(adds); + this.removes.addAll(removes); + } + + public SetOp() {} + + public SetOp add(BinaryValue element) + { + this.adds.add(element); + return this; + } + + public SetOp remove(BinaryValue element) + { + this.removes.add(element); + return this; + } + + public Set getAdds() + { + return adds; + } + + public Set getRemoves() + { + return removes; + } + + @Override + public String toString() + { + return "{Add: " + adds + " Remove: " + removes + "}"; + } +} diff --git a/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakCounter.java b/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakCounter.java new file mode 100644 index 000000000..9e204fe0d --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakCounter.java @@ -0,0 +1,46 @@ +/* + * 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.core.query.crdt.types; + +/** + * Representation of the Riak counter datatype. + *

+ * This is an immutable value returned when querying Riak for a counter datatype. + *

+ * @author Dave Rusek + * @since 2.0 + */ +public class RiakCounter extends RiakDatatype +{ + + private long value = 0; + + public RiakCounter(long value) + { + this.value = value; + } + + /** + * Get this RiakCounter as a {@link Long} value. + * @return a Long value for this RiakCounter. + */ + @Override + public Long view() + { + return value; + } + +} diff --git a/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakDatatype.java b/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakDatatype.java new file mode 100644 index 000000000..75ceff01d --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakDatatype.java @@ -0,0 +1,146 @@ +/* + * 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.core.query.crdt.types; + +/** + * Base abstract class for all datatypes. + * + * @author Dave Rusek + * @since 2.0 + */ +public abstract class RiakDatatype +{ + + public abstract Object view(); + + /** + * Determine if this datatype is a map. + * @return true if a map, false otherwise. + */ + public boolean isMap() + { + return this instanceof RiakMap; + } + + /** + * Determine if this datatype is a set. + * @return true if a set, false otherwise. + */ + public boolean isSet() + { + return this instanceof RiakSet; + } + + /** + * Determine if this datatype is a counter. + * @return true if a counter, false otherwise. + */ + public boolean isCounter() + { + return this instanceof RiakCounter; + } + + /** + * Determine if this datatype is a register. + * @return true if a register, false otherwise. + */ + public boolean isRegister() + { + return this instanceof RiakRegister; + } + + /** + * Determine if this datatype is a flag. + * @return true if a flag, false otherwise. + */ + public boolean isFlag() + { + return this instanceof RiakFlag; + } + + /** + * Get this datatype as a map. + * @return a RiakMap + * @throws IllegalStateException if this is not a map. + */ + public RiakMap getAsMap() + { + if (!isMap()) + { + throw new IllegalStateException("This is not an instance of a CrdtMap"); + } + return (RiakMap) this; + } + + /** + * Get this datatype as a set. + * @return a RiakSet + * @throws IllegalStateException if this is not a set. + */ + public RiakSet getAsSet() + { + if (!isSet()) + { + throw new IllegalStateException("This is not an instance of a CrdtSet"); + } + return (RiakSet) this; + } + + /** + * Get this datatype as a counter. + * @return a RiakCounter + * @throws IllegalStateException if this is not a counter. + */ + public RiakCounter getAsCounter() + { + if (!isCounter()) + { + throw new IllegalStateException("This is not an instance of a CrdtCounter"); + } + return (RiakCounter) this; + } + + /** + * Get this datatype as a register. + * @return a RiakRegister + * @throws IllegalStateException if this is not a register. + */ + public RiakRegister getAsRegister() + { + if (!isRegister()) + { + throw new IllegalStateException("This is not an instance of a CrdtRegister"); + } + return (RiakRegister) this; + } + + /** + * Get this datatype as a flag. + * @return a RiakFlag + * @throws IllegalStateException if this is not a flag. + */ + public RiakFlag getAsFlag() + { + if (!isFlag()) + { + throw new IllegalStateException("This is not an instance of a CrdtFlag"); + } + return (RiakFlag) this; + } + + + +} diff --git a/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakFlag.java b/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakFlag.java new file mode 100644 index 000000000..1a005b30a --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakFlag.java @@ -0,0 +1,55 @@ +/* + * 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.core.query.crdt.types; + +/** + * Representation of the Riak flag datatype. + *

+ * This in an immutable boolean value which can be returned within a + * {@link RiakMap}. + *

+ * @author Dave Rusek + * @since 2.0 + */ +public class RiakFlag extends RiakDatatype +{ + + private boolean enabled = false; + + public RiakFlag(boolean enabled) + { + this.enabled = enabled; + } + + /** + * Return whether this flag is enabled. + * @return true if enabled, false otherwise. + */ + public boolean getEnabled() + { + return enabled; + } + + /** + * Return whether this flag is enabled. + * @return true if enabled, false otherwise. + */ + @Override + public Boolean view() + { + return enabled; + } +} diff --git a/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakMap.java b/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakMap.java new file mode 100644 index 000000000..6cdb827c2 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakMap.java @@ -0,0 +1,253 @@ +/* + * 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.core.query.crdt.types; + +import com.basho.riak.client.core.util.BinaryValue; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import static java.util.Collections.unmodifiableMap; + +/** + * Representation of the Riak map datatype. + *

+ * This is an immutable map returned when querying Riak for + * a map datatype. + *

+ * + * @author Dave Rusek + * @since 2.0 + */ +public class RiakMap extends RiakDatatype +{ + + private final Map> entries = + new HashMap>(); + + public RiakMap(List mapEntries) + { + + for (MapEntry entry : mapEntries) + { + List datatypes; + if ((datatypes = entries.get(entry.field)) == null) + { + datatypes = new LinkedList(); + entries.put(entry.field, datatypes); + } + datatypes.add(entry.element); + } + } + + /** + * Returns the value(s) to which the specified key is mapped, or {@literal null} + * if the map contains no mapping for the key. + * @param key key whose associated value(s) is to be returned. + * @return a List containing one or more datatypes, or null if this map contains no mapping for the key. + */ + public List get(BinaryValue key) + { + return entries.get(key); + } + + /** + * Returns a RiakMap to which the specified key is mapped, or {@literal null} + * if no RiakMap is present. + * @param key key whose associated RiakMap is to be returned. + * @return a RiakMap, or null if one is not present. + */ + public RiakMap getMap(BinaryValue key) + { + if (entries.containsKey(key)) { + for (RiakDatatype dt : entries.get(key)) { + if (dt.isMap()) { + return dt.getAsMap(); + } + } + } + return null; + } + + /** + * Returns a RiakMap to which the specified key is mapped, or {@literal null} + * if no RiakMap is present. + * @param key key whose associated RiakMap is to be returned. + * @return a RiakMap, or null if one is not present. + */ + public RiakMap getMap(String key) + { + return getMap(BinaryValue.create(key)); + } + + /** + * Returns a RiakSet to which the specified key is mapped, or {@literal null} + * if no RiakSet is present. + * @param key key whose associated RiakSet is to be returned. + * @return a RiakSet, or null if one is not present. + */ + public RiakSet getSet(BinaryValue key) + { + if (entries.containsKey(key)) { + for (RiakDatatype dt : entries.get(key)) { + if (dt.isSet()) { + return dt.getAsSet(); + } + } + } + return null; + } + + /** + * Returns a RiakSet to which the specified key is mapped, or {@literal null} + * if no RiakSet is present. + * @param key key whose associated RiakSet is to be returned. + * @return a RiakSet, or null if one is not present. + */ + public RiakSet getSet(String key) + { + return getSet(BinaryValue.create(key)); + } + + /** + * Returns a RiakCounter to which the specified key is mapped, or {@literal null} + * if no RiakCounter is present. + * @param key key whose associated RiakCounter is to be returned. + * @return a RiakCounter, or null if one is not present. + */ + public RiakCounter getCounter(BinaryValue key) + { + if (entries.containsKey(key)) { + for (RiakDatatype dt : entries.get(key)) { + if (dt.isCounter()) { + return dt.getAsCounter(); + } + } + } + return null; + } + + /** + * Returns a RiakCounter to which the specified key is mapped, or {@literal null} + * if no RiakCounter is present. + * @param key key whose associated RiakCounter is to be returned. + * @return a RiakCounter, or null if one is not present. + */ + public RiakCounter getCounter(String key) + { + return getCounter(BinaryValue.create(key)); + } + + /** + * Returns a RiakFlag to which the specified key is mapped, or {@literal null} + * if no RiakFlag is present. + * @param key key whose associated RiakFlag is to be returned. + * @return a RiakFlag, or null if one is not present. + */ + public RiakFlag getFlag(BinaryValue key) + { + if (entries.containsKey(key)) { + for (RiakDatatype dt : entries.get(key)) { + if (dt.isFlag()) { + return dt.getAsFlag(); + } + } + } + return null; + } + + /** + * Returns a RiakFlag to which the specified key is mapped, or {@literal null} + * if no RiakFlag is present. + * @param key key whose associated RiakFlag is to be returned. + * @return a RiakFlag, or null if one is not present. + */ + public RiakFlag getFlag(String key) + { + return getFlag(BinaryValue.create(key)); + } + + /** + * Returns a RiakRegister to which the specified key is mapped, or {@literal null} + * if no RiakRegister is present. + * @param key key whose associated RiakRegister is to be returned. + * @return a RiakRegister, or null if one is not present. + */ + public RiakRegister getRegister(BinaryValue key) + { + if (entries.containsKey(key)) { + for (RiakDatatype dt : entries.get(key)) { + if (dt.isRegister()) { + return dt.getAsRegister(); + } + } + } + return null; + } + + /** + * Returns a RiakRegister to which the specified key is mapped, or {@literal null} + * if no RiakRegister is present. + * @param key key whose associated RiakRegister is to be returned. + * @return a RiakRegister, or null if one is not present. + */ + public RiakRegister getRegister(String key) + { + return getRegister(BinaryValue.create(key)); + } + + /** + * Get this RiakMap as a {@link Map}. The returned Map is unmodifiable. + * + * @return a read-only view of the RiakMap. + */ + @Override + public Map> view() + { + return unmodifiableMap(entries); + } + + /** + * A RiakMap entry (key/value pair). + */ + public static final class MapEntry + { + + private final BinaryValue field; + private final RiakDatatype element; + + public MapEntry(BinaryValue field, RiakDatatype element) + { + this.field = field; + this.element = element; + } + + public BinaryValue getField() + { + return field; + } + + public RiakDatatype getElement() + { + return element; + } + + } + + +} diff --git a/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakRegister.java b/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakRegister.java new file mode 100644 index 000000000..50cfea10e --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakRegister.java @@ -0,0 +1,61 @@ +/* + * 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.core.query.crdt.types; + +import com.basho.riak.client.core.util.BinaryValue; + +/** + * Representation of the Riak register datatype. + *

+ * This is an immutable register which can be returned within a {@link RiakMap}. + *

+ * @author Dave Rusek + * @since 2.0 + */ +public class RiakRegister extends RiakDatatype +{ + private final BinaryValue value; + + public RiakRegister(BinaryValue value) + { + this.value = value; + } + + /** + * Returns the RiakRegister as a BinaryValue. + * @return the register. + */ + public BinaryValue getValue() + { + return value; + } + + /** + * Returns the RiakRegister as a BinaryValue. + * @return the register. + */ + @Override + public BinaryValue view() + { + return value; + } + + @Override + public String toString() + { + return value.toString(); + } +} diff --git a/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakSet.java b/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakSet.java new file mode 100644 index 000000000..8f80abc88 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/crdt/types/RiakSet.java @@ -0,0 +1,75 @@ +/* + * 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.core.query.crdt.types; + +import com.basho.riak.client.core.util.BinaryValue; + +import java.util.*; + +/** + * Representation of the Riak set datatype. + *

+ * This is an immutable set returned when querying Riak for a set datatype. + *

+ * @author Dave Rusek + * @since 2.0 + */ +public class RiakSet extends RiakDatatype +{ + private final Set elements = + new HashSet(); + + public RiakSet(List elements) + { + this.elements.addAll(elements); + } + + /** + * Check to see if the supplied value exists in this RiakSet. + * @param element The value to check. + * @return true if this RiakSet contains the value, false otherwise. + */ + public boolean contains(BinaryValue element) + { + return elements.contains(element); + } + + /** + * Check to see if the supplied value exists in this RiakSet. + * @param element The value to check. + * @return true if this RiakSet contains the value, false otherwise. + */ + public boolean contains(String element) + { + return elements.contains(BinaryValue.create(element)); + } + + /** + * Get this set as a {@link Set}. The returned Set is unmodifiable. + * @return a read-only view of this RiakSet. + */ + @Override + public Set view() + { + return Collections.unmodifiableSet(elements); + } + + @Override + public String toString() + { + return elements.toString(); + } +} diff --git a/src/main/java/com/basho/riak/client/core/query/functions/Function.java b/src/main/java/com/basho/riak/client/core/query/functions/Function.java new file mode 100644 index 000000000..cecf11d61 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/functions/Function.java @@ -0,0 +1,433 @@ +/* + * 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.core.query.functions; + +/** + * A Function for use with bucket properties or asMap reduce. + * + * Instances are created via the provided static factory methods or by using the Builder. + * @author Brian Roach + * @since 2.0 + */ +public class Function +{ + private final boolean isJavascript; + + private final String name; + private final String bucket; + private final String key; + private final String source; + + private final String module; + private final String function; + + private Function(Builder builder) + { + switch(builder.type) + { + case NAMED_JS: + case STORED_JS: + case ANON_JS: + isJavascript = true; + break; + default: + isJavascript = false; + break; + } + + this.name = builder.name; + this.bucket = builder.bucket; + this.key = builder.key; + this.source = builder.source; + this.module = builder.module; + this.function = builder.function; + } + + /** + * Returns whether this function is Javascript or not. + * @return true if this is a JS function, false if Erlang. + */ + public boolean isJavascript() + { + return isJavascript; + } + + // JS stuff + /** + * Return whether this is a named JS function or not. + * @return true if this is a named JS function, false otherwise. + */ + public boolean isNamed() + { + return name != null; + } + + /** + * Return whether this is a JS function stored as an object in Riak. + * @return true if this is a stored JS function, false otherwise. + */ + public boolean isStored() + { + return bucket != null; + } + + /** + * Return whether this is an anonymous JS function or not. + * @return true if this is an anonymous JS function, false otherwise. + */ + public boolean isAnonymous() + { + return source != null; + } + + /** + * Returns the name of this named JS function. + * @return The name for this function or null if this is not a named JS function. + */ + public String getName() + { + return name; + } + + /** + * Returns the bucket for this stored JS function. + * @return The bucket for this function or null if this is not a stored JS function. + */ + public String getBucket() + { + return bucket; + } + + /** + * Returns the key for this stored JS function. + * @return The key for function or null is this is not a stored JS function. + */ + public String getKey() + { + return key; + } + + /** + * Returns the source for this anonymous JS function. + * @return The source for this function, or null if it is not an anonymous function. + */ + public String getSource() + { + return source; + } + + // Erlang + /** + * Returns the module for this Erlang function. + * @return the module for this function or null if this is not an Erlang function. + */ + public String getModule() + { + return module; + } + + /** + * Returns the function for this Erlang function. + * @return the function or null if this is not an erlang function. + */ + public String getFunction() + { + return function; + } + + /** + * Static factory method for Named Javascript Functions. + * @param name the name of the Javascript function. + * @return a Function representing a named JS Function. + */ + public static Function newNamedJsFunction(String name) + { + return new Builder().withName(name).build(); + } + + /** + * Static factory method for Stored Javascript Functions. + * @param bucket The bucket where the JS function is stored + * @param key the key for the object containing the JS function + * @return a Function representing a stored JS function. + */ + public static Function newStoredJsFunction(String bucket, String key) + { + return new Builder().withBucket(bucket).withKey(key).build(); + } + + /** + * Static factory method for Anonymous JS Functions. + * @param source the javascript source + * @return a Function representing an anonymous JS function. + */ + public static Function newAnonymousJsFunction(String source) + { + return new Builder().withSource(source).build(); + } + + /** + * Static factory method for Erlang Functions. + * @param module the module that contains the Erlang function. + * @param function the name of the erlang function. + * @return a Function representing a Erlang function. + */ + public static Function newErlangFunction(String module, String function) + { + return new Builder().withModule(module).withFunction(function).build(); + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + (null == name ? 0 : name.hashCode()); + result = prime * result + (null == bucket ? 0 : bucket.hashCode()); + result = prime * result + (null == key ? 0 : key.hashCode()); + result = prime * result + (null == source ? 0 : source.hashCode()); + result = prime * result + (null == module ? 0 : module.hashCode()); + result = prime * result + (null == function ? 0 : function.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (!(obj instanceof Function)) + { + return false; + } + Function other = (Function) obj; + if ( (name == other.name || (name != null && name.equals(other.name))) && + (bucket == other.bucket || (bucket != null && bucket.equals(other.bucket))) && + (key == other.key || (key != null && key.equals(other.key))) && + (source == other.source || (source != null && source.equals(other.source))) && + (module == other.module || (module != null && module.equals(other.module))) && + (function == other.function || (function != null && function.equals(other.function))) + ) + { + return true; + } + else + { + return false; + } + } + + /** + * Builder class for creating Function instances. + */ + public static class Builder + { + private enum Type { + UNKNOWN, + NAMED_JS, + STORED_JS, + ANON_JS, + ERLANG + } + private Type type = Type.UNKNOWN; + + private String name; + private String bucket; + private String key; + private String source; + + private String module; + private String function; + + + + public Builder() + { + + } + + /** + * Set the name for a Named Javascript function. + * @param name the name of the function. + * @return a reference to this builder. + */ + public Builder withName(String name) + { + stringCheck(name); + switch(type) + { + case UNKNOWN: + case NAMED_JS: + this.name = name; + type = Type.NAMED_JS; + break; + default: + throw new IllegalArgumentException(type + " can not have a name."); + } + + return this; + } + + /** + * Set the bucket for a stored JS function. + * @param bucket the name of the bucket where this function is stored. + * @return a reference to this builder. + */ + public Builder withBucket(String bucket) + { + stringCheck(bucket); + switch(type) + { + case UNKNOWN: + case STORED_JS: + this.bucket = bucket; + type = Type.STORED_JS; + break; + default: + throw new IllegalArgumentException(type + " can not have a bucket."); + } + + return this; + } + + /** + * Set the key for a stored JS function. + * @param key the key for the object that contains the JS function. + * @return a reference to this builder. + */ + public Builder withKey(String key) + { + stringCheck(key); + switch(type) + { + case UNKNOWN: + case STORED_JS: + this.key = key; + type = Type.STORED_JS; + break; + default: + throw new IllegalArgumentException(type + " can not have a key."); + } + return this; + } + + /** + * Set the source for an anonymous JS function + * @param source the Javascript source. + * @return a reference to this builder. + */ + public Builder withSource(String source) + { + stringCheck(source); + switch(type) + { + case UNKNOWN: + this.source = source; + type = Type.ANON_JS; + break; + default: + throw new IllegalArgumentException(type + " can not have a source."); + } + return this; + } + + /** + * Set the module for an Erlang function. + * @param module the name of the module containing the Erlang function. + * @return a reference to this builder. + */ + public Builder withModule(String module) + { + stringCheck(module); + switch(type) + { + case UNKNOWN: + case ERLANG: + this.module = module; + type = Type.ERLANG; + break; + default: + throw new IllegalArgumentException(type + " can not have a module."); + } + return this; + } + + /** + * Set the function name for an Erlang function. + * @param function the name of the Erlang function. + * @return a reference to this builder. + */ + public Builder withFunction(String function) + { + stringCheck(function); + switch(type) + { + case UNKNOWN: + case ERLANG: + this.function = function; + type = Type.ERLANG; + break; + default: + throw new IllegalArgumentException(type + " can not have a function."); + } + return this; + } + + private void stringCheck(String arg) + { + if (null == arg || arg.length() == 0) + { + throw new IllegalArgumentException("String can not be null or zero length."); + } + } + + /** + * Construct and return a Function. + * @return a Function + */ + public Function build() + { + switch(type) + { + case UNKNOWN: + throw new IllegalStateException("Nothing to build."); + case STORED_JS: + if (null == bucket || null == key) + { + throw new IllegalArgumentException("Stored Javascript requires both a bucket and key"); + } + break; + case ERLANG: + if (null == module || null == function) + { + throw new IllegalArgumentException("Erlang requires both a module and a function"); + } + break; + default: + break; + } + + return new Function(this); + } + } + +} diff --git a/src/main/java/com/basho/riak/client/core/query/indexes/BigIntIndex.java b/src/main/java/com/basho/riak/client/core/query/indexes/BigIntIndex.java new file mode 100644 index 000000000..0d9be08e7 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/indexes/BigIntIndex.java @@ -0,0 +1,77 @@ +/* + * 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.core.query.indexes; + +import com.basho.riak.client.core.util.BinaryValue; +import java.math.BigInteger; +import java.nio.charset.Charset; + +/** + * + * @author Brian Roach + */ +public class BigIntIndex extends RiakIndex +{ + private BigIntIndex(Name name) + { + super(name); + } + + @Override + protected BinaryValue convert(BigInteger value) + { + // The Protocol Buffers API takes the bytes for the textual representation + // of the number rather than an actual bytes for the number :/ + return BinaryValue.unsafeCreate(value.toString().getBytes(Charset.forName("UTF-8"))); + } + + @Override + protected BigInteger convert(BinaryValue value) + { + // The Protocol Buffers API returns the bytes for the textual representation + // of the number rather than an actual bytes for the number :/ + return new BigInteger(value.toString(Charset.forName("UTF-8"))); + } + + public static Name named(String name) + { + return new Name(name); + } + + /** + * Encapsulates the name and {@code IndexType} for a {@code BigIntIndex} + */ + public static class Name extends RiakIndex.Name + { + /** + * Constructs a RiakIndex.Name to be used with {@link RiakIndexes} + * @param name The name of this index. + */ + Name(String name) + { + super(name, IndexType.INT); + } + + @Override + BigIntIndex createIndex() + { + return new BigIntIndex(this); + } + + } + +} diff --git a/src/main/java/com/basho/riak/client/core/query/indexes/IndexType.java b/src/main/java/com/basho/riak/client/core/query/indexes/IndexType.java new file mode 100644 index 000000000..f9b13969d --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/indexes/IndexType.java @@ -0,0 +1,105 @@ +/* + * 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.core.query.indexes; + +/** + * Enum that encapsulates the suffix used to determine and index type in Riak. + *

+ * There are two types of Seconrady Indexes (2i) in Riak; "Integer" and + * "Binary". The current server API distinguishes between them via a + * suffix ({@code "_int"} and {@code "_bin"} respectively). + *

+ * @author Brian Roach + * @since 2.0 + * @see RiakIndex + */ +public enum IndexType +{ + /** + * Encapsulates the {@code "_int"} suffix for Riak index names. + */ + INT("_int"), + /** + * Encapsulates the {@code "_bin"} suffix for Riak index names. + */ + BIN("_bin"), + /** + * Used for the special $bucket index + */ + BUCKET(""), + /** + * Used for the special $key index + */ + KEY(""); + + private final String suffix; + + private IndexType(String suffix) + { + this.suffix = suffix; + } + + /** + * Returns the suffix for this type. + * @return a {@code String} containing the suffix for this index type. + */ + public String suffix() + { + return suffix; + } + + /** + * Returns the index type from its fully qualified name

There are two + * types of Seconrady Indexes (2i) in Riak; "Integer" and "Binary". The + * current server API distinguishes between them via a suffix ({@code _int} + * and {@code _bin} respectively). This method takes a "fully qualified" 2i + * name (e.g. "my_index_int") and returns an enum that represents the type. + * + * @param fullname a "fully qualified" 2i name ending with the suffix "_int" + * or "_bin" + * @return the {@link IndexType} + * @throws IllegalArgumentException if the supplied index name does not have + * a valid suffix. + */ + public static IndexType typeFromFullname(String fullname) + { + if (fullname.equalsIgnoreCase("$bucket")) + { + return IndexType.BUCKET; + } + else if (fullname.equalsIgnoreCase("$key")) + { + return IndexType.KEY; + } + else + { + int i = fullname.lastIndexOf('_'); + if (i != -1) + { + String suffix = fullname.substring(i); + for (IndexType t : IndexType.values()) + { + if (t.suffix().equalsIgnoreCase(suffix)) + { + return t; + } + } + } + + throw new IllegalArgumentException("Indexname does not end with valid suffix"); + } + } +} diff --git a/src/main/java/com/basho/riak/client/core/query/indexes/LongIntIndex.java b/src/main/java/com/basho/riak/client/core/query/indexes/LongIntIndex.java new file mode 100644 index 000000000..c0e867a50 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/indexes/LongIntIndex.java @@ -0,0 +1,84 @@ +/* + * 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.core.query.indexes; + +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; +import java.nio.charset.Charset; + +/** + * {@code RiakIndex} implementation used to access a Riak {@code _int} Secondary Index using {@code Long} values. + * + *

+ * Data in Riak including secondary indexes is stored as bytes. This implementation + * of {@code RiakIndex} provides access to those bytes by converting to + * and from {@code Long} values. Its type is {@link IndexType#INT} + *

+ * @author Brian Roach + * @since 2.0 + * @see RiakIndexes + * @see RiakObject#getIndexes() + */ +public class LongIntIndex extends RiakIndex +{ + private LongIntIndex(Name name) + { + super(name); + } + + @Override + protected BinaryValue convert(Long value) + { + // The Protocol Buffers API takes the bytes for the textual representation + // of the number rather than an actual bytes for the number :/ + return BinaryValue.unsafeCreate(value.toString().getBytes(Charset.forName("UTF-8"))); + } + + @Override + protected Long convert(BinaryValue value) + { + // The Protocol Buffers API returns the bytes for the textual representation + // of the number rather than an actual bytes for the number :/ + return Long.valueOf(value.toString(Charset.forName("UTF-8"))); + } + + public static Name named(String name) + { + return new Name(name); + } + + /** + * Encapsulates the name and {@code IndexType} for a {@code LongIntIndex} + */ + public static class Name extends RiakIndex.Name + { + /** + * Constructs a RiakIndex.Name to be used with {@link RiakIndexes} + * @param name The name of this index. + */ + Name(String name) + { + super(name, IndexType.INT); + } + + @Override + LongIntIndex createIndex() + { + return new LongIntIndex(this); + } + + } +} diff --git a/src/main/java/com/basho/riak/client/core/query/indexes/RawIndex.java b/src/main/java/com/basho/riak/client/core/query/indexes/RawIndex.java new file mode 100644 index 000000000..6d84de8e7 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/indexes/RawIndex.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.core.query.indexes; + +import com.basho.riak.client.core.util.BinaryValue; + +/** + * {@code RiakIndex} implementation used to access a Riak {@code _int} or {@code _bin} + * Secondary Index using {@code BinaryValue} ({@code byte[]}) values. + *

+ * Data in Riak including secondary indexes is stored as bytes. This implementation + * of {@code RiakIndex} provides direct access to those bytes. + *

+ * + * @author Brian Roach + * @since 2.0 + */ +public class RawIndex extends RiakIndex +{ + private RawIndex(Name name) + { + super(name); + } + + @Override + protected BinaryValue convert(BinaryValue value) + { + return value; + } + + public static Name named(String name, IndexType type) + { + return new Name(name, type); + } + + /** + * Encapsulates the name and {@code IndexType} for a {@code RawIndex} + */ + public static class Name extends RiakIndex.Name + { + /** + * Constructs a RiakIndex.Name to be used with {@link RiakIndexes} + * @param name The name of this index. + * @param type The IndexType of this index + */ + Name(String name, IndexType type) + { + super(name, type); + } + + @Override + RawIndex createIndex() + { + return new RawIndex(this); + } + + } + +} diff --git a/src/main/java/com/basho/riak/client/core/query/indexes/RiakIndex.java b/src/main/java/com/basho/riak/client/core/query/indexes/RiakIndex.java new file mode 100644 index 000000000..a6ee76616 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/indexes/RiakIndex.java @@ -0,0 +1,446 @@ +/* + * 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.core.query.indexes; + +import com.basho.riak.client.core.util.BinaryValue; +import java.util.Collection; +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Abstract base class for modeling a Riak Secondary Index (2i). + * + *

Secondary Indexing (2i) in Riak gives developers the ability, at write + * time, to tag an object stored in Riak with one or more queryable values. + * Since the KV data is completely opaque to 2i, the user must tell 2i exactly + * what attribute to index on and what its index value should be, via key/value + * metadata. This is different from Search, which parses the data and builds + * indexes based on a schema. Riak 2i currently requires the LevelDB or Memory + * backend. + *

+ *

A {@code RiakIndex} is made up of the index name, a type, + * then one or more queryable index values. + *

+ *

+ * {@code RiakIndex} instances are created and managed via the {@link RiakIndexes} + * container. + *

+ * @riak.threadsafety This class is designed to be thread safe. + * @author Brian Roach + * @since 2.0 + * @see RiakIndexes + * @see Using Secondary + * Indexes in Riak + */ +public abstract class RiakIndex implements Iterable +{ + private final Set values; + private final IndexType type; + private final String name; + + /** + * Constructs a RiakIndex from the supplied RiakIndex.Name + * @param name A {@link Name} to build this RiakIndex from. + */ + protected RiakIndex(Name name) + { + this.name = name.name; + this.type = name.type; + + if (name.values != null) + { + this.values = name.values; + } + else + { + this.values = Collections.newSetFromMap(new ConcurrentHashMap()); + } + } + + /** + * Add a value to this secondary index. + * + * @param value the value to add + * @return a reference to this object + */ + public final RiakIndex add(T value) + { + values.add(convert(value)); + return this; + } + + /** + * Add a asSet of values to this secondary index. + * + * @param values a collection of values to add + * @return a reference to this object + */ + public final RiakIndex add(Collection values) + { + for (T value : values) + { + add(value); + } + + return this; + } + + /** + * Determine if this index contains a value + * + * @param value the value to check for + * @return {@code true} if this index contains the value, {@code false} + * otherwise. + */ + public final boolean hasValue(T value) + { + return values.contains(convert(value)); + } + + /** + * Remove a value from this index. + * + * @param value the value to remove + * @return a reference to this object + */ + public final RiakIndex remove(T value) + { + values.remove(convert(value)); + return this; + } + + /** + * Remove a asSet of values from this index + * + * @param values a collection of values to remove + * @return a reference to this object + */ + public final RiakIndex remove(Collection values) + { + for (T value : values) + { + remove(value); + } + return this; + } + + /** + * Remove all values from this index + * @return a reference to this object + */ + public final RiakIndex removeAll() + { + values.clear(); + return this; + } + + /** + * Returns an iterator over the asSet of values in this index. + * This iterator is a "weakly consistent" iterator that will never throw + * {@link ConcurrentModificationException}, and guarantees to traverse elements + * as they existed upon construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + * @return an Iterator. + */ + @Override + public final Iterator iterator() + { + return new Iterator() + { + private Iterator i = values.iterator(); + + @Override + public boolean hasNext() + { + return i.hasNext(); + } + + @Override + public T next() + { + return convert(i.next()); + } + + @Override + public void remove() + { + i.remove(); + } + }; + } + + /** + * Return the number of values in this index. + * @return the number of values + */ + public final int size() + { + return values.size(); + } + + /** + * Determine if this index has any values. + * @return {@code true} if this index has no values, {@code false} otherwise. + */ + public final boolean isEmpty() + { + return values.isEmpty(); + } + + /** + * Return the values in this index as raw bytes. + * + * @return an unmodifiable view of the raw values in this index. + */ + public final Set rawValues() + { + return Collections.unmodifiableSet(values); + } + + /** + * Return the values in this index. + * The returned {@code Set} is unmodifiable. + * @return an unmodifiable view of the values in this index. + */ + public final Set values() + { + Set convertedValues = new HashSet(); + for (BinaryValue baw : values) + { + convertedValues.add(convert(baw)); + } + return Collections.unmodifiableSet(convertedValues); + } + + /** + * Get the type of this index. + * + * @return the enum representing the type of this index + */ + public final IndexType getType() + { + return type; + } + + /** + * Get the index's name. + * + * @return the name of this index without the type suffix + */ + public final String getName() + { + return name; + } + + /** + * Get the fully qualified name of this index + * + * @return the name of this index including the type suffix + */ + public final String getFullname() + { + return name + type.suffix(); + } + + /** + * Convert a value to a BinaryValue. + *

Index values are stored + * internally as bytes. Concrete classes implement this method to convert + * values to bytes.

+ * + * @param value the value to convert + * @return a BinaryValue containing the converted bytes + */ + protected abstract BinaryValue convert(T value); + + /** + * Convert bytes to a value type. + *

Index values are stored internally as + * bytes. Concrete classes implement this method to convert bytes to values. + *

+ * + * @param value the value to convert + * @return a value of type T + */ + protected abstract T convert(BinaryValue value); + + /** + * Returns a hash code value for the object. + * This method is supported for the benefit of hash tables such as those provided by HashMap. + * @return a hash code value for this object. + * @see RiakIndex#equals(java.lang.Object) + */ + @Override + public final int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((getFullname() == null) ? 0 : getFullname().hashCode()); + result = prime * result + ((getName() == null) ? 0 : getName().hashCode()); + return result; + } + + /** + * Indicates whether this RiakIndex is "equal to" another RiakIndex. + *

+ * Only the name and index type of the RiakIndex are used to determine equality. + *

+ * + * @param obj a RiakIndex + * @return true if this RiakIndex has the same name and type + */ + @Override + public final boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (!(obj instanceof RiakIndex)) + { + return false; + } + + RiakIndex other = (RiakIndex) obj; + + if (getType() != other.getType()) + { + return false; + } + else if (!getName().equals(other.getName())) + { + return false; + } + + return true; + } + + @Override + public String toString() + { + return String.format("RiakIndex [name=%s, type=%s]", name, type); + } + + /** + * Abstract base class used to encapsulate a {@code RiakIndex} name and type. + * + * This class serves two purposes; encapsulating the name and type of an + * index as well as being a builder used with {@link RiakIndexes}. + * + * @param the RiakIndex this Name encapsulates + * @see RiakIndexes + */ + public static abstract class Name + { + protected final String name; + protected final IndexType type; + private volatile Set values; + + protected Name(String name, IndexType type) + { + name = name.toLowerCase(); + this.name = stripSuffix(name, type); + this.type = type; + } + + /** + * If the index name has the suffix, strip it + * + * @param name + * @return the name, stripped + */ + private String stripSuffix(String name, IndexType type) + { + if (name.endsWith(type.suffix())) + { + return name.substring(0, name.indexOf(type.suffix())); + } + else + { + return name; + } + } + + /** + * Get the short name of this index + * + * @return the name of this index without the type suffix + */ + public final String getName() + { + return name; + } + + /** + * Get the fully qualified name of this index + * + * @return the name of this index including the type suffix + */ + public final String getFullname() + { + return name + type.suffix(); + } + + /** + * Get the type of this index + * + * @return the enum representing the type of this index + */ + public final IndexType getType() + { + return type; + } + + /** + * Wrap an existing index + * + * @param otherIndex + * @return a reference to this object + */ + final Name wrap(RiakIndex otherIndex) + { + values = otherIndex.values; + return this; + } + + /** + * Copy the values from the supplied index into this one. + * + * @param otherIndex + * @return a reference to this object + */ + final Name copyFrom(RiakIndex otherIndex) + { + values = Collections.newSetFromMap(new ConcurrentHashMap()); + for (BinaryValue baw : otherIndex.values) + { + values.add(baw); + } + return this; + } + + abstract T createIndex(); + } +} diff --git a/src/main/java/com/basho/riak/client/core/query/indexes/RiakIndexes.java b/src/main/java/com/basho/riak/client/core/query/indexes/RiakIndexes.java new file mode 100644 index 000000000..6f25f6cfc --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/indexes/RiakIndexes.java @@ -0,0 +1,200 @@ +/* + * 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.core.query.indexes; + +import com.basho.riak.client.core.query.RiakObject; + +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Container used to instantiate and Manage {@code RiakIndex} objects to be used with a {@code RiakObject}. + *

+ * This container manages and allows for an arbitrary number and types of {@link RiakIndex}s to + * be used with a {@link RiakObject}. + *

+ *

Working with RiakIndexes

+ *

Data in Riak, including secondary indexes, is stored as raw bytes. The conversion + * to and from bytes is handled by the concrete {@code RiakIndex} implementations + * and all indexes are managed by this container.

+ *

+ * Each concrete {@code RiakIndex} includes a hybrid builder class named {@code Name}. + * The methods of this class take an instance of that builder as an + * argument to allow for proper type inference and construction of {@code RiakIndex} + * objects to expose. + *

+ *

{@code getIndex()} will either return a reference to + * the existing {@code RiakIndex} or atomically add and return a new one. The + * returned reference is of the type provided by the {@code Name} and is the + * mutable index; changes are made directly to it. + *

+ *
+ * LongIntIndex myIndex = riakIndexes.getIndex(new LongIntIndex.Name("number_on_hand"));
+ * myIndex.removeAll();
+ * myIndex.add(6L);
+ * 
+ *

Calls can be chained, allowing for easy addition or removal of values from + * an index. + *

+ *
+ * riakIndexes.getIndex(new StringBinIndex.Name("colors")).remove("blue").add("red");
+ * 
+ *
Special note when using RawIndex
+ * A {@code RiakIndex} is uniquely identified by its textual name and {@code IndexType} + * regardless of the concrete {@code RiakIndex} implementation being used to view + * or update it. This container enforces this uniqueness by being the source of + * all {@code RiakIndex} instances and managing them in a thread-safe way with + * atomic operations. + *

+ * What this means is that any {@code RiakIndex} having the same name and {@code Indextype} + * will refer to the same index. This is only important to note if you are mixing + * access to the indexes using {@link RawIndex}. The test case copied below demonstrates + * the relation.

+ *
+ * public void wrapping()
+ * {
+ *     // creates or fetches the BIN (_bin) index named "foo", adds a value to it  
+ *     RawIndex index = indexes.getIndex(new RawIndex.Name("foo", IndexType.BIN));
+ *     BinaryValue baw = BinaryValue.unsafeCreate("value".getBytes());
+ *     index.add(baw);
+ *       
+ *     // fetches the previously created index as a StringBinIndex
+ *     StringBinIndex wrapper = indexes.getIndex(new StringBinIndex.Name("foo"));
+ *
+ *     // The references are to different objects
+ *     assertNotSame(index, wrapper);
+ *     // The two objects are equal ( index.equals(wrapper) == true )
+ *     assertEquals(index, wrapper);
+ *     // The value exists
+ *     assertTrue(wrapper.hasValue("value"));
+ *     
+ *     // Removing the value via the StringBinIndex is reflected in the RawIndex
+ *     wrapper.remove("value");
+ *     assertFalse(index.hasValue(baw));
+ * }
+ * 
+ * @riak.threadsafety This is a thread safe container. + * @author Brian Roach + * @since 2.0 + * @see RiakObject#getIndexes() + */ +public class RiakIndexes implements Iterable> +{ + private final ConcurrentHashMap> indexes = + new ConcurrentHashMap>(); + + /** + * Instantiates a new RiakIndexes object containing no RiakIndex objects + */ + public RiakIndexes() + { + + } + + /** + * Return the number of indexes present + * @return the number of indexes + */ + public int size() + { + return indexes.size(); + } + + /** + * Returns whether any {@code RiakIndex} objects are present + * @return {@code true} if there are indexes, {@code false} otherwise + */ + public boolean isEmpty() + { + return indexes.isEmpty(); + } + + /** + * Returns whether a specific RiakIndex is present + * @param name the {@link RiakIndex.Name} representing the index to check for + * @return {@code true} if the index is present, {@code false} otherwise + */ + public boolean hasIndex(RiakIndex.Name name) + { + return indexes.containsKey(name.getFullname()); + } + + /** + * Get the named RiakIndex + *

+ * If the named index does not exist it is created and added to the container + * before being returned. + *

+ * @param name The {@link RiakIndex.Name} of the index to retrieve + * @return The requested index typed accordingly. + */ + public > V getIndex(T name) + { + RiakIndex existing = indexes.get(name.getFullname()); + if (existing != null) + { + return name.wrap(existing).createIndex(); + } + else + { + V newIndex = name.createIndex(); + existing = indexes.putIfAbsent(newIndex.getFullname(), newIndex); + if (existing != null) + { + return getIndex(name); + } + else + { + return newIndex; + } + } + } + + /** + * Remove the named RiakIndex + * @param name the {@code RiakIndex.Name} representing the index to remove + * @return the removed {@code RiakIndex} (typed accordingly) if the index was present, + * {@code null} otherwise + */ + public > T removeIndex(RiakIndex.Name name) + { + RiakIndex removed = indexes.remove(name.getFullname()); + if (removed != null) + { + T index = name.wrap(removed).createIndex(); + return index; + } + else + { + return null; + } + } + + /** + * Remove all indexes. + */ + public void removeAllIndexes() + { + indexes.clear(); + } + + @Override + public Iterator> iterator() + { + return indexes.values().iterator(); + } +} diff --git a/src/main/java/com/basho/riak/client/core/query/indexes/StringBinIndex.java b/src/main/java/com/basho/riak/client/core/query/indexes/StringBinIndex.java new file mode 100644 index 000000000..f421ae1ec --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/indexes/StringBinIndex.java @@ -0,0 +1,115 @@ +/* + * 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.core.query.indexes; + +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; +import java.nio.charset.Charset; + +/** + * {@code RiakIndex} implementation used to access a Riak {@code _bin} Secondary Index using {@code String} values. + *

+ * Data in Riak including secondary indexes is stored as bytes. This implementation + * of {@code RiakIndex} provides access to those bytes by converting to + * and from {@code String} values. Its type is {@link IndexType#BIN} + *

+ *
Riak 2i _bin indexes and sorting
+ *

+ * One of the key features of 2i is the ability to do range queries. As previously + * noted the values are stored in Riak as bytes. Comparison is done byte-by-byte. UTF-8 + * lends itself well to this as its byte ordering is the same as its lexical ordering. + *

+ *

+ * If you are using a character asSet whose byte ordering differs from its lexical ordering, + * range queries will be affected. + *

+ * + * @author Brian Roach + * @since 2.0 + * @see RiakIndexes + * @see RiakObject + */ +public class StringBinIndex extends RiakIndex +{ + private final Charset charset; + + private StringBinIndex(Name name) + { + super(name); + this.charset = name.charset; + } + + @Override + protected BinaryValue convert(String value) + { + return BinaryValue.unsafeCreate(value.getBytes(charset)); + } + + @Override + protected String convert(BinaryValue value) + { + return value.toString(charset); + } + + public static RiakIndex.Name named(String name) + { + return named(name, Charset.defaultCharset()); + } + + public static Name named(String name, Charset charset) + { + return new Name(name, charset); + } + + /** + * Encapsulates the name, character asSet, and {@code IndexType} for a {@code StringBinIndex} + */ + public static class Name extends RiakIndex.Name + { + private final Charset charset; + + /** + * Constructs a RiakIndex.Name to be used with {@link RiakIndexes} + * The default character asSet is used for encoding the values. + * @param name The name of this index. + */ + Name(String name) + { + this(name, Charset.defaultCharset()); + } + /** + * Constructs a RiakIndex.Name to be used with {@link RiakIndexes} + * The supplied character asSet is used for encoding the values. + * @param name The name of this index. + * @param charset The character asSet to use for encoding values + */ + Name(String name, Charset charset) + { + super(name, IndexType.BIN); + this.charset = charset; + } + + @Override + StringBinIndex createIndex() + { + return new StringBinIndex(this); + } + + + + } + +} diff --git a/src/main/java/com/basho/riak/client/core/query/indexes/package-info.java b/src/main/java/com/basho/riak/client/core/query/indexes/package-info.java new file mode 100644 index 000000000..66988b238 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/indexes/package-info.java @@ -0,0 +1,115 @@ +/** + * Low-level API for managing Riak Secondary Indexes (2i) + * + *

Introduction

+ * Secondary Indexing (2i) in Riak gives developers the ability, at write + * time, to tag an object stored in Riak with one or more queryable values. + *

+ * Since the KV data in Riak is completely opaque to 2i, the user must tell 2i exactly + * what attribute to index on and what its index value should be via key/value + * metadata. This is different from Search, which parses the data and builds + * indexes based on a schema. + *

+ *

+ * The classes in this package provide an API for managing secondary indexes. + *

+ *

Important note: 2i currently requires Riak to be configured to + * use the eleveldb or memory backend. The default bitcask backend does not support + * 2i. + *

+ *

Overview

+ * In Riak there are two types of secondary indexes; "integer" and "binary". + * The former represents an index with numeric values whereas the second is used + * for textual (String) data. The server API distinguishes between the two via + * a suffix ({@code "_int"} and {@code "_bin"} respectively) added to the index's + * name. In the client this is encapsulated in the + * {@link com.basho.riak.client.core.query.indexes.IndexType} enum. When specifying + * an index name you do not have to append this suffix; it's done automatically. + *

A {@link com.basho.riak.client.core.query.indexes.RiakIndex} is made up of the index name, it's type, + * then one or more queryable index values. + *

+ *

+ * {@code RiakIndex} instances are created and managed via the {@link com.basho.riak.client.core.query.indexes.RiakIndexes} + * container. The container is stored in a {@link com.basho.riak.client.core.query.RiakObject}. + *

+ *

Working with RiakIndexes

+ *

Data in Riak, including secondary indexes, is stored as raw bytes. The conversion + * to and from bytes is handled by the concrete {@code RiakIndex} implementations + * and all indexes are managed by the {@code RiakIndexes} container. + *

+ * Each concrete {@code RiakIndex} includes a hybrid builder class named {@code Name}. + * The methods of this class take an instance of that builder as an + * argument to allow for proper type inference and construction of {@code RiakIndex} + * objects to expose. + *

+ *

The {@code RiakIndexes}' {@code getIndex()} method will either return a reference to + * the existing {@code RiakIndex} or atomically add and return a new one. The + * returned reference is of the type provided by the {@code Name} and is the + * mutable index; changes are made directly to it. + *

+ *
+ * RiakIndexes myIndexes = riakObject.getIndexes();
+ * LongIntIndex myIndex = myIndexes.getIndex(new LongIntIndex.Name("number_on_hand"));
+ * myIndex.removeAll();
+ * myIndex.add(6L);
+ * 
+ *

Calls can be chained, allowing for easy addition or removal of values from + * an index. + *

+ *
+ * riakObject.getIndexes()
+ *           .getIndex(new StringBinIndex.Name("colors"))
+ *           .remove("blue")
+ *           .add("red");
+ * 
+ *
Special note when using RawIndex
+ * A {@code RiakIndex} is uniquely identified by its textual name and {@code IndexType} + * regardless of the concrete {@code RiakIndex} implementation being used to view + * or update it. This container enforces this uniqueness by being the source of + * all {@code RiakIndex} instances and managing them in a thread-safe way with + * atomic operations. + *

+ * What this means is that any {@code RiakIndex} having the same name and {@code Indextype} + * will refer to the same index. This is only important to note if you are mixing + * access to the indexes using {@link com.basho.riak.client.core.query.indexes.RawIndex}. + * The test case below demonstrates + * the relation.

+ *
+ * public void wrapping()
+ * {
+ *     // creates or fetches the BIN (_bin) index named "foo", adds a value to it  
+ *     RawIndex index = indexes.getIndex(new RawIndex.Name("foo", IndexType.BIN));
+ *     BinaryValue baw = BinaryValue.unsafeCreate("value".getBytes());
+ *     index.add(baw);
+ *       
+ *     // fetches the previously created index as a StringBinIndex
+ *     StringBinIndex wrapper = indexes.getIndex(new StringBinIndex.Name("foo"));
+ *
+ *     // The references are to different objects
+ *     assertNotSame(index, wrapper);
+ *     // The two objects are equal ( index.equals(wrapper) == true )
+ *     assertEquals(index, wrapper);
+ *     // The value exists
+ *     assertTrue(wrapper.hasValue("value"));
+ *     
+ *     // Removing the value via the StringBinIndex is reflected in the RawIndex
+ *     wrapper.remove("value");
+ *     assertFalse(index.hasValue(baw));
+ * }
+ * 
+ *
Riak 2i _bin indexes and sorting
+ *

+ * One of the key features of 2i is the ability to do range queries. As previously + * noted the values are stored in Riak as bytes. Comparison is done byte-by-byte. UTF-8 + * lends itself well to this as its byte ordering is the same as its lexical ordering. + *

+ *

+ * If you are using a {@code _bin} index with a character asSet whose byte ordering + * differs from its lexical ordering, range queries will be affected. + *

+ * @see Using Secondary + * Indexes in Riak + */ +package com.basho.riak.client.core.query.indexes; +// TODO: add more info for using annotations once we get there \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/core/query/links/RiakLink.java b/src/main/java/com/basho/riak/client/core/query/links/RiakLink.java new file mode 100644 index 000000000..fd1506703 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/links/RiakLink.java @@ -0,0 +1,280 @@ +/* + * 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.core.query.links; + +import com.basho.riak.client.core.util.BinaryValue; +import java.nio.charset.Charset; + +/** + * Models a link from one object to another in Riak. + *

+ * Links are metadata that establish one-way relationships between objects in Riak. + * They can be used to loosely model graph like relationships between objects in Riak. + *

+ *

+ * Data in Riak is character asSet agnostic; it's simply raw bytes. Methods are provided here + * to either use your default character asSet or supply a specific one to convert to and + * from {@code String}s. + *

+ * + * @author Russel Brown + * @author Brian Roach + * @since 1.0 + */ +public class RiakLink +{ + + private final BinaryValue bucket; + private final BinaryValue key; + private final BinaryValue tag; + + /** + * Create a RiakLink from the specified parameters. + *

+ * The values are stored internally as bytes. The default {@code Charset} will + * be used to convert the supplied {@code String}s + *

+ * @param bucket the bucket name + * @param key the key + * @param tag the link tag + */ + public RiakLink(String bucket, String key, String tag) + { + this(bucket, key, tag, Charset.defaultCharset()); + } + + /** + * Create a RiakLink from the specified parameters. + *

+ * The values are stored internally as bytes. The supplied {@code Charset} will + * be used to convert the supplied {@code String}s + *

+ * @param bucket the bucket + * @param key the key + * @param tag the link tag + * @param charset the character asSet for the supplied {@code String}s + */ + public RiakLink(String bucket, String key, String tag, Charset charset) + { + this.bucket = BinaryValue.unsafeCreate(bucket.getBytes(charset)); + this.key = BinaryValue.unsafeCreate(key.getBytes(charset)); + this.tag = BinaryValue.unsafeCreate(tag.getBytes(charset)); + } + + /** + * Create a RiakLink from the specified parameters. + * @param bucket the bucket name + * @param key the key + * @param tag the link tag + */ + public RiakLink(BinaryValue bucket, BinaryValue key, BinaryValue tag) + { + this.bucket = bucket; + this.key = key; + this.tag = tag; + } + + /** + * Create a RiakLink that is a copy of another RiakLink. + * + * @param riakLink the RiakLink to copy + */ + public RiakLink(final RiakLink riakLink) + { + this.bucket = riakLink.bucket; + this.key = riakLink.key; + this.tag = riakLink.tag; + } + + /** + * Get the bucket for this RiakLink as a String + *

+ * The bucket is stored internally as bytes. The default {@code Charset} + * is used to convert to a {@code String} + *

+ * @return the bucket as a {@code String} encoded using the default {@code Charset} + */ + public String getBucket() + { + return bucket.toString(); + } + + /** + * Get the bucket for this RiakLink as a String + *

+ * The bucket is stored internally as bytes. The supplied {@code Charset} + * is used to convert to a {@code String} + *

+ * @return the bucket as a {@code String} encoded using the supplied {@code Charset} + */ + public String getBucket(Charset charset) + { + return bucket.toString(charset); + } + + /** + * Get the bucket as a wrapped byte array + * @return the bucket name in a {@link BinaryValue} + */ + public BinaryValue getBucketAsBytes() + { + return bucket; + } + + /** + * Get the key for this RiakLink as a String + *

+ * The key is stored internally as bytes. The default {@code Charset} + * is used to convert to a {@code String} + *

+ * @return the key as a {@code String} encoded using the default {@code Charset} + */ + public String getKey() + { + return key.toString(); + } + + /** + * Get the key for this RiakLink as a String + *

+ * The key is stored internally as bytes. The supplied {@code Charset} + * is used to convert to a {@code String} + *

+ * @return the key as a {@code String} encoded using the supplied {@code Charset} + */ + public String getKey(Charset charset) + { + return key.toString(charset); + } + + /** + * Return the key as a wrapped byte array + * @return the key in a {@link BinaryValue} + */ + public BinaryValue getKeyAsBytes() + { + return key; + } + + /** + * Get the tag for this RiakLink as a String + *

+ * The tag is stored internally as bytes. The default {@code Charset} + * is used to convert to a {@code String} + *

+ * @return the tag as a {@code String} encoded using the default {@code Charset} + */ + public String getTag() + { + return tag.toString(); + } + + /** + * Get the tag for this RiakLink as a String + *

+ * The tag is stored internally as bytes. The supplied {@code Charset} + * is used to convert to a {@code String} + *

+ * @return the tag as a {@code String} encoded using the supplied {@code Charset} + */ + public String getTag(Charset charset) + { + return tag.toString(charset); + } + + /** + * Get the tag as bytes + * @return the tag in a {@link BinaryValue} + */ + public BinaryValue getTagAsBytes() + { + return tag; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((bucket == null) ? 0 : bucket.hashCode()); + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((tag == null) ? 0 : tag.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (!(obj instanceof RiakLink)) + { + return false; + } + RiakLink other = (RiakLink) obj; + if (bucket == null) + { + if (other.bucket != null) + { + return false; + } + } + else if (!bucket.equals(other.bucket)) + { + return false; + } + if (key == null) + { + if (other.key != null) + { + return false; + } + } + else if (!key.equals(other.key)) + { + return false; + } + if (tag == null) + { + if (other.tag != null) + { + return false; + } + } + else if (!tag.equals(other.tag)) + { + return false; + } + return true; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return String.format("RiakLink [tag=%s, bucket=%s, key=%s]", tag, bucket, key); + } +} diff --git a/src/main/java/com/basho/riak/client/core/query/links/RiakLinks.java b/src/main/java/com/basho/riak/client/core/query/links/RiakLinks.java new file mode 100644 index 000000000..4241ec562 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/links/RiakLinks.java @@ -0,0 +1,130 @@ +/* + * 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.core.query.links; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A thread safe container for {@link RiakLink} objects. + * + * @riak.threadsafety This is a thread safe container. + * @author Brian Roach + * @since 2.0 + * @see RiakLink + */ +public class RiakLinks implements Iterable +{ + private final Set links = + Collections.newSetFromMap(new ConcurrentHashMap()); + + /** + * Reports if there are any {@code RiakLink} objects present + * @return {@code true} if there are links present, {@code false} otherwise + */ + public boolean isEmpty() + { + return links.isEmpty(); + } + + /** + * Returns the number of links present + * @return the number of links present + */ + public int size() + { + return links.size(); + } + /** + * Determine if a specific {@code RiakLink} is present. + * @param link The {@code RiakLink} to check for. + * @return {@code true} if the link is present, {@code false} otherwise + */ + public boolean hasLink(RiakLink link) + { + return links.contains(link); + } + + /** + * Add {@link RiakLink}s + * + * @param links a Collection of RiakLink objects to add + * @return a reference to this object + */ + public RiakLinks addLinks(Collection links) + { + this.links.addAll(links); + return this; + } + + /** + * Adds a {@link RiakLink} + * @param link a {@code RiakLink} to be added + * @return a reference to this object + */ + public RiakLinks addLink(RiakLink link) + { + links.add(link); + return this; + } + + /** + * Remove a {@code RiakLink} + * @param link the {@code RiakLink} to remove + * @return {@code true} if the link was present and was removed, {@code false} otherwise + */ + public boolean removeLink(RiakLink link) + { + return links.remove(link); + } + + /** + * Remove all links + */ + public void removeAllLinks() + { + links.clear(); + } + + /** + * Return all the links. + *

+ * Changes to the returned set do not modify this container. + *

+ * @return the set of RiakLink objects + */ + public Set getLinks() + { + return new HashSet(links); + } + + /** + * Return an iterator + * @return an {@code Iterator} that returns the links in this container + */ + @Override + public Iterator iterator() + { + return links.iterator(); + } + + + +} diff --git a/src/main/java/com/basho/riak/client/core/query/search/YokozunaIndex.java b/src/main/java/com/basho/riak/client/core/query/search/YokozunaIndex.java new file mode 100644 index 000000000..2d64736ea --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/search/YokozunaIndex.java @@ -0,0 +1,79 @@ +/* + * 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.core.query.search; + +/** + * Represents a Yokozuna Index. + * + * @author Brian Roach + */ +public class YokozunaIndex +{ + private final String name; + private final String schema; + + /** + * Constructs a Yokozuna index without naming the schema. + * + * Due to an implementation detail on the Riak side, the index name is restricted + * to US-ASCII characters. The supplied String is converted to bytes + * using the UTF-8 Charset. + * + * @param name The name for this index. + */ + public YokozunaIndex(String name) + { + this(name, null); + } + + /** + * Constructs a Yokozuna index. + * Due to an implementation detail on the Riak side, the index name is restricted + * to US-ASCII characters. The supplied String is converted to bytes + * using the UTF-8 Charset. + * + * @param name The name of the index. + * @param schema The name of a schema + */ + public YokozunaIndex(String name, String schema) + { + if (null == name || name.length() == 0) + { + throw new IllegalArgumentException("Index name can not be null or zero length"); + } + this.name = name; + this.schema = schema; + } + + /** + * Returns the name of this index as a UTF-8. + * + * @return The name of the index as UTF-8 encoded String. + */ + public String getName() + { + return name; + } + + /** + * Returns the schema name for this index + * @return The schema name + */ + public String getSchema() + { + return schema; + } +} diff --git a/src/main/java/com/basho/riak/client/core/query/search/YokozunaSchema.java b/src/main/java/com/basho/riak/client/core/query/search/YokozunaSchema.java new file mode 100644 index 000000000..b196c11a2 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/query/search/YokozunaSchema.java @@ -0,0 +1,63 @@ +/* + * 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.core.query.search; + +/** + * + * @author Brian Roach + */ +public class YokozunaSchema +{ + private final String name; + private final String content; + + /** + * Constructs a YokozunaSchema. + * Both name and content must be UTF-8 encoded strings. + * @param name the name of the schema + * @param content the XML that defines the schema + */ + public YokozunaSchema(String name, String content) + { + if ((null == name || name.length() == 0) || + (null == content || content.length() == 0)) + { + throw new IllegalArgumentException("Name and schema cannot be null or zero length"); + } + + this.name = name; + this.content = content; + } + + /** + * Returns the name of this schema. + * @return a UTF-8 string containing the name of this schema + */ + public String getName() + { + return name; + } + + /** + * Returns the XML that defines this schema. + * @return A UTF-8 encoded string containing the XML that defines this schema. + */ + public String getContent() + { + return content; + } + +} diff --git a/src/main/java/com/basho/riak/client/core/util/BinaryValue.java b/src/main/java/com/basho/riak/client/core/util/BinaryValue.java new file mode 100644 index 000000000..9ada3fc2e --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/util/BinaryValue.java @@ -0,0 +1,229 @@ +/* + * 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.core.util; + +import java.nio.charset.Charset; +import java.util.Arrays; + +/** + * Lightweight utility class for byte arrays. + *

+ * In almost all cases Riak is character asSet agnostic when it comes to data; buckets, + * keys, values and metadata are all stored in Riak simply as bytes. + *

+ *

+ * This utility class is used throughout the client in order to encapsulate + * and allow access to those bytes by the user when the data is generally + * representative of a {@code String} so that any character asSet may be used. + *

+ * @riak.threadsafety If you are using this class directly keep in mind that any modification to the + * wrapped byte[] post creation via either a retained reference to the + * array after calling unsafeCreate() or accessing the array directly via + * unsafeGetValue() will lead to undefined behavior in regard to thread + * safety and visibility. + *

+ * + * @author Brian Roach + * @since 2.0 + */ +public final class BinaryValue +{ + private final byte[] data; + + private BinaryValue(byte[] data) + { + this.data = data; + } + + /** + * Create a BinaryValue containing a copy of the supplied {@code byte[]} + *

+ * A copy of the supplied {@code byte[]} is made. + *

+ * + * @param data the {@code byte[]} to copy + * @return a new {@code BinaryValue} + */ + public static BinaryValue create(byte[] data) + { + if (data != null) + { + data = Arrays.copyOf(data, data.length); + } + return new BinaryValue(data); + } + + /** + * Create a BinaryValue containing a copy of the supplied String + * encoded using the default character asSet. + * + * @param data the data to copy + * @return a new {@code BinaryValue} + */ + public static BinaryValue create(String data) + { + return create(data, Charset.defaultCharset()); + } + + /** + * Create a BinaryValue containing a copy of the supplied String + encoded using UTF-8. + * + * @param data the data to copy + * @return a new {@code BinaryValue} + */ + public static BinaryValue createFromUtf8(String data) + { + return create(data, Charset.forName("UTF-8")); + } + + /** + * Create a BinaryValue containing a copy of the supplied string + encoded using the supplied Charset. + * + * @param data the data to copy + * @param charset the charset to use for encoding + * @return a new {@code BinaryValue} + */ + public static BinaryValue create(String data, Charset charset) + { + byte[] bytes = null; + if (data != null) + { + bytes = data.getBytes(charset); + } + return new BinaryValue(bytes); + } + + /** + * Create a BinaryValue containing the supplied {@code byte[]} + *

+ * The {@code byte[]} is not copied. + *

+ * @param data the {@code byte[]} to wrap + * @return a new {@code BinaryValue} + * @riak.threadsafety The supplied byte[] is not copied and the reference is used + * directly. Retaining a reference to this array and making subsequent + * changes will lead to undefined behavior in regard to thread safety and + * visibility. + */ + public static BinaryValue unsafeCreate(byte[] data) + { + return new BinaryValue(data); + } + + /** + * Return a copy of the wrapped {@code byte[]} + *

+ * {@link Arrays#copyOf(byte[], int) } is used to copy the internal {@code byte[]} + *

+ * @return a copy of the internal {@code byte[]} + */ + public byte[] getValue() + { + if (data != null) + { + return Arrays.copyOf(data, data.length); + } + else + { + return null; + } + } + + /** + * Return the wrapped {@code byte[]} + * @return the internal {@code byte[]} + * @riak.threadsafety This method exposes the internal byte[] directly. + * Modifying the contents of this array will lead to undefined behavior in + * regard to thread safety and visibility. + */ + public byte[] unsafeGetValue() + { + return data; + } + + @Override + public boolean equals(Object other) + { + if (!(other instanceof BinaryValue)) + { + return false; + } + else + { + return Arrays.equals(data, ((BinaryValue)other).data); + } + } + + @Override + public int hashCode() + { + return Arrays.hashCode(data); + } + + /** + * Return the wrapped {@code byte[]} as a String. + *

+ * The default character asSet is used for encoding. + *

+ * @return a {@code String} created using the default {@code Charset} + */ + @Override + public String toString() + { + return toString(Charset.defaultCharset()); + } + + /** + * Return the wrapped {@code byte[]} as a String. + *

+ * UTF-8 is used for encoding. + *

+ * @return a {@code String} created using the UTF-8 {@code Charset} + */ + public String toStringUtf8() + { + return toString(Charset.forName("UTF-8")); + } + + /** + * Return the wrapped {@code byte[]} as a String + *

+ * The supplied {@code Charset} is used to convert the bytes. + * @return a {@code String} created using the supplied {@code Charset} + */ + public String toString(Charset charset) + { + if (data != null) + { + return new String(data, charset); + } + else + { + return null; + } + } + + /** + * Return the length of the wrapped {@code byte[]} + * @return the length. + */ + public int length() + { + return data.length; + } +} diff --git a/src/main/java/com/basho/riak/client/core/util/CharsetUtils.java b/src/main/java/com/basho/riak/client/core/util/CharsetUtils.java new file mode 100644 index 000000000..b1c6a72d8 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/util/CharsetUtils.java @@ -0,0 +1,250 @@ +/* + * 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.core.util; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Utils for dealing with {@code byte}, {@code String} charset + * issues + * + * This code is mainly from the Trifork fork of the original HTTP client and was written by + * Krestan Krab and/or Erik Søe Sørensen. + * + * @author Russell Brown + */ +public class CharsetUtils +{ + public static Charset ASCII = Charset.forName("ASCII"); + public static Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); + public static Charset UTF_8 = Charset.forName("UTF-8"); + + /** + * RegEx pattern to get the charset from a content-type value. + */ + private static final Pattern CHARSET_PATT = Pattern.compile("\\bcharset *= *\"?([^ ;\"]+)\"?", Pattern.CASE_INSENSITIVE); + + /** + * Attempts to parse the {@link Charset} from a contentType string. + * + * If contentType is null or no charset declaration found, then UTF-8 is + * returned. If the found Charset declaration is unknown on this platform + * then a runtime exception is thrown. + * + * @param contentType + * @return a {@link Charset} parsed from a charset declaration in a + * {@code contentType} String. + */ + public static Charset getCharset(String contentType) { + if (contentType == null) { + return UTF_8; + } + + if (Constants.CTYPE_JSON_UTF8.equals(contentType)) { + return UTF_8; // Fast-track + } + + Matcher matcher = CHARSET_PATT.matcher(contentType); + if (matcher.find()) { + String encstr = matcher.group(1); + + if (encstr.equalsIgnoreCase("UTF-8")) { + return UTF_8; // Fast-track + } else { + try { + return Charset.forName(encstr.toUpperCase()); + } catch (Exception e) { + // ignore // + } + } + } + + return ISO_8859_1; + } + + /** + * Get the actual string value declared as the charset in a content-type + * string, regardless of its validity. + *

+ * NOTE: this is different from getCharset, which will always return a + * default value. + *

+ * + * @param contentType + * the content-type string + * @return the verbatim charset declared or null if non-exists + */ + public static String getDeclaredCharset(String contentType) { + if (contentType == null) { + return null; + } + + Matcher matcher = CHARSET_PATT.matcher(contentType); + if (matcher.find()) { + String encstr = matcher.group(1); + return encstr; + } else { + return null; + } + } + + /** + * Adds the utf-8 charset to a content type. + * + * @param contentType + * @return the {@code contentType} with {@literal ;charset=utf-8} appended. + */ + public static String addUtf8Charset(String contentType) { + if (contentType == null) { + return "text/plain;charset=utf-8"; + } + + Matcher matcher = CHARSET_PATT.matcher(contentType); + if (matcher.find()) { + // replace what ever content-type with utf8 + return contentType.substring(0, matcher.start(1)) + "utf-8" + contentType.substring(matcher.end(1)); + } + + return contentType + ";charset=utf-8"; + } + + /** + * Adds the charset to a content type. + * + * @param contentType + * @return the {@code contentType} with {@code charset.name()} appended. + */ + public static String addCharset(String charset, String contentType) + { + if (null == contentType) + { + return "text/plain;charset=" + charset; + } + else + { + Matcher matcher = CHARSET_PATT.matcher(contentType); + if (matcher.find()) + { + // replace what ever content-type with the charset + return contentType.substring(0, matcher.start(1)) + + charset + contentType.substring(matcher.end(1)); + } + else + { + return contentType + ";charset=" + charset; + } + } + + } + + public static String addCharset(Charset charset, String contentType) + { + return addCharset(charset.name(), contentType); + } + + /** + * Turns a byte[] array into a string in the provided {@link Charset} + * + * @param bytes + * @param charset + * @return a String + */ + public static String asString(byte[] bytes, Charset charset) { + if(bytes == null) { + return null; + } + + if(charset == null) { + throw new IllegalArgumentException("Cannot get bytes without a Charset"); + } + + try { + return new String(bytes, charset.name()); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException(charset.name() + " must be present", e); + } + } + + /** + * Turns a byte[] array into a UTF8 string + * + * @param bytes + * @return a String + */ + public static String asUTF8String(byte[] bytes) { + return asString(bytes, UTF_8); + } + + public static String asASCIIString(byte[] bytes) + { + return asString(bytes, ASCII); + } + + /** + * Turn a string into an array of bytes using the passed {@link Charset} + * + * @param string + * @param charset + * @return a byte[] array + */ + public static byte[] asBytes(String string, Charset charset) { + if(string == null) { + return null; + } + + if(charset == null) { + throw new IllegalArgumentException("Cannot get bytes without a Charset"); + } + + try { + return string.getBytes(charset.name()); + } catch (UnsupportedEncodingException e) { + //since we are using *actual* charsets, not string lookups, this + //should *never* happen. But it is better to throw it up than swallow it. + throw new IllegalStateException("Charset present", e); + } + } + + /** + * Turn a UTF-8 encoded string into an array of bytes + * + * @param string + * @return the bytes for the supplied String + */ + public static byte[] utf8StringToBytes(String string) { + return asBytes(string, UTF_8); + } + + /** + * Check if a content-type string has a charset field appended. + * + * @param ctype + * the content-type string + * @return true if {@code ctype} has a charset, false otherwise + */ + public static boolean hasCharset(String ctype) { + if(ctype == null) { + return false; + } + Matcher matcher = CHARSET_PATT.matcher(ctype); + if (matcher.find()) { + return true; + } else { + return false; + } + } +} diff --git a/src/main/java/com/basho/riak/client/core/util/Constants.java b/src/main/java/com/basho/riak/client/core/util/Constants.java new file mode 100644 index 000000000..117fad736 --- /dev/null +++ b/src/main/java/com/basho/riak/client/core/util/Constants.java @@ -0,0 +1,101 @@ +/* + * 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.core.util; + +import com.basho.riak.client.core.netty.RiakResponseHandler; + +/** + * + * @author Brian Roach + * @since 1.0 + */ +public interface Constants { + + // JSON fields used by Riak + public static String FL_NAME = "name"; + public static String FL_KEYS = "keys"; + public static String FL_SCHEMA = "props"; + public static String FL_SCHEMA_ALLOW_MULT = "allow_mult"; + public static String FL_SCHEMA_CHASHFUN = "chash_keyfun"; + public static String FL_SCHEMA_CHASHFUN_MOD = "mod"; + public static String FL_SCHEMA_CHASHFUN_FUN = "fun"; + public static String FL_SCHEMA_LINKFUN = "linkfun"; + public static String FL_SCHEMA_LINKFUN_MOD = "mod"; + public static String FL_SCHEMA_LINKFUN_FUN = "fun"; + public static String FL_SCHEMA_NVAL = "n_val"; + public static String FL_SCHEMA_FUN_MOD = "mod"; + public static String FL_SCHEMA_FUN_FUN = "fun"; + public static String FL_SCHEMA_FUN_NAME = "name"; + public static String FL_SCHEMA_LAST_WRITE_WINS = "last_write_wins"; + public static String FL_SCHEMA_BACKEND = "backend"; + public static String FL_SCHEMA_SMALL_VCLOCK = "small_vclock"; + public static String FL_SCHEMA_BIG_VCLOCK = "big_vclock"; + public static String FL_SCHEMA_YOUNG_VCLOCK = "young_vclock"; + public static String FL_SCHEMA_OLD_VCLOCK = "old_vclock"; + public static String FL_SCHEMA_R = "r"; + public static String FL_SCHEMA_W = "w"; + public static String FL_SCHEMA_DW = "dw"; + public static String FL_SCHEMA_RW = "rw"; + public static String FL_SCHEMA_PR = "pr"; + public static String FL_SCHEMA_PW = "pw"; + public static String FL_SCHEMA_BASIC_QUORUM = "basic_quorum"; + public static String FL_SCHEMA_NOT_FOUND_OK = "notfound_ok"; + public static String FL_SCHEMA_POSTCOMMIT = "postcommit"; + public static String FL_SCHEMA_PRECOMMIT = "precommit"; + public static String FL_SCHEMA_SEARCH = "search"; + public static String FL_BUCKETS = "buckets"; + + // Header directives used by Riak + public static String LINK_TAG = "riaktag"; + + // Content types used in Riak + public static String CTYPE_ANY = "*/*"; + public static String CTYPE_JSON = "application/json"; + public static String CTYPE_JSON_UTF8 = "application/json; charset=UTF-8"; + public static String CTYPE_OCTET_STREAM = "application/octet-stream"; + public static String CTYPE_MULTIPART_MIXED = "multipart/mixed"; + public static String CTYPE_TEXT = "text/plain"; + public static String CTYPE_TEXT_UTF8 = "text/plain; charset=UTF-8"; + + // Values for the "keys" query parameter + public static String NO_KEYS = "false"; + public static String INCLUDE_KEYS = "true"; + public static String STREAM_KEYS = "stream"; + + // Query parameters used in Riak + public static String QP_RETURN_BODY = "returnbody"; + public static String QP_R = "r"; + public static String QP_W = "w"; + public static String QP_DW = "dw"; + public static String QP_RW = "rw"; + public static String QP_KEYS = "keys"; + public static String QP_BUCKETS = "buckets"; + public static String QP_PR = "pr"; + public static String QP_PW = "pw"; + public static String QP_NOT_FOUND_OK = "notfound_ok"; + public static String QP_BASIC_QUORUM = "basic_quorum"; + + // List bucket operation parameters + public static String LIST_BUCKETS = "true"; + + // Netty Channel handler constants + public static final String MESSAGE_CODEC = "codec"; + public static final String OPERATION_ENCODER = "operationEncoder"; + public static final String RESPONSE_HANDLER = "responseHandler"; + public static final String SSL_HANDLER = "sslHandler"; + public static final String HEALTHCHECK_CODEC = "healthCheckCodec"; + +} diff --git a/src/main/java/com/basho/riak/client/javadoc/JavadocFilter.java b/src/main/java/com/basho/riak/client/javadoc/JavadocFilter.java new file mode 100644 index 000000000..ab7f818a7 --- /dev/null +++ b/src/main/java/com/basho/riak/client/javadoc/JavadocFilter.java @@ -0,0 +1,119 @@ +package com.basho.riak.client.javadoc; + +import java.lang.reflect.Array; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; + +import com.sun.javadoc.Doc; +import com.sun.javadoc.DocErrorReporter; +import com.sun.javadoc.LanguageVersion; +import com.sun.javadoc.ProgramElementDoc; +import com.sun.javadoc.RootDoc; +import com.sun.tools.doclets.standard.Standard; +import com.sun.tools.javadoc.Main; + +/** + * Used to decide what gets included/excluded in the Javadoc + *

+ *

  • It filters out all inner classes with "UnitTest" in their name.
  • + *
  • It filters out classes with @ExcludeFromJavadoc in the javadoc comment
  • + */ +public class JavadocFilter { + public static void main(String[] args) { + String name = JavadocFilter.class.getName(); + Main.execute(name, name, args); + } + + public static boolean validOptions(String[][] options, DocErrorReporter reporter) throws java.io.IOException { + return Standard.validOptions(options, reporter); + } + + public static LanguageVersion languageVersion() { + return LanguageVersion.JAVA_1_5; + } + + public static int optionLength(String option) { + return Standard.optionLength(option); + } + + public static boolean start(RootDoc root) throws java.io.IOException { + return Standard.start((RootDoc) process(root, RootDoc.class)); + } + + private static boolean exclude(Doc doc) { + if (doc.name().contains("UnitTest")) { + return true; + } else if (doc.tags("@ExcludeFromJavadoc").length > 0) { + return true; + } else if (doc instanceof ProgramElementDoc) { + if (((ProgramElementDoc) doc).containingPackage().tags("@ExcludeFromJavadoc").length > 0) + return true; + } + // nothing above found a reason to exclude + return false; + } + + private static Object process(Object obj, Class expect) { + if (obj == null) + return null; + Class cls = obj.getClass(); + if (cls.getName().startsWith("com.sun.")) { + return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), new ExcludeHandler(obj)); + } else if (obj instanceof Object[]) { + Class componentType = expect.getComponentType(); + if (componentType == null) + { + componentType = Object.class; + } + Object[] array = (Object[]) obj; + List list = new ArrayList(array.length); + for (Object entry : array) + { + if ((entry instanceof Doc) && exclude((Doc) entry)) + continue; + list.add(process(entry, componentType)); + } + if (componentType == null) + { + System.out.println("Object: " + obj + "Expect: " + expect + " NULL; " + list); + + } + return list.toArray((Object[]) Array.newInstance(componentType, list.size())); + } else { + return obj; + } + } + + private static class ExcludeHandler implements InvocationHandler { + private final Object target; + + public ExcludeHandler(Object target) { + this.target = target; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (args != null) { + String methodName = method.getName(); + if (methodName.equals("compareTo") || methodName.equals("equals") || methodName.equals("overrides") || methodName.equals("subclassOf")) { + args[0] = unwrap(args[0]); + } + } + try { + return process(method.invoke(target, args), method.getReturnType()); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } + + private Object unwrap(Object proxy) { + if (proxy instanceof Proxy) + return ((ExcludeHandler) Proxy.getInvocationHandler(proxy)).target; + return proxy; + } + } +} diff --git a/src/main/java/com/basho/riak/client/javadoc/RiakThreadSafetyTaglet.java b/src/main/java/com/basho/riak/client/javadoc/RiakThreadSafetyTaglet.java new file mode 100644 index 000000000..fc4a3ab99 --- /dev/null +++ b/src/main/java/com/basho/riak/client/javadoc/RiakThreadSafetyTaglet.java @@ -0,0 +1,115 @@ +/* + * 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.javadoc; +import com.sun.javadoc.*; +import com.sun.tools.doclets.Taglet; +import java.util.Map; +/** + * Taglet for our docs to describe the thread safety of a class or method + * + * + * @author Brian Roach + * @since 2.0 + */ +public class RiakThreadSafetyTaglet implements Taglet +{ + private final static String NAME = "riak.threadsafety"; + private final static String HEADER = "Thread Safety:"; + + @Override + public boolean inField() + { + return true; + } + + @Override + public boolean inConstructor() + { + return true; + } + + @Override + public boolean inMethod() + { + return true; + } + + @Override + public boolean inOverview() + { + return true; + } + + @Override + public boolean inPackage() + { + return true; + } + + @Override + public boolean inType() + { + return true; + } + + @Override + public boolean isInlineTag() + { + return false; + } + + @Override + public String getName() + { + return NAME; + } + + @Override + public String toString(Tag tag) + { + return "
    " + HEADER + "
    " + + "" + + "
    " + + tag.text() + + "
    \n"; + } + + @Override + public String toString(Tag[] tags) + { + if (tags.length == 0) { + return null; + } + String result = "\n
    " + HEADER + "
    "; + result += "
    "; + for (int i = 0; i < tags.length; i++) { + if (i > 0) { + result += ", "; + } + result += tags[i].text(); + } + return result + "
    \n"; + } + + public static void register(Map tagletMap) { + RiakThreadSafetyTaglet tag = new RiakThreadSafetyTaglet(); + Taglet t = tagletMap.get(tag.getName()); + if (t != null) { + tagletMap.remove(tag.getName()); + } + tagletMap.put(tag.getName(), tag); + } +} diff --git a/src/main/java/com/basho/riak/client/mapreduce/ErlangFunction.java b/src/main/java/com/basho/riak/client/mapreduce/ErlangFunction.java deleted file mode 100644 index 3b3ba3889..000000000 --- a/src/main/java/com/basho/riak/client/mapreduce/ErlangFunction.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.mapreduce; - -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Represents an Erlang function used in a map or reduce phase - * of a map/reduce job - * - */ -public class ErlangFunction implements MapReduceFunction { - - private String module; - private String function; - - /** - * Constructs a new ErlangFunction instance - * @param module Erlang module name - * @param functionName Erlang function name - */ - public ErlangFunction(String module, String functionName) { - this.module = module; - this.function = functionName; - } - - /** - * Converts the function definition to JSON - */ - public JSONObject toJson() { - try { - JSONObject retval = new JSONObject(); - retval.put("language", "erlang"); - retval.put("module", this.module); - retval.put("function", this.function); - return retval; - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to a string"); - } - } - -} diff --git a/src/main/java/com/basho/riak/client/mapreduce/JavascriptFunction.java b/src/main/java/com/basho/riak/client/mapreduce/JavascriptFunction.java deleted file mode 100644 index 62740c477..000000000 --- a/src/main/java/com/basho/riak/client/mapreduce/JavascriptFunction.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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.mapreduce; - -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Represents a Javascript function used in a map or reduce phase - * of a map/reduce job - * - */ -public class JavascriptFunction implements MapReduceFunction { - - private String source; - private MapReduceFunction.Types type; - - /** - * Shortcut for creating a reference to a named - * Javascript function - * @param functionName Name of Javascript function ("Riak.mapValuesJson") - */ - public static JavascriptFunction named(String functionName) { - return new JavascriptFunction(MapReduceFunction.Types.NAMED, functionName); - } - - /** - * Shortcut for creating a reference to an anonymous - * Javascript function - * @param functionSource Javascript function source ("function(v) { return [v]; }") - */ - public static JavascriptFunction anon(String functionSource) { - return new JavascriptFunction(MapReduceFunction.Types.ANONYMOUS, functionSource); - } - - /** - * Converts the function definition to JSON - */ - public JSONObject toJson() { - try { - JSONObject retval = new JSONObject(); - retval.put("language", "javascript"); - if (type == MapReduceFunction.Types.NAMED) { - retval.put("name", this.source); - } - else { - retval.put("source", this.source); - } - - return retval; - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to a string"); - } - } - - private JavascriptFunction(MapReduceFunction.Types functionType, - String functionSource) { - this.type = functionType; - this.source = functionSource; - } - -} diff --git a/src/main/java/com/basho/riak/client/mapreduce/LinkFunction.java b/src/main/java/com/basho/riak/client/mapreduce/LinkFunction.java deleted file mode 100644 index 1db54a7be..000000000 --- a/src/main/java/com/basho/riak/client/mapreduce/LinkFunction.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.mapreduce; - -import org.json.JSONException; -import org.json.JSONObject; - -public class LinkFunction implements MapReduceFunction { - - private String bucket = null; - private String tag = null; - - public LinkFunction(String bucket) { - this.bucket = bucket; - } - - public LinkFunction(String bucket, String tag) { - this.bucket = bucket; - this.tag = tag; - } - - public JSONObject toJson() { - try { - JSONObject link = new JSONObject(); - link.put("bucket", this.bucket); - - if (this.tag != null) { - link.put("tag", this.tag); - } - - return link; - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to a string"); - } - } - -} diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/BetweenFilter.java b/src/main/java/com/basho/riak/client/mapreduce/filter/BetweenFilter.java deleted file mode 100644 index 4530241a8..000000000 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/BetweenFilter.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.mapreduce.filter; - -import org.json.JSONException; -import org.json.JSONArray; - -public class BetweenFilter implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.FILTER; - JSONArray args = new JSONArray(); - - public BetweenFilter(String from, String to) { - args.put("between"); - args.put(from); - args.put(to); - } - - public BetweenFilter(int from, int to) { - args.put("between"); - args.put(from); - args.put(to); - } - - public BetweenFilter(long from, long to) { - args.put("between"); - args.put(from); - args.put(to); - } - - public BetweenFilter(double from, double to) throws JSONException { - args.put("between"); - args.put(from); - args.put(to); - } - - public JSONArray toJson() { - return args; - } -} diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/EqualToFilter.java b/src/main/java/com/basho/riak/client/mapreduce/filter/EqualToFilter.java deleted file mode 100644 index 9faef7059..000000000 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/EqualToFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.mapreduce.filter; - -import org.json.JSONException; -import org.json.JSONArray; - -public class EqualToFilter implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.FILTER; - private JSONArray args = new JSONArray(); - - public EqualToFilter(String equalTo) { - args.put("eq"); - args.put(equalTo); - } - - public EqualToFilter(int equalTo) { - args.put("eq"); - args.put(equalTo); - } - - public EqualToFilter(double equalTo) throws JSONException { - args.put("eq"); - args.put(equalTo); - } - - public JSONArray toJson() { - return args; - } -} diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/FloatToStringFilter.java b/src/main/java/com/basho/riak/client/mapreduce/filter/FloatToStringFilter.java deleted file mode 100644 index b3957b10e..000000000 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/FloatToStringFilter.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.mapreduce.filter; - -import org.json.JSONArray; - -public class FloatToStringFilter implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.TRANSFORM; - - public JSONArray toJson() { - JSONArray filter = new JSONArray(); - filter.put("float_to_string"); - return filter; - } -} diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/GreaterThanFilter.java b/src/main/java/com/basho/riak/client/mapreduce/filter/GreaterThanFilter.java deleted file mode 100644 index 657bc374d..000000000 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/GreaterThanFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.mapreduce.filter; - -import org.json.JSONException; -import org.json.JSONArray; - -public class GreaterThanFilter implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.FILTER; - private JSONArray args = new JSONArray(); - - public GreaterThanFilter(String greaterThan) { - args.put("greater_than"); - args.put(greaterThan); - } - - public GreaterThanFilter(int greaterThan) { - args.put("greater_than"); - args.put(greaterThan); - } - - public GreaterThanFilter(double greaterThan) throws JSONException { - args.put("greater_than"); - args.put(greaterThan); - } - - public JSONArray toJson() { - return args; - } -} diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/GreaterThanOrEqualFilter.java b/src/main/java/com/basho/riak/client/mapreduce/filter/GreaterThanOrEqualFilter.java deleted file mode 100644 index 5b7dad220..000000000 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/GreaterThanOrEqualFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.mapreduce.filter; - -import org.json.JSONException; -import org.json.JSONArray; - -public class GreaterThanOrEqualFilter implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.FILTER; - private JSONArray args = new JSONArray(); - - public GreaterThanOrEqualFilter(String greaterThanOrEqualTo) { - args.put("greater_than_eq"); - args.put(greaterThanOrEqualTo); - } - - public GreaterThanOrEqualFilter(int greaterThanOrEqualTo) { - args.put("greater_than_eq"); - args.put(greaterThanOrEqualTo); - } - - public GreaterThanOrEqualFilter(double greaterThanOrEqualTo) throws JSONException { - args.put("greater_than_eq"); - args.put(greaterThanOrEqualTo); - } - - public JSONArray toJson() { - return args; - } -} diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/LessThanFilter.java b/src/main/java/com/basho/riak/client/mapreduce/filter/LessThanFilter.java deleted file mode 100644 index a78c3c35e..000000000 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/LessThanFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.mapreduce.filter; - -import org.json.JSONException; -import org.json.JSONArray; - -public class LessThanFilter implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.FILTER; - private JSONArray args = new JSONArray(); - - public LessThanFilter(String lessThan) { - args.put("less_than"); - args.put(lessThan); - } - - public LessThanFilter(int lessThan) { - args.put("less_than"); - args.put(lessThan); - } - - public LessThanFilter(double lessThan) throws JSONException { - args.put("less_than"); - args.put(lessThan); - } - - public JSONArray toJson() { - return args; - } -} diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/LessThanOrEqualFilter.java b/src/main/java/com/basho/riak/client/mapreduce/filter/LessThanOrEqualFilter.java deleted file mode 100644 index 308524cb9..000000000 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/LessThanOrEqualFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.mapreduce.filter; - -import org.json.JSONException; -import org.json.JSONArray; - -public class LessThanOrEqualFilter implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.FILTER; - private JSONArray args = new JSONArray(); - - public LessThanOrEqualFilter(String lessThanOrEqualTo) { - args.put("less_than_eq"); - args.put(lessThanOrEqualTo); - } - - public LessThanOrEqualFilter(int lessThanOrEqualTo) { - args.put("less_than_eq"); - args.put(lessThanOrEqualTo); - } - - public LessThanOrEqualFilter(double lessThanOrEqualTo) throws JSONException { - args.put("less_than_eq"); - args.put(lessThanOrEqualTo); - } - - public JSONArray toJson() { - return args; - } -} diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/LogicalAndFilter.java b/src/main/java/com/basho/riak/client/mapreduce/filter/LogicalAndFilter.java deleted file mode 100644 index 13f5a30b4..000000000 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/LogicalAndFilter.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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.mapreduce.filter; - -import org.json.JSONArray; - -public class LogicalAndFilter implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.LOGICAL; - private JSONArray args = new JSONArray(); - - public LogicalAndFilter(MapReduceFilter... filters) { - args.put("and"); - for(MapReduceFilter filter: filters) { - args.put(filter.toJson()); - } - } - - public LogicalAndFilter add(MapReduceFilter filter) { - args.put(filter.toJson()); - return this; - } - - public JSONArray toJson() { - return args; - } -} diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/LogicalFilterGroup.java b/src/main/java/com/basho/riak/client/mapreduce/filter/LogicalFilterGroup.java deleted file mode 100644 index ed52fa190..000000000 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/LogicalFilterGroup.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.mapreduce.filter; - -import org.json.JSONArray; - -public class LogicalFilterGroup implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.LOGICAL; - private JSONArray filterArray = new JSONArray(); - - public LogicalFilterGroup(MapReduceFilter... filters) { - for(MapReduceFilter filter: filters) { - filterArray.put(filter.toJson()); - } - } - - public LogicalFilterGroup add(MapReduceFilter filter) { - filterArray.put(filter.toJson()); - return this; - } - - public JSONArray toJson() { - return filterArray; - } -} diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/LogicalNotFilter.java b/src/main/java/com/basho/riak/client/mapreduce/filter/LogicalNotFilter.java deleted file mode 100644 index 1a0738af7..000000000 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/LogicalNotFilter.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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.mapreduce.filter; - -import org.json.JSONArray; - -public class LogicalNotFilter implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.LOGICAL; - private JSONArray args = new JSONArray(); - - public LogicalNotFilter(MapReduceFilter... filters) { - args.put("not"); - for(MapReduceFilter filter: filters) { - args.put(filter.toJson()); - } - } - - public LogicalNotFilter add(MapReduceFilter filter) { - args.put(filter.toJson()); - return this; - } - - public JSONArray toJson() { - return args; - } -} diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/LogicalOrFilter.java b/src/main/java/com/basho/riak/client/mapreduce/filter/LogicalOrFilter.java deleted file mode 100644 index 2e3b50e05..000000000 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/LogicalOrFilter.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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.mapreduce.filter; - -import org.json.JSONArray; - -public class LogicalOrFilter implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.LOGICAL; - private JSONArray args = new JSONArray(); - - public LogicalOrFilter(MapReduceFilter... filters) { - args.put("or"); - for(MapReduceFilter filter: filters) { - args.put(filter.toJson()); - } - } - - public LogicalOrFilter add(MapReduceFilter filter) { - args.put(filter.toJson()); - return this; - } - - public JSONArray toJson() { - return args; - } -} diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/MatchFilter.java b/src/main/java/com/basho/riak/client/mapreduce/filter/MatchFilter.java deleted file mode 100644 index fbeb4aacd..000000000 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/MatchFilter.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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.mapreduce.filter; - -import org.json.JSONArray; - -public class MatchFilter implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.FILTER; - private JSONArray args = new JSONArray(); - - public MatchFilter(String matchFilter) { - args.put("matches"); - args.put(matchFilter); - } - - public JSONArray toJson() { - return args; - } -} diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/NotEqualToFilter.java b/src/main/java/com/basho/riak/client/mapreduce/filter/NotEqualToFilter.java deleted file mode 100644 index d8be998ac..000000000 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/NotEqualToFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.mapreduce.filter; - -import org.json.JSONException; -import org.json.JSONArray; - -public class NotEqualToFilter implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.FILTER; - private JSONArray args = new JSONArray(); - - public NotEqualToFilter(String notEqualToFilter) { - args.put("neq"); - args.put(notEqualToFilter); - } - - public NotEqualToFilter(int notEqualToFilter) { - args.put("neq"); - args.put(notEqualToFilter); - } - - public NotEqualToFilter(double notEqualToFilter) throws JSONException { - args.put("neq"); - args.put(notEqualToFilter); - } - - public JSONArray toJson() { - return args; - } -} diff --git a/src/main/java/com/basho/riak/client/mapreduce/filter/SetMemberFilter.java b/src/main/java/com/basho/riak/client/mapreduce/filter/SetMemberFilter.java deleted file mode 100644 index 376888736..000000000 --- a/src/main/java/com/basho/riak/client/mapreduce/filter/SetMemberFilter.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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.mapreduce.filter; - -import java.util.List; - -import org.json.JSONException; -import org.json.JSONArray; - -public class SetMemberFilter implements MapReduceFilter { - private MapReduceFilter.Types type = MapReduceFilter.Types.FILTER; - private JSONArray args = new JSONArray(); - - public SetMemberFilter(List setMembers) { - args.put("set_member"); - for(String setMember: setMembers) { - args.put(setMember); - } - } - - public SetMemberFilter(JSONArray setMembers) throws JSONException { - args.put("set_member"); - for(int i=0; i fetchAll(String bucket, String key, RequestMeta meta) - throws RiakIOException, RiakResponseException { - FetchResponse r = impl.fetch(bucket, key, meta); - - if (r.getStatusCode() == 404) - return null; - - if (r.getStatusCode() != 200 && r.getStatusCode() != 304) - throw new RiakResponseException(new RiakResponseRuntimeException(r, r.getBodyAsString())); - - if (r.getStatusCode() == 200 && !(r.hasObject() || r.hasSiblings())) - throw new RiakResponseException(new RiakResponseRuntimeException(r, "Failed to parse object")); - - if (r.hasSiblings()) - return r.getSiblings(); - return Arrays.asList(r.getObject()); - } - - public Collection fetchAll(String bucket, String key) throws RiakIOException, - RiakResponseException { - return fetchAll(bucket, key, null); - } - - /** - * Identical to - * {@link RiakClient#stream(String, String, StreamHandler, RequestMeta)}. - */ - public boolean stream(String bucket, String key, StreamHandler handler, RequestMeta meta) throws IOException { - return impl.stream(bucket, key, handler, meta); - } - - /** - * Like {@link RiakClient#delete(String, String, RequestMeta)}, except - * throws on a non-200 or 404 response. Note that delete succeeds if the - * object did not previously exist (404 response). - * - * @throws RiakIOException - * If an error occurs during communication with the Riak server. - * @throws RiakResponseException - * If the object was not deleted. - */ - public void delete(String bucket, String key, RequestMeta meta) throws RiakIOException, RiakResponseException { - HttpResponse r = impl.delete(bucket, key, meta); - - if (r.getStatusCode() != 204 && r.getStatusCode() != 404) - throw new RiakResponseException(new RiakResponseRuntimeException(r, r.getBodyAsString())); - } - - public void delete(String bucket, String key) throws RiakIOException, RiakResponseException { - delete(bucket, key, null); - } - - /** - * Like {@link RiakClient#walk(String, String, String, RequestMeta)}, except - * throws on a non-200 or 404 response. - * - * @return list of lists of {@link RiakObject}s corresponding to steps of - * the walk. Returns null if the source object doesn't exist. - * @throws RiakIOException - * If an error occurs during communication with the Riak server. - * @throws RiakResponseException - * If the links could not be walked or the result steps were not - * returned. - */ - public List> walk(String bucket, String key, String walkSpec, RequestMeta meta) - throws RiakIOException, RiakResponseException { - WalkResponse r = impl.walk(bucket, key, walkSpec, meta); - - if (r.getStatusCode() == 404) - return null; - - if (r.getStatusCode() != 200) - throw new RiakResponseException(new RiakResponseRuntimeException(r, r.getBodyAsString())); - - if (!r.hasSteps()) - throw new RiakResponseException(new RiakResponseRuntimeException(r, "Failed to parse walk results")); - - return r.getSteps(); - } - - public List> walk(String bucket, String key, String walkSpec) - throws RiakIOException, RiakResponseException { - return walk(bucket, key, walkSpec, null); - } - - public List> walk(String bucket, String key, RiakWalkSpec walkSpec) - throws RiakIOException, RiakResponseException { - return walk(bucket, key, walkSpec.toString(), null); - } -} \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/plain/RiakResponseException.java b/src/main/java/com/basho/riak/client/plain/RiakResponseException.java deleted file mode 100644 index 53db6e2ce..000000000 --- a/src/main/java/com/basho/riak/client/plain/RiakResponseException.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.plain; - -import java.io.InputStream; -import java.util.Map; - -import org.apache.commons.httpclient.HttpMethod; - -import com.basho.riak.client.response.HttpResponse; -import com.basho.riak.client.response.RiakResponseRuntimeException; - -/** - * A checked decorator for {@link RiakResponseRuntimeException} - */ -public class RiakResponseException extends Exception implements HttpResponse { - - private static final long serialVersionUID = 5932513075276473483L; - private RiakResponseRuntimeException impl; - - public RiakResponseException(RiakResponseRuntimeException e) { - super(e.getMessage(), e.getCause()); - impl = e; - } - - public byte[] getBody() { - return impl.getBody(); - } - - public String getBodyAsString() { - return impl.getBodyAsString(); - } - - public InputStream getStream() { - return impl.getStream(); - } - - public boolean isStreamed() { - return impl.isStreamed(); - } - - public String getBucket() { - return impl.getBucket(); - } - - public Map getHttpHeaders() { - return impl.getHttpHeaders(); - } - - public HttpMethod getHttpMethod() { - return impl.getHttpMethod(); - } - - public String getKey() { - return impl.getKey(); - } - - public int getStatusCode() { - return impl.getStatusCode(); - } - - public boolean isError() { - return impl.isError(); - } - - public boolean isSuccess() { - return impl.isSuccess(); - } - - public void close() { - impl.close(); - } -} diff --git a/src/main/java/com/basho/riak/client/request/MapReduceBuilder.java b/src/main/java/com/basho/riak/client/request/MapReduceBuilder.java deleted file mode 100644 index 9fd9f193a..000000000 --- a/src/main/java/com/basho/riak/client/request/MapReduceBuilder.java +++ /dev/null @@ -1,475 +0,0 @@ -/* - * 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.request; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import com.basho.riak.client.RiakClient; -import com.basho.riak.client.RiakObject; -import com.basho.riak.client.mapreduce.LinkFunction; -import com.basho.riak.client.mapreduce.MapReduceFunction; -import com.basho.riak.client.mapreduce.filter.MapReduceFilter; -import com.basho.riak.client.response.MapReduceResponse; -import com.basho.riak.client.response.RiakIORuntimeException; -import com.basho.riak.client.response.RiakResponseRuntimeException; - -/** - * Builds a map/reduce job description and submits it Uses the same chained - * method metaphor as StringBuilder or StringBuffer - */ -public class MapReduceBuilder { - - private static enum Types { - MAP, REDUCE, LINK - } - - private String bucket = null; - private Map> objects = new LinkedHashMap>(); - private List keyFilters = new ArrayList(); - private List phases = new LinkedList(); - private int timeout = -1; - private RiakClient riak = null; - - /** - * @param riak - * RiakClient instance which is pointing to the map/reduce URL - */ - public MapReduceBuilder(RiakClient riak) { - this.riak = riak; - } - - public MapReduceBuilder() { /* nop */ } - - /** - * The {@link RiakClient} to which this map reduce job will be submitted to - * when {@link MapReduceBuilder#submit()} is called. - */ - public RiakClient getRiakClient() { - return riak; - } - - public MapReduceBuilder setRiakClient(RiakClient client) { - riak = client; - return this; - } - - /** - * Gets the name of the Riak bucket the map/reduce job will process - */ - public String getBucket() { - return bucket; - } - - /** - * Sets the name of the Riak bucket the map/reduce job will process - * - * @throws IllegalStateException - * - If objects have already been added to the job - */ - public MapReduceBuilder setBucket(String newBucket) { - if (objects.size() > 0) - throw new IllegalStateException("Cannot map/reduce over buckets and objects"); - bucket = newBucket; - return this; - } - - /** - * Adds a Riak object (bucket name/key pair) to the map/reduce job as inputs - * - * @throws IllegalStateException - * - If a bucket name has already been set on the job - */ - public void addRiakObject(String bucket, String key) { - if (this.bucket != null) - throw new IllegalStateException("Cannot map/reduce over buckets and objects"); - Set keys = objects.get(bucket); - if (keys == null) { - keys = new LinkedHashSet(); - objects.put(bucket, keys); - } - keys.add(key); - } - - /** - * Removes a Riak object (bucket name/key pair) for the job's input list - */ - public void removeRiakObject(String bucket, String key) { - Set keys = objects.get(bucket); - if (keys != null) { - keys.remove(key); - if (keys.size() == 0) { - objects.remove(bucket); - } - } - } - - /** - * Returns a copy of the Riak objects on the input list for a map/reduce job - */ - public Map> getRiakObjects() { - return new HashMap>(objects); - } - - /** - * Sets a collection of Riak object (bucket name/key pair) as the map/reduce - * job as inputs - * - * @throws IllegalStateException - * - If a bucket name has already been set on the job - */ - public MapReduceBuilder setRiakObjects(Map> objects) { - if (bucket != null) - throw new IllegalStateException("Cannot map/reduce over buckets and objects"); - - if (objects == null) { - clearRiakObjects(); - } else { - this.objects = new HashMap>(objects); - } - - return this; - } - - public MapReduceBuilder setRiakObjects(Collection objects) { - if (bucket != null) - throw new IllegalStateException("Cannot map/reduce over buckets and objects"); - - clearRiakObjects(); - if (objects != null) { - for (RiakObject o : objects) { - addRiakObject(o.getBucket(), o.getKey()); - } - } - - return this; - } - - /** - * Remove all Riak objects from the input list - */ - public void clearRiakObjects() { - objects.clear(); - } - - /** - * How long the map/reduce job is allowed to execute Time is in milliseconds - */ - public void setTimeout(int timeout) { - this.timeout = timeout; - } - - /** - * Gets the currently assigned timeout - */ - public int getTimeout() { - return timeout; - } - - /** - * Adds a map phase to the job - * - * @param function - * function to run for the phase - * - * @param keep - * should the server keep and return the results - * @return current MapReduceBuilder instance. This is done so multiple calls - * to map, reduce, and link can be chained together a la - * StringBuffer - */ - public MapReduceBuilder keyFilter(MapReduceFilter... filters) { - for(MapReduceFilter filter: filters) { - this.keyFilters.add(filter); - } - return this; - } - - /** - * Adds a map phase to the job - * - * @param function - * function to run for the phase - * - * @param keep - * should the server keep and return the results - * @return current MapReduceBuilder instance. This is done so multiple calls - * to map, reduce, and link can be chained together a la - * StringBuffer - */ - public MapReduceBuilder map(MapReduceFunction function, boolean keep) { - return this.map(function, null, keep); - } - - - /** - * Adds a map phase to the job - * - * @param function - * function to run for the phase - * - * @param arg - * Static argument to pass to the function. Should be an - * object easily converted to JSON - * - * @param keep - * should the server keep and return the results - * @return current MapReduceBuilder instance. This is done so multiple calls - * to map, reduce, and link can be chained together a la - * StringBuffer - */ - public MapReduceBuilder map(MapReduceFunction function, Object arg, boolean keep) { - this.addPhase(MapReduceBuilder.Types.MAP, function, arg, keep); - return this; - } - - /** - * Adds a reduce phase to the job - * - * @param function - * function to run for the phase - * - * @param keep - * should the server keep and return the results - * @return current MapReduceBuilder instance. This is done so multiple calls - * to map, reduce, and link can be chained together a la - * StringBuffer - */ - public MapReduceBuilder reduce(MapReduceFunction function, boolean keep) { - return this.reduce(function, null, keep); - } - - - /** - * Adds a reduce phase to the job - * - * @param function - * function to run for the phase - * - * @param arg - * Static argument to pass to the function. Should be an - * object easily converted to JSON - * - * @param keep - * should the server keep and return the results - * @return current MapReduceBuilder instance. This is done so multiple calls - * to map, reduce, and link can be chained together a la - * StringBuffer - */ - public MapReduceBuilder reduce(MapReduceFunction function, Object arg, boolean keep) { - this.addPhase(MapReduceBuilder.Types.REDUCE, function, arg, keep); - return this; - } - - /** - * Adds a link phase to the job - * - * @param bucket - * bucket to link walk - * @param keep - * should the server keep and return the results - * @return current MapReduceBuilder instance. This is done so multiple calls - * to map, reduce, and link can be chained together a la - * StringBuffer - * - * Pointing at a bucket without specifying a link tag will follow - * all links pointing to objects in the bucket - */ - public MapReduceBuilder link(String bucket, boolean keep) { - this.addPhase(MapReduceBuilder.Types.LINK, new LinkFunction(bucket), keep); - return this; - } - - /** - * Adds a link phase to the job - * - * @param bucket - * bucket to link walk - * @param tag - * link tag to match - * @param keep - * should the server keep and return the results - * @return current MapReduceBuilder instance. This is done so multiple calls - * to map, reduce, and link can be chained together a la - * StringBuffer - */ - public MapReduceBuilder link(String bucket, String tag, boolean keep) { - this.addPhase(MapReduceBuilder.Types.LINK, new LinkFunction(bucket, tag), keep); - return this; - } - - /** - * Submits the job to the Riak server - * - * @param meta - * Extra metadata to attach to the request such as HTTP headers - * or query parameters. - * - * @return {@link MapReduceResponse} containing job results - * - * @throws IllegalStateException - * If this job has not been associated with a Riak instance by - * calling {@link MapReduceBuilder#setRiakClient(RiakClient)} - * @throws RiakIORuntimeException - * If an error occurs during communication with the Riak server. - * @throws RiakResponseRuntimeException - * If the Riak server returns a malformed response. - */ - public MapReduceResponse submit(RequestMeta meta) { - if (riak == null) - throw new IllegalStateException("Cannot perform map reduce without a RiakClient"); - return riak.mapReduce(toJSON().toString(), meta); - } - - public MapReduceResponse submit() throws JSONException { - return submit(null); - } - - /** - * Builds the JSON representation of a map/reduce job - */ - public JSONObject toJSON() { - JSONObject job = new JSONObject(); - JSONArray query = new JSONArray(); - - for (MapReducePhase phase : phases) { - renderPhase(phase, query); - } - buildInputs(job); - try { - job.put("query", query); - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to a valid JSONArray"); - } - if (timeout > 0) { - try { - job.put("timeout", timeout); - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to an int"); - } - } - return job; - } - - private MapReduceBuilder addPhase(Types phaseType, MapReduceFunction function, boolean keep) { - return addPhase(phaseType, function, null, keep); - } - - private MapReduceBuilder addPhase(Types phaseType, MapReduceFunction function, Object arg, boolean keep) { - MapReducePhase phase = new MapReducePhase(); - phase.type = phaseType; - phase.function = function; - phase.arg = arg; - phase.keep = keep; - phases.add(phase); - return this; - } - - private JSONArray buildFilters(List filterList) { - JSONArray filters = new JSONArray(); - for(MapReduceFilter filter: filterList) { - filters.put(filter.toJson()); - } - return filters; - } - - private void buildInputs(JSONObject job) { - if (bucket != null) { - if (keyFilters.size() > 0) { - try { - JSONObject jobInputs = new JSONObject(); - jobInputs.put("bucket", bucket); - jobInputs.put("key_filters", buildFilters(this.keyFilters)); - job.put("inputs", jobInputs); - } catch (JSONException e) { - throw new RuntimeException("Can always map a collection of MapReduceFilter objects to a JSONArray"); - } - } else { - try { - job.put("inputs", bucket); - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to a string"); - } - } - } else { - JSONArray inputs = new JSONArray(); - for (String bucket : objects.keySet()) { - Set keys = objects.get(bucket); - for (String key : keys) { - String[] pair = { bucket, key }; - inputs.put(pair); - } - } - try { - job.put("inputs", inputs); - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to a valid JSONArray"); - } - } - } - - private void renderPhase(MapReducePhase phase, JSONArray query) { - JSONObject phaseJson = new JSONObject(); - JSONObject functionJson = phase.function.toJson(); - try { - functionJson.put("keep", phase.keep); - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to a boolean"); - } - try { - if (phase.arg != null) { - functionJson.put("arg", phase.arg); - } - } catch (JSONException e) { - throw new RuntimeException("Cannot convert phase arg to JSON"); - } - String type = null; - switch (phase.type) { - case MAP: - type = "map"; - break; - case REDUCE: - type = "reduce"; - break; - case LINK: - type = "link"; - break; - } - try { - phaseJson.put(type, functionJson); - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to a valid JSONObject"); - } - query.put(phaseJson); - } - - private class MapReducePhase { - Types type; - MapReduceFunction function; - Object arg; - boolean keep; - } - -} diff --git a/src/main/java/com/basho/riak/client/request/RequestMeta.java b/src/main/java/com/basho/riak/client/request/RequestMeta.java deleted file mode 100644 index 91d1e7ccc..000000000 --- a/src/main/java/com/basho/riak/client/request/RequestMeta.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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.request; - -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.apache.commons.httpclient.util.DateUtil; - -import com.basho.riak.client.util.ClientUtils; -import com.basho.riak.client.util.Constants; - -/** - * Extra headers and query parameters to send with a Riak operation. - */ -public class RequestMeta { - - private Map queryParams = new LinkedHashMap(); - private Map headers = new HashMap(); - - /** - * Use the given r parameter for fetchMeta, fetch, or stream operations - * - * @param r - * r- parameter for fetchMeta, fetch, or stream: the number of - * successful read response required for a successful overall - * response - * @return A {@link RequestMeta} object with the appropriate query - * parameters - */ - public static RequestMeta readParams(int r) { - RequestMeta meta = new RequestMeta(); - meta.setQueryParam(Constants.QP_R, Integer.toString(r)); - return meta; - } - - /** - * Use the given w and dw params for store or delete operations. - * - * @param w - * w- parameter for store and delete: the number of successful - * write responses required for a successful store operation - * @param dw - * dw- parameter for store and delete: The number of successful - * durable write responses required for a successful store - * operation - * @return A {@link RequestMeta} object with the appropriate query - * parameters - */ - public static RequestMeta writeParams(Integer w, Integer dw) { - RequestMeta meta = new RequestMeta(); - if (w != null) { - meta.setQueryParam(Constants.QP_W, Integer.toString(w)); - } - if (dw != null) { - meta.setQueryParam(Constants.QP_DW, Integer.toString(dw)); - } - return meta; - } - - /** - * Use the given rw parameter for delete operations - * - * @param rw - * rw- parameter for delete: the number of - * successful read/write response required for a successful overall - * response - * @return A {@link RequestMeta} object with the appropriate query - * parameters - */ - public static RequestMeta deleteParams(int rw) { - RequestMeta meta = new RequestMeta(); - meta.setQueryParam(Constants.QP_RW, Integer.toString(rw)); - return meta; - } - - /** - * Add the specified HTTP header - * - * @param key - * header name - * @param value - * header value - */ - public RequestMeta setHeader(String key, String value) { - headers.put(key, value); - return this; - } - - /** - * Return the value for the HTTP header or null if not set - * - * @param key - * header name - * @return value of header or null if not set - */ - public String getHeader(String key) { - return headers.get(key); - } - - /** - * Whether the HTTP header has been set - * - * @param key - * header name - */ - public boolean hasHeader(String key) { - return headers.containsKey(key); - } - - /** - * Map of HTTP header names to values - */ - public Map getHeaders() { - return headers; - } - - /** - * Query parameter value or null if not set - * - * @param param - * query parameter name - */ - public String getQueryParam(String param) { - return queryParams.get(param); - } - - /** - * Add the given query parameter to the request - * - * @param param - * query parameter name - * @param value - * query parameter value - */ - public RequestMeta setQueryParam(String param, String value) { - queryParams.put(param, value); - return this; - } - - /** - * A string containing all the specified query parameters in this - * {@link RequestMeta} in the form: p1=v1&p2=v2 - */ - public String getQueryParams() { - StringBuilder qp = new StringBuilder(); - for (String param : queryParams.keySet()) { - if (queryParams.get(param) != null) { - if (qp.length() > 0) { - qp.append("&"); - } - qp.append(param).append("=").append(queryParams.get(param)); - } - } - return qp.toString(); - } - - /** Convenience method for the X-Riak-ClientId HTTP header */ - public String getClientId() { - return getHeader(Constants.HDR_CLIENT_ID); - } - - public RequestMeta setClientId(String clientId) { - return setHeader(Constants.HDR_CLIENT_ID, clientId); - } - - /** Convenience method for the If-Modified-Since HTTP header */ - public String getIfModifiedSince() { - return getHeader(Constants.HDR_IF_MODIFIED_SINCE); - } - - public RequestMeta setIfModifiedSince(String lastmod) { - return setHeader(Constants.HDR_IF_MODIFIED_SINCE, lastmod); - } - - public RequestMeta setIfModifiedSince(Date lastmod) { - return setHeader(Constants.HDR_IF_MODIFIED_SINCE, DateUtil.formatDate(lastmod)); - } - - /** Convenience method for the If-Unmodified-Since HTTP header */ - public String getIfUnmodifiedSince() { - return getHeader(Constants.HDR_IF_UNMODIFIED_SINCE); - } - - public RequestMeta setIfUnmodifiedSince(String lastmod) { - return setHeader(Constants.HDR_IF_UNMODIFIED_SINCE, lastmod); - } - - public RequestMeta setIfUnmodifiedSince(Date lastmod) { - return setHeader(Constants.HDR_IF_UNMODIFIED_SINCE, DateUtil.formatDate(lastmod)); - } - - /** Convenience method for the If-Match HTTP header */ - public String getIfMatch() { - return getHeader(Constants.HDR_IF_MATCH); - } - - public RequestMeta setIfMatch(String etags) { - return setHeader(Constants.HDR_IF_MATCH, etags); - } - - public RequestMeta setIfMatch(String[] etags) { - return setHeader(Constants.HDR_IF_MATCH, ClientUtils.join(etags, ",")); - } - - /** Convenience method for the If-None-Match HTTP header */ - public String getIfNoneMatch() { - return getHeader(Constants.HDR_IF_NONE_MATCH); - } - - public RequestMeta setIfNoneMatch(String etags) { - return setHeader(Constants.HDR_IF_NONE_MATCH, etags); - } - - public RequestMeta setIfNoneMatch(String[] etags) { - return setHeader(Constants.HDR_IF_NONE_MATCH, ClientUtils.join(etags, ",")); - } - - /** Convenience method for the Accept HTTP header */ - public String getAccept() { - return getHeader(Constants.HDR_ACCEPT); - } - - public RequestMeta setAccept(String contentTypes) { - return setHeader(Constants.HDR_ACCEPT, contentTypes); - } -} diff --git a/src/main/java/com/basho/riak/client/request/RiakWalkSpec.java b/src/main/java/com/basho/riak/client/request/RiakWalkSpec.java deleted file mode 100644 index a19109b61..000000000 --- a/src/main/java/com/basho/riak/client/request/RiakWalkSpec.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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.request; - -import java.util.ArrayList; - -import com.basho.riak.client.util.ClientUtils; - -/** - * Taken from Jiak client in Riak source 12/1/09. - * - * RiakWalkSpecStep is the internal representation of a RiakWalkSpec segment. It - * should not be used directly. - */ -class RiakWalkSpecStep { - public final String bucket; - public final String tag; - public final String accumulateFlag; - - public RiakWalkSpecStep(final String b, final String t, final String a) { - bucket = b; - tag = t; - accumulateFlag = a; - } -} - -/** - * Tool for building Riak/Jaywalker specs. Using this class to specify walk - * specs ensures that bucket and tag names will be properly URL-escaped. - * - * @author Bryan Fink - * @version 0.1 - */ -public class RiakWalkSpec extends ArrayList { - - private static final long serialVersionUID = 7896627605420162292L; - - /** - * The "don't care" signifier. Pass this as the bucket, tag, or accumulate - * flag to select the default, or match-all option. - */ - public static final String WILDCARD = "_"; - - /** - * Create an empty Riak walk spec. - */ - public RiakWalkSpec() { - super(); - } - - /** - * Append a step to this walk spec. - * - * @param bucket - * The bucket of the step, or the wildcard. - * @param tag - * The tag of the step, or the wildcard. - * @param accumulateFlag - * The string "1" to force this step to be accumulated in the - * results. "0" to force this step not to be accumulated. - * WILDCARD to accept the default accumulation setting ("yes" for - * the last step, "no" for all others). - */ - public void addStep(final String bucket, final String tag, final String accumulateFlag) { - this.add(new RiakWalkSpecStep(bucket, tag, accumulateFlag)); - } - - /** - * Append a step to this walk spec. Same as the other addStep function, but - * allows the use of a boolean instead of a string for the accumulateFlag - * parameter. - */ - public void addStep(final String bucket, final String tag, boolean accumulateFlag) { - addStep(bucket, tag, accumulateFlag ? "1" : "0"); - } - - /** - * Append a step to this walk spec, and take the default option for the - * accumulate flag. - */ - public void addStep(final String bucket, final String tag) { - addStep(bucket, tag, WILDCARD); - } - - /** - * Convert this walk step to a string. All bucket and tag names will be - * URL-escaped in the return value. - */ - @Override public String toString() { - StringBuilder result = new StringBuilder(); - for (RiakWalkSpecStep s : this) { - result.append(ClientUtils.urlEncode(s.bucket)); - result.append(','); - result.append(ClientUtils.urlEncode(s.tag)); - result.append(','); - result.append(s.accumulateFlag); - result.append('/'); - } - return result.toString(); - } -} diff --git a/src/main/java/com/basho/riak/client/response/BucketResponse.java b/src/main/java/com/basho/riak/client/response/BucketResponse.java deleted file mode 100644 index 74f6bbcf7..000000000 --- a/src/main/java/com/basho/riak/client/response/BucketResponse.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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.response; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.Collection; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONTokener; - -import com.basho.riak.client.RiakBucketInfo; -import com.basho.riak.client.util.ClientUtils; -import com.basho.riak.client.util.Constants; - -/** - * Response from a GET request at a bucket's URL. Decorates an HttpResponse to - * interpret listBucket response from Riak, which is a JSON object with the keys - * "props" and "keys". - */ -public class BucketResponse extends HttpResponseDecorator implements HttpResponse { - - private RiakBucketInfo bucketInfo = null; - - /** - * On a 2xx response, parses the JSON response into a {@link RiakBucketInfo} - * - * @param r - * The HTTP response from a GET at a bucket - * @throws JSONException - * If the response is a 2xx but contains invalid JSON - * @throws IOException - * If a communication error with the Riak server while trying to - * read the streamed response - */ - public BucketResponse(HttpResponse r) throws JSONException, IOException { - super(r); - - if (r != null && r.isSuccess()) { - JSONObject props; - Collection keys; - if (!r.isStreamed()) { - JSONObject json = new JSONObject(r.getBodyAsString()); - JSONArray jsonKeys = json.optJSONArray(Constants.FL_KEYS); - props = json.optJSONObject(Constants.FL_SCHEMA); - keys = ClientUtils.jsonArrayAsList(jsonKeys); - } else { - InputStream stream = r.getStream(); - JSONTokener tokens = new JSONTokener(new InputStreamReader(stream)); - - // suck in the first object from the stream, which is the schema - // and give the rest to the streamed keys collection - props = new JSONObject(tokens).optJSONObject(Constants.FL_SCHEMA); - keys = new StreamedKeysCollection(tokens); - } - bucketInfo = new RiakBucketInfo(props, keys); - } - } - - /** - * Whether the bucket's schema and keys were returned in the response from - * Riak - */ - public boolean hasBucketInfo() { - return bucketInfo != null; - } - - /** - * The bucket's schema and keys - */ - public RiakBucketInfo getBucketInfo() { - return bucketInfo; - } -} \ No newline at end of file diff --git a/src/main/java/com/basho/riak/client/response/DefaultHttpResponse.java b/src/main/java/com/basho/riak/client/response/DefaultHttpResponse.java deleted file mode 100644 index be19df1e1..000000000 --- a/src/main/java/com/basho/riak/client/response/DefaultHttpResponse.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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.response; - -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; - -import org.apache.commons.httpclient.HttpMethod; - -import com.basho.riak.client.util.Constants; - -/** - * Simple implementation of HttpResponse interface. Simply stores and returns - * the various fields. - */ -public class DefaultHttpResponse implements HttpResponse { - - private String bucket; - private String key; - private int status = -1; - private Map headers = null; - private byte[] body = null; - private InputStream stream = null; - private HttpMethod httpMethod = null; - - public DefaultHttpResponse(String bucket, String key, int status, Map headers, byte[] body, - InputStream stream, HttpMethod httpMethod) { - if (headers == null) { - headers = new HashMap(); - } - - this.bucket = bucket; - this.key = key; - this.status = status; - this.headers = headers; - if(body != null) { - this.body = body.clone(); - } - this.stream = stream; - this.httpMethod = httpMethod; - } - - public String getBucket() { - return bucket; - } - - public String getKey() { - return key; - } - - public int getStatusCode() { - return status; - } - - public Map getHttpHeaders() { - return headers; - } - - public byte[] getBody() { - if (body != null) { - return body.clone(); - } - return null; - } - - public String getBodyAsString() { - if (body == null) { - return null; - } - return new String(body); - } - - public InputStream getStream() { - return stream; - } - - public boolean isStreamed() { - return stream != null; - } - - public HttpMethod getHttpMethod() { - return httpMethod; - } - - public boolean isSuccess() { - String method = null; - if (httpMethod != null) { - method = httpMethod.getName(); - } - - return (status >= 200 && status < 300) || - ((status == 300 || status == 304) && Constants.HTTP_HEAD_METHOD.equals(method)) || - ((status == 300 || status == 304) && Constants.HTTP_GET_METHOD.equals(method)) || - ((status == 300) && Constants.HTTP_PUT_METHOD.equals(method)) || - ((status == 404) && Constants.HTTP_DELETE_METHOD.equals(method)); - } - - public boolean isError() { - String method = null; - if (httpMethod != null) { - method = httpMethod.getName(); - } - - return (status < 100 || status >= 400) && !((status == 404) && Constants.HTTP_DELETE_METHOD.equals(method)); - } - - public void close() { - if (httpMethod != null) { - httpMethod.releaseConnection(); - } - } -} diff --git a/src/main/java/com/basho/riak/client/response/FetchResponse.java b/src/main/java/com/basho/riak/client/response/FetchResponse.java deleted file mode 100644 index 09d74682a..000000000 --- a/src/main/java/com/basho/riak/client/response/FetchResponse.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * 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.response; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import com.basho.riak.client.RiakClient; -import com.basho.riak.client.RiakLink; -import com.basho.riak.client.RiakObject; -import com.basho.riak.client.util.ClientUtils; -import com.basho.riak.client.util.Constants; -import com.basho.riak.client.util.StreamedMultipart; - -/** - * Response from a HEAD or GET request for an object. Decorates an HttpResponse - * to interpret fetch and fetchMeta responses from Riak's HTTP interface which - * returns object metadata in HTTP headers and value in the body. - */ -public class FetchResponse extends HttpResponseDecorator implements WithBodyResponse { - - private RiakObject object = null; - private Collection siblings = new ArrayList(); - - /** - * On a 2xx response, parse the HTTP response from Riak into a - * {@link RiakObject}. On a 300 response, parse the multipart/mixed HTTP - * body into a collection of sibling {@link RiakObject}s. - * - * A streaming response (i.e. r.isStreaming() == true), will have a null - * body and non-null stream. The resulting {@link RiakObject}(s) will return - * null for getValue() and the stream for getValueStream(). Users must - * remember to release the return value's underlying stream by calling - * close(). - * - * Sibling objects are also streamed. The values of the objects are buffered - * in memory as the stream is read. Consume and/or close each - * {@link RiakObject}'s stream as the collection is iterated to allow the - * buffers to be freed. - * - * @throws RiakResponseRuntimeException - * If the server returns a 300 without a proper multipart/mixed - * body - * @throws RiakIORuntimeException - * If an error occurs during communication with the Riak server. - */ - public FetchResponse(HttpResponse r, RiakClient riak) throws RiakResponseRuntimeException, RiakIORuntimeException { - super(r); - - if (r == null) - return; - - Map headers = r.getHttpHeaders(); - List links = ClientUtils.parseLinkHeader(headers.get(Constants.HDR_LINK)); - Map usermeta = ClientUtils.parseUsermeta(headers); - - if (r.getStatusCode() == 300) { - String contentType = headers.get(Constants.HDR_CONTENT_TYPE); - - if (contentType == null || !(contentType.trim().toLowerCase().startsWith(Constants.CTYPE_MULTIPART_MIXED))) { - throw new RiakResponseRuntimeException(r, "multipart/mixed content expected when object has siblings"); - } - - if (r.isStreamed()) { - try { - StreamedMultipart multipart = new StreamedMultipart(headers, r.getStream()); - siblings = new StreamedSiblingsCollection(riak, r.getBucket(), r.getKey(), multipart); - } catch (IOException e) { - throw new RiakIORuntimeException("Error finding initial boundary", e); - } - } else { - siblings = ClientUtils.parseMultipart(riak, r.getBucket(), r.getKey(), headers, r.getBody()); - } - - object = siblings.iterator().next(); - } else if (r.isSuccess()) { - object = new RiakObject(riak, r.getBucket(), r.getKey(), r.getBody(), - headers.get(Constants.HDR_CONTENT_TYPE), links, usermeta, - headers.get(Constants.HDR_VCLOCK), headers.get(Constants.HDR_LAST_MODIFIED), - headers.get(Constants.HDR_ETAG)); - - Long contentLength = null; - try { - contentLength = Long.parseLong(headers - .get(Constants.HDR_CONTENT_LENGTH)); - } catch (NumberFormatException ignored) {} - - object.setValueStream(r.getStream(), contentLength); - } - } - - public FetchResponse(HttpResponse r) throws RiakResponseRuntimeException { - this(r, null); - } - - /** - * Whether response contained a Riak object - */ - public boolean hasObject() { - return getObject() != null; - } - - /** - * Returns the first Riak object contained in the response. Equivalent to - * the first object in getSiblings() when hasSiblings() is true. - */ - public RiakObject getObject() { - return object; - } - - public void setObject(RiakObject object) { - this.object = object; - } - - /** - * Whether response contained a multiple Riak objects - */ - public boolean hasSiblings() { - return getSiblings().size() > 0; - } - - /** - * Returns a collection of the Riak objects contained in the response. - */ - public Collection getSiblings() { - return siblings; - } -} diff --git a/src/main/java/com/basho/riak/client/response/HttpResponse.java b/src/main/java/com/basho/riak/client/response/HttpResponse.java deleted file mode 100644 index 9bf359a43..000000000 --- a/src/main/java/com/basho/riak/client/response/HttpResponse.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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.response; - -import java.io.InputStream; -import java.util.Map; - -import org.apache.commons.httpclient.HttpMethod; - -/** - * HTTP response information resulting from some HTTP operation - */ -public interface HttpResponse { - - /** - * The target object's bucket - */ - public String getBucket(); - - /** - * The target object's key or null if bucket is target - */ - public String getKey(); - - /** - * Resulting status code from the HTTP request. - */ - public int getStatusCode(); - - /** - * The HTTP response headers. - */ - public Map getHttpHeaders(); - - /** - * The HTTP response body or null if isStreamed() - */ - public byte[] getBody(); - - public String getBodyAsString(); - - /** - * The HTTP response body as an input stream if isStreamed(); null otherwise - */ - public InputStream getStream(); - - /** - * Whether the response body is available as an input stream - */ - public boolean isStreamed(); - - /** - * The actual {@link HttpMethod} used to make the HTTP request. Most of the - * data here can be retrieved more simply using methods in this class. Also, - * note that the connection will already be closed, so calling - * getHttpMethod().getResponseBodyAsStream() will return null. - */ - public HttpMethod getHttpMethod(); - - /** - * Whether the HTTP response is considered a success. Generally this - * translates to a 2xx for any request, a 304 for GET and HEAD requests, or - * 404 for DELETE requests. - */ - public boolean isSuccess(); - - /** - * Whether the HTTP request returned a 4xx or 5xx response - */ - public boolean isError(); - - /** - * Releases the underlying the HTTP connection when the response is streamed - */ - public void close(); -} diff --git a/src/main/java/com/basho/riak/client/response/HttpResponseDecorator.java b/src/main/java/com/basho/riak/client/response/HttpResponseDecorator.java deleted file mode 100644 index a89cafb6f..000000000 --- a/src/main/java/com/basho/riak/client/response/HttpResponseDecorator.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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.response; - -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; - -import org.apache.commons.httpclient.HttpMethod; - -/** - * A default decorator implementation for HttpResponse - */ -public class HttpResponseDecorator implements HttpResponse { - - protected HttpResponse impl = null; - - public HttpResponseDecorator(HttpResponse r) { - impl = r; - } - - public String getBucket() { - if (impl == null) - return null; - return impl.getBucket(); - } - - public String getKey() { - if (impl == null) - return null; - return impl.getKey(); - } - - public byte[] getBody() { - if (impl == null) - return null; - return impl.getBody(); - } - - public String getBodyAsString() { - if (impl == null) - return null; - return impl.getBodyAsString(); - } - - public InputStream getStream() { - if (impl == null) - return null; - return impl.getStream(); - } - - public boolean isStreamed() { - if (impl == null) - return false; - return impl.isStreamed(); - } - - public Map getHttpHeaders() { - if (impl == null) - return new HashMap(); - return impl.getHttpHeaders(); - } - - public HttpMethod getHttpMethod() { - if (impl == null) - return null; - return impl.getHttpMethod(); - } - - public int getStatusCode() { - if (impl == null) - return -1; - return impl.getStatusCode(); - } - - public boolean isError() { - if (impl == null) - return true; - return impl.isError(); - } - - public boolean isSuccess() { - if (impl == null) - return false; - return impl.isSuccess(); - } - - public void close() { - if (impl != null) { - impl.close(); - } - } -} diff --git a/src/main/java/com/basho/riak/client/response/MapReduceResponse.java b/src/main/java/com/basho/riak/client/response/MapReduceResponse.java deleted file mode 100644 index 81147582c..000000000 --- a/src/main/java/com/basho/riak/client/response/MapReduceResponse.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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.response; - -import org.json.JSONArray; -import org.json.JSONException; - -/** - * Response from a map-reduce query (POST to /mapred). Decorates an HttpResponse - * and parses returned JSON array returned from Riak. - */ -public class MapReduceResponse extends HttpResponseDecorator implements HttpResponse { - - JSONArray result = null; - - /** - * On a 2xx response, parses the response into a {@link JSONArray} - * - * @param r - * The HTTP response query POST'd to the map-reduce resource - * @throws JSONException - * Response is a 2xx but doesn't contain a valid JSON array - */ - public MapReduceResponse(HttpResponse r) throws JSONException { - super(r); - - if (r != null && r.isSuccess() && (r.getBody() != null)) { - result = new JSONArray(r.getBodyAsString()); - } - } - - /** - * The result of the map-reduce query as a JSON array - */ - public JSONArray getResults() { - return result; - } -} diff --git a/src/main/java/com/basho/riak/client/response/RiakExceptionHandler.java b/src/main/java/com/basho/riak/client/response/RiakExceptionHandler.java deleted file mode 100644 index f29048255..000000000 --- a/src/main/java/com/basho/riak/client/response/RiakExceptionHandler.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.response; - -/** - * Allows clients to handle exceptions in a separate class rather than inline - * with the requests. If an RiakExceptionHandler is installed (use the client's - * setExceptionHandler() method), then exceptions RiakIOExceptions and - * RiakResponseExceptions will be passed to the handler rather than being - * thrown. If exceptions can be handled centrally by the caller, using an - * exception handler can result in cleaner code by avoiding repeated try/catch - * blocks for every operation. - */ -public interface RiakExceptionHandler { - - /** Handle exceptions caused by communication errors with the sever */ - public void handle(RiakIORuntimeException e); - - /** Handle exceptions caused by malformed responses from the sever */ - public void handle(RiakResponseRuntimeException e); - -} diff --git a/src/main/java/com/basho/riak/client/response/RiakIORuntimeException.java b/src/main/java/com/basho/riak/client/response/RiakIORuntimeException.java deleted file mode 100644 index 14fe242bf..000000000 --- a/src/main/java/com/basho/riak/client/response/RiakIORuntimeException.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.response; - -import java.io.IOException; - -/** - * Thrown when an error occurs during communication with the Riak server. - */ -public class RiakIORuntimeException extends RuntimeException { - - private static final long serialVersionUID = -3451479917953961929L; - - public RiakIORuntimeException() { - super(); - } - - public RiakIORuntimeException(String message, IOException cause) { - super(message, cause); - } - - public RiakIORuntimeException(String message) { - super(message); - } - - public RiakIORuntimeException(Throwable cause) { - super(cause); - } - -} diff --git a/src/main/java/com/basho/riak/client/response/RiakResponseRuntimeException.java b/src/main/java/com/basho/riak/client/response/RiakResponseRuntimeException.java deleted file mode 100644 index 59ed6bf2f..000000000 --- a/src/main/java/com/basho/riak/client/response/RiakResponseRuntimeException.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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.response; - -import java.io.InputStream; -import java.util.Map; - -import org.apache.commons.httpclient.HttpMethod; - -/** - * Thrown when the Riak server returns a malformed response. The HTTP response - * is returned in the exception. - */ -public class RiakResponseRuntimeException extends RuntimeException implements HttpResponse { - - private static final long serialVersionUID = 2853253336513247178L; - private HttpResponse response = null; - - public RiakResponseRuntimeException(HttpResponse response) { - super(); - this.response = response; - } - - public RiakResponseRuntimeException(HttpResponse response, String message, Throwable cause) { - super(message, cause); - this.response = response; - } - - public RiakResponseRuntimeException(HttpResponse response, String message) { - super(message); - this.response = response; - } - - public RiakResponseRuntimeException(HttpResponse response, Throwable cause) { - super(cause); - this.response = response; - } - - public byte[] getBody() { - if (response == null) - return null; - return response.getBody(); - } - - public String getBodyAsString() { - if (response == null) - return null; - return response.getBodyAsString(); - } - - public InputStream getStream() { - if (response == null) - return null; - return response.getStream(); - } - - public boolean isStreamed() { - if (response == null) - return false; - return response.isStreamed(); - } - - public String getBucket() { - if (response == null) - return null; - return response.getBucket(); - } - - public Map getHttpHeaders() { - if (response == null) - return null; - return response.getHttpHeaders(); - } - - public HttpMethod getHttpMethod() { - if (response == null) - return null; - return response.getHttpMethod(); - } - - public String getKey() { - if (response == null) - return null; - return response.getKey(); - } - - public int getStatusCode() { - if (response == null) - return -1; - return response.getStatusCode(); - } - - public boolean isError() { - return true; - } - - public boolean isSuccess() { - return false; - } - - public void close() { - if (response != null) - response.close(); - } -} diff --git a/src/main/java/com/basho/riak/client/response/StoreResponse.java b/src/main/java/com/basho/riak/client/response/StoreResponse.java deleted file mode 100644 index 8d50245aa..000000000 --- a/src/main/java/com/basho/riak/client/response/StoreResponse.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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.response; - -import java.util.Collection; -import java.util.Map; - -import com.basho.riak.client.RiakObject; -import com.basho.riak.client.util.Constants; - -/** - * Response from a PUT request for an object. Decorates an HttpResponse to - * interpret store responses from Riak which returns updated object metadata in - * HTTP headers. - */ -public class StoreResponse extends HttpResponseDecorator implements WithBodyResponse { - - private final FetchResponse fetchResponse; - private String vclock = null; - private String lastmod = null; - private String vtag = null; - - /** - * On a 2xx response, parses the HTTP headers into updated object metadata. - */ - public StoreResponse(FetchResponse fetchResponse) { - super(fetchResponse); - - this.fetchResponse = fetchResponse; - - if (fetchResponse != null && fetchResponse.isSuccess()) { - Map headers = fetchResponse.getHttpHeaders(); - vclock = headers.get(Constants.HDR_VCLOCK); - lastmod = headers.get(Constants.HDR_LAST_MODIFIED); - vtag = headers.get(Constants.HDR_ETAG); - } - } - - /** The object's updated vclock or null if Riak didn't return one. */ - public String getVclock() { - return vclock; - } - - /** - * The object's last modified date or null if Riak didn't return one. - */ - public String getLastmod() { - return lastmod; - } - - /** The object's updated etag or null if Riak didn't return one. */ - public String getVtag() { - return vtag; - } - - /** - * @return - * @see com.basho.riak.client.response.FetchResponse#hasObject() - */ - public boolean hasObject() { - return fetchResponse.hasObject(); - } - - /** - * @return - * @see com.basho.riak.client.response.FetchResponse#getObject() - */ - public RiakObject getObject() { - return fetchResponse.getObject(); - } - - /** - * @return - * @see com.basho.riak.client.response.FetchResponse#hasSiblings() - */ - public boolean hasSiblings() { - return fetchResponse.hasSiblings(); - } - - /** - * @return - * @see com.basho.riak.client.response.FetchResponse#getSiblings() - */ - public Collection getSiblings() { - return fetchResponse.getSiblings(); - } -} diff --git a/src/main/java/com/basho/riak/client/response/StreamHandler.java b/src/main/java/com/basho/riak/client/response/StreamHandler.java deleted file mode 100644 index 7f67f6f5d..000000000 --- a/src/main/java/com/basho/riak/client/response/StreamHandler.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.response; - -import java.io.InputStream; -import java.util.Map; - -import org.apache.commons.httpclient.HttpMethod; - -/** - * Used with RiakClient.stream() to process the HTTP responses for fetch - * requests as a stream. - */ -public interface StreamHandler { - - /** - * Process the HTTP response whose value is given as a stream. - * - * @param bucket - * The object's bucket - * @param key - * The object's key - * @param status - * The HTTP status code returned for the request - * @param headers - * The HTTP headers returned in the response - * @param in - * InputStream of the object's value (body) - * @param httpMethod - * The original {@link HttpMethod} used to make the request. Its - * connection is still open and will be closed by the caller on - * return. - * @return true if the object was processed; false otherwise - */ - public boolean process(String bucket, String key, int status, Map headers, InputStream in, - HttpMethod httpMethod); -} diff --git a/src/main/java/com/basho/riak/client/response/StreamedKeysCollection.java b/src/main/java/com/basho/riak/client/response/StreamedKeysCollection.java deleted file mode 100644 index 846fc9a9f..000000000 --- a/src/main/java/com/basho/riak/client/response/StreamedKeysCollection.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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.response; - -import org.json.JSONException; -import org.json.JSONTokener; - -import com.basho.riak.client.util.CollectionWrapper; - -/** - * Presents the stream of keys from a Riak bucket response with query parameter - * keys=stream as a collection. Keys are read from the stream as needed. Note, - * this class is NOT thread-safe! - */ -public class StreamedKeysCollection extends CollectionWrapper { - - JSONTokener tokens; - boolean readingArray = false; - - public StreamedKeysCollection(JSONTokener tokens) { - this.tokens = tokens; - } - - /** - * Tries to read and cache another set of keys from the input stream. This - * function is actually just a hacked-up implementation that finds the next - * available array in the stream and sucks elements out of it. - */ - @Override protected boolean cacheNext() { - if (tokens == null) - return false; - - try { - while (!tokens.end()) { - char c = tokens.nextClean(); - if ((!readingArray && c == '[') || (readingArray && c == ',')) { - if (tokens.nextClean() != ']') { - tokens.back(); - readingArray = true; - cache(tokens.nextValue().toString()); - return true; - } - } else if (readingArray && c == ']') { - readingArray = false; - } else if (c == '\\') { - tokens.nextClean(); // skip over escaped chars - } - } - } catch (JSONException e) { /* nop */} - - return false; - } - - @Override protected void closeBackend() { - tokens = null; - } -} diff --git a/src/main/java/com/basho/riak/client/response/StreamedSiblingsCollection.java b/src/main/java/com/basho/riak/client/response/StreamedSiblingsCollection.java deleted file mode 100644 index 7f6c3b82d..000000000 --- a/src/main/java/com/basho/riak/client/response/StreamedSiblingsCollection.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.basho.riak.client.response; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import com.basho.riak.client.RiakClient; -import com.basho.riak.client.RiakLink; -import com.basho.riak.client.RiakObject; -import com.basho.riak.client.util.ClientUtils; -import com.basho.riak.client.util.CollectionWrapper; -import com.basho.riak.client.util.Constants; -import com.basho.riak.client.util.Multipart; -import com.basho.riak.client.util.StreamedMultipart; - -public class StreamedSiblingsCollection extends CollectionWrapper { - - String bucket; - String key; - RiakClient riak; - StreamedMultipart multipart; - - public StreamedSiblingsCollection(RiakClient riak, String bucket, String key, StreamedMultipart multipart) { - this.bucket = bucket; - this.key = key; - this.riak = riak; - this.multipart = multipart; - } - - /** - * Tries to read and cache another part of the multipart/mixed stream. - * - * @throws RiakIORuntimeException - * If an error occurs during communication with the Riak server. - */ - @Override protected boolean cacheNext() { - if (multipart == null) - return false; - - String vclock = null; - - if (multipart.getHeaders() != null) { - vclock = multipart.getHeaders().get(Constants.HDR_VCLOCK); - } - - Multipart.Part part; - try { - part = multipart.next(); - } catch (RuntimeException e) { - if (e.getCause() instanceof IOException) { - throw new RiakIORuntimeException(e); - } - throw e; - } - - if (part != null) { - Map headers = part.getHeaders(); - List links = ClientUtils.parseLinkHeader(headers.get(Constants.HDR_LINK)); - Map usermeta = ClientUtils.parseUsermeta(headers); - String location = headers.get(Constants.HDR_LOCATION); - String partBucket = bucket; - String partKey = key; - - if (location != null) { - String[] locationParts = location.split("/"); - if (locationParts.length >= 2) { - partBucket = locationParts[locationParts.length - 2]; - partKey = locationParts[locationParts.length - 1]; - } - } - - RiakObject o = new RiakObject(riak, partBucket, partKey, null, headers.get(Constants.HDR_CONTENT_TYPE), - links, usermeta, vclock, headers.get(Constants.HDR_LAST_MODIFIED), - headers.get(Constants.HDR_ETAG)); - o.setValueStream(part.getStream()); - cache(o); - return true; - } - - return false; - } - - @Override protected void closeBackend() { - riak = null; - multipart = null; - } -} diff --git a/src/main/java/com/basho/riak/client/response/WalkResponse.java b/src/main/java/com/basho/riak/client/response/WalkResponse.java deleted file mode 100644 index 351738ef4..000000000 --- a/src/main/java/com/basho/riak/client/response/WalkResponse.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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.response; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import com.basho.riak.client.RiakClient; -import com.basho.riak.client.RiakObject; -import com.basho.riak.client.util.ClientUtils; -import com.basho.riak.client.util.Constants; -import com.basho.riak.client.util.Multipart; - -/** - * Response from a GET request for an object with link walking. Decorates an - * HttpResponse to interpret walk responses from Riak which returns - * multipart/mixed documents. - */ -public class WalkResponse extends HttpResponseDecorator implements HttpResponse { - - private List> steps = new ArrayList>(); - - /** - * On a 2xx response, parses the HTTP body into a list of steps. Each step - * contains a list of objects returned in that step. The HTTP body is a - * multipart/mixed message with multipart/mixed subparts - */ - public WalkResponse(HttpResponse r, RiakClient riak) throws RiakResponseRuntimeException { - super(r); - - if (r != null && r.isSuccess()) { - steps = parseSteps(r, riak); - } - } - - public WalkResponse(HttpResponse r) throws RiakResponseRuntimeException { - this(r, null); - } - - /** Whether objects were contained in the response */ - public boolean hasSteps() { - return steps.size() > 0; - } - - /** - * Steps accumulated from link walking. See RiakClient.walk() for more - * information. - */ - public List> getSteps() { - return steps; - } - - /** - * Parse a multipart/mixed message with multipart/mixed subparts into a list - * of lists. - * - * @param r - * HTTP response from Riak - * @param riak - * {@link RiakClient} to associate this object with - * @return A list of lists of {@link RiakObject}s represented by the - * response. - * @throws RiakResponseRuntimeException - * If one of the parts of the body doesn't contain a proper - * multipart/mixed message - */ - private static List> parseSteps(HttpResponse r, RiakClient riak) - throws RiakResponseRuntimeException { - String bucket = r.getBucket(); - String key = r.getKey(); - List> parsedSteps = new ArrayList>(); - List parts = Multipart.parse(r.getHttpHeaders(), r.getBody()); - - if (parts != null) { - for (Multipart.Part part : parts) { - Map partHeaders = part.getHeaders(); - String contentType = partHeaders.get(Constants.HDR_CONTENT_TYPE); - - if (contentType == null || - !(contentType.trim().toLowerCase().startsWith(Constants.CTYPE_MULTIPART_MIXED))) - throw new RiakResponseRuntimeException(r, "multipart/mixed subparts expected in link walk results"); - - parsedSteps.add(ClientUtils.parseMultipart(riak, bucket, key, partHeaders, part.getBody())); - } - } - - return parsedSteps; - } -} diff --git a/src/main/java/com/basho/riak/client/util/BranchableInputStream.java b/src/main/java/com/basho/riak/client/util/BranchableInputStream.java deleted file mode 100644 index c3df1c5c4..000000000 --- a/src/main/java/com/basho/riak/client/util/BranchableInputStream.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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.util; - -import java.io.IOException; - -import java.io.InputStream; - -/** - * An input stream that can be branched into other InputStreams, each - * maintaining its own location, with the main read() method always returning - * bytes from the furthest advanced branch. - * - * @author jlee - */ -public class BranchableInputStream extends InputStream { - - static final int DEFAULT_BASE_CHUNK_SIZE = 1024; - static final int MAX_BYTES_PER_READ = 1024; - int nextChunkSize; - - InputStream impl; - LinkedChunk lastChunk = null; - int dataLen = 0; - int pos; - boolean eof = false; - - public BranchableInputStream(InputStream in) { - this(in, DEFAULT_BASE_CHUNK_SIZE); - } - - public BranchableInputStream(InputStream in, int initialBufferSize) { - impl = in; - lastChunk = new LinkedChunk(0, 0); - nextChunkSize = initialBufferSize; - } - - @Override public int read() throws IOException { - int curpos = pos; - if (readUntil(curpos)) - return lastChunk.get(curpos); - return -1; - } - - @Override public void close() throws IOException { - eof = true; - impl.close(); - } - - public int peek() throws IOException { - int curpos = pos; - int c = read(); - pos = curpos; - return c; - } - - public InputStream branch() { - return new InputStreamBranch(lastChunk, pos); - } - - boolean readUntil(int pos) throws IOException { - if (!eof) { - while ((pos >= dataLen) && !eof) { - if (lastChunk.full()) { - lastChunk.setNext(new LinkedChunk(lastChunk.lastIndex() + 1, nextChunkSize)); - lastChunk = lastChunk.next(); - nextChunkSize *= 2; - } - - int bytesRead = lastChunk.readFrom(impl, MAX_BYTES_PER_READ); - if (bytesRead < 0) { - eof = true; - } else { - dataLen += bytesRead; - } - } - } - if (pos < dataLen) { - this.pos = Math.max(this.pos, pos + 1); - return true; - } - return false; - } - - class InputStreamBranch extends InputStream { - - LinkedChunk chunk; - int pos; - - InputStreamBranch(LinkedChunk chunk, int pos) { - this.chunk = chunk; - this.pos = pos; - } - - @Override public int read() throws IOException { - if (chunk == null || !readUntil(pos)) - return -1; - - while (pos > chunk.lastIndex()) { - chunk = chunk.next; - } - return chunk.get(pos++); - } - - @Override public void close() { - chunk = null; - } - } - - class LinkedChunk { - int offset; - int len; - byte[] buf; - LinkedChunk next = null; - - LinkedChunk(int offset, int size) { - this.offset = offset; - buf = new byte[size]; - len = 0; - } - - int readFrom(InputStream in, int maxBytes) throws IOException { - int bytesRead = in.read(buf, len, Math.min(remaining(), maxBytes)); - if (bytesRead > 0) { - len += bytesRead; - } - return bytesRead; - } - - int get(int index) { - if ((index < offset) || (index - offset >= len)) - return -1; - return buf[index - offset] & 0xff; - } - - int lastIndex() { - return offset + buf.length - 1; - } - - boolean full() { - return (len == buf.length); - } - - int remaining() { - return (buf.length - len); - } - - LinkedChunk next() { - return next; - } - - void setNext(LinkedChunk next) { - this.next = next; - } - } -} diff --git a/src/main/java/com/basho/riak/client/util/ClientHelper.java b/src/main/java/com/basho/riak/client/util/ClientHelper.java deleted file mode 100644 index 9fb41a38f..000000000 --- a/src/main/java/com/basho/riak/client/util/ClientHelper.java +++ /dev/null @@ -1,401 +0,0 @@ -/* - * 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.util; - -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.util.Map; - -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; -import org.apache.commons.httpclient.methods.DeleteMethod; -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.commons.httpclient.methods.HeadMethod; -import org.apache.commons.httpclient.methods.PostMethod; -import org.apache.commons.httpclient.methods.PutMethod; -import org.apache.commons.httpclient.methods.StringRequestEntity; -import org.json.JSONObject; - -import com.basho.riak.client.RiakClient; -import com.basho.riak.client.RiakConfig; -import com.basho.riak.client.RiakObject; -import com.basho.riak.client.request.RequestMeta; -import com.basho.riak.client.response.BucketResponse; -import com.basho.riak.client.response.DefaultHttpResponse; -import com.basho.riak.client.response.HttpResponse; -import com.basho.riak.client.response.RiakExceptionHandler; -import com.basho.riak.client.response.RiakIORuntimeException; -import com.basho.riak.client.response.RiakResponseRuntimeException; -import com.basho.riak.client.response.StreamHandler; - -/** - * This class performs the actual HTTP requests underlying the operations in - * RiakClient and returns the resulting HTTP responses. It is up to RiakClient - * to interpret the responses and translate them into the appropriate format. - */ -public class ClientHelper { - - private RiakConfig config; - private HttpClient httpClient; - private String clientId = null; - private RiakExceptionHandler exceptionHandler = null; - - public ClientHelper(RiakConfig config, String clientId) { - this.config = config; - httpClient = ClientUtils.newHttpClient(config); - setClientId(clientId); - } - - /** Used for testing -- inject an HttpClient */ - void setHttpClient(HttpClient httpClient) { - this.httpClient = httpClient; - } - - /** - * See {@link RiakClient#getClientId()} - */ - public byte[] getClientId() { - try { - return Base64.decodeBase64(clientId.getBytes("UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException("UTF-8 support required in JVM"); - } - } - - public void setClientId(String clientId) { - if (clientId != null) { - this.clientId = ClientUtils.encodeClientId(clientId); - } else { - this.clientId = ClientUtils.randomClientId(); - } - } - - /** - * See - * {@link RiakClient#setBucketSchema(String, com.basho.riak.client.RiakBucketInfo, RequestMeta)} - */ - public HttpResponse setBucketSchema(String bucket, JSONObject schema, RequestMeta meta) { - if (schema == null) { - schema = new JSONObject(); - } - if (meta == null) { - meta = new RequestMeta(); - } - - meta.setHeader(Constants.HDR_ACCEPT, Constants.CTYPE_JSON); - - PutMethod put = new PutMethod(ClientUtils.makeURI(config, bucket)); - put.setRequestEntity(new ByteArrayRequestEntity(schema.toString().getBytes(), Constants.CTYPE_JSON)); - - return executeMethod(bucket, null, put, meta); - } - - /** - * Same as {@link RiakClient#getBucketSchema(String, RequestMeta)}, except - * only returning the HTTP response. - */ - public HttpResponse getBucketSchema(String bucket, RequestMeta meta) { - if (meta == null) { - meta = new RequestMeta(); - } - if (meta.getQueryParam(Constants.QP_KEYS) == null) { - meta.setQueryParam(Constants.QP_KEYS, Constants.NO_KEYS); - } - return listBucket(bucket, meta, false); - } - - /** - * Same as {@link RiakClient}, except only returning the HTTP response, and - * if streamResponse==true, the response will be streamed back, so the user - * is responsible for calling {@link BucketResponse#close()} - */ - public HttpResponse listBucket(String bucket, RequestMeta meta, boolean streamResponse) { - if (meta == null) { - meta = new RequestMeta(); - } - if (meta.getQueryParam(Constants.QP_KEYS) == null) { - if (streamResponse) { - meta.setQueryParam(Constants.QP_KEYS, Constants.STREAM_KEYS); - } else { - meta.setQueryParam(Constants.QP_KEYS, Constants.INCLUDE_KEYS); - } - } - if (meta.getHeader(Constants.HDR_CONTENT_TYPE) == null) { - meta.setHeader(Constants.HDR_CONTENT_TYPE, Constants.CTYPE_JSON); - } - if (meta.getHeader(Constants.HDR_ACCEPT) == null) { - meta.setHeader(Constants.HDR_ACCEPT, Constants.CTYPE_JSON); - } - - GetMethod get = new GetMethod(ClientUtils.makeURI(config, bucket)); - return executeMethod(bucket, null, get, meta, streamResponse); - } - - /** - * Same as {@link RiakClient}, except only returning the HTTP response - */ - public HttpResponse store(RiakObject object, RequestMeta meta) { - if (meta == null) { - meta = new RequestMeta(); - } - if (meta.getClientId() == null) { - meta.setClientId(clientId); - } - if (meta.getHeader(Constants.HDR_CONNECTION) == null) { - meta.setHeader(Constants.HDR_CONNECTION, "keep-alive"); - } - - String bucket = object.getBucket(); - String key = object.getKey(); - String url = ClientUtils.makeURI(config, bucket, key); - PutMethod put = new PutMethod(url); - - object.writeToHttpMethod(put); - return executeMethod(bucket, key, put, meta); - } - - /** - * Same as {@link RiakClient}, except only returning the HTTP response - */ - public HttpResponse fetchMeta(String bucket, String key, RequestMeta meta) { - if (meta == null) { - meta = new RequestMeta(); - } - if (meta.getQueryParam(Constants.QP_R) == null) { - meta.setQueryParam(Constants.QP_R, Constants.DEFAULT_R.toString()); - } - HeadMethod head = new HeadMethod(ClientUtils.makeURI(config, bucket, key)); - return executeMethod(bucket, key, head, meta); - } - - /** - * Same as {@link RiakClient}, except only returning the HTTP response and - * allows the response to be streamed. - * - * @param bucket - * Same as {@link RiakClient} - * @param key - * Same as {@link RiakClient} - * @param meta - * Same as {@link RiakClient} - * @param streamResponse - * If true, the connection will NOT be released. Use - * HttpResponse.getHttpMethod().getResponseBodyAsStream() to get - * the response stream; HttpResponse.getBody() will return null. - * - * @return Same as {@link RiakClient} - */ - public HttpResponse fetch(String bucket, String key, RequestMeta meta, boolean streamResponse) { - if (meta == null) { - meta = new RequestMeta(); - } - if (meta.getQueryParam(Constants.QP_R) == null) { - meta.setQueryParam(Constants.QP_R, Constants.DEFAULT_R.toString()); - } - GetMethod get = new GetMethod(ClientUtils.makeURI(config, bucket, key)); - return executeMethod(bucket, key, get, meta, streamResponse); - } - - public HttpResponse fetch(String bucket, String key, RequestMeta meta) { - return fetch(bucket, key, meta, false); - } - - /** - * Same as {@link RiakClient}, except only returning the HTTP response - */ - public boolean stream(String bucket, String key, StreamHandler handler, RequestMeta meta) throws IOException { - if (meta == null) { - meta = new RequestMeta(); - } - if (meta.getQueryParam(Constants.QP_R) == null) { - meta.setQueryParam(Constants.QP_R, Constants.DEFAULT_R.toString()); - } - GetMethod get = new GetMethod(ClientUtils.makeURI(config, bucket, key)); - try { - int status = httpClient.executeMethod(get); - if (handler == null) - return true; - - return handler.process(bucket, key, status, ClientUtils.asHeaderMap(get.getResponseHeaders()), - get.getResponseBodyAsStream(), get); - } finally { - get.releaseConnection(); - } - } - - /** - * Same as {@link RiakClient}, except only returning the HTTP response - */ - public HttpResponse delete(String bucket, String key, RequestMeta meta) { - if (meta == null) { - meta = new RequestMeta(); - } - String url = ClientUtils.makeURI(config, bucket, key); - DeleteMethod delete = new DeleteMethod(url); - return executeMethod(bucket, key, delete, meta); - } - - /** - * Same as {@link RiakClient}, except only returning the HTTP response - */ - public HttpResponse walk(String bucket, String key, String walkSpec, RequestMeta meta) { - GetMethod get = new GetMethod(ClientUtils.makeURI(config, bucket, key, walkSpec)); - return executeMethod(bucket, key, get, meta); - } - - /** - * Same as {@link RiakClient}, except only returning the HTTP response - */ - public HttpResponse mapReduce(String job, RequestMeta meta) { - PostMethod post = new PostMethod(config.getMapReduceUrl()); - try { - post.setRequestEntity(new StringRequestEntity(job, Constants.CTYPE_JSON, null)); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException("StringRequestEntity should always support no charset", e); - } - return executeMethod(null, null, post, meta); - } - - /** @return the installed exception handler or null if not installed */ - public RiakExceptionHandler getExceptionHandler() { - return exceptionHandler; - } - - /** - * Install an exception handler. If an exception handler is provided, then - * the Riak client will hand exceptions to the handler rather than throwing - * them. - */ - public void setExceptionHandler(RiakExceptionHandler exceptionHandler) { - this.exceptionHandler = exceptionHandler; - } - - /** - * Hands exception e to installed exception handler if there is - * one or throw it. - * - * @return A 0-status {@link HttpResponse}. - */ - public HttpResponse toss(RiakIORuntimeException e) { - if (exceptionHandler != null) { - exceptionHandler.handle(e); - return new DefaultHttpResponse(null, null, 0, null, null, null, null); - } else - throw e; - } - - public HttpResponse toss(RiakResponseRuntimeException e) { - if (exceptionHandler != null) { - exceptionHandler.handle(e); - return new DefaultHttpResponse(null, null, 0, null, null, null, null); - } else - throw e; - } - - /** - * Return the {@link HttpClient} used to make requests, which can be - * configured. - */ - public HttpClient getHttpClient() { - return httpClient; - } - - /** - * @return The config used to construct the HttpClient connecting to Riak. - */ - public RiakConfig getConfig() { - return config; - } - - /** - * Perform and HTTP request and return the resulting response using the - * internal HttpClient. - * - * @param bucket - * Bucket of the object receiving the request. - * @param key - * Key of the object receiving the request or null if the request - * is for a bucket. - * @param httpMethod - * The HTTP request to perform; must not be null. - * @param meta - * Extra HTTP headers to attach to the request. Query parameters - * are ignored; they should have already been used to construct - * httpMethod and query parameters. - * @param streamResponse - * If true, the connection will NOT be released. Use - * HttpResponse.getHttpMethod().getResponseBodyAsStream() to get - * the response stream; HttpResponse.getBody() will return null. - * - * @return The HTTP response returned by Riak from executing - * httpMethod. - * - * @throws RiakIORuntimeException - * If an error occurs during communication with the Riak server - * (i.e. HttpClient threw an IOException) - */ - HttpResponse executeMethod(String bucket, String key, HttpMethod httpMethod, RequestMeta meta, - boolean streamResponse) { - - if (meta != null) { - Map headers = meta.getHeaders(); - for (String header : headers.keySet()) { - httpMethod.setRequestHeader(header, headers.get(header)); - } - - String queryParams = meta.getQueryParams(); - if (queryParams != null && (queryParams.length() != 0)) { - String currentQuery = httpMethod.getQueryString(); - if (currentQuery != null && (currentQuery.length() != 0)) { - httpMethod.setQueryString(currentQuery + "&" + queryParams); - } else { - httpMethod.setQueryString(queryParams); - } - } - } - - try { - httpClient.executeMethod(httpMethod); - - int status = 0; - if (httpMethod.getStatusLine() != null) { - status = httpMethod.getStatusCode(); - } - - Map headers = ClientUtils.asHeaderMap(httpMethod.getResponseHeaders()); - byte[] body = null; - InputStream stream = null; - if (streamResponse) { - stream = httpMethod.getResponseBodyAsStream(); - } else { - body = httpMethod.getResponseBody(); - } - - return new DefaultHttpResponse(bucket, key, status, headers, body, stream, httpMethod); - } catch (IOException e) { - return toss(new RiakIORuntimeException(e)); - } finally { - if (!streamResponse) { - httpMethod.releaseConnection(); - } - } - } - - HttpResponse executeMethod(String bucket, String key, HttpMethod httpMethod, RequestMeta meta) { - return executeMethod(bucket, key, httpMethod, meta, false); - } -} diff --git a/src/main/java/com/basho/riak/client/util/ClientUtils.java b/src/main/java/com/basho/riak/client/util/ClientUtils.java deleted file mode 100644 index 86da23a95..000000000 --- a/src/main/java/com/basho/riak/client/util/ClientUtils.java +++ /dev/null @@ -1,475 +0,0 @@ -/* - * 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.util; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.Map.Entry; - -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.httpclient.Header; -import org.apache.commons.httpclient.HostConfiguration; -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpConnectionManager; -import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; -import org.apache.commons.httpclient.params.HttpClientParams; -import org.apache.commons.httpclient.params.HttpConnectionManagerParams; -import org.apache.commons.httpclient.params.HttpMethodParams; -import org.json.JSONArray; -import org.json.JSONObject; - -import com.basho.riak.client.RiakClient; -import com.basho.riak.client.RiakConfig; -import com.basho.riak.client.RiakLink; -import com.basho.riak.client.RiakObject; -import com.basho.riak.client.response.RiakExceptionHandler; - -/** - * Utility functions. - */ -public class ClientUtils { - - // Matches the scheme, host and port of a URL - private static String URL_PATH_MASK = "^(?:[A-Za-z0-9+-\\.]+://)?[^/]*"; - private static Random rng = new Random(); - /** - * Construct a new {@link HttpClient} instance given a {@link RiakConfig}. - * - * @param config - * {@link RiakConfig} containing HttpClient configuration - * specifics. - * @return A new {@link HttpClient} - */ - public static HttpClient newHttpClient(RiakConfig config) { - - HttpClient http = config.getHttpClient(); - HttpConnectionManager m; - - if (http == null) { - m = new MultiThreadedHttpConnectionManager(); - http = new HttpClient(m); - } else { - m = http.getHttpConnectionManager(); - } - - HttpConnectionManagerParams mp = m.getParams(); - if (config.getMaxConnections() != null) { - mp.setMaxTotalConnections(config.getMaxConnections()); - mp.setMaxConnectionsPerHost(HostConfiguration.ANY_HOST_CONFIGURATION, config.getMaxConnections()); - } - - HttpClientParams cp = http.getParams(); - if (config.getTimeout() != null) { - mp.setConnectionTimeout(config.getTimeout().intValue()); - cp.setConnectionManagerTimeout(config.getTimeout()); - cp.setSoTimeout(config.getTimeout().intValue()); - } - if (config.getRetryHandler() != null) { - cp.setParameter(HttpMethodParams.RETRY_HANDLER, config.getRetryHandler()); - } - - return http; - } - - /** - * Return a URL to the given bucket - * - * @param config - * RiakConfig containing the base URL to Riak - * @param bucket - * Bucket whose URL to retrieving - * @return URL to the bucket - */ - public static String makeURI(RiakConfig config, String bucket) { - return config.getUrl() + "/" + urlEncode(bucket); - } - - /** - * Return a URL to the given object - * - * @param config - * RiakConfig containing the base URL to Riak - * @param bucket - * Bucket of the object - * @param key - * Key of the object - * @return URL to the object - */ - public static String makeURI(RiakConfig config, String bucket, String key) { - if (key == null) - return makeURI(config, bucket); - return makeURI(config, bucket) + "/" + urlEncode(key); - } - - /** - * Return a URL to the given object - * - * @param config - * RiakConfig containing the base URL to Riak - * @param bucket - * Bucket of the object - * @param key - * Key of the object - * @param extra - * Extra path information beyond the bucket and key (e.g. for - * link walking or query parameters) - * @return URL to the object - */ - public static String makeURI(RiakConfig config, String bucket, String key, String extra) { - if (extra == null) - return makeURI(config, bucket, key); - - if (!extra.startsWith("?") && !extra.startsWith("/")) { - extra = "/" + extra; - } - - return makeURI(config, bucket, key) + extra; - } - - /** - * Return just the path portion of the given URL - */ - public static String getPathFromUrl(String url) { - if (url == null) - return null; - return url.replaceFirst(URL_PATH_MASK, ""); - } - - /** - * UTF-8 encode the string - */ - public static String urlEncode(String s) { - try { - return URLEncoder.encode(s, "UTF-8"); - } catch (UnsupportedEncodingException unreached) { - // UTF-8 must be supported by every Java implementation: - // http://java.sun.com/j2se/1.4.2/docs/api/java/nio/charset/Charset.html - throw new IllegalStateException("UTF-8 must be supported", unreached); - } - } - - /** - * Decodes a UTF-8 encoded string - */ - public static String urlDecode(String s) { - try { - return URLDecoder.decode(s, "UTF-8"); - } catch (UnsupportedEncodingException unreached) { - throw new IllegalStateException("UTF-8 must be supported", unreached); - } - } - - /** - * Base64 encodes the first 4 bytes of clientId into a value acceptable for - * the X-Riak-ClientId header. - */ - public static String encodeClientId(byte[] clientId) { - if (clientId == null || clientId.length < 4) - throw new IllegalArgumentException("ClientId must be at least 4 bytes"); - - try { - return new String(Base64.encodeBase64(new byte[] { clientId[0], clientId[1], clientId[2], clientId[3] }), "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException("UTF-8 support is required by JVM"); - } - } - - public static String encodeClientId(String clientId) { - return encodeClientId(clientId.getBytes()); - } - - /** - * Returns a random X-Riak-ClientId header value. - */ - public static String randomClientId() { - byte[] rnd = new byte[4]; - rng.nextBytes(rnd); - return encodeClientId(rnd); - } - - /** - * Unquote and unescape an HTTP quoted-string: - * - * http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 - * - * Does nothing if s is not quoted. - * - * @param s - * quoted-string to unquote - * @return s with quotes and backslash-escaped characters unescaped - */ - public static String unquoteString(String s) { - if (s.startsWith("\"") && s.endsWith("\"")) { - s = s.substring(1, s.length() - 1); - } - return s.replaceAll("\\\\(.)", "$1"); - } - - /** - * Convert a header array returned from {@link HttpClient} to a map - * - * @param headers - * Header array returned from HttpClient - * @return Map of the header names to values - */ - public static Map asHeaderMap(Header[] headers) { - Map m = new HashMap(); - if (headers != null) { - for (Header header : headers) { - m.put(header.getName().toLowerCase(), header.getValue()); - } - } - return m; - } - - /** - * Convert a {@link JSONObject} to a map - * - * @param json - * {@link JSONObject} to convert - * @return Map of the field names to string representations of the values - */ - @SuppressWarnings("rawtypes") public static Map jsonObjectAsMap(JSONObject json) { - if (json == null) - return null; - - Map m = new HashMap(); - for (Iterator iter = json.keys(); iter.hasNext();) { - Object obj = iter.next(); - if (obj != null) { - String key = obj.toString(); - m.put(key, json.optString(key)); - } - } - return m; - } - - /** - * Convert a {@link JSONArray} to a list - * - * @param json - * {@link JSONArray} to convert - * @return List of string representations of the elements - */ - public static List jsonArrayAsList(JSONArray json) { - if (json == null) - return null; - - List l = new ArrayList(); - for (int i = 0; i < json.length(); i++) { - l.add(json.optString(i)); - } - return l; - } - - /** - * Join the elements in arr in to a single string separated by delimiter. - */ - public static String join(String[] arr, String delimiter) { - StringBuffer buf = new StringBuffer(); - if (arr == null || arr.length == 0) - return null; - - buf.append(arr[0]); - for (int i = 1; i < arr.length; i++) { - buf.append(delimiter); - buf.append(arr[i]); - } - return buf.toString(); - } - - /** - * Copies data from an {@link InputStream} to an {@link OutputStream} in - * blocks - * - * @param in - * InputStream to copy - * @param out - * OutputStream to copy to - * @throws IOException - */ - public static void copyStream(InputStream in, OutputStream out) throws IOException { - byte[] buffer = new byte[1024]; - while (true) { - final int readCount = in.read(buffer); - if (readCount == -1) { - break; - } - out.write(buffer, 0, readCount); - } - } - - /** - * Parse a link header into a {@link RiakLink}. See {@link LinkHeader}. - * - * @param header - * The HTTP Link header value. - * @return List of {@link RiakLink} objects constructed from the links in - * header in order. - */ - public static List parseLinkHeader(String header) { - List links = new ArrayList(); - Map> parsedLinks = LinkHeader.parse(header); - for (Entry> e: parsedLinks.entrySet()) { - String url = e.getKey(); - RiakLink link = parseOneLink(url, e.getValue()); - if (link != null) { - links.add(link); - } - } - return links; - } - - /** - * Create a {@link RiakLink} object from a single parsed link from the Link - * header - * - * @param url - * The link URL - * @param params - * The link parameters - * @return {@link RiakLink} object - */ - private static RiakLink parseOneLink(String url, Map params) { - String tag = params.get(Constants.LINK_TAG); - if (tag != null) { - String[] parts = url.split("/"); - if (parts.length >= 2) - return new RiakLink(parts[parts.length - 2], parts[parts.length - 1], tag); - } - return null; - } - - /** - * Extract only the user-specified metadata headers from a header set: all - * headers prefixed with X-Riak-Meta-. The prefix is removed before - * returning. - * - * @param headers - * The full HTTP header set from the response - * @return Map of all headers prefixed with X-Riak-Meta- with prefix - * removed. - */ - public static Map parseUsermeta(Map headers) { - Map usermeta = new HashMap(); - if (headers != null) { - for (Entry e : headers.entrySet()) { - String header = e.getKey(); - if (header != null && header.toLowerCase().startsWith(Constants.HDR_USERMETA_PREFIX)) { - usermeta.put(header.substring(Constants.HDR_USERMETA_PREFIX.length()), e.getValue()); - } - } - } - return usermeta; - } - - /** - * Convert a multipart/mixed document to a list of {@link RiakObject}s. - * - * @param riak - * {@link RiakClient} this object should be associate with, or - * null if none - * @param bucket - * original object's bucket - * @param key - * original object's key - * @param docHeaders - * original document's headers - * @param docBody - * original document's body - * @return List of {@link RiakObject}s represented by the multipart document - */ - public static List parseMultipart(RiakClient riak, String bucket, String key, - Map docHeaders, byte[] docBody) { - - String vclock = null; - boolean siblingVclock = false; - - if (docHeaders != null) { - vclock = docHeaders.get(Constants.HDR_VCLOCK); - if( vclock != null) { - siblingVclock = true; - } - } - - List parts = Multipart.parse(docHeaders, docBody); - List objects = new ArrayList(); - if (parts != null) { - for (Multipart.Part part : parts) { - Map headers = part.getHeaders(); - - // handles the case of link walk multi part responses where the vclock header is in the part not the top response - if (!siblingVclock) { - vclock = headers.get(Constants.HDR_VCLOCK); - } - - if(vclock == null) { - // this should never happen - // exception here to shorten path from bug occurrence - // to bug manifestation - throw new IllegalStateException("no vclock found"); - } - - List links = parseLinkHeader(headers.get(Constants.HDR_LINK)); - Map usermeta = parseUsermeta(headers); - String location = headers.get(Constants.HDR_LOCATION); - String partBucket = bucket; - String partKey = key; - - if (location != null) { - String[] locationParts = location.split("/"); - if (locationParts.length >= 2) { - partBucket = locationParts[locationParts.length - 2]; - partKey = locationParts[locationParts.length - 1]; - } - } - - RiakObject o = new RiakObject(riak, partBucket, partKey, part.getBody(), - headers.get(Constants.HDR_CONTENT_TYPE), links, usermeta, vclock, - headers.get(Constants.HDR_LAST_MODIFIED), headers.get(Constants.HDR_ETAG)); - objects.add(o); - } - } - return objects; - } - - /** - * Throws a checked {@link Exception} not declared in the method signature, - * which can be particularly useful for throwing checked exceptions within a - * {@link RiakExceptionHandler}. Clearly, this circumvents compiler - * safeguards, so use with caution. You've been warned. - * - * @param exception - * A checked (or unchecked) exception to be thrown. - */ - public static void throwChecked(final Throwable exception) { - new CheckedThrower().throwChecked(exception); - } -} - -class CheckedThrower { - @SuppressWarnings("unchecked") public void throwChecked(Throwable exception) throws T { - throw (T) exception; - } -} diff --git a/src/main/java/com/basho/riak/client/util/CollectionWrapper.java b/src/main/java/com/basho/riak/client/util/CollectionWrapper.java deleted file mode 100644 index ac8256c3f..000000000 --- a/src/main/java/com/basho/riak/client/util/CollectionWrapper.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.basho.riak.client.util; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; - -public abstract class CollectionWrapper implements Collection { - - List cache = new ArrayList(); - - /** - * Cache one or more objects from the backend by calling cache(T) - * - * @return true if an object was added to the cache; false otherwise. - */ - abstract protected boolean cacheNext(); - - /** - * Close the backend so no more objects can be read from it (getNext() - * should only return null afterwards). Called by clear(). - */ - abstract protected void closeBackend(); - - /** - * Called by subclasses to add an object to the cache when executing cacheNext(). - */ - protected void cache(T object) { - cache.add(object); - } - - public boolean add(T e) { - return cache.add(e); - } - - public boolean addAll(Collection c) { - return cache.addAll(c); - } - - public void clear() { - cache.clear(); - closeBackend(); - } - - public boolean contains(Object o) { - if (cache.contains(o)) - return true; - - cacheAll(); - return cache.contains(o); - } - - public boolean containsAll(Collection c) { - if (cache.containsAll(c)) - return true; - - cacheAll(); - return cache.containsAll(c); - } - - public boolean isEmpty() { - cacheAll(); - return cache.isEmpty(); - } - - public Iterator iterator() { - return new WrappedCollectionIterator(); - } - - public boolean remove(Object o) { - if (contains(o)) - return cache.remove(o); - - cacheAll(); - return cache.remove(o); - } - - public boolean removeAll(Collection c) { - cacheAll(); - return cache.removeAll(c); - } - - public boolean retainAll(Collection c) { - cacheAll(); - return cache.retainAll(c); - } - - public int size() { - cacheAll(); - return cache.size(); - } - - public Object[] toArray() { - cacheAll(); - return cache.toArray(); - } - - public A[] toArray(A[] a) { - cacheAll(); - return cache.toArray(a); - } - - List getCache() { - return this.cache; - } - - /** - * Reads and caches all the of keys from the input stream - */ - void cacheAll() { - while (cacheNext()) { /* nop */} - } - - class WrappedCollectionIterator implements Iterator { - - int index = 0; - boolean removed = false; - - public boolean hasNext() { - if (index < cache.size()) - return true; - - return cacheNext(); - } - - public T next() { - removed = false; - while (index >= cache.size() && cacheNext()) { /* nop */} - if (index < cache.size()) - return cache.get(index++); - return null; - } - - public void remove() { - if (!removed && (index > 0) && (index <= cache.size())) { - index--; - cache.remove(index); - removed = true; - } - } - - } -} diff --git a/src/main/java/com/basho/riak/client/util/Constants.java b/src/main/java/com/basho/riak/client/util/Constants.java deleted file mode 100644 index 5547c5ccc..000000000 --- a/src/main/java/com/basho/riak/client/util/Constants.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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.util; - -public interface Constants { - - // Default URL path prefixes Riak HTTP interface - public static String RIAK_URL_PREFIX = "/riak"; - - // JSON fields used by Riak - public static String FL_NAME = "name"; - public static String FL_KEYS = "keys"; - public static String FL_SCHEMA = "props"; - public static String FL_SCHEMA_ALLOW_MULT = "allow_mult"; - public static String FL_SCHEMA_CHASHFUN = "chash_keyfun"; - public static String FL_SCHEMA_CHASHFUN_MOD = "mod"; - public static String FL_SCHEMA_CHASHFUN_FUN = "fun"; - public static String FL_SCHEMA_LINKFUN = "linkfun"; - public static String FL_SCHEMA_LINKFUN_MOD = "mod"; - public static String FL_SCHEMA_LINKFUN_FUN = "fun"; - public static String FL_SCHEMA_NVAL = "n_val"; - - // Header directives used by Riak - public static String LINK_TAG = "riaktag"; - - // HTTP headers used in Riak - public static String HDR_ACCEPT = "accept"; - public static String HDR_CLIENT_ID = "x-riak-clientid"; - public static String HDR_CONNECTION = "connection"; - public static String HDR_CONTENT_LENGTH = "content-length"; - public static String HDR_CONTENT_TYPE = "content-type"; - public static String HDR_ETAG = "etag"; - public static String HDR_IF_MATCH = "if-match"; - public static String HDR_IF_MODIFIED_SINCE = "if-modified-since"; - public static String HDR_IF_UNMODIFIED_SINCE = "if-unmodified-since"; - public static String HDR_IF_NONE_MATCH = "if-none-match"; - public static String HDR_LAST_MODIFIED = "last-modified"; - public static String HDR_LINK = "link"; - public static String HDR_LOCATION = "location"; - public static String HDR_VCLOCK = "x-riak-vclock"; - // Declared twice because of Erlang has bizarre HTTP header case handling. - // If a header name is 21 chars or shorteer, it is auto-capitalized between - // dashes. Otherwise, it is passed as is. Therefore, we just make sure this - // headers prefix is correctly capitalized in requests. - public static String HDR_USERMETA_PREFIX = "x-riak-meta-"; - public static String HDR_USERMETA_REQ_PREFIX = "X-Riak-Meta-"; - - // Content types used in Riak - public static String CTYPE_ANY = "*/*"; - public static String CTYPE_JSON = "application/json"; - public static String CTYPE_OCTET_STREAM = "application/octet-stream"; - public static String CTYPE_MULTIPART_MIXED = "multipart/mixed"; - public static String CTYPE_TEXT = "text/plain"; - - // Default r, w, and dw values to use when not specified - public static Integer DEFAULT_R = 2; - public static Integer DEFAULT_W = null; - public static Integer DEFAULT_DW = null; - - // Values for the "keys" query parameter - public static String NO_KEYS = "false"; - public static String INCLUDE_KEYS = "true"; - public static String STREAM_KEYS = "stream"; - - // Query parameters used in Riak - public static String QP_RETURN_BODY = "returnbody"; - public static String QP_R = "r"; - public static String QP_W = "w"; - public static String QP_DW = "dw"; - public static String QP_RW = "rw"; - public static String QP_KEYS = "keys"; - - // HTTP method names - public static String HTTP_HEAD_METHOD = "HEAD"; - public static String HTTP_GET_METHOD = "GET"; - public static String HTTP_PUT_METHOD = "PUT"; - public static String HTTP_DELETE_METHOD = "DELETE"; - - // Riak magic numbers - public static int RIAK_CLIENT_ID_LENGTH = 4; -} diff --git a/src/main/java/com/basho/riak/client/util/LinkHeader.java b/src/main/java/com/basho/riak/client/util/LinkHeader.java deleted file mode 100644 index b6c0a2ae0..000000000 --- a/src/main/java/com/basho/riak/client/util/LinkHeader.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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.util; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Parses the HTTP Link header as described here: - * - * http://tools.ietf.org/html/draft-nottingham-http-link-header - * - * This implementation is more or less a direct port of mnot's Python - * implementation here: - * - * http://gist.github.com/210535 - * - * @author jlee - */ -public class LinkHeader { - - private static String TOKEN = "(?:[^\\(\\)<>@,;:\\\\\"/\\[\\]\\?={} \\t]+?)"; - private static String QUOTED_STRING = "(?:\"(?:\\\\\"|[^\"])*\")"; - private static String PARAMETER = String.format("(?:%s(?:=(?:%s|%s))?)", TOKEN, TOKEN, QUOTED_STRING); - private static String LINK = "<[^>]*>\\s*(?:;\\s*" + PARAMETER + "?\\s*)*"; - private static String COMMA = "(?:\\s*(?:,\\s*)+)"; - private static String SEMICOLON = "(?:\\s*(?:;\\s*)+)"; - private static String LINK_SPLIT = LINK + "(?=" + COMMA + "|\\s*$)"; - private static String PARAM_SPLIT = PARAMETER + "(?=" + SEMICOLON + "|\\s*$)"; - private static Pattern LINK_SPLITTER = Pattern.compile(LINK_SPLIT); - private static Pattern PARAM_SPLITTER = Pattern.compile(PARAM_SPLIT); - - /** - * Returns a map of links to their parameters. Parameters are a map of - * parameter name to value. - * - * @param header - * Value of the Link header in the format described here: - * - * http://tools.ietf.org/html/draft-nottingham-http-link-header - * - * e.g. {@literal ; param="value", - * } - * - * @return A map of links to their parameters. Parameters are a map of - * parameter name to value. - */ - public static Map> parse(String header) { - Map> out = new LinkedHashMap>(); - - if (header == null || header.length() == 0) - return out; - - Matcher m = LINK_SPLITTER.matcher(header); - while (m.find()) { - String link = m.group().trim(); - String[] urlandparams = link.split(">", 2); - String url = urlandparams[0].substring(1); - Map parsedLink = new HashMap(); - - if (urlandparams.length > 1) { - String params = urlandparams[1]; - for (String param : splitParams(params)) { - String[] parts = param.split("=", 2); - if (parts.length > 1) { - parsedLink.put(parts[0].toLowerCase(), ClientUtils.unquoteString(parts[1])); - } else { - parsedLink.put(parts[0].toLowerCase(), null); - } - } - } - out.put(url, parsedLink); - } - - return out; - } - - private static List splitParams(String s) { - - List items = new ArrayList(); - if (s == null || s.length() == 0) - return items; - - Matcher m = PARAM_SPLITTER.matcher(s); - while (m.find()) { - items.add(m.group().trim()); - } - - return items; - } - -} diff --git a/src/main/java/com/basho/riak/client/util/Multipart.java b/src/main/java/com/basho/riak/client/util/Multipart.java deleted file mode 100644 index 029468364..000000000 --- a/src/main/java/com/basho/riak/client/util/Multipart.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * 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.util; - -import org.apache.commons.httpclient.util.EncodingUtil; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.*; - -/** - * Represents a multipart entity as described here: - * - * http://tools.ietf.org/html/rfc2046#section-5.1 - */ -public class Multipart { - - private static byte[] HEADER_DELIM = "\r\n\r\n".getBytes(); - - private static int indexOf(byte[] text, byte[] pattern, int fromIndex) { - if (fromIndex >= text.length || fromIndex < 0) { - throw new IllegalArgumentException("index not within range"); - } - - if (pattern.length == 0) { - throw new IllegalArgumentException("pattern must not be empty"); - } - - byte first = pattern[0]; - int max = text.length - pattern.length; - - for (int i = fromIndex; i <= max; i++) { - if (text[i] != first) { - while (i <= max && text[i] != first) { - i++; - } - } - - if (i <= max) { - int j = i + 1; - int end = j + pattern.length - 1; - for (int k = 1; j < end && text[j] == pattern[k]; j++, k++); - if (j == end) { - return i; - } - } - } - return -1; - } - - /** - * Parses a multipart message or a multipart subpart of a multipart message. - * - * @return A list of the parts parsed into headers and body of this - * multipart message - */ - public static List parse(Map headers, byte[] body) { - if (headers == null || body == null || body.length == 0) - return null; - - - - if (!(body.length >= 2 && body[0] == '\r' && body[1] == '\n')) { - // In order to parse the multipart efficiently, we want to treat the - // first boundary identically to the others, so make sure that the - // first boundary is preceded by a '\r\n' like the others - byte[] newBody = new byte[body.length + 2]; - newBody[0] = '\r'; - newBody[1] = '\n'; - System.arraycopy(body, 0, newBody, 2, body.length); - body = newBody; - } - - String boundary = "\r\n--" + getBoundary(headers.get(Constants.HDR_CONTENT_TYPE)); - byte[] boundaryBytes = boundary.getBytes(); - int boundarySize = boundary.length(); - if ("\r\n--".equals(boundary)) - return null; - - // While this parsing could be more efficiently done in one pass with a - // hand written FSM, hopefully this method is more readable/intuitive. - List parts = new ArrayList(); - int pos = indexOf(body, boundaryBytes, 0); - if (pos != -1) { - while (pos < body.length) { - // first char of part - int start = pos + boundarySize; - // last char of part + 1 - int end = indexOf(body, boundaryBytes, start); - // end of header section + 1 - int headerEnd = indexOf(body, HEADER_DELIM, pos); - // start of body section - int bodyStart = headerEnd + HEADER_DELIM.length; - - // check for end boundary, which is (boundary + "--") - if (body.length >= (start + 2) && body[start] == '-' && body[start+1] == '-') { - break; - } - - if (end == -1) { - end = body.length; - } - - if (headerEnd == -1) { - headerEnd = body.length; - bodyStart = end; - } - - if (bodyStart > end) { - bodyStart = end; - } - - Map partHeaders = parseHeaders(EncodingUtil.getAsciiString(copyOfRange(body, start, headerEnd))); - parts.add(new Part(partHeaders, copyOfRange(body, bodyStart, end))); - - pos = end; - } - } - - return parts; - } - - /** - * A Java6 Arrays.copyOfRange style method locally. - * - * @param original the array to copy - * @param start the start of the copy range - * @param end the end of the copy range - * @return a new array populated with the bytes from original[start] to original[end]. - */ - private static byte[] copyOfRange(byte[] original, int start, int end) { - final byte[] copy = new byte[end-start]; - System.arraycopy(original, start, copy, 0, end-start); - return copy; - } - - /** - * Parse a block of header lines as defined here: - * - * http://tools.ietf.org/html/rfc822#section-3.2 - * - * @param s - * The header blob - * @return Map of header names to values - */ - public static Map parseHeaders(String s) { - // "unfold" header lines (http://tools.ietf.org/html/rfc822#section-3.1) - s.replaceAll("\r\n\\s+", " "); - - String[] headers = s.split("\r\n"); - Map parsedHeaders = new HashMap(); - for (String header : headers) { - // Split header line into name and value - String[] nv = header.split("\\s*:\\s*", 2); - if (nv.length > 1) { - parsedHeaders.put(nv[0].trim().toLowerCase(), nv[1].trim()); - } - } - return parsedHeaders; - } - - /** - * Given a content type value, get the "boundary" parameter - * - * @param contentType - * Content type value with boundary parameter. Should be of the - * form "type/subtype; boundary=foobar; param=value" - * @return Value of the boundary parameter - */ - public static String getBoundary(String contentType) { - String[] params = contentType.split("\\s*;\\s*"); - for (String param : params) { - String[] nv = param.split("\\s*=\\s*", 2); - if (nv.length > 1) { - if ("boundary".equals(nv[0].toLowerCase())) - return ClientUtils.unquoteString(nv[1]); - } - } - return ""; - } - - /** - * A single part of a multipart entity - */ - public static class Part { - private Map headers; - private byte[] body = null; - private InputStream stream; - - public Part(Map headers, byte[] body) { - this.headers = headers; - if(body != null) { - this.body = body.clone(); - } - } - - public Part(Map headers, InputStream body) { - this.headers = headers; - stream = body; - } - - /** - * Headers defined in the part - */ - public Map getHeaders() { - return headers; - } - - /** - * Body of this part - */ - public byte[] getBody() { - if (body == null && stream != null) { - try { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - for (int readCount = 0; readCount != -1; readCount = stream.read(buffer)) { - os.write(buffer, 0, readCount); - } - body = os.toByteArray(); - } catch (IOException e) { /* nop */} - stream = null; - } - return body; - } - - public String getBodyAsString() { - byte[] body = getBody(); - if (body == null) - return null; - return new String(body); - } - - public InputStream getStream() { - if (stream == null && body != null) { - stream = new ByteArrayInputStream(body); - } - - return stream; - } - } -} diff --git a/src/main/java/com/basho/riak/client/util/OneTokenInputStream.java b/src/main/java/com/basho/riak/client/util/OneTokenInputStream.java deleted file mode 100644 index da2a0b39b..000000000 --- a/src/main/java/com/basho/riak/client/util/OneTokenInputStream.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.basho.riak.client.util; - -import java.io.IOException; -import java.io.InputStream; - -/** - * A wrapper that reads a single element an underlying {@link InputStream} - * containing contains a delimited list - * - * @author jlee - */ -public class OneTokenInputStream extends InputStream { - - int maxBufferLen; - InputStream impl; - boolean eof = false; - int pos = 0; - int dataLen = 0; - int bufOffset = 0; - StringBuilder buf = null; - String delimiter; - - public OneTokenInputStream(InputStream in, String delimiter) { - impl = in; - maxBufferLen = Math.max(1024, delimiter.length() * 2); - this.delimiter = delimiter; - } - - @Override public int read() throws IOException { - while (!eof && pos >= dataLen) { - buffer(); - } - if (pos >= dataLen) - return -1; - - char c = buf.charAt(pos - bufOffset); - pos++; - return c; - } - - @Override public void close() throws IOException { - impl.close(); - impl = null; - eof = true; - } - - public boolean done() { - return eof; - } - - private void buffer() throws IOException { - if (eof) - return; - - if (buf == null) { - initBuffer(); - } - if (!eof) { - int c = impl.read(); - if (c == -1) { - dataLen = buf.length(); - eof = true; - } else { - buf.append((char) c); - if (buf.length() > maxBufferLen) { - bufOffset += buf.length() - delimiter.length(); - buf.delete(0, buf.length() - delimiter.length()); - } - if (buf.indexOf(delimiter) >= 0) { - eof = true; - } else { - dataLen++; - } - } - } - } - - private void initBuffer() throws IOException { - byte[] headStart = new byte[delimiter.length() - 1]; - int offset = 0; - while (offset < headStart.length) { - int bytesRead = impl.read(headStart, offset, headStart.length - offset); - if (bytesRead == -1) { - eof = true; - return; - } else { - offset += bytesRead; - } - } - buf = new StringBuilder(new String(headStart)); - } -} diff --git a/src/main/java/com/basho/riak/client/util/StreamedMultipart.java b/src/main/java/com/basho/riak/client/util/StreamedMultipart.java deleted file mode 100644 index 39f2182a9..000000000 --- a/src/main/java/com/basho/riak/client/util/StreamedMultipart.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.basho.riak.client.util; - -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.util.Iterator; -import java.util.Map; - -import com.basho.riak.client.util.Multipart.Part; - -public class StreamedMultipart implements Iterator { - - Map headers = null; - BranchableInputStream stream; - String boundary; - boolean foundNext = false; - BranchableInputStream currentPartStream = null; - - /** - * Parses a multipart message or a multipart subpart of a multipart message. - * Each parts of the message is parsed into a map of headers and the body as - * an InputStream. stream is not consumed until the return value is iterated - * over. It is consumed as each part is encountered. A part's body - * InputStream does not need to be consumed before proceed to the next part. - * If it is not consumed, it will be buffered in memory and accessible - * later. - * - * @param headers - * The headers from the original message, which contains the - * Content-Type header including the boundary string - * @param stream - * The input stream to read from - * @throws IOException - * There was a communication error reading the input stream while - * looking for the initial boundary - * @throws EOFException - * The initial boundary was not found - */ - public StreamedMultipart(Map headers, InputStream stream) throws IOException, EOFException { - if (headers == null || stream == null) - throw new IllegalArgumentException(); - - String initialBoundary = "--" + Multipart.getBoundary(headers.get(Constants.HDR_CONTENT_TYPE)); - String boundary = "\r\n" + initialBoundary; - - // Find the first boundary, ignoring everything preceding it - StringBuilder sb = new StringBuilder(); - while (true) { - int c = stream.read(); - if (c == -1) - throw new EOFException(); - sb.append((char) c); - if ((sb.length() == initialBoundary.length() && initialBoundary.equals(sb.toString())) || - (sb.indexOf(boundary, sb.length() - boundary.length()) >= 0)) { - finishReadingLine(stream); - break; - } - } - - this.headers = headers; - this.boundary = boundary; - this.stream = new BranchableInputStream(new OneTokenInputStream(stream, boundary + "--")); - } - - private void finishReadingLine(InputStream in) throws IOException { - while (true) { - int c = in.read(); - if (c == -1 || c == '\n') - break; - } - } - - /** - * Return the map of document headers that this object was constructed with. - */ - public Map getHeaders() { - return headers; - } - - /** - * See {@link Iterator#hasNext()}. - * - * @throws RuntimeException - * (IOException) if there is an error reading the next part from - * the input stream - */ - public boolean hasNext() { - if (foundNext) - return true; - - try { - foundNext = findNext(); - } catch (IOException e) { - throw new RuntimeException(e); - } - return foundNext; - } - - /** - * See {@link Iterator#next()}. - * - * @throws RuntimeException - * (IOException) if there is an error reading the next part from - * the input stream - */ - public Part next() { - if (!hasNext()) - return null; - foundNext = false; - - String headerBlock = null; - try { - headerBlock = readHeaderBlock(currentPartStream); - } catch (IOException e) { - throw new RuntimeException(e); - } - - Map headers = Multipart.parseHeaders(headerBlock); - return new Part(headers, currentPartStream.branch()); - } - - public void remove() { /* nop */} - - /** - * Move this.currentPartStream to the next part in the entity - * - * @return true if there is another part was found - * - * @throws IOException - */ - private boolean findNext() throws IOException { - if (currentPartStream != null) { - InputStream is = currentPartStream.branch(); - try { - while (is.read() != -1) { /* nop */} // advance stream to end of next boundary - finishReadingLine(stream); // and consume the rest of the boundary line including the newline - } catch (IOException e) { - throw new RuntimeException(e); - } - } - if (stream.peek() != -1) { - currentPartStream = new BranchableInputStream(new OneTokenInputStream(stream.branch(), boundary)); - return true; - } - return false; - } - - /** - * Read in the header block from a stream (i.e. everything before and - * including the first empty line) - * - * @param in - * Stream to read header block from - * - * @return String containing the header block - */ - private String readHeaderBlock(InputStream in) throws IOException { - StringBuilder headers = new StringBuilder(); - boolean currentLineEmpty = true; - while (true) { - int c = in.read(); - if (c == -1 || (currentLineEmpty && c == '\n')) { - break; - } else if (c == '\n') { - currentLineEmpty = true; - } else if (c != '\r' && c != '\n') { - currentLineEmpty = false; - } - headers.append((char) c); - } - return headers.toString(); - } -} \ No newline at end of file diff --git a/src/main/java/com/basho/riak/pbc/BucketProperties.java b/src/main/java/com/basho/riak/pbc/BucketProperties.java deleted file mode 100644 index fd9a2d9f7..000000000 --- a/src/main/java/com/basho/riak/pbc/BucketProperties.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.basho.riak.pbc; - -import com.basho.riak.pbc.RPB.RpbBucketProps; -import com.basho.riak.pbc.RPB.RpbGetBucketResp; -import com.basho.riak.pbc.RPB.RpbBucketProps.Builder; - -public class BucketProperties { - - private Boolean allowMult; - private Integer nValue; - - public void init(RpbGetBucketResp resp) { - if (resp.hasProps()) { - - RpbBucketProps props = resp.getProps(); - if (props.hasAllowMult()) { - allowMult = Boolean.valueOf(props.getAllowMult()); - } - if (props.hasNVal()) { - nValue = new Integer(props.getNVal()); - } - } - } - - public Boolean getAllowMult() { - return allowMult; - } - - public Integer getNValue() { - return nValue; - } - - public BucketProperties allowMult(boolean val) { - this.allowMult = val; - return this; - } - - public BucketProperties nValue(int val) { - this.nValue = val; - return this; - } - - RpbBucketProps build() { - Builder builder = RpbBucketProps.newBuilder(); - if (allowMult != null) { - builder.setAllowMult(allowMult); - } - if (nValue != null) { - builder.setNVal(nValue); - } - return builder.build(); - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((allowMult == null) ? 0 : allowMult.hashCode()); - result = prime * result + ((nValue == null) ? 0 : nValue.hashCode()); - return result; - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof BucketProperties)) { - return false; - } - BucketProperties other = (BucketProperties) obj; - if (allowMult == null) { - if (other.allowMult != null) { - return false; - } - } else if (!allowMult.equals(other.allowMult)) { - return false; - } - if (nValue == null) { - if (other.nValue != null) { - return false; - } - } else if (!nValue.equals(other.nValue)) { - return false; - } - return true; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override public String toString() { - return String.format("BucketProperties [allowMult=%s, nValue=%s]", allowMult, nValue); - } - -} diff --git a/src/main/java/com/basho/riak/pbc/IRequestMeta.java b/src/main/java/com/basho/riak/pbc/IRequestMeta.java deleted file mode 100644 index 47026843c..000000000 --- a/src/main/java/com/basho/riak/pbc/IRequestMeta.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.basho.riak.pbc; - -import com.google.protobuf.ByteString; - -public interface IRequestMeta { - - public abstract void preparePut(RPB.RpbPutReq.Builder builder); - - public abstract IRequestMeta returnBody(boolean ret); - - public abstract IRequestMeta w(int w); - - public abstract IRequestMeta dw(int dw); - - public abstract IRequestMeta contentType(String contentType); - - public abstract ByteString getContentType(); - -} \ No newline at end of file diff --git a/src/main/java/com/basho/riak/pbc/KeySource.java b/src/main/java/com/basho/riak/pbc/KeySource.java deleted file mode 100644 index 3592b557b..000000000 --- a/src/main/java/com/basho/riak/pbc/KeySource.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * This file is part of riak-java-pb-client - * - * Copyright (c) 2010 by Trifork - * - * 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.pbc; - -import java.io.IOException; -import java.util.NoSuchElementException; - -import com.basho.riak.pbc.RPB.RpbListKeysResp; -import com.google.protobuf.ByteString; - -public class KeySource extends RiakStreamClient { - - private RpbListKeysResp r; - private int i; - - public KeySource(RiakClient client, RiakConnection conn) throws IOException { - super(client, conn); - get_next_response(); - } - - public boolean hasNext() throws IOException { - if (isClosed()) { - return false; - } - - if (r_is_exhausted()) { - get_next_response(); - } - - return !isClosed(); - } - - private boolean r_is_exhausted() { - return i == r.getKeysCount(); - } - - public ByteString next() throws IOException { - if (!hasNext()) - throw new NoSuchElementException(); - return r.getKeys(i++); - } - - private void get_next_response() throws IOException { - if (isClosed()) - return; - - // either we're in the first call (r == null) - // or we got here because we ran out of keys. - assert r == null || r_is_exhausted(); - - do { - - if (r != null) { - if (r.hasDone() && r.getDone()) { - close(); - return; - } - } - - byte[] data = conn.receive(RiakMessageCodes.MSG_ListKeysResp); - if (data == null) { - close(); - throw new IOException("received empty response"); - } - - r = RPB.RpbListKeysResp.parseFrom(data); - i = 0; - - // did we got an empty chunk! get another one. - } while (r.getKeysCount() == 0); - } - -} diff --git a/src/main/java/com/basho/riak/pbc/MapReduceResponseSource.java b/src/main/java/com/basho/riak/pbc/MapReduceResponseSource.java deleted file mode 100644 index ee1bdf51f..000000000 --- a/src/main/java/com/basho/riak/pbc/MapReduceResponseSource.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * This file is part of riak-java-pb-client - * - * Copyright (c) 2010 by Trifork - * - * 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.pbc; - -import java.io.IOException; -import java.util.NoSuchElementException; - -import com.basho.riak.pbc.RPB.RpbMapRedResp; -import com.basho.riak.pbc.mapreduce.MapReduceResponse; -import com.google.protobuf.ByteString; - -public class MapReduceResponseSource extends - RiakStreamClient { - - private RpbMapRedResp r; - private boolean is_given; - private final ByteString contentType; - - protected MapReduceResponseSource(RiakClient client, RiakConnection conn, - ByteString contentType) throws IOException { - super(client, conn); - this.contentType = contentType; - get_next_response(); - } - - @Override - public boolean hasNext() throws IOException { - if (isClosed()) { - return false; - } - - if (is_given) { - get_next_response(); - } - - return !isClosed(); - } - - @Override - public MapReduceResponse next() throws IOException { - if (!hasNext()) - throw new NoSuchElementException(); - is_given = true; - return new MapReduceResponse(r, contentType); - } - - private void get_next_response() throws IOException { - if (isClosed()) - return; - - // either we're in the first call (r == null) - // or we got here because we gave the reply away. - assert r == null || is_given; - - if (r != null && is_given) { - if (r.hasDone() && r.getDone()) { - close(); - return; - } - } - - byte[] data = conn.receive(RiakMessageCodes.MSG_MapRedResp); - if (data == null) { - close(); - throw new IOException("received empty response"); - } - - r = RPB.RpbMapRedResp.parseFrom(data); - is_given = false; - - } - -} diff --git a/src/main/java/com/basho/riak/pbc/RPB.java b/src/main/java/com/basho/riak/pbc/RPB.java deleted file mode 100644 index f268a312e..000000000 --- a/src/main/java/com/basho/riak/pbc/RPB.java +++ /dev/null @@ -1,7979 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: src/riakclient.proto - -package com.basho.riak.pbc; - -public final class RPB { - private RPB() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - } - public static final class RpbErrorResp extends - com.google.protobuf.GeneratedMessage { - // Use RpbErrorResp.newBuilder() to construct. - private RpbErrorResp() { - initFields(); - } - private RpbErrorResp(boolean noInit) {} - - private static final RpbErrorResp defaultInstance; - public static RpbErrorResp getDefaultInstance() { - return defaultInstance; - } - - public RpbErrorResp getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbErrorResp_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbErrorResp_fieldAccessorTable; - } - - // required bytes errmsg = 1; - public static final int ERRMSG_FIELD_NUMBER = 1; - private boolean hasErrmsg; - private com.google.protobuf.ByteString errmsg_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasErrmsg() { return hasErrmsg; } - public com.google.protobuf.ByteString getErrmsg() { return errmsg_; } - - // required uint32 errcode = 2; - public static final int ERRCODE_FIELD_NUMBER = 2; - private boolean hasErrcode; - private int errcode_ = 0; - public boolean hasErrcode() { return hasErrcode; } - public int getErrcode() { return errcode_; } - - private void initFields() { - } - public final boolean isInitialized() { - if (!hasErrmsg) return false; - if (!hasErrcode) return false; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (hasErrmsg()) { - output.writeBytes(1, getErrmsg()); - } - if (hasErrcode()) { - output.writeUInt32(2, getErrcode()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (hasErrmsg()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getErrmsg()); - } - if (hasErrcode()) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(2, getErrcode()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbErrorResp parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbErrorResp parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbErrorResp parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbErrorResp parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbErrorResp parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbErrorResp parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbErrorResp parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbErrorResp parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbErrorResp parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbErrorResp parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbErrorResp prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbErrorResp result; - - // Construct using com.trifork.riak.RPB.RpbErrorResp.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbErrorResp(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbErrorResp internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbErrorResp(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbErrorResp.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbErrorResp getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbErrorResp.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbErrorResp build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbErrorResp buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbErrorResp buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - com.basho.riak.pbc.RPB.RpbErrorResp returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbErrorResp) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbErrorResp)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbErrorResp other) { - if (other == com.basho.riak.pbc.RPB.RpbErrorResp.getDefaultInstance()) return this; - if (other.hasErrmsg()) { - setErrmsg(other.getErrmsg()); - } - if (other.hasErrcode()) { - setErrcode(other.getErrcode()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - setErrmsg(input.readBytes()); - break; - } - case 16: { - setErrcode(input.readUInt32()); - break; - } - } - } - } - - - // required bytes errmsg = 1; - public boolean hasErrmsg() { - return result.hasErrmsg(); - } - public com.google.protobuf.ByteString getErrmsg() { - return result.getErrmsg(); - } - public Builder setErrmsg(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasErrmsg = true; - result.errmsg_ = value; - return this; - } - public Builder clearErrmsg() { - result.hasErrmsg = false; - result.errmsg_ = getDefaultInstance().getErrmsg(); - return this; - } - - // required uint32 errcode = 2; - public boolean hasErrcode() { - return result.hasErrcode(); - } - public int getErrcode() { - return result.getErrcode(); - } - public Builder setErrcode(int value) { - result.hasErrcode = true; - result.errcode_ = value; - return this; - } - public Builder clearErrcode() { - result.hasErrcode = false; - result.errcode_ = 0; - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbErrorResp) - } - - static { - defaultInstance = new RpbErrorResp(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbErrorResp) - } - - public static final class RpbGetClientIdResp extends - com.google.protobuf.GeneratedMessage { - // Use RpbGetClientIdResp.newBuilder() to construct. - private RpbGetClientIdResp() { - initFields(); - } - private RpbGetClientIdResp(boolean noInit) {} - - private static final RpbGetClientIdResp defaultInstance; - public static RpbGetClientIdResp getDefaultInstance() { - return defaultInstance; - } - - public RpbGetClientIdResp getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbGetClientIdResp_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbGetClientIdResp_fieldAccessorTable; - } - - // required bytes client_id = 1; - public static final int CLIENT_ID_FIELD_NUMBER = 1; - private boolean hasClientId; - private com.google.protobuf.ByteString clientId_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasClientId() { return hasClientId; } - public com.google.protobuf.ByteString getClientId() { return clientId_; } - - private void initFields() { - } - public final boolean isInitialized() { - if (!hasClientId) return false; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (hasClientId()) { - output.writeBytes(1, getClientId()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (hasClientId()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getClientId()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbGetClientIdResp parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetClientIdResp parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetClientIdResp parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetClientIdResp parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetClientIdResp parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetClientIdResp parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetClientIdResp parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbGetClientIdResp parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbGetClientIdResp parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetClientIdResp parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbGetClientIdResp prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbGetClientIdResp result; - - // Construct using com.trifork.riak.RPB.RpbGetClientIdResp.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbGetClientIdResp(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbGetClientIdResp internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbGetClientIdResp(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbGetClientIdResp.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbGetClientIdResp getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbGetClientIdResp.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbGetClientIdResp build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbGetClientIdResp buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbGetClientIdResp buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - com.basho.riak.pbc.RPB.RpbGetClientIdResp returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbGetClientIdResp) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbGetClientIdResp)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbGetClientIdResp other) { - if (other == com.basho.riak.pbc.RPB.RpbGetClientIdResp.getDefaultInstance()) return this; - if (other.hasClientId()) { - setClientId(other.getClientId()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - setClientId(input.readBytes()); - break; - } - } - } - } - - - // required bytes client_id = 1; - public boolean hasClientId() { - return result.hasClientId(); - } - public com.google.protobuf.ByteString getClientId() { - return result.getClientId(); - } - public Builder setClientId(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasClientId = true; - result.clientId_ = value; - return this; - } - public Builder clearClientId() { - result.hasClientId = false; - result.clientId_ = getDefaultInstance().getClientId(); - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbGetClientIdResp) - } - - static { - defaultInstance = new RpbGetClientIdResp(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbGetClientIdResp) - } - - public static final class RpbSetClientIdReq extends - com.google.protobuf.GeneratedMessage { - // Use RpbSetClientIdReq.newBuilder() to construct. - private RpbSetClientIdReq() { - initFields(); - } - private RpbSetClientIdReq(boolean noInit) {} - - private static final RpbSetClientIdReq defaultInstance; - public static RpbSetClientIdReq getDefaultInstance() { - return defaultInstance; - } - - public RpbSetClientIdReq getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbSetClientIdReq_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbSetClientIdReq_fieldAccessorTable; - } - - // required bytes client_id = 1; - public static final int CLIENT_ID_FIELD_NUMBER = 1; - private boolean hasClientId; - private com.google.protobuf.ByteString clientId_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasClientId() { return hasClientId; } - public com.google.protobuf.ByteString getClientId() { return clientId_; } - - private void initFields() { - } - public final boolean isInitialized() { - if (!hasClientId) return false; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (hasClientId()) { - output.writeBytes(1, getClientId()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (hasClientId()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getClientId()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbSetClientIdReq parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbSetClientIdReq parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbSetClientIdReq parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbSetClientIdReq parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbSetClientIdReq parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbSetClientIdReq parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbSetClientIdReq parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbSetClientIdReq parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbSetClientIdReq parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbSetClientIdReq parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbSetClientIdReq prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbSetClientIdReq result; - - // Construct using com.trifork.riak.RPB.RpbSetClientIdReq.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbSetClientIdReq(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbSetClientIdReq internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbSetClientIdReq(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbSetClientIdReq.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbSetClientIdReq getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbSetClientIdReq.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbSetClientIdReq build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbSetClientIdReq buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbSetClientIdReq buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - com.basho.riak.pbc.RPB.RpbSetClientIdReq returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbSetClientIdReq) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbSetClientIdReq)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbSetClientIdReq other) { - if (other == com.basho.riak.pbc.RPB.RpbSetClientIdReq.getDefaultInstance()) return this; - if (other.hasClientId()) { - setClientId(other.getClientId()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - setClientId(input.readBytes()); - break; - } - } - } - } - - - // required bytes client_id = 1; - public boolean hasClientId() { - return result.hasClientId(); - } - public com.google.protobuf.ByteString getClientId() { - return result.getClientId(); - } - public Builder setClientId(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasClientId = true; - result.clientId_ = value; - return this; - } - public Builder clearClientId() { - result.hasClientId = false; - result.clientId_ = getDefaultInstance().getClientId(); - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbSetClientIdReq) - } - - static { - defaultInstance = new RpbSetClientIdReq(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbSetClientIdReq) - } - - public static final class RpbGetServerInfoResp extends - com.google.protobuf.GeneratedMessage { - // Use RpbGetServerInfoResp.newBuilder() to construct. - private RpbGetServerInfoResp() { - initFields(); - } - private RpbGetServerInfoResp(boolean noInit) {} - - private static final RpbGetServerInfoResp defaultInstance; - public static RpbGetServerInfoResp getDefaultInstance() { - return defaultInstance; - } - - public RpbGetServerInfoResp getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbGetServerInfoResp_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbGetServerInfoResp_fieldAccessorTable; - } - - // optional bytes node = 1; - public static final int NODE_FIELD_NUMBER = 1; - private boolean hasNode; - private com.google.protobuf.ByteString node_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasNode() { return hasNode; } - public com.google.protobuf.ByteString getNode() { return node_; } - - // optional bytes server_version = 2; - public static final int SERVER_VERSION_FIELD_NUMBER = 2; - private boolean hasServerVersion; - private com.google.protobuf.ByteString serverVersion_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasServerVersion() { return hasServerVersion; } - public com.google.protobuf.ByteString getServerVersion() { return serverVersion_; } - - private void initFields() { - } - public final boolean isInitialized() { - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (hasNode()) { - output.writeBytes(1, getNode()); - } - if (hasServerVersion()) { - output.writeBytes(2, getServerVersion()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (hasNode()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getNode()); - } - if (hasServerVersion()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getServerVersion()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbGetServerInfoResp parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetServerInfoResp parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetServerInfoResp parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetServerInfoResp parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetServerInfoResp parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetServerInfoResp parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetServerInfoResp parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbGetServerInfoResp parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbGetServerInfoResp parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetServerInfoResp parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbGetServerInfoResp prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbGetServerInfoResp result; - - // Construct using com.trifork.riak.RPB.RpbGetServerInfoResp.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbGetServerInfoResp(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbGetServerInfoResp internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbGetServerInfoResp(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbGetServerInfoResp.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbGetServerInfoResp getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbGetServerInfoResp.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbGetServerInfoResp build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbGetServerInfoResp buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbGetServerInfoResp buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - com.basho.riak.pbc.RPB.RpbGetServerInfoResp returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbGetServerInfoResp) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbGetServerInfoResp)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbGetServerInfoResp other) { - if (other == com.basho.riak.pbc.RPB.RpbGetServerInfoResp.getDefaultInstance()) return this; - if (other.hasNode()) { - setNode(other.getNode()); - } - if (other.hasServerVersion()) { - setServerVersion(other.getServerVersion()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - setNode(input.readBytes()); - break; - } - case 18: { - setServerVersion(input.readBytes()); - break; - } - } - } - } - - - // optional bytes node = 1; - public boolean hasNode() { - return result.hasNode(); - } - public com.google.protobuf.ByteString getNode() { - return result.getNode(); - } - public Builder setNode(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasNode = true; - result.node_ = value; - return this; - } - public Builder clearNode() { - result.hasNode = false; - result.node_ = getDefaultInstance().getNode(); - return this; - } - - // optional bytes server_version = 2; - public boolean hasServerVersion() { - return result.hasServerVersion(); - } - public com.google.protobuf.ByteString getServerVersion() { - return result.getServerVersion(); - } - public Builder setServerVersion(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasServerVersion = true; - result.serverVersion_ = value; - return this; - } - public Builder clearServerVersion() { - result.hasServerVersion = false; - result.serverVersion_ = getDefaultInstance().getServerVersion(); - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbGetServerInfoResp) - } - - static { - defaultInstance = new RpbGetServerInfoResp(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbGetServerInfoResp) - } - - public static final class RpbGetReq extends - com.google.protobuf.GeneratedMessage { - // Use RpbGetReq.newBuilder() to construct. - private RpbGetReq() { - initFields(); - } - private RpbGetReq(boolean noInit) {} - - private static final RpbGetReq defaultInstance; - public static RpbGetReq getDefaultInstance() { - return defaultInstance; - } - - public RpbGetReq getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbGetReq_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbGetReq_fieldAccessorTable; - } - - // required bytes bucket = 1; - public static final int BUCKET_FIELD_NUMBER = 1; - private boolean hasBucket; - private com.google.protobuf.ByteString bucket_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasBucket() { return hasBucket; } - public com.google.protobuf.ByteString getBucket() { return bucket_; } - - // required bytes key = 2; - public static final int KEY_FIELD_NUMBER = 2; - private boolean hasKey; - private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasKey() { return hasKey; } - public com.google.protobuf.ByteString getKey() { return key_; } - - // optional uint32 r = 3; - public static final int R_FIELD_NUMBER = 3; - private boolean hasR; - private int r_ = 0; - public boolean hasR() { return hasR; } - public int getR() { return r_; } - - private void initFields() { - } - public final boolean isInitialized() { - if (!hasBucket) return false; - if (!hasKey) return false; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (hasBucket()) { - output.writeBytes(1, getBucket()); - } - if (hasKey()) { - output.writeBytes(2, getKey()); - } - if (hasR()) { - output.writeUInt32(3, getR()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (hasBucket()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getBucket()); - } - if (hasKey()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getKey()); - } - if (hasR()) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(3, getR()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbGetReq parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetReq parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetReq parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetReq parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetReq parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetReq parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetReq parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbGetReq parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbGetReq parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetReq parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbGetReq prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbGetReq result; - - // Construct using com.trifork.riak.RPB.RpbGetReq.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbGetReq(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbGetReq internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbGetReq(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbGetReq.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbGetReq getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbGetReq.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbGetReq build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbGetReq buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbGetReq buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - com.basho.riak.pbc.RPB.RpbGetReq returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbGetReq) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbGetReq)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbGetReq other) { - if (other == com.basho.riak.pbc.RPB.RpbGetReq.getDefaultInstance()) return this; - if (other.hasBucket()) { - setBucket(other.getBucket()); - } - if (other.hasKey()) { - setKey(other.getKey()); - } - if (other.hasR()) { - setR(other.getR()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - setBucket(input.readBytes()); - break; - } - case 18: { - setKey(input.readBytes()); - break; - } - case 24: { - setR(input.readUInt32()); - break; - } - } - } - } - - - // required bytes bucket = 1; - public boolean hasBucket() { - return result.hasBucket(); - } - public com.google.protobuf.ByteString getBucket() { - return result.getBucket(); - } - public Builder setBucket(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasBucket = true; - result.bucket_ = value; - return this; - } - public Builder clearBucket() { - result.hasBucket = false; - result.bucket_ = getDefaultInstance().getBucket(); - return this; - } - - // required bytes key = 2; - public boolean hasKey() { - return result.hasKey(); - } - public com.google.protobuf.ByteString getKey() { - return result.getKey(); - } - public Builder setKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasKey = true; - result.key_ = value; - return this; - } - public Builder clearKey() { - result.hasKey = false; - result.key_ = getDefaultInstance().getKey(); - return this; - } - - // optional uint32 r = 3; - public boolean hasR() { - return result.hasR(); - } - public int getR() { - return result.getR(); - } - public Builder setR(int value) { - result.hasR = true; - result.r_ = value; - return this; - } - public Builder clearR() { - result.hasR = false; - result.r_ = 0; - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbGetReq) - } - - static { - defaultInstance = new RpbGetReq(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbGetReq) - } - - public static final class RpbGetResp extends - com.google.protobuf.GeneratedMessage { - // Use RpbGetResp.newBuilder() to construct. - private RpbGetResp() { - initFields(); - } - private RpbGetResp(boolean noInit) {} - - private static final RpbGetResp defaultInstance; - public static RpbGetResp getDefaultInstance() { - return defaultInstance; - } - - public RpbGetResp getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbGetResp_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbGetResp_fieldAccessorTable; - } - - // repeated .RpbContent content = 1; - public static final int CONTENT_FIELD_NUMBER = 1; - private java.util.List content_ = - java.util.Collections.emptyList(); - public java.util.List getContentList() { - return content_; - } - public int getContentCount() { return content_.size(); } - public com.basho.riak.pbc.RPB.RpbContent getContent(int index) { - return content_.get(index); - } - - // optional bytes vclock = 2; - public static final int VCLOCK_FIELD_NUMBER = 2; - private boolean hasVclock; - private com.google.protobuf.ByteString vclock_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasVclock() { return hasVclock; } - public com.google.protobuf.ByteString getVclock() { return vclock_; } - - private void initFields() { - } - public final boolean isInitialized() { - for (com.basho.riak.pbc.RPB.RpbContent element : getContentList()) { - if (!element.isInitialized()) return false; - } - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - for (com.basho.riak.pbc.RPB.RpbContent element : getContentList()) { - output.writeMessage(1, element); - } - if (hasVclock()) { - output.writeBytes(2, getVclock()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - for (com.basho.riak.pbc.RPB.RpbContent element : getContentList()) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(1, element); - } - if (hasVclock()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getVclock()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbGetResp parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetResp parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetResp parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetResp parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetResp parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetResp parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetResp parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbGetResp parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbGetResp parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetResp parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbGetResp prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbGetResp result; - - // Construct using com.trifork.riak.RPB.RpbGetResp.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbGetResp(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbGetResp internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbGetResp(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbGetResp.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbGetResp getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbGetResp.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbGetResp build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbGetResp buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbGetResp buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - if (result.content_ != java.util.Collections.EMPTY_LIST) { - result.content_ = - java.util.Collections.unmodifiableList(result.content_); - } - com.basho.riak.pbc.RPB.RpbGetResp returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbGetResp) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbGetResp)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbGetResp other) { - if (other == com.basho.riak.pbc.RPB.RpbGetResp.getDefaultInstance()) return this; - if (!other.content_.isEmpty()) { - if (result.content_.isEmpty()) { - result.content_ = new java.util.ArrayList(); - } - result.content_.addAll(other.content_); - } - if (other.hasVclock()) { - setVclock(other.getVclock()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - com.basho.riak.pbc.RPB.RpbContent.Builder subBuilder = com.basho.riak.pbc.RPB.RpbContent.newBuilder(); - input.readMessage(subBuilder, extensionRegistry); - addContent(subBuilder.buildPartial()); - break; - } - case 18: { - setVclock(input.readBytes()); - break; - } - } - } - } - - - // repeated .RpbContent content = 1; - public java.util.List getContentList() { - return java.util.Collections.unmodifiableList(result.content_); - } - public int getContentCount() { - return result.getContentCount(); - } - public com.basho.riak.pbc.RPB.RpbContent getContent(int index) { - return result.getContent(index); - } - public Builder setContent(int index, com.basho.riak.pbc.RPB.RpbContent value) { - if (value == null) { - throw new NullPointerException(); - } - result.content_.set(index, value); - return this; - } - public Builder setContent(int index, com.basho.riak.pbc.RPB.RpbContent.Builder builderForValue) { - result.content_.set(index, builderForValue.build()); - return this; - } - public Builder addContent(com.basho.riak.pbc.RPB.RpbContent value) { - if (value == null) { - throw new NullPointerException(); - } - if (result.content_.isEmpty()) { - result.content_ = new java.util.ArrayList(); - } - result.content_.add(value); - return this; - } - public Builder addContent(com.basho.riak.pbc.RPB.RpbContent.Builder builderForValue) { - if (result.content_.isEmpty()) { - result.content_ = new java.util.ArrayList(); - } - result.content_.add(builderForValue.build()); - return this; - } - public Builder addAllContent( - java.lang.Iterable values) { - if (result.content_.isEmpty()) { - result.content_ = new java.util.ArrayList(); - } - super.addAll(values, result.content_); - return this; - } - public Builder clearContent() { - result.content_ = java.util.Collections.emptyList(); - return this; - } - - // optional bytes vclock = 2; - public boolean hasVclock() { - return result.hasVclock(); - } - public com.google.protobuf.ByteString getVclock() { - return result.getVclock(); - } - public Builder setVclock(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasVclock = true; - result.vclock_ = value; - return this; - } - public Builder clearVclock() { - result.hasVclock = false; - result.vclock_ = getDefaultInstance().getVclock(); - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbGetResp) - } - - static { - defaultInstance = new RpbGetResp(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbGetResp) - } - - public static final class RpbPutReq extends - com.google.protobuf.GeneratedMessage { - // Use RpbPutReq.newBuilder() to construct. - private RpbPutReq() { - initFields(); - } - private RpbPutReq(boolean noInit) {} - - private static final RpbPutReq defaultInstance; - public static RpbPutReq getDefaultInstance() { - return defaultInstance; - } - - public RpbPutReq getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbPutReq_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbPutReq_fieldAccessorTable; - } - - // required bytes bucket = 1; - public static final int BUCKET_FIELD_NUMBER = 1; - private boolean hasBucket; - private com.google.protobuf.ByteString bucket_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasBucket() { return hasBucket; } - public com.google.protobuf.ByteString getBucket() { return bucket_; } - - // required bytes key = 2; - public static final int KEY_FIELD_NUMBER = 2; - private boolean hasKey; - private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasKey() { return hasKey; } - public com.google.protobuf.ByteString getKey() { return key_; } - - // optional bytes vclock = 3; - public static final int VCLOCK_FIELD_NUMBER = 3; - private boolean hasVclock; - private com.google.protobuf.ByteString vclock_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasVclock() { return hasVclock; } - public com.google.protobuf.ByteString getVclock() { return vclock_; } - - // required .RpbContent content = 4; - public static final int CONTENT_FIELD_NUMBER = 4; - private boolean hasContent; - private com.basho.riak.pbc.RPB.RpbContent content_; - public boolean hasContent() { return hasContent; } - public com.basho.riak.pbc.RPB.RpbContent getContent() { return content_; } - - // optional uint32 w = 5; - public static final int W_FIELD_NUMBER = 5; - private boolean hasW; - private int w_ = 0; - public boolean hasW() { return hasW; } - public int getW() { return w_; } - - // optional uint32 dw = 6; - public static final int DW_FIELD_NUMBER = 6; - private boolean hasDw; - private int dw_ = 0; - public boolean hasDw() { return hasDw; } - public int getDw() { return dw_; } - - // optional bool return_body = 7; - public static final int RETURN_BODY_FIELD_NUMBER = 7; - private boolean hasReturnBody; - private boolean returnBody_ = false; - public boolean hasReturnBody() { return hasReturnBody; } - public boolean getReturnBody() { return returnBody_; } - - private void initFields() { - content_ = com.basho.riak.pbc.RPB.RpbContent.getDefaultInstance(); - } - public final boolean isInitialized() { - if (!hasBucket) return false; - if (!hasKey) return false; - if (!hasContent) return false; - if (!getContent().isInitialized()) return false; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (hasBucket()) { - output.writeBytes(1, getBucket()); - } - if (hasKey()) { - output.writeBytes(2, getKey()); - } - if (hasVclock()) { - output.writeBytes(3, getVclock()); - } - if (hasContent()) { - output.writeMessage(4, getContent()); - } - if (hasW()) { - output.writeUInt32(5, getW()); - } - if (hasDw()) { - output.writeUInt32(6, getDw()); - } - if (hasReturnBody()) { - output.writeBool(7, getReturnBody()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (hasBucket()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getBucket()); - } - if (hasKey()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getKey()); - } - if (hasVclock()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, getVclock()); - } - if (hasContent()) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(4, getContent()); - } - if (hasW()) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(5, getW()); - } - if (hasDw()) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(6, getDw()); - } - if (hasReturnBody()) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(7, getReturnBody()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbPutReq parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPutReq parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPutReq parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPutReq parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPutReq parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPutReq parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPutReq parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbPutReq parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbPutReq parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPutReq parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbPutReq prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbPutReq result; - - // Construct using com.trifork.riak.RPB.RpbPutReq.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbPutReq(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbPutReq internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbPutReq(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbPutReq.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbPutReq getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbPutReq.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbPutReq build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbPutReq buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbPutReq buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - com.basho.riak.pbc.RPB.RpbPutReq returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbPutReq) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbPutReq)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbPutReq other) { - if (other == com.basho.riak.pbc.RPB.RpbPutReq.getDefaultInstance()) return this; - if (other.hasBucket()) { - setBucket(other.getBucket()); - } - if (other.hasKey()) { - setKey(other.getKey()); - } - if (other.hasVclock()) { - setVclock(other.getVclock()); - } - if (other.hasContent()) { - mergeContent(other.getContent()); - } - if (other.hasW()) { - setW(other.getW()); - } - if (other.hasDw()) { - setDw(other.getDw()); - } - if (other.hasReturnBody()) { - setReturnBody(other.getReturnBody()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - setBucket(input.readBytes()); - break; - } - case 18: { - setKey(input.readBytes()); - break; - } - case 26: { - setVclock(input.readBytes()); - break; - } - case 34: { - com.basho.riak.pbc.RPB.RpbContent.Builder subBuilder = com.basho.riak.pbc.RPB.RpbContent.newBuilder(); - if (hasContent()) { - subBuilder.mergeFrom(getContent()); - } - input.readMessage(subBuilder, extensionRegistry); - setContent(subBuilder.buildPartial()); - break; - } - case 40: { - setW(input.readUInt32()); - break; - } - case 48: { - setDw(input.readUInt32()); - break; - } - case 56: { - setReturnBody(input.readBool()); - break; - } - } - } - } - - - // required bytes bucket = 1; - public boolean hasBucket() { - return result.hasBucket(); - } - public com.google.protobuf.ByteString getBucket() { - return result.getBucket(); - } - public Builder setBucket(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasBucket = true; - result.bucket_ = value; - return this; - } - public Builder clearBucket() { - result.hasBucket = false; - result.bucket_ = getDefaultInstance().getBucket(); - return this; - } - - // required bytes key = 2; - public boolean hasKey() { - return result.hasKey(); - } - public com.google.protobuf.ByteString getKey() { - return result.getKey(); - } - public Builder setKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasKey = true; - result.key_ = value; - return this; - } - public Builder clearKey() { - result.hasKey = false; - result.key_ = getDefaultInstance().getKey(); - return this; - } - - // optional bytes vclock = 3; - public boolean hasVclock() { - return result.hasVclock(); - } - public com.google.protobuf.ByteString getVclock() { - return result.getVclock(); - } - public Builder setVclock(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasVclock = true; - result.vclock_ = value; - return this; - } - public Builder clearVclock() { - result.hasVclock = false; - result.vclock_ = getDefaultInstance().getVclock(); - return this; - } - - // required .RpbContent content = 4; - public boolean hasContent() { - return result.hasContent(); - } - public com.basho.riak.pbc.RPB.RpbContent getContent() { - return result.getContent(); - } - public Builder setContent(com.basho.riak.pbc.RPB.RpbContent value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasContent = true; - result.content_ = value; - return this; - } - public Builder setContent(com.basho.riak.pbc.RPB.RpbContent.Builder builderForValue) { - result.hasContent = true; - result.content_ = builderForValue.build(); - return this; - } - public Builder mergeContent(com.basho.riak.pbc.RPB.RpbContent value) { - if (result.hasContent() && - result.content_ != com.basho.riak.pbc.RPB.RpbContent.getDefaultInstance()) { - result.content_ = - com.basho.riak.pbc.RPB.RpbContent.newBuilder(result.content_).mergeFrom(value).buildPartial(); - } else { - result.content_ = value; - } - result.hasContent = true; - return this; - } - public Builder clearContent() { - result.hasContent = false; - result.content_ = com.basho.riak.pbc.RPB.RpbContent.getDefaultInstance(); - return this; - } - - // optional uint32 w = 5; - public boolean hasW() { - return result.hasW(); - } - public int getW() { - return result.getW(); - } - public Builder setW(int value) { - result.hasW = true; - result.w_ = value; - return this; - } - public Builder clearW() { - result.hasW = false; - result.w_ = 0; - return this; - } - - // optional uint32 dw = 6; - public boolean hasDw() { - return result.hasDw(); - } - public int getDw() { - return result.getDw(); - } - public Builder setDw(int value) { - result.hasDw = true; - result.dw_ = value; - return this; - } - public Builder clearDw() { - result.hasDw = false; - result.dw_ = 0; - return this; - } - - // optional bool return_body = 7; - public boolean hasReturnBody() { - return result.hasReturnBody(); - } - public boolean getReturnBody() { - return result.getReturnBody(); - } - public Builder setReturnBody(boolean value) { - result.hasReturnBody = true; - result.returnBody_ = value; - return this; - } - public Builder clearReturnBody() { - result.hasReturnBody = false; - result.returnBody_ = false; - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbPutReq) - } - - static { - defaultInstance = new RpbPutReq(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbPutReq) - } - - public static final class RpbPutResp extends - com.google.protobuf.GeneratedMessage { - // Use RpbPutResp.newBuilder() to construct. - private RpbPutResp() { - initFields(); - } - private RpbPutResp(boolean noInit) {} - - private static final RpbPutResp defaultInstance; - public static RpbPutResp getDefaultInstance() { - return defaultInstance; - } - - public RpbPutResp getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbPutResp_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbPutResp_fieldAccessorTable; - } - - // repeated .RpbContent contents = 1; - public static final int CONTENTS_FIELD_NUMBER = 1; - private java.util.List contents_ = - java.util.Collections.emptyList(); - public java.util.List getContentsList() { - return contents_; - } - public int getContentsCount() { return contents_.size(); } - public com.basho.riak.pbc.RPB.RpbContent getContents(int index) { - return contents_.get(index); - } - - // optional bytes vclock = 2; - public static final int VCLOCK_FIELD_NUMBER = 2; - private boolean hasVclock; - private com.google.protobuf.ByteString vclock_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasVclock() { return hasVclock; } - public com.google.protobuf.ByteString getVclock() { return vclock_; } - - private void initFields() { - } - public final boolean isInitialized() { - for (com.basho.riak.pbc.RPB.RpbContent element : getContentsList()) { - if (!element.isInitialized()) return false; - } - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - for (com.basho.riak.pbc.RPB.RpbContent element : getContentsList()) { - output.writeMessage(1, element); - } - if (hasVclock()) { - output.writeBytes(2, getVclock()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - for (com.basho.riak.pbc.RPB.RpbContent element : getContentsList()) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(1, element); - } - if (hasVclock()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getVclock()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbPutResp parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPutResp parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPutResp parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPutResp parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPutResp parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPutResp parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPutResp parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbPutResp parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbPutResp parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPutResp parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbPutResp prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbPutResp result; - - // Construct using com.trifork.riak.RPB.RpbPutResp.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbPutResp(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbPutResp internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbPutResp(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbPutResp.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbPutResp getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbPutResp.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbPutResp build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbPutResp buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbPutResp buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - if (result.contents_ != java.util.Collections.EMPTY_LIST) { - result.contents_ = - java.util.Collections.unmodifiableList(result.contents_); - } - com.basho.riak.pbc.RPB.RpbPutResp returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbPutResp) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbPutResp)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbPutResp other) { - if (other == com.basho.riak.pbc.RPB.RpbPutResp.getDefaultInstance()) return this; - if (!other.contents_.isEmpty()) { - if (result.contents_.isEmpty()) { - result.contents_ = new java.util.ArrayList(); - } - result.contents_.addAll(other.contents_); - } - if (other.hasVclock()) { - setVclock(other.getVclock()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - com.basho.riak.pbc.RPB.RpbContent.Builder subBuilder = com.basho.riak.pbc.RPB.RpbContent.newBuilder(); - input.readMessage(subBuilder, extensionRegistry); - addContents(subBuilder.buildPartial()); - break; - } - case 18: { - setVclock(input.readBytes()); - break; - } - } - } - } - - - // repeated .RpbContent contents = 1; - public java.util.List getContentsList() { - return java.util.Collections.unmodifiableList(result.contents_); - } - public int getContentsCount() { - return result.getContentsCount(); - } - public com.basho.riak.pbc.RPB.RpbContent getContents(int index) { - return result.getContents(index); - } - public Builder setContents(int index, com.basho.riak.pbc.RPB.RpbContent value) { - if (value == null) { - throw new NullPointerException(); - } - result.contents_.set(index, value); - return this; - } - public Builder setContents(int index, com.basho.riak.pbc.RPB.RpbContent.Builder builderForValue) { - result.contents_.set(index, builderForValue.build()); - return this; - } - public Builder addContents(com.basho.riak.pbc.RPB.RpbContent value) { - if (value == null) { - throw new NullPointerException(); - } - if (result.contents_.isEmpty()) { - result.contents_ = new java.util.ArrayList(); - } - result.contents_.add(value); - return this; - } - public Builder addContents(com.basho.riak.pbc.RPB.RpbContent.Builder builderForValue) { - if (result.contents_.isEmpty()) { - result.contents_ = new java.util.ArrayList(); - } - result.contents_.add(builderForValue.build()); - return this; - } - public Builder addAllContents( - java.lang.Iterable values) { - if (result.contents_.isEmpty()) { - result.contents_ = new java.util.ArrayList(); - } - super.addAll(values, result.contents_); - return this; - } - public Builder clearContents() { - result.contents_ = java.util.Collections.emptyList(); - return this; - } - - // optional bytes vclock = 2; - public boolean hasVclock() { - return result.hasVclock(); - } - public com.google.protobuf.ByteString getVclock() { - return result.getVclock(); - } - public Builder setVclock(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasVclock = true; - result.vclock_ = value; - return this; - } - public Builder clearVclock() { - result.hasVclock = false; - result.vclock_ = getDefaultInstance().getVclock(); - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbPutResp) - } - - static { - defaultInstance = new RpbPutResp(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbPutResp) - } - - public static final class RpbDelReq extends - com.google.protobuf.GeneratedMessage { - // Use RpbDelReq.newBuilder() to construct. - private RpbDelReq() { - initFields(); - } - private RpbDelReq(boolean noInit) {} - - private static final RpbDelReq defaultInstance; - public static RpbDelReq getDefaultInstance() { - return defaultInstance; - } - - public RpbDelReq getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbDelReq_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbDelReq_fieldAccessorTable; - } - - // required bytes bucket = 1; - public static final int BUCKET_FIELD_NUMBER = 1; - private boolean hasBucket; - private com.google.protobuf.ByteString bucket_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasBucket() { return hasBucket; } - public com.google.protobuf.ByteString getBucket() { return bucket_; } - - // required bytes key = 2; - public static final int KEY_FIELD_NUMBER = 2; - private boolean hasKey; - private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasKey() { return hasKey; } - public com.google.protobuf.ByteString getKey() { return key_; } - - // optional uint32 rw = 3; - public static final int RW_FIELD_NUMBER = 3; - private boolean hasRw; - private int rw_ = 0; - public boolean hasRw() { return hasRw; } - public int getRw() { return rw_; } - - private void initFields() { - } - public final boolean isInitialized() { - if (!hasBucket) return false; - if (!hasKey) return false; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (hasBucket()) { - output.writeBytes(1, getBucket()); - } - if (hasKey()) { - output.writeBytes(2, getKey()); - } - if (hasRw()) { - output.writeUInt32(3, getRw()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (hasBucket()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getBucket()); - } - if (hasKey()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getKey()); - } - if (hasRw()) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(3, getRw()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbDelReq parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbDelReq parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbDelReq parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbDelReq parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbDelReq parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbDelReq parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbDelReq parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbDelReq parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbDelReq parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbDelReq parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbDelReq prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbDelReq result; - - // Construct using com.trifork.riak.RPB.RpbDelReq.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbDelReq(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbDelReq internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbDelReq(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbDelReq.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbDelReq getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbDelReq.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbDelReq build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbDelReq buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbDelReq buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - com.basho.riak.pbc.RPB.RpbDelReq returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbDelReq) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbDelReq)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbDelReq other) { - if (other == com.basho.riak.pbc.RPB.RpbDelReq.getDefaultInstance()) return this; - if (other.hasBucket()) { - setBucket(other.getBucket()); - } - if (other.hasKey()) { - setKey(other.getKey()); - } - if (other.hasRw()) { - setRw(other.getRw()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - setBucket(input.readBytes()); - break; - } - case 18: { - setKey(input.readBytes()); - break; - } - case 24: { - setRw(input.readUInt32()); - break; - } - } - } - } - - - // required bytes bucket = 1; - public boolean hasBucket() { - return result.hasBucket(); - } - public com.google.protobuf.ByteString getBucket() { - return result.getBucket(); - } - public Builder setBucket(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasBucket = true; - result.bucket_ = value; - return this; - } - public Builder clearBucket() { - result.hasBucket = false; - result.bucket_ = getDefaultInstance().getBucket(); - return this; - } - - // required bytes key = 2; - public boolean hasKey() { - return result.hasKey(); - } - public com.google.protobuf.ByteString getKey() { - return result.getKey(); - } - public Builder setKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasKey = true; - result.key_ = value; - return this; - } - public Builder clearKey() { - result.hasKey = false; - result.key_ = getDefaultInstance().getKey(); - return this; - } - - // optional uint32 rw = 3; - public boolean hasRw() { - return result.hasRw(); - } - public int getRw() { - return result.getRw(); - } - public Builder setRw(int value) { - result.hasRw = true; - result.rw_ = value; - return this; - } - public Builder clearRw() { - result.hasRw = false; - result.rw_ = 0; - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbDelReq) - } - - static { - defaultInstance = new RpbDelReq(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbDelReq) - } - - public static final class RpbListBucketsResp extends - com.google.protobuf.GeneratedMessage { - // Use RpbListBucketsResp.newBuilder() to construct. - private RpbListBucketsResp() { - initFields(); - } - private RpbListBucketsResp(boolean noInit) {} - - private static final RpbListBucketsResp defaultInstance; - public static RpbListBucketsResp getDefaultInstance() { - return defaultInstance; - } - - public RpbListBucketsResp getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbListBucketsResp_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbListBucketsResp_fieldAccessorTable; - } - - // repeated bytes buckets = 1; - public static final int BUCKETS_FIELD_NUMBER = 1; - private java.util.List buckets_ = - java.util.Collections.emptyList(); - public java.util.List getBucketsList() { - return buckets_; - } - public int getBucketsCount() { return buckets_.size(); } - public com.google.protobuf.ByteString getBuckets(int index) { - return buckets_.get(index); - } - - private void initFields() { - } - public final boolean isInitialized() { - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - for (com.google.protobuf.ByteString element : getBucketsList()) { - output.writeBytes(1, element); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - { - int dataSize = 0; - for (com.google.protobuf.ByteString element : getBucketsList()) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(element); - } - size += dataSize; - size += 1 * getBucketsList().size(); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbListBucketsResp parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListBucketsResp parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListBucketsResp parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListBucketsResp parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListBucketsResp parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListBucketsResp parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListBucketsResp parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbListBucketsResp parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbListBucketsResp parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListBucketsResp parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbListBucketsResp prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbListBucketsResp result; - - // Construct using com.trifork.riak.RPB.RpbListBucketsResp.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbListBucketsResp(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbListBucketsResp internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbListBucketsResp(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbListBucketsResp.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbListBucketsResp getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbListBucketsResp.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbListBucketsResp build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbListBucketsResp buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbListBucketsResp buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - if (result.buckets_ != java.util.Collections.EMPTY_LIST) { - result.buckets_ = - java.util.Collections.unmodifiableList(result.buckets_); - } - com.basho.riak.pbc.RPB.RpbListBucketsResp returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbListBucketsResp) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbListBucketsResp)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbListBucketsResp other) { - if (other == com.basho.riak.pbc.RPB.RpbListBucketsResp.getDefaultInstance()) return this; - if (!other.buckets_.isEmpty()) { - if (result.buckets_.isEmpty()) { - result.buckets_ = new java.util.ArrayList(); - } - result.buckets_.addAll(other.buckets_); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - addBuckets(input.readBytes()); - break; - } - } - } - } - - - // repeated bytes buckets = 1; - public java.util.List getBucketsList() { - return java.util.Collections.unmodifiableList(result.buckets_); - } - public int getBucketsCount() { - return result.getBucketsCount(); - } - public com.google.protobuf.ByteString getBuckets(int index) { - return result.getBuckets(index); - } - public Builder setBuckets(int index, com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.buckets_.set(index, value); - return this; - } - public Builder addBuckets(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - if (result.buckets_.isEmpty()) { - result.buckets_ = new java.util.ArrayList(); - } - result.buckets_.add(value); - return this; - } - public Builder addAllBuckets( - java.lang.Iterable values) { - if (result.buckets_.isEmpty()) { - result.buckets_ = new java.util.ArrayList(); - } - super.addAll(values, result.buckets_); - return this; - } - public Builder clearBuckets() { - result.buckets_ = java.util.Collections.emptyList(); - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbListBucketsResp) - } - - static { - defaultInstance = new RpbListBucketsResp(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbListBucketsResp) - } - - public static final class RpbListKeysReq extends - com.google.protobuf.GeneratedMessage { - // Use RpbListKeysReq.newBuilder() to construct. - private RpbListKeysReq() { - initFields(); - } - private RpbListKeysReq(boolean noInit) {} - - private static final RpbListKeysReq defaultInstance; - public static RpbListKeysReq getDefaultInstance() { - return defaultInstance; - } - - public RpbListKeysReq getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbListKeysReq_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbListKeysReq_fieldAccessorTable; - } - - // required bytes bucket = 1; - public static final int BUCKET_FIELD_NUMBER = 1; - private boolean hasBucket; - private com.google.protobuf.ByteString bucket_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasBucket() { return hasBucket; } - public com.google.protobuf.ByteString getBucket() { return bucket_; } - - private void initFields() { - } - public final boolean isInitialized() { - if (!hasBucket) return false; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (hasBucket()) { - output.writeBytes(1, getBucket()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (hasBucket()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getBucket()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbListKeysReq parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListKeysReq parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListKeysReq parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListKeysReq parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListKeysReq parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListKeysReq parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListKeysReq parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbListKeysReq parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbListKeysReq parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListKeysReq parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbListKeysReq prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbListKeysReq result; - - // Construct using com.trifork.riak.RPB.RpbListKeysReq.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbListKeysReq(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbListKeysReq internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbListKeysReq(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbListKeysReq.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbListKeysReq getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbListKeysReq.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbListKeysReq build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbListKeysReq buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbListKeysReq buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - com.basho.riak.pbc.RPB.RpbListKeysReq returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbListKeysReq) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbListKeysReq)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbListKeysReq other) { - if (other == com.basho.riak.pbc.RPB.RpbListKeysReq.getDefaultInstance()) return this; - if (other.hasBucket()) { - setBucket(other.getBucket()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - setBucket(input.readBytes()); - break; - } - } - } - } - - - // required bytes bucket = 1; - public boolean hasBucket() { - return result.hasBucket(); - } - public com.google.protobuf.ByteString getBucket() { - return result.getBucket(); - } - public Builder setBucket(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasBucket = true; - result.bucket_ = value; - return this; - } - public Builder clearBucket() { - result.hasBucket = false; - result.bucket_ = getDefaultInstance().getBucket(); - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbListKeysReq) - } - - static { - defaultInstance = new RpbListKeysReq(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbListKeysReq) - } - - public static final class RpbListKeysResp extends - com.google.protobuf.GeneratedMessage { - // Use RpbListKeysResp.newBuilder() to construct. - private RpbListKeysResp() { - initFields(); - } - private RpbListKeysResp(boolean noInit) {} - - private static final RpbListKeysResp defaultInstance; - public static RpbListKeysResp getDefaultInstance() { - return defaultInstance; - } - - public RpbListKeysResp getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbListKeysResp_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbListKeysResp_fieldAccessorTable; - } - - // repeated bytes keys = 1; - public static final int KEYS_FIELD_NUMBER = 1; - private java.util.List keys_ = - java.util.Collections.emptyList(); - public java.util.List getKeysList() { - return keys_; - } - public int getKeysCount() { return keys_.size(); } - public com.google.protobuf.ByteString getKeys(int index) { - return keys_.get(index); - } - - // optional bool done = 2; - public static final int DONE_FIELD_NUMBER = 2; - private boolean hasDone; - private boolean done_ = false; - public boolean hasDone() { return hasDone; } - public boolean getDone() { return done_; } - - private void initFields() { - } - public final boolean isInitialized() { - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - for (com.google.protobuf.ByteString element : getKeysList()) { - output.writeBytes(1, element); - } - if (hasDone()) { - output.writeBool(2, getDone()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - { - int dataSize = 0; - for (com.google.protobuf.ByteString element : getKeysList()) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(element); - } - size += dataSize; - size += 1 * getKeysList().size(); - } - if (hasDone()) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(2, getDone()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbListKeysResp parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListKeysResp parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListKeysResp parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListKeysResp parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListKeysResp parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListKeysResp parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListKeysResp parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbListKeysResp parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbListKeysResp parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbListKeysResp parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbListKeysResp prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbListKeysResp result; - - // Construct using com.trifork.riak.RPB.RpbListKeysResp.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbListKeysResp(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbListKeysResp internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbListKeysResp(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbListKeysResp.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbListKeysResp getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbListKeysResp.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbListKeysResp build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbListKeysResp buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbListKeysResp buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - if (result.keys_ != java.util.Collections.EMPTY_LIST) { - result.keys_ = - java.util.Collections.unmodifiableList(result.keys_); - } - com.basho.riak.pbc.RPB.RpbListKeysResp returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbListKeysResp) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbListKeysResp)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbListKeysResp other) { - if (other == com.basho.riak.pbc.RPB.RpbListKeysResp.getDefaultInstance()) return this; - if (!other.keys_.isEmpty()) { - if (result.keys_.isEmpty()) { - result.keys_ = new java.util.ArrayList(); - } - result.keys_.addAll(other.keys_); - } - if (other.hasDone()) { - setDone(other.getDone()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - addKeys(input.readBytes()); - break; - } - case 16: { - setDone(input.readBool()); - break; - } - } - } - } - - - // repeated bytes keys = 1; - public java.util.List getKeysList() { - return java.util.Collections.unmodifiableList(result.keys_); - } - public int getKeysCount() { - return result.getKeysCount(); - } - public com.google.protobuf.ByteString getKeys(int index) { - return result.getKeys(index); - } - public Builder setKeys(int index, com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.keys_.set(index, value); - return this; - } - public Builder addKeys(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - if (result.keys_.isEmpty()) { - result.keys_ = new java.util.ArrayList(); - } - result.keys_.add(value); - return this; - } - public Builder addAllKeys( - java.lang.Iterable values) { - if (result.keys_.isEmpty()) { - result.keys_ = new java.util.ArrayList(); - } - super.addAll(values, result.keys_); - return this; - } - public Builder clearKeys() { - result.keys_ = java.util.Collections.emptyList(); - return this; - } - - // optional bool done = 2; - public boolean hasDone() { - return result.hasDone(); - } - public boolean getDone() { - return result.getDone(); - } - public Builder setDone(boolean value) { - result.hasDone = true; - result.done_ = value; - return this; - } - public Builder clearDone() { - result.hasDone = false; - result.done_ = false; - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbListKeysResp) - } - - static { - defaultInstance = new RpbListKeysResp(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbListKeysResp) - } - - public static final class RpbGetBucketReq extends - com.google.protobuf.GeneratedMessage { - // Use RpbGetBucketReq.newBuilder() to construct. - private RpbGetBucketReq() { - initFields(); - } - private RpbGetBucketReq(boolean noInit) {} - - private static final RpbGetBucketReq defaultInstance; - public static RpbGetBucketReq getDefaultInstance() { - return defaultInstance; - } - - public RpbGetBucketReq getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbGetBucketReq_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbGetBucketReq_fieldAccessorTable; - } - - // required bytes bucket = 1; - public static final int BUCKET_FIELD_NUMBER = 1; - private boolean hasBucket; - private com.google.protobuf.ByteString bucket_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasBucket() { return hasBucket; } - public com.google.protobuf.ByteString getBucket() { return bucket_; } - - private void initFields() { - } - public final boolean isInitialized() { - if (!hasBucket) return false; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (hasBucket()) { - output.writeBytes(1, getBucket()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (hasBucket()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getBucket()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbGetBucketReq parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetBucketReq parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetBucketReq parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetBucketReq parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetBucketReq parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetBucketReq parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetBucketReq parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbGetBucketReq parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbGetBucketReq parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetBucketReq parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbGetBucketReq prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbGetBucketReq result; - - // Construct using com.trifork.riak.RPB.RpbGetBucketReq.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbGetBucketReq(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbGetBucketReq internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbGetBucketReq(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbGetBucketReq.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbGetBucketReq getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbGetBucketReq.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbGetBucketReq build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbGetBucketReq buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbGetBucketReq buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - com.basho.riak.pbc.RPB.RpbGetBucketReq returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbGetBucketReq) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbGetBucketReq)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbGetBucketReq other) { - if (other == com.basho.riak.pbc.RPB.RpbGetBucketReq.getDefaultInstance()) return this; - if (other.hasBucket()) { - setBucket(other.getBucket()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - setBucket(input.readBytes()); - break; - } - } - } - } - - - // required bytes bucket = 1; - public boolean hasBucket() { - return result.hasBucket(); - } - public com.google.protobuf.ByteString getBucket() { - return result.getBucket(); - } - public Builder setBucket(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasBucket = true; - result.bucket_ = value; - return this; - } - public Builder clearBucket() { - result.hasBucket = false; - result.bucket_ = getDefaultInstance().getBucket(); - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbGetBucketReq) - } - - static { - defaultInstance = new RpbGetBucketReq(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbGetBucketReq) - } - - public static final class RpbGetBucketResp extends - com.google.protobuf.GeneratedMessage { - // Use RpbGetBucketResp.newBuilder() to construct. - private RpbGetBucketResp() { - initFields(); - } - private RpbGetBucketResp(boolean noInit) {} - - private static final RpbGetBucketResp defaultInstance; - public static RpbGetBucketResp getDefaultInstance() { - return defaultInstance; - } - - public RpbGetBucketResp getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbGetBucketResp_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbGetBucketResp_fieldAccessorTable; - } - - // required .RpbBucketProps props = 1; - public static final int PROPS_FIELD_NUMBER = 1; - private boolean hasProps; - private com.basho.riak.pbc.RPB.RpbBucketProps props_; - public boolean hasProps() { return hasProps; } - public com.basho.riak.pbc.RPB.RpbBucketProps getProps() { return props_; } - - private void initFields() { - props_ = com.basho.riak.pbc.RPB.RpbBucketProps.getDefaultInstance(); - } - public final boolean isInitialized() { - if (!hasProps) return false; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (hasProps()) { - output.writeMessage(1, getProps()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (hasProps()) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(1, getProps()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbGetBucketResp parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetBucketResp parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetBucketResp parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetBucketResp parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetBucketResp parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetBucketResp parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetBucketResp parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbGetBucketResp parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbGetBucketResp parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbGetBucketResp parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbGetBucketResp prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbGetBucketResp result; - - // Construct using com.trifork.riak.RPB.RpbGetBucketResp.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbGetBucketResp(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbGetBucketResp internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbGetBucketResp(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbGetBucketResp.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbGetBucketResp getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbGetBucketResp.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbGetBucketResp build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbGetBucketResp buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbGetBucketResp buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - com.basho.riak.pbc.RPB.RpbGetBucketResp returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbGetBucketResp) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbGetBucketResp)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbGetBucketResp other) { - if (other == com.basho.riak.pbc.RPB.RpbGetBucketResp.getDefaultInstance()) return this; - if (other.hasProps()) { - mergeProps(other.getProps()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - com.basho.riak.pbc.RPB.RpbBucketProps.Builder subBuilder = com.basho.riak.pbc.RPB.RpbBucketProps.newBuilder(); - if (hasProps()) { - subBuilder.mergeFrom(getProps()); - } - input.readMessage(subBuilder, extensionRegistry); - setProps(subBuilder.buildPartial()); - break; - } - } - } - } - - - // required .RpbBucketProps props = 1; - public boolean hasProps() { - return result.hasProps(); - } - public com.basho.riak.pbc.RPB.RpbBucketProps getProps() { - return result.getProps(); - } - public Builder setProps(com.basho.riak.pbc.RPB.RpbBucketProps value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasProps = true; - result.props_ = value; - return this; - } - public Builder setProps(com.basho.riak.pbc.RPB.RpbBucketProps.Builder builderForValue) { - result.hasProps = true; - result.props_ = builderForValue.build(); - return this; - } - public Builder mergeProps(com.basho.riak.pbc.RPB.RpbBucketProps value) { - if (result.hasProps() && - result.props_ != com.basho.riak.pbc.RPB.RpbBucketProps.getDefaultInstance()) { - result.props_ = - com.basho.riak.pbc.RPB.RpbBucketProps.newBuilder(result.props_).mergeFrom(value).buildPartial(); - } else { - result.props_ = value; - } - result.hasProps = true; - return this; - } - public Builder clearProps() { - result.hasProps = false; - result.props_ = com.basho.riak.pbc.RPB.RpbBucketProps.getDefaultInstance(); - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbGetBucketResp) - } - - static { - defaultInstance = new RpbGetBucketResp(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbGetBucketResp) - } - - public static final class RpbSetBucketReq extends - com.google.protobuf.GeneratedMessage { - // Use RpbSetBucketReq.newBuilder() to construct. - private RpbSetBucketReq() { - initFields(); - } - private RpbSetBucketReq(boolean noInit) {} - - private static final RpbSetBucketReq defaultInstance; - public static RpbSetBucketReq getDefaultInstance() { - return defaultInstance; - } - - public RpbSetBucketReq getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbSetBucketReq_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbSetBucketReq_fieldAccessorTable; - } - - // required bytes bucket = 1; - public static final int BUCKET_FIELD_NUMBER = 1; - private boolean hasBucket; - private com.google.protobuf.ByteString bucket_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasBucket() { return hasBucket; } - public com.google.protobuf.ByteString getBucket() { return bucket_; } - - // required .RpbBucketProps props = 2; - public static final int PROPS_FIELD_NUMBER = 2; - private boolean hasProps; - private com.basho.riak.pbc.RPB.RpbBucketProps props_; - public boolean hasProps() { return hasProps; } - public com.basho.riak.pbc.RPB.RpbBucketProps getProps() { return props_; } - - private void initFields() { - props_ = com.basho.riak.pbc.RPB.RpbBucketProps.getDefaultInstance(); - } - public final boolean isInitialized() { - if (!hasBucket) return false; - if (!hasProps) return false; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (hasBucket()) { - output.writeBytes(1, getBucket()); - } - if (hasProps()) { - output.writeMessage(2, getProps()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (hasBucket()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getBucket()); - } - if (hasProps()) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(2, getProps()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbSetBucketReq parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbSetBucketReq parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbSetBucketReq parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbSetBucketReq parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbSetBucketReq parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbSetBucketReq parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbSetBucketReq parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbSetBucketReq parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbSetBucketReq parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbSetBucketReq parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbSetBucketReq prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbSetBucketReq result; - - // Construct using com.trifork.riak.RPB.RpbSetBucketReq.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbSetBucketReq(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbSetBucketReq internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbSetBucketReq(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbSetBucketReq.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbSetBucketReq getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbSetBucketReq.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbSetBucketReq build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbSetBucketReq buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbSetBucketReq buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - com.basho.riak.pbc.RPB.RpbSetBucketReq returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbSetBucketReq) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbSetBucketReq)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbSetBucketReq other) { - if (other == com.basho.riak.pbc.RPB.RpbSetBucketReq.getDefaultInstance()) return this; - if (other.hasBucket()) { - setBucket(other.getBucket()); - } - if (other.hasProps()) { - mergeProps(other.getProps()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - setBucket(input.readBytes()); - break; - } - case 18: { - com.basho.riak.pbc.RPB.RpbBucketProps.Builder subBuilder = com.basho.riak.pbc.RPB.RpbBucketProps.newBuilder(); - if (hasProps()) { - subBuilder.mergeFrom(getProps()); - } - input.readMessage(subBuilder, extensionRegistry); - setProps(subBuilder.buildPartial()); - break; - } - } - } - } - - - // required bytes bucket = 1; - public boolean hasBucket() { - return result.hasBucket(); - } - public com.google.protobuf.ByteString getBucket() { - return result.getBucket(); - } - public Builder setBucket(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasBucket = true; - result.bucket_ = value; - return this; - } - public Builder clearBucket() { - result.hasBucket = false; - result.bucket_ = getDefaultInstance().getBucket(); - return this; - } - - // required .RpbBucketProps props = 2; - public boolean hasProps() { - return result.hasProps(); - } - public com.basho.riak.pbc.RPB.RpbBucketProps getProps() { - return result.getProps(); - } - public Builder setProps(com.basho.riak.pbc.RPB.RpbBucketProps value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasProps = true; - result.props_ = value; - return this; - } - public Builder setProps(com.basho.riak.pbc.RPB.RpbBucketProps.Builder builderForValue) { - result.hasProps = true; - result.props_ = builderForValue.build(); - return this; - } - public Builder mergeProps(com.basho.riak.pbc.RPB.RpbBucketProps value) { - if (result.hasProps() && - result.props_ != com.basho.riak.pbc.RPB.RpbBucketProps.getDefaultInstance()) { - result.props_ = - com.basho.riak.pbc.RPB.RpbBucketProps.newBuilder(result.props_).mergeFrom(value).buildPartial(); - } else { - result.props_ = value; - } - result.hasProps = true; - return this; - } - public Builder clearProps() { - result.hasProps = false; - result.props_ = com.basho.riak.pbc.RPB.RpbBucketProps.getDefaultInstance(); - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbSetBucketReq) - } - - static { - defaultInstance = new RpbSetBucketReq(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbSetBucketReq) - } - - public static final class RpbMapRedReq extends - com.google.protobuf.GeneratedMessage { - // Use RpbMapRedReq.newBuilder() to construct. - private RpbMapRedReq() { - initFields(); - } - private RpbMapRedReq(boolean noInit) {} - - private static final RpbMapRedReq defaultInstance; - public static RpbMapRedReq getDefaultInstance() { - return defaultInstance; - } - - public RpbMapRedReq getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbMapRedReq_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbMapRedReq_fieldAccessorTable; - } - - // required bytes request = 1; - public static final int REQUEST_FIELD_NUMBER = 1; - private boolean hasRequest; - private com.google.protobuf.ByteString request_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasRequest() { return hasRequest; } - public com.google.protobuf.ByteString getRequest() { return request_; } - - // required bytes content_type = 2; - public static final int CONTENT_TYPE_FIELD_NUMBER = 2; - private boolean hasContentType; - private com.google.protobuf.ByteString contentType_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasContentType() { return hasContentType; } - public com.google.protobuf.ByteString getContentType() { return contentType_; } - - private void initFields() { - } - public final boolean isInitialized() { - if (!hasRequest) return false; - if (!hasContentType) return false; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (hasRequest()) { - output.writeBytes(1, getRequest()); - } - if (hasContentType()) { - output.writeBytes(2, getContentType()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (hasRequest()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getRequest()); - } - if (hasContentType()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getContentType()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbMapRedReq parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbMapRedReq parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbMapRedReq parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbMapRedReq parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbMapRedReq parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbMapRedReq parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbMapRedReq parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbMapRedReq parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbMapRedReq parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbMapRedReq parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbMapRedReq prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbMapRedReq result; - - // Construct using com.trifork.riak.RPB.RpbMapRedReq.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbMapRedReq(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbMapRedReq internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbMapRedReq(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbMapRedReq.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbMapRedReq getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbMapRedReq.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbMapRedReq build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbMapRedReq buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbMapRedReq buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - com.basho.riak.pbc.RPB.RpbMapRedReq returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbMapRedReq) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbMapRedReq)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbMapRedReq other) { - if (other == com.basho.riak.pbc.RPB.RpbMapRedReq.getDefaultInstance()) return this; - if (other.hasRequest()) { - setRequest(other.getRequest()); - } - if (other.hasContentType()) { - setContentType(other.getContentType()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - setRequest(input.readBytes()); - break; - } - case 18: { - setContentType(input.readBytes()); - break; - } - } - } - } - - - // required bytes request = 1; - public boolean hasRequest() { - return result.hasRequest(); - } - public com.google.protobuf.ByteString getRequest() { - return result.getRequest(); - } - public Builder setRequest(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasRequest = true; - result.request_ = value; - return this; - } - public Builder clearRequest() { - result.hasRequest = false; - result.request_ = getDefaultInstance().getRequest(); - return this; - } - - // required bytes content_type = 2; - public boolean hasContentType() { - return result.hasContentType(); - } - public com.google.protobuf.ByteString getContentType() { - return result.getContentType(); - } - public Builder setContentType(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasContentType = true; - result.contentType_ = value; - return this; - } - public Builder clearContentType() { - result.hasContentType = false; - result.contentType_ = getDefaultInstance().getContentType(); - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbMapRedReq) - } - - static { - defaultInstance = new RpbMapRedReq(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbMapRedReq) - } - - public static final class RpbMapRedResp extends - com.google.protobuf.GeneratedMessage { - // Use RpbMapRedResp.newBuilder() to construct. - private RpbMapRedResp() { - initFields(); - } - private RpbMapRedResp(boolean noInit) {} - - private static final RpbMapRedResp defaultInstance; - public static RpbMapRedResp getDefaultInstance() { - return defaultInstance; - } - - public RpbMapRedResp getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbMapRedResp_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbMapRedResp_fieldAccessorTable; - } - - // optional uint32 phase = 1; - public static final int PHASE_FIELD_NUMBER = 1; - private boolean hasPhase; - private int phase_ = 0; - public boolean hasPhase() { return hasPhase; } - public int getPhase() { return phase_; } - - // optional bytes response = 2; - public static final int RESPONSE_FIELD_NUMBER = 2; - private boolean hasResponse; - private com.google.protobuf.ByteString response_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasResponse() { return hasResponse; } - public com.google.protobuf.ByteString getResponse() { return response_; } - - // optional bool done = 3; - public static final int DONE_FIELD_NUMBER = 3; - private boolean hasDone; - private boolean done_ = false; - public boolean hasDone() { return hasDone; } - public boolean getDone() { return done_; } - - private void initFields() { - } - public final boolean isInitialized() { - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (hasPhase()) { - output.writeUInt32(1, getPhase()); - } - if (hasResponse()) { - output.writeBytes(2, getResponse()); - } - if (hasDone()) { - output.writeBool(3, getDone()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (hasPhase()) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, getPhase()); - } - if (hasResponse()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getResponse()); - } - if (hasDone()) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(3, getDone()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbMapRedResp parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbMapRedResp parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbMapRedResp parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbMapRedResp parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbMapRedResp parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbMapRedResp parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbMapRedResp parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbMapRedResp parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbMapRedResp parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbMapRedResp parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbMapRedResp prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbMapRedResp result; - - // Construct using com.trifork.riak.RPB.RpbMapRedResp.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbMapRedResp(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbMapRedResp internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbMapRedResp(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbMapRedResp.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbMapRedResp getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbMapRedResp.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbMapRedResp build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbMapRedResp buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbMapRedResp buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - com.basho.riak.pbc.RPB.RpbMapRedResp returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbMapRedResp) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbMapRedResp)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbMapRedResp other) { - if (other == com.basho.riak.pbc.RPB.RpbMapRedResp.getDefaultInstance()) return this; - if (other.hasPhase()) { - setPhase(other.getPhase()); - } - if (other.hasResponse()) { - setResponse(other.getResponse()); - } - if (other.hasDone()) { - setDone(other.getDone()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 8: { - setPhase(input.readUInt32()); - break; - } - case 18: { - setResponse(input.readBytes()); - break; - } - case 24: { - setDone(input.readBool()); - break; - } - } - } - } - - - // optional uint32 phase = 1; - public boolean hasPhase() { - return result.hasPhase(); - } - public int getPhase() { - return result.getPhase(); - } - public Builder setPhase(int value) { - result.hasPhase = true; - result.phase_ = value; - return this; - } - public Builder clearPhase() { - result.hasPhase = false; - result.phase_ = 0; - return this; - } - - // optional bytes response = 2; - public boolean hasResponse() { - return result.hasResponse(); - } - public com.google.protobuf.ByteString getResponse() { - return result.getResponse(); - } - public Builder setResponse(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasResponse = true; - result.response_ = value; - return this; - } - public Builder clearResponse() { - result.hasResponse = false; - result.response_ = getDefaultInstance().getResponse(); - return this; - } - - // optional bool done = 3; - public boolean hasDone() { - return result.hasDone(); - } - public boolean getDone() { - return result.getDone(); - } - public Builder setDone(boolean value) { - result.hasDone = true; - result.done_ = value; - return this; - } - public Builder clearDone() { - result.hasDone = false; - result.done_ = false; - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbMapRedResp) - } - - static { - defaultInstance = new RpbMapRedResp(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbMapRedResp) - } - - public static final class RpbContent extends - com.google.protobuf.GeneratedMessage { - // Use RpbContent.newBuilder() to construct. - private RpbContent() { - initFields(); - } - private RpbContent(boolean noInit) {} - - private static final RpbContent defaultInstance; - public static RpbContent getDefaultInstance() { - return defaultInstance; - } - - public RpbContent getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbContent_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbContent_fieldAccessorTable; - } - - // required bytes value = 1; - public static final int VALUE_FIELD_NUMBER = 1; - private boolean hasValue; - private com.google.protobuf.ByteString value_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasValue() { return hasValue; } - public com.google.protobuf.ByteString getValue() { return value_; } - - // optional bytes content_type = 2; - public static final int CONTENT_TYPE_FIELD_NUMBER = 2; - private boolean hasContentType; - private com.google.protobuf.ByteString contentType_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasContentType() { return hasContentType; } - public com.google.protobuf.ByteString getContentType() { return contentType_; } - - // optional bytes charset = 3; - public static final int CHARSET_FIELD_NUMBER = 3; - private boolean hasCharset; - private com.google.protobuf.ByteString charset_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasCharset() { return hasCharset; } - public com.google.protobuf.ByteString getCharset() { return charset_; } - - // optional bytes content_encoding = 4; - public static final int CONTENT_ENCODING_FIELD_NUMBER = 4; - private boolean hasContentEncoding; - private com.google.protobuf.ByteString contentEncoding_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasContentEncoding() { return hasContentEncoding; } - public com.google.protobuf.ByteString getContentEncoding() { return contentEncoding_; } - - // optional bytes vtag = 5; - public static final int VTAG_FIELD_NUMBER = 5; - private boolean hasVtag; - private com.google.protobuf.ByteString vtag_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasVtag() { return hasVtag; } - public com.google.protobuf.ByteString getVtag() { return vtag_; } - - // repeated .RpbLink links = 6; - public static final int LINKS_FIELD_NUMBER = 6; - private java.util.List links_ = - java.util.Collections.emptyList(); - public java.util.List getLinksList() { - return links_; - } - public int getLinksCount() { return links_.size(); } - public com.basho.riak.pbc.RPB.RpbLink getLinks(int index) { - return links_.get(index); - } - - // optional uint32 last_mod = 7; - public static final int LAST_MOD_FIELD_NUMBER = 7; - private boolean hasLastMod; - private int lastMod_ = 0; - public boolean hasLastMod() { return hasLastMod; } - public int getLastMod() { return lastMod_; } - - // optional uint32 last_mod_usecs = 8; - public static final int LAST_MOD_USECS_FIELD_NUMBER = 8; - private boolean hasLastModUsecs; - private int lastModUsecs_ = 0; - public boolean hasLastModUsecs() { return hasLastModUsecs; } - public int getLastModUsecs() { return lastModUsecs_; } - - // repeated .RpbPair usermeta = 9; - public static final int USERMETA_FIELD_NUMBER = 9; - private java.util.List usermeta_ = - java.util.Collections.emptyList(); - public java.util.List getUsermetaList() { - return usermeta_; - } - public int getUsermetaCount() { return usermeta_.size(); } - public com.basho.riak.pbc.RPB.RpbPair getUsermeta(int index) { - return usermeta_.get(index); - } - - private void initFields() { - } - public final boolean isInitialized() { - if (!hasValue) return false; - for (com.basho.riak.pbc.RPB.RpbPair element : getUsermetaList()) { - if (!element.isInitialized()) return false; - } - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (hasValue()) { - output.writeBytes(1, getValue()); - } - if (hasContentType()) { - output.writeBytes(2, getContentType()); - } - if (hasCharset()) { - output.writeBytes(3, getCharset()); - } - if (hasContentEncoding()) { - output.writeBytes(4, getContentEncoding()); - } - if (hasVtag()) { - output.writeBytes(5, getVtag()); - } - for (com.basho.riak.pbc.RPB.RpbLink element : getLinksList()) { - output.writeMessage(6, element); - } - if (hasLastMod()) { - output.writeUInt32(7, getLastMod()); - } - if (hasLastModUsecs()) { - output.writeUInt32(8, getLastModUsecs()); - } - for (com.basho.riak.pbc.RPB.RpbPair element : getUsermetaList()) { - output.writeMessage(9, element); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (hasValue()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getValue()); - } - if (hasContentType()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getContentType()); - } - if (hasCharset()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, getCharset()); - } - if (hasContentEncoding()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, getContentEncoding()); - } - if (hasVtag()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(5, getVtag()); - } - for (com.basho.riak.pbc.RPB.RpbLink element : getLinksList()) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(6, element); - } - if (hasLastMod()) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(7, getLastMod()); - } - if (hasLastModUsecs()) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(8, getLastModUsecs()); - } - for (com.basho.riak.pbc.RPB.RpbPair element : getUsermetaList()) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(9, element); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbContent parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbContent parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbContent parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbContent parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbContent parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbContent parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbContent parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbContent parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbContent parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbContent parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbContent prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbContent result; - - // Construct using com.trifork.riak.RPB.RpbContent.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbContent(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbContent internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbContent(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbContent.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbContent getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbContent.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbContent build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbContent buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbContent buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - if (result.links_ != java.util.Collections.EMPTY_LIST) { - result.links_ = - java.util.Collections.unmodifiableList(result.links_); - } - if (result.usermeta_ != java.util.Collections.EMPTY_LIST) { - result.usermeta_ = - java.util.Collections.unmodifiableList(result.usermeta_); - } - com.basho.riak.pbc.RPB.RpbContent returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbContent) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbContent)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbContent other) { - if (other == com.basho.riak.pbc.RPB.RpbContent.getDefaultInstance()) return this; - if (other.hasValue()) { - setValue(other.getValue()); - } - if (other.hasContentType()) { - setContentType(other.getContentType()); - } - if (other.hasCharset()) { - setCharset(other.getCharset()); - } - if (other.hasContentEncoding()) { - setContentEncoding(other.getContentEncoding()); - } - if (other.hasVtag()) { - setVtag(other.getVtag()); - } - if (!other.links_.isEmpty()) { - if (result.links_.isEmpty()) { - result.links_ = new java.util.ArrayList(); - } - result.links_.addAll(other.links_); - } - if (other.hasLastMod()) { - setLastMod(other.getLastMod()); - } - if (other.hasLastModUsecs()) { - setLastModUsecs(other.getLastModUsecs()); - } - if (!other.usermeta_.isEmpty()) { - if (result.usermeta_.isEmpty()) { - result.usermeta_ = new java.util.ArrayList(); - } - result.usermeta_.addAll(other.usermeta_); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - setValue(input.readBytes()); - break; - } - case 18: { - setContentType(input.readBytes()); - break; - } - case 26: { - setCharset(input.readBytes()); - break; - } - case 34: { - setContentEncoding(input.readBytes()); - break; - } - case 42: { - setVtag(input.readBytes()); - break; - } - case 50: { - com.basho.riak.pbc.RPB.RpbLink.Builder subBuilder = com.basho.riak.pbc.RPB.RpbLink.newBuilder(); - input.readMessage(subBuilder, extensionRegistry); - addLinks(subBuilder.buildPartial()); - break; - } - case 56: { - setLastMod(input.readUInt32()); - break; - } - case 64: { - setLastModUsecs(input.readUInt32()); - break; - } - case 74: { - com.basho.riak.pbc.RPB.RpbPair.Builder subBuilder = com.basho.riak.pbc.RPB.RpbPair.newBuilder(); - input.readMessage(subBuilder, extensionRegistry); - addUsermeta(subBuilder.buildPartial()); - break; - } - } - } - } - - - // required bytes value = 1; - public boolean hasValue() { - return result.hasValue(); - } - public com.google.protobuf.ByteString getValue() { - return result.getValue(); - } - public Builder setValue(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasValue = true; - result.value_ = value; - return this; - } - public Builder clearValue() { - result.hasValue = false; - result.value_ = getDefaultInstance().getValue(); - return this; - } - - // optional bytes content_type = 2; - public boolean hasContentType() { - return result.hasContentType(); - } - public com.google.protobuf.ByteString getContentType() { - return result.getContentType(); - } - public Builder setContentType(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasContentType = true; - result.contentType_ = value; - return this; - } - public Builder clearContentType() { - result.hasContentType = false; - result.contentType_ = getDefaultInstance().getContentType(); - return this; - } - - // optional bytes charset = 3; - public boolean hasCharset() { - return result.hasCharset(); - } - public com.google.protobuf.ByteString getCharset() { - return result.getCharset(); - } - public Builder setCharset(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasCharset = true; - result.charset_ = value; - return this; - } - public Builder clearCharset() { - result.hasCharset = false; - result.charset_ = getDefaultInstance().getCharset(); - return this; - } - - // optional bytes content_encoding = 4; - public boolean hasContentEncoding() { - return result.hasContentEncoding(); - } - public com.google.protobuf.ByteString getContentEncoding() { - return result.getContentEncoding(); - } - public Builder setContentEncoding(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasContentEncoding = true; - result.contentEncoding_ = value; - return this; - } - public Builder clearContentEncoding() { - result.hasContentEncoding = false; - result.contentEncoding_ = getDefaultInstance().getContentEncoding(); - return this; - } - - // optional bytes vtag = 5; - public boolean hasVtag() { - return result.hasVtag(); - } - public com.google.protobuf.ByteString getVtag() { - return result.getVtag(); - } - public Builder setVtag(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasVtag = true; - result.vtag_ = value; - return this; - } - public Builder clearVtag() { - result.hasVtag = false; - result.vtag_ = getDefaultInstance().getVtag(); - return this; - } - - // repeated .RpbLink links = 6; - public java.util.List getLinksList() { - return java.util.Collections.unmodifiableList(result.links_); - } - public int getLinksCount() { - return result.getLinksCount(); - } - public com.basho.riak.pbc.RPB.RpbLink getLinks(int index) { - return result.getLinks(index); - } - public Builder setLinks(int index, com.basho.riak.pbc.RPB.RpbLink value) { - if (value == null) { - throw new NullPointerException(); - } - result.links_.set(index, value); - return this; - } - public Builder setLinks(int index, com.basho.riak.pbc.RPB.RpbLink.Builder builderForValue) { - result.links_.set(index, builderForValue.build()); - return this; - } - public Builder addLinks(com.basho.riak.pbc.RPB.RpbLink value) { - if (value == null) { - throw new NullPointerException(); - } - if (result.links_.isEmpty()) { - result.links_ = new java.util.ArrayList(); - } - result.links_.add(value); - return this; - } - public Builder addLinks(com.basho.riak.pbc.RPB.RpbLink.Builder builderForValue) { - if (result.links_.isEmpty()) { - result.links_ = new java.util.ArrayList(); - } - result.links_.add(builderForValue.build()); - return this; - } - public Builder addAllLinks( - java.lang.Iterable values) { - if (result.links_.isEmpty()) { - result.links_ = new java.util.ArrayList(); - } - super.addAll(values, result.links_); - return this; - } - public Builder clearLinks() { - result.links_ = java.util.Collections.emptyList(); - return this; - } - - // optional uint32 last_mod = 7; - public boolean hasLastMod() { - return result.hasLastMod(); - } - public int getLastMod() { - return result.getLastMod(); - } - public Builder setLastMod(int value) { - result.hasLastMod = true; - result.lastMod_ = value; - return this; - } - public Builder clearLastMod() { - result.hasLastMod = false; - result.lastMod_ = 0; - return this; - } - - // optional uint32 last_mod_usecs = 8; - public boolean hasLastModUsecs() { - return result.hasLastModUsecs(); - } - public int getLastModUsecs() { - return result.getLastModUsecs(); - } - public Builder setLastModUsecs(int value) { - result.hasLastModUsecs = true; - result.lastModUsecs_ = value; - return this; - } - public Builder clearLastModUsecs() { - result.hasLastModUsecs = false; - result.lastModUsecs_ = 0; - return this; - } - - // repeated .RpbPair usermeta = 9; - public java.util.List getUsermetaList() { - return java.util.Collections.unmodifiableList(result.usermeta_); - } - public int getUsermetaCount() { - return result.getUsermetaCount(); - } - public com.basho.riak.pbc.RPB.RpbPair getUsermeta(int index) { - return result.getUsermeta(index); - } - public Builder setUsermeta(int index, com.basho.riak.pbc.RPB.RpbPair value) { - if (value == null) { - throw new NullPointerException(); - } - result.usermeta_.set(index, value); - return this; - } - public Builder setUsermeta(int index, com.basho.riak.pbc.RPB.RpbPair.Builder builderForValue) { - result.usermeta_.set(index, builderForValue.build()); - return this; - } - public Builder addUsermeta(com.basho.riak.pbc.RPB.RpbPair value) { - if (value == null) { - throw new NullPointerException(); - } - if (result.usermeta_.isEmpty()) { - result.usermeta_ = new java.util.ArrayList(); - } - result.usermeta_.add(value); - return this; - } - public Builder addUsermeta(com.basho.riak.pbc.RPB.RpbPair.Builder builderForValue) { - if (result.usermeta_.isEmpty()) { - result.usermeta_ = new java.util.ArrayList(); - } - result.usermeta_.add(builderForValue.build()); - return this; - } - public Builder addAllUsermeta( - java.lang.Iterable values) { - if (result.usermeta_.isEmpty()) { - result.usermeta_ = new java.util.ArrayList(); - } - super.addAll(values, result.usermeta_); - return this; - } - public Builder clearUsermeta() { - result.usermeta_ = java.util.Collections.emptyList(); - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbContent) - } - - static { - defaultInstance = new RpbContent(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbContent) - } - - public static final class RpbPair extends - com.google.protobuf.GeneratedMessage { - // Use RpbPair.newBuilder() to construct. - private RpbPair() { - initFields(); - } - private RpbPair(boolean noInit) {} - - private static final RpbPair defaultInstance; - public static RpbPair getDefaultInstance() { - return defaultInstance; - } - - public RpbPair getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbPair_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbPair_fieldAccessorTable; - } - - // required bytes key = 1; - public static final int KEY_FIELD_NUMBER = 1; - private boolean hasKey; - private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasKey() { return hasKey; } - public com.google.protobuf.ByteString getKey() { return key_; } - - // optional bytes value = 2; - public static final int VALUE_FIELD_NUMBER = 2; - private boolean hasValue; - private com.google.protobuf.ByteString value_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasValue() { return hasValue; } - public com.google.protobuf.ByteString getValue() { return value_; } - - private void initFields() { - } - public final boolean isInitialized() { - if (!hasKey) return false; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (hasKey()) { - output.writeBytes(1, getKey()); - } - if (hasValue()) { - output.writeBytes(2, getValue()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (hasKey()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getKey()); - } - if (hasValue()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getValue()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbPair parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPair parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPair parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPair parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPair parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPair parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPair parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbPair parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbPair parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbPair parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbPair prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbPair result; - - // Construct using com.trifork.riak.RPB.RpbPair.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbPair(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbPair internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbPair(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbPair.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbPair getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbPair.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbPair build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbPair buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbPair buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - com.basho.riak.pbc.RPB.RpbPair returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbPair) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbPair)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbPair other) { - if (other == com.basho.riak.pbc.RPB.RpbPair.getDefaultInstance()) return this; - if (other.hasKey()) { - setKey(other.getKey()); - } - if (other.hasValue()) { - setValue(other.getValue()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - setKey(input.readBytes()); - break; - } - case 18: { - setValue(input.readBytes()); - break; - } - } - } - } - - - // required bytes key = 1; - public boolean hasKey() { - return result.hasKey(); - } - public com.google.protobuf.ByteString getKey() { - return result.getKey(); - } - public Builder setKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasKey = true; - result.key_ = value; - return this; - } - public Builder clearKey() { - result.hasKey = false; - result.key_ = getDefaultInstance().getKey(); - return this; - } - - // optional bytes value = 2; - public boolean hasValue() { - return result.hasValue(); - } - public com.google.protobuf.ByteString getValue() { - return result.getValue(); - } - public Builder setValue(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasValue = true; - result.value_ = value; - return this; - } - public Builder clearValue() { - result.hasValue = false; - result.value_ = getDefaultInstance().getValue(); - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbPair) - } - - static { - defaultInstance = new RpbPair(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbPair) - } - - public static final class RpbLink extends - com.google.protobuf.GeneratedMessage { - // Use RpbLink.newBuilder() to construct. - private RpbLink() { - initFields(); - } - private RpbLink(boolean noInit) {} - - private static final RpbLink defaultInstance; - public static RpbLink getDefaultInstance() { - return defaultInstance; - } - - public RpbLink getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbLink_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbLink_fieldAccessorTable; - } - - // optional bytes bucket = 1; - public static final int BUCKET_FIELD_NUMBER = 1; - private boolean hasBucket; - private com.google.protobuf.ByteString bucket_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasBucket() { return hasBucket; } - public com.google.protobuf.ByteString getBucket() { return bucket_; } - - // optional bytes key = 2; - public static final int KEY_FIELD_NUMBER = 2; - private boolean hasKey; - private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasKey() { return hasKey; } - public com.google.protobuf.ByteString getKey() { return key_; } - - // optional bytes tag = 3; - public static final int TAG_FIELD_NUMBER = 3; - private boolean hasTag; - private com.google.protobuf.ByteString tag_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasTag() { return hasTag; } - public com.google.protobuf.ByteString getTag() { return tag_; } - - private void initFields() { - } - public final boolean isInitialized() { - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (hasBucket()) { - output.writeBytes(1, getBucket()); - } - if (hasKey()) { - output.writeBytes(2, getKey()); - } - if (hasTag()) { - output.writeBytes(3, getTag()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (hasBucket()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getBucket()); - } - if (hasKey()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getKey()); - } - if (hasTag()) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, getTag()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbLink parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbLink parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbLink parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbLink parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbLink parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbLink parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbLink parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbLink parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbLink parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbLink parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbLink prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbLink result; - - // Construct using com.trifork.riak.RPB.RpbLink.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbLink(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbLink internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbLink(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbLink.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbLink getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbLink.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbLink build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbLink buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbLink buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - com.basho.riak.pbc.RPB.RpbLink returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbLink) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbLink)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbLink other) { - if (other == com.basho.riak.pbc.RPB.RpbLink.getDefaultInstance()) return this; - if (other.hasBucket()) { - setBucket(other.getBucket()); - } - if (other.hasKey()) { - setKey(other.getKey()); - } - if (other.hasTag()) { - setTag(other.getTag()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 10: { - setBucket(input.readBytes()); - break; - } - case 18: { - setKey(input.readBytes()); - break; - } - case 26: { - setTag(input.readBytes()); - break; - } - } - } - } - - - // optional bytes bucket = 1; - public boolean hasBucket() { - return result.hasBucket(); - } - public com.google.protobuf.ByteString getBucket() { - return result.getBucket(); - } - public Builder setBucket(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasBucket = true; - result.bucket_ = value; - return this; - } - public Builder clearBucket() { - result.hasBucket = false; - result.bucket_ = getDefaultInstance().getBucket(); - return this; - } - - // optional bytes key = 2; - public boolean hasKey() { - return result.hasKey(); - } - public com.google.protobuf.ByteString getKey() { - return result.getKey(); - } - public Builder setKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasKey = true; - result.key_ = value; - return this; - } - public Builder clearKey() { - result.hasKey = false; - result.key_ = getDefaultInstance().getKey(); - return this; - } - - // optional bytes tag = 3; - public boolean hasTag() { - return result.hasTag(); - } - public com.google.protobuf.ByteString getTag() { - return result.getTag(); - } - public Builder setTag(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasTag = true; - result.tag_ = value; - return this; - } - public Builder clearTag() { - result.hasTag = false; - result.tag_ = getDefaultInstance().getTag(); - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbLink) - } - - static { - defaultInstance = new RpbLink(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbLink) - } - - public static final class RpbBucketProps extends - com.google.protobuf.GeneratedMessage { - // Use RpbBucketProps.newBuilder() to construct. - private RpbBucketProps() { - initFields(); - } - private RpbBucketProps(boolean noInit) {} - - private static final RpbBucketProps defaultInstance; - public static RpbBucketProps getDefaultInstance() { - return defaultInstance; - } - - public RpbBucketProps getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.basho.riak.pbc.RPB.internal_static_RpbBucketProps_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.basho.riak.pbc.RPB.internal_static_RpbBucketProps_fieldAccessorTable; - } - - // optional uint32 n_val = 1; - public static final int N_VAL_FIELD_NUMBER = 1; - private boolean hasNVal; - private int nVal_ = 0; - public boolean hasNVal() { return hasNVal; } - public int getNVal() { return nVal_; } - - // optional bool allow_mult = 2; - public static final int ALLOW_MULT_FIELD_NUMBER = 2; - private boolean hasAllowMult; - private boolean allowMult_ = false; - public boolean hasAllowMult() { return hasAllowMult; } - public boolean getAllowMult() { return allowMult_; } - - private void initFields() { - } - public final boolean isInitialized() { - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (hasNVal()) { - output.writeUInt32(1, getNVal()); - } - if (hasAllowMult()) { - output.writeBool(2, getAllowMult()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (hasNVal()) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, getNVal()); - } - if (hasAllowMult()) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(2, getAllowMult()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - public static com.basho.riak.pbc.RPB.RpbBucketProps parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbBucketProps parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbBucketProps parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbBucketProps parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbBucketProps parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbBucketProps parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbBucketProps parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbBucketProps parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.basho.riak.pbc.RPB.RpbBucketProps parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.basho.riak.pbc.RPB.RpbBucketProps parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.basho.riak.pbc.RPB.RpbBucketProps prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder { - private com.basho.riak.pbc.RPB.RpbBucketProps result; - - // Construct using com.trifork.riak.RPB.RpbBucketProps.newBuilder() - private Builder() {} - - private static Builder create() { - Builder builder = new Builder(); - builder.result = new com.basho.riak.pbc.RPB.RpbBucketProps(); - return builder; - } - - protected com.basho.riak.pbc.RPB.RpbBucketProps internalGetResult() { - return result; - } - - public Builder clear() { - if (result == null) { - throw new IllegalStateException( - "Cannot call clear() after build()."); - } - result = new com.basho.riak.pbc.RPB.RpbBucketProps(); - return this; - } - - public Builder clone() { - return create().mergeFrom(result); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.basho.riak.pbc.RPB.RpbBucketProps.getDescriptor(); - } - - public com.basho.riak.pbc.RPB.RpbBucketProps getDefaultInstanceForType() { - return com.basho.riak.pbc.RPB.RpbBucketProps.getDefaultInstance(); - } - - public boolean isInitialized() { - return result.isInitialized(); - } - public com.basho.riak.pbc.RPB.RpbBucketProps build() { - if (result != null && !isInitialized()) { - throw newUninitializedMessageException(result); - } - return buildPartial(); - } - - private com.basho.riak.pbc.RPB.RpbBucketProps buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - if (!isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return buildPartial(); - } - - public com.basho.riak.pbc.RPB.RpbBucketProps buildPartial() { - if (result == null) { - throw new IllegalStateException( - "build() has already been called on this Builder."); - } - com.basho.riak.pbc.RPB.RpbBucketProps returnMe = result; - result = null; - return returnMe; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.basho.riak.pbc.RPB.RpbBucketProps) { - return mergeFrom((com.basho.riak.pbc.RPB.RpbBucketProps)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.basho.riak.pbc.RPB.RpbBucketProps other) { - if (other == com.basho.riak.pbc.RPB.RpbBucketProps.getDefaultInstance()) return this; - if (other.hasNVal()) { - setNVal(other.getNVal()); - } - if (other.hasAllowMult()) { - setAllowMult(other.getAllowMult()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - return this; - } - break; - } - case 8: { - setNVal(input.readUInt32()); - break; - } - case 16: { - setAllowMult(input.readBool()); - break; - } - } - } - } - - - // optional uint32 n_val = 1; - public boolean hasNVal() { - return result.hasNVal(); - } - public int getNVal() { - return result.getNVal(); - } - public Builder setNVal(int value) { - result.hasNVal = true; - result.nVal_ = value; - return this; - } - public Builder clearNVal() { - result.hasNVal = false; - result.nVal_ = 0; - return this; - } - - // optional bool allow_mult = 2; - public boolean hasAllowMult() { - return result.hasAllowMult(); - } - public boolean getAllowMult() { - return result.getAllowMult(); - } - public Builder setAllowMult(boolean value) { - result.hasAllowMult = true; - result.allowMult_ = value; - return this; - } - public Builder clearAllowMult() { - result.hasAllowMult = false; - result.allowMult_ = false; - return this; - } - - // @@protoc_insertion_point(builder_scope:RpbBucketProps) - } - - static { - defaultInstance = new RpbBucketProps(true); - com.basho.riak.pbc.RPB.internalForceInit(); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:RpbBucketProps) - } - - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbErrorResp_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbErrorResp_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbGetClientIdResp_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbGetClientIdResp_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbSetClientIdReq_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbSetClientIdReq_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbGetServerInfoResp_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbGetServerInfoResp_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbGetReq_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbGetReq_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbGetResp_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbGetResp_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbPutReq_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbPutReq_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbPutResp_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbPutResp_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbDelReq_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbDelReq_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbListBucketsResp_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbListBucketsResp_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbListKeysReq_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbListKeysReq_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbListKeysResp_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbListKeysResp_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbGetBucketReq_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbGetBucketReq_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbGetBucketResp_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbGetBucketResp_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbSetBucketReq_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbSetBucketReq_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbMapRedReq_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbMapRedReq_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbMapRedResp_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbMapRedResp_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbContent_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbContent_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbPair_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbPair_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbLink_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbLink_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_RpbBucketProps_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_RpbBucketProps_fieldAccessorTable; - - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - java.lang.String[] descriptorData = { - "\n\024src/riakclient.proto\"/\n\014RpbErrorResp\022\016" + - "\n\006errmsg\030\001 \002(\014\022\017\n\007errcode\030\002 \002(\r\"\'\n\022RpbGe" + - "tClientIdResp\022\021\n\tclient_id\030\001 \002(\014\"&\n\021RpbS" + - "etClientIdReq\022\021\n\tclient_id\030\001 \002(\014\"<\n\024RpbG" + - "etServerInfoResp\022\014\n\004node\030\001 \001(\014\022\026\n\016server" + - "_version\030\002 \001(\014\"3\n\tRpbGetReq\022\016\n\006bucket\030\001 " + - "\002(\014\022\013\n\003key\030\002 \002(\014\022\t\n\001r\030\003 \001(\r\":\n\nRpbGetRes" + - "p\022\034\n\007content\030\001 \003(\0132\013.RpbContent\022\016\n\006vcloc" + - "k\030\002 \001(\014\"\202\001\n\tRpbPutReq\022\016\n\006bucket\030\001 \002(\014\022\013\n" + - "\003key\030\002 \002(\014\022\016\n\006vclock\030\003 \001(\014\022\034\n\007content\030\004 ", - "\002(\0132\013.RpbContent\022\t\n\001w\030\005 \001(\r\022\n\n\002dw\030\006 \001(\r\022" + - "\023\n\013return_body\030\007 \001(\010\";\n\nRpbPutResp\022\035\n\010co" + - "ntents\030\001 \003(\0132\013.RpbContent\022\016\n\006vclock\030\002 \001(" + - "\014\"4\n\tRpbDelReq\022\016\n\006bucket\030\001 \002(\014\022\013\n\003key\030\002 " + - "\002(\014\022\n\n\002rw\030\003 \001(\r\"%\n\022RpbListBucketsResp\022\017\n" + - "\007buckets\030\001 \003(\014\" \n\016RpbListKeysReq\022\016\n\006buck" + - "et\030\001 \002(\014\"-\n\017RpbListKeysResp\022\014\n\004keys\030\001 \003(" + - "\014\022\014\n\004done\030\002 \001(\010\"!\n\017RpbGetBucketReq\022\016\n\006bu" + - "cket\030\001 \002(\014\"2\n\020RpbGetBucketResp\022\036\n\005props\030" + - "\001 \002(\0132\017.RpbBucketProps\"A\n\017RpbSetBucketRe", - "q\022\016\n\006bucket\030\001 \002(\014\022\036\n\005props\030\002 \002(\0132\017.RpbBu" + - "cketProps\"5\n\014RpbMapRedReq\022\017\n\007request\030\001 \002" + - "(\014\022\024\n\014content_type\030\002 \002(\014\">\n\rRpbMapRedRes" + - "p\022\r\n\005phase\030\001 \001(\r\022\020\n\010response\030\002 \001(\014\022\014\n\004do" + - "ne\030\003 \001(\010\"\311\001\n\nRpbContent\022\r\n\005value\030\001 \002(\014\022\024" + - "\n\014content_type\030\002 \001(\014\022\017\n\007charset\030\003 \001(\014\022\030\n" + - "\020content_encoding\030\004 \001(\014\022\014\n\004vtag\030\005 \001(\014\022\027\n" + - "\005links\030\006 \003(\0132\010.RpbLink\022\020\n\010last_mod\030\007 \001(\r" + - "\022\026\n\016last_mod_usecs\030\010 \001(\r\022\032\n\010usermeta\030\t \003" + - "(\0132\010.RpbPair\"%\n\007RpbPair\022\013\n\003key\030\001 \002(\014\022\r\n\005", - "value\030\002 \001(\014\"3\n\007RpbLink\022\016\n\006bucket\030\001 \001(\014\022\013" + - "\n\003key\030\002 \001(\014\022\013\n\003tag\030\003 \001(\014\"3\n\016RpbBucketPro" + - "ps\022\r\n\005n_val\030\001 \001(\r\022\022\n\nallow_mult\030\002 \001(\010B\027\n" + - "\020com.trifork.riakB\003RPB" - }; - com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = - new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { - public com.google.protobuf.ExtensionRegistry assignDescriptors( - com.google.protobuf.Descriptors.FileDescriptor root) { - descriptor = root; - internal_static_RpbErrorResp_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_RpbErrorResp_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbErrorResp_descriptor, - new java.lang.String[] { "Errmsg", "Errcode", }, - com.basho.riak.pbc.RPB.RpbErrorResp.class, - com.basho.riak.pbc.RPB.RpbErrorResp.Builder.class); - internal_static_RpbGetClientIdResp_descriptor = - getDescriptor().getMessageTypes().get(1); - internal_static_RpbGetClientIdResp_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbGetClientIdResp_descriptor, - new java.lang.String[] { "ClientId", }, - com.basho.riak.pbc.RPB.RpbGetClientIdResp.class, - com.basho.riak.pbc.RPB.RpbGetClientIdResp.Builder.class); - internal_static_RpbSetClientIdReq_descriptor = - getDescriptor().getMessageTypes().get(2); - internal_static_RpbSetClientIdReq_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbSetClientIdReq_descriptor, - new java.lang.String[] { "ClientId", }, - com.basho.riak.pbc.RPB.RpbSetClientIdReq.class, - com.basho.riak.pbc.RPB.RpbSetClientIdReq.Builder.class); - internal_static_RpbGetServerInfoResp_descriptor = - getDescriptor().getMessageTypes().get(3); - internal_static_RpbGetServerInfoResp_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbGetServerInfoResp_descriptor, - new java.lang.String[] { "Node", "ServerVersion", }, - com.basho.riak.pbc.RPB.RpbGetServerInfoResp.class, - com.basho.riak.pbc.RPB.RpbGetServerInfoResp.Builder.class); - internal_static_RpbGetReq_descriptor = - getDescriptor().getMessageTypes().get(4); - internal_static_RpbGetReq_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbGetReq_descriptor, - new java.lang.String[] { "Bucket", "Key", "R", }, - com.basho.riak.pbc.RPB.RpbGetReq.class, - com.basho.riak.pbc.RPB.RpbGetReq.Builder.class); - internal_static_RpbGetResp_descriptor = - getDescriptor().getMessageTypes().get(5); - internal_static_RpbGetResp_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbGetResp_descriptor, - new java.lang.String[] { "Content", "Vclock", }, - com.basho.riak.pbc.RPB.RpbGetResp.class, - com.basho.riak.pbc.RPB.RpbGetResp.Builder.class); - internal_static_RpbPutReq_descriptor = - getDescriptor().getMessageTypes().get(6); - internal_static_RpbPutReq_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbPutReq_descriptor, - new java.lang.String[] { "Bucket", "Key", "Vclock", "Content", "W", "Dw", "ReturnBody", }, - com.basho.riak.pbc.RPB.RpbPutReq.class, - com.basho.riak.pbc.RPB.RpbPutReq.Builder.class); - internal_static_RpbPutResp_descriptor = - getDescriptor().getMessageTypes().get(7); - internal_static_RpbPutResp_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbPutResp_descriptor, - new java.lang.String[] { "Contents", "Vclock", }, - com.basho.riak.pbc.RPB.RpbPutResp.class, - com.basho.riak.pbc.RPB.RpbPutResp.Builder.class); - internal_static_RpbDelReq_descriptor = - getDescriptor().getMessageTypes().get(8); - internal_static_RpbDelReq_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbDelReq_descriptor, - new java.lang.String[] { "Bucket", "Key", "Rw", }, - com.basho.riak.pbc.RPB.RpbDelReq.class, - com.basho.riak.pbc.RPB.RpbDelReq.Builder.class); - internal_static_RpbListBucketsResp_descriptor = - getDescriptor().getMessageTypes().get(9); - internal_static_RpbListBucketsResp_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbListBucketsResp_descriptor, - new java.lang.String[] { "Buckets", }, - com.basho.riak.pbc.RPB.RpbListBucketsResp.class, - com.basho.riak.pbc.RPB.RpbListBucketsResp.Builder.class); - internal_static_RpbListKeysReq_descriptor = - getDescriptor().getMessageTypes().get(10); - internal_static_RpbListKeysReq_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbListKeysReq_descriptor, - new java.lang.String[] { "Bucket", }, - com.basho.riak.pbc.RPB.RpbListKeysReq.class, - com.basho.riak.pbc.RPB.RpbListKeysReq.Builder.class); - internal_static_RpbListKeysResp_descriptor = - getDescriptor().getMessageTypes().get(11); - internal_static_RpbListKeysResp_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbListKeysResp_descriptor, - new java.lang.String[] { "Keys", "Done", }, - com.basho.riak.pbc.RPB.RpbListKeysResp.class, - com.basho.riak.pbc.RPB.RpbListKeysResp.Builder.class); - internal_static_RpbGetBucketReq_descriptor = - getDescriptor().getMessageTypes().get(12); - internal_static_RpbGetBucketReq_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbGetBucketReq_descriptor, - new java.lang.String[] { "Bucket", }, - com.basho.riak.pbc.RPB.RpbGetBucketReq.class, - com.basho.riak.pbc.RPB.RpbGetBucketReq.Builder.class); - internal_static_RpbGetBucketResp_descriptor = - getDescriptor().getMessageTypes().get(13); - internal_static_RpbGetBucketResp_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbGetBucketResp_descriptor, - new java.lang.String[] { "Props", }, - com.basho.riak.pbc.RPB.RpbGetBucketResp.class, - com.basho.riak.pbc.RPB.RpbGetBucketResp.Builder.class); - internal_static_RpbSetBucketReq_descriptor = - getDescriptor().getMessageTypes().get(14); - internal_static_RpbSetBucketReq_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbSetBucketReq_descriptor, - new java.lang.String[] { "Bucket", "Props", }, - com.basho.riak.pbc.RPB.RpbSetBucketReq.class, - com.basho.riak.pbc.RPB.RpbSetBucketReq.Builder.class); - internal_static_RpbMapRedReq_descriptor = - getDescriptor().getMessageTypes().get(15); - internal_static_RpbMapRedReq_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbMapRedReq_descriptor, - new java.lang.String[] { "Request", "ContentType", }, - com.basho.riak.pbc.RPB.RpbMapRedReq.class, - com.basho.riak.pbc.RPB.RpbMapRedReq.Builder.class); - internal_static_RpbMapRedResp_descriptor = - getDescriptor().getMessageTypes().get(16); - internal_static_RpbMapRedResp_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbMapRedResp_descriptor, - new java.lang.String[] { "Phase", "Response", "Done", }, - com.basho.riak.pbc.RPB.RpbMapRedResp.class, - com.basho.riak.pbc.RPB.RpbMapRedResp.Builder.class); - internal_static_RpbContent_descriptor = - getDescriptor().getMessageTypes().get(17); - internal_static_RpbContent_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbContent_descriptor, - new java.lang.String[] { "Value", "ContentType", "Charset", "ContentEncoding", "Vtag", "Links", "LastMod", "LastModUsecs", "Usermeta", }, - com.basho.riak.pbc.RPB.RpbContent.class, - com.basho.riak.pbc.RPB.RpbContent.Builder.class); - internal_static_RpbPair_descriptor = - getDescriptor().getMessageTypes().get(18); - internal_static_RpbPair_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbPair_descriptor, - new java.lang.String[] { "Key", "Value", }, - com.basho.riak.pbc.RPB.RpbPair.class, - com.basho.riak.pbc.RPB.RpbPair.Builder.class); - internal_static_RpbLink_descriptor = - getDescriptor().getMessageTypes().get(19); - internal_static_RpbLink_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbLink_descriptor, - new java.lang.String[] { "Bucket", "Key", "Tag", }, - com.basho.riak.pbc.RPB.RpbLink.class, - com.basho.riak.pbc.RPB.RpbLink.Builder.class); - internal_static_RpbBucketProps_descriptor = - getDescriptor().getMessageTypes().get(20); - internal_static_RpbBucketProps_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_RpbBucketProps_descriptor, - new java.lang.String[] { "NVal", "AllowMult", }, - com.basho.riak.pbc.RPB.RpbBucketProps.class, - com.basho.riak.pbc.RPB.RpbBucketProps.Builder.class); - return null; - } - }; - com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }, assigner); - } - - public static void internalForceInit() {} - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/src/main/java/com/basho/riak/pbc/RequestMeta.java b/src/main/java/com/basho/riak/pbc/RequestMeta.java deleted file mode 100644 index a36e9d439..000000000 --- a/src/main/java/com/basho/riak/pbc/RequestMeta.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * This file is part of riak-java-pb-client - * - * Copyright (c) 2010 by Trifork - * - * 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.pbc; - -import com.google.protobuf.ByteString; - -public class RequestMeta implements IRequestMeta { - - Boolean returnBody; - Integer writeQuorum; - Integer durableWriteQuorum; - String contentType; - - public RequestMeta() { - } - - /* (non-Javadoc) - * @see com.trifork.riak.IRequestMeta#preparePut(com.trifork.riak.RPB.RpbPutReq.Builder) - */ - public void preparePut(RPB.RpbPutReq.Builder builder) { - - if (returnBody != null) { - builder.setReturnBody(returnBody.booleanValue()); - } - - if (writeQuorum != null) { - builder.setW(writeQuorum.intValue()); - } - - if (durableWriteQuorum != null) { - builder.setDw(durableWriteQuorum.intValue()); - } - } - - /* (non-Javadoc) - * @see com.trifork.riak.IRequestMeta#returnBody(boolean) - */ - public IRequestMeta returnBody(boolean ret) { - returnBody = Boolean.valueOf(ret); - return this; - } - - /* (non-Javadoc) - * @see com.trifork.riak.IRequestMeta#w(int) - */ - public IRequestMeta w(int w) { - writeQuorum = new Integer(w); - return this; - } - - /* (non-Javadoc) - * @see com.trifork.riak.IRequestMeta#dw(int) - */ - public IRequestMeta dw(int dw) { - durableWriteQuorum = new Integer(dw); - return this; - } - - /* (non-Javadoc) - * @see com.trifork.riak.IRequestMeta#contentType(java.lang.String) - */ - public IRequestMeta contentType(String contentType) { - this.contentType = contentType; - return this; - } - - /* (non-Javadoc) - * @see com.trifork.riak.IRequestMeta#getContentType() - */ - public ByteString getContentType() { - return ByteString.copyFromUtf8(contentType); - } -} diff --git a/src/main/java/com/basho/riak/pbc/RiakClient.java b/src/main/java/com/basho/riak/pbc/RiakClient.java deleted file mode 100644 index f7ba6b2f9..000000000 --- a/src/main/java/com/basho/riak/pbc/RiakClient.java +++ /dev/null @@ -1,551 +0,0 @@ -/** - * This file is part of riak-java-pb-client - * - * Copyright (c) 2010 by Trifork - * - * 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.pbc; - -import java.io.DataOutputStream; -import java.io.IOException; -import java.net.InetAddress; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.prefs.BackingStoreException; -import java.util.prefs.Preferences; -import org.apache.commons.codec.binary.Base64; - -import org.json.JSONObject; - -import com.basho.riak.client.util.Constants; -import com.basho.riak.pbc.RPB.RpbDelReq; -import com.basho.riak.pbc.RPB.RpbGetClientIdResp; -import com.basho.riak.pbc.RPB.RpbGetReq; -import com.basho.riak.pbc.RPB.RpbGetResp; -import com.basho.riak.pbc.RPB.RpbGetServerInfoResp; -import com.basho.riak.pbc.RPB.RpbListBucketsResp; -import com.basho.riak.pbc.RPB.RpbMapRedReq; -import com.basho.riak.pbc.RPB.RpbPutReq; -import com.basho.riak.pbc.RPB.RpbPutResp; -import com.basho.riak.pbc.RPB.RpbSetClientIdReq; -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -//import com.trifork.riak.RPB.RpbListKeysResp; -//import com.trifork.riak.RPB.RpbMapRedResp; - -public class RiakClient implements RiakMessageCodes { - - private static final RiakObject[] NO_RIAK_OBJECTS = new RiakObject[0]; - private static final ByteString[] NO_BYTE_STRINGS = new ByteString[0]; - //private static final String[] NO_STRINGS = new String[0]; - //private static final MapReduceResponse[] NO_MAP_REDUCE_RESPONSES = new MapReduceResponse[0]; - - private String node; - private String serverVersion; - private InetAddress addr; - private int port; - - /** - * if this has been set (or gotten) then it will be applied to new - * connections - */ - private volatile ByteString clientID; - - public RiakClient(String host) throws IOException { - this(host, RiakConnection.DEFAULT_RIAK_PB_PORT); - } - - public RiakClient(String host, int port) throws IOException { - this(InetAddress.getByName(host), port); - } - - public RiakClient(InetAddress addr, int port) throws IOException { - this.addr = addr; - this.port = port; - } - - private ThreadLocal connections = new ThreadLocal(); - - RiakConnection getConnection() throws IOException { - return getConnection(true); - } - - RiakConnection getConnection(boolean setClientId) throws IOException { - RiakConnection c = connections.get(); - if (c == null || !c.endIdleAndCheckValid()) { - c = new RiakConnection(addr, port); - - if (this.clientID != null && setClientId) { - connections.set(c); - setClientID(clientID); - } - } - connections.set(null); - return c; - } - - void release(RiakConnection c) { - RiakConnection cc = connections.get(); - if (cc == null) { - c.beginIdle(); - connections.set(c); - } else { - c.close(); - } - } - - /** - * helper method to use a reasonable default client id - * - * @throws IOException - */ - public void prepareClientID() throws IOException { - Preferences prefs = Preferences.userNodeForPackage(RiakClient.class); - - String clid = prefs.get("client_id", null); - if (clid == null) { - SecureRandom sr; - try { - sr = SecureRandom.getInstance("SHA1PRNG"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - byte[] data = new byte[6]; - sr.nextBytes(data); - clid = new String(Base64.encodeBase64Chunked(data)); - prefs.put("client_id", clid); - try { - prefs.flush(); - } catch (BackingStoreException e) { - throw new IOException(e.toString()); - } - } - - setClientID(clid); - } - - public void ping() throws IOException { - RiakConnection c = getConnection(); - try { - c.send(MSG_PingReq); - c.receive_code(MSG_PingResp); - } finally { - release(c); - } - } - - public void setClientID(String id) throws IOException { - if(id == null || id.length() < Constants.RIAK_CLIENT_ID_LENGTH) { - throw new IllegalArgumentException("Client ID must be at least " + Constants.RIAK_CLIENT_ID_LENGTH + " bytes long"); - } - setClientID(ByteString.copyFrom(id.getBytes(), 0, Constants.RIAK_CLIENT_ID_LENGTH)); - } - - // ///////////////////// - - public void setClientID(ByteString id) throws IOException { - if(id.size() > Constants.RIAK_CLIENT_ID_LENGTH) { - id = ByteString.copyFrom(id.toByteArray(), 0, Constants.RIAK_CLIENT_ID_LENGTH); - } - RpbSetClientIdReq req = RPB.RpbSetClientIdReq.newBuilder().setClientId( - id).build(); - RiakConnection c = getConnection(false); - try { - c.send(MSG_SetClientIdReq, req); - c.receive_code(MSG_SetClientIdResp); - } finally { - release(c); - } - - this.clientID = id; - } - - public String getClientID() throws IOException { - RiakConnection c = getConnection(); - try { - c.send(MSG_GetClientIdReq); - byte[] data = c.receive(MSG_GetClientIdResp); - if (data == null) - return null; - RpbGetClientIdResp res = RPB.RpbGetClientIdResp.parseFrom(data); - clientID = res.getClientId(); - return clientID.toStringUtf8(); - } finally { - release(c); - } - } - - public Map getServerInfo() throws IOException { - RiakConnection c = getConnection(); - try { - c.send(MSG_GetServerInfoReq); - byte[] data = c.receive(MSG_GetServerInfoResp); - if (data == null) - return Collections.emptyMap(); - - RpbGetServerInfoResp res = RPB.RpbGetServerInfoResp.parseFrom(data); - if (res.hasNode()) { - this.node = res.getNode().toStringUtf8(); - } - if (res.hasServerVersion()) { - this.serverVersion = res.getServerVersion().toStringUtf8(); - } - Map result = new HashMap(); - result.put("node", node); - result.put("server_version", serverVersion); - return result; - } finally { - release(c); - } - } - - // ///////////////////// - - public RiakObject[] fetch(String bucket, String key, int readQuorum) - throws IOException { - return fetch(ByteString.copyFromUtf8(bucket), ByteString - .copyFromUtf8(key), readQuorum); - } - - public RiakObject[] fetch(ByteString bucket, ByteString key, int readQuorum) - throws IOException { - RpbGetReq req = RPB.RpbGetReq.newBuilder().setBucket(bucket) - .setKey(key).setR(readQuorum).build(); - - RiakConnection c = getConnection(); - try { - c.send(MSG_GetReq, req); - return process_fetch_reply(c, bucket, key); - } finally { - release(c); - } - - } - - public RiakObject[] fetch(String bucket, String key) throws IOException { - return fetch(ByteString.copyFromUtf8(bucket), ByteString - .copyFromUtf8(key)); - } - - public RiakObject[] fetch(ByteString bucket, ByteString key) - throws IOException { - RpbGetReq req = RPB.RpbGetReq.newBuilder().setBucket(bucket) - .setKey(key).build(); - - RiakConnection c = getConnection(); - try { - c.send(MSG_GetReq, req); - return process_fetch_reply(c, bucket, key); - } finally { - release(c); - } - } - - private RiakObject[] process_fetch_reply(RiakConnection c, - ByteString bucket, ByteString key) throws IOException, - InvalidProtocolBufferException { - byte[] rep = c.receive(MSG_GetResp); - - if (rep == null) { - return NO_RIAK_OBJECTS; - } - - RpbGetResp resp = RPB.RpbGetResp.parseFrom(rep); - int count = resp.getContentCount(); - RiakObject[] out = new RiakObject[count]; - ByteString vclock = resp.getVclock(); - for (int i = 0; i < count; i++) { - out[i] = new RiakObject(vclock, bucket, key, resp.getContent(i)); - } - return out; - } - - // ///////////////////// - - public ByteString[] store(RiakObject[] values, RequestMeta meta) - throws IOException { - - RiakConnection c = getConnection(); - try { - BulkReader reader = new BulkReader(c, values.length); - Thread worker = new Thread(reader); - worker.start(); - - DataOutputStream dout = c.getOutputStream(); - - for (int i = 0; i < values.length; i++) { - RiakObject value = values[i]; - - RPB.RpbPutReq.Builder builder = RPB.RpbPutReq.newBuilder() - .setBucket(value.getBucketBS()) - .setKey(value.getKeyBS()).setContent( - value.buildContent()); - - if (value.getVclock() != null) { - builder.setVclock(value.getVclock()); - } - - builder.setReturnBody(true); - - if (meta != null) { - - if (meta.writeQuorum != null) { - builder.setW(meta.writeQuorum.intValue()); - } - - if (meta.durableWriteQuorum != null) { - builder.setDw(meta.durableWriteQuorum.intValue()); - } - } - - RpbPutReq req = builder.build(); - - int len = req.getSerializedSize(); - dout.writeInt(len + 1); - dout.write(MSG_PutReq); - req.writeTo(dout); - } - - dout.flush(); - - try { - worker.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - return reader.vclocks; - } finally { - release(c); - } - } - - class BulkReader implements Runnable { - - private ByteString[] vclocks; - private final RiakConnection c; - - public BulkReader(RiakConnection c, int count) { - this.c = c; - this.vclocks = new ByteString[count]; - } - - public void run() { - - try { - for (int i = 0; i < vclocks.length; i++) { - byte[] data = c.receive(MSG_PutResp); - if (data != null) { - RpbPutResp resp = RPB.RpbPutResp.parseFrom(data); - if (resp.hasVclock()) { - vclocks[i] = resp.getVclock(); - } - } - } - } catch (IOException e) { - // TODO - e.printStackTrace(); - } - - } - - } - - public void store(RiakObject value) throws IOException { - store(value, null); - } - - public RiakObject[] store(RiakObject value, IRequestMeta meta) - throws IOException { - - RPB.RpbPutReq.Builder builder = RPB.RpbPutReq.newBuilder().setBucket( - value.getBucketBS()).setKey(value.getKeyBS()).setContent( - value.buildContent()); - - if (value.getVclock() != null) { - builder.setVclock(value.getVclock()); - } - - if (meta != null) { - meta.preparePut(builder); - } - - RiakConnection c = getConnection(); - try { - c.send(MSG_PutReq, builder.build()); - byte[] r = c.receive(MSG_PutResp); - - if (r == null) { - return NO_RIAK_OBJECTS; - } - - RpbPutResp resp = RPB.RpbPutResp.parseFrom(r); - - RiakObject[] res = new RiakObject[resp.getContentsCount()]; - ByteString vclock = resp.getVclock(); - - for (int i = 0; i < res.length; i++) { - res[i] = new RiakObject(vclock, value.getBucketBS(), value - .getKeyBS(), resp.getContents(i)); - } - - return res; - } finally { - release(c); - } - } - - // ///////////////////// - - public void delete(String bucket, String key, int rw) throws IOException { - delete(ByteString.copyFromUtf8(bucket), ByteString.copyFromUtf8(key), - rw); - } - - public void delete(ByteString bucket, ByteString key, int rw) - throws IOException { - RpbDelReq req = RPB.RpbDelReq.newBuilder().setBucket(bucket) - .setKey(key).setRw(rw).build(); - - RiakConnection c = getConnection(); - try { - c.send(MSG_DelReq, req); - c.receive_code(MSG_DelResp); - } finally { - release(c); - } - - } - - public void delete(String bucket, String key) throws IOException { - delete(ByteString.copyFromUtf8(bucket), ByteString.copyFromUtf8(key)); - } - - public void delete(ByteString bucket, ByteString key) throws IOException { - RpbDelReq req = RPB.RpbDelReq.newBuilder().setBucket(bucket) - .setKey(key).build(); - - RiakConnection c = getConnection(); - try { - c.send(MSG_DelReq, req); - c.receive_code(MSG_DelResp); - } finally { - release(c); - } - } - - public ByteString[] listBuckets() throws IOException { - - byte[] data; - RiakConnection c = getConnection(); - try { - c.send(MSG_ListBucketsReq); - - data = c.receive(MSG_ListBucketsResp); - if (data == null) { - return NO_BYTE_STRINGS; - } - } finally { - release(c); - } - - RpbListBucketsResp resp = RPB.RpbListBucketsResp.parseFrom(data); - ByteString[] out = new ByteString[resp.getBucketsCount()]; - for (int i = 0; i < out.length; i++) { - out[i] = resp.getBuckets(i); - } - return out; - } - - public BucketProperties getBucketProperties(ByteString bucket) - throws IOException { - - RiakConnection c = getConnection(); - try { - c.send(MSG_GetBucketReq, RPB.RpbGetBucketReq.newBuilder() - .setBucket(bucket).build()); - - byte[] data = c.receive(MSG_GetBucketResp); - BucketProperties bp = new BucketProperties(); - if (data == null) { - return bp; - } - - bp.init(RPB.RpbGetBucketResp.parseFrom(data)); - return bp; - } finally { - release(c); - } - - } - - public void setBucketProperties(ByteString bucket, BucketProperties props) - throws IOException { - - RPB.RpbSetBucketReq req = RPB.RpbSetBucketReq.newBuilder().setBucket( - bucket).setProps(props.build()).build(); - - RiakConnection c = getConnection(); - try { - c.send(MSG_SetBucketReq, req); - c.receive_code(MSG_SetBucketResp); - } finally { - release(c); - } - } - - // ///////////////////// - - public KeySource listKeys(ByteString bucket) throws IOException { - - RiakConnection c = getConnection(); - c.send(MSG_ListKeysReq, RPB.RpbListKeysReq.newBuilder().setBucket( - bucket).build()); - - return new KeySource(this, c); - } - - // ///////////////////// - - public MapReduceResponseSource mapReduce(JSONObject obj) throws IOException { - return mapReduce(ByteString.copyFromUtf8(obj.toString()), - new RequestMeta().contentType("application/json")); - } - - public MapReduceResponseSource mapReduce(String request, - IRequestMeta meta) throws IOException { - return mapReduce(ByteString.copyFromUtf8(request), meta); - } - - public MapReduceResponseSource mapReduce(ByteString request, - IRequestMeta meta) throws IOException { - RiakConnection c = getConnection(); - - ByteString contentType = meta.getContentType(); - if (contentType == null) { - throw new IllegalArgumentException("no content type"); - } - RpbMapRedReq req = RPB.RpbMapRedReq.newBuilder().setRequest(request) - .setContentType(meta.getContentType()).build(); - - c.send(MSG_MapRedReq, req); - - return new MapReduceResponseSource(this, c, contentType); - } - -} diff --git a/src/main/java/com/basho/riak/pbc/RiakConnection.java b/src/main/java/com/basho/riak/pbc/RiakConnection.java deleted file mode 100644 index 52f5657a2..000000000 --- a/src/main/java/com/basho/riak/pbc/RiakConnection.java +++ /dev/null @@ -1,171 +0,0 @@ -/** - * This file is part of riak-java-pb-client - * - * Copyright (c) 2010 by Trifork - * - * 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.pbc; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.net.InetAddress; -import java.net.Socket; -import java.util.Timer; -import java.util.TimerTask; - -import com.basho.riak.pbc.RPB.RpbErrorResp; -import com.google.protobuf.MessageLite; - -class RiakConnection { - - static final int DEFAULT_RIAK_PB_PORT = 8087; - - private Socket sock; - private DataOutputStream dout; - private DataInputStream din; - - public RiakConnection(String host) throws IOException { - this(host, DEFAULT_RIAK_PB_PORT); - } - - public RiakConnection(String host, int port) throws IOException { - this(InetAddress.getByName(host), port); - } - - public RiakConnection(InetAddress addr, int port) throws IOException { - sock = new Socket(addr, port); - - sock.setSendBufferSize(1024 * 200); - - dout = new DataOutputStream(new BufferedOutputStream(sock - .getOutputStream(), 1024 * 200)); - din = new DataInputStream( - new BufferedInputStream(sock.getInputStream(), 1024 * 200)); - } - - /////////////////////// - - void send(int code, MessageLite req) throws IOException { - int len = req.getSerializedSize(); - dout.writeInt(len + 1); - dout.write(code); - req.writeTo(dout); - dout.flush(); - } - - void send(int code) throws IOException { - dout.writeInt(1); - dout.write(code); - dout.flush(); - } - - byte[] receive(int code) throws IOException { - int len = din.readInt(); - int get_code = din.read(); - - if (code == RiakClient.MSG_ErrorResp) { - RpbErrorResp err = com.basho.riak.pbc.RPB.RpbErrorResp.parseFrom(din); - throw new RiakError(err); - } - - byte[] data = null; - if (len > 1) { - data = new byte[len - 1]; - din.readFully(data); - } - - if (code != get_code) { - throw new IOException("bad message code"); - } - - return data; - } - - void receive_code(int code) throws IOException, RiakError { - int len = din.readInt(); - int get_code = din.read(); - if (code == RiakClient.MSG_ErrorResp) { - RpbErrorResp err = com.basho.riak.pbc.RPB.RpbErrorResp.parseFrom(din); - throw new RiakError(err); - } - if (len != 1 || code != get_code) { - throw new IOException("bad message code"); - } - } - - static Timer TIMER = new Timer("riak-client-timeout-thread", true); - TimerTask idle_timeout; - - public void beginIdle() { - idle_timeout = new TimerTask() { - - @Override - public void run() { - RiakConnection.this.timer_fired(this); - } - }; - - TIMER.schedule(idle_timeout, 1000); - } - - synchronized void timer_fired(TimerTask fired_timer) { - if (idle_timeout != fired_timer) { - // if it is not our current timer, then ignore - return; - } - - close(); - } - - void close() { - if (isClosed()) - return; - - try { - sock.close(); - din = null; - dout = null; - sock = null; - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - synchronized boolean endIdleAndCheckValid() { - TimerTask tt = idle_timeout; - if (tt != null) { tt.cancel(); } - idle_timeout = null; - - if (isClosed()) { - return false; - } else { - return true; - } - } - - public DataOutputStream getOutputStream() { - return dout; - } - - public boolean isClosed() { - return sock == null || sock.isClosed(); - } - - -} diff --git a/src/main/java/com/basho/riak/pbc/RiakLink.java b/src/main/java/com/basho/riak/pbc/RiakLink.java deleted file mode 100644 index b2e09f972..000000000 --- a/src/main/java/com/basho/riak/pbc/RiakLink.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * This file is part of riak-java-pb-client - * - * Copyright (c) 2010 by Trifork - * - * 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.pbc; - -import java.util.ArrayList; -import java.util.List; - -import com.basho.riak.pbc.RPB.RpbLink; -import com.basho.riak.pbc.RPB.RpbLink.Builder; -import com.google.protobuf.ByteString; - -public class RiakLink { - - private final ByteString bucket; - private final ByteString key; - private final ByteString tag; - - RiakLink(RpbLink rpbLink) { - this.bucket = rpbLink.getBucket(); - this.key = rpbLink.getKey(); - this.tag = rpbLink.getTag(); - } - - public RiakLink(String bucket, String key, String tag) { - this.bucket = ByteString.copyFromUtf8(bucket); - this.key = ByteString.copyFromUtf8(key); - this.tag = ByteString.copyFromUtf8(tag); - } - - public RiakLink(ByteString bucket, ByteString key, ByteString tag) { - this.bucket = (bucket); - this.key = (key); - this.tag = (tag); - } - - public static List decode(List list) { - List res = new ArrayList(); - for (int i = 0; i < list.size(); i++) { - res.add ( new RiakLink(list.get(i)) ); - } - return res; - } - - public ByteString getBucket() { - return bucket; - } - - public ByteString getKey() { - return key; - } - - public ByteString getTag() { - return tag; - } - - public RpbLink build() { - Builder b = RpbLink.newBuilder(); - - if (bucket != null) - b.setBucket(bucket); - - if (key != null) - b.setKey(key); - - if (tag != null) - b.setTag(tag); - - return b.build(); - } - -} diff --git a/src/main/java/com/basho/riak/pbc/RiakMessageCodes.java b/src/main/java/com/basho/riak/pbc/RiakMessageCodes.java deleted file mode 100644 index 83e2ea579..000000000 --- a/src/main/java/com/basho/riak/pbc/RiakMessageCodes.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * This file is part of riak-java-pb-client - * - * Copyright (c) 2010 by Trifork - * - * 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.pbc; - -interface RiakMessageCodes { - public static final int MSG_ErrorResp = 0; - public static final int MSG_PingReq = 1; - public static final int MSG_PingResp = 2; - public static final int MSG_GetClientIdReq = 3; - public static final int MSG_GetClientIdResp = 4; - public static final int MSG_SetClientIdReq = 5; - public static final int MSG_SetClientIdResp = 6; - public static final int MSG_GetServerInfoReq = 7; - public static final int MSG_GetServerInfoResp = 8; - public static final int MSG_GetReq = 9; - public static final int MSG_GetResp = 10; - public static final int MSG_PutReq = 11; - public static final int MSG_PutResp = 12; - public static final int MSG_DelReq = 13; - public static final int MSG_DelResp = 14; - public static final int MSG_ListBucketsReq = 15; - public static final int MSG_ListBucketsResp = 16; - public static final int MSG_ListKeysReq = 17; - public static final int MSG_ListKeysResp = 18; - public static final int MSG_GetBucketReq = 19; - public static final int MSG_GetBucketResp = 20; - public static final int MSG_SetBucketReq = 21; - public static final int MSG_SetBucketResp = 22; - public static final int MSG_MapRedReq = 23; - public static final int MSG_MapRedResp = 24; - -} diff --git a/src/main/java/com/basho/riak/pbc/RiakObject.java b/src/main/java/com/basho/riak/pbc/RiakObject.java deleted file mode 100644 index cd331432f..000000000 --- a/src/main/java/com/basho/riak/pbc/RiakObject.java +++ /dev/null @@ -1,204 +0,0 @@ -/** - * This file is part of riak-java-pb-client - * - * Copyright (c) 2010 by Trifork - * - * 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.pbc; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.basho.riak.pbc.RPB.RpbContent; -import com.basho.riak.pbc.RPB.RpbPair; -import com.basho.riak.pbc.RPB.RpbContent.Builder; -import com.google.protobuf.ByteString; - -public class RiakObject { - - private ByteString vclock; - private ByteString bucket; - private ByteString key; - - private ByteString value; - - private String contentType; - private List links = Collections.synchronizedList(new ArrayList()); - private String vtag; - private String contentEncoding; - private String charset; - private Map userMeta; - private Integer lastModified; - private Integer lastModifiedUsec; - - - RiakObject(ByteString vclock, ByteString bucket, ByteString key, RpbContent content) { - this.vclock = vclock; - this.bucket = bucket; - this.key = key; - this.value = content.getValue(); - this.contentType = str(content.getContentType()); - this.charset = str(content.getCharset()); - this.contentEncoding = str(content.getContentEncoding()); - this.vtag = str(content.getVtag()); - this.links = content.getLinksCount() == 0 - ? Collections.synchronizedList(new ArrayList()) - : Collections.synchronizedList(RiakLink.decode(content.getLinksList())); - - if (content.hasLastMod()) { - this.lastModified = new Integer(content.getLastMod()); - this.lastModifiedUsec = new Integer(content.getLastModUsecs()); - } - - if (content.getUsermetaCount() == 0) { - userMeta = Collections.emptyMap(); - } else { - userMeta = new LinkedHashMap(); - for (int i = 0; i < content.getUsermetaCount(); i++) { - RpbPair um = content.getUsermeta(i); - userMeta.put(um.getKey().toStringUtf8(), - str(um.getValue())); - } - } - } - - public RiakObject(ByteString vclock, ByteString bucket, ByteString key, ByteString content) { - this.bucket = bucket; - this.key = key; - this.value = content; - this.vclock = vclock; - } - - public RiakObject(ByteString bucket, ByteString key, ByteString content) { - this.bucket = bucket; - this.key = key; - this.value = content; - } - - public RiakObject(String bucket, String key, byte[] content) { - this.bucket = ByteString.copyFromUtf8(bucket); - this.key = ByteString.copyFromUtf8(key); - this.value = ByteString.copyFrom(content); - } - - public RiakObject(String bucket, String key, String content) { - this.bucket = ByteString.copyFromUtf8(bucket); - this.key = ByteString.copyFromUtf8(key); - this.value = ByteString.copyFromUtf8(content); - } - - private String str(ByteString str) { - if (str == null) return null; - return str.toStringUtf8(); - } - - public ByteString getBucketBS() { - return bucket; - } - - public String getBucket() { - return bucket.toStringUtf8(); - } - - - public ByteString getKeyBS() { - return key; - } - - public String getKey() { - return key.toStringUtf8(); - } - - public ByteString getVclock() { - return vclock; - } - - public ByteString getValue(){ - return value; - } - - RpbContent buildContent() { - Builder b = - RpbContent.newBuilder() - .setValue(value); - - if (contentType != null) { - b.setContentType(ByteString.copyFromUtf8(contentType)); - } - - if (charset != null) { - b.setCharset(ByteString.copyFromUtf8(charset)); - } - - if (contentEncoding != null) { - b.setContentEncoding(ByteString.copyFromUtf8(contentEncoding)); - } - - if (vtag != null) { - b.setVtag(ByteString.copyFromUtf8(vtag)); - } - - if (links.size() != 0) { - RiakLink[] localLinks = null; - synchronized (links) { - localLinks = links.toArray(new RiakLink[links.size()]); - } - for (RiakLink l : localLinks) { - b.addLinks(l.build()); - } - } - - if (lastModified != null) { - b.setLastMod(lastModified); - } - - if (lastModifiedUsec != null) { - b.setLastModUsecs(lastModifiedUsec); - } - - if (userMeta != null && !userMeta.isEmpty()) { - for (Map.Entry ent : userMeta.entrySet()) { - ByteString key = ByteString.copyFromUtf8(ent.getKey()); - com.basho.riak.pbc.RPB.RpbPair.Builder pb = RPB.RpbPair.newBuilder().setKey(key); - if (ent.getValue() != null) { - pb.setValue(ByteString.copyFromUtf8(ent.getValue())); - } - b.addUsermeta(pb); - } - } - - return b.build(); - } - - public void setContentType(String contentType) { - this.contentType = contentType; - } - - public void addLink(String tag, String bucket, String key) { - links.add(new RiakLink(bucket, key, tag)); - } - - public void addLink(ByteString tag, ByteString bucket, ByteString key) { - links.add(new RiakLink(bucket, key, tag)); - } - - public List getLinks() { - return links != null ? Collections.unmodifiableList(links) : Collections.EMPTY_LIST; - } - -} diff --git a/src/main/java/com/basho/riak/pbc/RiakStreamClient.java b/src/main/java/com/basho/riak/pbc/RiakStreamClient.java deleted file mode 100644 index c7915a764..000000000 --- a/src/main/java/com/basho/riak/pbc/RiakStreamClient.java +++ /dev/null @@ -1,110 +0,0 @@ -/** - * This file is part of riak-java-pb-client - * - * Copyright (c) 2010 by Trifork - * - * 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.pbc; - -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.util.Iterator; -import java.util.TimerTask; - -abstract class RiakStreamClient implements Iterable { - - private RiakClient client; - protected RiakConnection conn; - private ReaperTask reaper; - - protected RiakStreamClient(RiakClient client, RiakConnection conn) { - this.client = client; - this.conn = conn; - this.reaper = new ReaperTask(this, conn); - } - - static class ReaperTask extends TimerTask { - - private final RiakConnection conn; - private WeakReference ref; - - ReaperTask (Object holder, RiakConnection conn) { - this.conn = conn; - this.ref = new WeakReference(holder); - RiakConnection.TIMER.scheduleAtFixedRate(this, 1000, 1000); - } - - @Override - public synchronized void run() { - if (ref == null) { - // do nothing; we were explicitly cancelled // - } else if (ref.get() == null) { - - // the reference was lost; cancel this timer and - // close the connection - cancel(); - conn.close(); - } else if (conn.isClosed()) { - cancel(); - } - } - - @Override - public synchronized boolean cancel() { - ref = null; - return super.cancel(); - } - } - - public synchronized void close() { - if (!isClosed()) { - reaper.cancel(); - client.release(conn); - conn = null; - } - } - - public boolean isClosed() { - return conn == null; - } - - public abstract boolean hasNext() throws IOException; - public abstract T next() throws IOException; - - public Iterator iterator() { - return new Iterator() { - - public boolean hasNext() { - try { - return RiakStreamClient.this.hasNext(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public T next() { - try { - return RiakStreamClient.this.next(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } -} diff --git a/src/main/java/com/basho/riak/pbc/mapreduce/ErlangFunction.java b/src/main/java/com/basho/riak/pbc/mapreduce/ErlangFunction.java deleted file mode 100644 index 9dd9fcc0d..000000000 --- a/src/main/java/com/basho/riak/pbc/mapreduce/ErlangFunction.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.pbc.mapreduce; - -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Represents an Erlang function used in a map or reduce phase - * of a map/reduce job - * - */ -public class ErlangFunction implements MapReduceFunction { - - private String module; - private String function; - - /** - * Constructs a new ErlangFunction instance - * @param module Erlang module name - * @param functionName Erlang function name - */ - public ErlangFunction(String module, String functionName) { - this.module = module; - this.function = functionName; - } - - /** - * Converts the function definition to JSON - */ - public JSONObject toJson() { - try { - JSONObject retval = new JSONObject(); - retval.put("language", "erlang"); - retval.put("module", this.module); - retval.put("function", this.function); - return retval; - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to a string"); - } - } - -} diff --git a/src/main/java/com/basho/riak/pbc/mapreduce/JavascriptFunction.java b/src/main/java/com/basho/riak/pbc/mapreduce/JavascriptFunction.java deleted file mode 100644 index 929ffdac7..000000000 --- a/src/main/java/com/basho/riak/pbc/mapreduce/JavascriptFunction.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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.pbc.mapreduce; - -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Represents a Javascript function used in a map or reduce phase - * of a map/reduce job - * - */ -public class JavascriptFunction implements MapReduceFunction { - - private String source; - private MapReduceFunction.Types type; - - /** - * Shortcut for creating a reference to a named - * Javascript function - * @param functionName Name of Javascript function ("Riak.mapValuesJson") - */ - public static JavascriptFunction named(String functionName) { - return new JavascriptFunction(MapReduceFunction.Types.NAMED, functionName); - } - - /** - * Shortcut for creating a reference to an anonymous - * Javascript function - * @param functionSource Javascript function source ("function(v) { return [v]; }") - */ - public static JavascriptFunction anon(String functionSource) { - return new JavascriptFunction(MapReduceFunction.Types.ANONYMOUS, functionSource); - } - - /** - * Converts the function definition to JSON - */ - public JSONObject toJson() { - try { - JSONObject retval = new JSONObject(); - retval.put("language", "javascript"); - if (type == MapReduceFunction.Types.NAMED) { - retval.put("name", this.source); - } - else { - retval.put("source", this.source); - } - - return retval; - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to a string"); - } - } - - private JavascriptFunction(MapReduceFunction.Types functionType, - String functionSource) { - this.type = functionType; - this.source = functionSource; - } - -} diff --git a/src/main/java/com/basho/riak/pbc/mapreduce/LinkFunction.java b/src/main/java/com/basho/riak/pbc/mapreduce/LinkFunction.java deleted file mode 100644 index a797b86db..000000000 --- a/src/main/java/com/basho/riak/pbc/mapreduce/LinkFunction.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.pbc.mapreduce; - -import org.json.JSONException; -import org.json.JSONObject; - -public class LinkFunction implements MapReduceFunction { - - private String bucket = null; - private String tag = null; - - public LinkFunction(String bucket) { - this.bucket = bucket; - } - - public LinkFunction(String bucket, String tag) { - this.bucket = bucket; - this.tag = tag; - } - - public JSONObject toJson() { - try { - JSONObject link = new JSONObject(); - link.put("bucket", this.bucket); - - if (this.tag != null) { - link.put("tag", this.tag); - } - - return link; - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to a string"); - } - } - -} diff --git a/src/main/java/com/basho/riak/pbc/mapreduce/MapReduceBuilder.java b/src/main/java/com/basho/riak/pbc/mapreduce/MapReduceBuilder.java deleted file mode 100644 index e6994a8eb..000000000 --- a/src/main/java/com/basho/riak/pbc/mapreduce/MapReduceBuilder.java +++ /dev/null @@ -1,434 +0,0 @@ -/* - * 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.pbc.mapreduce; - -import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import com.basho.riak.pbc.IRequestMeta; -import com.basho.riak.pbc.MapReduceResponseSource; -import com.basho.riak.pbc.RequestMeta; -import com.basho.riak.pbc.RiakClient; -import com.basho.riak.pbc.RiakObject; -import com.google.protobuf.ByteString; - -/** - * Builds a map/reduce job description and submits it Uses the same chained - * method metaphor as StringBuilder or StringBuffer - */ -public class MapReduceBuilder { - - private static enum Types { - MAP, REDUCE, LINK - } - - private String bucket = null; - private Map> objects = new LinkedHashMap>(); - private List phases = new LinkedList(); - private int timeout = -1; - private RiakClient riak = null; - - /** - * @param riak - * RiakClient instance which is pointing to the map/reduce URL - */ - public MapReduceBuilder(RiakClient riak) { - this.riak = riak; - } - - public MapReduceBuilder() { /* nop */} - - /** - * The {@link RiakClient} to which this map reduce job will be submitted to - * when {@link MapReduceBuilder#submit()} is called. - */ - public RiakClient getRiakClient() { - return riak; - } - - public MapReduceBuilder setRiakClient(RiakClient client) { - riak = client; - return this; - } - - /** - * Gets the name of the Riak bucket the map/reduce job will process - */ - public String getBucket() { - return bucket; - } - - /** - * Sets the name of the Riak bucket the map/reduce job will process - * - * @throws IllegalStateException - * - If objects have already been added to the job - */ - public MapReduceBuilder setBucket(String newBucket) { - if (objects.size() > 0) - throw new IllegalStateException("Cannot map/reduce over buckets and objects"); - bucket = newBucket; - return this; - } - - /** - * Adds a Riak object (bucket name/key pair) to the map/reduce job as inputs - * - * @throws IllegalStateException - * - If a bucket name has already been set on the job - */ - public void addRiakObject(String bucket, String key) { - if (this.bucket != null) - throw new IllegalStateException("Cannot map/reduce over buckets and objects"); - Set keys = objects.get(bucket); - if (keys == null) { - keys = new LinkedHashSet(); - objects.put(bucket, keys); - } - keys.add(key); - } - - /** - * Removes a Riak object (bucket name/key pair) for the job's input list - */ - public void removeRiakObject(String bucket, String key) { - Set keys = objects.get(bucket); - if (keys != null) { - keys.remove(key); - if (keys.size() == 0) { - objects.remove(bucket); - } - } - } - - /** - * Returns a copy of the Riak objects on the input list for a map/reduce job - */ - public Map> getRiakObjects() { - return new HashMap>(objects); - } - - /** - * Sets a collection of Riak object (bucket name/key pair) as the map/reduce - * job as inputs - * - * @throws IllegalStateException - * - If a bucket name has already been set on the job - */ - public MapReduceBuilder setRiakObjects(Map> objects) { - if (bucket != null) - throw new IllegalStateException("Cannot map/reduce over buckets and objects"); - - if (objects == null) { - clearRiakObjects(); - } else { - this.objects = new HashMap>(objects); - } - - return this; - } - - public MapReduceBuilder setRiakObjects(Collection objects) { - if (bucket != null) - throw new IllegalStateException("Cannot map/reduce over buckets and objects"); - - clearRiakObjects(); - if (objects != null) { - for (RiakObject o : objects) { - addRiakObject(o.getBucket(), o.getKey()); - } - } - - return this; - } - - /** - * Remove all Riak objects from the input list - */ - public void clearRiakObjects() { - objects.clear(); - } - - /** - * How long the map/reduce job is allowed to execute Time is in milliseconds - */ - public void setTimeout(int timeout) { - this.timeout = timeout; - } - - /** - * Gets the currently assigned timeout - */ - public int getTimeout() { - return timeout; - } - - /** - * Adds a map phase to the job - * - * @param function - * function to run for the phase - * - * @param keep - * should the server keep and return the results - * @return current MapReduceBuilder instance. This is done so multiple calls - * to map, reduce, and link can be chained together a la - * StringBuffer - */ - public MapReduceBuilder map(MapReduceFunction function, boolean keep) { - return this.map(function, null, keep); - } - - - /** - * Adds a map phase to the job - * - * @param function - * function to run for the phase - * - * @param arg - * Static argument to pass to the function. Should be an - * object easily converted to JSON - * - * @param keep - * should the server keep and return the results - * @return current MapReduceBuilder instance. This is done so multiple calls - * to map, reduce, and link can be chained together a la - * StringBuffer - */ - public MapReduceBuilder map(MapReduceFunction function, Object arg, boolean keep) { - this.addPhase(MapReduceBuilder.Types.MAP, function, arg, keep); - return this; - } - - /** - * Adds a reduce phase to the job - * - * @param function - * function to run for the phase - * - * @param keep - * should the server keep and return the results - * @return current MapReduceBuilder instance. This is done so multiple calls - * to map, reduce, and link can be chained together a la - * StringBuffer - */ - public MapReduceBuilder reduce(MapReduceFunction function, boolean keep) { - return this.reduce(function, null, keep); - } - - - /** - * Adds a reduce phase to the job - * - * @param function - * function to run for the phase - * - * @param arg - * Static argument to pass to the function. Should be an - * object easily converted to JSON - * - * @param keep - * should the server keep and return the results - * @return current MapReduceBuilder instance. This is done so multiple calls - * to map, reduce, and link can be chained together a la - * StringBuffer - */ - public MapReduceBuilder reduce(MapReduceFunction function, Object arg, boolean keep) { - this.addPhase(MapReduceBuilder.Types.REDUCE, function, arg, keep); - return this; - } - - /** - * Adds a link phase to the job - * - * @param bucket - * bucket to link walk - * @param keep - * should the server keep and return the results - * @return current MapReduceBuilder instance. This is done so multiple calls - * to map, reduce, and link can be chained together a la - * StringBuffer - * - * Pointing at a bucket without specifying a link tag will follow - * all links pointing to objects in the bucket - */ - public MapReduceBuilder link(String bucket, boolean keep) { - this.addPhase(MapReduceBuilder.Types.LINK, new LinkFunction(bucket), keep); - return this; - } - - /** - * Adds a link phase to the job - * - * @param bucket - * bucket to link walk - * @param tag - * link tag to match - * @param keep - * should the server keep and return the results - * @return current MapReduceBuilder instance. This is done so multiple calls - * to map, reduce, and link can be chained together a la - * StringBuffer - */ - public MapReduceBuilder link(String bucket, String tag, boolean keep) { - this.addPhase(MapReduceBuilder.Types.LINK, new LinkFunction(bucket, tag), keep); - return this; - } - - /** - * Submits the job to the Riak server - * - * @param meta - * Extra metadata to attach to the request such as HTTP headers - * or query parameters. - * - * @return {@link MapReduceResponse} containing job results - * @throws IOException - * - * @throws IllegalStateException - * If this job has not been associated with a Riak instance by - * calling {@link MapReduceBuilder#setRiakClient(RiakClient)} - * @throws RiakIORuntimeException - * If an error occurs during communication with the Riak server. - * @throws RiakResponseRuntimeException - * If the Riak server returns a malformed response. - */ - public MapReduceResponseSource submit(IRequestMeta meta) throws IOException { - if (riak == null) - throw new IllegalStateException("Cannot perform map reduce without a RiakClient"); - return riak.mapReduce(ByteString.copyFromUtf8( toJSON().toString() ), meta); - } - - public MapReduceResponseSource submit() throws JSONException, IOException { - return submit(new RequestMeta().contentType("application/json")); - } - - /** - * Builds the JSON representation of a map/reduce job - */ - public JSONObject toJSON() { - JSONObject job = new JSONObject(); - JSONArray query = new JSONArray(); - for (MapReducePhase phase : phases) { - renderPhase(phase, query); - } - buildInputs(job); - try { - job.put("query", query); - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to a valid JSONArray"); - } - if (timeout > 0) { - try { - job.put("timeout", timeout); - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to an int"); - } - } - return job; - } - - private MapReduceBuilder addPhase(Types phaseType, MapReduceFunction function, boolean keep) { - return addPhase(phaseType, function, null, keep); - } - - private MapReduceBuilder addPhase(Types phaseType, MapReduceFunction function, Object arg, boolean keep) { - MapReducePhase phase = new MapReducePhase(); - phase.type = phaseType; - phase.function = function; - phase.arg = arg; - phase.keep = keep; - phases.add(phase); - return this; - } - - private void buildInputs(JSONObject job) { - if (bucket != null) { - try { - job.put("inputs", bucket); - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to a string"); - } - } else { - JSONArray inputs = new JSONArray(); - for (String bucket : objects.keySet()) { - Set keys = objects.get(bucket); - for (String key : keys) { - String[] pair = { bucket, key }; - inputs.put(pair); - } - } - try { - job.put("inputs", inputs); - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to a valid JSONArray"); - } - } - } - - private void renderPhase(MapReducePhase phase, JSONArray query) { - JSONObject phaseJson = new JSONObject(); - JSONObject functionJson = phase.function.toJson(); - try { - functionJson.put("keep", phase.keep); - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to a boolean"); - } - try { - if (phase.arg != null) { - functionJson.put("arg", phase.arg); - } - } catch (JSONException e) { - throw new RuntimeException("Cannot convert phase arg to JSON"); - } - String type = null; - switch (phase.type) { - case MAP: - type = "map"; - break; - case REDUCE: - type = "reduce"; - break; - case LINK: - type = "link"; - break; - } - try { - phaseJson.put(type, functionJson); - } catch (JSONException e) { - throw new RuntimeException("Can always map a string to a valid JSONObject"); - } - query.put(phaseJson); - } - - private class MapReducePhase { - Types type; - MapReduceFunction function; - Object arg; - boolean keep; - } - -} diff --git a/src/main/java/com/basho/riak/pbc/mapreduce/MapReduceResponse.java b/src/main/java/com/basho/riak/pbc/mapreduce/MapReduceResponse.java deleted file mode 100644 index 6504fde5a..000000000 --- a/src/main/java/com/basho/riak/pbc/mapreduce/MapReduceResponse.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * This file is part of riak-java-pb-client - * - * Copyright (c) 2010 by Trifork - * - * 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.pbc.mapreduce; - -import org.json.JSONArray; -import org.json.JSONException; - -import com.basho.riak.pbc.RPB.RpbMapRedResp; -import com.google.protobuf.ByteString; - -public class MapReduceResponse { - - static final ByteString APPLICATION_JSON = ByteString.copyFromUtf8("application/json"); - - public final Integer phase; - public final ByteString response; - private ByteString contentType; - - public MapReduceResponse(RpbMapRedResp resp, ByteString contentType2) { - if (resp.hasPhase()) { - this.phase = new Integer(resp.getPhase()); - } else { - this.phase = null; - } - if (resp.hasResponse()) { - this.response = resp.getResponse(); - } else { - this.response = null; - } - this.contentType = contentType2; - } - - public JSONArray getJSON() throws JSONException { - if (response == null) - return null; - if (!APPLICATION_JSON.equals(contentType)) - throw new IllegalArgumentException("not application/json"); - return new JSONArray(response.toStringUtf8()); - } - - public ByteString getContent() { - if (response == null) - return null; - return response; - } - - public String getContentType() { - return contentType.toStringUtf8(); - } -} diff --git a/src/main/java/org/json/CDL.java b/src/main/java/org/json/CDL.java deleted file mode 100755 index d76dd686f..000000000 --- a/src/main/java/org/json/CDL.java +++ /dev/null @@ -1,279 +0,0 @@ -package org.json; - -/* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -/** - * This provides static methods to convert comma delimited text into a - * JSONArray, and to covert a JSONArray into comma delimited text. Comma - * delimited text is a very popular format for data interchange. It is - * understood by most database, spreadsheet, and organizer programs. - *

    - * Each row of text represents a row in a table or a data record. Each row - * ends with a NEWLINE character. Each row contains one or more values. - * Values are separated by commas. A value can contain any character except - * for comma, unless is is wrapped in single quotes or double quotes. - *

    - * The first row usually contains the names of the columns. - *

    - * A comma delimited list can be converted into a JSONArray of JSONObjects. - * The names for the elements in the JSONObjects can be taken from the names - * in the first row. - * @author JSON.org - * @version 2009-09-11 - */ -public class CDL { - - /** - * Get the next value. The value can be wrapped in quotes. The value can - * be empty. - * @param x A JSONTokener of the source text. - * @return The value string, or null if empty. - * @throws JSONException if the quoted string is badly formed. - */ - private static String getValue(JSONTokener x) throws JSONException { - char c; - char q; - StringBuffer sb; - do { - c = x.next(); - } while (c == ' ' || c == '\t'); - switch (c) { - case 0: - return null; - case '"': - case '\'': - q = c; - sb = new StringBuffer(); - for (;;) { - c = x.next(); - if (c == q) { - break; - } - if (c == 0 || c == '\n' || c == '\r') { - throw x.syntaxError("Missing close quote '" + q + "'."); - } - sb.append(c); - } - return sb.toString(); - case ',': - x.back(); - return ""; - default: - x.back(); - return x.nextTo(','); - } - } - - /** - * Produce a JSONArray of strings from a row of comma delimited values. - * @param x A JSONTokener of the source text. - * @return A JSONArray of strings. - * @throws JSONException - */ - public static JSONArray rowToJSONArray(JSONTokener x) throws JSONException { - JSONArray ja = new JSONArray(); - for (;;) { - String value = getValue(x); - char c = x.next(); - if (value == null || - (ja.length() == 0 && value.length() == 0 && c != ',')) { - return null; - } - ja.put(value); - for (;;) { - if (c == ',') { - break; - } - if (c != ' ') { - if (c == '\n' || c == '\r' || c == 0) { - return ja; - } - throw x.syntaxError("Bad character '" + c + "' (" + - (int)c + ")."); - } - c = x.next(); - } - } - } - - /** - * Produce a JSONObject from a row of comma delimited text, using a - * parallel JSONArray of strings to provides the names of the elements. - * @param names A JSONArray of names. This is commonly obtained from the - * first row of a comma delimited text file using the rowToJSONArray - * method. - * @param x A JSONTokener of the source text. - * @return A JSONObject combining the names and values. - * @throws JSONException - */ - public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x) - throws JSONException { - JSONArray ja = rowToJSONArray(x); - return ja != null ? ja.toJSONObject(names) : null; - } - - /** - * Produce a JSONArray of JSONObjects from a comma delimited text string, - * using the first row as a source of names. - * @param string The comma delimited text. - * @return A JSONArray of JSONObjects. - * @throws JSONException - */ - public static JSONArray toJSONArray(String string) throws JSONException { - return toJSONArray(new JSONTokener(string)); - } - - /** - * Produce a JSONArray of JSONObjects from a comma delimited text string, - * using the first row as a source of names. - * @param x The JSONTokener containing the comma delimited text. - * @return A JSONArray of JSONObjects. - * @throws JSONException - */ - public static JSONArray toJSONArray(JSONTokener x) throws JSONException { - return toJSONArray(rowToJSONArray(x), x); - } - - /** - * Produce a JSONArray of JSONObjects from a comma delimited text string - * using a supplied JSONArray as the source of element names. - * @param names A JSONArray of strings. - * @param string The comma delimited text. - * @return A JSONArray of JSONObjects. - * @throws JSONException - */ - public static JSONArray toJSONArray(JSONArray names, String string) - throws JSONException { - return toJSONArray(names, new JSONTokener(string)); - } - - /** - * Produce a JSONArray of JSONObjects from a comma delimited text string - * using a supplied JSONArray as the source of element names. - * @param names A JSONArray of strings. - * @param x A JSONTokener of the source text. - * @return A JSONArray of JSONObjects. - * @throws JSONException - */ - public static JSONArray toJSONArray(JSONArray names, JSONTokener x) - throws JSONException { - if (names == null || names.length() == 0) { - return null; - } - JSONArray ja = new JSONArray(); - for (;;) { - JSONObject jo = rowToJSONObject(names, x); - if (jo == null) { - break; - } - ja.put(jo); - } - if (ja.length() == 0) { - return null; - } - return ja; - } - - - /** - * Produce a comma delimited text row from a JSONArray. Values containing - * the comma character will be quoted. Troublesome characters may be - * removed. - * @param ja A JSONArray of strings. - * @return A string ending in NEWLINE. - */ - public static String rowToString(JSONArray ja) { - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < ja.length(); i += 1) { - if (i > 0) { - sb.append(','); - } - Object o = ja.opt(i); - if (o != null) { - String s = o.toString(); - if (s.length() > 0 && (s.indexOf(',') >= 0 || s.indexOf('\n') >= 0 || - s.indexOf('\r') >= 0 || s.indexOf(0) >= 0 || - s.charAt(0) == '"')) { - sb.append('"'); - int length = s.length(); - for (int j = 0; j < length; j += 1) { - char c = s.charAt(j); - if (c >= ' ' && c != '"') { - sb.append(c); - } - } - sb.append('"'); - } else { - sb.append(s); - } - } - } - sb.append('\n'); - return sb.toString(); - } - - /** - * Produce a comma delimited text from a JSONArray of JSONObjects. The - * first row will be a list of names obtained by inspecting the first - * JSONObject. - * @param ja A JSONArray of JSONObjects. - * @return A comma delimited text. - * @throws JSONException - */ - public static String toString(JSONArray ja) throws JSONException { - JSONObject jo = ja.optJSONObject(0); - if (jo != null) { - JSONArray names = jo.names(); - if (names != null) { - return rowToString(names) + toString(names, ja); - } - } - return null; - } - - /** - * Produce a comma delimited text from a JSONArray of JSONObjects using - * a provided list of names. The list of names is not included in the - * output. - * @param names A JSONArray of strings. - * @param ja A JSONArray of JSONObjects. - * @return A comma delimited text. - * @throws JSONException - */ - public static String toString(JSONArray names, JSONArray ja) - throws JSONException { - if (names == null || names.length() == 0) { - return null; - } - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < ja.length(); i += 1) { - JSONObject jo = ja.optJSONObject(i); - if (jo != null) { - sb.append(rowToString(jo.toJSONArray(names))); - } - } - return sb.toString(); - } -} diff --git a/src/main/java/org/json/Cookie.java b/src/main/java/org/json/Cookie.java deleted file mode 100755 index 85b992e4c..000000000 --- a/src/main/java/org/json/Cookie.java +++ /dev/null @@ -1,169 +0,0 @@ -package org.json; - -/* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -/** - * Convert a web browser cookie specification to a JSONObject and back. - * JSON and Cookies are both notations for name/value pairs. - * @author JSON.org - * @version 2008-09-18 - */ -public class Cookie { - - /** - * Produce a copy of a string in which the characters '+', '%', '=', ';' - * and control characters are replaced with "%hh". This is a gentle form - * of URL encoding, attempting to cause as little distortion to the - * string as possible. The characters '=' and ';' are meta characters in - * cookies. By convention, they are escaped using the URL-encoding. This is - * only a convention, not a standard. Often, cookies are expected to have - * encoded values. We encode '=' and ';' because we must. We encode '%' and - * '+' because they are meta characters in URL encoding. - * @param string The source string. - * @return The escaped result. - */ - public static String escape(String string) { - char c; - String s = string.trim(); - StringBuffer sb = new StringBuffer(); - int len = s.length(); - for (int i = 0; i < len; i += 1) { - c = s.charAt(i); - if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') { - sb.append('%'); - sb.append(Character.forDigit((char)((c >>> 4) & 0x0f), 16)); - sb.append(Character.forDigit((char)(c & 0x0f), 16)); - } else { - sb.append(c); - } - } - return sb.toString(); - } - - - /** - * Convert a cookie specification string into a JSONObject. The string - * will contain a name value pair separated by '='. The name and the value - * will be unescaped, possibly converting '+' and '%' sequences. The - * cookie properties may follow, separated by ';', also represented as - * name=value (except the secure property, which does not have a value). - * The name will be stored under the key "name", and the value will be - * stored under the key "value". This method does not do checking or - * validation of the parameters. It only converts the cookie string into - * a JSONObject. - * @param string The cookie specification string. - * @return A JSONObject containing "name", "value", and possibly other - * members. - * @throws JSONException - */ - public static JSONObject toJSONObject(String string) throws JSONException { - String n; - JSONObject o = new JSONObject(); - Object v; - JSONTokener x = new JSONTokener(string); - o.put("name", x.nextTo('=')); - x.next('='); - o.put("value", x.nextTo(';')); - x.next(); - while (x.more()) { - n = unescape(x.nextTo("=;")); - if (x.next() != '=') { - if (n.equals("secure")) { - v = Boolean.TRUE; - } else { - throw x.syntaxError("Missing '=' in cookie parameter."); - } - } else { - v = unescape(x.nextTo(';')); - x.next(); - } - o.put(n, v); - } - return o; - } - - - /** - * Convert a JSONObject into a cookie specification string. The JSONObject - * must contain "name" and "value" members. - * If the JSONObject contains "expires", "domain", "path", or "secure" - * members, they will be appended to the cookie specification string. - * All other members are ignored. - * @param o A JSONObject - * @return A cookie specification string - * @throws JSONException - */ - public static String toString(JSONObject o) throws JSONException { - StringBuffer sb = new StringBuffer(); - - sb.append(escape(o.getString("name"))); - sb.append("="); - sb.append(escape(o.getString("value"))); - if (o.has("expires")) { - sb.append(";expires="); - sb.append(o.getString("expires")); - } - if (o.has("domain")) { - sb.append(";domain="); - sb.append(escape(o.getString("domain"))); - } - if (o.has("path")) { - sb.append(";path="); - sb.append(escape(o.getString("path"))); - } - if (o.optBoolean("secure")) { - sb.append(";secure"); - } - return sb.toString(); - } - - /** - * Convert %hh sequences to single characters, and - * convert plus to space. - * @param s A string that may contain - * + (plus) and - * %hh sequences. - * @return The unescaped string. - */ - public static String unescape(String s) { - int len = s.length(); - StringBuffer b = new StringBuffer(); - for (int i = 0; i < len; ++i) { - char c = s.charAt(i); - if (c == '+') { - c = ' '; - } else if (c == '%' && i + 2 < len) { - int d = JSONTokener.dehexchar(s.charAt(i + 1)); - int e = JSONTokener.dehexchar(s.charAt(i + 2)); - if (d >= 0 && e >= 0) { - c = (char)(d * 16 + e); - i += 2; - } - } - b.append(c); - } - return b.toString(); - } -} diff --git a/src/main/java/org/json/CookieList.java b/src/main/java/org/json/CookieList.java deleted file mode 100755 index 400ee6fa2..000000000 --- a/src/main/java/org/json/CookieList.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.json; - -/* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -import java.util.Iterator; - -/** - * Convert a web browser cookie list string to a JSONObject and back. - * @author JSON.org - * @version 2008-09-18 - */ -public class CookieList { - - /** - * Convert a cookie list into a JSONObject. A cookie list is a sequence - * of name/value pairs. The names are separated from the values by '='. - * The pairs are separated by ';'. The names and the values - * will be unescaped, possibly converting '+' and '%' sequences. - * - * To add a cookie to a cooklist, - * cookielistJSONObject.put(cookieJSONObject.getString("name"), - * cookieJSONObject.getString("value")); - * @param string A cookie list string - * @return A JSONObject - * @throws JSONException - */ - public static JSONObject toJSONObject(String string) throws JSONException { - JSONObject o = new JSONObject(); - JSONTokener x = new JSONTokener(string); - while (x.more()) { - String name = Cookie.unescape(x.nextTo('=')); - x.next('='); - o.put(name, Cookie.unescape(x.nextTo(';'))); - x.next(); - } - return o; - } - - - /** - * Convert a JSONObject into a cookie list. A cookie list is a sequence - * of name/value pairs. The names are separated from the values by '='. - * The pairs are separated by ';'. The characters '%', '+', '=', and ';' - * in the names and values are replaced by "%hh". - * @param o A JSONObject - * @return A cookie list string - * @throws JSONException - */ - public static String toString(JSONObject o) throws JSONException { - boolean b = false; - @SuppressWarnings("unchecked") Iterator keys = o.keys(); - String s; - StringBuffer sb = new StringBuffer(); - while (keys.hasNext()) { - s = keys.next().toString(); - if (!o.isNull(s)) { - if (b) { - sb.append(';'); - } - sb.append(Cookie.escape(s)); - sb.append("="); - sb.append(Cookie.escape(o.getString(s))); - b = true; - } - } - return sb.toString(); - } -} diff --git a/src/main/java/org/json/HTTP.java b/src/main/java/org/json/HTTP.java deleted file mode 100755 index 31d5e982f..000000000 --- a/src/main/java/org/json/HTTP.java +++ /dev/null @@ -1,163 +0,0 @@ -package org.json; - -/* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -import java.util.Iterator; - -/** - * Convert an HTTP header to a JSONObject and back. - * @author JSON.org - * @version 2008-09-18 - */ -public class HTTP { - - /** Carriage return/line feed. */ - public static final String CRLF = "\r\n"; - - /** - * Convert an HTTP header string into a JSONObject. It can be a request - * header or a response header. A request header will contain - *

    {
    -     *    Method: "POST" (for example),
    -     *    "Request-URI": "/" (for example),
    -     *    "HTTP-Version": "HTTP/1.1" (for example)
    -     * }
    - * A response header will contain - *
    {
    -     *    "HTTP-Version": "HTTP/1.1" (for example),
    -     *    "Status-Code": "200" (for example),
    -     *    "Reason-Phrase": "OK" (for example)
    -     * }
    - * In addition, the other parameters in the header will be captured, using - * the HTTP field names as JSON names, so that
    -     *    Date: Sun, 26 May 2002 18:06:04 GMT
    -     *    Cookie: Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s
    -     *    Cache-Control: no-cache
    - * become - *
    {...
    -     *    Date: "Sun, 26 May 2002 18:06:04 GMT",
    -     *    Cookie: "Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s",
    -     *    "Cache-Control": "no-cache",
    -     * ...}
    - * It does no further checking or conversion. It does not parse dates. - * It does not do '%' transforms on URLs. - * @param string An HTTP header string. - * @return A JSONObject containing the elements and attributes - * of the XML string. - * @throws JSONException - */ - public static JSONObject toJSONObject(String string) throws JSONException { - JSONObject o = new JSONObject(); - HTTPTokener x = new HTTPTokener(string); - String t; - - t = x.nextToken(); - if (t.toUpperCase().startsWith("HTTP")) { - -// Response - - o.put("HTTP-Version", t); - o.put("Status-Code", x.nextToken()); - o.put("Reason-Phrase", x.nextTo('\0')); - x.next(); - - } else { - -// Request - - o.put("Method", t); - o.put("Request-URI", x.nextToken()); - o.put("HTTP-Version", x.nextToken()); - } - -// Fields - - while (x.more()) { - String name = x.nextTo(':'); - x.next(':'); - o.put(name, x.nextTo('\0')); - x.next(); - } - return o; - } - - - /** - * Convert a JSONObject into an HTTP header. A request header must contain - *
    {
    -     *    Method: "POST" (for example),
    -     *    "Request-URI": "/" (for example),
    -     *    "HTTP-Version": "HTTP/1.1" (for example)
    -     * }
    - * A response header must contain - *
    {
    -     *    "HTTP-Version": "HTTP/1.1" (for example),
    -     *    "Status-Code": "200" (for example),
    -     *    "Reason-Phrase": "OK" (for example)
    -     * }
    - * Any other members of the JSONObject will be output as HTTP fields. - * The result will end with two CRLF pairs. - * @param o A JSONObject - * @return An HTTP header string. - * @throws JSONException if the object does not contain enough - * information. - */ - public static String toString(JSONObject o) throws JSONException { - @SuppressWarnings("unchecked") Iterator keys = o.keys(); - String s; - StringBuffer sb = new StringBuffer(); - if (o.has("Status-Code") && o.has("Reason-Phrase")) { - sb.append(o.getString("HTTP-Version")); - sb.append(' '); - sb.append(o.getString("Status-Code")); - sb.append(' '); - sb.append(o.getString("Reason-Phrase")); - } else if (o.has("Method") && o.has("Request-URI")) { - sb.append(o.getString("Method")); - sb.append(' '); - sb.append('"'); - sb.append(o.getString("Request-URI")); - sb.append('"'); - sb.append(' '); - sb.append(o.getString("HTTP-Version")); - } else { - throw new JSONException("Not enough material for an HTTP header."); - } - sb.append(CRLF); - while (keys.hasNext()) { - s = keys.next().toString(); - if (!s.equals("HTTP-Version") && !s.equals("Status-Code") && - !s.equals("Reason-Phrase") && !s.equals("Method") && - !s.equals("Request-URI") && !o.isNull(s)) { - sb.append(s); - sb.append(": "); - sb.append(o.getString(s)); - sb.append(CRLF); - } - } - sb.append(CRLF); - return sb.toString(); - } -} diff --git a/src/main/java/org/json/HTTPTokener.java b/src/main/java/org/json/HTTPTokener.java deleted file mode 100755 index 410a77c57..000000000 --- a/src/main/java/org/json/HTTPTokener.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.json; - -/* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -/** - * The HTTPTokener extends the JSONTokener to provide additional methods - * for the parsing of HTTP headers. - * @author JSON.org - * @version 2008-09-18 - */ -public class HTTPTokener extends JSONTokener { - - /** - * Construct an HTTPTokener from a string. - * @param s A source string. - */ - public HTTPTokener(String s) { - super(s); - } - - - /** - * Get the next token or string. This is used in parsing HTTP headers. - * @throws JSONException - * @return A String. - */ - public String nextToken() throws JSONException { - char c; - char q; - StringBuffer sb = new StringBuffer(); - do { - c = next(); - } while (Character.isWhitespace(c)); - if (c == '"' || c == '\'') { - q = c; - for (;;) { - c = next(); - if (c < ' ') { - throw syntaxError("Unterminated string."); - } - if (c == q) { - return sb.toString(); - } - sb.append(c); - } - } - for (;;) { - if (c == 0 || Character.isWhitespace(c)) { - return sb.toString(); - } - sb.append(c); - c = next(); - } - } -} diff --git a/src/main/java/org/json/JSONArray.java b/src/main/java/org/json/JSONArray.java deleted file mode 100755 index 5bd718183..000000000 --- a/src/main/java/org/json/JSONArray.java +++ /dev/null @@ -1,917 +0,0 @@ -package org.json; - -/* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -import java.io.IOException; -import java.io.Writer; -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; - -/** - * A JSONArray is an ordered sequence of values. Its external text form is a - * string wrapped in square brackets with commas separating the values. The - * internal form is an object having get and opt - * methods for accessing the values by index, and put methods for - * adding or replacing values. The values can be any of these types: - * Boolean, JSONArray, JSONObject, - * Number, String, or the - * JSONObject.NULL object. - *

    - * The constructor can convert a JSON text into a Java object. The - * toString method converts to JSON text. - *

    - * A get method returns a value if one can be found, and throws an - * exception if one cannot be found. An opt method returns a - * default value instead of throwing an exception, and so is useful for - * obtaining optional values. - *

    - * The generic get() and opt() methods return an - * object which you can cast or query for type. There are also typed - * get and opt methods that do type checking and type - * coercion for you. - *

    - * The texts produced by the toString methods strictly conform to - * JSON syntax rules. The constructors are more forgiving in the texts they will - * accept: - *

      - *
    • An extra , (comma) may appear just - * before the closing bracket.
    • - *
    • The null value will be inserted when there - * is , (comma) elision.
    • - *
    • Strings may be quoted with ' (single - * quote).
    • - *
    • Strings do not need to be quoted at all if they do not begin with a quote - * or single quote, and if they do not contain leading or trailing spaces, - * and if they do not contain any of these characters: - * { } [ ] / \ : , = ; # and if they do not look like numbers - * and if they are not the reserved words true, - * false, or null.
    • - *
    • Values can be separated by ; (semicolon) as - * well as by , (comma).
    • - *
    • Numbers may have the - * 0x- (hex) prefix.
    • - *
    - - * @author JSON.org - * @version 2009-04-14 - */ -@SuppressWarnings("unchecked") public class JSONArray { - - - /** - * The arrayList where the JSONArray's properties are kept. - */ - private ArrayList myArrayList; - - - /** - * Construct an empty JSONArray. - */ - public JSONArray() { - this.myArrayList = new ArrayList(); - } - - /** - * Construct a JSONArray from a JSONTokener. - * @param x A JSONTokener - * @throws JSONException If there is a syntax error. - */ - public JSONArray(JSONTokener x) throws JSONException { - this(); - char c = x.nextClean(); - char q; - if (c == '[') { - q = ']'; - } else if (c == '(') { - q = ')'; - } else { - throw x.syntaxError("A JSONArray text must start with '['"); - } - if (x.nextClean() == ']') { - return; - } - x.back(); - for (;;) { - if (x.nextClean() == ',') { - x.back(); - this.myArrayList.add(null); - } else { - x.back(); - this.myArrayList.add(x.nextValue()); - } - c = x.nextClean(); - switch (c) { - case ';': - case ',': - if (x.nextClean() == ']') { - return; - } - x.back(); - break; - case ']': - case ')': - if (q != c) { - throw x.syntaxError("Expected a '" + new Character(q) + "'"); - } - return; - default: - throw x.syntaxError("Expected a ',' or ']'"); - } - } - } - - - /** - * Construct a JSONArray from a source JSON text. - * @param source A string that begins with - * [ (left bracket) - * and ends with ] (right bracket). - * @throws JSONException If there is a syntax error. - */ - public JSONArray(String source) throws JSONException { - this(new JSONTokener(source)); - } - - - /** - * Construct a JSONArray from a Collection. - * @param collection A Collection. - */ - public JSONArray(Collection collection) { - this.myArrayList = new ArrayList(); - if (collection != null) { - Iterator iter = collection.iterator();; - while (iter.hasNext()) { - Object o = iter.next(); - this.myArrayList.add(JSONObject.wrap(o)); - } - } - } - - - /** - * Construct a JSONArray from an array - * @throws JSONException If not an array. - */ - public JSONArray(Object array) throws JSONException { - this(); - if (array.getClass().isArray()) { - int length = Array.getLength(array); - for (int i = 0; i < length; i += 1) { - this.put(JSONObject.wrap(Array.get(array, i))); - } - } else { - throw new JSONException("JSONArray initial value should be a string or collection or array."); - } - } - - - /** - * Get the object value associated with an index. - * @param index - * The index must be between 0 and length() - 1. - * @return An object value. - * @throws JSONException If there is no value for the index. - */ - public Object get(int index) throws JSONException { - Object o = opt(index); - if (o == null) { - throw new JSONException("JSONArray[" + index + "] not found."); - } - return o; - } - - - /** - * Get the boolean value associated with an index. - * The string values "true" and "false" are converted to boolean. - * - * @param index The index must be between 0 and length() - 1. - * @return The truth. - * @throws JSONException If there is no value for the index or if the - * value is not convertable to boolean. - */ - public boolean getBoolean(int index) throws JSONException { - Object o = get(index); - if (o.equals(Boolean.FALSE) || - (o instanceof String && - ((String)o).equalsIgnoreCase("false"))) { - return false; - } else if (o.equals(Boolean.TRUE) || - (o instanceof String && - ((String)o).equalsIgnoreCase("true"))) { - return true; - } - throw new JSONException("JSONArray[" + index + "] is not a Boolean."); - } - - - /** - * Get the double value associated with an index. - * - * @param index The index must be between 0 and length() - 1. - * @return The value. - * @throws JSONException If the key is not found or if the value cannot - * be converted to a number. - */ - public double getDouble(int index) throws JSONException { - Object o = get(index); - try { - return o instanceof Number ? - ((Number)o).doubleValue() : - Double.valueOf((String)o).doubleValue(); - } catch (Exception e) { - throw new JSONException("JSONArray[" + index + - "] is not a number."); - } - } - - - /** - * Get the int value associated with an index. - * - * @param index The index must be between 0 and length() - 1. - * @return The value. - * @throws JSONException If the key is not found or if the value cannot - * be converted to a number. - * if the value cannot be converted to a number. - */ - public int getInt(int index) throws JSONException { - Object o = get(index); - return o instanceof Number ? - ((Number)o).intValue() : (int)getDouble(index); - } - - - /** - * Get the JSONArray associated with an index. - * @param index The index must be between 0 and length() - 1. - * @return A JSONArray value. - * @throws JSONException If there is no value for the index. or if the - * value is not a JSONArray - */ - public JSONArray getJSONArray(int index) throws JSONException { - Object o = get(index); - if (o instanceof JSONArray) { - return (JSONArray)o; - } - throw new JSONException("JSONArray[" + index + - "] is not a JSONArray."); - } - - - /** - * Get the JSONObject associated with an index. - * @param index subscript - * @return A JSONObject value. - * @throws JSONException If there is no value for the index or if the - * value is not a JSONObject - */ - public JSONObject getJSONObject(int index) throws JSONException { - Object o = get(index); - if (o instanceof JSONObject) { - return (JSONObject)o; - } - throw new JSONException("JSONArray[" + index + - "] is not a JSONObject."); - } - - - /** - * Get the long value associated with an index. - * - * @param index The index must be between 0 and length() - 1. - * @return The value. - * @throws JSONException If the key is not found or if the value cannot - * be converted to a number. - */ - public long getLong(int index) throws JSONException { - Object o = get(index); - return o instanceof Number ? - ((Number)o).longValue() : (long)getDouble(index); - } - - - /** - * Get the string associated with an index. - * @param index The index must be between 0 and length() - 1. - * @return A string value. - * @throws JSONException If there is no value for the index. - */ - public String getString(int index) throws JSONException { - return get(index).toString(); - } - - - /** - * Determine if the value is null. - * @param index The index must be between 0 and length() - 1. - * @return true if the value at the index is null, or if there is no value. - */ - public boolean isNull(int index) { - return JSONObject.NULL.equals(opt(index)); - } - - - /** - * Make a string from the contents of this JSONArray. The - * separator string is inserted between each element. - * Warning: This method assumes that the data structure is acyclical. - * @param separator A string that will be inserted between the elements. - * @return a string. - * @throws JSONException If the array contains an invalid number. - */ - public String join(String separator) throws JSONException { - int len = length(); - StringBuffer sb = new StringBuffer(); - - for (int i = 0; i < len; i += 1) { - if (i > 0) { - sb.append(separator); - } - sb.append(JSONObject.valueToString(this.myArrayList.get(i))); - } - return sb.toString(); - } - - - /** - * Get the number of elements in the JSONArray, included nulls. - * - * @return The length (or size). - */ - public int length() { - return this.myArrayList.size(); - } - - - /** - * Get the optional object value associated with an index. - * @param index The index must be between 0 and length() - 1. - * @return An object value, or null if there is no - * object at that index. - */ - public Object opt(int index) { - return (index < 0 || index >= length()) ? - null : this.myArrayList.get(index); - } - - - /** - * Get the optional boolean value associated with an index. - * It returns false if there is no value at that index, - * or if the value is not Boolean.TRUE or the String "true". - * - * @param index The index must be between 0 and length() - 1. - * @return The truth. - */ - public boolean optBoolean(int index) { - return optBoolean(index, false); - } - - - /** - * Get the optional boolean value associated with an index. - * It returns the defaultValue if there is no value at that index or if - * it is not a Boolean or the String "true" or "false" (case insensitive). - * - * @param index The index must be between 0 and length() - 1. - * @param defaultValue A boolean default. - * @return The truth. - */ - public boolean optBoolean(int index, boolean defaultValue) { - try { - return getBoolean(index); - } catch (Exception e) { - return defaultValue; - } - } - - - /** - * Get the optional double value associated with an index. - * NaN is returned if there is no value for the index, - * or if the value is not a number and cannot be converted to a number. - * - * @param index The index must be between 0 and length() - 1. - * @return The value. - */ - public double optDouble(int index) { - return optDouble(index, Double.NaN); - } - - - /** - * Get the optional double value associated with an index. - * The defaultValue is returned if there is no value for the index, - * or if the value is not a number and cannot be converted to a number. - * - * @param index subscript - * @param defaultValue The default value. - * @return The value. - */ - public double optDouble(int index, double defaultValue) { - try { - return getDouble(index); - } catch (Exception e) { - return defaultValue; - } - } - - - /** - * Get the optional int value associated with an index. - * Zero is returned if there is no value for the index, - * or if the value is not a number and cannot be converted to a number. - * - * @param index The index must be between 0 and length() - 1. - * @return The value. - */ - public int optInt(int index) { - return optInt(index, 0); - } - - - /** - * Get the optional int value associated with an index. - * The defaultValue is returned if there is no value for the index, - * or if the value is not a number and cannot be converted to a number. - * @param index The index must be between 0 and length() - 1. - * @param defaultValue The default value. - * @return The value. - */ - public int optInt(int index, int defaultValue) { - try { - return getInt(index); - } catch (Exception e) { - return defaultValue; - } - } - - - /** - * Get the optional JSONArray associated with an index. - * @param index subscript - * @return A JSONArray value, or null if the index has no value, - * or if the value is not a JSONArray. - */ - public JSONArray optJSONArray(int index) { - Object o = opt(index); - return o instanceof JSONArray ? (JSONArray)o : null; - } - - - /** - * Get the optional JSONObject associated with an index. - * Null is returned if the key is not found, or null if the index has - * no value, or if the value is not a JSONObject. - * - * @param index The index must be between 0 and length() - 1. - * @return A JSONObject value. - */ - public JSONObject optJSONObject(int index) { - Object o = opt(index); - return o instanceof JSONObject ? (JSONObject)o : null; - } - - - /** - * Get the optional long value associated with an index. - * Zero is returned if there is no value for the index, - * or if the value is not a number and cannot be converted to a number. - * - * @param index The index must be between 0 and length() - 1. - * @return The value. - */ - public long optLong(int index) { - return optLong(index, 0); - } - - - /** - * Get the optional long value associated with an index. - * The defaultValue is returned if there is no value for the index, - * or if the value is not a number and cannot be converted to a number. - * @param index The index must be between 0 and length() - 1. - * @param defaultValue The default value. - * @return The value. - */ - public long optLong(int index, long defaultValue) { - try { - return getLong(index); - } catch (Exception e) { - return defaultValue; - } - } - - - /** - * Get the optional string value associated with an index. It returns an - * empty string if there is no value at that index. If the value - * is not a string and is not null, then it is coverted to a string. - * - * @param index The index must be between 0 and length() - 1. - * @return A String value. - */ - public String optString(int index) { - return optString(index, ""); - } - - - /** - * Get the optional string associated with an index. - * The defaultValue is returned if the key is not found. - * - * @param index The index must be between 0 and length() - 1. - * @param defaultValue The default value. - * @return A String value. - */ - public String optString(int index, String defaultValue) { - Object o = opt(index); - return o != null ? o.toString() : defaultValue; - } - - - /** - * Append a boolean value. This increases the array's length by one. - * - * @param value A boolean value. - * @return this. - */ - public JSONArray put(boolean value) { - put(value ? Boolean.TRUE : Boolean.FALSE); - return this; - } - - - /** - * Put a value in the JSONArray, where the value will be a - * JSONArray which is produced from a Collection. - * @param value A Collection value. - * @return this. - */ - public JSONArray put(Collection value) { - put(new JSONArray(value)); - return this; - } - - - /** - * Append a double value. This increases the array's length by one. - * - * @param value A double value. - * @throws JSONException if the value is not finite. - * @return this. - */ - public JSONArray put(double value) throws JSONException { - Double d = new Double(value); - JSONObject.testValidity(d); - put(d); - return this; - } - - - /** - * Append an int value. This increases the array's length by one. - * - * @param value An int value. - * @return this. - */ - public JSONArray put(int value) { - put(new Integer(value)); - return this; - } - - - /** - * Append an long value. This increases the array's length by one. - * - * @param value A long value. - * @return this. - */ - public JSONArray put(long value) { - put(new Long(value)); - return this; - } - - - /** - * Put a value in the JSONArray, where the value will be a - * JSONObject which is produced from a Map. - * @param value A Map value. - * @return this. - */ - public JSONArray put(Map value) { - put(new JSONObject(value)); - return this; - } - - - /** - * Append an object value. This increases the array's length by one. - * @param value An object value. The value should be a - * Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the - * JSONObject.NULL object. - * @return this. - */ - public JSONArray put(Object value) { - this.myArrayList.add(value); - return this; - } - - - /** - * Put or replace a boolean value in the JSONArray. If the index is greater - * than the length of the JSONArray, then null elements will be added as - * necessary to pad it out. - * @param index The subscript. - * @param value A boolean value. - * @return this. - * @throws JSONException If the index is negative. - */ - public JSONArray put(int index, boolean value) throws JSONException { - put(index, value ? Boolean.TRUE : Boolean.FALSE); - return this; - } - - - /** - * Put a value in the JSONArray, where the value will be a - * JSONArray which is produced from a Collection. - * @param index The subscript. - * @param value A Collection value. - * @return this. - * @throws JSONException If the index is negative or if the value is - * not finite. - */ - public JSONArray put(int index, Collection value) throws JSONException { - put(index, new JSONArray(value)); - return this; - } - - - /** - * Put or replace a double value. If the index is greater than the length of - * the JSONArray, then null elements will be added as necessary to pad - * it out. - * @param index The subscript. - * @param value A double value. - * @return this. - * @throws JSONException If the index is negative or if the value is - * not finite. - */ - public JSONArray put(int index, double value) throws JSONException { - put(index, new Double(value)); - return this; - } - - - /** - * Put or replace an int value. If the index is greater than the length of - * the JSONArray, then null elements will be added as necessary to pad - * it out. - * @param index The subscript. - * @param value An int value. - * @return this. - * @throws JSONException If the index is negative. - */ - public JSONArray put(int index, int value) throws JSONException { - put(index, new Integer(value)); - return this; - } - - - /** - * Put or replace a long value. If the index is greater than the length of - * the JSONArray, then null elements will be added as necessary to pad - * it out. - * @param index The subscript. - * @param value A long value. - * @return this. - * @throws JSONException If the index is negative. - */ - public JSONArray put(int index, long value) throws JSONException { - put(index, new Long(value)); - return this; - } - - - /** - * Put a value in the JSONArray, where the value will be a - * JSONObject which is produced from a Map. - * @param index The subscript. - * @param value The Map value. - * @return this. - * @throws JSONException If the index is negative or if the the value is - * an invalid number. - */ - public JSONArray put(int index, Map value) throws JSONException { - put(index, new JSONObject(value)); - return this; - } - - - /** - * Put or replace an object value in the JSONArray. If the index is greater - * than the length of the JSONArray, then null elements will be added as - * necessary to pad it out. - * @param index The subscript. - * @param value The value to put into the array. The value should be a - * Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the - * JSONObject.NULL object. - * @return this. - * @throws JSONException If the index is negative or if the the value is - * an invalid number. - */ - public JSONArray put(int index, Object value) throws JSONException { - JSONObject.testValidity(value); - if (index < 0) { - throw new JSONException("JSONArray[" + index + "] not found."); - } - if (index < length()) { - this.myArrayList.set(index, value); - } else { - while (index != length()) { - put(JSONObject.NULL); - } - put(value); - } - return this; - } - - - /** - * Remove an index and close the hole. - * @param index The index of the element to be removed. - * @return The value that was associated with the index, - * or null if there was no value. - */ - public Object remove(int index) { - Object o = opt(index); - this.myArrayList.remove(index); - return o; - } - - - /** - * Produce a JSONObject by combining a JSONArray of names with the values - * of this JSONArray. - * @param names A JSONArray containing a list of key strings. These will be - * paired with the values. - * @return A JSONObject, or null if there are no names or if this JSONArray - * has no values. - * @throws JSONException If any of the names are null. - */ - public JSONObject toJSONObject(JSONArray names) throws JSONException { - if (names == null || names.length() == 0 || length() == 0) { - return null; - } - JSONObject jo = new JSONObject(); - for (int i = 0; i < names.length(); i += 1) { - jo.put(names.getString(i), this.opt(i)); - } - return jo; - } - - - /** - * Make a JSON text of this JSONArray. For compactness, no - * unnecessary whitespace is added. If it is not possible to produce a - * syntactically correct JSON text then null will be returned instead. This - * could occur if the array contains an invalid number. - *

    - * Warning: This method assumes that the data structure is acyclical. - * - * @return a printable, displayable, transmittable - * representation of the array. - */ - public String toString() { - try { - return '[' + join(",") + ']'; - } catch (Exception e) { - return null; - } - } - - - /** - * Make a prettyprinted JSON text of this JSONArray. - * Warning: This method assumes that the data structure is acyclical. - * @param indentFactor The number of spaces to add to each level of - * indentation. - * @return a printable, displayable, transmittable - * representation of the object, beginning - * with [ (left bracket) and ending - * with ] (right bracket). - * @throws JSONException - */ - public String toString(int indentFactor) throws JSONException { - return toString(indentFactor, 0); - } - - - /** - * Make a prettyprinted JSON text of this JSONArray. - * Warning: This method assumes that the data structure is acyclical. - * @param indentFactor The number of spaces to add to each level of - * indentation. - * @param indent The indention of the top level. - * @return a printable, displayable, transmittable - * representation of the array. - * @throws JSONException - */ - String toString(int indentFactor, int indent) throws JSONException { - int len = length(); - if (len == 0) { - return "[]"; - } - int i; - StringBuffer sb = new StringBuffer("["); - if (len == 1) { - sb.append(JSONObject.valueToString(this.myArrayList.get(0), - indentFactor, indent)); - } else { - int newindent = indent + indentFactor; - sb.append('\n'); - for (i = 0; i < len; i += 1) { - if (i > 0) { - sb.append(",\n"); - } - for (int j = 0; j < newindent; j += 1) { - sb.append(' '); - } - sb.append(JSONObject.valueToString(this.myArrayList.get(i), - indentFactor, newindent)); - } - sb.append('\n'); - for (i = 0; i < indent; i += 1) { - sb.append(' '); - } - } - sb.append(']'); - return sb.toString(); - } - - - /** - * Write the contents of the JSONArray as JSON text to a writer. - * For compactness, no whitespace is added. - *

    - * Warning: This method assumes that the data structure is acyclical. - * - * @return The writer. - * @throws JSONException - */ - public Writer write(Writer writer) throws JSONException { - try { - boolean b = false; - int len = length(); - - writer.write('['); - - for (int i = 0; i < len; i += 1) { - if (b) { - writer.write(','); - } - Object v = this.myArrayList.get(i); - if (v instanceof JSONObject) { - ((JSONObject)v).write(writer); - } else if (v instanceof JSONArray) { - ((JSONArray)v).write(writer); - } else { - writer.write(JSONObject.valueToString(v)); - } - b = true; - } - writer.write(']'); - return writer; - } catch (IOException e) { - throw new JSONException(e); - } - } -} \ No newline at end of file diff --git a/src/main/java/org/json/JSONException.java b/src/main/java/org/json/JSONException.java deleted file mode 100755 index 45e3b8d06..000000000 --- a/src/main/java/org/json/JSONException.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.json; - -/** - * The JSONException is thrown by the JSON.org classes when things are amiss. - * @author JSON.org - * @version 2008-09-18 - */ -public class JSONException extends Exception { - /** - * - */ - private static final long serialVersionUID = 0; - private Throwable cause; - - /** - * Constructs a JSONException with an explanatory message. - * @param message Detail about the reason for the exception. - */ - public JSONException(String message) { - super(message); - } - - public JSONException(Throwable t) { - super(t.getMessage()); - this.cause = t; - } - - public Throwable getCause() { - return this.cause; - } -} diff --git a/src/main/java/org/json/JSONML.java b/src/main/java/org/json/JSONML.java deleted file mode 100755 index dfc381815..000000000 --- a/src/main/java/org/json/JSONML.java +++ /dev/null @@ -1,455 +0,0 @@ -package org.json; - -/* -Copyright (c) 2008 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -import java.util.Iterator; - - -/** - * This provides static methods to convert an XML text into a JSONArray or - * JSONObject, and to covert a JSONArray or JSONObject into an XML text using - * the JsonML transform. - * @author JSON.org - * @version 2008-11-20 - */ -@SuppressWarnings("unchecked") public class JSONML { - - /** - * Parse XML values and store them in a JSONArray. - * @param x The XMLTokener containing the source string. - * @param arrayForm true if array form, false if object form. - * @param ja The JSONArray that is containing the current tag or null - * if we are at the outermost level. - * @return A JSONArray if the value is the outermost tag, otherwise null. - * @throws JSONException - */ - private static Object parse(XMLTokener x, boolean arrayForm, - JSONArray ja) throws JSONException { - String attribute; - char c; - String closeTag = null; - int i; - JSONArray newja = null; - JSONObject newjo = null; - Object token; - String tagName = null; - -// Test for and skip past these forms: -// -// -// -// - - while (true) { - token = x.nextContent(); - if (token == XML.LT) { - token = x.nextToken(); - if (token instanceof Character) { - if (token == XML.SLASH) { - -// Close tag "); - } - x.back(); - } else if (c == '[') { - token = x.nextToken(); - if (token.equals("CDATA") && x.next() == '[') { - if (ja != null) { - ja.put(x.nextCDATA()); - } - } else { - throw x.syntaxError("Expected 'CDATA['"); - } - } else { - i = 1; - do { - token = x.nextMeta(); - if (token == null) { - throw x.syntaxError("Missing '>' after ' 0); - } - } else if (token == XML.QUEST) { - -// "); - } else { - throw x.syntaxError("Misshaped tag"); - } - -// Open tag < - - } else { - if (!(token instanceof String)) { - throw x.syntaxError("Bad tagName '" + token + "'."); - } - tagName = (String)token; - newja = new JSONArray(); - newjo = new JSONObject(); - if (arrayForm) { - newja.put(tagName); - if (ja != null) { - ja.put(newja); - } - } else { - newjo.put("tagName", tagName); - if (ja != null) { - ja.put(newjo); - } - } - token = null; - for (;;) { - if (token == null) { - token = x.nextToken(); - } - if (token == null) { - throw x.syntaxError("Misshaped tag"); - } - if (!(token instanceof String)) { - break; - } - -// attribute = value - - attribute = (String)token; - if (!arrayForm && (attribute == "tagName" || attribute == "childNode")) { - throw x.syntaxError("Reserved attribute."); - } - token = x.nextToken(); - if (token == XML.EQ) { - token = x.nextToken(); - if (!(token instanceof String)) { - throw x.syntaxError("Missing value"); - } - newjo.accumulate(attribute, JSONObject.stringToValue((String)token)); - token = null; - } else { - newjo.accumulate(attribute, ""); - } - } - if (arrayForm && newjo.length() > 0) { - newja.put(newjo); - } - -// Empty tag <.../> - - if (token == XML.SLASH) { - if (x.nextToken() != XML.GT) { - throw x.syntaxError("Misshaped tag"); - } - if (ja == null) { - if (arrayForm) { - return newja; - } else { - return newjo; - } - } - -// Content, between <...> and - - } else { - if (token != XML.GT) { - throw x.syntaxError("Misshaped tag"); - } - closeTag = (String)parse(x, arrayForm, newja); - if (closeTag != null) { - if (!closeTag.equals(tagName)) { - throw x.syntaxError("Mismatched '" + tagName + - "' and '" + closeTag + "'"); - } - tagName = null; - if (!arrayForm && newja.length() > 0) { - newjo.put("childNodes", newja); - } - if (ja == null) { - if (arrayForm) { - return newja; - } else { - return newjo; - } - } - } - } - } - } else { - if (ja != null) { - ja.put(token instanceof String ? - JSONObject.stringToValue((String)token) : token); - } - } - } - } - - - /** - * Convert a well-formed (but not necessarily valid) XML string into a - * JSONArray using the JsonML transform. Each XML tag is represented as - * a JSONArray in which the first element is the tag name. If the tag has - * attributes, then the second element will be JSONObject containing the - * name/value pairs. If the tag contains children, then strings and - * JSONArrays will represent the child tags. - * Comments, prologs, DTDs, and <[ [ ]]> are ignored. - * @param string The source string. - * @return A JSONArray containing the structured data from the XML string. - * @throws JSONException - */ - public static JSONArray toJSONArray(String string) throws JSONException { - return toJSONArray(new XMLTokener(string)); - } - - - /** - * Convert a well-formed (but not necessarily valid) XML string into a - * JSONArray using the JsonML transform. Each XML tag is represented as - * a JSONArray in which the first element is the tag name. If the tag has - * attributes, then the second element will be JSONObject containing the - * name/value pairs. If the tag contains children, then strings and - * JSONArrays will represent the child content and tags. - * Comments, prologs, DTDs, and <[ [ ]]> are ignored. - * @param x An XMLTokener. - * @return A JSONArray containing the structured data from the XML string. - * @throws JSONException - */ - public static JSONArray toJSONArray(XMLTokener x) throws JSONException { - return (JSONArray)parse(x, true, null); - } - - - - /** - * Convert a well-formed (but not necessarily valid) XML string into a - * JSONObject using the JsonML transform. Each XML tag is represented as - * a JSONObject with a "tagName" property. If the tag has attributes, then - * the attributes will be in the JSONObject as properties. If the tag - * contains children, the object will have a "childNodes" property which - * will be an array of strings and JsonML JSONObjects. - - * Comments, prologs, DTDs, and <[ [ ]]> are ignored. - * @param x An XMLTokener of the XML source text. - * @return A JSONObject containing the structured data from the XML string. - * @throws JSONException - */ - public static JSONObject toJSONObject(XMLTokener x) throws JSONException { - return (JSONObject)parse(x, false, null); - } - /** - * Convert a well-formed (but not necessarily valid) XML string into a - * JSONObject using the JsonML transform. Each XML tag is represented as - * a JSONObject with a "tagName" property. If the tag has attributes, then - * the attributes will be in the JSONObject as properties. If the tag - * contains children, the object will have a "childNodes" property which - * will be an array of strings and JsonML JSONObjects. - - * Comments, prologs, DTDs, and <[ [ ]]> are ignored. - * @param string The XML source text. - * @return A JSONObject containing the structured data from the XML string. - * @throws JSONException - */ - public static JSONObject toJSONObject(String string) throws JSONException { - return toJSONObject(new XMLTokener(string)); - } - - - /** - * Reverse the JSONML transformation, making an XML text from a JSONArray. - * @param ja A JSONArray. - * @return An XML string. - * @throws JSONException - */ - public static String toString(JSONArray ja) throws JSONException { - Object e; - int i; - JSONObject jo; - String k; - Iterator keys; - int length; - StringBuffer sb = new StringBuffer(); - String tagName; - String v; - -// Emit = length) { - sb.append('/'); - sb.append('>'); - } else { - sb.append('>'); - do { - e = ja.get(i); - i += 1; - if (e != null) { - if (e instanceof String) { - sb.append(XML.escape(e.toString())); - } else if (e instanceof JSONObject) { - sb.append(toString((JSONObject)e)); - } else if (e instanceof JSONArray) { - sb.append(toString((JSONArray)e)); - } - } - } while (i < length); - sb.append('<'); - sb.append('/'); - sb.append(tagName); - sb.append('>'); - } - return sb.toString(); - } - - /** - * Reverse the JSONML transformation, making an XML text from a JSONObject. - * The JSONObject must contain a "tagName" property. If it has children, - * then it must have a "childNodes" property containing an array of objects. - * The other properties are attributes with string values. - * @param jo A JSONObject. - * @return An XML string. - * @throws JSONException - */ - public static String toString(JSONObject jo) throws JSONException { - StringBuffer sb = new StringBuffer(); - Object e; - int i; - JSONArray ja; - String k; - Iterator keys; - int len; - String tagName; - String v; - -//Emit '); - } else { - sb.append('>'); - len = ja.length(); - for (i = 0; i < len; i += 1) { - e = ja.get(i); - if (e != null) { - if (e instanceof String) { - sb.append(XML.escape(e.toString())); - } else if (e instanceof JSONObject) { - sb.append(toString((JSONObject)e)); - } else if (e instanceof JSONArray) { - sb.append(toString((JSONArray)e)); - } - } - } - sb.append('<'); - sb.append('/'); - sb.append(tagName); - sb.append('>'); - } - return sb.toString(); - } -} \ No newline at end of file diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java deleted file mode 100755 index bb76a696e..000000000 --- a/src/main/java/org/json/JSONObject.java +++ /dev/null @@ -1,1573 +0,0 @@ -package org.json; - -/* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -import java.io.IOException; -import java.io.Writer; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.TreeSet; - -/** - * A JSONObject is an unordered collection of name/value pairs. Its - * external form is a string wrapped in curly braces with colons between the - * names and values, and commas between the values and names. The internal form - * is an object having get and opt methods for - * accessing the values by name, and put methods for adding or - * replacing values by name. The values can be any of these types: - * Boolean, JSONArray, JSONObject, - * Number, String, or the JSONObject.NULL - * object. A JSONObject constructor can be used to convert an external form - * JSON text into an internal form whose values can be retrieved with the - * get and opt methods, or to convert values into a - * JSON text using the put and toString methods. - * A get method returns a value if one can be found, and throws an - * exception if one cannot be found. An opt method returns a - * default value instead of throwing an exception, and so is useful for - * obtaining optional values. - *

    - * The generic get() and opt() methods return an - * object, which you can cast or query for type. There are also typed - * get and opt methods that do type checking and type - * coercion for you. - *

    - * The put methods adds values to an object. For example,

    - *     myString = new JSONObject().put("JSON", "Hello, World!").toString();
    - * produces the string {"JSON": "Hello, World"}. - *

    - * The texts produced by the toString methods strictly conform to - * the JSON syntax rules. - * The constructors are more forgiving in the texts they will accept: - *

      - *
    • An extra , (comma) may appear just - * before the closing brace.
    • - *
    • Strings may be quoted with ' (single - * quote).
    • - *
    • Strings do not need to be quoted at all if they do not begin with a quote - * or single quote, and if they do not contain leading or trailing spaces, - * and if they do not contain any of these characters: - * { } [ ] / \ : , = ; # and if they do not look like numbers - * and if they are not the reserved words true, - * false, or null.
    • - *
    • Keys can be followed by = or => as well as - * by :.
    • - *
    • Values can be followed by ; (semicolon) as - * well as by , (comma).
    • - *
    • Numbers may have the 0- (octal) or - * 0x- (hex) prefix.
    • - *
    - * @author JSON.org - * @version 2010-01-05 - */ -@SuppressWarnings("unchecked") public class JSONObject { - - /** - * JSONObject.NULL is equivalent to the value that JavaScript calls null, - * whilst Java's null is equivalent to the value that JavaScript calls - * undefined. - */ - private static final class Null { - - /** - * There is only intended to be a single instance of the NULL object, - * so the clone method returns itself. - * @return NULL. - */ - protected final Object clone() { - return this; - } - - - /** - * A Null object is equal to the null value and to itself. - * @param object An object to test for nullness. - * @return true if the object parameter is the JSONObject.NULL object - * or null. - */ - public boolean equals(Object object) { - return object == null || object == this; - } - - - /** - * Get the "null" string value. - * @return The string "null". - */ - public String toString() { - return "null"; - } - } - - - /** - * The map where the JSONObject's properties are kept. - */ - private Map map; - - - /** - * It is sometimes more convenient and less ambiguous to have a - * NULL object than to use Java's null value. - * JSONObject.NULL.equals(null) returns true. - * JSONObject.NULL.toString() returns "null". - */ - public static final Object NULL = new Null(); - - - /** - * Construct an empty JSONObject. - */ - public JSONObject() { - this.map = new HashMap(); - } - - - /** - * Construct a JSONObject from a subset of another JSONObject. - * An array of strings is used to identify the keys that should be copied. - * Missing keys are ignored. - * @param jo A JSONObject. - * @param names An array of strings. - * @throws JSONException - * @exception JSONException If a value is a non-finite number or if a name is duplicated. - */ - public JSONObject(JSONObject jo, String[] names) { - this(); - for (int i = 0; i < names.length; i += 1) { - try { - putOnce(names[i], jo.opt(names[i])); - } catch (Exception ignore) { - } - } - } - - - /** - * Construct a JSONObject from a JSONTokener. - * @param x A JSONTokener object containing the source string. - * @throws JSONException If there is a syntax error in the source string - * or a duplicated key. - */ - public JSONObject(JSONTokener x) throws JSONException { - this(); - char c; - String key; - - if (x.nextClean() != '{') { - throw x.syntaxError("A JSONObject text must begin with '{'"); - } - for (;;) { - c = x.nextClean(); - switch (c) { - case 0: - throw x.syntaxError("A JSONObject text must end with '}'"); - case '}': - return; - default: - x.back(); - key = x.nextValue().toString(); - } - - /* - * The key is followed by ':'. We will also tolerate '=' or '=>'. - */ - - c = x.nextClean(); - if (c == '=') { - if (x.next() != '>') { - x.back(); - } - } else if (c != ':') { - throw x.syntaxError("Expected a ':' after a key"); - } - putOnce(key, x.nextValue()); - - /* - * Pairs are separated by ','. We will also tolerate ';'. - */ - - switch (x.nextClean()) { - case ';': - case ',': - if (x.nextClean() == '}') { - return; - } - x.back(); - break; - case '}': - return; - default: - throw x.syntaxError("Expected a ',' or '}'"); - } - } - } - - - /** - * Construct a JSONObject from a Map. - * - * @param map A map object that can be used to initialize the contents of - * the JSONObject. - * @throws JSONException - */ - public JSONObject(Map map) { - this.map = new HashMap(); - if (map != null) { - Iterator i = map.entrySet().iterator(); - while (i.hasNext()) { - Map.Entry e = (Map.Entry)i.next(); - this.map.put(e.getKey(), wrap(e.getValue())); - } - } - } - - - /** - * Construct a JSONObject from an Object using bean getters. - * It reflects on all of the public methods of the object. - * For each of the methods with no parameters and a name starting - * with "get" or "is" followed by an uppercase letter, - * the method is invoked, and a key and the value returned from the getter method - * are put into the new JSONObject. - * - * The key is formed by removing the "get" or "is" prefix. - * If the second remaining character is not upper case, then the first - * character is converted to lower case. - * - * For example, if an object has a method named "getName", and - * if the result of calling object.getName() is "Larry Fine", - * then the JSONObject will contain "name": "Larry Fine". - * - * @param bean An object that has getter methods that should be used - * to make a JSONObject. - */ - public JSONObject(Object bean) { - this(); - populateMap(bean); - } - - - /** - * Construct a JSONObject from an Object, using reflection to find the - * public members. The resulting JSONObject's keys will be the strings - * from the names array, and the values will be the field values associated - * with those keys in the object. If a key is not found or not visible, - * then it will not be copied into the new JSONObject. - * @param object An object that has fields that should be used to make a - * JSONObject. - * @param names An array of strings, the names of the fields to be obtained - * from the object. - */ - public JSONObject(Object object, String names[]) { - this(); - Class c = object.getClass(); - for (int i = 0; i < names.length; i += 1) { - String name = names[i]; - try { - putOpt(name, c.getField(name).get(object)); - } catch (Exception ignore) { - } - } - } - - - /** - * Construct a JSONObject from a source JSON text string. - * This is the most commonly used JSONObject constructor. - * @param source A string beginning - * with { (left brace) and ending - * with } (right brace). - * @exception JSONException If there is a syntax error in the source - * string or a duplicated key. - */ - public JSONObject(String source) throws JSONException { - this(new JSONTokener(source)); - } - - - /** - * Accumulate values under a key. It is similar to the put method except - * that if there is already an object stored under the key then a - * JSONArray is stored under the key to hold all of the accumulated values. - * If there is already a JSONArray, then the new value is appended to it. - * In contrast, the put method replaces the previous value. - * @param key A key string. - * @param value An object to be accumulated under the key. - * @return this. - * @throws JSONException If the value is an invalid number - * or if the key is null. - */ - public JSONObject accumulate(String key, Object value) - throws JSONException { - testValidity(value); - Object o = opt(key); - if (o == null) { - put(key, value instanceof JSONArray ? - new JSONArray().put(value) : - value); - } else if (o instanceof JSONArray) { - ((JSONArray)o).put(value); - } else { - put(key, new JSONArray().put(o).put(value)); - } - return this; - } - - - /** - * Append values to the array under a key. If the key does not exist in the - * JSONObject, then the key is put in the JSONObject with its value being a - * JSONArray containing the value parameter. If the key was already - * associated with a JSONArray, then the value parameter is appended to it. - * @param key A key string. - * @param value An object to be accumulated under the key. - * @return this. - * @throws JSONException If the key is null or if the current value - * associated with the key is not a JSONArray. - */ - public JSONObject append(String key, Object value) throws JSONException { - testValidity(value); - Object o = opt(key); - if (o == null) { - put(key, new JSONArray().put(value)); - } else if (o instanceof JSONArray) { - put(key, ((JSONArray)o).put(value)); - } else { - throw new JSONException("JSONObject[" + key + - "] is not a JSONArray."); - } - return this; - } - - - /** - * Produce a string from a double. The string "null" will be returned if - * the number is not finite. - * @param d A double. - * @return A String. - */ - static public String doubleToString(double d) { - if (Double.isInfinite(d) || Double.isNaN(d)) { - return "null"; - } - -// Shave off trailing zeros and decimal point, if possible. - - String s = Double.toString(d); - if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) { - while (s.endsWith("0")) { - s = s.substring(0, s.length() - 1); - } - if (s.endsWith(".")) { - s = s.substring(0, s.length() - 1); - } - } - return s; - } - - - /** - * Get the value object associated with a key. - * - * @param key A key string. - * @return The object associated with the key. - * @throws JSONException if the key is not found. - */ - public Object get(String key) throws JSONException { - Object o = opt(key); - if (o == null) { - throw new JSONException("JSONObject[" + quote(key) + - "] not found."); - } - return o; - } - - - /** - * Get the boolean value associated with a key. - * - * @param key A key string. - * @return The truth. - * @throws JSONException - * if the value is not a Boolean or the String "true" or "false". - */ - public boolean getBoolean(String key) throws JSONException { - Object o = get(key); - if (o.equals(Boolean.FALSE) || - (o instanceof String && - ((String)o).equalsIgnoreCase("false"))) { - return false; - } else if (o.equals(Boolean.TRUE) || - (o instanceof String && - ((String)o).equalsIgnoreCase("true"))) { - return true; - } - throw new JSONException("JSONObject[" + quote(key) + - "] is not a Boolean."); - } - - - /** - * Get the double value associated with a key. - * @param key A key string. - * @return The numeric value. - * @throws JSONException if the key is not found or - * if the value is not a Number object and cannot be converted to a number. - */ - public double getDouble(String key) throws JSONException { - Object o = get(key); - try { - return o instanceof Number ? - ((Number)o).doubleValue() : - Double.valueOf((String)o).doubleValue(); - } catch (Exception e) { - throw new JSONException("JSONObject[" + quote(key) + - "] is not a number."); - } - } - - - /** - * Get the int value associated with a key. If the number value is too - * large for an int, it will be clipped. - * - * @param key A key string. - * @return The integer value. - * @throws JSONException if the key is not found or if the value cannot - * be converted to an integer. - */ - public int getInt(String key) throws JSONException { - Object o = get(key); - return o instanceof Number ? - ((Number)o).intValue() : (int)getDouble(key); - } - - - /** - * Get the JSONArray value associated with a key. - * - * @param key A key string. - * @return A JSONArray which is the value. - * @throws JSONException if the key is not found or - * if the value is not a JSONArray. - */ - public JSONArray getJSONArray(String key) throws JSONException { - Object o = get(key); - if (o instanceof JSONArray) { - return (JSONArray)o; - } - throw new JSONException("JSONObject[" + quote(key) + - "] is not a JSONArray."); - } - - - /** - * Get the JSONObject value associated with a key. - * - * @param key A key string. - * @return A JSONObject which is the value. - * @throws JSONException if the key is not found or - * if the value is not a JSONObject. - */ - public JSONObject getJSONObject(String key) throws JSONException { - Object o = get(key); - if (o instanceof JSONObject) { - return (JSONObject)o; - } - throw new JSONException("JSONObject[" + quote(key) + - "] is not a JSONObject."); - } - - - /** - * Get the long value associated with a key. If the number value is too - * long for a long, it will be clipped. - * - * @param key A key string. - * @return The long value. - * @throws JSONException if the key is not found or if the value cannot - * be converted to a long. - */ - public long getLong(String key) throws JSONException { - Object o = get(key); - return o instanceof Number ? - ((Number)o).longValue() : (long)getDouble(key); - } - - - /** - * Get an array of field names from a JSONObject. - * - * @return An array of field names, or null if there are no names. - */ - public static String[] getNames(JSONObject jo) { - int length = jo.length(); - if (length == 0) { - return null; - } - Iterator i = jo.keys(); - String[] names = new String[length]; - int j = 0; - while (i.hasNext()) { - names[j] = (String)i.next(); - j += 1; - } - return names; - } - - - /** - * Get an array of field names from an Object. - * - * @return An array of field names, or null if there are no names. - */ - public static String[] getNames(Object object) { - if (object == null) { - return null; - } - Class klass = object.getClass(); - Field[] fields = klass.getFields(); - int length = fields.length; - if (length == 0) { - return null; - } - String[] names = new String[length]; - for (int i = 0; i < length; i += 1) { - names[i] = fields[i].getName(); - } - return names; - } - - - /** - * Get the string associated with a key. - * - * @param key A key string. - * @return A string which is the value. - * @throws JSONException if the key is not found. - */ - public String getString(String key) throws JSONException { - return get(key).toString(); - } - - - /** - * Determine if the JSONObject contains a specific key. - * @param key A key string. - * @return true if the key exists in the JSONObject. - */ - public boolean has(String key) { - return this.map.containsKey(key); - } - - - /** - * Increment a property of a JSONObject. If there is no such property, - * create one with a value of 1. If there is such a property, and if - * it is an Integer, Long, Double, or Float, then add one to it. - * @param key A key string. - * @return this. - * @throws JSONException If there is already a property with this name - * that is not an Integer, Long, Double, or Float. - */ - public JSONObject increment(String key) throws JSONException { - Object value = opt(key); - if (value == null) { - put(key, 1); - } else { - if (value instanceof Integer) { - put(key, ((Integer)value).intValue() + 1); - } else if (value instanceof Long) { - put(key, ((Long)value).longValue() + 1); - } else if (value instanceof Double) { - put(key, ((Double)value).doubleValue() + 1); - } else if (value instanceof Float) { - put(key, ((Float)value).floatValue() + 1); - } else { - throw new JSONException("Unable to increment [" + key + "]."); - } - } - return this; - } - - - /** - * Determine if the value associated with the key is null or if there is - * no value. - * @param key A key string. - * @return true if there is no value associated with the key or if - * the value is the JSONObject.NULL object. - */ - public boolean isNull(String key) { - return JSONObject.NULL.equals(opt(key)); - } - - - /** - * Get an enumeration of the keys of the JSONObject. - * - * @return An iterator of the keys. - */ - public Iterator keys() { - return this.map.keySet().iterator(); - } - - - /** - * Get the number of keys stored in the JSONObject. - * - * @return The number of keys in the JSONObject. - */ - public int length() { - return this.map.size(); - } - - - /** - * Produce a JSONArray containing the names of the elements of this - * JSONObject. - * @return A JSONArray containing the key strings, or null if the JSONObject - * is empty. - */ - public JSONArray names() { - JSONArray ja = new JSONArray(); - Iterator keys = keys(); - while (keys.hasNext()) { - ja.put(keys.next()); - } - return ja.length() == 0 ? null : ja; - } - - /** - * Produce a string from a Number. - * @param n A Number - * @return A String. - * @throws JSONException If n is a non-finite number. - */ - static public String numberToString(Number n) - throws JSONException { - if (n == null) { - throw new JSONException("Null pointer"); - } - testValidity(n); - -// Shave off trailing zeros and decimal point, if possible. - - String s = n.toString(); - if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) { - while (s.endsWith("0")) { - s = s.substring(0, s.length() - 1); - } - if (s.endsWith(".")) { - s = s.substring(0, s.length() - 1); - } - } - return s; - } - - - /** - * Get an optional value associated with a key. - * @param key A key string. - * @return An object which is the value, or null if there is no value. - */ - public Object opt(String key) { - return key == null ? null : this.map.get(key); - } - - - /** - * Get an optional boolean associated with a key. - * It returns false if there is no such key, or if the value is not - * Boolean.TRUE or the String "true". - * - * @param key A key string. - * @return The truth. - */ - public boolean optBoolean(String key) { - return optBoolean(key, false); - } - - - /** - * Get an optional boolean associated with a key. - * It returns the defaultValue if there is no such key, or if it is not - * a Boolean or the String "true" or "false" (case insensitive). - * - * @param key A key string. - * @param defaultValue The default. - * @return The truth. - */ - public boolean optBoolean(String key, boolean defaultValue) { - try { - return getBoolean(key); - } catch (Exception e) { - return defaultValue; - } - } - - - /** - * Get an optional double associated with a key, - * or NaN if there is no such key or if its value is not a number. - * If the value is a string, an attempt will be made to evaluate it as - * a number. - * - * @param key A string which is the key. - * @return An object which is the value. - */ - public double optDouble(String key) { - return optDouble(key, Double.NaN); - } - - - /** - * Get an optional double associated with a key, or the - * defaultValue if there is no such key or if its value is not a number. - * If the value is a string, an attempt will be made to evaluate it as - * a number. - * - * @param key A key string. - * @param defaultValue The default. - * @return An object which is the value. - */ - public double optDouble(String key, double defaultValue) { - try { - Object o = opt(key); - return o instanceof Number ? ((Number)o).doubleValue() : - new Double((String)o).doubleValue(); - } catch (Exception e) { - return defaultValue; - } - } - - - /** - * Get an optional int value associated with a key, - * or zero if there is no such key or if the value is not a number. - * If the value is a string, an attempt will be made to evaluate it as - * a number. - * - * @param key A key string. - * @return An object which is the value. - */ - public int optInt(String key) { - return optInt(key, 0); - } - - - /** - * Get an optional int value associated with a key, - * or the default if there is no such key or if the value is not a number. - * If the value is a string, an attempt will be made to evaluate it as - * a number. - * - * @param key A key string. - * @param defaultValue The default. - * @return An object which is the value. - */ - public int optInt(String key, int defaultValue) { - try { - return getInt(key); - } catch (Exception e) { - return defaultValue; - } - } - - - /** - * Get an optional JSONArray associated with a key. - * It returns null if there is no such key, or if its value is not a - * JSONArray. - * - * @param key A key string. - * @return A JSONArray which is the value. - */ - public JSONArray optJSONArray(String key) { - Object o = opt(key); - return o instanceof JSONArray ? (JSONArray)o : null; - } - - - /** - * Get an optional JSONObject associated with a key. - * It returns null if there is no such key, or if its value is not a - * JSONObject. - * - * @param key A key string. - * @return A JSONObject which is the value. - */ - public JSONObject optJSONObject(String key) { - Object o = opt(key); - return o instanceof JSONObject ? (JSONObject)o : null; - } - - - /** - * Get an optional long value associated with a key, - * or zero if there is no such key or if the value is not a number. - * If the value is a string, an attempt will be made to evaluate it as - * a number. - * - * @param key A key string. - * @return An object which is the value. - */ - public long optLong(String key) { - return optLong(key, 0); - } - - - /** - * Get an optional long value associated with a key, - * or the default if there is no such key or if the value is not a number. - * If the value is a string, an attempt will be made to evaluate it as - * a number. - * - * @param key A key string. - * @param defaultValue The default. - * @return An object which is the value. - */ - public long optLong(String key, long defaultValue) { - try { - return getLong(key); - } catch (Exception e) { - return defaultValue; - } - } - - - /** - * Get an optional string associated with a key. - * It returns an empty string if there is no such key. If the value is not - * a string and is not null, then it is coverted to a string. - * - * @param key A key string. - * @return A string which is the value. - */ - public String optString(String key) { - return optString(key, ""); - } - - - /** - * Get an optional string associated with a key. - * It returns the defaultValue if there is no such key. - * - * @param key A key string. - * @param defaultValue The default. - * @return A string which is the value. - */ - public String optString(String key, String defaultValue) { - Object o = opt(key); - return o != null ? o.toString() : defaultValue; - } - - - private void populateMap(Object bean) { - Class klass = bean.getClass(); - -// If klass is a System class then set includeSuperClass to false. - - boolean includeSuperClass = klass.getClassLoader() != null; - - Method[] methods = (includeSuperClass) ? - klass.getMethods() : klass.getDeclaredMethods(); - for (int i = 0; i < methods.length; i += 1) { - try { - Method method = methods[i]; - if (Modifier.isPublic(method.getModifiers())) { - String name = method.getName(); - String key = ""; - if (name.startsWith("get")) { - if (name.equals("getClass") || - name.equals("getDeclaringClass")) { - key = ""; - } else { - key = name.substring(3); - } - } else if (name.startsWith("is")) { - key = name.substring(2); - } - if (key.length() > 0 && - Character.isUpperCase(key.charAt(0)) && - method.getParameterTypes().length == 0) { - if (key.length() == 1) { - key = key.toLowerCase(); - } else if (!Character.isUpperCase(key.charAt(1))) { - key = key.substring(0, 1).toLowerCase() + - key.substring(1); - } - - Object result = method.invoke(bean, (Object[])null); - - map.put(key, wrap(result)); - } - } - } catch (Exception ignore) { - } - } - } - - - /** - * Put a key/boolean pair in the JSONObject. - * - * @param key A key string. - * @param value A boolean which is the value. - * @return this. - * @throws JSONException If the key is null. - */ - public JSONObject put(String key, boolean value) throws JSONException { - put(key, value ? Boolean.TRUE : Boolean.FALSE); - return this; - } - - - /** - * Put a key/value pair in the JSONObject, where the value will be a - * JSONArray which is produced from a Collection. - * @param key A key string. - * @param value A Collection value. - * @return this. - * @throws JSONException - */ - public JSONObject put(String key, Collection value) throws JSONException { - put(key, new JSONArray(value)); - return this; - } - - - /** - * Put a key/double pair in the JSONObject. - * - * @param key A key string. - * @param value A double which is the value. - * @return this. - * @throws JSONException If the key is null or if the number is invalid. - */ - public JSONObject put(String key, double value) throws JSONException { - put(key, new Double(value)); - return this; - } - - - /** - * Put a key/int pair in the JSONObject. - * - * @param key A key string. - * @param value An int which is the value. - * @return this. - * @throws JSONException If the key is null. - */ - public JSONObject put(String key, int value) throws JSONException { - put(key, new Integer(value)); - return this; - } - - - /** - * Put a key/long pair in the JSONObject. - * - * @param key A key string. - * @param value A long which is the value. - * @return this. - * @throws JSONException If the key is null. - */ - public JSONObject put(String key, long value) throws JSONException { - put(key, new Long(value)); - return this; - } - - - /** - * Put a key/value pair in the JSONObject, where the value will be a - * JSONObject which is produced from a Map. - * @param key A key string. - * @param value A Map value. - * @return this. - * @throws JSONException - */ - public JSONObject put(String key, Map value) throws JSONException { - put(key, new JSONObject(value)); - return this; - } - - - /** - * Put a key/value pair in the JSONObject. If the value is null, - * then the key will be removed from the JSONObject if it is present. - * @param key A key string. - * @param value An object which is the value. It should be of one of these - * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, - * or the JSONObject.NULL object. - * @return this. - * @throws JSONException If the value is non-finite number - * or if the key is null. - */ - public JSONObject put(String key, Object value) throws JSONException { - if (key == null) { - throw new JSONException("Null key."); - } - if (value != null) { - testValidity(value); - this.map.put(key, value); - } else { - remove(key); - } - return this; - } - - - /** - * Put a key/value pair in the JSONObject, but only if the key and the - * value are both non-null, and only if there is not already a member - * with that name. - * @param key - * @param value - * @return his. - * @throws JSONException if the key is a duplicate - */ - public JSONObject putOnce(String key, Object value) throws JSONException { - if (key != null && value != null) { - if (opt(key) != null) { - throw new JSONException("Duplicate key \"" + key + "\""); - } - put(key, value); - } - return this; - } - - - /** - * Put a key/value pair in the JSONObject, but only if the - * key and the value are both non-null. - * @param key A key string. - * @param value An object which is the value. It should be of one of these - * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, - * or the JSONObject.NULL object. - * @return this. - * @throws JSONException If the value is a non-finite number. - */ - public JSONObject putOpt(String key, Object value) throws JSONException { - if (key != null && value != null) { - put(key, value); - } - return this; - } - - - /** - * Produce a string in double quotes with backslash sequences in all the - * right places. A backslash will be inserted within = '\u0080' && c < '\u00a0') || - (c >= '\u2000' && c < '\u2100')) { - t = "000" + Integer.toHexString(c); - sb.append("\\u" + t.substring(t.length() - 4)); - } else { - sb.append(c); - } - } - } - sb.append('"'); - return sb.toString(); - } - - /** - * Remove a name and its value, if present. - * @param key The name to be removed. - * @return The value that was associated with the name, - * or null if there was no value. - */ - public Object remove(String key) { - return this.map.remove(key); - } - - /** - * Get an enumeration of the keys of the JSONObject. - * The keys will be sorted alphabetically. - * - * @return An iterator of the keys. - */ - public Iterator sortedKeys() { - return new TreeSet(this.map.keySet()).iterator(); - } - - /** - * Try to convert a string into a number, boolean, or null. If the string - * can't be converted, return the string. - * @param s A String. - * @return A simple JSON value. - */ - static public Object stringToValue(String s) { - if (s.equals("")) { - return s; - } - if (s.equalsIgnoreCase("true")) { - return Boolean.TRUE; - } - if (s.equalsIgnoreCase("false")) { - return Boolean.FALSE; - } - if (s.equalsIgnoreCase("null")) { - return JSONObject.NULL; - } - - /* - * If it might be a number, try converting it. - * We support the non-standard 0x- convention. - * If a number cannot be produced, then the value will just - * be a string. Note that the 0x-, plus, and implied string - * conventions are non-standard. A JSON parser may accept - * non-JSON forms as long as it accepts all correct JSON forms. - */ - - char b = s.charAt(0); - if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') { - if (b == '0' && s.length() > 2 && - (s.charAt(1) == 'x' || s.charAt(1) == 'X')) { - try { - return new Integer(Integer.parseInt(s.substring(2), 16)); - } catch (Exception ignore) { - } - } - try { - if (s.indexOf('.') > -1 || s.indexOf('e') > -1 || s.indexOf('E') > -1) { - return Double.valueOf(s); - } else { - Long myLong = new Long(s); - if (myLong.longValue() == myLong.intValue()) { - return new Integer(myLong.intValue()); - } else { - return myLong; - } - } - } catch (Exception ignore) { - } - } - return s; - } - - - /** - * Throw an exception if the object is an NaN or infinite number. - * @param o The object to test. - * @throws JSONException If o is a non-finite number. - */ - static void testValidity(Object o) throws JSONException { - if (o != null) { - if (o instanceof Double) { - if (((Double)o).isInfinite() || ((Double)o).isNaN()) { - throw new JSONException( - "JSON does not allow non-finite numbers."); - } - } else if (o instanceof Float) { - if (((Float)o).isInfinite() || ((Float)o).isNaN()) { - throw new JSONException( - "JSON does not allow non-finite numbers."); - } - } - } - } - - - /** - * Produce a JSONArray containing the values of the members of this - * JSONObject. - * @param names A JSONArray containing a list of key strings. This - * determines the sequence of the values in the result. - * @return A JSONArray of values. - * @throws JSONException If any of the values are non-finite numbers. - */ - public JSONArray toJSONArray(JSONArray names) throws JSONException { - if (names == null || names.length() == 0) { - return null; - } - JSONArray ja = new JSONArray(); - for (int i = 0; i < names.length(); i += 1) { - ja.put(this.opt(names.getString(i))); - } - return ja; - } - - /** - * Make a JSON text of this JSONObject. For compactness, no whitespace - * is added. If this would not result in a syntactically correct JSON text, - * then null will be returned instead. - *

    - * Warning: This method assumes that the data structure is acyclical. - * - * @return a printable, displayable, portable, transmittable - * representation of the object, beginning - * with { (left brace) and ending - * with } (right brace). - */ - public String toString() { - try { - Iterator keys = keys(); - StringBuffer sb = new StringBuffer("{"); - - while (keys.hasNext()) { - if (sb.length() > 1) { - sb.append(','); - } - Object o = keys.next(); - sb.append(quote(o.toString())); - sb.append(':'); - sb.append(valueToString(this.map.get(o))); - } - sb.append('}'); - return sb.toString(); - } catch (Exception e) { - return null; - } - } - - - /** - * Make a prettyprinted JSON text of this JSONObject. - *

    - * Warning: This method assumes that the data structure is acyclical. - * @param indentFactor The number of spaces to add to each level of - * indentation. - * @return a printable, displayable, portable, transmittable - * representation of the object, beginning - * with { (left brace) and ending - * with } (right brace). - * @throws JSONException If the object contains an invalid number. - */ - public String toString(int indentFactor) throws JSONException { - return toString(indentFactor, 0); - } - - - /** - * Make a prettyprinted JSON text of this JSONObject. - *

    - * Warning: This method assumes that the data structure is acyclical. - * @param indentFactor The number of spaces to add to each level of - * indentation. - * @param indent The indentation of the top level. - * @return a printable, displayable, transmittable - * representation of the object, beginning - * with { (left brace) and ending - * with } (right brace). - * @throws JSONException If the object contains an invalid number. - */ - String toString(int indentFactor, int indent) throws JSONException { - int j; - int n = length(); - if (n == 0) { - return "{}"; - } - Iterator keys = sortedKeys(); - StringBuffer sb = new StringBuffer("{"); - int newindent = indent + indentFactor; - Object o; - if (n == 1) { - o = keys.next(); - sb.append(quote(o.toString())); - sb.append(": "); - sb.append(valueToString(this.map.get(o), indentFactor, - indent)); - } else { - while (keys.hasNext()) { - o = keys.next(); - if (sb.length() > 1) { - sb.append(",\n"); - } else { - sb.append('\n'); - } - for (j = 0; j < newindent; j += 1) { - sb.append(' '); - } - sb.append(quote(o.toString())); - sb.append(": "); - sb.append(valueToString(this.map.get(o), indentFactor, - newindent)); - } - if (sb.length() > 1) { - sb.append('\n'); - for (j = 0; j < indent; j += 1) { - sb.append(' '); - } - } - } - sb.append('}'); - return sb.toString(); - } - - - /** - * Make a JSON text of an Object value. If the object has an - * value.toJSONString() method, then that method will be used to produce - * the JSON text. The method is required to produce a strictly - * conforming text. If the object does not contain a toJSONString - * method (which is the most common case), then a text will be - * produced by other means. If the value is an array or Collection, - * then a JSONArray will be made from it and its toJSONString method - * will be called. If the value is a MAP, then a JSONObject will be made - * from it and its toJSONString method will be called. Otherwise, the - * value's toString method will be called, and the result will be quoted. - * - *

    - * Warning: This method assumes that the data structure is acyclical. - * @param value The value to be serialized. - * @return a printable, displayable, transmittable - * representation of the object, beginning - * with { (left brace) and ending - * with } (right brace). - * @throws JSONException If the value is or contains an invalid number. - */ - static String valueToString(Object value) throws JSONException { - if (value == null || value.equals(null)) { - return "null"; - } - if (value instanceof JSONString) { - Object o; - try { - o = ((JSONString)value).toJSONString(); - } catch (Exception e) { - throw new JSONException(e); - } - if (o instanceof String) { - return (String)o; - } - throw new JSONException("Bad value from toJSONString: " + o); - } - if (value instanceof Number) { - return numberToString((Number) value); - } - if (value instanceof Boolean || value instanceof JSONObject || - value instanceof JSONArray) { - return value.toString(); - } - if (value instanceof Map) { - return new JSONObject((Map)value).toString(); - } - if (value instanceof Collection) { - return new JSONArray((Collection)value).toString(); - } - if (value.getClass().isArray()) { - return new JSONArray(value).toString(); - } - return quote(value.toString()); - } - - - /** - * Make a prettyprinted JSON text of an object value. - *

    - * Warning: This method assumes that the data structure is acyclical. - * @param value The value to be serialized. - * @param indentFactor The number of spaces to add to each level of - * indentation. - * @param indent The indentation of the top level. - * @return a printable, displayable, transmittable - * representation of the object, beginning - * with { (left brace) and ending - * with } (right brace). - * @throws JSONException If the object contains an invalid number. - */ - static String valueToString(Object value, int indentFactor, int indent) - throws JSONException { - if (value == null || value.equals(null)) { - return "null"; - } - try { - if (value instanceof JSONString) { - Object o = ((JSONString)value).toJSONString(); - if (o instanceof String) { - return (String)o; - } - } - } catch (Exception ignore) { - } - if (value instanceof Number) { - return numberToString((Number) value); - } - if (value instanceof Boolean) { - return value.toString(); - } - if (value instanceof JSONObject) { - return ((JSONObject)value).toString(indentFactor, indent); - } - if (value instanceof JSONArray) { - return ((JSONArray)value).toString(indentFactor, indent); - } - if (value instanceof Map) { - return new JSONObject((Map)value).toString(indentFactor, indent); - } - if (value instanceof Collection) { - return new JSONArray((Collection)value).toString(indentFactor, indent); - } - if (value.getClass().isArray()) { - return new JSONArray(value).toString(indentFactor, indent); - } - return quote(value.toString()); - } - - - /** - * Wrap an object, if necessary. If the object is null, return the NULL - * object. If it is an array or collection, wrap it in a JSONArray. If - * it is a map, wrap it in a JSONObject. If it is a standard property - * (Double, String, et al) then it is already wrapped. Otherwise, if it - * comes from one of the java packages, turn it into a string. And if - * it doesn't, try to wrap it in a JSONObject. If the wrapping fails, - * then null is returned. - * - * @param object The object to wrap - * @return The wrapped value - */ - static Object wrap(Object object) { - try { - if (object == null) { - return NULL; - } - if (object instanceof JSONObject || object instanceof JSONArray || - object instanceof Byte || object instanceof Character || - object instanceof Short || object instanceof Integer || - object instanceof Long || object instanceof Boolean || - object instanceof Float || object instanceof Double || - object instanceof String || NULL.equals(object)) { - return object; - } - - if (object instanceof Collection) { - return new JSONArray((Collection)object); - } - if (object.getClass().isArray()) { - return new JSONArray(object); - } - if (object instanceof Map) { - return new JSONObject((Map)object); - } - Package objectPackage = object.getClass().getPackage(); - String objectPackageName = ( objectPackage != null ? objectPackage.getName() : "" ); - if (objectPackageName.startsWith("java.") || - objectPackageName.startsWith("javax.") || - object.getClass().getClassLoader() == null) { - return object.toString(); - } - return new JSONObject(object); - } catch(Exception exception) { - return null; - } - } - - - /** - * Write the contents of the JSONObject as JSON text to a writer. - * For compactness, no whitespace is added. - *

    - * Warning: This method assumes that the data structure is acyclical. - * - * @return The writer. - * @throws JSONException - */ - public Writer write(Writer writer) throws JSONException { - try { - boolean b = false; - Iterator keys = keys(); - writer.write('{'); - - while (keys.hasNext()) { - if (b) { - writer.write(','); - } - Object k = keys.next(); - writer.write(quote(k.toString())); - writer.write(':'); - Object v = this.map.get(k); - if (v instanceof JSONObject) { - ((JSONObject)v).write(writer); - } else if (v instanceof JSONArray) { - ((JSONArray)v).write(writer); - } else { - writer.write(valueToString(v)); - } - b = true; - } - writer.write('}'); - return writer; - } catch (IOException exception) { - throw new JSONException(exception); - } - } -} \ No newline at end of file diff --git a/src/main/java/org/json/JSONString.java b/src/main/java/org/json/JSONString.java deleted file mode 100755 index 17f438426..000000000 --- a/src/main/java/org/json/JSONString.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.json; -/** - * The JSONString interface allows a toJSONString() - * method so that a class can change the behavior of - * JSONObject.toString(), JSONArray.toString(), - * and JSONWriter.value(Object). The - * toJSONString method will be used instead of the default behavior - * of using the Object's toString() method and quoting the result. - */ -public interface JSONString { - /** - * The toJSONString method allows a class to produce its own JSON - * serialization. - * - * @return A strictly syntactically correct JSON text. - */ - public String toJSONString(); -} diff --git a/src/main/java/org/json/JSONStringer.java b/src/main/java/org/json/JSONStringer.java deleted file mode 100755 index 25c2e5d78..000000000 --- a/src/main/java/org/json/JSONStringer.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.json; - -/* -Copyright (c) 2006 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -import java.io.StringWriter; - -/** - * JSONStringer provides a quick and convenient way of producing JSON text. - * The texts produced strictly conform to JSON syntax rules. No whitespace is - * added, so the results are ready for transmission or storage. Each instance of - * JSONStringer can produce one JSON text. - *

    - * A JSONStringer instance provides a value method for appending - * values to the - * text, and a key - * method for adding keys before values in objects. There are array - * and endArray methods that make and bound array values, and - * object and endObject methods which make and bound - * object values. All of these methods return the JSONWriter instance, - * permitting cascade style. For example,

    - * myString = new JSONStringer()
    - *     .object()
    - *         .key("JSON")
    - *         .value("Hello, World!")
    - *     .endObject()
    - *     .toString();
    which produces the string
    - * {"JSON":"Hello, World!"}
    - *

    - * The first method called must be array or object. - * There are no methods for adding commas or colons. JSONStringer adds them for - * you. Objects and arrays can be nested up to 20 levels deep. - *

    - * This can sometimes be easier than using a JSONObject to build a string. - * @author JSON.org - * @version 2008-09-18 - */ -public class JSONStringer extends JSONWriter { - /** - * Make a fresh JSONStringer. It can be used to build one JSON text. - */ - public JSONStringer() { - super(new StringWriter()); - } - - /** - * Return the JSON text. This method is used to obtain the product of the - * JSONStringer instance. It will return null if there was a - * problem in the construction of the JSON text (such as the calls to - * array were not properly balanced with calls to - * endArray). - * @return The JSON text. - */ - public String toString() { - return this.mode == 'd' ? this.writer.toString() : null; - } -} diff --git a/src/main/java/org/json/JSONTokener.java b/src/main/java/org/json/JSONTokener.java deleted file mode 100755 index fe52f3159..000000000 --- a/src/main/java/org/json/JSONTokener.java +++ /dev/null @@ -1,435 +0,0 @@ -package org.json; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; - -/* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -/** - * A JSONTokener takes a source string and extracts characters and tokens from - * it. It is used by the JSONObject and JSONArray constructors to parse - * JSON source strings. - * @author JSON.org - * @version 2010-02-02 - */ -public class JSONTokener { - - private int character; - private boolean eof; - private int index; - private int line; - private char previous; - private Reader reader; - private boolean usePrevious; - - - /** - * Construct a JSONTokener from a reader. - * - * @param reader A reader. - */ - public JSONTokener(Reader reader) { - this.reader = reader.markSupported() ? - reader : new BufferedReader(reader); - this.eof = false; - this.usePrevious = false; - this.previous = 0; - this.index = 0; - this.character = 1; - this.line = 1; - } - - - /** - * Construct a JSONTokener from a string. - * - * @param s A source string. - */ - public JSONTokener(String s) { - this(new StringReader(s)); - } - - - /** - * Back up one character. This provides a sort of lookahead capability, - * so that you can test for a digit or letter before attempting to parse - * the next number or identifier. - */ - public void back() throws JSONException { - if (usePrevious || index <= 0) { - throw new JSONException("Stepping back two steps is not supported"); - } - this.index -= 1; - this.character -= 1; - this.usePrevious = true; - this.eof = false; - } - - - /** - * Get the hex value of a character (base16). - * @param c A character between '0' and '9' or between 'A' and 'F' or - * between 'a' and 'f'. - * @return An int between 0 and 15, or -1 if c was not a hex digit. - */ - public static int dehexchar(char c) { - if (c >= '0' && c <= '9') { - return c - '0'; - } - if (c >= 'A' && c <= 'F') { - return c - ('A' - 10); - } - if (c >= 'a' && c <= 'f') { - return c - ('a' - 10); - } - return -1; - } - - public boolean end() { - return eof && !usePrevious; - } - - - /** - * Determine if the source string still contains characters that next() - * can consume. - * @return true if not yet at the end of the source. - */ - public boolean more() throws JSONException { - next(); - if (end()) { - return false; - } - back(); - return true; - } - - - /** - * Get the next character in the source string. - * - * @return The next character, or 0 if past the end of the source string. - */ - public char next() throws JSONException { - int c; - if (this.usePrevious) { - this.usePrevious = false; - c = this.previous; - } else { - try { - c = this.reader.read(); - } catch (IOException exception) { - throw new JSONException(exception); - } - - if (c <= 0) { // End of stream - this.eof = true; - c = 0; - } - } - this.index += 1; - if (this.previous == '\r') { - this.line += 1; - this.character = c == '\n' ? 0 : 1; - } else if (c == '\n') { - this.line += 1; - this.character = 0; - } else { - this.character += 1; - } - this.previous = (char) c; - return this.previous; - } - - - /** - * Consume the next character, and check that it matches a specified - * character. - * @param c The character to match. - * @return The character. - * @throws JSONException if the character does not match. - */ - public char next(char c) throws JSONException { - char n = next(); - if (n != c) { - throw syntaxError("Expected '" + c + "' and instead saw '" + - n + "'"); - } - return n; - } - - - /** - * Get the next n characters. - * - * @param n The number of characters to take. - * @return A string of n characters. - * @throws JSONException - * Substring bounds error if there are not - * n characters remaining in the source string. - */ - public String next(int n) throws JSONException { - if (n == 0) { - return ""; - } - - char[] buffer = new char[n]; - int pos = 0; - - while (pos < n) { - buffer[pos] = next(); - if (end()) { - throw syntaxError("Substring bounds error"); - } - pos += 1; - } - return new String(buffer); - } - - - /** - * Get the next char in the string, skipping whitespace. - * @throws JSONException - * @return A character, or 0 if there are no more characters. - */ - public char nextClean() throws JSONException { - for (;;) { - char c = next(); - if (c == 0 || c > ' ') { - return c; - } - } - } - - - /** - * Return the characters up to the next close quote character. - * Backslash processing is done. The formal JSON format does not - * allow strings in single quotes, but an implementation is allowed to - * accept them. - * @param quote The quoting character, either - * " (double quote) or - * ' (single quote). - * @return A String. - * @throws JSONException Unterminated string. - */ - public String nextString(char quote) throws JSONException { - char c; - StringBuffer sb = new StringBuffer(); - for (;;) { - c = next(); - switch (c) { - case 0: - case '\n': - case '\r': - throw syntaxError("Unterminated string"); - case '\\': - c = next(); - switch (c) { - case 'b': - sb.append('\b'); - break; - case 't': - sb.append('\t'); - break; - case 'n': - sb.append('\n'); - break; - case 'f': - sb.append('\f'); - break; - case 'r': - sb.append('\r'); - break; - case 'u': - sb.append((char)Integer.parseInt(next(4), 16)); - break; - case '"': - case '\'': - case '\\': - case '/': - sb.append(c); - break; - default: - throw syntaxError("Illegal escape."); - } - break; - default: - if (c == quote) { - return sb.toString(); - } - sb.append(c); - } - } - } - - - /** - * Get the text up but not including the specified character or the - * end of line, whichever comes first. - * @param d A delimiter character. - * @return A string. - */ - public String nextTo(char d) throws JSONException { - StringBuffer sb = new StringBuffer(); - for (;;) { - char c = next(); - if (c == d || c == 0 || c == '\n' || c == '\r') { - if (c != 0) { - back(); - } - return sb.toString().trim(); - } - sb.append(c); - } - } - - - /** - * Get the text up but not including one of the specified delimiter - * characters or the end of line, whichever comes first. - * @param delimiters A set of delimiter characters. - * @return A string, trimmed. - */ - public String nextTo(String delimiters) throws JSONException { - char c; - StringBuffer sb = new StringBuffer(); - for (;;) { - c = next(); - if (delimiters.indexOf(c) >= 0 || c == 0 || - c == '\n' || c == '\r') { - if (c != 0) { - back(); - } - return sb.toString().trim(); - } - sb.append(c); - } - } - - - /** - * Get the next value. The value can be a Boolean, Double, Integer, - * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. - * @throws JSONException If syntax error. - * - * @return An object. - */ - public Object nextValue() throws JSONException { - char c = nextClean(); - String s; - - switch (c) { - case '"': - case '\'': - return nextString(c); - case '{': - back(); - return new JSONObject(this); - case '[': - case '(': - back(); - return new JSONArray(this); - } - - /* - * Handle unquoted text. This could be the values true, false, or - * null, or it can be a number. An implementation (such as this one) - * is allowed to also accept non-standard forms. - * - * Accumulate characters until we reach the end of the text or a - * formatting character. - */ - - StringBuffer sb = new StringBuffer(); - while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) { - sb.append(c); - c = next(); - } - back(); - - s = sb.toString().trim(); - if (s.equals("")) { - throw syntaxError("Missing value"); - } - return JSONObject.stringToValue(s); - } - - - /** - * Skip characters until the next character is the requested character. - * If the requested character is not found, no characters are skipped. - * @param to A character to skip to. - * @return The requested character, or zero if the requested character - * is not found. - */ - public char skipTo(char to) throws JSONException { - char c; - try { - int startIndex = this.index; - int startCharacter = this.character; - int startLine = this.line; - reader.mark(Integer.MAX_VALUE); - do { - c = next(); - if (c == 0) { - reader.reset(); - this.index = startIndex; - this.character = startCharacter; - this.line = startLine; - return c; - } - } while (c != to); - } catch (IOException exc) { - throw new JSONException(exc); - } - - back(); - return c; - } - - - /** - * Make a JSONException to signal a syntax error. - * - * @param message The error message. - * @return A JSONException object, suitable for throwing - */ - public JSONException syntaxError(String message) { - return new JSONException(message + toString()); - } - - - /** - * Make a printable string of this JSONTokener. - * - * @return " at {index} [character {character} line {line}]" - */ - public String toString() { - return " at " + index + " [character " + this.character + " line " + this.line + "]"; - } -} \ No newline at end of file diff --git a/src/main/java/org/json/JSONWriter.java b/src/main/java/org/json/JSONWriter.java deleted file mode 100755 index b4f1dca21..000000000 --- a/src/main/java/org/json/JSONWriter.java +++ /dev/null @@ -1,323 +0,0 @@ -package org.json; - -import java.io.IOException; -import java.io.Writer; - -/* -Copyright (c) 2006 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -/** - * JSONWriter provides a quick and convenient way of producing JSON text. - * The texts produced strictly conform to JSON syntax rules. No whitespace is - * added, so the results are ready for transmission or storage. Each instance of - * JSONWriter can produce one JSON text. - *

    - * A JSONWriter instance provides a value method for appending - * values to the - * text, and a key - * method for adding keys before values in objects. There are array - * and endArray methods that make and bound array values, and - * object and endObject methods which make and bound - * object values. All of these methods return the JSONWriter instance, - * permitting a cascade style. For example,

    - * new JSONWriter(myWriter)
    - *     .object()
    - *         .key("JSON")
    - *         .value("Hello, World!")
    - *     .endObject();
    which writes
    - * {"JSON":"Hello, World!"}
    - *

    - * The first method called must be array or object. - * There are no methods for adding commas or colons. JSONWriter adds them for - * you. Objects and arrays can be nested up to 20 levels deep. - *

    - * This can sometimes be easier than using a JSONObject to build a string. - * @author JSON.org - * @version 2008-09-22 - */ -public class JSONWriter { - private static final int maxdepth = 20; - - /** - * The comma flag determines if a comma should be output before the next - * value. - */ - private boolean comma; - - /** - * The current mode. Values: - * 'a' (array), - * 'd' (done), - * 'i' (initial), - * 'k' (key), - * 'o' (object). - */ - protected char mode; - - /** - * The object/array stack. - */ - private JSONObject stack[]; - - /** - * The stack top index. A value of 0 indicates that the stack is empty. - */ - private int top; - - /** - * The writer that will receive the output. - */ - protected Writer writer; - - /** - * Make a fresh JSONWriter. It can be used to build one JSON text. - */ - public JSONWriter(Writer w) { - this.comma = false; - this.mode = 'i'; - this.stack = new JSONObject[maxdepth]; - this.top = 0; - this.writer = w; - } - - /** - * Append a value. - * @param s A string value. - * @return this - * @throws JSONException If the value is out of sequence. - */ - private JSONWriter append(String s) throws JSONException { - if (s == null) { - throw new JSONException("Null pointer"); - } - if (this.mode == 'o' || this.mode == 'a') { - try { - if (this.comma && this.mode == 'a') { - this.writer.write(','); - } - this.writer.write(s); - } catch (IOException e) { - throw new JSONException(e); - } - if (this.mode == 'o') { - this.mode = 'k'; - } - this.comma = true; - return this; - } - throw new JSONException("Value out of sequence."); - } - - /** - * Begin appending a new array. All values until the balancing - * endArray will be appended to this array. The - * endArray method must be called to mark the array's end. - * @return this - * @throws JSONException If the nesting is too deep, or if the object is - * started in the wrong place (for example as a key or after the end of the - * outermost array or object). - */ - public JSONWriter array() throws JSONException { - if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') { - this.push(null); - this.append("["); - this.comma = false; - return this; - } - throw new JSONException("Misplaced array."); - } - - /** - * End something. - * @param m Mode - * @param c Closing character - * @return this - * @throws JSONException If unbalanced. - */ - private JSONWriter end(char m, char c) throws JSONException { - if (this.mode != m) { - throw new JSONException(m == 'o' ? "Misplaced endObject." : - "Misplaced endArray."); - } - this.pop(m); - try { - this.writer.write(c); - } catch (IOException e) { - throw new JSONException(e); - } - this.comma = true; - return this; - } - - /** - * End an array. This method most be called to balance calls to - * array. - * @return this - * @throws JSONException If incorrectly nested. - */ - public JSONWriter endArray() throws JSONException { - return this.end('a', ']'); - } - - /** - * End an object. This method most be called to balance calls to - * object. - * @return this - * @throws JSONException If incorrectly nested. - */ - public JSONWriter endObject() throws JSONException { - return this.end('k', '}'); - } - - /** - * Append a key. The key will be associated with the next value. In an - * object, every value must be preceded by a key. - * @param s A key string. - * @return this - * @throws JSONException If the key is out of place. For example, keys - * do not belong in arrays or if the key is null. - */ - public JSONWriter key(String s) throws JSONException { - if (s == null) { - throw new JSONException("Null key."); - } - if (this.mode == 'k') { - try { - stack[top - 1].putOnce(s, Boolean.TRUE); - if (this.comma) { - this.writer.write(','); - } - this.writer.write(JSONObject.quote(s)); - this.writer.write(':'); - this.comma = false; - this.mode = 'o'; - return this; - } catch (IOException e) { - throw new JSONException(e); - } - } - throw new JSONException("Misplaced key."); - } - - - /** - * Begin appending a new object. All keys and values until the balancing - * endObject will be appended to this object. The - * endObject method must be called to mark the object's end. - * @return this - * @throws JSONException If the nesting is too deep, or if the object is - * started in the wrong place (for example as a key or after the end of the - * outermost array or object). - */ - public JSONWriter object() throws JSONException { - if (this.mode == 'i') { - this.mode = 'o'; - } - if (this.mode == 'o' || this.mode == 'a') { - this.append("{"); - this.push(new JSONObject()); - this.comma = false; - return this; - } - throw new JSONException("Misplaced object."); - - } - - - /** - * Pop an array or object scope. - * @param c The scope to close. - * @throws JSONException If nesting is wrong. - */ - private void pop(char c) throws JSONException { - if (this.top <= 0) { - throw new JSONException("Nesting error."); - } - char m = this.stack[this.top - 1] == null ? 'a' : 'k'; - if (m != c) { - throw new JSONException("Nesting error."); - } - this.top -= 1; - this.mode = this.top == 0 ? 'd' : this.stack[this.top - 1] == null ? 'a' : 'k'; - } - - /** - * Push an array or object scope. - * @param c The scope to open. - * @throws JSONException If nesting is too deep. - */ - private void push(JSONObject jo) throws JSONException { - if (this.top >= maxdepth) { - throw new JSONException("Nesting too deep."); - } - this.stack[this.top] = jo; - this.mode = jo == null ? 'a' : 'k'; - this.top += 1; - } - - - /** - * Append either the value true or the value - * false. - * @param b A boolean. - * @return this - * @throws JSONException - */ - public JSONWriter value(boolean b) throws JSONException { - return this.append(b ? "true" : "false"); - } - - /** - * Append a double value. - * @param d A double. - * @return this - * @throws JSONException If the number is not finite. - */ - public JSONWriter value(double d) throws JSONException { - return this.value(new Double(d)); - } - - /** - * Append a long value. - * @param l A long. - * @return this - * @throws JSONException - */ - public JSONWriter value(long l) throws JSONException { - return this.append(Long.toString(l)); - } - - - /** - * Append an object value. - * @param o The object to append. It can be null, or a Boolean, Number, - * String, JSONObject, or JSONArray, or an object with a toJSONString() - * method. - * @return this - * @throws JSONException If the value is out of sequence. - */ - public JSONWriter value(Object o) throws JSONException { - return this.append(JSONObject.valueToString(o)); - } -} diff --git a/src/main/java/org/json/Test.java b/src/main/java/org/json/Test.java deleted file mode 100755 index 68651752c..000000000 --- a/src/main/java/org/json/Test.java +++ /dev/null @@ -1,678 +0,0 @@ -package org.json; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.io.StringWriter; - -/** - * Test class. This file is not formally a member of the org.json library. - * It is just a casual test tool. - */ -@SuppressWarnings("unchecked") public class Test { - - /** - * Entry point. - * @param args - */ - public static void main(String args[]) { - Iterator it; - JSONArray a; - JSONObject j; - JSONStringer jj; - Object o; - String s; - -/** - * Obj is a typical class that implements JSONString. It also - * provides some beanie methods that can be used to - * construct a JSONObject. It also demonstrates constructing - * a JSONObject with an array of names. - */ - class Obj implements JSONString { - public String aString; - public double aNumber; - public boolean aBoolean; - - public Obj(String string, double n, boolean b) { - this.aString = string; - this.aNumber = n; - this.aBoolean = b; - } - - public double getNumber() { - return this.aNumber; - } - - public String getString() { - return this.aString; - } - - public boolean isBoolean() { - return this.aBoolean; - } - - public String getBENT() { - return "All uppercase key"; - } - - public String getX() { - return "x"; - } - - public String toJSONString() { - return "{" + JSONObject.quote(this.aString) + ":" + - JSONObject.doubleToString(this.aNumber) + "}"; - } - public String toString() { - return this.getString() + " " + this.getNumber() + " " + - this.isBoolean() + "." + this.getBENT() + " " + this.getX(); - } - } - - - Obj obj = new Obj("A beany object", 42, true); - - try { - s = "[0.1]"; - a = new JSONArray(s); - System.out.println(a.toString()); - System.out.println(""); - - j = XML.toJSONObject(" Ignore the stuff past the end. "); - System.out.println(j.toString()); - System.out.println(""); - - j = new JSONObject(); - o = null; - j.put("booga", o); - j.put("wooga", JSONObject.NULL); - System.out.println(j.toString()); - System.out.println(""); - - j = new JSONObject(); - j.increment("two"); - j.increment("two"); - System.out.println(j.toString()); - System.out.println(""); - - - s = ""; - j = XML.toJSONObject(s); - System.out.println(j.toString(2)); - System.out.println(XML.toString(j)); - System.out.println(""); - - s = "{ \"list of lists\" : [ [1, 2, 3], [4, 5, 6], ] }"; - j = new JSONObject(s); - System.out.println(j.toString(4)); - System.out.println(XML.toString(j)); - - s = " Basic bread Flour Yeast Water Salt Mix all ingredients together. Knead thoroughly. Cover with a cloth, and leave for one hour in warm room. Knead again. Place in a bread baking tin. Cover with a cloth, and leave for one hour in warm room. Bake in the oven at 180(degrees)C for 30 minutes. "; - j = XML.toJSONObject(s); - System.out.println(j.toString(4)); - System.out.println(); - - j = JSONML.toJSONObject(s); - System.out.println(j.toString()); - System.out.println(JSONML.toString(j)); - System.out.println(); - - a = JSONML.toJSONArray(s); - System.out.println(a.toString(4)); - System.out.println(JSONML.toString(a)); - System.out.println(); - - s = "

    JSONML is a transformation between JSON and XML that preserves ordering of document features.

    JSONML can work with JSON arrays or JSON objects.

    Three
    little
    words

    "; - j = JSONML.toJSONObject(s); - System.out.println(j.toString(4)); - System.out.println(JSONML.toString(j)); - System.out.println(); - - a = JSONML.toJSONArray(s); - System.out.println(a.toString(4)); - System.out.println(JSONML.toString(a)); - System.out.println(); - - s = "\n Robert\n Smith\n
    \n 12345 Sixth Ave\n Anytown\n CA\n 98765-4321\n
    \n
    "; - j = XML.toJSONObject(s); - System.out.println(j.toString(4)); - - j = new JSONObject(obj); - System.out.println(j.toString()); - - s = "{ \"entity\": { \"imageURL\": \"\", \"name\": \"IXXXXXXXXXXXXX\", \"id\": 12336, \"ratingCount\": null, \"averageRating\": null } }"; - j = new JSONObject(s); - System.out.println(j.toString(2)); - - jj = new JSONStringer(); - s = jj - .object() - .key("single") - .value("MARIE HAA'S") - .key("Johnny") - .value("MARIE HAA\\'S") - .key("foo") - .value("bar") - .key("baz") - .array() - .object() - .key("quux") - .value("Thanks, Josh!") - .endObject() - .endArray() - .key("obj keys") - .value(JSONObject.getNames(obj)) - .endObject() - .toString(); - System.out.println(s); - - System.out.println(new JSONStringer() - .object() - .key("a") - .array() - .array() - .array() - .value("b") - .endArray() - .endArray() - .endArray() - .endObject() - .toString()); - - jj = new JSONStringer(); - jj.array(); - jj.value(1); - jj.array(); - jj.value(null); - jj.array(); - jj.object(); - jj.key("empty-array").array().endArray(); - jj.key("answer").value(42); - jj.key("null").value(null); - jj.key("false").value(false); - jj.key("true").value(true); - jj.key("big").value(123456789e+88); - jj.key("small").value(123456789e-88); - jj.key("empty-object").object().endObject(); - jj.key("long"); - jj.value(9223372036854775807L); - jj.endObject(); - jj.value("two"); - jj.endArray(); - jj.value(true); - jj.endArray(); - jj.value(98.6); - jj.value(-100.0); - jj.object(); - jj.endObject(); - jj.object(); - jj.key("one"); - jj.value(1.00); - jj.endObject(); - jj.value(obj); - jj.endArray(); - System.out.println(jj.toString()); - - System.out.println(new JSONArray(jj.toString()).toString(4)); - - int ar[] = {1, 2, 3}; - JSONArray ja = new JSONArray(ar); - System.out.println(ja.toString()); - - String sa[] = {"aString", "aNumber", "aBoolean"}; - j = new JSONObject(obj, sa); - j.put("Testing JSONString interface", obj); - System.out.println(j.toString(4)); - - j = new JSONObject("{slashes: '///', closetag: '', backslash:'\\\\', ei: {quotes: '\"\\''},eo: {a: '\"quoted\"', b:\"don't\"}, quotes: [\"'\", '\"']}"); - System.out.println(j.toString(2)); - System.out.println(XML.toString(j)); - System.out.println(""); - - j = new JSONObject( - "{foo: [true, false,9876543210, 0.0, 1.00000001, 1.000000000001, 1.00000000000000001," + - " .00000000000000001, 2.00, 0.1, 2e100, -32,[],{}, \"string\"], " + - " to : null, op : 'Good'," + - "ten:10} postfix comment"); - j.put("String", "98.6"); - j.put("JSONObject", new JSONObject()); - j.put("JSONArray", new JSONArray()); - j.put("int", 57); - j.put("double", 123456789012345678901234567890.); - j.put("true", true); - j.put("false", false); - j.put("null", JSONObject.NULL); - j.put("bool", "true"); - j.put("zero", -0.0); - j.put("\\u2028", "\u2028"); - j.put("\\u2029", "\u2029"); - a = j.getJSONArray("foo"); - a.put(666); - a.put(2001.99); - a.put("so \"fine\"."); - a.put("so ."); - a.put(true); - a.put(false); - a.put(new JSONArray()); - a.put(new JSONObject()); - j.put("keys", JSONObject.getNames(j)); - System.out.println(j.toString(4)); - System.out.println(XML.toString(j)); - - System.out.println("String: " + j.getDouble("String")); - System.out.println(" bool: " + j.getBoolean("bool")); - System.out.println(" to: " + j.getString("to")); - System.out.println(" true: " + j.getString("true")); - System.out.println(" foo: " + j.getJSONArray("foo")); - System.out.println(" op: " + j.getString("op")); - System.out.println(" ten: " + j.getInt("ten")); - System.out.println(" oops: " + j.optBoolean("oops")); - - s = "First \u0009<content> This is \"content\". 3 JSON does not preserve the sequencing of elements and contents. III T H R E EContent text is an implied structure in XML. JSON does not have implied structure:7everything is explicit.!]]>"; - j = XML.toJSONObject(s); - System.out.println(j.toString(2)); - System.out.println(XML.toString(j)); - System.out.println(""); - - ja = JSONML.toJSONArray(s); - System.out.println(ja.toString(4)); - System.out.println(JSONML.toString(ja)); - System.out.println(""); - - s = "unodostrestruequatrocinqoseis"; - ja = JSONML.toJSONArray(s); - System.out.println(ja.toString(4)); - System.out.println(JSONML.toString(ja)); - System.out.println(""); - - s = " "; - j = XML.toJSONObject(s); - - System.out.println(j.toString(2)); - System.out.println(XML.toString(j)); - System.out.println(""); - ja = JSONML.toJSONArray(s); - System.out.println(ja.toString(4)); - System.out.println(JSONML.toString(ja)); - System.out.println(""); - - j = XML.toJSONObject("Sample BookThis is chapter 1. It is not very long or interesting.This is chapter 2. Although it is longer than chapter 1, it is not any more interesting."); - System.out.println(j.toString(2)); - System.out.println(XML.toString(j)); - System.out.println(""); - - j = XML.toJSONObject(""); - System.out.println(j.toString(2)); - System.out.println(XML.toString(j)); - System.out.println(""); - - j = XML.toJSONObject(" Fred fbs0001 Scerbo B "); - System.out.println(j.toString(2)); - System.out.println(XML.toString(j)); - System.out.println(""); - - j = XML.toJSONObject("Repository Address Special Collections LibraryABC UniversityMain Library, 40 Circle DriveOurtown, Pennsylvania17654 USA"); - System.out.println(j.toString()); - System.out.println(XML.toString(j)); - System.out.println(""); - - j = XML.toJSONObject("deluxe&"toot"&toot;Aeksbonusbonus2"); - System.out.println(j.toString(2)); - System.out.println(XML.toString(j)); - System.out.println(""); - - j = HTTP.toJSONObject("GET / HTTP/1.0\nAccept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*\nAccept-Language: en-us\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; Win 9x 4.90; T312461; Q312461)\nHost: www.nokko.com\nConnection: keep-alive\nAccept-encoding: gzip, deflate\n"); - System.out.println(j.toString(2)); - System.out.println(HTTP.toString(j)); - System.out.println(""); - - j = HTTP.toJSONObject("HTTP/1.1 200 Oki Doki\nDate: Sun, 26 May 2002 17:38:52 GMT\nServer: Apache/1.3.23 (Unix) mod_perl/1.26\nKeep-Alive: timeout=15, max=100\nConnection: Keep-Alive\nTransfer-Encoding: chunked\nContent-Type: text/html\n"); - System.out.println(j.toString(2)); - System.out.println(HTTP.toString(j)); - System.out.println(""); - - j = new JSONObject("{nix: null, nux: false, null: 'null', 'Request-URI': '/', Method: 'GET', 'HTTP-Version': 'HTTP/1.0'}"); - System.out.println(j.toString(2)); - System.out.println("isNull: " + j.isNull("nix")); - System.out.println(" has: " + j.has("nix")); - System.out.println(XML.toString(j)); - System.out.println(HTTP.toString(j)); - System.out.println(""); - - j = XML.toJSONObject(""+"\n\n"+""+ - ""+ - "GOOGLEKEY '+search+' 0 10 true false latin1 latin1"+ - ""+ - ""); - System.out.println(j.toString(2)); - System.out.println(XML.toString(j)); - System.out.println(""); - - j = new JSONObject("{Envelope: {Body: {\"ns1:doGoogleSearch\": {oe: \"latin1\", filter: true, q: \"'+search+'\", key: \"GOOGLEKEY\", maxResults: 10, \"SOAP-ENV:encodingStyle\": \"http://schemas.xmlsoap.org/soap/encoding/\", start: 0, ie: \"latin1\", safeSearch:false, \"xmlns:ns1\": \"urn:GoogleSearch\"}}}}"); - System.out.println(j.toString(2)); - System.out.println(XML.toString(j)); - System.out.println(""); - - j = CookieList.toJSONObject(" f%oo = b+l=ah ; o;n%40e = t.wo "); - System.out.println(j.toString(2)); - System.out.println(CookieList.toString(j)); - System.out.println(""); - - j = Cookie.toJSONObject("f%oo=blah; secure ;expires = April 24, 2002"); - System.out.println(j.toString(2)); - System.out.println(Cookie.toString(j)); - System.out.println(""); - - j = new JSONObject("{script: 'It is not allowed in HTML to send a close script tag in a stringso we insert a backslash before the /'}"); - System.out.println(j.toString()); - System.out.println(""); - - JSONTokener jt = new JSONTokener("{op:'test', to:'session', pre:1}{op:'test', to:'session', pre:2}"); - j = new JSONObject(jt); - System.out.println(j.toString()); - System.out.println("pre: " + j.optInt("pre")); - int i = jt.skipTo('{'); - System.out.println(i); - j = new JSONObject(jt); - System.out.println(j.toString()); - System.out.println(""); - - a = CDL.toJSONArray("Comma delimited list test, '\"Strip\"Quotes', 'quote, comma', No quotes, 'Single Quotes', \"Double Quotes\"\n1,'2',\"3\"\n,'It is \"good,\"', \"It works.\"\n\n"); - - s = CDL.toString(a); - System.out.println(s); - System.out.println(""); - System.out.println(a.toString(4)); - System.out.println(""); - a = CDL.toJSONArray(s); - System.out.println(a.toString(4)); - System.out.println(""); - - a = new JSONArray(" [\"\", next is an implied null , , ok,] "); - System.out.println(a.toString()); - System.out.println(""); - System.out.println(XML.toString(a)); - System.out.println(""); - - j = new JSONObject("{ fun => with non-standard forms ; forgiving => This package can be used to parse formats that are similar to but not stricting conforming to JSON; why=To make it easier to migrate existing data to JSON,one = [[1.00]]; uno=[[{1=>1}]];'+':+6e66 ;pluses=+++;empty = '' , 'double':0.666,true: TRUE, false: FALSE, null=NULL;[true] = [[!,@;*]]; string=> o. k. ; \r oct=0666; hex=0x666; dec=666; o=0999; noh=0x0x}"); - System.out.println(j.toString(4)); - System.out.println(""); - if (j.getBoolean("true") && !j.getBoolean("false")) { - System.out.println("It's all good"); - } - - System.out.println(""); - j = new JSONObject(j, new String[]{"dec", "oct", "hex", "missing"}); - System.out.println(j.toString(4)); - - System.out.println(""); - System.out.println(new JSONStringer().array().value(a).value(j).endArray()); - - j = new JSONObject("{string: \"98.6\", long: 2147483648, int: 2147483647, longer: 9223372036854775807, double: 9223372036854775808}"); - System.out.println(j.toString(4)); - - System.out.println("\ngetInt"); - System.out.println("int " + j.getInt("int")); - System.out.println("long " + j.getInt("long")); - System.out.println("longer " + j.getInt("longer")); - System.out.println("double " + j.getInt("double")); - System.out.println("string " + j.getInt("string")); - - System.out.println("\ngetLong"); - System.out.println("int " + j.getLong("int")); - System.out.println("long " + j.getLong("long")); - System.out.println("longer " + j.getLong("longer")); - System.out.println("double " + j.getLong("double")); - System.out.println("string " + j.getLong("string")); - - System.out.println("\ngetDouble"); - System.out.println("int " + j.getDouble("int")); - System.out.println("long " + j.getDouble("long")); - System.out.println("longer " + j.getDouble("longer")); - System.out.println("double " + j.getDouble("double")); - System.out.println("string " + j.getDouble("string")); - - j.put("good sized", 9223372036854775807L); - System.out.println(j.toString(4)); - - a = new JSONArray("[2147483647, 2147483648, 9223372036854775807, 9223372036854775808]"); - System.out.println(a.toString(4)); - - System.out.println("\nKeys: "); - it = j.keys(); - while (it.hasNext()) { - s = (String)it.next(); - System.out.println(s + ": " + j.getString(s)); - } - - - System.out.println("\naccumulate: "); - j = new JSONObject(); - j.accumulate("stooge", "Curly"); - j.accumulate("stooge", "Larry"); - j.accumulate("stooge", "Moe"); - a = j.getJSONArray("stooge"); - a.put(5, "Shemp"); - System.out.println(j.toString(4)); - - System.out.println("\nwrite:"); - System.out.println(j.write(new StringWriter())); - - s = "122333"; - j = XML.toJSONObject(s); - System.out.println(j.toString(4)); - System.out.println(XML.toString(j)); - - s = "Content of the first chapterContent of the second chapter Content of the first subchapter Content of the second subchapterThird Chapter"; - j = XML.toJSONObject(s); - System.out.println(j.toString(4)); - System.out.println(XML.toString(j)); - - a = JSONML.toJSONArray(s); - System.out.println(a.toString(4)); - System.out.println(JSONML.toString(a)); - - Collection c = null; - Map m = null; - - j = new JSONObject(m); - a = new JSONArray(c); - j.append("stooge", "Joe DeRita"); - j.append("stooge", "Shemp"); - j.accumulate("stooges", "Curly"); - j.accumulate("stooges", "Larry"); - j.accumulate("stooges", "Moe"); - j.accumulate("stoogearray", j.get("stooges")); - j.put("map", m); - j.put("collection", c); - j.put("array", a); - a.put(m); - a.put(c); - System.out.println(j.toString(4)); - - s = "{plist=Apple; AnimalSmells = { pig = piggish; lamb = lambish; worm = wormy; }; AnimalSounds = { pig = oink; lamb = baa; worm = baa; Lisa = \"Why is the worm talking like a lamb?\" } ; AnimalColors = { pig = pink; lamb = black; worm = pink; } } "; - j = new JSONObject(s); - System.out.println(j.toString(4)); - - s = " (\"San Francisco\", \"New York\", \"Seoul\", \"London\", \"Seattle\", \"Shanghai\")"; - a = new JSONArray(s); - System.out.println(a.toString()); - - s = "The content of b and The content of cdoremi"; - j = XML.toJSONObject(s); - - System.out.println(j.toString(2)); - System.out.println(XML.toString(j)); - System.out.println(""); - ja = JSONML.toJSONArray(s); - System.out.println(ja.toString(4)); - System.out.println(JSONML.toString(ja)); - System.out.println(""); - - s = "111111111111111"; - j = JSONML.toJSONObject(s); - System.out.println(j); - ja = JSONML.toJSONArray(s); - System.out.println(ja); - - - System.out.println("\nTesting Exceptions: "); - - System.out.print("Exception: "); - try { - a = new JSONArray("[\n\r\n\r}"); - System.out.println(a.toString()); - } catch (Exception e) { - System.out.println(e); - } - - System.out.print("Exception: "); - try { - a = new JSONArray("<\n\r\n\r "); - System.out.println(a.toString()); - } catch (Exception e) { - System.out.println(e); - } - - System.out.print("Exception: "); - try { - a = new JSONArray(); - a.put(Double.NEGATIVE_INFINITY); - a.put(Double.NaN); - System.out.println(a.toString()); - } catch (Exception e) { - System.out.println(e); - } - System.out.print("Exception: "); - try { - System.out.println(j.getDouble("stooge")); - } catch (Exception e) { - System.out.println(e); - } - System.out.print("Exception: "); - try { - System.out.println(j.getDouble("howard")); - } catch (Exception e) { - System.out.println(e); - } - System.out.print("Exception: "); - try { - System.out.println(j.put(null, "howard")); - } catch (Exception e) { - System.out.println(e); - } - System.out.print("Exception: "); - try { - System.out.println(a.getDouble(0)); - } catch (Exception e) { - System.out.println(e); - } - System.out.print("Exception: "); - try { - System.out.println(a.get(-1)); - } catch (Exception e) { - System.out.println(e); - } - System.out.print("Exception: "); - try { - System.out.println(a.put(Double.NaN)); - } catch (Exception e) { - System.out.println(e); - } - System.out.print("Exception: "); - try { - j = XML.toJSONObject(" "); - } catch (Exception e) { - System.out.println(e); - } - System.out.print("Exception: "); - try { - j = XML.toJSONObject(" "); - } catch (Exception e) { - System.out.println(e); - } - System.out.print("Exception: "); - try { - j = XML.toJSONObject("'. */ - public static final Character GT = new Character('>'); - - /** The Character '<'. */ - public static final Character LT = new Character('<'); - - /** The Character '?'. */ - public static final Character QUEST = new Character('?'); - - /** The Character '"'. */ - public static final Character QUOT = new Character('"'); - - /** The Character '/'. */ - public static final Character SLASH = new Character('/'); - - /** - * Replace special characters with XML escapes: - *
    -     * & (ampersand) is replaced by &amp;
    -     * < (less than) is replaced by &lt;
    -     * > (greater than) is replaced by &gt;
    -     * " (double quote) is replaced by &quot;
    -     * 
    - * @param string The string to be escaped. - * @return The escaped string. - */ - public static String escape(String string) { - StringBuffer sb = new StringBuffer(); - for (int i = 0, len = string.length(); i < len; i++) { - char c = string.charAt(i); - switch (c) { - case '&': - sb.append("&"); - break; - case '<': - sb.append("<"); - break; - case '>': - sb.append(">"); - break; - case '"': - sb.append("""); - break; - default: - sb.append(c); - } - } - return sb.toString(); - } - - /** - * Throw an exception if the string contains whitespace. - * Whitespace is not allowed in tagNames and attributes. - * @param string - * @throws JSONException - */ - public static void noSpace(String string) throws JSONException { - int i, length = string.length(); - if (length == 0) { - throw new JSONException("Empty string."); - } - for (i = 0; i < length; i += 1) { - if (Character.isWhitespace(string.charAt(i))) { - throw new JSONException("'" + string + - "' contains a space character."); - } - } - } - - /** - * Scan the content following the named tag, attaching it to the context. - * @param x The XMLTokener containing the source string. - * @param context The JSONObject that will include the new material. - * @param name The tag name. - * @return true if the close tag is processed. - * @throws JSONException - */ - private static boolean parse(XMLTokener x, JSONObject context, - String name) throws JSONException { - char c; - int i; - String n; - JSONObject o = null; - String s; - Object t; - -// Test for and skip past these forms: -// -// -// -// -// Report errors for these forms: -// <> -// <= -// << - - t = x.nextToken(); - -// "); - return false; - } - x.back(); - } else if (c == '[') { - t = x.nextToken(); - if (t.equals("CDATA")) { - if (x.next() == '[') { - s = x.nextCDATA(); - if (s.length() > 0) { - context.accumulate("content", s); - } - return false; - } - } - throw x.syntaxError("Expected 'CDATA['"); - } - i = 1; - do { - t = x.nextMeta(); - if (t == null) { - throw x.syntaxError("Missing '>' after ' 0); - return false; - } else if (t == QUEST) { - -// "); - return false; - } else if (t == SLASH) { - -// Close tag - - } else if (t == SLASH) { - if (x.nextToken() != GT) { - throw x.syntaxError("Misshaped tag"); - } - context.accumulate(n, ""); - return false; - -// Content, between <...> and - - } else if (t == GT) { - for (;;) { - t = x.nextContent(); - if (t == null) { - if (n != null) { - throw x.syntaxError("Unclosed tag " + n); - } - return false; - } else if (t instanceof String) { - s = (String)t; - if (s.length() > 0) { - o.accumulate("content", JSONObject.stringToValue(s)); - } - -// Nested element - - } else if (t == LT) { - if (parse(x, o, n)) { - if (o.length() == 0) { - context.accumulate(n, ""); - } else if (o.length() == 1 && - o.opt("content") != null) { - context.accumulate(n, o.opt("content")); - } else { - context.accumulate(n, o); - } - return false; - } - } - } - } else { - throw x.syntaxError("Misshaped tag"); - } - } - } - } - - - /** - * Convert a well-formed (but not necessarily valid) XML string into a - * JSONObject. Some information may be lost in this transformation - * because JSON is a data format and XML is a document format. XML uses - * elements, attributes, and content text, while JSON uses unordered - * collections of name/value pairs and arrays of values. JSON does not - * does not like to distinguish between elements and attributes. - * Sequences of similar elements are represented as JSONArrays. Content - * text may be placed in a "content" member. Comments, prologs, DTDs, and - * <[ [ ]]> are ignored. - * @param string The source string. - * @return A JSONObject containing the structured data from the XML string. - * @throws JSONException - */ - public static JSONObject toJSONObject(String string) throws JSONException { - JSONObject o = new JSONObject(); - XMLTokener x = new XMLTokener(string); - while (x.more() && x.skipPast("<")) { - parse(x, o, null); - } - return o; - } - - - /** - * Convert a JSONObject into a well-formed, element-normal XML string. - * @param o A JSONObject. - * @return A string. - * @throws JSONException - */ - public static String toString(Object o) throws JSONException { - return toString(o, null); - } - - - /** - * Convert a JSONObject into a well-formed, element-normal XML string. - * @param o A JSONObject. - * @param tagName The optional name of the enclosing tag. - * @return A string. - * @throws JSONException - */ - public static String toString(Object o, String tagName) - throws JSONException { - StringBuffer b = new StringBuffer(); - int i; - JSONArray ja; - JSONObject jo; - String k; - Iterator keys; - int len; - String s; - Object v; - if (o instanceof JSONObject) { - -// Emit - - if (tagName != null) { - b.append('<'); - b.append(tagName); - b.append('>'); - } - -// Loop thru the keys. - - jo = (JSONObject)o; - keys = jo.keys(); - while (keys.hasNext()) { - k = keys.next().toString(); - v = jo.opt(k); - if (v == null) { - v = ""; - } - if (v instanceof String) { - s = (String)v; - } else { - s = null; - } - -// Emit content in body - - if (k.equals("content")) { - if (v instanceof JSONArray) { - ja = (JSONArray)v; - len = ja.length(); - for (i = 0; i < len; i += 1) { - if (i > 0) { - b.append('\n'); - } - b.append(escape(ja.get(i).toString())); - } - } else { - b.append(escape(v.toString())); - } - -// Emit an array of similar keys - - } else if (v instanceof JSONArray) { - ja = (JSONArray)v; - len = ja.length(); - for (i = 0; i < len; i += 1) { - v = ja.get(i); - if (v instanceof JSONArray) { - b.append('<'); - b.append(k); - b.append('>'); - b.append(toString(v)); - b.append("'); - } else { - b.append(toString(v, k)); - } - } - } else if (v.equals("")) { - b.append('<'); - b.append(k); - b.append("/>"); - -// Emit a new tag - - } else { - b.append(toString(v, k)); - } - } - if (tagName != null) { - -// Emit the close tag - - b.append("'); - } - return b.toString(); - -// XML does not have good support for arrays. If an array appears in a place -// where XML is lacking, synthesize an element. - - } else if (o instanceof JSONArray) { - ja = (JSONArray)o; - len = ja.length(); - for (i = 0; i < len; ++i) { - v = ja.opt(i); - b.append(toString(v, (tagName == null) ? "array" : tagName)); - } - return b.toString(); - } else { - s = (o == null) ? "null" : escape(o.toString()); - return (tagName == null) ? "\"" + s + "\"" : - (s.length() == 0) ? "<" + tagName + "/>" : - "<" + tagName + ">" + s + ""; - } - } -} \ No newline at end of file diff --git a/src/main/java/org/json/XMLTokener.java b/src/main/java/org/json/XMLTokener.java deleted file mode 100755 index c7b9b68da..000000000 --- a/src/main/java/org/json/XMLTokener.java +++ /dev/null @@ -1,365 +0,0 @@ -package org.json; - -/* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -/** - * The XMLTokener extends the JSONTokener to provide additional methods - * for the parsing of XML texts. - * @author JSON.org - * @version 2010-01-30 - */ -@SuppressWarnings("unchecked") public class XMLTokener extends JSONTokener { - - - /** The table of entity values. It initially contains Character values for - * amp, apos, gt, lt, quot. - */ - public static final java.util.HashMap entity; - - static { - entity = new java.util.HashMap(8); - entity.put("amp", XML.AMP); - entity.put("apos", XML.APOS); - entity.put("gt", XML.GT); - entity.put("lt", XML.LT); - entity.put("quot", XML.QUOT); - } - - /** - * Construct an XMLTokener from a string. - * @param s A source string. - */ - public XMLTokener(String s) { - super(s); - } - - /** - * Get the text in the CDATA block. - * @return The string up to the ]]>. - * @throws JSONException If the ]]> is not found. - */ - public String nextCDATA() throws JSONException { - char c; - int i; - StringBuffer sb = new StringBuffer(); - for (;;) { - c = next(); - if (end()) { - throw syntaxError("Unclosed CDATA"); - } - sb.append(c); - i = sb.length() - 3; - if (i >= 0 && sb.charAt(i) == ']' && - sb.charAt(i + 1) == ']' && sb.charAt(i + 2) == '>') { - sb.setLength(i); - return sb.toString(); - } - } - } - - - /** - * Get the next XML outer token, trimming whitespace. There are two kinds - * of tokens: the '<' character which begins a markup tag, and the content - * text between markup tags. - * - * @return A string, or a '<' Character, or null if there is no more - * source text. - * @throws JSONException - */ - public Object nextContent() throws JSONException { - char c; - StringBuffer sb; - do { - c = next(); - } while (Character.isWhitespace(c)); - if (c == 0) { - return null; - } - if (c == '<') { - return XML.LT; - } - sb = new StringBuffer(); - for (;;) { - if (c == '<' || c == 0) { - back(); - return sb.toString().trim(); - } - if (c == '&') { - sb.append(nextEntity(c)); - } else { - sb.append(c); - } - c = next(); - } - } - - - /** - * Return the next entity. These entities are translated to Characters: - * & ' > < ". - * @param a An ampersand character. - * @return A Character or an entity String if the entity is not recognized. - * @throws JSONException If missing ';' in XML entity. - */ - public Object nextEntity(char a) throws JSONException { - StringBuffer sb = new StringBuffer(); - for (;;) { - char c = next(); - if (Character.isLetterOrDigit(c) || c == '#') { - sb.append(Character.toLowerCase(c)); - } else if (c == ';') { - break; - } else { - throw syntaxError("Missing ';' in XML entity: &" + sb); - } - } - String s = sb.toString(); - Object e = entity.get(s); - return e != null ? e : a + s + ";"; - } - - - /** - * Returns the next XML meta token. This is used for skipping over - * and structures. - * @return Syntax characters (< > / = ! ?) are returned as - * Character, and strings and names are returned as Boolean. We don't care - * what the values actually are. - * @throws JSONException If a string is not properly closed or if the XML - * is badly structured. - */ - public Object nextMeta() throws JSONException { - char c; - char q; - do { - c = next(); - } while (Character.isWhitespace(c)); - switch (c) { - case 0: - throw syntaxError("Misshaped meta tag"); - case '<': - return XML.LT; - case '>': - return XML.GT; - case '/': - return XML.SLASH; - case '=': - return XML.EQ; - case '!': - return XML.BANG; - case '?': - return XML.QUEST; - case '"': - case '\'': - q = c; - for (;;) { - c = next(); - if (c == 0) { - throw syntaxError("Unterminated string"); - } - if (c == q) { - return Boolean.TRUE; - } - } - default: - for (;;) { - c = next(); - if (Character.isWhitespace(c)) { - return Boolean.TRUE; - } - switch (c) { - case 0: - case '<': - case '>': - case '/': - case '=': - case '!': - case '?': - case '"': - case '\'': - back(); - return Boolean.TRUE; - } - } - } - } - - - /** - * Get the next XML Token. These tokens are found inside of angle - * brackets. It may be one of these characters: / > = ! ? or it - * may be a string wrapped in single quotes or double quotes, or it may be a - * name. - * @return a String or a Character. - * @throws JSONException If the XML is not well formed. - */ - public Object nextToken() throws JSONException { - char c; - char q; - StringBuffer sb; - do { - c = next(); - } while (Character.isWhitespace(c)); - switch (c) { - case 0: - throw syntaxError("Misshaped element"); - case '<': - throw syntaxError("Misplaced '<'"); - case '>': - return XML.GT; - case '/': - return XML.SLASH; - case '=': - return XML.EQ; - case '!': - return XML.BANG; - case '?': - return XML.QUEST; - -// Quoted string - - case '"': - case '\'': - q = c; - sb = new StringBuffer(); - for (;;) { - c = next(); - if (c == 0) { - throw syntaxError("Unterminated string"); - } - if (c == q) { - return sb.toString(); - } - if (c == '&') { - sb.append(nextEntity(c)); - } else { - sb.append(c); - } - } - default: - -// Name - - sb = new StringBuffer(); - for (;;) { - sb.append(c); - c = next(); - if (Character.isWhitespace(c)) { - return sb.toString(); - } - switch (c) { - case 0: - return sb.toString(); - case '>': - case '/': - case '=': - case '!': - case '?': - case '[': - case ']': - back(); - return sb.toString(); - case '<': - case '"': - case '\'': - throw syntaxError("Bad character in a name"); - } - } - } - } - - - /** - * Skip characters until past the requested string. - * If it is not found, we are left at the end of the source with a result of false. - * @param to A string to skip past. - * @throws JSONException - */ - public boolean skipPast(String to) throws JSONException { - boolean b; - char c; - int i; - int j; - int offset = 0; - int n = to.length(); - char[] circle = new char[n]; - - /* - * First fill the circle buffer with as many characters as are in the - * to string. If we reach an early end, bail. - */ - - for (i = 0; i < n; i += 1) { - c = next(); - if (c == 0) { - return false; - } - circle[i] = c; - } - /* - * We will loop, possibly for all of the remaining characters. - */ - for (;;) { - j = offset; - b = true; - /* - * Compare the circle buffer with the to string. - */ - for (i = 0; i < n; i += 1) { - if (circle[j] != to.charAt(i)) { - b = false; - break; - } - j += 1; - if (j >= n) { - j -= n; - } - } - /* - * If we exit the loop with b intact, then victory is ours. - */ - if (b) { - return true; - } - /* - * Get the next character. If there isn't one, then defeat is ours. - */ - c = next(); - if (c == 0) { - return false; - } - /* - * Shove the character in the circle buffer and advance the - * circle offset. The offset is mod n. - */ - circle[offset] = c; - offset += 1; - if (offset >= n) { - offset -= n; - } - } - } -} diff --git a/src/test/java/com/basho/riak/client/AppTest.java b/src/test/java/com/basho/riak/client/AppTest.java new file mode 100755 index 000000000..9ad63e7c1 --- /dev/null +++ b/src/test/java/com/basho/riak/client/AppTest.java @@ -0,0 +1,38 @@ +package com.basho.riak.client; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +} diff --git a/src/test/java/com/basho/riak/client/Hosts.java b/src/test/java/com/basho/riak/client/Hosts.java deleted file mode 100644 index 2ee540917..000000000 --- a/src/test/java/com/basho/riak/client/Hosts.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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; - -import java.util.regex.Pattern; - -/** - * unite host names for testing.
    - * your environment is not {@code 127.0.0.1}, you also modify app.config.
    - * there is a example. in this case, your riak server is {@code 192.168.0.2}. - * - *
    - * from {http, [ {"127.0.0.1", 8098 } ]},
    - * to   {http, [ {"127.0.0.1", 8098 }, {"192.168.0.2",8098} ]},
    - * 
    - * from {pb_ip,    "127.0.0.1" },
    - * to   {pb_ip,    "192.168.0.2" },
    - * 
    - * and then use there System properties.
    - *
      - *
    • {@code com.basho.riak.host}
        riak server host name
      - *
    • {@code com.basho.riak.pbc.port}
        riak server protocol buffers port
      - *
    • {@code com.basho.riak.http.port}
        riak server REST port
      - *
    - * set System properties like this. - *
    - * -Dcom.basho.riak.host=192.168.0.2
    - * 
    - * @author taichi - */ -public class Hosts { - - public static final String PREFIX = "com.basho.riak."; - public static final String KEY_HOST = PREFIX + "host"; - public static final String KEY_PBC_PORT = PREFIX + "pbc.port"; - public static final String KEY_HTTP_PORT = PREFIX + "http.port"; - - public static final String RIAK_HOST; - - public static final int RIAK_PORT; - - static { - RIAK_HOST = System.getProperty(KEY_HOST, "127.0.0.1"); - String port = System.getProperty(KEY_PBC_PORT); - if (port != null && Pattern.matches("\\d+", port)) { - RIAK_PORT = Integer.parseInt(port); - } else { - RIAK_PORT = 8087; - } - } - - public static final String RIAK_URL; - - static { - StringBuilder stb = new StringBuilder(); - stb.append("http://"); - stb.append(RIAK_HOST); - stb.append(":"); - stb.append(System.getProperty(KEY_HTTP_PORT, "8098")); - stb.append("/riak"); - RIAK_URL = stb.toString(); - } - -} diff --git a/src/test/java/com/basho/riak/client/TestRiakBucketInfo.java b/src/test/java/com/basho/riak/client/TestRiakBucketInfo.java deleted file mode 100644 index 89bc92afd..000000000 --- a/src/test/java/com/basho/riak/client/TestRiakBucketInfo.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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; - -import static org.junit.Assert.*; - -import java.util.ArrayList; -import java.util.List; - -import org.json.JSONException; -import org.json.JSONObject; -import org.junit.Test; - -import com.basho.riak.client.util.Constants; - -public class TestRiakBucketInfo { - - RiakBucketInfo impl; - JSONObject schema = new JSONObject(); - List keys = new ArrayList(); - - @Test public void constructor_schema_and_keys_returned_by_accessors() { - final String KEY = "key"; - keys.add(KEY); - - impl = new RiakBucketInfo(schema, keys); - - assertSame(schema, impl.getSchema()); - assertTrue(impl.getKeys().contains(KEY)); - } - - @Test public void objects_constructed_for_null_constructor_arguments() { - impl = new RiakBucketInfo(null, null); - assertNotNull(impl.getSchema()); - assertNotNull(impl.getKeys()); - } - - @Test public void allow_mult_sets_schema() throws JSONException { - final boolean ALLOW_MULT = true; - - impl = new RiakBucketInfo(); - impl.setAllowMult(ALLOW_MULT); - - assertEquals(ALLOW_MULT, impl.getAllowMult()); - assertEquals(ALLOW_MULT, impl.getSchema().getBoolean(Constants.FL_SCHEMA_ALLOW_MULT)); - } - - @Test public void n_val_sets_schema() throws JSONException { - final int N_VAL = 10; - - impl = new RiakBucketInfo(); - impl.setNVal(N_VAL); - - assertEquals(N_VAL, (int) impl.getNVal()); - assertEquals(N_VAL, impl.getSchema().getInt(Constants.FL_SCHEMA_NVAL)); - } - - @Test public void link_fun_sets_schema() throws JSONException { - final String LINK_MOD = "link_mod"; - final String LINK_FUN = "link_fun"; - - impl = new RiakBucketInfo(); - impl.setLinkFun(LINK_MOD, LINK_FUN); - - assertEquals(LINK_MOD + ":" + LINK_FUN, impl.getLinkFun()); - assertEquals(LINK_MOD, - impl.getSchema().getJSONObject(Constants.FL_SCHEMA_LINKFUN).getString(Constants.FL_SCHEMA_LINKFUN_MOD)); - assertEquals(LINK_FUN, - impl.getSchema().getJSONObject(Constants.FL_SCHEMA_LINKFUN).getString(Constants.FL_SCHEMA_LINKFUN_FUN)); - } - - @Test public void chash_fun_sets_schema() throws JSONException { - final String HASH_MOD = "hash_mod"; - final String HASH_FUN = "hash_fun"; - - impl = new RiakBucketInfo(); - impl.setCHashFun(HASH_MOD, HASH_FUN); - - assertEquals(HASH_MOD + ":" + HASH_FUN, impl.getCHashFun()); - assertEquals(HASH_MOD, - impl.getSchema().getJSONObject(Constants.FL_SCHEMA_CHASHFUN).getString(Constants.FL_SCHEMA_CHASHFUN_MOD)); - assertEquals(HASH_FUN, - impl.getSchema().getJSONObject(Constants.FL_SCHEMA_CHASHFUN).getString(Constants.FL_SCHEMA_CHASHFUN_FUN)); - } -} diff --git a/src/test/java/com/basho/riak/client/TestRiakClient.java b/src/test/java/com/basho/riak/client/TestRiakClient.java deleted file mode 100644 index 47e56a081..000000000 --- a/src/test/java/com/basho/riak/client/TestRiakClient.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * 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; - -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import org.json.JSONException; -import org.json.JSONObject; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import com.basho.riak.client.request.RequestMeta; -import com.basho.riak.client.request.RiakWalkSpec; -import com.basho.riak.client.response.FetchResponse; -import com.basho.riak.client.response.HttpResponse; -import com.basho.riak.client.response.RiakResponseRuntimeException; -import com.basho.riak.client.response.StreamHandler; -import com.basho.riak.client.response.WalkResponse; -import com.basho.riak.client.util.ClientHelper; -import com.basho.riak.client.util.Constants; - -public class TestRiakClient { - - final String bucket = "bucket"; - final String key = "key"; - final String walkSpec = "walkSpec"; - final String mrJob = "mrJob"; - - @Mock ClientHelper mockHelper; - @Mock RiakBucketInfo bucketInfo; - @Mock StreamHandler handler; - @Mock RequestMeta meta; - @Mock RiakObject object; - @Mock RiakWalkSpec riakWalkSpec; - @Mock HttpResponse mockHttpResponse; - RiakClient impl; - - @Before public void setup() { - MockitoAnnotations.initMocks(this); - when(mockHttpResponse.getHttpHeaders()).thenReturn(new HashMap()); - impl = new RiakClient(mockHelper); - } - - @Test public void interface_methods_defer_to_helper() throws IOException { - - impl.setBucketSchema(bucket, bucketInfo, meta); - verify(mockHelper).setBucketSchema(eq(bucket), any(JSONObject.class), same(meta)); - - impl.getBucketSchema(bucket, meta); - verify(mockHelper).getBucketSchema(bucket, meta); - - impl.listBucket(bucket, meta); - verify(mockHelper).listBucket(bucket, meta, false); - - impl.streamBucket(bucket, meta); - verify(mockHelper).listBucket(bucket, meta, true); - - impl.store(object, meta); - verify(mockHelper).store(object, meta); - - impl.fetchMeta(bucket, key, meta); - verify(mockHelper).fetchMeta(bucket, key, meta); - - impl.fetch(bucket, key, meta, false); - verify(mockHelper).fetch(bucket, key, meta, false); - - impl.stream(bucket, key, handler, meta); - verify(mockHelper).stream(bucket, key, handler, meta); - - impl.delete(bucket, key, meta); - verify(mockHelper).delete(bucket, key, meta); - - impl.walk(bucket, key, walkSpec, meta); - verify(mockHelper).walk(bucket, key, walkSpec, meta); - - impl.mapReduce(mrJob, meta); - verify(mockHelper).mapReduce(mrJob, meta); - } - - @Test public void convenience_methods_defer_to_main_methods_with_null_meta() { - impl = spy(impl); - - impl.setBucketSchema(bucket, bucketInfo); - verify(impl).setBucketSchema(bucket, bucketInfo, null); - - impl.getBucketSchema(bucket); - verify(impl).getBucketSchema(bucket, null); - - impl.listBucket(bucket); - verify(impl).listBucket(bucket, null); - - impl.streamBucket(bucket); - verify(impl).streamBucket(bucket, null); - - impl.store(object); - verify(impl).store(object, null); - - impl.fetchMeta(bucket, key); - verify(impl).fetchMeta(bucket, key, null); - - impl.fetch(bucket, key); - verify(impl).fetch(bucket, key, null, false); - - impl.fetch(bucket, key, meta); - verify(impl).fetch(bucket, key, meta, false); - - impl.stream(bucket, key); - verify(impl).fetch(bucket, key, null, true); - - impl.stream(bucket, key, meta); - verify(impl).fetch(bucket, key, meta, true); - - impl.delete(bucket, key); - verify(impl).delete(bucket, key, null); - - when(riakWalkSpec.toString()).thenReturn(walkSpec); - impl.walk(bucket, key, walkSpec); - impl.walk(bucket, key, riakWalkSpec); - verify(impl, times(2)).walk(bucket, key, walkSpec, null); - - impl.mapReduce(mrJob); - verify(impl).mapReduce(mrJob, null); - } - - @Test public void method_defers_to_helper_on_expection() throws JSONException, IOException { - impl = spy(impl); - - doThrow(new JSONException("")).when(impl).getBucketResponse(any(HttpResponse.class)); - doThrow(new RiakResponseRuntimeException(null)).when(impl).getFetchResponse(any(HttpResponse.class)); - doThrow(new RiakResponseRuntimeException(null)).when(impl).getWalkResponse(any(HttpResponse.class)); - doThrow(new JSONException("")).when(impl).getMapReduceResponse(any(HttpResponse.class)); - - impl.getBucketSchema(bucket, meta); - verify(mockHelper).toss(any(RiakResponseRuntimeException.class)); - reset(mockHelper); - - impl.listBucket(bucket, meta); - verify(mockHelper).toss(any(RiakResponseRuntimeException.class)); - reset(mockHelper); - - impl.streamBucket(bucket, meta); - verify(mockHelper).toss(any(RiakResponseRuntimeException.class)); - reset(mockHelper); - - impl.fetchMeta(bucket, key, meta); - verify(mockHelper).toss(any(RiakResponseRuntimeException.class)); - reset(mockHelper); - - impl.fetch(bucket, key, meta); - verify(mockHelper).toss(any(RiakResponseRuntimeException.class)); - reset(mockHelper); - - impl.stream(bucket, key, meta); - verify(mockHelper).toss(any(RiakResponseRuntimeException.class)); - reset(mockHelper); - - impl.walk(bucket, key, "", meta); - verify(mockHelper).toss(any(RiakResponseRuntimeException.class)); - reset(mockHelper); - - impl.mapReduce(mrJob, meta); - verify(mockHelper).toss(any(RiakResponseRuntimeException.class)); - reset(mockHelper); - } - - @Test public void setBucketSchema_puts_schema_in_props_field() { - final JSONObject mockJSONObject = mock(JSONObject.class); - - when(mockHelper.setBucketSchema(eq(bucket), any(JSONObject.class), same(meta))).thenAnswer(new Answer() { - public HttpResponse answer(InvocationOnMock invocation) throws Throwable { - assertSame(mockJSONObject, - ((JSONObject) invocation.getArguments()[1]) // second argument is "schema" - .getJSONObject(Constants.FL_SCHEMA)); - return null; - } - }); - when(bucketInfo.getSchema()).thenReturn(mockJSONObject); - - impl.setBucketSchema(bucket, bucketInfo, meta); - - // since we need to peer into the internals of the JSONObject created by setBucketSchema(), - // verification is done in the Answer impl above. - } - - @Test public void fetch_adds_accept_header_for_multipart_content() { - final RequestMeta meta = new RequestMeta(); - impl.fetch(bucket, key, meta, false); - assertTrue(meta.getHeader("accept").contains("multipart/mixed")); - - meta.setHeader("accept", "text/plain"); - impl.fetch(bucket, key, meta, false); - assertTrue(meta.getHeader("accept").contains("text/plain")); - assertTrue(meta.getHeader("accept").contains("multipart/mixed")); - } - - @Test public void store_adds_accept_header_for_multipart_content() { - final RequestMeta meta = new RequestMeta(); - RiakObject o = new RiakObject(bucket, key); - impl.store(o, meta); - assertTrue(meta.getHeader("accept").contains("multipart/mixed")); - - meta.setHeader("accept", "text/plain"); - impl.fetch(bucket, key, meta, false); - assertTrue(meta.getHeader("accept").contains("text/plain")); - assertTrue(meta.getHeader("accept").contains("multipart/mixed")); - } - - @Test public void fetch_meta_asks_fetchresponse_to_associate_new_objects_with_client() { - when(mockHelper.fetchMeta(anyString(), anyString(), any(RequestMeta.class))).thenReturn(mockHttpResponse); - when(mockHttpResponse.isSuccess()).thenReturn(true); - - FetchResponse r = impl.fetchMeta(bucket, key); - - assertSame(impl, r.getObject().getRiakClient()); - } - - @Test public void fetch_asks_fetchresponse_to_associate_new_objects_with_client() { - when(mockHelper.fetch(anyString(), anyString(), any(RequestMeta.class), anyBoolean())).thenReturn(mockHttpResponse); - when(mockHttpResponse.isSuccess()).thenReturn(true); - - FetchResponse r = impl.fetch(bucket, key); - - assertSame(impl, r.getObject().getRiakClient()); - } - - @Test public void stream_asks_fetchresponse_to_associate_new_objects_with_client() { - when(mockHelper.fetch(anyString(), anyString(), any(RequestMeta.class), anyBoolean())).thenReturn(mockHttpResponse); - when(mockHttpResponse.isSuccess()).thenReturn(true); - - FetchResponse r = impl.stream(bucket, key); - - assertSame(impl, r.getObject().getRiakClient()); - } - - @Test public void walk_asks_fetchresponse_to_associate_new_objects_with_client() { - @SuppressWarnings("serial") final Map HEADERS = new HashMap() {{ - put("Content-Type".toLowerCase(), "multipart/mixed; boundary=BCVLGEKnH0gY7KsH5nW3xnzhYbU"); - }}; - final String BODY = "\r\n" + "--BCVLGEKnH0gY7KsH5nW3xnzhYbU\r\n" - + "Content-Type: multipart/mixed; boundary=7Ymillu08Tqzwb9Cm6Bs8OewFd5\r\n" - + "\r\n" - + "--7Ymillu08Tqzwb9Cm6Bs8OewFd5\r\n" - + "X-Riak-Vclock: vclock\r\n" - + "Location: /riak/b/k1\r\n" - + "\r\n" - + "foo\r\n" - + "--7Ymillu08Tqzwb9Cm6Bs8OewFd5--\r\n" - + "\r\n" - + "--BCVLGEKnH0gY7KsH5nW3xnzhYbU--\r\n"; - - when(mockHelper.walk(anyString(), anyString(), anyString(), any(RequestMeta.class))).thenReturn(mockHttpResponse); - when(mockHttpResponse.getHttpHeaders()).thenReturn(HEADERS); - when(mockHttpResponse.getBody()).thenReturn(BODY.getBytes()); - when(mockHttpResponse.getBodyAsString()).thenReturn(BODY); - when(mockHttpResponse.isSuccess()).thenReturn(true); - - WalkResponse r = impl.walk(bucket, key, walkSpec); - - assertSame(impl, r.getSteps().get(0).get(0).getRiakClient()); - } -} \ No newline at end of file diff --git a/src/test/java/com/basho/riak/client/TestRiakConfig.java b/src/test/java/com/basho/riak/client/TestRiakConfig.java deleted file mode 100644 index 5516504d1..000000000 --- a/src/test/java/com/basho/riak/client/TestRiakConfig.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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; - -import static org.junit.Assert.*; - -import org.junit.Test; - -public class TestRiakConfig { - - @Test public void chomps_ending_slash() { - final String URL = "url"; - RiakConfig impl = new RiakConfig(URL + "/"); - assertEquals(URL, impl.getUrl()); - - impl.setUrl(URL + "/"); - assertEquals(URL, impl.getUrl()); - - impl = new RiakConfig("ip", "port", URL + "/"); - assertFalse(impl.getUrl().endsWith("/")); - } - - @Test public void builds_correct_url_from_ip_port_and_prefix() { - RiakConfig impl = new RiakConfig("ip", "port", "/prefix"); - assertEquals("http://ip:port/prefix", impl.getUrl()); - } - - @Test public void calculates_correct_base_url() { - RiakConfig impl = new RiakConfig("http://ip:port/path/to/riak"); - assertEquals("http://ip:port", impl.getBaseUrl()); - - impl = new RiakConfig("http://ip:port/prefix"); - assertEquals("http://ip:port", impl.getBaseUrl()); - - impl = new RiakConfig("http://ip:port"); - assertEquals("http://ip:port", impl.getBaseUrl()); - - impl = new RiakConfig("http://ip"); - assertEquals("http://ip", impl.getBaseUrl()); - - impl = new RiakConfig("ip:port/prefix"); - assertEquals("ip:port", impl.getBaseUrl()); - - impl = new RiakConfig("ip/prefix"); - assertEquals("ip", impl.getBaseUrl()); - - impl = new RiakConfig("ip"); - assertEquals("ip", impl.getBaseUrl()); - } - - @Test public void calculates_correct_base_mapred_url() { - RiakConfig impl = new RiakConfig("http://ip:port/path/to/riak"); - assertEquals("http://ip:port/mapred", impl.getMapReduceUrl()); - - impl.setMapReducePath("/path/to/mapred/"); - assertEquals("http://ip:port/path/to/mapred", impl.getMapReduceUrl()); - } -} diff --git a/src/test/java/com/basho/riak/client/TestRiakLink.java b/src/test/java/com/basho/riak/client/TestRiakLink.java deleted file mode 100644 index 498d70089..000000000 --- a/src/test/java/com/basho/riak/client/TestRiakLink.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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; - -import static org.junit.Assert.*; - -import org.junit.Test; - -public class TestRiakLink { - - @Test public void constructor_args_persisted() { - RiakLink link = new RiakLink("bucket", "key", "tag"); - assertEquals("bucket", link.getBucket()); - assertEquals("key", link.getKey()); - assertEquals("tag", link.getTag()); - } - - @Test public void equals_handles_null_values() { - RiakLink link1 = new RiakLink("bucket", "key", "tag"); - RiakLink link2 = new RiakLink(null, null, null); - assertFalse(link1.equals(link2)); - assertFalse(link2.equals(link1)); - } - - @Test public void equals_performs_equality_check_on_fields() { - RiakLink link1 = new RiakLink("bucket", "key", "tag"); - RiakLink link2 = new RiakLink("bucket", "key", "tag"); - RiakLink link3 = new RiakLink("bucket", "key", "different"); - assertTrue(link1.equals(link2)); - assertFalse(link1.equals(link3)); - } -} diff --git a/src/test/java/com/basho/riak/client/TestRiakObject.java b/src/test/java/com/basho/riak/client/TestRiakObject.java deleted file mode 100644 index f042158bd..000000000 --- a/src/test/java/com/basho/riak/client/TestRiakObject.java +++ /dev/null @@ -1,695 +0,0 @@ -/* - * 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; - -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import org.apache.commons.httpclient.methods.EntityEnclosingMethod; -import org.apache.commons.httpclient.methods.RequestEntity; -import org.junit.Before; -import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import com.basho.riak.client.request.RequestMeta; -import com.basho.riak.client.response.FetchResponse; -import com.basho.riak.client.response.HttpResponse; -import com.basho.riak.client.response.StoreResponse; -import com.basho.riak.client.response.WalkResponse; -import com.basho.riak.client.util.Constants; - -public class TestRiakObject { - - RiakObject impl; - - @Before public void setup() { - impl = new RiakObject("b", "k"); - } - - @Test public void content_type_defaults_to_octet_stream() { - assertEquals("application/octet-stream", impl.getContentType()); - } - - @Test public void links_never_null() { - impl = new RiakObject("b", "k", null, null, null, null, null, null, null); - assertNotNull(impl.getLinks()); - - impl.setLinks((List) null); - assertNotNull(impl.getLinks()); - - impl.copyData(new RiakObject(null, null)); - assertNotNull(impl.getLinks()); - } - - @Test public void usermeta_never_null() { - impl = new RiakObject("b", "k", null, null, null, null, null, null, null); - assertNotNull(impl.getUsermeta()); - - impl.setUsermeta((Map) null); - assertNotNull(impl.getUsermeta()); - - impl.copyData(new RiakObject(null, null)); - assertNotNull(impl.getUsermeta()); - } - - @Test public void copyData_does_deep_copy() { - final String value = "value"; - final InputStream valueStream = mock(InputStream.class); - final long valueStreamLength = 10; - final String ctype = "ctype"; - final List links = new ArrayList(); - final Map usermeta = new HashMap(); - usermeta.put("testKey", "testValue"); - final String vclock = "vclock"; - final String lastmod = "lastmod"; - final String vtag = "vtag"; - final RiakLink link = new RiakLink("b", "l", "t"); - links.add(link); - - RiakObject copy = new RiakObject("b", "k2", value.getBytes(), ctype, links, usermeta, vclock, lastmod, vtag); - copy.setValueStream(valueStream, valueStreamLength); - impl.copyData(copy); - - assertEquals("b", impl.getBucket()); - assertEquals("k", impl.getKey()); - assertEquals(value, impl.getValue()); - assertSame(valueStream, impl.getValueStream()); - assertEquals(valueStreamLength, impl.getValueStreamLength().longValue()); - assertEquals(ctype, impl.getContentType()); - - // links and impl links are equal - assertEquals(links.size(), impl.numLinks()); - for(RiakLink l : links) { - assertTrue(impl.hasLink(l)); - } - - // user meta are equal - assertEquals(usermeta.size(), impl.numUsermetaItems()); - - for(Map.Entry e : usermeta.entrySet()) { - assertTrue(impl.hasUsermetaItem(e.getKey())); - assertEquals(e.getValue(), impl.getUsermetaItem(e.getKey())); - } - - assertEquals(vclock, impl.getVclock()); - assertEquals(lastmod, impl.getLastmod()); - assertEquals(vtag, impl.getVtag()); - - assertNotSame(copy.getValueAsBytes(), impl.getValueAsBytes()); - - // assert that the collections *and* contents of the collections are not the same - // for the copy and the original object - assertEquals(copy.numLinks(), impl.numLinks()); - for(RiakLink l : impl.iterableLinks()) { - assertTrue(copy.hasLink(l)); - l.setKey("new Key"); - assertFalse(copy.hasLink(l)); - assertTrue(impl.hasLink(l)); - } - - assertEquals(copy.numUsermetaItems(), impl.numUsermetaItems()); - for(String key : impl.usermetaKeys()) { - assertTrue(impl.hasUsermetaItem(key)); - assertTrue(copy.hasUsermetaItem(key)); - assertEquals(copy.getUsermetaItem(key), impl.getUsermetaItem(key)); - } - - impl.removeUsermetaItem("testKey"); - assertEquals(0, impl.numUsermetaItems()); - assertEquals(1, copy.numUsermetaItems()); - } - - @Test public void copyData_copies_null_data() { - final String value = "value"; - final String ctype = "ctype"; - final List links = new ArrayList(); - final Map usermeta = new HashMap(); - final String vclock = "vclock"; - final String lastmod = "lastmod"; - final String vtag = "vtag"; - final RiakLink link = new RiakLink("b", "l", "t"); - links.add(link); - - impl = new RiakObject("b", "k", value.getBytes(), ctype, links, usermeta, vclock, lastmod, vtag); - impl.copyData(new RiakObject(null, null)); - - assertEquals("b", impl.getBucket()); - assertEquals("k", impl.getKey()); - assertNull(impl.getValue()); - assertEquals(0, impl.numLinks()); - assertEquals(0, impl.numUsermetaItems()); - assertNull(impl.getVclock()); - assertNull(impl.getLastmod()); - assertNull(impl.getVtag()); - } - - @Test public void updateMeta_nulls_out_meta_when_given_null_response() { - final String vclock = "vclock"; - final String lastmod = "lastmod"; - final String vtag = "vtag"; - - impl = new RiakObject("b", "k", null, null, null, null, vclock, lastmod, vtag); - impl.updateMeta((StoreResponse) null); - - assertNull(impl.getVclock()); - assertNull(impl.getLastmod()); - assertNull(impl.getVtag()); - - impl = new RiakObject("b", "k", null, null, null, null, vclock, lastmod, vtag); - impl.updateMeta((FetchResponse) null); - - assertNull(impl.getVclock()); - assertNull(impl.getLastmod()); - assertNull(impl.getVtag()); - } - - @Test public void value_stream_is_separate_from_value() { - final String value = "value"; - final byte[] isvalue = "isbytes".getBytes(); - final InputStream is = new ByteArrayInputStream(isvalue); - - impl.setValue(value); - impl.setValueStream(is); - - assertEquals(value, impl.getValue()); - assertSame(is, impl.getValueStream()); - } - - @Test public void convenience_riak_client_methods_defer_to_riak_client() { - RiakClient mockRiakClient = mock(RiakClient.class); - RequestMeta mockRequestMeta = mock(RequestMeta.class); - StoreResponse mockStoreResponse = mock(StoreResponse.class); - FetchResponse mockFetchResponse = mock(FetchResponse.class); - HttpResponse mockHttpResponse = mock(HttpResponse.class); - WalkResponse mockWalkResponse = mock(WalkResponse.class); - - when(mockRiakClient.store(any(RiakObject.class), any(RequestMeta.class))).thenReturn(mockStoreResponse); - impl = new RiakObject(mockRiakClient, "b", "k"); - StoreResponse sr1 = impl.store(); - StoreResponse sr2 = impl.store(mockRequestMeta); - - verify(mockRiakClient).store(impl, null); - verify(mockRiakClient).store(impl, mockRequestMeta); - assertSame(mockStoreResponse, sr1); - assertSame(mockStoreResponse, sr2); - - when(mockRiakClient.fetchMeta(anyString(), anyString(), any(RequestMeta.class))).thenReturn(mockFetchResponse); - FetchResponse fr1 = impl.fetchMeta(); - FetchResponse fr2 = impl.fetchMeta(mockRequestMeta); - - verify(mockRiakClient).fetchMeta("b", "k", null); - verify(mockRiakClient).fetchMeta("b", "k", mockRequestMeta); - assertSame(mockFetchResponse, fr1); - assertSame(mockFetchResponse, fr2); - - when(mockRiakClient.fetch(anyString(), anyString(), any(RequestMeta.class))).thenReturn(mockFetchResponse); - fr1 = impl.fetch(); - fr2 = impl.fetch(mockRequestMeta); - - verify(mockRiakClient).fetch("b", "k", null); - verify(mockRiakClient).fetch("b", "k", mockRequestMeta); - assertSame(mockFetchResponse, fr1); - assertSame(mockFetchResponse, fr2); - - when(mockRiakClient.delete(anyString(), anyString(), any(RequestMeta.class))).thenReturn(mockHttpResponse); - HttpResponse hr1 = impl.delete(); - HttpResponse hr2 = impl.delete(mockRequestMeta); - - verify(mockRiakClient).delete("b", "k", null); - verify(mockRiakClient).delete("b", "k", mockRequestMeta); - assertSame(mockHttpResponse, hr1); - assertSame(mockHttpResponse, hr2); - - when(mockRiakClient.walk(anyString(), anyString(), anyString(), any(RequestMeta.class))).thenReturn(mockWalkResponse); - WalkResponse wr1 = impl.walk().run(); - WalkResponse wr2 = impl.walk().run(mockRequestMeta); - - verify(mockRiakClient).walk("b", "k", "_,_,_/", null); - verify(mockRiakClient).walk("b", "k", "_,_,_/", mockRequestMeta); - assertSame(mockWalkResponse, wr1); - assertSame(mockWalkResponse, wr2); - } - - @Test public void store_updates_meta_on_success() { - RiakClient mockRiakClient = mock(RiakClient.class); - StoreResponse mockStoreResponse = mock(StoreResponse.class); - - when(mockRiakClient.store(any(RiakObject.class), any(RequestMeta.class))).thenReturn(mockStoreResponse); - when(mockStoreResponse.isSuccess()).thenReturn(true); - impl = spy(new RiakObject(mockRiakClient, "b", "k")); - impl.store(); - - verify(impl).updateMeta(mockStoreResponse); - } - - @Test public void store_does_not_modify_meta_on_failure() { - RiakClient mockRiakClient = mock(RiakClient.class); - StoreResponse mockStoreResponse = mock(StoreResponse.class); - - when(mockRiakClient.store(any(RiakObject.class), any(RequestMeta.class))).thenReturn(mockStoreResponse); - when(mockStoreResponse.isSuccess()).thenReturn(false); - impl = spy(new RiakObject(mockRiakClient, "b", "k")); - impl.store(); - - verify(impl, never()).updateMeta(any(StoreResponse.class)); - verify(impl, never()).copyData(any(RiakObject.class)); - } - - @Test public void fetchMeta_updates_meta_on_success() { - RiakClient mockRiakClient = mock(RiakClient.class); - FetchResponse mockFetchResponse = mock(FetchResponse.class); - - when(mockRiakClient.fetchMeta(anyString(), anyString(), any(RequestMeta.class))).thenReturn(mockFetchResponse); - when(mockFetchResponse.isSuccess()).thenReturn(true); - impl = spy(new RiakObject(mockRiakClient, "b", "k")); - impl.fetchMeta(); - - verify(impl).updateMeta(mockFetchResponse); - } - - @Test public void fetchMeta_does_not_modify_meta_on_failure() { - RiakClient mockRiakClient = mock(RiakClient.class); - FetchResponse mockFetchResponse = mock(FetchResponse.class); - - when(mockRiakClient.fetchMeta(anyString(), anyString(), any(RequestMeta.class))).thenReturn(mockFetchResponse); - when(mockFetchResponse.isSuccess()).thenReturn(false); - impl = spy(new RiakObject(mockRiakClient, "b", "k")); - impl.fetchMeta(); - - verify(impl, never()).updateMeta(any(FetchResponse.class)); - verify(impl, never()).copyData(any(RiakObject.class)); - } - - @Test public void fetch_shallow_copies_object_on_success() { - RiakClient mockRiakClient = mock(RiakClient.class); - FetchResponse mockFetchResponse = mock(FetchResponse.class); - RiakObject mockRiakObject = mock(RiakObject.class); - when(mockRiakClient.fetch(anyString(), anyString(), any(RequestMeta.class))).thenReturn(mockFetchResponse); - when(mockFetchResponse.isSuccess()).thenReturn(true); - when(mockFetchResponse.getObject()).thenReturn(mockRiakObject); - impl = spy(new RiakObject(mockRiakClient, "b", "k")); - impl.fetch(); - - verify(impl).shallowCopy(mockRiakObject); - verify(mockFetchResponse).setObject(impl); - } - - @Test public void fetch_does_not_update_data_on_failure() { - RiakClient mockRiakClient = mock(RiakClient.class); - FetchResponse mockFetchResponse = mock(FetchResponse.class); - - when(mockRiakClient.fetch(anyString(), anyString(), any(RequestMeta.class))).thenReturn(mockFetchResponse); - when(mockFetchResponse.isSuccess()).thenReturn(false); - when(mockFetchResponse.getObject()).thenReturn(null); - impl = spy(new RiakObject(mockRiakClient, "b", "k")); - impl.fetch(); - - verify(impl, never()).updateMeta(any(FetchResponse.class)); - verify(impl, never()).shallowCopy(any(RiakObject.class)); - verify(impl, never()).copyData(any(RiakObject.class)); - } - - @Test public void walk_returns_one_step_correctly() { - RiakClient mockRiakClient = mock(RiakClient.class); - - impl = new RiakObject(mockRiakClient, "b", "k"); - - impl.walk().run(); - verify(mockRiakClient).walk("b", "k", "_,_,_/", null); - - impl.walk(false).run(); - verify(mockRiakClient).walk("b", "k", "_,_,0/", null); - - impl.walk("bucket").run(); - verify(mockRiakClient).walk("b", "k", "bucket,_,_/", null); - - impl.walk("bucket", false).run(); - verify(mockRiakClient).walk("b", "k", "bucket,_,0/", null); - - impl.walk("bucket", "tag").run(); - verify(mockRiakClient).walk("b", "k", "bucket,tag,_/", null); - - impl.walk("bucket", "tag", false).run(); - verify(mockRiakClient).walk("b", "k", "bucket,tag,0/", null); - } - - @Test public void walk_returns_multiple_step_correctly() { - RiakClient mockRiakClient = mock(RiakClient.class); - impl = new RiakObject(mockRiakClient, "b", "k"); - - impl.walk().walk("bucket").walk("bucket", "tag", false).run(); - - verify(mockRiakClient).walk("b", "k", "_,_,_/bucket,_,_/bucket,tag,0/", null); - } - - // The following could be combined as "convenience_riak_client_methods_throw_if_no_associated_riak_client" - @Test(expected=IllegalStateException.class) public void store_throws_if_no_associated_riak_client() { - impl.store(); - } - @Test(expected=IllegalStateException.class) public void fetchMeta_throws_if_no_associated_riak_client() { - impl.fetchMeta(); - } - @Test(expected=IllegalStateException.class) public void fetch_throws_if_no_associated_riak_client() { - impl.fetch(); - } - @Test(expected=IllegalStateException.class) public void delete_throws_if_no_associated_riak_client() { - impl.delete(); - } - @Test(expected=IllegalStateException.class) public void walk_throws_if_no_associated_riak_client() { - impl.walk().run(); - } - - @SuppressWarnings("unchecked") @Test public void write_to_http_method_gives_value_stream_priority_over_value() { - final String value = "value"; - final byte[] isvalue = "isbytes".getBytes(); - final InputStream is = new ByteArrayInputStream(isvalue); - final ByteArrayOutputStream os = new ByteArrayOutputStream(); - final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class); - - when(mockHttpMethod.getPath()).thenReturn("/path/to/object"); - doAnswer(new Answer() { - public Object answer(InvocationOnMock invocation) throws Throwable { - ((RequestEntity) invocation.getArguments()[0]).writeRequest(os); - return null; - } - }).when(mockHttpMethod).setRequestEntity(any(RequestEntity.class)); - - impl.setValue(value); - impl.setValueStream(is); - impl.writeToHttpMethod(mockHttpMethod); - - assertArrayEquals(os.toByteArray(), isvalue); - } - - @Test public void write_to_http_method_always_sets_entity_even_if_value_is_null() { - final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class); - - impl.setValue((String) null); - impl.setValueStream(null); - impl.writeToHttpMethod(mockHttpMethod); - - verify(mockHttpMethod).setRequestEntity((RequestEntity) notNull()); - } - - @Test public void write_to_http_method_sets_link_header() { - final RiakLink link = new RiakLink("b", "l", "t"); - final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class); - - when(mockHttpMethod.getPath()).thenReturn("/riak/b/k"); - - impl.addLink(link); - impl.writeToHttpMethod(mockHttpMethod); - - verify(mockHttpMethod).addRequestHeader(eq(Constants.HDR_LINK), contains("; riaktag=\"t\"")); - } - - @Test public void write_to_http_method_doesnt_sets_link_header_if_no_links() { - final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class); - - impl.writeToHttpMethod(mockHttpMethod); - - verify(mockHttpMethod, never()).addRequestHeader(eq(Constants.HDR_LINK), anyString()); - } - - @Test public void write_to_http_method_sets_user_meta_headers() { - final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class); - - impl.addUsermetaItem("k", "v"); - impl.writeToHttpMethod(mockHttpMethod); - - verify(mockHttpMethod).setRequestHeader(Constants.HDR_USERMETA_REQ_PREFIX + "k", "v"); - } - - @Test public void write_to_http_method_doesnt_sets_user_meta_headers_if_no_usermeta() { - final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class); - - impl.writeToHttpMethod(mockHttpMethod); - - verify(mockHttpMethod, never()).setRequestHeader(contains(Constants.HDR_USERMETA_REQ_PREFIX), anyString()); - } - - @Test public void write_to_http_method_sets_vclock() { - final String vclock = "vclock"; - final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class); - - impl = new RiakObject("b", "k", null, null, null, null, vclock, null, null); - impl.writeToHttpMethod(mockHttpMethod); - - verify(mockHttpMethod).setRequestHeader(Constants.HDR_VCLOCK, vclock); - } - - @Test public void get_base_path_finds_empty_base_path() { - final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class); - - when(mockHttpMethod.getPath()).thenReturn(null); - assertEquals("", impl.getBasePathFromHttpMethod(mockHttpMethod)); - - when(mockHttpMethod.getPath()).thenReturn(""); - assertEquals("", impl.getBasePathFromHttpMethod(mockHttpMethod)); - - when(mockHttpMethod.getPath()).thenReturn("/"); - assertEquals("", impl.getBasePathFromHttpMethod(mockHttpMethod)); - - when(mockHttpMethod.getPath()).thenReturn("/b"); - assertEquals("", impl.getBasePathFromHttpMethod(mockHttpMethod)); - - when(mockHttpMethod.getPath()).thenReturn("/b/k"); - assertEquals("", impl.getBasePathFromHttpMethod(mockHttpMethod)); - } - - @Test public void get_base_path_finds_one_element_base_path() { - final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class); - - when(mockHttpMethod.getPath()).thenReturn("/riak/b/k"); - assertEquals("/riak", impl.getBasePathFromHttpMethod(mockHttpMethod)); - } - - @Test public void get_base_path_finds_multiple_element_base_path() { - final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class); - - when(mockHttpMethod.getPath()).thenReturn("/path/to/riak/b/k"); - assertEquals("/path/to/riak", impl.getBasePathFromHttpMethod(mockHttpMethod)); - } - - @Test public void get_base_path_handles_trailing_slash() { - final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class); - - when(mockHttpMethod.getPath()).thenReturn("/riak/b/k/"); - assertEquals("/riak", impl.getBasePathFromHttpMethod(mockHttpMethod)); - } - - /* - * Encapsulated links - */ - - @Test public void linkEncapsulationIsAsGoodAsDirectAccess() { - final List links = Arrays.asList(new RiakLink("b", "l", "t"), new RiakLink("b", "e", "c"), - new RiakLink("g", "c", "s"), new RiakLink("q", "p", "c")); - - final RiakObject riakObject = new RiakObject("b", "k", "v".getBytes(), "", links, (Map) null, "", "", ""); - - assertEquals(links.size(), riakObject.numLinks()); - assertTrue(riakObject.hasLinks()); - - for(RiakLink link : riakObject.iterableLinks()) { - assertTrue(links.contains(link)); - } - - //updates on iterator fail - try { - riakObject.iterableLinks().iterator().remove(); - fail("Expected UnsupportedOperationException"); - } catch(UnsupportedOperationException e) { - //NO-OP - } - - //updates on collection succeed - final RiakLink addedLink = new RiakLink("s", "e", "c"); - riakObject.getLinks().add(addedLink); - assertTrue(linkPresent(riakObject, addedLink)); - - riakObject.getLinks().remove(addedLink); - assertFalse(linkPresent(riakObject, addedLink)); - - //updates from encapsulation API succeed - riakObject.addLink(addedLink); - assertTrue(linkPresent(riakObject, addedLink)); - - riakObject.removeLink(addedLink); - assertFalse(linkPresent(riakObject, addedLink)); - - riakObject.addLink(addedLink); - assertTrue(riakObject.hasLink(addedLink)); - - riakObject.removeLink(addedLink); - assertFalse(riakObject.hasLink(addedLink)); - } - - /** - * Checks if the riakObject's collection of RiakLinks contains the passed RiakLink. - * @param riakObject the RiakObject to test - * @param link the RiakLink to test - * @return true if the RiakObject's collection of links contains the RiakLink. - */ - private boolean linkPresent(final RiakObject riakObject, final RiakLink link) { - boolean linkPresent = false; - - for (RiakLink l : riakObject.iterableLinks()) { - if (l.equals(link)) { - linkPresent = true; - } - } - return linkPresent; - } - - /* - * Encapsulated user meta - */ - @Test public void userMetaEncapsulationIsAsGoodAsDirectAccess() { - final Map userMeta = new HashMap(); - userMeta.put("acl", "admin"); - userMeta.put("my-meta", "my-value"); - - final RiakObject riakObject = new RiakObject("b", "k", "v".getBytes(), "", null, userMeta, "", "", ""); - - assertTrue(riakObject.hasUsermeta()); - assertTrue(riakObject.hasUsermetaItem("acl")); - assertTrue(riakObject.hasUsermetaItem("my-meta")); - - userMeta.clear(); - - assertTrue(riakObject.hasUsermetaItem("acl")); - assertTrue(riakObject.hasUsermetaItem("my-meta")); - - riakObject.addUsermetaItem("my-meta2", "my-value2"); - - assertTrue(riakObject.hasUsermetaItem("my-meta2")); - assertEquals("my-value2", riakObject.getUsermetaItem("my-meta2")); - - riakObject.removeUsermetaItem("acl"); - - assertFalse(riakObject.hasUsermetaItem("acl")); - - final Map leakedUserMeta = riakObject.getUsermeta(); - - assertEquals("my-value2", leakedUserMeta.get("my-meta2")); - assertEquals("my-value", leakedUserMeta.get("my-meta")); - - // still writes through - leakedUserMeta.put("my-meta3", "my-value3"); - assertTrue(riakObject.hasUsermetaItem("my-meta3")); - assertEquals("my-value3", riakObject.getUsermetaItem("my-meta3")); - } - - @Test public void modifyLinksAndWriteToMethodConcurrently() throws InterruptedException { - final RiakObject riakObject = new RiakObject("b", "k", "v".getBytes()); - final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class); - - when(mockHttpMethod.getPath()).thenReturn("/riak/b/k"); - - final int cnt = 20; - Thread[] threads = new Thread[cnt]; - - for (int i = 0; i < cnt; i++) { - threads[i] = new Thread(new Runnable() { - - public void run() { - String bucket = UUID.randomUUID().toString(); - String key = UUID.randomUUID().toString(); - String tag = UUID.randomUUID().toString(); - int cnt = 0; - while (true) { - riakObject.addLink(new RiakLink(bucket + cnt, key + cnt, tag + cnt)); - cnt++; - try { - Thread.sleep(10); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - } - }); - threads[i].setDaemon(true); - threads[i].start(); - } - - Thread.sleep(500); - - riakObject.writeToHttpMethod(mockHttpMethod); - } - - @Test public void modifyUserMetaAndWriteToMethodConcurrently() throws InterruptedException { - final RiakObject riakObject = new RiakObject("b", "k", "v".getBytes()); - final EntityEnclosingMethod mockHttpMethod = mock(EntityEnclosingMethod.class); - - when(mockHttpMethod.getPath()).thenReturn("/riak/b/k"); - - final int cnt = 20; - Thread[] threads = new Thread[cnt]; - - for (int i = 0; i < cnt; i++) { - threads[i] = new Thread(new Runnable() { - - public void run() { - String key = UUID.randomUUID().toString(); - String tag = UUID.randomUUID().toString(); - int cnt = 0; - while (true) { - riakObject.addUsermetaItem(key + cnt, tag + cnt); - cnt++; - try { - Thread.sleep(10); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - } - }); - threads[i].setDaemon(true); - threads[i].start(); - } - - Thread.sleep(500); - - riakObject.writeToHttpMethod(mockHttpMethod); - } - - @Test public void valueByteArraySafelyEncapsulated() { - final byte[] value = "vvvv".getBytes(); - final RiakObject riakObject = new RiakObject("b", "k", value); - - assertArrayEquals("vvvv".getBytes(), riakObject.getValueAsBytes()); - value[0] = 'b'; - assertArrayEquals("vvvv".getBytes(), riakObject.getValueAsBytes()); - - byte[] roInternalValue = riakObject.getValueAsBytes(); - roInternalValue[0] = 'z'; - - assertArrayEquals("vvvv".getBytes(), riakObject.getValueAsBytes()); - } -} diff --git a/src/test/java/com/basho/riak/client/api/cap/ConflictResolverFactoryTest.java b/src/test/java/com/basho/riak/client/api/cap/ConflictResolverFactoryTest.java new file mode 100644 index 000000000..8891a3e04 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/cap/ConflictResolverFactoryTest.java @@ -0,0 +1,89 @@ +/* + * 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.cap; + +import com.basho.riak.client.api.cap.UnresolvedConflictException; +import com.basho.riak.client.api.cap.DefaultResolver; +import com.basho.riak.client.api.cap.ConflictResolverFactory; +import com.basho.riak.client.api.cap.ConflictResolver; +import java.util.Arrays; +import java.util.List; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class ConflictResolverFactoryTest +{ + @Before + public void setUp() + { + ConflictResolverFactory factory = ConflictResolverFactory.getInstance(); + factory.unregisterConflictResolver(Pojo.class); + } + + @Test + public void getDefaultResolver() throws UnresolvedConflictException + { + ConflictResolverFactory factory = ConflictResolverFactory.getInstance(); + ConflictResolver resolver = factory.getConflictResolver(Pojo.class); + + assertTrue(resolver instanceof DefaultResolver); + + resolver.resolve(Arrays.asList(new Pojo())); + } + + @Test + public void registerResolverClass() throws UnresolvedConflictException + { + ConflictResolverFactory factory = ConflictResolverFactory.getInstance(); + MyResolver resolver = new MyResolver(); + factory.registerConflictResolver(Pojo.class, resolver); + + ConflictResolver resolver2 = factory.getConflictResolver(Pojo.class); + + assertTrue(resolver2 instanceof MyResolver); + assertEquals(resolver, resolver2); + + } + + + + + public static class Pojo + { + public Pojo(){} + + String foo; + int bar; + } + + public static class MyResolver implements ConflictResolver + { + + @Override + public Pojo resolve(List objectList) throws UnresolvedConflictException + { + return objectList.get(0); + } + + } + +} diff --git a/src/test/java/com/basho/riak/client/api/commands/DeleteValueTest.java b/src/test/java/com/basho/riak/client/api/commands/DeleteValueTest.java new file mode 100644 index 000000000..e19034a7b --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/DeleteValueTest.java @@ -0,0 +1,107 @@ +/* + * 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; + +import com.basho.riak.client.api.commands.kv.DeleteValue; +import com.basho.riak.client.api.commands.kv.DeleteValue.Option; +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.api.cap.BasicVClock; +import com.basho.riak.client.api.cap.Quorum; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.DeleteOperation; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.protobuf.RiakKvPB; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.internal.util.reflection.Whitebox; + +import java.util.concurrent.TimeUnit; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; +import static org.mockito.Mockito.*; + +public class DeleteValueTest +{ + + @Mock RiakCluster mockCluster; + @Mock RiakFuture mockFuture; + VClock vClock = new BasicVClock(new byte[]{'1'}); + Location key = new Location(new Namespace("type","bucket"), "key"); + RiakClient client; + + @Before + @SuppressWarnings("unchecked") + public void init() throws Exception + { + MockitoAnnotations.initMocks(this); + when(mockFuture.get()).thenReturn(null); + when(mockFuture.get(anyLong(), any(TimeUnit.class))).thenReturn(null); + when(mockFuture.isCancelled()).thenReturn(false); + when(mockFuture.isDone()).thenReturn(true); + when(mockFuture.isSuccess()).thenReturn(true); + when(mockCluster.execute(any(FutureOperation.class))).thenReturn(mockFuture); + client = new RiakClient(mockCluster); + } + + @Test + public void testDelete() throws Exception + { + DeleteValue.Builder delete = new DeleteValue.Builder(key) + .withVClock(vClock) + .withOption(Option.DW, new Quorum(1)) + .withOption(Option.N_VAL, 1) + .withOption(Option.PR, new Quorum(1)) + .withOption(Option.PW, new Quorum(1)) + .withOption(Option.R, new Quorum(1)) + .withOption(Option.RW, new Quorum(1)) + .withOption(Option.DW, new Quorum(1)) + .withOption(Option.SLOPPY_QUORUM, true) + .withOption(Option.TIMEOUT, 100) + .withOption(Option.W, new Quorum(1)); + + + client.execute(delete.build()); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(DeleteOperation.class); + verify(mockCluster).execute(captor.capture()); + + DeleteOperation operation = captor.getValue(); + RiakKvPB.RpbDelReq.Builder builder = + (RiakKvPB.RpbDelReq.Builder) Whitebox.getInternalState(operation, "reqBuilder"); + + assertTrue(builder.hasVclock()); + assertEquals(1, builder.getDw()); + assertEquals(1, builder.getNVal()); + assertEquals(1, builder.getPr()); + assertEquals(1, builder.getPw()); + assertEquals(1, builder.getR()); + assertEquals(1, builder.getRw()); + assertEquals(1, builder.getDw()); + assertEquals(true, builder.getSloppyQuorum()); + assertEquals(100, builder.getTimeout()); + assertEquals(1, builder.getW()); + + } +} diff --git a/src/test/java/com/basho/riak/client/api/commands/FetchDatatypeTest.java b/src/test/java/com/basho/riak/client/api/commands/FetchDatatypeTest.java new file mode 100644 index 000000000..354883e81 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/FetchDatatypeTest.java @@ -0,0 +1,110 @@ +/* + * 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; + +import com.basho.riak.client.api.commands.datatypes.FetchMap; +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.api.cap.Quorum; +import com.basho.riak.client.core.FutureOperation; +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.api.commands.datatypes.FetchDatatype.Option; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.crdt.types.RiakMap; +import com.basho.riak.client.core.util.BinaryValue; +import com.basho.riak.protobuf.RiakDtPB; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.internal.util.reflection.Whitebox; + +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; + +import static junit.framework.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class FetchDatatypeTest +{ + + @Mock RiakCluster mockCluster; + @Mock RiakFuture mockFuture; + @Mock DtFetchOperation.Response mockResponse; + Location key = new Location(new Namespace("type", "bucket"), "key"); + RiakClient client; + + @Before + @SuppressWarnings("unchecked") + public void init() throws Exception + { + MockitoAnnotations.initMocks(this); + when(mockResponse.getCrdtElement()).thenReturn(new RiakMap(new ArrayList())); + when(mockResponse.getContext()).thenReturn(BinaryValue.create(new byte[]{'1'})); + when(mockFuture.get()).thenReturn(mockResponse); + when(mockFuture.get(anyLong(), any(TimeUnit.class))).thenReturn(mockResponse); + when(mockFuture.isCancelled()).thenReturn(false); + when(mockFuture.isDone()).thenReturn(true); + when(mockFuture.isSuccess()).thenReturn(true); + when(mockCluster.execute(any(FutureOperation.class))).thenReturn(mockFuture); + client = new RiakClient(mockCluster); + } + + @Test + public void testFetch() throws Exception + { + + FetchMap fetchValue = new FetchMap.Builder(key) + .withOption(Option.TIMEOUT, 100) + .withOption(Option.BASIC_QUORUM, true) + .withOption(Option.N_VAL, 1) + .withOption(Option.NOTFOUND_OK, true) + .withOption(Option.PR, new Quorum(1)) + .withOption(Option.R, new Quorum(1)) + .withOption(Option.SLOPPY_QUORUM, true) + .withOption(Option.INCLUDE_CONTEXT, true) + .build(); + + client.execute(fetchValue); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(DtFetchOperation.class); + verify(mockCluster).execute(captor.capture()); + + DtFetchOperation operation = captor.getValue(); + RiakDtPB.DtFetchReq.Builder builder = + (RiakDtPB.DtFetchReq.Builder) Whitebox.getInternalState(operation, "reqBuilder"); + + assertEquals("type", builder.getType().toStringUtf8()); + assertEquals("bucket", builder.getBucket().toStringUtf8()); + assertEquals("key", builder.getKey().toStringUtf8()); + assertEquals(100, builder.getTimeout()); + assertEquals(true, builder.getBasicQuorum()); + assertEquals(1, builder.getNVal()); + assertEquals(true, builder.getNotfoundOk()); + assertEquals(1, builder.getPr()); + assertEquals(1, builder.getR()); + assertEquals(true, builder.getSloppyQuorum()); + } + + +} diff --git a/src/test/java/com/basho/riak/client/api/commands/FetchValueTest.java b/src/test/java/com/basho/riak/client/api/commands/FetchValueTest.java new file mode 100644 index 000000000..cc824e45f --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/FetchValueTest.java @@ -0,0 +1,112 @@ +/* + * 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; + +import com.basho.riak.client.api.commands.kv.FetchValue.Option; +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.api.commands.kv.FetchValue; +import com.basho.riak.client.api.cap.BasicVClock; +import com.basho.riak.client.api.cap.Quorum; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.FetchOperation; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.protobuf.RiakKvPB; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.internal.util.reflection.Whitebox; + +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; + +import static junit.framework.Assert.assertEquals; +import org.junit.Ignore; +import static org.mockito.Mockito.*; + +// TODO: Do something with this. You can't mock the responses because the parents aren't public + +@Ignore +public class FetchValueTest +{ + @Mock RiakCluster mockCluster; + @Mock RiakFuture mockFuture; + @Mock FetchOperation.Response mockResponse; + VClock vClock = new BasicVClock(new byte[]{'1'}); + Location key = new Location(new Namespace("type","bucket"), "key"); + RiakClient client; + + @Before + @SuppressWarnings("unchecked") + public void init() throws Exception + { + MockitoAnnotations.initMocks(this); + when(mockResponse.getObjectList()).thenReturn(new ArrayList()); + when(mockFuture.get()).thenReturn(mockResponse); + when(mockFuture.get(anyLong(), any(TimeUnit.class))).thenReturn(mockResponse); + when(mockFuture.isCancelled()).thenReturn(false); + when(mockFuture.isDone()).thenReturn(true); + when(mockCluster.execute(any(FutureOperation.class))).thenReturn(mockFuture); + client = new RiakClient(mockCluster); + } + + @Test + public void testFetch() throws Exception + { + + FetchValue.Builder fetchValue = new FetchValue.Builder(key) + .withOption(Option.TIMEOUT, 100) + .withOption(Option.BASIC_QUORUM, true) + .withOption(Option.DELETED_VCLOCK, true) + .withOption(Option.HEAD, true) + .withOption(Option.IF_MODIFIED, vClock) + .withOption(Option.N_VAL, 1) + .withOption(Option.NOTFOUND_OK, true) + .withOption(Option.PR, new Quorum(1)) + .withOption(Option.R, new Quorum(1)) + .withOption(Option.SLOPPY_QUORUM, true); + + client.execute(fetchValue.build()); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(FetchOperation.class); + verify(mockCluster).execute(captor.capture()); + + FetchOperation operation = captor.getValue(); + RiakKvPB.RpbGetReq.Builder builder = + (RiakKvPB.RpbGetReq.Builder) Whitebox.getInternalState(operation, "reqBuilder"); + + assertEquals("type", builder.getType().toStringUtf8()); + assertEquals("bucket", builder.getBucket().toStringUtf8()); + assertEquals("key", builder.getKey().toStringUtf8()); + assertEquals(100, builder.getTimeout()); + assertEquals(true, builder.getBasicQuorum()); + assertEquals(true, builder.getDeletedvclock()); + assertEquals(true, builder.getHead()); + assertEquals(1, builder.getNVal()); + assertEquals(true, builder.getNotfoundOk()); + assertEquals(1, builder.getPr()); + assertEquals(1, builder.getR()); + assertEquals(true, builder.getSloppyQuorum()); + } + +} diff --git a/src/test/java/com/basho/riak/client/api/commands/ImmediateRiakFuture.java b/src/test/java/com/basho/riak/client/api/commands/ImmediateRiakFuture.java new file mode 100644 index 000000000..f431763c1 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/ImmediateRiakFuture.java @@ -0,0 +1,110 @@ +/* + * 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; + +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.RiakFutureListener; + +import java.util.concurrent.TimeUnit; + +class ImmediateRiakFuture implements RiakFuture +{ + + private final V value; + + public ImmediateRiakFuture(V value) + { + this.value = value; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) + { + return false; + } + + @Override + public V get() throws InterruptedException + { + return value; + } + + @Override + public V get(long timeout, TimeUnit unit) throws InterruptedException + { + return value; + } + + @Override + public V getNow() + { + return value; + } + + @Override + public boolean isCancelled() + { + return false; + } + + @Override + public boolean isDone() + { + return true; + } + + @Override + public void addListener(RiakFutureListener listener) + { + listener.handle(this); + } + + @Override + public void removeListener(RiakFutureListener listener) + { + //no-op + } + + @Override + public void await() throws InterruptedException + { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public void await(long timeout, TimeUnit unit) throws InterruptedException + { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public boolean isSuccess() + { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Throwable cause() + { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public S getQueryInfo() + { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } +} diff --git a/src/test/java/com/basho/riak/client/api/commands/StoreValueTest.java b/src/test/java/com/basho/riak/client/api/commands/StoreValueTest.java new file mode 100644 index 000000000..8e7d7a7c4 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/StoreValueTest.java @@ -0,0 +1,128 @@ +/* + * 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; + +import com.basho.riak.client.api.commands.kv.StoreValue.Option; +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.api.commands.kv.StoreValue; +import com.basho.riak.client.api.cap.BasicVClock; +import com.basho.riak.client.api.cap.Quorum; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.StoreOperation; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; +import com.basho.riak.protobuf.RiakKvPB; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.internal.util.reflection.Whitebox; + +import java.util.ArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; +import org.junit.Ignore; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +// TODO: Do something with this. You can't mock the responses because the parents aren't public + +@Ignore +public class StoreValueTest +{ + + + @Mock RiakCluster mockCluster; + @Mock RiakFuture mockFuture; + @Mock StoreOperation.Response mockResponse; + VClock vClock = new BasicVClock(new byte[]{'1'}); + Location key = new Location(new Namespace("type", "bucket"), "key"); + RiakClient client; + RiakObject riakObject; + + @Before + @SuppressWarnings("unchecked") + public void init() throws Exception + { + MockitoAnnotations.initMocks(this); + when(mockResponse.getObjectList()).thenReturn(new ArrayList()); + when(mockFuture.get()).thenReturn(mockResponse); + when(mockFuture.get(anyLong(), any(TimeUnit.class))).thenReturn(mockResponse); + when(mockFuture.isCancelled()).thenReturn(false); + when(mockFuture.isDone()).thenReturn(true); + when(mockCluster.execute(any(FutureOperation.class))).thenReturn(mockFuture); + client = new RiakClient(mockCluster); + riakObject = new RiakObject(); + riakObject.setVClock(vClock); + riakObject.setValue(BinaryValue.create(new byte[]{'O', '_', 'o'})); + } + + @Test + public void testStore() throws ExecutionException, InterruptedException + { + + StoreValue.Builder store = + new StoreValue.Builder(riakObject).withLocation(key) + .withOption(Option.ASIS, true) + .withOption(Option.DW, new Quorum(1)) + .withOption(Option.IF_NONE_MATCH, true) + .withOption(Option.IF_NOT_MODIFIED, true) + .withOption(Option.PW, new Quorum(1)) + .withOption(Option.N_VAL, 1) + .withOption(Option.RETURN_BODY, true) + .withOption(Option.RETURN_HEAD, true) + .withOption(Option.SLOPPY_QUORUM, true) + .withOption(Option.TIMEOUT, 1000) + .withOption(Option.W, new Quorum(1)); + + client.execute(store.build()); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(StoreOperation.class); + verify(mockCluster).execute(captor.capture()); + + StoreOperation operation = captor.getValue(); + RiakKvPB.RpbPutReq.Builder builder = + (RiakKvPB.RpbPutReq.Builder) Whitebox.getInternalState(operation, "reqBuilder"); + + assertTrue(builder.hasVclock()); + assertEquals(true, builder.getAsis()); + assertEquals(1, builder.getDw()); + assertEquals(true, builder.getIfNotModified()); + assertEquals(true, builder.getIfNoneMatch()); + assertEquals(1, builder.getPw()); + assertEquals(1, builder.getNVal()); + assertEquals(true, builder.getReturnBody()); + assertEquals(true, builder.getReturnHead()); + assertEquals(true, builder.getSloppyQuorum()); + assertEquals(1000, builder.getTimeout()); + assertEquals(1, builder.getW()); + + } + + +} diff --git a/src/test/java/com/basho/riak/client/api/commands/UpdateDatatypeTest.java b/src/test/java/com/basho/riak/client/api/commands/UpdateDatatypeTest.java new file mode 100644 index 000000000..195308f43 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/UpdateDatatypeTest.java @@ -0,0 +1,112 @@ +/* + * 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; + +import com.basho.riak.client.api.commands.datatypes.UpdateMap; +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.api.cap.Quorum; +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.DtUpdateOperation; +import com.basho.riak.client.api.commands.datatypes.UpdateDatatype.Option; +import com.basho.riak.client.api.commands.datatypes.Context; +import com.basho.riak.client.api.commands.datatypes.MapUpdate; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.crdt.types.RiakMap; +import com.basho.riak.client.core.util.BinaryValue; +import com.basho.riak.protobuf.RiakDtPB; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.internal.util.reflection.Whitebox; + +import java.util.ArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import static junit.framework.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class UpdateDatatypeTest +{ + @Mock RiakCluster mockCluster; + @Mock RiakFuture mockFuture; + @Mock DtUpdateOperation.Response mockResponse; + @Mock Context context; + RiakClient client; + Location key = new Location(new Namespace("type","bucket"), "key"); + + @Before + @SuppressWarnings("unchecked") + public void init() throws Exception + { + MockitoAnnotations.initMocks(this); + when(mockResponse.getCrdtElement()).thenReturn(new RiakMap(new ArrayList())); + when(mockResponse.getContext()).thenReturn(BinaryValue.create(new byte[]{'1'})); + when(mockFuture.get()).thenReturn(mockResponse); + when(mockFuture.get(anyLong(), any(TimeUnit.class))).thenReturn(mockResponse); + when(mockFuture.isCancelled()).thenReturn(false); + when(mockFuture.isDone()).thenReturn(true); + when(mockFuture.isSuccess()).thenReturn(true); + when(mockCluster.execute(any(FutureOperation.class))).thenReturn(mockFuture); + when(context.getValue()).thenReturn(BinaryValue.unsafeCreate(new byte[] {'1'})); + client = new RiakClient(mockCluster); + } + + @Test + public void testStore() throws ExecutionException, InterruptedException + { + + MapUpdate update = new MapUpdate(); + + UpdateMap store = new UpdateMap.Builder(key, update) + .withContext(context) + .withOption(Option.DW, new Quorum(1)) + .withOption(Option.PW, new Quorum(1)) + .withOption(Option.N_VAL, 1) + .withOption(Option.RETURN_BODY, true) + .withOption(Option.SLOPPY_QUORUM, true) + .withOption(Option.TIMEOUT, 1000) + .withOption(Option.W, new Quorum(1)) + .build(); + + client.execute(store); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(DtUpdateOperation.class); + verify(mockCluster).execute(captor.capture()); + + DtUpdateOperation operation = captor.getValue(); + RiakDtPB.DtUpdateReq.Builder builder = + (RiakDtPB.DtUpdateReq.Builder) Whitebox.getInternalState(operation, "reqBuilder"); + + assertEquals(1, builder.getDw()); + assertEquals(1, builder.getPw()); + assertEquals(1, builder.getNVal()); + assertEquals(true, builder.getReturnBody()); + assertEquals(true, builder.getSloppyQuorum()); + assertEquals(1000, builder.getTimeout()); + assertEquals(1, builder.getW()); + + } +} diff --git a/src/test/java/com/basho/riak/client/api/commands/UpdateValueTest.java b/src/test/java/com/basho/riak/client/api/commands/UpdateValueTest.java new file mode 100644 index 000000000..0dec67bef --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/UpdateValueTest.java @@ -0,0 +1,114 @@ +/* + * 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; + +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.api.commands.kv.UpdateValue; +import com.basho.riak.client.api.cap.ConflictResolver; +import com.basho.riak.client.api.cap.ConflictResolverFactory; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.api.convert.Converter; +import com.basho.riak.client.api.convert.PassThroughConverter; +import com.basho.riak.client.core.FutureOperation; +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.operations.FetchOperation; +import com.basho.riak.client.core.operations.StoreOperation; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.concurrent.ExecutionException; +import org.junit.Ignore; + +import static org.mockito.Mockito.*; + +// TODO: Do something with this. You can't mock the responses because the parents aren't public + +@Ignore +public class UpdateValueTest +{ + @Mock RiakCluster mockCluster; + Location key = new Location(new Namespace("type", "bucket"), "key"); + RiakClient client; + RiakObject riakObject; + + @Before + @SuppressWarnings("unchecked") + public void init() throws Exception + { + MockitoAnnotations.initMocks(this); + + riakObject = new RiakObject(); + riakObject.setValue(BinaryValue.create(new byte[]{'O', '_', 'o'})); + + ArrayList objects = new ArrayList(); + objects.add(riakObject); + + FetchOperation.Response fetchResponse = mock(FetchOperation.Response.class); + when(fetchResponse.getObjectList()).thenReturn(objects); + + StoreOperation.Response storeResponse = mock(StoreOperation.Response.class); + when(storeResponse.getObjectList()).thenReturn(objects); + + when(mockCluster.execute(any(FutureOperation.class))) + .thenReturn(new ImmediateRiakFuture(fetchResponse)) + .thenReturn(new ImmediateRiakFuture(storeResponse)); + + client = new RiakClient(mockCluster); + + } + + + @Test + @SuppressWarnings("unchecked") + public void testUpdateValue() throws ExecutionException, InterruptedException + { + UpdateValue.Update spiedUpdate = spy(new NoopUpdate()); + ConflictResolver spiedResolver = + spy(ConflictResolverFactory.getInstance().getConflictResolver(RiakObject.class)); + + Converter spiedConverter = spy(new PassThroughConverter()); + + UpdateValue.Builder update = + new UpdateValue.Builder(key) + .withUpdate(spiedUpdate); + + client.execute(update.build()); + + verify(mockCluster, times(2)).execute(any(FutureOperation.class)); + verify(spiedResolver, times(1)).resolve(anyList()); + verify(spiedUpdate, times(1)).apply(any(RiakObject.class)); + verify(spiedConverter, times(1)).fromDomain(any(RiakObject.class), any(Namespace.class), any(BinaryValue.class)); + verify(spiedConverter, times(2)).toDomain(any(RiakObject.class), any(Location.class)); + + } + + private static class NoopUpdate extends UpdateValue.Update + { + @Override + public RiakObject apply(RiakObject original) + { + return original; + } + } + +} diff --git a/src/test/java/com/basho/riak/client/api/commands/indexes/SecondaryIndexQueryTest.java b/src/test/java/com/basho/riak/client/api/commands/indexes/SecondaryIndexQueryTest.java new file mode 100644 index 000000000..d08fdacdd --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/indexes/SecondaryIndexQueryTest.java @@ -0,0 +1,165 @@ +/* + * 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.commands.indexes; + +import com.basho.riak.client.core.operations.SecondaryIndexQueryOperation; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.util.BinaryValue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class SecondaryIndexQueryTest +{ + @Test + public void intIndexQueryBuildsCorrectly() + { + Namespace ns = new Namespace("bucket_type", "bucket_name"); + + IntIndexQuery iiq = + new IntIndexQuery.Builder(ns, "test_index", Long.MAX_VALUE) + .withKeyAndIndex(true) + .withContinuation(BinaryValue.create("continuation")) + .withMaxResults(Integer.MAX_VALUE) + .withPaginationSort(true) + .withTimeout(Integer.MAX_VALUE) + .build(); + + SecondaryIndexQueryOperation.Query query = iiq.createCoreQuery(); + + assertEquals(ns, query.getNamespace()); + assertEquals("test_index_int", query.getIndexName().toString()); + assertEquals(Long.MAX_VALUE, Long.valueOf(query.getIndexKey().toString()).longValue()); + assertEquals(BinaryValue.create("continuation"), query.getContinuation()); + assertEquals(Integer.MAX_VALUE, query.getMaxResults()); + assertEquals(true, query.isPaginationSort()); + assertEquals(true, query.isReturnKeyAndIndex()); + assertEquals(Integer.MAX_VALUE, query.getTimeout().intValue()); + + + iiq = new IntIndexQuery.Builder(ns, "test_index", Long.MIN_VALUE, Long.MAX_VALUE) + .withKeyAndIndex(true) + .withContinuation(BinaryValue.create("continuation")) + .withMaxResults(Integer.MAX_VALUE) + .withPaginationSort(true) + .build(); + + query = iiq.createCoreQuery(); + + assertEquals(ns, query.getNamespace()); + assertEquals("test_index_int", query.getIndexName().toString()); + assertEquals(Long.MIN_VALUE, Long.valueOf(query.getRangeStart().toString()).longValue()); + assertEquals(Long.MAX_VALUE, Long.valueOf(query.getRangeEnd().toString()).longValue()); + assertEquals(BinaryValue.create("continuation"), query.getContinuation()); + assertEquals(Integer.MAX_VALUE, query.getMaxResults()); + assertEquals(true, query.isPaginationSort()); + assertEquals(true, query.isReturnKeyAndIndex()); + + + // You can't use a term filter with an _int query + try + { + iiq = new IntIndexQuery.Builder(ns, "test_index", Long.MIN_VALUE, Long.MAX_VALUE) + .withRegexTermFilter("filter") + .build(); + + fail("Expected IllegalArgumentException"); + } + catch (IllegalArgumentException e) + {} + } + + @Test + public void binIndexQueryBuildsCorrectly() + { + Namespace ns = new Namespace("bucket_type", "bucket_name"); + + BinIndexQuery biq = new BinIndexQuery.Builder(ns, "test_index", "index_key") + .withKeyAndIndex(true) + .withMaxResults(Integer.MAX_VALUE) + .withContinuation(BinaryValue.create("continuation")) + .withPaginationSort(true) + .withRegexTermFilter("filter") + .build(); + + SecondaryIndexQueryOperation.Query query = biq.createCoreQuery(); + + assertEquals(ns, query.getNamespace()); + assertEquals("test_index_bin", query.getIndexName().toString()); + assertEquals("index_key", query.getIndexKey().toString()); + assertEquals(BinaryValue.create("continuation"), query.getContinuation()); + assertEquals(Integer.MAX_VALUE, query.getMaxResults()); + assertEquals(true, query.isPaginationSort()); + assertEquals(true, query.isReturnKeyAndIndex()); + + biq = new BinIndexQuery.Builder(ns, "test_index", "aaa", "zzz") + .withKeyAndIndex(true) + .withMaxResults(Integer.MAX_VALUE) + .withContinuation(BinaryValue.create("continuation")) + .withPaginationSort(true) + .withRegexTermFilter("filter") + .build(); + + query = biq.createCoreQuery(); + + assertEquals(ns, query.getNamespace()); + assertEquals("test_index_bin", query.getIndexName().toString()); + assertEquals("aaa", query.getRangeStart().toString()); + assertEquals("zzz", query.getRangeEnd().toString()); + assertEquals(BinaryValue.create("continuation"), query.getContinuation()); + assertEquals(Integer.MAX_VALUE, query.getMaxResults()); + assertEquals(true, query.isPaginationSort()); + assertEquals(true, query.isReturnKeyAndIndex()); + + } + + public void rawIndexQueryBuildsCorrectly() + { + Namespace ns = new Namespace("bucket_type", "bucket_name"); + + BinaryValue indexMatch = BinaryValue.create("match"); + BinaryValue indexStart = BinaryValue.create("start"); + BinaryValue indexEnd = BinaryValue.create("end"); + + RawIndexQuery riq = + new RawIndexQuery.Builder(ns, "test_index", SecondaryIndexQuery.Type._INT, indexMatch) + .build(); + + SecondaryIndexQueryOperation.Query query = riq.createCoreQuery(); + + assertEquals(ns, query.getNamespace()); + assertEquals("test_index_int", query.getIndexName().toString()); + assertEquals(indexMatch, query.getIndexKey()); + + riq = + new RawIndexQuery.Builder(ns, "test_index", SecondaryIndexQuery.Type._INT, + indexStart, indexEnd) + .build(); + + query = riq.createCoreQuery(); + + assertEquals(ns, query.getNamespace()); + assertEquals("test_index_int", query.getIndexName().toString()); + assertEquals(indexStart, query.getRangeStart()); + assertEquals(indexEnd, query.getRangeEnd()); + + } +} diff --git a/src/test/java/com/basho/riak/client/api/commands/indexes/itest/ITestBigIntIndexQuery.java b/src/test/java/com/basho/riak/client/api/commands/indexes/itest/ITestBigIntIndexQuery.java new file mode 100644 index 000000000..764f3f4fd --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/indexes/itest/ITestBigIntIndexQuery.java @@ -0,0 +1,174 @@ +/* + * 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.commands.indexes.itest; + +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.api.annotations.RiakBucketName; +import com.basho.riak.client.api.annotations.RiakIndex; +import com.basho.riak.client.api.annotations.RiakKey; +import com.basho.riak.client.api.annotations.RiakVClock; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.itest.ITestBase; +import com.basho.riak.client.api.commands.indexes.BigIntIndexQuery; +import com.basho.riak.client.api.commands.kv.StoreValue; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import java.math.BigInteger; +import java.util.concurrent.ExecutionException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Assume; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class ITestBigIntIndexQuery extends ITestBase +{ + + @Test + public void testMatchQuery() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + + RiakClient client = new RiakClient(cluster); + + IndexedPojo ip = new IndexedPojo(); + ip.key = "index_test_object_key"; + ip.bucketName = bucketName.toString(); + ip.indexKey = new BigInteger("91234567890123456789012345678901234567890"); + ip.value = "My Object Value!"; + + StoreValue sv = new StoreValue.Builder(ip).build(); + RiakFuture svFuture = client.executeAsync(sv); + + svFuture.await(); + assertTrue(svFuture.isSuccess()); + + IndexedPojo ip2 = new IndexedPojo(); + ip2.key = "index_test_object_key2"; + ip2.bucketName = bucketName.toString(); + ip2.indexKey = new BigInteger("91234567890123456789012345678901234567890"); + ip2.value = "My Object Value!"; + + sv = new StoreValue.Builder(ip2).build(); + svFuture = client.executeAsync(sv); + + svFuture.await(); + assertTrue(svFuture.isSuccess()); + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + + BigIntIndexQuery iiq = + new BigIntIndexQuery.Builder(ns, "test_index", new BigInteger("91234567890123456789012345678901234567890")) + .withKeyAndIndex(true).build(); + BigIntIndexQuery.Response iResp = client.execute(iiq); + + assertTrue(iResp.hasEntries()); + assertEquals(2, iResp.getEntries().size()); + + boolean found = false; + for (BigIntIndexQuery.Response.Entry e : iResp.getEntries()) + { + if (e.getRiakObjectLocation().getKey().toString().equals("index_test_object_key")) + { + found = true; + assertEquals(ip.indexKey, e.getIndexKey()); + } + } + + assertTrue(found); + + } + + @Test + public void testRangeQuery() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + + RiakClient client = new RiakClient(cluster); + + IndexedPojo ip = new IndexedPojo(); + ip.key = "index_test_object_key1"; + ip.bucketName = bucketName.toString(); + ip.indexKey = new BigInteger("91234567890123456789012345678901234567890"); + ip.value = "My Object Value!"; + + StoreValue sv = new StoreValue.Builder(ip).build(); + RiakFuture svFuture = client.executeAsync(sv); + + svFuture.await(); + assertTrue(svFuture.isSuccess()); + + IndexedPojo ip2 = new IndexedPojo(); + ip2.key = "index_test_object_key2"; + ip2.bucketName = bucketName.toString(); + ip2.indexKey = new BigInteger("91234567890123456789012345678901234567898"); + ip2.value = "My Object Value!"; + + sv = new StoreValue.Builder(ip2).build(); + svFuture = client.executeAsync(sv); + + svFuture.await(); + assertTrue(svFuture.isSuccess()); + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + BigIntIndexQuery iiq = + new BigIntIndexQuery.Builder(ns, "test_index", + new BigInteger("91234567890123456789012345678901234567890"), + new BigInteger("91234567890123456789012345678901234567898")) + .withKeyAndIndex(true) + .build(); + + BigIntIndexQuery.Response iResp = client.execute(iiq); + assertTrue(iResp.hasEntries()); + assertEquals(2, iResp.getEntries().size()); + + boolean found = false; + for (BigIntIndexQuery.Response.Entry e : iResp.getEntries()) + { + if (e.getRiakObjectLocation().getKey().toString().equals("index_test_object_key1")) + { + found = true; + assertEquals(ip.indexKey, e.getIndexKey()); + } + } + + assertTrue(found); + } + + + + public static class IndexedPojo + { + @RiakKey + public String key; + + @RiakBucketName + public String bucketName; + + @RiakIndex(name="test_index") + BigInteger indexKey; + + @RiakVClock + VClock vclock; + + public String value; + } +} diff --git a/src/test/java/com/basho/riak/client/api/commands/indexes/itest/ITestBinIndexQuery.java b/src/test/java/com/basho/riak/client/api/commands/indexes/itest/ITestBinIndexQuery.java new file mode 100644 index 000000000..4525fc885 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/indexes/itest/ITestBinIndexQuery.java @@ -0,0 +1,167 @@ +/* + * 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.indexes.itest; + +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.api.annotations.RiakBucketName; +import com.basho.riak.client.api.annotations.RiakIndex; +import com.basho.riak.client.api.annotations.RiakKey; +import com.basho.riak.client.api.annotations.RiakVClock; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.itest.ITestBase; +import com.basho.riak.client.api.commands.indexes.BinIndexQuery; +import com.basho.riak.client.api.commands.kv.StoreValue; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import java.util.concurrent.ExecutionException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Assume; +import org.junit.Test; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public class ITestBinIndexQuery extends ITestBase +{ + @Test + public void testMatchQuery() throws ExecutionException, InterruptedException + { + Assume.assumeTrue(test2i); + + RiakClient client = new RiakClient(cluster); + + IndexedPojo ip = new IndexedPojo(); + ip.key = "index_test_object_key"; + ip.bucketName = bucketName.toString(); + ip.indexKey = "index_test_index_key"; + ip.value = "My Object Value!"; + + StoreValue sv = new StoreValue.Builder(ip).build(); + RiakFuture svFuture = client.executeAsync(sv); + + svFuture.await(); + assertTrue(svFuture.isSuccess()); + + IndexedPojo ip2 = new IndexedPojo(); + ip2.key = "index_test_object_key2"; + ip2.bucketName = bucketName.toString(); + ip2.indexKey = "index_test_index_key"; + ip2.value = "My Object Value!"; + + sv = new StoreValue.Builder(ip2).build(); + svFuture = client.executeAsync(sv); + + svFuture.await(); + assertTrue(svFuture.isSuccess()); + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + + BinIndexQuery biq = + new BinIndexQuery.Builder(ns, "test_index", "index_test_index_key").withKeyAndIndex(true).build(); + BinIndexQuery.Response iResp = client.execute(biq); + + assertTrue(iResp.hasEntries()); + assertEquals(2, iResp.getEntries().size()); + + boolean found = false; + for (BinIndexQuery.Response.Entry e : iResp.getEntries()) + { + if (e.getRiakObjectLocation().getKey().toString().equals("index_test_object_key")) + { + found = true; + assertEquals(ip.indexKey, e.getIndexKey()); + } + } + + assertTrue(found); + + } + + @Test + public void testRangeQuery() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + + RiakClient client = new RiakClient(cluster); + + IndexedPojo ip = new IndexedPojo(); + ip.key = "index_test_object_key1"; + ip.bucketName = bucketName.toString(); + ip.indexKey = "index_test_index_key1"; + ip.value = "My Object Value!"; + + StoreValue sv = new StoreValue.Builder(ip).build(); + RiakFuture svFuture = client.executeAsync(sv); + + svFuture.await(); + assertTrue(svFuture.isSuccess()); + + IndexedPojo ip2 = new IndexedPojo(); + ip2.key = "index_test_object_key2"; + ip2.bucketName = bucketName.toString(); + ip2.indexKey = "index_test_index_key2"; + ip2.value = "My Object Value!"; + + sv = new StoreValue.Builder(ip2).build(); + svFuture = client.executeAsync(sv); + + svFuture.await(); + assertTrue(svFuture.isSuccess()); + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + BinIndexQuery biq = + new BinIndexQuery.Builder(ns, "test_index", "index_test_index_key0", "index_test_index_key9").withKeyAndIndex(true).build(); + BinIndexQuery.Response iResp = client.execute(biq); + + assertTrue(iResp.hasEntries()); + assertEquals(2, iResp.getEntries().size()); + + boolean found = false; + for (BinIndexQuery.Response.Entry e : iResp.getEntries()) + { + if (e.getRiakObjectLocation().getKey().toString().equals("index_test_object_key1")) + { + found = true; + assertEquals(ip.indexKey, e.getIndexKey()); + } + } + + assertTrue(found); + + } + + public static class IndexedPojo + { + @RiakKey + public String key; + + @RiakBucketName + public String bucketName; + + @RiakIndex(name="test_index") + String indexKey; + + @RiakVClock + VClock vclock; + + public String value; + } +} diff --git a/src/test/java/com/basho/riak/client/api/commands/indexes/itest/ITestIntIndexQuery.java b/src/test/java/com/basho/riak/client/api/commands/indexes/itest/ITestIntIndexQuery.java new file mode 100644 index 000000000..96f21dfff --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/indexes/itest/ITestIntIndexQuery.java @@ -0,0 +1,170 @@ +/* + * 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.indexes.itest; + +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.api.annotations.RiakBucketName; +import com.basho.riak.client.api.annotations.RiakIndex; +import com.basho.riak.client.api.annotations.RiakKey; +import com.basho.riak.client.api.annotations.RiakVClock; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.itest.ITestBase; +import com.basho.riak.client.api.commands.indexes.IntIndexQuery; +import com.basho.riak.client.api.commands.kv.StoreValue; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import java.util.concurrent.ExecutionException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Assume; +import org.junit.Test; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public class ITestIntIndexQuery extends ITestBase +{ + @Test + public void testMatchQuery() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + + RiakClient client = new RiakClient(cluster); + + IndexedPojo ip = new IndexedPojo(); + ip.key = "index_test_object_key"; + ip.bucketName = bucketName.toString(); + ip.indexKey = 123456L; + ip.value = "My Object Value!"; + + StoreValue sv = new StoreValue.Builder(ip).build(); + RiakFuture svFuture = client.executeAsync(sv); + + svFuture.await(); + assertTrue(svFuture.isSuccess()); + + IndexedPojo ip2 = new IndexedPojo(); + ip2.key = "index_test_object_key2"; + ip2.bucketName = bucketName.toString(); + ip2.indexKey = 123456L; + ip2.value = "My Object Value!"; + + sv = new StoreValue.Builder(ip2).build(); + svFuture = client.executeAsync(sv); + + svFuture.await(); + assertTrue(svFuture.isSuccess()); + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + + IntIndexQuery iiq = + new IntIndexQuery.Builder(ns, "test_index", 123456L).withKeyAndIndex(true).build(); + IntIndexQuery.Response iResp = client.execute(iiq); + + assertTrue(iResp.hasEntries()); + assertEquals(2, iResp.getEntries().size()); + + boolean found = false; + for (IntIndexQuery.Response.Entry e : iResp.getEntries()) + { + if (e.getRiakObjectLocation().getKey().toString().equals("index_test_object_key")) + { + found = true; + assertEquals(ip.indexKey, e.getIndexKey()); + } + } + + assertTrue(found); + + } + + @Test + public void testRangeQuery() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + + RiakClient client = new RiakClient(cluster); + + IndexedPojo ip = new IndexedPojo(); + ip.key = "index_test_object_key1"; + ip.bucketName = bucketName.toString(); + ip.indexKey = 1L; + ip.value = "My Object Value!"; + + StoreValue sv = new StoreValue.Builder(ip).build(); + RiakFuture svFuture = client.executeAsync(sv); + + svFuture.await(); + assertTrue(svFuture.isSuccess()); + + IndexedPojo ip2 = new IndexedPojo(); + ip2.key = "index_test_object_key2"; + ip2.bucketName = bucketName.toString(); + ip2.indexKey = 25L; + ip2.value = "My Object Value!"; + + sv = new StoreValue.Builder(ip2).build(); + svFuture = client.executeAsync(sv); + + svFuture.await(); + assertTrue(svFuture.isSuccess()); + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + + IntIndexQuery iiq = + new IntIndexQuery.Builder(ns, "test_index", Long.MIN_VALUE, Long.MAX_VALUE) + .withKeyAndIndex(true) + .build(); + + IntIndexQuery.Response iResp = client.execute(iiq); + assertTrue(iResp.hasEntries()); + assertEquals(2, iResp.getEntries().size()); + + boolean found = false; + for (IntIndexQuery.Response.Entry e : iResp.getEntries()) + { + if (e.getRiakObjectLocation().getKey().toString().equals("index_test_object_key1")) + { + found = true; + assertEquals(ip.indexKey, e.getIndexKey()); + } + } + + assertTrue(found); + + } + + public static class IndexedPojo + { + @RiakKey + public String key; + + @RiakBucketName + public String bucketName; + + @RiakIndex(name="test_index") + Long indexKey; + + @RiakVClock + VClock vclock; + + public String value; + } +} diff --git a/src/test/java/com/basho/riak/client/api/commands/indexes/itest/ITestRawIndexQuery.java b/src/test/java/com/basho/riak/client/api/commands/indexes/itest/ITestRawIndexQuery.java new file mode 100644 index 000000000..0e46ad919 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/indexes/itest/ITestRawIndexQuery.java @@ -0,0 +1,164 @@ +/* + * 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.indexes.itest; + +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.api.annotations.RiakBucketName; +import com.basho.riak.client.api.annotations.RiakIndex; +import com.basho.riak.client.api.annotations.RiakKey; +import com.basho.riak.client.api.annotations.RiakVClock; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.itest.ITestBase; +import com.basho.riak.client.api.commands.indexes.RawIndexQuery; +import com.basho.riak.client.api.commands.indexes.SecondaryIndexQuery.Type; +import com.basho.riak.client.api.commands.kv.StoreValue; +import com.basho.riak.client.core.operations.StoreOperation; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.query.indexes.LongIntIndex; +import com.basho.riak.client.core.util.BinaryValue; +import java.util.concurrent.ExecutionException; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Assume; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class ITestRawIndexQuery extends ITestBase +{ + @Test + public void simpleTest() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + + RiakClient client = new RiakClient(cluster); + + BinaryValue indexKey = BinaryValue.create("index_test_index_key"); + + IndexedPojo ip = new IndexedPojo(); + ip.key = "index_test_object_key"; + ip.bucketName = bucketName.toString(); + ip.indexKey = indexKey.getValue(); + ip.value = "My Object Value!"; + + StoreValue sv = new StoreValue.Builder(ip).build(); + RiakFuture svFuture = client.executeAsync(sv); + + svFuture.await(); + assertTrue(svFuture.isSuccess()); + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + + RawIndexQuery biq = + new RawIndexQuery.Builder(ns, "test_index", Type._BIN, indexKey).withKeyAndIndex(true).build(); + RawIndexQuery.Response iResp = client.execute(biq); + + assertTrue(iResp.hasEntries()); + RawIndexQuery.Response.Entry first = iResp.getEntries().iterator().next(); + assertEquals(ip.key, first.getRiakObjectLocation().getKey().toString()); + assertArrayEquals(ip.indexKey, first.getIndexKey().getValue()); + + } + + @Test + public void testKeyIndexHack() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + RiakClient client = new RiakClient(cluster); + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + + for (long i = 0; i < 100; i++) + { + RiakObject obj = new RiakObject().setValue(BinaryValue.create("some_value")); + + Location location = new Location(ns, "my_key" + i); + StoreOperation storeOp = + new StoreOperation.Builder(location) + .withContent(obj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + } + + RawIndexQuery biq = + new RawIndexQuery.Builder(ns, "$key", Type._KEY, BinaryValue.create("my_key10"), BinaryValue.create("my_key19")) + .withKeyAndIndex(true).build(); + RawIndexQuery.Response iResp = client.execute(biq); + assertTrue(iResp.hasEntries()); + assertEquals(iResp.getEntries().size(), 10); + + + } + + @Test + public void testBucketIndexHack() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + RiakClient client = new RiakClient(cluster); + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + + for (long i = 0; i < 100; i++) + { + RiakObject obj = new RiakObject().setValue(BinaryValue.create("some_value")); + + Location location = new Location(ns, "my_key" + i); + StoreOperation storeOp = + new StoreOperation.Builder(location) + .withContent(obj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + } + + RawIndexQuery biq = + new RawIndexQuery.Builder(ns, "$bucket", Type._BUCKET, bucketName) + .withKeyAndIndex(true).build(); + + RawIndexQuery.Response iResp = client.execute(biq); + assertTrue(iResp.hasEntries()); + assertEquals(100, iResp.getEntries().size()); + + } + + + public static class IndexedPojo + { + @RiakKey + public String key; + + @RiakBucketName + public String bucketName; + + @RiakIndex(name="test_index_bin") + byte[] indexKey; + + @RiakVClock + VClock vclock; + + public String value; + } +} diff --git a/src/test/java/com/basho/riak/client/api/commands/itest/ITestBucketKeyMapReduce.java b/src/test/java/com/basho/riak/client/api/commands/itest/ITestBucketKeyMapReduce.java new file mode 100644 index 000000000..e6a611d3f --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/itest/ITestBucketKeyMapReduce.java @@ -0,0 +1,212 @@ +/* + * 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.itest; + +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.core.operations.itest.ITestBase; +import com.basho.riak.client.api.commands.buckets.StoreBucketProperties; +import com.basho.riak.client.api.commands.kv.StoreValue; +import com.basho.riak.client.api.commands.mapreduce.BucketKeyMapReduce; +import com.basho.riak.client.api.commands.mapreduce.MapReduce; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.query.functions.Function; +import com.basho.riak.client.core.util.BinaryValue; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import org.junit.Test; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import org.junit.After; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import org.junit.Assume; +import org.junit.Before; + +/** + * @author Brian Roach + * @since 2.0 + */ +public class ITestBucketKeyMapReduce extends ITestBase +{ + + RiakClient client = new RiakClient(cluster); + static final String mrBucket = "mr_bucket"; + + @Before + public void changeBucketProps() throws ExecutionException, InterruptedException + { + if (testBucketType) + { + Namespace ns = new Namespace(bucketType.toString(), mrBucket); + StoreBucketProperties op = new StoreBucketProperties.Builder(ns).withAllowMulti(false).build(); + client.execute(op); + } + } + + @After + public void clearMrBucket() throws InterruptedException, ExecutionException + { + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, mrBucket); + resetAndEmptyBucket(ns); + if (testBucketType) + { + ns = new Namespace(bucketType.toString(), mrBucket); + resetAndEmptyBucket(ns); + } + } + + private void initValues(String bucketType) throws ExecutionException, InterruptedException + { + RiakObject obj = new RiakObject(); + + obj.setValue(BinaryValue.create("Alice was beginning to get very tired of sitting by her sister on the " + + "bank, and of having nothing to do: once or twice she had peeped into the " + + "book her sister was reading, but it had no pictures or conversations in " + + "it, 'and what is the use of a book,' thought Alice 'without pictures or " + + "conversation?'")); + Namespace ns = new Namespace(bucketType, mrBucket); + Location location = new Location(ns, "p1"); + client.execute(new StoreValue.Builder(obj).withLocation(location).build()); + + obj.setValue(BinaryValue.create("So she was considering in her own mind (as well as she could, for the " + + "hot day made her feel very sleepy and stupid), whether the pleasure " + + "of making a daisy-chain would be worth the trouble of getting up and " + + "picking the daisies, when suddenly a White Rabbit with pink eyes ran " + + "close by her.")); + + location = new Location(ns, "p2"); + client.execute(new StoreValue.Builder(obj).withLocation(location).build()); + + + obj.setValue(BinaryValue.create("The rabbit-hole went straight on like a tunnel for some way, and then " + + "dipped suddenly down, so suddenly that Alice had not a moment to think " + + "about stopping herself before she found herself falling down a very deep " + + "well.")); + location = new Location(ns, "p3"); + client.execute(new StoreValue.Builder(obj).withLocation(location).build()); + + } + + @Test + public void JsBucketKeyMRDefaultType() throws InterruptedException, ExecutionException, IOException + { + JsBucketKeyMR(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void JsBucketKeyMRTestType() throws InterruptedException, ExecutionException, IOException + { + Assume.assumeTrue(testBucketType); + JsBucketKeyMR(bucketType.toString()); + } + + private void JsBucketKeyMR(String bucketType) throws InterruptedException, ExecutionException, IOException + { + initValues(bucketType); + + Namespace ns = new Namespace(bucketType, mrBucket); + MapReduce mr = new BucketKeyMapReduce.Builder() + .withLocation(new Location(ns, "p1")) + .withLocation(new Location(ns, "p2")) + .withLocation(new Location(ns, "p3")) + .withMapPhase(Function.newAnonymousJsFunction( + "function(v, key_data) {" + + " var m = v.values[0].data.toLowerCase().match(/\\w*/g);" + + " var r = [];" + + " for(var i in m) {" + + " if(m[i] != '') {" + + " var o = {};" + + " o[m[i]] = 1;" + + " r.push(o);" + + " }" + + " }" + + " return r;" + + "}" + )) + .withReducePhase(Function.newAnonymousJsFunction( + "function(v, key_data) {" + + " var r = {};" + + " for(var i in v) {" + + " for(var w in v[i]) {" + + " if(w in r)" + + " r[w] += v[i][w];" + + " else" + + " r[w] = v[i][w];" + + " }" + + " }" + + " return [r];" + + "}" + ), true) + .build(); + + MapReduce.Response response = client.execute(mr); + + // The query should return one phase result which is a JSON array containing a + // single JSON object that is a set of word counts. + ArrayNode resultList = response.getResultForPhase(1); + + assertEquals(1, response.getResultsFromAllPhases().size()); + + String json = resultList.get(0).toString(); + ObjectMapper oMapper = new ObjectMapper(); + + TypeReference> type = new TypeReference>(){}; + Map resultMap = oMapper.readValue(json, type); + + assertNotNull(resultMap.containsKey("the")); + assertEquals(Integer.valueOf(8), resultMap.get("the")); + } + + @Test + public void erlangBucketKeyMRDefaultType() throws ExecutionException, InterruptedException + { + erlangBucketKeyMR(Namespace.DEFAULT_BUCKET_TYPE); + } + + // This will fail due to a bug in Riak. Named functions in a type other than default are + // broken. + @Test + public void erlangBucketKeyMRTestType() throws ExecutionException, InterruptedException + { + Assume.assumeTrue(testBucketType); + erlangBucketKeyMR(bucketType.toString()); + } + + private void erlangBucketKeyMR(String bucketType) throws ExecutionException, InterruptedException + { + initValues(bucketType); + Namespace ns = new Namespace(bucketType, mrBucket); + + MapReduce mr = new BucketKeyMapReduce.Builder() + .withLocation(new Location(ns, "p1")) + .withLocation(new Location(ns, "p2")) + .withLocation(new Location(ns, "p3")) + .withMapPhase(Function.newErlangFunction("riak_kv_mapreduce", "map_object_value"), false) + .withReducePhase(Function.newErlangFunction("riak_kv_mapreduce","reduce_sort"), true) + .build(); + + MapReduce.Response response = client.execute(mr); + + assertEquals(3, response.getResultsFromAllPhases().size()); + } + +} diff --git a/src/test/java/com/basho/riak/client/api/commands/itest/ITestBucketMapReduce.java b/src/test/java/com/basho/riak/client/api/commands/itest/ITestBucketMapReduce.java new file mode 100644 index 000000000..b89d99227 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/itest/ITestBucketMapReduce.java @@ -0,0 +1,303 @@ +/* + * 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.commands.itest; + +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.itest.ITestBase; +import com.basho.riak.client.api.commands.buckets.StoreBucketProperties; +import com.basho.riak.client.api.commands.kv.StoreValue; +import com.basho.riak.client.api.commands.mapreduce.BucketMapReduce; +import com.basho.riak.client.api.commands.mapreduce.MapReduce; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.api.commands.mapreduce.filters.GreaterThanFilter; +import com.basho.riak.client.api.commands.mapreduce.filters.LessThanFilter; +import com.basho.riak.client.api.commands.mapreduce.filters.LogicalAndFilter; +import com.basho.riak.client.api.commands.mapreduce.filters.StringToIntFilter; +import com.basho.riak.client.api.commands.mapreduce.filters.TokenizeFilter; +import com.basho.riak.client.core.query.functions.Function; +import com.basho.riak.client.core.util.BinaryValue; +import com.fasterxml.jackson.databind.node.ArrayNode; +import java.util.concurrent.ExecutionException; +import org.junit.After; +import org.junit.Test; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Assume; +import org.junit.Before; + +/** + * + * @author Brian Roach + */ +public class ITestBucketMapReduce extends ITestBase +{ + private final static RiakClient client = new RiakClient(cluster); + private final static String mrBucketName = bucketName.toString() + "_mr"; + + @Before + public void changeBucketProps() throws ExecutionException, InterruptedException + { + if (testBucketType) + { + Namespace ns = new Namespace(bucketType.toString(), mrBucketName); + StoreBucketProperties op = new StoreBucketProperties.Builder(ns).withAllowMulti(false).build(); + client.execute(op); + } + } + + @After + public void cleanUp() throws InterruptedException, ExecutionException + { + // Because some of these tests blow up due to Riak bugs we need + // to clean up the mess here + Namespace ns = new Namespace(mrBucketName); + resetAndEmptyBucket(ns); + + if (testBucketType) + { + ns = new Namespace(bucketType.toString(), mrBucketName); + resetAndEmptyBucket(ns); + } + + } + + private void initValues(String bucketType) throws InterruptedException + { + // insert 200 items into a bucket + + Namespace ns = new Namespace(bucketType, mrBucketName); + + String keyPrefix = "mr_test_"; + for (int i = 0; i < 200; i++) + { + Location loc = new Location(ns, keyPrefix + i); + RiakObject ro = new RiakObject().setContentType("text/plain") + .setValue(BinaryValue.create(Integer.toString(i))); + StoreValue sv = new StoreValue.Builder(ro).withLocation(loc).build(); + RiakFuture future = client.executeAsync(sv); + future.await(); + assertTrue(future.isSuccess()); + } + } + + @Test + public void erlangBucketMRDefaultType() throws InterruptedException, ExecutionException + { + erlangBucketMR(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void erlangBucketMRTestType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(testBucketType); + erlangBucketMR(bucketType.toString()); + } + + private void erlangBucketMR(String bucketType) throws InterruptedException, ExecutionException + { + initValues(bucketType); + Namespace ns = new Namespace(bucketType, mrBucketName); + BucketMapReduce bmr = + new BucketMapReduce.Builder() + .withNamespace(ns) + .withMapPhase(Function.newErlangFunction("riak_kv_mapreduce", "map_object_value"), false) + .withReducePhase(Function.newErlangFunction("riak_kv_mapreduce", "reduce_string_to_integer"), false) + .withReducePhase(Function.newErlangFunction("riak_kv_mapreduce", "reduce_sort"), true) + .build(); + + MapReduce.Response response = client.execute(bmr); + + // The query should return one phase result which is a JSON array containing + // all the values, 0 - 199 + assertEquals(200, response.getResultsFromAllPhases().size()); + ArrayNode result = response.getResultForPhase(2); + assertEquals(200, result.size()); + + assertEquals(42, result.get(42).asInt()); + assertEquals(199, result.get(199).asInt()); + + resetAndEmptyBucket(ns); + } + + @Test + public void JsBucketMRDefaultType() throws InterruptedException, ExecutionException + { + JsBucketMR(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void JsBucketMRTestType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(testBucketType); + JsBucketMR(bucketType.toString()); + } + + private void JsBucketMR(String bucketType) throws InterruptedException, ExecutionException + { + initValues(bucketType); + + Namespace ns = new Namespace(bucketType, mrBucketName); + BucketMapReduce bmr = + new BucketMapReduce.Builder() + .withNamespace(ns) + .withMapPhase(Function.newNamedJsFunction("Riak.mapValuesJson"), false) + .withReducePhase(Function.newNamedJsFunction("Riak.reduceNumericSort"), true) + .build(); + + RiakFuture future = client.executeAsync(bmr); + + future.await(); + assertTrue("Map reduce operation Operation failed:" + future.cause(), future.isSuccess()); + + MapReduce.Response response = future.get(); + + // The query should return one phase result which is a JSON array containing + // all the values, 0 - 199 + assertEquals(200, response.getResultsFromAllPhases().size()); + ArrayNode result = response.getResultForPhase(1); + assertEquals(200, result.size()); + + assertEquals(42, result.get(42).asInt()); + assertEquals(199, result.get(199).asInt()); + + resetAndEmptyBucket(ns); + } + + @Test + public void multiPhaseResult() throws InterruptedException, ExecutionException + { + initValues(Namespace.DEFAULT_BUCKET_TYPE); + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, mrBucketName); + BucketMapReduce bmr = + new BucketMapReduce.Builder() + .withNamespace(ns) + .withMapPhase(Function.newNamedJsFunction("Riak.mapValuesJson"), true) + .withReducePhase(Function.newNamedJsFunction("Riak.reduceNumericSort"), true) + .build(); + + RiakFuture future = client.executeAsync(bmr); + + future.await(); + assertTrue(future.isSuccess()); + + MapReduce.Response response = future.get(); + + // The query should return two phase results, each a JSON array containing + // all the values, 0 - 199 + assertEquals(400, response.getResultsFromAllPhases().size()); + assertEquals(200, response.getResultForPhase(0).size()); + ArrayNode result = response.getResultForPhase(1); + assertEquals(200, result.size()); + + assertEquals(42, result.get(42).asInt()); + assertEquals(199, result.get(199).asInt()); + + resetAndEmptyBucket(ns); + } + + @Test + public void keyFilter() throws InterruptedException, ExecutionException + { + initValues(Namespace.DEFAULT_BUCKET_TYPE); + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, mrBucketName); + BucketMapReduce bmr = + new BucketMapReduce.Builder() + .withNamespace(ns) + .withMapPhase(Function.newNamedJsFunction("Riak.mapValuesJson")) + .withReducePhase(Function.newErlangFunction("riak_kv_mapreduce", "reduce_sort"),true) + .withKeyFilter(new TokenizeFilter("_",3)) + .withKeyFilter(new StringToIntFilter()) + .withKeyFilter(new LogicalAndFilter(new LessThanFilter(50), new GreaterThanFilter(45))) + .build(); + + RiakFuture future = client.executeAsync(bmr); + + future.await(); + assertTrue(future.isSuccess()); + + MapReduce.Response response = future.get(); + assertEquals(4, response.getResultsFromAllPhases().size()); + assertEquals(46, response.getResultsFromAllPhases().get(0).asInt()); + assertEquals(49, response.getResultsFromAllPhases().get(3).asInt()); + + resetAndEmptyBucket(ns); + } + + @Test + public void differentBucketType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(testBucketType); + + initValues(bucketType.toString()); + Namespace ns = new Namespace(bucketType.toString(), mrBucketName); + BucketMapReduce bmr = + new BucketMapReduce.Builder() + .withNamespace(ns) + .withMapPhase(Function.newAnonymousJsFunction( + "function(value, keydata, arg) {" + + " var data = value.values[0].data;" + + " if(data > 20)" + + " return [data];" + + " else" + + " return[];" + + "}"), true) + .build(); + + MapReduce.Response response = client.execute(bmr); + + assertEquals(179, response.getResultsFromAllPhases().size()); + + resetAndEmptyBucket(ns); + } + + @Test + public void differentBucketTypeWithFilter() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(testBucketType); + + initValues(bucketType.toString()); + + Namespace ns = new Namespace(bucketType.toString(), mrBucketName); + BucketMapReduce bmr = + new BucketMapReduce.Builder() + .withNamespace(ns) + .withKeyFilter(new TokenizeFilter("_",3)) + .withKeyFilter(new StringToIntFilter()) + .withKeyFilter(new LogicalAndFilter(new LessThanFilter(50), new GreaterThanFilter(45))) + .withMapPhase(Function.newAnonymousJsFunction( + "function(value, keydata, arg) {" + + " var data = value.values[0].data;" + + " return [data];" + + "}"), true) + .build(); + + RiakFuture future = client.executeAsync(bmr); + + future.await(); + assertTrue(future.isSuccess()); + assertEquals(4, future.get().getResultsFromAllPhases().size()); + resetAndEmptyBucket(ns); + + } + +} diff --git a/src/test/java/com/basho/riak/client/api/commands/itest/ITestDatatype.java b/src/test/java/com/basho/riak/client/api/commands/itest/ITestDatatype.java new file mode 100644 index 000000000..1c3b6b3dc --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/itest/ITestDatatype.java @@ -0,0 +1,167 @@ +package com.basho.riak.client.api.commands.itest; + +import com.basho.riak.client.core.query.crdt.types.RiakSet; +import com.basho.riak.client.core.query.crdt.types.RiakMap; +import com.basho.riak.client.core.query.crdt.types.RiakCounter; +import com.basho.riak.client.core.query.crdt.types.RiakRegister; +import com.basho.riak.client.core.query.crdt.types.RiakFlag; +import com.basho.riak.client.api.commands.datatypes.CounterUpdate; +import com.basho.riak.client.api.commands.datatypes.MapUpdate; +import com.basho.riak.client.api.commands.datatypes.RegisterUpdate; +import com.basho.riak.client.api.commands.datatypes.FlagUpdate; +import com.basho.riak.client.api.commands.datatypes.SetUpdate; +import com.basho.riak.client.api.commands.datatypes.FetchMap; +import com.basho.riak.client.api.commands.datatypes.UpdateDatatype.Option; +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.core.operations.itest.ITestBase; +import com.basho.riak.client.api.commands.datatypes.UpdateMap; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.util.BinaryValue; +import org.junit.Assume; +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import java.util.concurrent.ExecutionException; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class ITestDatatype extends ITestBase +{ + + private final String numLogins = "logins"; + private final String lastLoginTime = "last-login"; + private final ByteBuffer nowBinary = ByteBuffer.allocate(8).putLong(System.currentTimeMillis()); + private final byte[] now = nowBinary.array(); + private final String username = "username"; + private final String loggedIn = "logged-in"; + private final String shoppingCart = "cart"; + + private final Namespace carts = new Namespace(mapBucketType, bucketName); + + private final RiakClient client = new RiakClient(cluster); + + @Test + public void simpleTest() throws ExecutionException, InterruptedException + { + Assume.assumeTrue(testCrdt); + + /** + * Update some info about a user in a table of users + */ + + resetAndEmptyBucket(carts); + + BinaryValue key = BinaryValue.create("user-info"); + + /* + + Data structure: + + bucket-type == asMap + -> "username" : asMap + -> "logins" : counter + -> "last-login" : register + -> "logged-in" : flag + -> "cart" : asSet + + */ + + // Build a shopping cart. We're buying the digits!!!! + ByteBuffer buffer = ByteBuffer.allocate(4); + SetUpdate favorites = new SetUpdate(); + for (int i = 0; i < 10; ++i) + { + buffer.putInt(i); + favorites.add(BinaryValue.create(buffer.array())); + buffer.rewind(); + } + + // Create an update for the user's values + MapUpdate userMapUpdate = new MapUpdate() + .update(numLogins, new CounterUpdate(1)) // counter + .update(lastLoginTime, new RegisterUpdate(now)) // register + .update(loggedIn, new FlagUpdate(true)) // flag + .update(shoppingCart, favorites); // asSet + + // Now create an update for the user's entry + MapUpdate userEntryUpdate = new MapUpdate() + .update(username, userMapUpdate); + + UpdateMap update = new UpdateMap.Builder(carts, userEntryUpdate) + .withOption(Option.RETURN_BODY, true) + .build(); + UpdateMap.Response updateResponse = client.execute(update); + + Location loc = new Location(carts, updateResponse.getGeneratedKey()); + FetchMap fetch = new FetchMap.Builder(loc).build(); + FetchMap.Response fetchResponse = client.execute(fetch); + + // users + RiakMap usersMap = fetchResponse.getDatatype(); + + // username + RiakMap usernameMap = usersMap.getMap(username); + assertNotNull(usernameMap); + + // logins - counter + RiakCounter numLoginsCounter = usernameMap.getCounter(numLogins); + assertEquals((Long) 1L, numLoginsCounter.view()); + + // last-login - register + RiakRegister lastLoginTimeRegister = usernameMap.getRegister(lastLoginTime); + + assertTrue(Arrays.equals(now, lastLoginTimeRegister.view().getValue())); + + + // logged-in - flag + RiakFlag loggedInFlag = usernameMap.getFlag(loggedIn); + assertEquals((Boolean) true, loggedInFlag.view()); + + // cart - asSet + RiakSet shoppingCartSet = usernameMap.getSet(shoppingCart); + Set setView = shoppingCartSet.view(); + Set expectedSet = new HashSet(); + for (int i = 0; i < 10; ++i) + { + ByteBuffer b = ByteBuffer.allocate(4); + b.putInt(i); + expectedSet.add(BinaryValue.create(b.array())); + } + assertTrue(setView.containsAll(expectedSet)); + assertTrue(expectedSet.containsAll(setView)); + + + } + + public void testConflict() throws ExecutionException, InterruptedException + { + + resetAndEmptyBucket(carts); + + MapUpdate innerMapUpdate = new MapUpdate().update("flag", new FlagUpdate(true)); + CounterUpdate innerCounterUpdate = new CounterUpdate(1); + + // Insert a Map and Counter into logins and observe both counter and map returned + UpdateMap conflictedUpdateCmd = + new UpdateMap.Builder(carts, new MapUpdate().update(numLogins, innerMapUpdate).update(numLogins, innerCounterUpdate)) + .withOption(Option.RETURN_BODY, true) + .build(); + + UpdateMap.Response conflictedResponse = + client.execute(conflictedUpdateCmd); + + assertNotNull(conflictedResponse.getDatatype()); + assertEquals(2, conflictedResponse.getDatatype().view().size()); + assertNotNull(conflictedResponse.getDatatype().getMap(numLogins)); + assertNotNull(conflictedResponse.getDatatype().getCounter(numLogins)); + + } + +} diff --git a/src/test/java/com/basho/riak/client/api/commands/itest/ITestFetchValue.java b/src/test/java/com/basho/riak/client/api/commands/itest/ITestFetchValue.java new file mode 100644 index 000000000..f4d77da97 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/itest/ITestFetchValue.java @@ -0,0 +1,333 @@ +/* + * 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.commands.itest; + +import com.basho.riak.client.api.cap.ConflictResolver; +import com.basho.riak.client.api.cap.ConflictResolverFactory; +import com.basho.riak.client.api.cap.UnresolvedConflictException; +import com.basho.riak.client.core.operations.itest.ITestBase; +import com.basho.riak.client.api.commands.kv.FetchValue.Option; +import com.basho.riak.client.api.commands.kv.FetchValue; +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.api.annotations.RiakVClock; +import com.basho.riak.client.api.cap.DefaultResolver; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.operations.StoreBucketPropsOperation; +import com.basho.riak.client.api.commands.kv.StoreValue; +import com.basho.riak.client.api.convert.JSONConverter; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.query.indexes.LongIntIndex; +import com.basho.riak.client.core.query.indexes.StringBinIndex; +import com.basho.riak.client.core.util.BinaryValue; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; +import static org.junit.Assert.*; +import org.junit.Assume; +import org.junit.Ignore; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class ITestFetchValue extends ITestBase +{ + @Test + public void simpleTestDefaultType() + { + simpleTest(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void simpleTestTestType() + { + Assume.assumeTrue(testBucketType); + simpleTest(bucketType.toString()); + } + + private void simpleTest(String bucketType) + { + try + { + RiakClient client = new RiakClient(cluster); + Namespace ns = new Namespace(bucketType, bucketName.toString()); + Location loc = new Location(ns, "test_fetch_key1"); + + Pojo pojo = new Pojo(); + pojo.value = "test value"; + StoreValue sv = + new StoreValue.Builder(pojo).withLocation(loc).build(); + + StoreValue.Response resp = client.execute(sv); + + + FetchValue fv = new FetchValue.Builder(loc).build(); + FetchValue.Response fResp = client.execute(fv); + + assertEquals(pojo.value, fResp.getValue(Pojo.class).value); + + RiakObject ro = fResp.getValue(RiakObject.class); + assertNotNull(ro.getValue()); + assertEquals("{\"value\":\"test value\"}", ro.getValue().toString()); + } + catch (ExecutionException ex) + { + System.out.println(ex.getCause().getCause()); + } + catch (InterruptedException ex) + { + Logger.getLogger(ITestFetchValue.class.getName()).log(Level.SEVERE, null, ex); + } + + } + + @Test + public void notFoundDefaultType() throws ExecutionException, InterruptedException + { + notFound(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void notFoundTestType() throws ExecutionException, InterruptedException + { + Assume.assumeTrue(testBucketType); + notFound(bucketType.toString()); + } + + private void notFound(String bucketType) throws ExecutionException, InterruptedException + { + RiakClient client = new RiakClient(cluster); + Namespace ns = new Namespace(bucketType, bucketName.toString()); + Location loc = new Location(ns, "test_fetch_key2"); + FetchValue fv = new FetchValue.Builder(loc).build(); + FetchValue.Response fResp = client.execute(fv); + + assertFalse(fResp.hasValues()); + assertTrue(fResp.isNotFound()); + assertNull(fResp.getValue(Pojo.class)); + RiakObject ro = fResp.getValue(RiakObject.class); + } + + // Apparently this isn't happening anymore. or it only happens with leveldb + // Leaving it here to investigate + @Ignore + @Test + public void ReproRiakTombstoneBehavior() throws ExecutionException, InterruptedException + { + // We're back to allow_mult=false as default + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + StoreBucketPropsOperation op = + new StoreBucketPropsOperation.Builder(ns) + .withAllowMulti(true) + .build(); + cluster.execute(op); + op.get(); + + + RiakClient client = new RiakClient(cluster); + Location loc = new Location(ns, "test_fetch_key3"); + + Pojo pojo = new Pojo(); + pojo.value = "test value"; + StoreValue sv = + new StoreValue.Builder(pojo).withLocation(loc).build(); + + client.execute(sv); + + resetAndEmptyBucket(bucketName); + + client.execute(sv); + + FetchValue fv = new FetchValue.Builder(loc) + .withOption(Option.DELETED_VCLOCK, false) + .build(); + + FetchValue.Response fResp = client.execute(fv); + + assertEquals(2, fResp.getValues(RiakObject.class).size()); + + } + + @Test + public void resolveSiblingsDefaultType() throws ExecutionException, InterruptedException + { + resolveSiblings(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void resolveSiblingsTestType() throws ExecutionException, InterruptedException + { + Assume.assumeTrue(testBucketType); + resolveSiblings(bucketType.toString()); + } + + private void resolveSiblings(String bucketType) throws ExecutionException, InterruptedException + { + RiakClient client = new RiakClient(cluster); + Namespace ns = new Namespace(bucketType, bucketName.toString()); + Location loc = new Location(ns, "test_fetch_key4"); + + Pojo pojo = new Pojo(); + pojo.value = "test value"; + StoreValue sv = + new StoreValue.Builder(pojo).withLocation(loc).build(); + + client.execute(sv); + + pojo.value = "Pick me!"; + + sv = new StoreValue.Builder(pojo).withLocation(loc).build(); + + client.execute(sv); + + ConflictResolverFactory.getInstance() + .registerConflictResolver(Pojo.class, new MyResolver()); + + FetchValue fv = new FetchValue.Builder(loc).build(); + + FetchValue.Response fResp = client.execute(fv); + + assertEquals(pojo.value, fResp.getValue(Pojo.class).value); + + JSONConverter converter = new JSONConverter(Pojo.class); + ConflictResolver resolver = new DefaultResolver(); + + assertEquals(pojo.value, fResp.getValues(converter).get(0).value); + assertEquals(pojo.value, fResp.getValue(converter, resolver).value); + + } + + @Test + public void fetchAnnotatedPojoDefaultType() throws ExecutionException, InterruptedException + { + fetchAnnotatedPojo(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void fetchAnnotatedPojoTestType() throws ExecutionException, InterruptedException + { + Assume.assumeTrue(testBucketType); + fetchAnnotatedPojo(bucketType.toString()); + } + + private void fetchAnnotatedPojo(String bucketType) throws ExecutionException, InterruptedException + { + RiakClient client = new RiakClient(cluster); + Namespace ns = new Namespace(bucketType, bucketName.toString()); + Location loc = new Location(ns, "test_fetch_key5"); + + String jsonValue = "{\"value\":\"my value\"}"; + + RiakObject ro = new RiakObject() + .setValue(BinaryValue.create(jsonValue)) + .setContentType("application/json"); + + StoreValue sv = new StoreValue.Builder(ro).withLocation(loc).build(); + client.execute(sv); + + FetchValue fv = new FetchValue.Builder(loc).build(); + FetchValue.Response resp = client.execute(fv); + + RiakAnnotatedPojo rap = resp.getValue(RiakAnnotatedPojo.class); + + assertNotNull(rap.bucketName); + assertEquals(ns.getBucketNameAsString(), rap.bucketName); + assertNotNull(rap.key); + assertEquals(loc.getKeyAsString(), rap.key); + assertNotNull(rap.bucketType); + assertEquals(ns.getBucketTypeAsString(), rap.bucketType); + assertNotNull(rap.contentType); + assertEquals(ro.getContentType(), rap.contentType); + assertNotNull(rap.vclock); + assertNotNull(rap.vtag); + assertNotNull(rap.lastModified); + assertNotNull(rap.value); + assertFalse(rap.deleted); + assertNotNull(rap.value); + assertEquals("my value", rap.value); + } + + @Test + public void fetchAnnotatedPojoWIthIndexes() throws ExecutionException, InterruptedException + { + RiakClient client = new RiakClient(cluster); + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + Location loc = new Location(ns,"test_fetch_key6"); + + String jsonValue = "{\"value\":\"my value\"}"; + + RiakObject ro = new RiakObject() + .setValue(BinaryValue.create(jsonValue)) + .setContentType("application/json"); + + ro.getIndexes().getIndex(StringBinIndex.named("email")).add("roach@basho.com"); + ro.getIndexes().getIndex(LongIntIndex.named("user_id")).add(1L); + + StoreValue sv = new StoreValue.Builder(ro).withLocation(loc).build(); + client.execute(sv); + + FetchValue fv = new FetchValue.Builder(loc).build(); + FetchValue.Response resp = client.execute(fv); + + RiakAnnotatedPojo rap = resp.getValue(RiakAnnotatedPojo.class); + + assertNotNull(rap.emailIndx); + assertTrue(rap.emailIndx.contains("roach@basho.com")); + assertEquals(rap.userId.longValue(), 1L); + + + + } + + public static class Pojo + { + @JsonProperty + String value; + + @RiakVClock + VClock vclock; + + } + + public static class MyResolver implements ConflictResolver + { + + @Override + public Pojo resolve(List objectList) throws UnresolvedConflictException + { + if (objectList.size() > 0) + { + for (Pojo p : objectList) + { + if (p.value.equals("Pick me!")) + { + return p; + } + } + + return objectList.get(0); + } + return null; + } + + } +} diff --git a/src/test/java/com/basho/riak/client/api/commands/itest/ITestIndexMapReduce.java b/src/test/java/com/basho/riak/client/api/commands/itest/ITestIndexMapReduce.java new file mode 100644 index 000000000..608f5ee7e --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/itest/ITestIndexMapReduce.java @@ -0,0 +1,199 @@ +/* + * 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.itest; + +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.itest.ITestBase; +import com.basho.riak.client.api.commands.buckets.StoreBucketProperties; +import com.basho.riak.client.api.commands.kv.StoreValue; +import com.basho.riak.client.api.commands.mapreduce.IndexMapReduce; +import com.basho.riak.client.api.commands.mapreduce.MapReduce; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.query.functions.Function; +import com.basho.riak.client.core.query.indexes.LongIntIndex; +import com.basho.riak.client.core.util.BinaryValue; +import java.util.concurrent.ExecutionException; +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class ITestIndexMapReduce extends ITestBase +{ + private final RiakClient client = new RiakClient(cluster); + private final String mrBucketName = bucketName.toString() + "_mr"; + + @Before + public void changeBucketProps() throws ExecutionException, InterruptedException + { + if (testBucketType) + { + Namespace ns = new Namespace(bucketType.toString(), mrBucketName); + StoreBucketProperties op = new StoreBucketProperties.Builder(ns).withAllowMulti(false).build(); + client.execute(op); + } + } + + private void initValues(String bucketType) throws InterruptedException + { + String keyPrefix = "mr_test_"; + Namespace ns = new Namespace(bucketType, mrBucketName); + for (int i = 0; i < 200; i++) + { + Location loc = new Location(ns, keyPrefix + i); + RiakObject ro = new RiakObject().setContentType("text/plain") + .setValue(BinaryValue.create(Integer.toString(i))); + ro.getIndexes().getIndex(LongIntIndex.named("user_id")).add((long)i); + StoreValue sv = new StoreValue.Builder(ro).withLocation(loc).build(); + RiakFuture future = client.executeAsync(sv); + future.await(); + assertTrue(future.isSuccess()); + } + } + + private void initValuesOneToN(String bucketType) throws InterruptedException + { + String keyPrefix = "mr_test_"; + Namespace ns = new Namespace(bucketType, mrBucketName); + for (int i = 0; i < 200; i++) + { + Location loc = new Location(ns, keyPrefix + i); + RiakObject ro = new RiakObject().setContentType("text/plain") + .setValue(BinaryValue.create(Integer.toString(i))); + // Single index key used on all 200 objects + ro.getIndexes().getIndex(LongIntIndex.named("user_id")).add(1L); + StoreValue sv = new StoreValue.Builder(ro).withLocation(loc).build(); + RiakFuture future = client.executeAsync(sv); + future.await(); + assertTrue(future.isSuccess()); + } + } + + @Test + public void matchIndex() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + initValuesOneToN(Namespace.DEFAULT_BUCKET_TYPE); + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, mrBucketName); + IndexMapReduce imr = new IndexMapReduce.Builder() + .withNamespace(ns) + .withIndex("user_id_int") + .withMatchValue(1L) + .withMapPhase(Function.newAnonymousJsFunction( + "function(value, keydata, arg) {" + + " var data = value.values[0].data;" + + " return [data];" + + "}"), true) + .build(); + + MapReduce.Response response = client.execute(imr); + + assertEquals(200, response.getResultsFromAllPhases().size()); + + resetAndEmptyBucket(ns); + + } + + @Test + public void matchIndexDiffType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(testBucketType); + Assume.assumeTrue(test2i); + + initValuesOneToN(bucketType.toString()); + Namespace ns = new Namespace(bucketType.toString(), mrBucketName); + IndexMapReduce imr = new IndexMapReduce.Builder() + .withNamespace(ns) + .withIndex("user_id_int") + .withMatchValue(1L) + .withMapPhase(Function.newAnonymousJsFunction( + "function(value, keydata, arg) {" + + " var data = value.values[0].data;" + + " return [data];" + + "}"), true) + .build(); + + MapReduce.Response response = client.execute(imr); + + assertEquals(200, response.getResultsFromAllPhases().size()); + + resetAndEmptyBucket(ns); + } + + @Test + public void rangeIndex() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + initValues(Namespace.DEFAULT_BUCKET_TYPE); + + Namespace ns = new Namespace(mrBucketName); + IndexMapReduce imr = new IndexMapReduce.Builder() + .withNamespace(ns) + .withIndex("user_id_int") + .withRange(0, 19) + .withMapPhase(Function.newAnonymousJsFunction( + "function(value, keydata, arg) {" + + " var data = value.values[0].data;" + + " return [data];" + + "}"), true) + .build(); + + MapReduce.Response response = client.execute(imr); + + assertEquals(20, response.getResultsFromAllPhases().size()); + + resetAndEmptyBucket(ns); + + + } + + @Test + public void rangeIndexDiffType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(testBucketType); + Assume.assumeTrue(test2i); + + initValues(bucketType.toString()); + Namespace ns = new Namespace(bucketType.toString(), mrBucketName); + IndexMapReduce imr = new IndexMapReduce.Builder() + .withNamespace(ns) + .withIndex("user_id_int") + .withRange(0, 19) + .withMapPhase(Function.newAnonymousJsFunction( + "function(value, keydata, arg) {" + + " var data = value.values[0].data;" + + " return [data];" + + "}"), true) + .build(); + + MapReduce.Response response = client.execute(imr); + + assertEquals(20, response.getResultsFromAllPhases().size()); + + resetAndEmptyBucket(ns); + + + } +} diff --git a/src/test/java/com/basho/riak/client/api/commands/itest/ITestMultiFetch.java b/src/test/java/com/basho/riak/client/api/commands/itest/ITestMultiFetch.java new file mode 100644 index 000000000..e2820499e --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/itest/ITestMultiFetch.java @@ -0,0 +1,90 @@ +/* + * 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.commands.itest; + +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.operations.itest.ITestBase; +import com.basho.riak.client.api.commands.kv.FetchValue; +import com.basho.riak.client.api.commands.kv.MultiFetch; +import com.basho.riak.client.api.commands.kv.StoreValue; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class ITestMultiFetch extends ITestBase +{ + + @Test + public void simpleTest() throws ExecutionException, InterruptedException + { + RiakClient client = new RiakClient(cluster); + + String keyPrefix = "key_"; + String valuePrefix = "value_"; + List locations = new LinkedList(); + List values = new LinkedList(); + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + + // Store some stuff + for (int i = 0; i < 5; i++) + { + String key = keyPrefix + i; + String value = valuePrefix + i; + Location loc = new Location(ns, key); + locations.add(loc); + values.add(BinaryValue.create(value)); + RiakObject o = new RiakObject().setValue(BinaryValue.create(value)); + + StoreValue sv = new StoreValue.Builder(o).withLocation(loc).build(); + client.execute(sv); + } + + MultiFetch mf = new MultiFetch.Builder().addLocations(locations).build(); + + MultiFetch.Response mfr = client.execute(mf); + + assertEquals(locations.size(), mfr.getResponses().size()); + + List returnedLocations = new LinkedList(); + List returnedValues = new LinkedList(); + + for (RiakFuture future : mfr.getResponses()) + { + returnedLocations.add(future.getQueryInfo()); + returnedValues.add(future.get().getValue(RiakObject.class).getValue()); + } + + assertTrue(returnedLocations.containsAll(locations)); + assertTrue(returnedValues.containsAll(values)); + + + + + } +} diff --git a/src/test/java/com/basho/riak/client/api/commands/itest/ITestORM.java b/src/test/java/com/basho/riak/client/api/commands/itest/ITestORM.java new file mode 100644 index 000000000..a0ec03a67 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/itest/ITestORM.java @@ -0,0 +1,594 @@ +/* + * 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.commands.itest; + +import com.basho.riak.client.api.cap.ConflictResolver; +import com.basho.riak.client.api.cap.ConflictResolverFactory; +import com.basho.riak.client.api.cap.UnresolvedConflictException; +import com.basho.riak.client.api.convert.ConversionException; +import com.basho.riak.client.api.convert.Converter; +import com.basho.riak.client.api.convert.ConverterFactory; +import com.basho.riak.client.core.operations.itest.ITestBase; +import com.basho.riak.client.api.commands.kv.FetchValue; +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.api.annotations.RiakVClock; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.api.convert.RiakJacksonModule; +import com.basho.riak.client.core.operations.StoreBucketPropsOperation; +import com.basho.riak.client.api.commands.kv.StoreValue.Option; +import com.basho.riak.client.api.commands.kv.StoreValue; +import com.basho.riak.client.api.commands.kv.UpdateValue; +import com.basho.riak.client.api.commands.kv.UpdateValue.Update; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class ITestORM extends ITestBase +{ + @Before + public void resetFactory() + { + TypeReference> tr = + new TypeReference>(){}; + ConverterFactory.getInstance().unregisterConverterForClass(tr); + ConflictResolverFactory.getInstance().unregisterConflictResolver(tr); + } + + @Test + public void storeParamterizedTypeJSON() throws ExecutionException, InterruptedException, JsonProcessingException + { + RiakClient client = new RiakClient(cluster); + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + Location loc = new Location(ns, "test_ORM_key1"); + + GenericPojo gpf = new GenericPojo(); + List fooList = new ArrayList(); + fooList.add(new Foo("Foo in list value")); + gpf.value = new Foo("Foo in gp value"); + gpf.list = fooList; + + StoreValue sv = + new StoreValue.Builder(gpf) + .withLocation(loc) + .withOption(Option.RETURN_BODY, true) + .build(); + + StoreValue.Response resp = client.execute(sv); + + + TypeReference> tr = + new TypeReference>(){}; + GenericPojo gpf2 = resp.getValue(tr); + + assertNotNull(gpf2); + assertNotNull(gpf2.value); + assertEquals(gpf.value, gpf2.value); + assertNotNull(gpf2.list); + assertTrue(gpf.list.containsAll(gpf2.list)); + + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new RiakJacksonModule()); + String json = mapper.writeValueAsString(gpf2); + RiakObject ro = resp.getValue(RiakObject.class); + assertEquals(json, ro.getValue().toString()); + + } + + @Test + public void storeAndFetchParamterizedTypeJSON() throws ExecutionException, InterruptedException, JsonProcessingException + { + RiakClient client = new RiakClient(cluster); + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + Location loc = new Location(ns, "test_ORM_key2"); + + GenericPojo gpf = new GenericPojo(); + List fooList = new ArrayList(); + fooList.add(new Foo("Foo in list value")); + gpf.value = new Foo("Foo in gp value"); + gpf.list = fooList; + + StoreValue sv = + new StoreValue.Builder(gpf) + .withLocation(loc) + .build(); + + client.execute(sv); + + FetchValue fv = new FetchValue.Builder(loc).build(); + FetchValue.Response resp = client.execute(fv); + + TypeReference> tr = + new TypeReference>(){}; + GenericPojo gpf2 = resp.getValue(tr); + + assertNotNull(gpf2); + assertNotNull(gpf2.value); + assertEquals(gpf.value, gpf2.value); + assertNotNull(gpf2.list); + assertTrue(gpf.list.containsAll(gpf2.list)); + + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new RiakJacksonModule()); + String json = mapper.writeValueAsString(gpf2); + RiakObject ro = resp.getValue(RiakObject.class); + assertEquals(json, ro.getValue().toString()); + + } + + @Test + public void updateParameterizedTypeJSON() throws ExecutionException, InterruptedException, JsonProcessingException + { + RiakClient client = new RiakClient(cluster); + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + Location loc = new Location(ns, "test_ORM_key3"); + + MyUpdate update = new MyUpdate(); + TypeReference> tr = + new TypeReference>(){}; + + UpdateValue uv = new UpdateValue.Builder(loc) + .withUpdate(update, tr) + .withStoreOption(Option.RETURN_BODY, true) + .build(); + + UpdateValue.Response resp = client.execute(uv); + + GenericPojo gpi = resp.getValue(tr); + + assertNotNull(gpi); + assertNotNull(gpi.value); + assertEquals(1, gpi.value.intValue()); + + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new RiakJacksonModule()); + String json = mapper.writeValueAsString(gpi); + RiakObject ro = resp.getValue(RiakObject.class); + assertEquals(json, ro.getValue().toString()); + + resp = client.execute(uv); + gpi = resp.getValue(tr); + assertNotNull(gpi); + assertNotNull(gpi.value); + assertEquals(2, gpi.value.intValue()); + } + + @Test + public void updateAndResolveParameterizedTypeJSON() throws ExecutionException, InterruptedException + { + // We're back to allow_mult=false as default + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + StoreBucketPropsOperation op = + new StoreBucketPropsOperation.Builder(ns) + .withAllowMulti(true) + .build(); + cluster.execute(op); + op.get(); + + RiakClient client = new RiakClient(cluster); + Location loc = new Location(ns, "test_ORM_key4"); + + MyUpdate update = new MyUpdate(); + TypeReference> tr = + new TypeReference>(){}; + + UpdateValue uv = new UpdateValue.Builder(loc) + .withUpdate(update, tr) + .build(); + + client.execute(uv); + + // Create a sibling + GenericPojo gpi = update.apply(null); + StoreValue sv = + new StoreValue.Builder(gpi) + .withLocation(loc) + .build(); + + client.execute(sv); + + ConflictResolverFactory.getInstance().registerConflictResolver(tr, new MyResolver()); + + uv = new UpdateValue.Builder(loc) + .withUpdate(update, tr) + .withStoreOption(Option.RETURN_BODY, true) + .build(); + + UpdateValue.Response uvResp = client.execute(uv); + + gpi = uvResp.getValue(tr); + assertNotNull(gpi); + assertNotNull(gpi.value); + assertEquals(3, gpi.value.intValue()); + + + } + + @Test + public void updateAndResolveParameterizedTypeCustom() throws ExecutionException, InterruptedException + { + // We're back to allow_mult=false as default + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + StoreBucketPropsOperation op = + new StoreBucketPropsOperation.Builder(ns) + .withAllowMulti(true) + .build(); + cluster.execute(op); + op.get(); + + + RiakClient client = new RiakClient(cluster); + Location loc = new Location(ns, "test_ORM_key5"); + + TypeReference> tr = + new TypeReference>(){}; + + ConflictResolverFactory.getInstance().registerConflictResolver(tr, new MyResolver()); + ConverterFactory.getInstance().registerConverterForClass(tr, new MyConverter(tr.getType())); + + + MyUpdate update = new MyUpdate(); + + + UpdateValue uv = new UpdateValue.Builder(loc) + .withUpdate(update, tr) + .build(); + + client.execute(uv); + + // Create a sibling + GenericPojo gpi = update.apply(null); + StoreValue sv = + new StoreValue.Builder(gpi, tr) + .withLocation(loc) + .build(); + + client.execute(sv); + + uv = new UpdateValue.Builder(loc) + .withUpdate(update, tr) + .withStoreOption(Option.RETURN_BODY, true) + .build(); + + UpdateValue.Response uvResp = client.execute(uv); + + gpi = uvResp.getValue(tr); + assertNotNull(gpi); + assertNotNull(gpi.value); + assertEquals(3, gpi.value.intValue()); + + // Check to see that the custom conversion is right + RiakObject ro = uvResp.getValue(RiakObject.class); + assertEquals("3", ro.getValue().toString()); + } + + + @Test + public void updateAndResolveRawTypeJSON() throws ExecutionException, InterruptedException, JsonProcessingException + { + RiakClient client = new RiakClient(cluster); + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + Location loc = new Location(ns, "test_ORM_key6"); + ConflictResolverFactory.getInstance().registerConflictResolver(Foo.class, new MyFooResolver()); + MyFooUpdate update = new MyFooUpdate(); + + UpdateValue uv = new UpdateValue.Builder(loc) + .withUpdate(update) + .build(); + + client.execute(uv); + + // Create a sibling + Foo f = update.apply(null); + StoreValue sv = + new StoreValue.Builder(f) + .withLocation(loc) + .build(); + + client.execute(sv); + + uv = new UpdateValue.Builder(loc) + .withUpdate(update) + .withStoreOption(Option.RETURN_BODY, true) + .build(); + + UpdateValue.Response uvResp = client.execute(uv); + + f = uvResp.getValue(Foo.class); + assertNotNull(f); + assertEquals("Little bunny", f.fooValue); + + uv = new UpdateValue.Builder(loc) + .withUpdate(update) + .withStoreOption(Option.RETURN_BODY, true) + .build(); + + uvResp = client.execute(uv); + + f = uvResp.getValue(Foo.class); + assertNotNull(f); + assertEquals("Little bunny foo foo.", f.fooValue); + + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new RiakJacksonModule()); + String json = mapper.writeValueAsString(f); + RiakObject ro = uvResp.getValue(RiakObject.class); + assertEquals(json, ro.getValue().toString()); + + } + + @Test + public void updateAndResolveRawTypeCustom() throws ExecutionException, InterruptedException + { + RiakClient client = new RiakClient(cluster); + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + Location loc = new Location(ns, "test_ORM_key7"); + + ConflictResolverFactory.getInstance().registerConflictResolver(Foo.class, new MyFooResolver()); + ConverterFactory.getInstance().registerConverterForClass(Foo.class, new MyFooConverter()); + MyFooUpdate update = new MyFooUpdate(); + + UpdateValue uv = new UpdateValue.Builder(loc) + .withUpdate(update) + .build(); + + client.execute(uv); + + // Create a sibling + Foo f = update.apply(null); + StoreValue sv = + new StoreValue.Builder(f) + .withLocation(loc) + .build(); + + client.execute(sv); + + uv = new UpdateValue.Builder(loc) + .withUpdate(update) + .withStoreOption(Option.RETURN_BODY, true) + .build(); + + UpdateValue.Response uvResp = client.execute(uv); + + f = uvResp.getValue(Foo.class); + assertNotNull(f); + assertEquals("Little bunny", f.fooValue); + + uv = new UpdateValue.Builder(loc) + .withUpdate(update) + .withStoreOption(Option.RETURN_BODY, true) + .build(); + + uvResp = client.execute(uv); + + f = uvResp.getValue(Foo.class); + assertNotNull(f); + assertEquals("Little bunny foo foo.", f.fooValue); + + RiakObject ro = uvResp.getValue(RiakObject.class); + assertEquals("Little bunny foo foo.", ro.getValue().toString()); + } + + @Test + public void updateRiakObject() throws ExecutionException, InterruptedException + { + RiakClient client = new RiakClient(cluster); + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + Location loc = new Location(ns, "test_ORM_key9"); + UpdateValue uv = new UpdateValue.Builder(loc) + .withUpdate(new Update(){ + + @Override + public RiakObject apply(RiakObject original) + { + return new RiakObject().setValue(BinaryValue.create("value")); + } + }) + .withStoreOption(Option.RETURN_BODY, true) + .build(); + + UpdateValue.Response response = client.execute(uv); + RiakObject ro = response.getValues().get(0); + assertNotNull(ro.getVClock()); + } + + public static class GenericPojo + { + public T value; + public List list; + + @RiakVClock + public VClock vclock; + } + + public static class Foo + { + public String fooValue; + @RiakVClock + public VClock vclock; + + public Foo() {} + + public Foo(String v) + { + this.fooValue = v; + } + + @Override + public int hashCode() + { + int hash = 7; + hash = 89 * hash + (this.fooValue != null ? this.fooValue.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object o) + { + Foo other = (Foo)o; + return fooValue.equals(other.fooValue); + } + + } + + public static class MyUpdate extends Update> + { + + @Override + public GenericPojo apply(GenericPojo original) + { + if (original == null) + { + original = new GenericPojo(); + original.value = 1; + } + else + { + original.value++; + } + + return original; + } + } + + public static class MyResolver implements ConflictResolver> + { + + @Override + public GenericPojo resolve(List> objectList) throws UnresolvedConflictException + { + int total = 0; + for (GenericPojo gpi : objectList) + { + total += gpi.value; + } + GenericPojo newObj = new GenericPojo(); + newObj.value = total; + return newObj; + } + + } + + public static class MyConverter extends Converter> + { + + public MyConverter(Type t) + { + super(t); + } + + @Override + public GenericPojo toDomain(BinaryValue value, String contentType) throws ConversionException + { + GenericPojo gpi = new GenericPojo(); + gpi.value = Integer.valueOf(value.toString()); + return gpi; + } + + @Override + public ContentAndType fromDomain(GenericPojo domainObject) throws ConversionException + { + return new ContentAndType(BinaryValue.create(String.valueOf(domainObject.value)), "text/plain"); + } + + } + + public class MyFooUpdate extends Update + { + + @Override + public Foo apply(Foo original) + { + if (original == null) + { + original = new Foo("Little"); + } + else + { + if (original.fooValue.endsWith("Little")) + { + original.fooValue = original.fooValue + " bunny"; + } + else if (original.fooValue.endsWith("bunny")) + { + original.fooValue = original.fooValue + " foo foo."; + } + } + return original; + } + } + + public class MyFooResolver implements ConflictResolver + { + + @Override + public Foo resolve(List objectList) throws UnresolvedConflictException + { + if (objectList.isEmpty()) + { + return null; + } + else + { + int longest = 0; + for (int i = 0; i < objectList.size(); i++) + { + if (objectList.get(i).fooValue.length() > longest) + { + longest = i; + } + } + return objectList.get(longest); + } + } + } + + public static class MyFooConverter extends Converter + { + public MyFooConverter() + { + super(Foo.class); + } + @Override + public Foo toDomain(BinaryValue value, String contentType) throws ConversionException + { + return new Foo(value.toString()); + } + + @Override + public ContentAndType fromDomain(Foo domainObject) throws ConversionException + { + return new ContentAndType(BinaryValue.create(domainObject.fooValue), "text/plain"); + } + + } + +} diff --git a/src/test/java/com/basho/riak/client/api/commands/itest/ITestSearchMapReduce.java b/src/test/java/com/basho/riak/client/api/commands/itest/ITestSearchMapReduce.java new file mode 100644 index 000000000..d623487ca --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/itest/ITestSearchMapReduce.java @@ -0,0 +1,118 @@ +/* + * 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.commands.itest; + +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.core.operations.YzDeleteIndexOperation; +import com.basho.riak.client.core.operations.itest.ITestBase; +import com.basho.riak.client.api.commands.buckets.StoreBucketProperties; +import com.basho.riak.client.api.commands.search.StoreIndex; +import com.basho.riak.client.api.commands.kv.StoreValue; +import com.basho.riak.client.api.commands.mapreduce.MapReduce; +import com.basho.riak.client.api.commands.mapreduce.SearchMapReduce; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.query.functions.Function; +import com.basho.riak.client.core.query.search.YokozunaIndex; +import com.basho.riak.client.core.util.BinaryValue; +import java.util.concurrent.ExecutionException; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; +import org.junit.Assume; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class ITestSearchMapReduce extends ITestBase +{ + private final RiakClient client = new RiakClient(cluster); + private final String mrBucketName = bucketName.toString() + "_search_mr"; + + @Test + public void serachMR() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(testYokozuna); + Assume.assumeTrue(testBucketType); + + // First we have to create an index and attach it to a bucket + // and the 'default' bucket type can't be used for search + + YokozunaIndex index = new YokozunaIndex("test_mr_index"); + StoreIndex ssi = new StoreIndex.Builder(index).build(); + client.execute(ssi); + + assertTrue("Index not created", assureIndexExists("test_mr_index")); + + Namespace ns = new Namespace(bucketType.toString(), mrBucketName); + StoreBucketProperties sbp = new StoreBucketProperties.Builder(ns) + .withSearchIndex(index.getName()) + .build(); + client.execute(sbp); + + RiakObject ro = new RiakObject() + .setContentType("application/json") + .setValue(BinaryValue.create("{\"name_s\":\"Lion-o\", \"age_i\":30, \"leader_b\":true}")); + Location location = new Location(ns, "liono"); + StoreValue sv = new StoreValue.Builder(ro).withLocation(location).build(); + client.execute(sv); + + ro = new RiakObject() + .setContentType("application/json") + .setValue(BinaryValue.create("{\"name_s\":\"Cheetara\", \"age_i\":28, \"leader_b\":false}")); + location = new Location(ns, "cheetara"); + sv = new StoreValue.Builder(ro).withLocation(location).build(); + client.execute(sv); + + ro = new RiakObject() + .setContentType("application/json") + .setValue(BinaryValue.create("{\"name_s\":\"Snarf\", \"age_i\":43}")); + location = new Location(ns, "snarf"); + sv = new StoreValue.Builder(ro).withLocation(location).build(); + client.execute(sv); + + ro = new RiakObject() + .setContentType("application/json") + .setValue(BinaryValue.create("{\"name_s\":\"Panthro\", \"age_i\":36}")); + location = new Location(ns, "panthro"); + sv = new StoreValue.Builder(ro).withLocation(location).build(); + client.execute(sv); + + // Sleep some more or ... yeah, it doesn't work. + Thread.sleep(3000); + + SearchMapReduce smr = new SearchMapReduce.Builder() + .withIndex(index.getName()) + .withQuery("NOT leader_b:true") + .withMapPhase(Function.newAnonymousJsFunction("function(v) { return [1]; }"), false) + .withReducePhase(Function.newNamedJsFunction("Riak.reduceSum"), true) + .build(); + + MapReduce.Response mrResp = client.execute(smr); + + assertEquals(3, mrResp.getResultsFromAllPhases().get(0).asInt()); + + resetAndEmptyBucket(ns); + YzDeleteIndexOperation delOp = new YzDeleteIndexOperation.Builder("test_mr_index").build(); + cluster.execute(delOp); + delOp.await(); + assertTrue(delOp.isSuccess()); + } + +} diff --git a/src/test/java/com/basho/riak/client/api/commands/itest/ITestStoreValue.java b/src/test/java/com/basho/riak/client/api/commands/itest/ITestStoreValue.java new file mode 100644 index 000000000..bd678d87d --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/itest/ITestStoreValue.java @@ -0,0 +1,185 @@ +/* + * 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.commands.itest; + +import com.basho.riak.client.core.operations.itest.ITestBase; +import com.basho.riak.client.api.commands.kv.FetchValue; +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.api.annotations.RiakVClock; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.api.commands.kv.StoreValue.Option; +import com.basho.riak.client.api.commands.kv.StoreValue; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import static org.junit.Assert.*; +import org.junit.Assume; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class ITestStoreValue extends ITestBase +{ + @Test + public void simpleTest() throws ExecutionException, InterruptedException + { + RiakClient client = new RiakClient(cluster); + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + Location loc = new Location(ns, "test_store_key"); + Pojo pojo = new Pojo(); + pojo.value = "test store value"; + StoreValue sv = + new StoreValue.Builder(pojo).withLocation(loc) + .withOption(Option.RETURN_BODY, true) + .build(); + + StoreValue.Response resp = client.execute(sv); + + Pojo pojo2 = resp.getValue(Pojo.class); + + assertEquals(pojo.value, pojo2.value); + + FetchValue fv = new FetchValue.Builder(loc) + .build(); + + FetchValue.Response fResp = client.execute(fv); + + assertEquals(pojo.value, fResp.getValue(Pojo.class).value); + + } + + + + @Test + public void storeAnnotatedPojo() throws ExecutionException, InterruptedException + { + RiakClient client = new RiakClient(cluster); + + RiakAnnotatedPojo pojo = new RiakAnnotatedPojo(); + + pojo.bucketType = "default"; + pojo.bucketName = bucketName.toString(); + pojo.key = "test_store_key_3"; + pojo.contentType = null; // converter will set + pojo.value = "my value"; + + StoreValue sv = + new StoreValue.Builder(pojo) + .withOption(Option.RETURN_BODY, true) + .build(); + + StoreValue.Response resp = client.execute(sv); + + RiakAnnotatedPojo rap = resp.getValue(RiakAnnotatedPojo.class); + + assertNotNull(rap.bucketName); + assertEquals(pojo.bucketName, rap.bucketName); + assertNotNull(rap.key); + assertEquals(pojo.key, rap.key); + assertNotNull(rap.bucketType); + assertEquals(pojo.bucketType, rap.bucketType); + assertNotNull(rap.contentType); + assertEquals("application/json", rap.contentType); + assertNotNull(rap.vclock); + assertNotNull(rap.vtag); + assertNotNull(rap.lastModified); + assertNotNull(rap.value); + assertFalse(rap.deleted); + assertNotNull(rap.value); + assertEquals(pojo.value, rap.value); + } + + @Test + public void storeAnnotatedPojoWithIndexes() throws ExecutionException, InterruptedException + { + Assume.assumeTrue(test2i); + + RiakClient client = new RiakClient(cluster); + + RiakAnnotatedPojo pojo = new RiakAnnotatedPojo(); + + pojo.bucketType = "default"; + pojo.bucketName = bucketName.toString(); + pojo.key = "test_store_key_3"; + pojo.contentType = null; // converter will set + pojo.value = "my value"; + + Set emailAddys = new HashSet(); + emailAddys.add("roach@basho.com"); + + pojo.emailIndx = emailAddys; + pojo.userId = 1L; + + StoreValue sv = + new StoreValue.Builder(pojo) + .withOption(Option.RETURN_BODY, true) + .build(); + + StoreValue.Response resp = client.execute(sv); + + RiakAnnotatedPojo rap = resp.getValue(RiakAnnotatedPojo.class); + + assertTrue(rap.emailIndx.containsAll(emailAddys)); + assertEquals(rap.userId, pojo.userId); + + + } + + @Test + public void riakGeneratesKey() throws ExecutionException, InterruptedException + { + RiakClient client = new RiakClient(cluster); + + RiakAnnotatedPojo pojo = new RiakAnnotatedPojo(); + + pojo.bucketType = "default"; + pojo.bucketName = bucketName.toString(); + pojo.contentType = null; // converter will set + pojo.value = "my value"; + + StoreValue sv = + new StoreValue.Builder(pojo) + .withOption(Option.RETURN_BODY, true) + .build(); + + StoreValue.Response resp = client.execute(sv); + + RiakAnnotatedPojo rap = resp.getValue(RiakAnnotatedPojo.class); + + assertNotNull(rap.key); + assertFalse(rap.key.isEmpty()); + assertTrue(resp.hasGeneratedKey()); + assertNotNull(resp.hasGeneratedKey()); + assertFalse(resp.getGeneratedKey().length() == 0); + } + + public static class Pojo + { + @JsonProperty + String value; + + @RiakVClock + VClock vclock; + + } + +} diff --git a/src/test/java/com/basho/riak/client/api/commands/itest/ITestUpdateValue.java b/src/test/java/com/basho/riak/client/api/commands/itest/ITestUpdateValue.java new file mode 100644 index 000000000..e57a69d5f --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/itest/ITestUpdateValue.java @@ -0,0 +1,135 @@ +/* + * 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.commands.itest; + +import com.basho.riak.client.core.operations.itest.ITestBase; +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.api.annotations.RiakVClock; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.api.commands.kv.StoreValue.Option; +import com.basho.riak.client.api.commands.kv.UpdateValue; +import com.basho.riak.client.api.commands.kv.UpdateValue.Update; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.concurrent.ExecutionException; +import static org.junit.Assert.*; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class ITestUpdateValue extends ITestBase +{ + @Test + public void simpleTest() throws ExecutionException, InterruptedException + { + RiakClient client = new RiakClient(cluster); + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + Location loc = new Location(ns, "test_update_key1"); + UpdateValue uv = new UpdateValue.Builder(loc) + .withStoreOption(Option.RETURN_BODY, true) + .withUpdate(new UpdatePojo()) + .build(); + + UpdateValue.Response resp = client.execute(uv); + Pojo pojo = resp.getValue(Pojo.class); + assertEquals(1, pojo.value); + + resp = client.execute(uv); + pojo = resp.getValue(Pojo.class); + + assertEquals(2, pojo.value); + + } + + @Test + public void updateAnnotatedPojo() throws ExecutionException, InterruptedException + { + RiakClient client = new RiakClient(cluster); + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + Location loc = new Location(ns, "test_update_key2"); + UpdateValue uv = new UpdateValue.Builder(loc) + .withStoreOption(Option.RETURN_BODY, true) + .withUpdate(new UpdateAnnotatedPojo()) + .build(); + + UpdateValue.Response resp = client.execute(uv); + RiakAnnotatedPojo rap = resp.getValue(RiakAnnotatedPojo.class); + + assertNotNull(rap.bucketName); + assertEquals(ns.getBucketNameAsString(), rap.bucketName); + assertNotNull(rap.key); + assertEquals(loc.getKeyAsString(), rap.key); + assertNotNull(rap.bucketType); + assertEquals(ns.getBucketTypeAsString(), rap.bucketType); + assertNotNull(rap.contentType); + assertEquals("application/json", rap.contentType); + assertNotNull(rap.vclock); + assertNotNull(rap.vtag); + assertNotNull(rap.lastModified); + assertNotNull(rap.value); + assertFalse(rap.deleted); + assertNotNull(rap.value); + assertEquals("updated value", rap.value); + + + } + + public static class UpdatePojo extends Update + { + + @Override + public Pojo apply(Pojo original) + { + if (original == null) + { + original = new Pojo(); + } + + original.value++; + return original; + } + + } + + public static class Pojo + { + @JsonProperty + int value; + + @RiakVClock + VClock vclock; + } + + public static class UpdateAnnotatedPojo extends Update + { + + @Override + public RiakAnnotatedPojo apply(RiakAnnotatedPojo original) + { + if (original == null) + { + original = new RiakAnnotatedPojo(); + original.value = "updated value"; + } + return original; + } + + } +} diff --git a/src/test/java/com/basho/riak/client/api/commands/itest/RiakAnnotatedPojo.java b/src/test/java/com/basho/riak/client/api/commands/itest/RiakAnnotatedPojo.java new file mode 100644 index 000000000..a4d63aa1a --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/itest/RiakAnnotatedPojo.java @@ -0,0 +1,71 @@ +/* + * 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.itest; + +import com.basho.riak.client.api.annotations.RiakBucketName; +import com.basho.riak.client.api.annotations.RiakBucketType; +import com.basho.riak.client.api.annotations.RiakContentType; +import com.basho.riak.client.api.annotations.RiakIndex; +import com.basho.riak.client.api.annotations.RiakKey; +import com.basho.riak.client.api.annotations.RiakLastModified; +import com.basho.riak.client.api.annotations.RiakTombstone; +import com.basho.riak.client.api.annotations.RiakVClock; +import com.basho.riak.client.api.annotations.RiakVTag; +import com.basho.riak.client.api.cap.VClock; +import java.util.Set; + +/** + * + * @author Brian Roach + */ +public class RiakAnnotatedPojo +{ + @RiakKey + public String key; + + @RiakBucketName + public String bucketName; + + @RiakBucketType + public String bucketType; + + @RiakVClock + public VClock vclock; + + @RiakContentType + public String contentType; + + @RiakLastModified + public Long lastModified; + + @RiakVTag + public String vtag; + + @RiakTombstone + public boolean deleted; + + @RiakIndex(name="email") + public Set emailIndx; + + @RiakIndex(name="user_id") + public Long userId; + + public String value; + + + +} diff --git a/src/test/java/com/basho/riak/client/api/commands/mapreduce/FunctionPhaseSerializerTest.java b/src/test/java/com/basho/riak/client/api/commands/mapreduce/FunctionPhaseSerializerTest.java new file mode 100644 index 000000000..4aaedf067 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/mapreduce/FunctionPhaseSerializerTest.java @@ -0,0 +1,69 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.api.commands.mapreduce.MapPhase; +import com.basho.riak.client.api.commands.mapreduce.ReducePhase; +import com.basho.riak.client.api.commands.mapreduce.PhaseFunction; +import com.basho.riak.client.api.commands.mapreduce.PhaseFunctionSerializer; +import com.basho.riak.client.api.commands.mapreduce.FunctionPhase; +import com.basho.riak.client.api.commands.mapreduce.FunctionPhaseSerializer; +import com.basho.riak.client.core.query.functions.Function; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringWriter; + +import static junit.framework.Assert.assertEquals; + +public class FunctionPhaseSerializerTest +{ + + private StringWriter out; + private JsonGenerator jg; + + @Before + public void init() throws IOException + { + this.out = new StringWriter(); + this.jg = new JsonFactory().createGenerator(out); + + ObjectMapper objectMapper = new ObjectMapper(); + SimpleModule specModule = new SimpleModule("SpecModule", Version.unknownVersion()); + specModule.addSerializer(PhaseFunction.class, new PhaseFunctionSerializer()); + specModule.addSerializer(FunctionPhase.class, new FunctionPhaseSerializer()); + objectMapper.registerModule(specModule); + + jg.setCodec(objectMapper); + + } + + @Test + public void testSerializeMapPhase() throws IOException + { + + Function function = Function.newNamedJsFunction("the_func"); + MapPhase phase = new MapPhase(function, "Arg", true); + + jg.writeObject(phase); + + assertEquals("{\"map\":{\"language\":\"javascript\",\"name\":\"the_func\",\"keep\":true,\"arg\":\"Arg\"}}", out.toString()); + + } + + @Test + public void testSerializeReducePhase() throws IOException + { + Function function = Function.newNamedJsFunction("the_func"); + ReducePhase phase = new ReducePhase(function, "Arg", true); + + jg.writeObject(phase); + + assertEquals("{\"reduce\":{\"language\":\"javascript\",\"name\":\"the_func\",\"keep\":true,\"arg\":\"Arg\"}}", out.toString()); + } + +} diff --git a/src/test/java/com/basho/riak/client/api/commands/mapreduce/InputSerializerTest.java b/src/test/java/com/basho/riak/client/api/commands/mapreduce/InputSerializerTest.java new file mode 100644 index 000000000..a27ba790f --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/mapreduce/InputSerializerTest.java @@ -0,0 +1,193 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.api.commands.mapreduce.BucketKeyInputSerializer; +import com.basho.riak.client.api.commands.mapreduce.SearchInputSerializer; +import com.basho.riak.client.api.commands.mapreduce.BucketKeyInput; +import com.basho.riak.client.api.commands.mapreduce.BucketInput; +import com.basho.riak.client.api.commands.mapreduce.IndexInputSerializer; +import com.basho.riak.client.api.commands.mapreduce.SearchInput; +import com.basho.riak.client.api.commands.mapreduce.IndexInput; +import com.basho.riak.client.api.commands.mapreduce.BucketInputSerializer; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.api.commands.mapreduce.filters.EndsWithFilter; +import com.basho.riak.client.api.commands.mapreduce.filters.KeyFilter; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; + +import static junit.framework.Assert.assertEquals; + +public class InputSerializerTest +{ + private StringWriter out; + private JsonGenerator jg; + + @Before + public void init() throws IOException + { + this.out = new StringWriter(); + this.jg = new JsonFactory().createGenerator(out); + + ObjectMapper objectMapper = new ObjectMapper(); + SimpleModule specModule = new SimpleModule("SpecModule", Version.unknownVersion()); + specModule.addSerializer(BucketInput.class, new BucketInputSerializer()); + specModule.addSerializer(SearchInput.class, new SearchInputSerializer()); + specModule.addSerializer(BucketKeyInput.class, new BucketKeyInputSerializer()); + specModule.addSerializer(IndexInput.class, new IndexInputSerializer()); + objectMapper.registerModule(specModule); + + jg.setCodec(objectMapper); + + + } + + @Test + public void testSearchInputSerializer() throws IOException + { + SearchInput input = new SearchInput("index", "query"); + + jg.writeObject(input); + + assertEquals("{\"module\":\"yokozuna\",\"function\":\"mapred_search\",\"arg\":[\"index\",\"query\"]}", out.toString()); + + } + + @Test + public void testBucketInputSerializer() throws IOException + { + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, "bucket"); + BucketInput input = new BucketInput(ns, null); + + jg.writeObject(input); + assertEquals("\"bucket\"", out.toString()); + } + + @Test + public void testBucketInputSerializerWithFilter() throws IOException + { + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, "bucket"); + ArrayList filters = new ArrayList(); + filters.add(new EndsWithFilter("dave")); + BucketInput input = new BucketInput(ns, filters); + + jg.writeObject(input); + + assertEquals("{\"bucket\":\"bucket\",\"key_filters\":[[\"ends_with\",\"dave\"]]}", out.toString()); + + } + + @Test public void testBucketInputSerializerWithType() throws IOException + { + Namespace ns = new Namespace("type", "bucket"); + BucketInput input = new BucketInput(ns, null); + jg.writeObject(input); + assertEquals("[\"type\",\"bucket\"]", out.toString()); + } + + @Test + public void testBucketInputSerializerWithTypeAndFilter() throws IOException + { + + Namespace ns = new Namespace("type", "bucket"); + ArrayList filters = new ArrayList(); + filters.add(new EndsWithFilter("dave")); + BucketInput input = new BucketInput(ns, filters); + + jg.writeObject(input); + + assertEquals("{\"bucket\":[\"type\",\"bucket\"],\"key_filters\":[[\"ends_with\",\"dave\"]]}", out.toString()); + + } + + + + @Test + public void testSerializeBucketKeyInput() throws IOException + { + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, "bucket"); + ArrayList inputs = new ArrayList(); + inputs.add(new BucketKeyInput.IndividualInput(new Location(ns, "key"))); + inputs.add(new BucketKeyInput.IndividualInput(new Location(ns, "key"), "data")); + BucketKeyInput input = new BucketKeyInput(inputs); + + jg.writeObject(input); + + assertEquals("[[\"bucket\",\"key\",\"\"],[\"bucket\",\"key\",\"data\"]]", out.toString()); + + + } + + @Test + public void testSerializeBucketKeyInputWithType() throws IOException + { + Namespace ns = new Namespace("type", "bucket"); + ArrayList inputs = new ArrayList(); + inputs.add(new BucketKeyInput.IndividualInput(new Location(ns, "key"))); + inputs.add(new BucketKeyInput.IndividualInput(new Location(ns, "key"), "data")); + BucketKeyInput input = new BucketKeyInput(inputs); + + jg.writeObject(input); + + assertEquals("[[\"bucket\",\"key\",\"\",\"type\"],[\"bucket\",\"key\",\"data\",\"type\"]]", out.toString()); + + + } + + @Test + public void testSearializeIndexInputMatch() throws Exception + { + Namespace ns = new Namespace("bucket"); + IndexInput.MatchCriteria criteria = new IndexInput.MatchCriteria("dave"); + IndexInput input = new IndexInput(ns, "index_int", criteria); + + jg.writeObject(input); + assertEquals("{\"bucket\":\"bucket\",\"index\":\"index_int\",\"key\":\"dave\"}", out.toString()); + } + + @Test + public void testSearializeIndexInputMatchWithType() throws Exception + { + Namespace ns = new Namespace("type", "bucket"); + IndexInput.MatchCriteria criteria = new IndexInput.MatchCriteria("dave"); + IndexInput input = new IndexInput(ns, "index_int", criteria); + + jg.writeObject(input); + + assertEquals("{\"bucket\":[\"type\",\"bucket\"],\"index\":\"index_int\",\"key\":\"dave\"}", out.toString()); + } + + @Test + public void testSearializeIndexInputRange() throws Exception + { + Namespace ns = new Namespace("bucket"); + IndexInput.RangeCriteria criteria = new IndexInput.RangeCriteria(1, 2); + IndexInput input = new IndexInput(ns, "index_int", criteria); + + jg.writeObject(input); + + assertEquals("{\"bucket\":\"bucket\",\"index\":\"index_int\",\"start\":1,\"end\":2}", out.toString()); + } + + @Test + public void testSearializeIndexInputRangeWithType() throws Exception + { + Namespace ns = new Namespace("type","bucket"); + IndexInput.RangeCriteria criteria = new IndexInput.RangeCriteria(1, 2); + IndexInput input = new IndexInput(ns, "index_int", criteria); + + jg.writeObject(input); + + assertEquals("{\"bucket\":[\"type\",\"bucket\"],\"index\":\"index_int\",\"start\":1,\"end\":2}", out.toString()); + } + +} diff --git a/src/test/java/com/basho/riak/client/api/commands/mapreduce/LinkPhaseSerializerTest.java b/src/test/java/com/basho/riak/client/api/commands/mapreduce/LinkPhaseSerializerTest.java new file mode 100644 index 000000000..77dc1f19b --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/mapreduce/LinkPhaseSerializerTest.java @@ -0,0 +1,49 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.api.commands.mapreduce.LinkPhase; +import com.basho.riak.client.api.commands.mapreduce.LinkPhaseSerializer; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringWriter; + +import static org.junit.Assert.assertEquals; + +public class LinkPhaseSerializerTest +{ + private StringWriter out; + private JsonGenerator jg; + + @Before + public void init() throws IOException + { + this.out = new StringWriter(); + this.jg = new JsonFactory().createGenerator(out); + + ObjectMapper objectMapper = new ObjectMapper(); + SimpleModule specModule = new SimpleModule("SpecModule", Version.unknownVersion()); + specModule.addSerializer(LinkPhase.class, new LinkPhaseSerializer()); + objectMapper.registerModule(specModule); + + jg.setCodec(objectMapper); + + } + + @Test + public void testSerializeLinkPhase() throws IOException + { + LinkPhase phase = new LinkPhase("bucket", "tag", true); + + jg.writeObject(phase); + + assertEquals("{\"link\":{\"bucket\":\"bucket\",\"tag\":\"tag\"}}", out.toString()); + } + + +} diff --git a/src/test/java/com/basho/riak/client/api/commands/mapreduce/MapReduceSpecSerializerTest.java b/src/test/java/com/basho/riak/client/api/commands/mapreduce/MapReduceSpecSerializerTest.java new file mode 100644 index 000000000..39af88381 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/mapreduce/MapReduceSpecSerializerTest.java @@ -0,0 +1,89 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.api.commands.mapreduce.MapPhase; +import com.basho.riak.client.api.commands.mapreduce.BucketInput; +import com.basho.riak.client.api.commands.mapreduce.IndexInputSerializer; +import com.basho.riak.client.api.commands.mapreduce.PhaseFunctionSerializer; +import com.basho.riak.client.api.commands.mapreduce.FunctionPhase; +import com.basho.riak.client.api.commands.mapreduce.LinkPhaseSerializer; +import com.basho.riak.client.api.commands.mapreduce.IndexInput; +import com.basho.riak.client.api.commands.mapreduce.FunctionPhaseSerializer; +import com.basho.riak.client.api.commands.mapreduce.MapReducePhase; +import com.basho.riak.client.api.commands.mapreduce.BucketKeyInputSerializer; +import com.basho.riak.client.api.commands.mapreduce.SearchInputSerializer; +import com.basho.riak.client.api.commands.mapreduce.ReducePhase; +import com.basho.riak.client.api.commands.mapreduce.BucketKeyInput; +import com.basho.riak.client.api.commands.mapreduce.PhaseFunction; +import com.basho.riak.client.api.commands.mapreduce.MapReduceSpec; +import com.basho.riak.client.api.commands.mapreduce.LinkPhase; +import com.basho.riak.client.api.commands.mapreduce.SearchInput; +import com.basho.riak.client.api.commands.mapreduce.BucketInputSerializer; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.api.commands.mapreduce.filters.KeyFilter; +import com.basho.riak.client.core.query.functions.Function; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import junit.framework.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; + +public class MapReduceSpecSerializerTest +{ + + private StringWriter out; + private JsonGenerator jg; + + @Before + public void init() throws IOException + { + this.out = new StringWriter(); + this.jg = new JsonFactory().createGenerator(out); + + ObjectMapper objectMapper = new ObjectMapper(); + SimpleModule specModule = new SimpleModule("SpecModule", Version.unknownVersion()); + specModule.addSerializer(PhaseFunction.class, new PhaseFunctionSerializer()); + specModule.addSerializer(LinkPhase.class, new LinkPhaseSerializer()); + specModule.addSerializer(FunctionPhase.class, new FunctionPhaseSerializer()); + specModule.addSerializer(BucketInput.class, new BucketInputSerializer()); + specModule.addSerializer(SearchInput.class, new SearchInputSerializer()); + specModule.addSerializer(BucketKeyInput.class, new BucketKeyInputSerializer()); + specModule.addSerializer(IndexInput.class, new IndexInputSerializer()); + objectMapper.registerModule(specModule); + + jg.setCodec(objectMapper); + + } + + + @Test + public void testSerializeMapReduceSpec() throws IOException + { + + ArrayList phases = new ArrayList(); + phases.add(new MapPhase(Function.newNamedJsFunction("map_func"))); + phases.add(new ReducePhase(Function.newNamedJsFunction("reduce_func"))); + phases.add(new LinkPhase("bucket", "tag")); + BucketInput input = new BucketInput(new Namespace("bucket"), Collections.emptyList()); + + MapReduceSpec spec = new MapReduceSpec(input, phases, 1000L); + + jg.writeObject(spec); + + Assert.assertEquals("{\"inputs\":\"bucket\"," + + "\"timeout\":1000,\"query\":" + + "[{\"map\":{\"language\":\"javascript\",\"name\":\"map_func\",\"keep\":false,\"arg\":null}}," + + "{\"reduce\":{\"language\":\"javascript\",\"name\":\"reduce_func\",\"keep\":false,\"arg\":null}}," + + "{\"link\":{\"bucket\":\"bucket\",\"tag\":\"tag\"}}]}", out.toString()); + + } + +} diff --git a/src/test/java/com/basho/riak/client/api/commands/mapreduce/PhaseFunctionSerializerTest.java b/src/test/java/com/basho/riak/client/api/commands/mapreduce/PhaseFunctionSerializerTest.java new file mode 100644 index 000000000..24dd0e685 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/mapreduce/PhaseFunctionSerializerTest.java @@ -0,0 +1,73 @@ +package com.basho.riak.client.api.commands.mapreduce; + +import com.basho.riak.client.api.commands.mapreduce.PhaseFunction; +import com.basho.riak.client.api.commands.mapreduce.PhaseFunctionSerializer; +import com.basho.riak.client.core.query.functions.Function; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringWriter; + +import static org.junit.Assert.assertEquals; + +public class PhaseFunctionSerializerTest +{ + + private StringWriter out; + private JsonGenerator jg; + + @Before + public void init() throws IOException + { + this.out = new StringWriter(); + this.jg = new JsonFactory().createGenerator(out); + + ObjectMapper objectMapper = new ObjectMapper(); + SimpleModule specModule = new SimpleModule("SpecModule", Version.unknownVersion()); + specModule.addSerializer(PhaseFunction.class, new PhaseFunctionSerializer()); + objectMapper.registerModule(specModule); + + jg.setCodec(objectMapper); + + } + + @Test + public void testSerializeErlangFunction() throws IOException + { + Function function = Function.newErlangFunction("mod", "fun"); + PhaseFunction phaseFunction = new PhaseFunction(function, true); + + jg.writeObject(phaseFunction); + + assertEquals("{\"language\":\"erlang\",\"module\":\"mod\",\"function\":\"fun\",\"keep\":true}", out.toString()); + } + + @Test + public void testSerializeAnonJsFunction() throws IOException + { + Function function = Function.newAnonymousJsFunction("function() {}"); + PhaseFunction phaseFunction = new PhaseFunction(function, true); + + jg.writeObject(phaseFunction); + + assertEquals("{\"language\":\"javascript\",\"source\":\"function() {}\",\"keep\":true}", out.toString()); + } + + @Test + public void testSerializeNamedJsFunction() throws IOException + { + Function function = Function.newNamedJsFunction("the_func"); + PhaseFunction phaseFunction = new PhaseFunction(function, true); + + jg.writeObject(phaseFunction); + + assertEquals("{\"language\":\"javascript\",\"name\":\"the_func\",\"keep\":true}", out.toString()); + } + +} diff --git a/src/test/java/com/basho/riak/client/api/commands/mapreduce/filters/KeyFilterTest.java b/src/test/java/com/basho/riak/client/api/commands/mapreduce/filters/KeyFilterTest.java new file mode 100644 index 000000000..a71105aba --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/commands/mapreduce/filters/KeyFilterTest.java @@ -0,0 +1,263 @@ +package com.basho.riak.client.api.commands.mapreduce.filters; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringWriter; + +import static junit.framework.Assert.assertEquals; + +public class KeyFilterTest +{ + + private ObjectWriter writer; + private StringWriter serialized; + + @Before + public void init() + { + ObjectMapper mapper = new ObjectMapper(); + writer = mapper.writer(); + serialized = new StringWriter(); + } + + @Test + public void testGreaterThanInt() throws IOException + { + GreaterThanFilter filter = + new GreaterThanFilter(1); + + writer.writeValue(serialized, filter); + assertEquals("[\"greater_than\",1]", serialized.toString()); + + } + + @Test + public void testLessThanInt() throws Exception + { + LessThanFilter filter = + new LessThanFilter(1); + + writer.writeValue(serialized, filter); + assertEquals("[\"less_than\",1]", serialized.toString()); + } + + @Test + public void testGreaterThanEqInt() throws IOException + { + GreaterThanOrEqualFilter filter = + new GreaterThanOrEqualFilter(1); + + writer.writeValue(serialized, filter); + assertEquals("[\"greater_than_eq\",1]", serialized.toString()); + + } + + @Test + public void testLessThanEqInt() throws Exception + { + LessThanOrEqualFilter filter = + new LessThanOrEqualFilter(1); + + writer.writeValue(serialized, filter); + assertEquals("[\"less_than_eq\",1]", serialized.toString()); + } + + @Test + public void testBetweenInt() throws Exception + { + BetweenFilter filter = + new BetweenFilter(1, 2); + + writer.writeValue(serialized, filter); + assertEquals("[\"between\",1,2]", serialized.toString()); + } + + @Test + public void testMatches() throws IOException + { + MatchFilter filter = new MatchFilter("string1"); + + writer.writeValue(serialized, filter); + assertEquals("[\"matches\",\"string1\"]", serialized.toString()); + } + + @Test + public void testNeqInt() throws IOException + { + NotEqualToFilter filter = + new NotEqualToFilter(1); + + writer.writeValue(serialized, filter); + assertEquals("[\"neq\",1]", serialized.toString()); + } + + @Test + public void testEqInt() throws IOException + { + EqualToFilter filter = + new EqualToFilter(1); + + writer.writeValue(serialized, filter); + assertEquals("[\"eq\",1]", serialized.toString()); + } + + @Test + public void testSetMemberInt() throws Exception + { + SetMemberFilter filter = + new SetMemberFilter(1, 2, 3); + + writer.writeValue(serialized, filter); + assertEquals("[\"set_member\",1,2,3]", serialized.toString()); + } + + @Test + public void testSimilarTo() throws IOException + { + SimilarToFilter filter = + new SimilarToFilter("string", 2); + + writer.writeValue(serialized, filter); + assertEquals("[\"similar_to\",\"string\",2]", serialized.toString()); + } + + @Test + public void testStartsWith() throws IOException + { + StartsWithFilter filter = + new StartsWithFilter("string"); + + writer.writeValue(serialized, filter); + assertEquals("[\"starts_with\",\"string\"]", serialized.toString()); + } + + @Test + public void testEndsWith() throws IOException + { + EndsWithFilter filter = + new EndsWithFilter("string"); + + writer.writeValue(serialized, filter); + assertEquals("[\"ends_with\",\"string\"]", serialized.toString()); + } + + @Test + public void testAnd() throws IOException + { + + LogicalAndFilter filter = + new LogicalAndFilter(new EmptyFilter(), new EmptyFilter()); + + writer.writeValue(serialized, filter); + assertEquals("[\"and\",[[\"empty\"]],[[\"empty\"]]]", serialized.toString()); + } + + @Test + public void testOr() throws IOException + { + + LogicalOrFilter filter = + new LogicalOrFilter(new EmptyFilter(), new EmptyFilter()); + + writer.writeValue(serialized, filter); + assertEquals("[\"or\",[[\"empty\"]],[[\"empty\"]]]", serialized.toString()); + } + + @Test + public void testNot() throws IOException + { + + LogicalNotFilter filter = + new LogicalNotFilter(new EmptyFilter(), new EmptyFilter()); + + writer.writeValue(serialized, filter); + assertEquals("[\"not\",[[\"empty\"]],[[\"empty\"]]]", serialized.toString()); + } + + @Test + public void testIntToString() throws Exception + { + IntToStringFilter filter = new IntToStringFilter(); + + writer.writeValue(serialized, filter); + assertEquals("[\"int_to_string\"]", serialized.toString()); + } + + @Test + public void testStringToInt() throws Exception + { + StringToIntFilter filter = new StringToIntFilter(); + + writer.writeValue(serialized, filter); + assertEquals("[\"string_to_int\"]", serialized.toString()); + } + + @Test + public void testFloatToString() throws Exception + { + FloatToStringFilter filter = new FloatToStringFilter(); + + writer.writeValue(serialized, filter); + assertEquals("[\"float_to_string\"]", serialized.toString()); + } + + @Test + public void testStringToFloat() throws Exception + { + StringToFloatFilter filter = new StringToFloatFilter(); + + writer.writeValue(serialized, filter); + assertEquals("[\"string_to_float\"]", serialized.toString()); + } + + @Test + public void testToUpper() throws Exception + { + ToUpperFilter filter = new ToUpperFilter(); + + writer.writeValue(serialized, filter); + assertEquals("[\"to_upper\"]", serialized.toString()); + } + + @Test + public void testToLower() throws Exception + { + ToLowerFilter filter = new ToLowerFilter(); + + writer.writeValue(serialized, filter); + assertEquals("[\"to_lower\"]", serialized.toString()); + } + + @Test + public void testTokenize() throws Exception + { + TokenizeFilter filter = new TokenizeFilter("-", 2); + + writer.writeValue(serialized, filter); + assertEquals("[\"tokenize\",\"-\",2]", serialized.toString()); + } + + @Test + public void testUrlDecode() throws Exception + { + UrlDecodeFilter filter = new UrlDecodeFilter(); + + writer.writeValue(serialized, filter); + assertEquals("[\"urldecode\"]", serialized.toString()); + } + + private static class EmptyFilter extends KeyFilter + { + + protected EmptyFilter() + { + super("empty"); + } + + } + +} diff --git a/src/test/java/com/basho/riak/client/api/convert/ConverterFactoryTest.java b/src/test/java/com/basho/riak/client/api/convert/ConverterFactoryTest.java new file mode 100644 index 000000000..a932a2f00 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/convert/ConverterFactoryTest.java @@ -0,0 +1,176 @@ +/* + * 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.convert; + +import com.basho.riak.client.api.convert.Converter; +import com.basho.riak.client.api.convert.StringConverter; +import com.basho.riak.client.api.convert.ConverterFactory; +import com.basho.riak.client.api.convert.JSONConverter; +import com.basho.riak.client.api.convert.ConversionException; +import com.basho.riak.client.api.convert.PassThroughConverter; +import com.basho.riak.client.api.annotations.RiakBucketName; +import com.basho.riak.client.api.annotations.RiakVClock; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.api.convert.Converter.OrmExtracted; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.type.TypeReference; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public class ConverterFactoryTest +{ + + @Before + public void setup() + { + ConverterFactory factory = ConverterFactory.getInstance(); + factory.unregisterConverterForClass(Pojo.class); + //factory.setDefaultConverter(JSONConverter.class); + } + + @Test + public void getDefaultConverter() + { + ConverterFactory factory = ConverterFactory.getInstance(); + //TypeReference tr = new TypeReference(){}; + Converter converter = factory.getConverter(Pojo.class); + + assertTrue(converter instanceof JSONConverter); + + Pojo pojo = new Pojo(); + pojo.foo = "foo_value"; + pojo.bar = "bar_value"; + + + OrmExtracted orm = converter.fromDomain(pojo , null, null); + RiakObject ro = orm.getRiakObject(); + + assertNotNull(ro.getValue()); + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, "bucket"); + Pojo pojo2 = converter.toDomain(ro, new Location(ns, "key")); + + assertEquals(pojo.foo, pojo2.foo); + assertEquals(pojo.bar, pojo2.bar); + + } + + @Test + public void stringConverter() + { + ConverterFactory factory = ConverterFactory.getInstance(); + Converter converter = factory.getConverter(String.class); + + assertTrue(converter instanceof StringConverter); + } + + @Test + public void riakObjectConverter() + { + ConverterFactory factory = ConverterFactory.getInstance(); + Converter converter = factory.getConverter(RiakObject.class); + + assertTrue(converter instanceof PassThroughConverter); + + RiakObject o = new RiakObject(); + + RiakObject o2 = converter.toDomain(o, null); + assertEquals(o, o2); + + OrmExtracted orm = converter.fromDomain(o, null, null); + assertEquals(o, orm.getRiakObject()); + + + } + + @Test + public void registerConverterClass() + { + ConverterFactory factory = ConverterFactory.getInstance(); + factory.registerConverterForClass(Pojo.class, new MyConverter()); + + Converter converter = factory.getConverter(Pojo.class); + + assertTrue(converter instanceof MyConverter); + + } + + @Test + public void registerConverterInstance() + { + ConverterFactory factory = ConverterFactory.getInstance(); + MyConverter converter = new MyConverter(); + factory.registerConverterForClass(Pojo.class, converter); + + Converter converter2 = factory.getConverter(Pojo.class); + assertTrue(converter2 instanceof MyConverter); + assertEquals(converter, converter2); + + + } + + public static class MyConverter extends Converter + { + + public MyConverter() + { + super(new TypeReference(){}.getType()); + } + + + + @Override + public Pojo toDomain(BinaryValue value, String contentType) throws ConversionException + { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ContentAndType fromDomain(Pojo domainObject) throws ConversionException + { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + } + + public static class Pojo + { + public Pojo(){} + + @RiakBucketName + String bucketName = "my_bucket"; + + @RiakVClock + VClock vclock; + + @JsonProperty + String foo; + @JsonProperty + String bar; + } + +} diff --git a/src/test/java/com/basho/riak/client/api/convert/JsonConverterTest.java b/src/test/java/com/basho/riak/client/api/convert/JsonConverterTest.java new file mode 100644 index 000000000..520faab64 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/convert/JsonConverterTest.java @@ -0,0 +1,561 @@ +/* + * 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.convert; + +import com.basho.riak.client.api.convert.JSONConverter; +import com.basho.riak.client.api.annotations.RiakBucketName; +import com.basho.riak.client.api.annotations.RiakContentType; +import com.basho.riak.client.api.annotations.RiakKey; +import com.basho.riak.client.api.annotations.RiakTombstone; +import com.basho.riak.client.api.annotations.RiakLastModified; +import com.basho.riak.client.api.annotations.RiakVClock; +import com.basho.riak.client.api.annotations.RiakLinks; +import com.basho.riak.client.api.annotations.RiakVTag; +import com.basho.riak.client.api.annotations.RiakBucketType; +import com.basho.riak.client.api.annotations.RiakIndex; +import com.basho.riak.client.api.annotations.RiakUsermeta; +import com.basho.riak.client.api.cap.BasicVClock; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.api.convert.Converter.OrmExtracted; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.query.links.RiakLink; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; +import static org.junit.Assert.*; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class JsonConverterTest +{ + private final VClock vclock = new BasicVClock(new byte[0]); + private final ObjectMapper mapper = new ObjectMapper(); + + @Test + public void testRiakAnnotatedtFieldsExcludedFromJson() throws IOException + { + + PojoWithRiakFields pojo = new PojoWithRiakFields(); + JSONConverter jc = + new JSONConverter(new TypeReference(){}); + + RiakObject o = jc.fromDomain(pojo, null, null).getRiakObject(); + + String json = o.getValue().toString(); + assertFalse(fieldExistsInJson(json,"key")); + assertFalse(fieldExistsInJson(json,"bucketName")); + assertFalse(fieldExistsInJson(json,"bucketType")); + assertFalse(fieldExistsInJson(json,"metadata")); + assertFalse(fieldExistsInJson(json,"index")); + assertFalse(fieldExistsInJson(json,"links")); + assertFalse(fieldExistsInJson(json,"vclock")); + assertFalse(fieldExistsInJson(json,"tombstone")); + + assertTrue(fieldExistsInJson(json, "value")); + + + + } + + @Test + public void testRiakAnnotatedtFieldsIncludedInJson() throws IOException + { + + PojoWithRiakFieldsIncluded pojo = new PojoWithRiakFieldsIncluded(); + JSONConverter jc = + new JSONConverter(new TypeReference(){}); + RiakObject o = jc.fromDomain(pojo, null, null).getRiakObject(); + + String json = o.getValue().toString(); + assertTrue(fieldExistsInJson(json,"key")); + assertTrue(fieldExistsInJson(json,"bucketName")); + assertTrue(fieldExistsInJson(json,"bucketType")); + assertTrue(fieldExistsInJson(json,"metadata")); + assertTrue(fieldExistsInJson(json,"index")); + assertTrue(fieldExistsInJson(json,"links")); + assertTrue(fieldExistsInJson(json,"vclock")); + assertTrue(fieldExistsInJson(json,"tombstone")); + + assertTrue(fieldExistsInJson(json, "value")); + + + } + + @Test + public void testRiakAnnotatedMethodsExcludedFromJson() throws IOException + { + PojoWithRiakMethodsExcluded pojo = new PojoWithRiakMethodsExcluded(); + JSONConverter jc = + new JSONConverter(new TypeReference(){}); + + RiakObject o = jc.fromDomain(pojo, null, null).getRiakObject(); + + String json = o.getValue().toString(); + assertFalse(fieldExistsInJson(json,"key")); + assertFalse(fieldExistsInJson(json,"bucketName")); + assertFalse(fieldExistsInJson(json,"bucketType")); + assertFalse(fieldExistsInJson(json,"usermeta")); + assertFalse(fieldExistsInJson(json,"index")); + assertFalse(fieldExistsInJson(json,"links")); + assertFalse(fieldExistsInJson(json,"vclock")); + assertFalse(fieldExistsInJson(json,"tombstone")); + + assertTrue(fieldExistsInJson(json, "value")); + + } + + @Test + public void testRiakAnnotatedMethodsIncludedInJson() throws IOException + { + PojoWithRiakMethodsIncluded pojo = new PojoWithRiakMethodsIncluded(); + JSONConverter jc = + new JSONConverter(new TypeReference(){}); + + RiakObject o = jc.fromDomain(pojo, null, null).getRiakObject(); + + String json = o.getValue().toString(); + assertTrue(fieldExistsInJson(json,"key")); + assertTrue(fieldExistsInJson(json,"bucketName")); + assertTrue(fieldExistsInJson(json,"bucketType")); + assertTrue(fieldExistsInJson(json,"usermeta")); + assertTrue(fieldExistsInJson(json,"index")); + assertTrue(fieldExistsInJson(json,"links")); + assertTrue(fieldExistsInJson(json,"vclock")); + assertTrue(fieldExistsInJson(json,"tombstone")); + + assertTrue(fieldExistsInJson(json, "value")); + + + } + + @Test + public void convertBackAndForth() + { + EmptyPojoWithRiakFields pojo = new EmptyPojoWithRiakFields(); + + pojo.key = "some_key"; + pojo.bucketName = "some_bucket"; + pojo.bucketType = "some_type"; + pojo.value = "some_value"; + pojo.metadata = + new HashMap(){{ put("metaKey", "metaValue");}}; + pojo.index = new HashSet(){{ add("bob@gmail.com");}}; + pojo.links = new LinkedList(){{ add(new RiakLink("bucket","key","tag"));}}; + pojo.vclock = vclock; + pojo.contentType = RiakObject.DEFAULT_CONTENT_TYPE; + + JSONConverter jc = + new JSONConverter(new TypeReference(){}); + OrmExtracted orm = jc.fromDomain(pojo, null, null); + + RiakObject riakObject = orm.getRiakObject(); + riakObject.setLastModified(123); + riakObject.setVTag("vtag"); + + Location loc = new Location(orm.getNamespace(), orm.getKey()); + EmptyPojoWithRiakFields convertedPojo = jc.toDomain(riakObject, loc); + + assertEquals(pojo.key, convertedPojo.key); + assertEquals(pojo.bucketName, convertedPojo.bucketName); + assertEquals(pojo.bucketType, convertedPojo.bucketType); + assertEquals(pojo.value, convertedPojo.value); + assertTrue(pojo.index.containsAll(convertedPojo.index)); + + for (String key : pojo.metadata.keySet()) + { + assertTrue(convertedPojo.metadata.containsKey(key)); + assertEquals(convertedPojo.metadata.get(key), pojo.metadata.get(key)); + } + + assertTrue(convertedPojo.links.containsAll(pojo.links)); + assertEquals(pojo.vclock, convertedPojo.vclock); + assertEquals(123, convertedPojo.lastModified.longValue()); + assertEquals("vtag", convertedPojo.vtag); + + } + + + private boolean fieldExistsInJson(String json, String fieldname) throws IOException + { + JsonNode node = mapper.readTree(json); + return node.has(fieldname); + } + +} + +class Pojo +{ + public Pojo() {} + public String value = "some_value"; +} + +class PojoWithRiakFields extends Pojo +{ + @RiakKey + public String key = "some_key"; + + @RiakBucketName + public String bucketName = "some_bucket"; + + @RiakBucketType + public String bucketType = "some_type"; + + @RiakUsermeta + public Map metadata = + new HashMap(){{ put("metaKey", "metaValue");}}; + + @RiakIndex(name="email") + public Set index = new HashSet(){{ add("bob@gmail.com");}}; + + @RiakLinks + public Collection links = new LinkedList(){{ add(new RiakLink("bucket","key","tag"));}}; + + @RiakVClock + public VClock vclock; + + @RiakTombstone + public boolean tombstone; + + @RiakContentType + public String contentType; + + @RiakLastModified + public Long lastModified; + + @RiakVTag + public String vtag; +} + +class PojoWithRiakFieldsIncluded extends Pojo +{ + @JsonProperty + @RiakKey + String key = "some_key"; + + @JsonProperty + @RiakBucketName + String bucketName = "some_bucket"; + + @JsonProperty + @RiakBucketType + String bucketType = "some_type"; + + @JsonProperty + @RiakUsermeta + Map metadata = + new HashMap(){{ put("metaKey", "metaValue");}}; + + @JsonProperty + @RiakIndex(name="email") + Set index = new HashSet(){{ add("bob@gmail.com");}}; + + @JsonProperty + @RiakLinks + Collection links = + new LinkedList(){{ add(new RiakLink("bucket","key","tag"));}}; + + @JsonProperty + @RiakVClock + VClock vclock; + + @JsonProperty + @RiakTombstone + boolean tombstone; + +} + +class PojoWithRiakMethods extends Pojo +{ + protected String key = "some_key"; + protected String bucketName = "some_bucket"; + protected String bucketType = "some_type"; + protected Map metadata = + new HashMap(){{ put("metaKey", "metaValue");}}; + protected Set index = new HashSet(){{ add("bob@gmail.com");}}; + protected Collection links = + new LinkedList(){{ add(new RiakLink("bucket","key","tag"));}}; + protected VClock vclock; + protected boolean tombstone; +} + +class PojoWithRiakMethodsExcluded extends PojoWithRiakMethods +{ + @RiakKey + public void setKey(String key) + { + this.key = key; + } + + @RiakKey + public String getKey() + { + return this.key; + } + + @RiakBucketName + public void setBucketName(String bucketName) + { + this.bucketName = bucketName; + } + + @RiakBucketName + public String getBucketName() + { + return bucketName; + } + + @RiakBucketType + public void setBucketType(String bucketType) + { + this.bucketType = bucketType; + } + + @RiakBucketType + public String getBucketType() + { + return bucketType; + } + + @RiakUsermeta + public Map getUsermeta() + { + return this.metadata; + } + + @RiakUsermeta + public void setUsermeta(Map usermeta) + { + this.metadata = usermeta; + } + + @RiakIndex(name="email") + public void setIndex(Set index) + { + this.index = index; + } + + @RiakIndex(name="email") + public Set getIndex() + { + return this.index; + } + + @RiakLinks + public Collection getLinks() + { + return this.links; + } + + @RiakLinks + public void setLinks(Collection links) + { + this.links = links; + } + + @RiakVClock + public void setVClock(VClock vclock) + { + this.vclock = vclock; + } + + @RiakVClock + public VClock getVClock() + { + return this.vclock; + } + + @RiakTombstone + public boolean getTombstone() + { + return this.tombstone; + } + + @RiakTombstone + public void setTombstone(boolean tombstone) + { + this.tombstone = tombstone; + } + +} + +class PojoWithRiakMethodsIncluded extends PojoWithRiakMethods +{ + + @JsonProperty + @RiakKey + public void setKey(String key) + { + this.key = key; + } + + @JsonProperty + @RiakKey + public String getKey() + { + return this.key; + } + + @JsonProperty + @RiakBucketName + public void setBucketName(String bucketName) + { + this.bucketName = bucketName; + } + + @JsonProperty + @RiakBucketName + public String getBucketName() + { + return bucketName; + } + + @JsonProperty + @RiakBucketType + public void setBucketType(String bucketType) + { + this.bucketType = bucketType; + } + + @JsonProperty + @RiakBucketType + public String getBucketType() + { + return bucketType; + } + + @JsonProperty + @RiakUsermeta + public Map getUsermeta() + { + return this.metadata; + } + + @JsonProperty + @RiakUsermeta + public void setUsermeta(Map usermeta) + { + this.metadata = usermeta; + } + + @JsonProperty + @RiakIndex(name="email") + public void setIndex(Set index) + { + this.index = index; + } + + @JsonProperty + @RiakIndex(name="email") + public Set getIndex() + { + return this.index; + } + + @JsonProperty + @RiakLinks + public Collection getLinks() + { + return this.links; + } + + @JsonProperty + @RiakLinks + public void setLinks(Collection links) + { + this.links = links; + } + + @JsonProperty + @RiakVClock + public void setVClock(VClock vclock) + { + this.vclock = vclock; + } + + @JsonProperty + @RiakVClock + public VClock getVClock() + { + return this.vclock; + } + + @JsonProperty + @RiakTombstone + public boolean getTombstone() + { + return this.tombstone; + } + + @JsonProperty + @RiakTombstone + public void setTombstone(boolean tombstone) + { + this.tombstone = tombstone; + } +} + +class EmptyPojoWithRiakFields +{ + @RiakKey + String key; + + @RiakBucketName + String bucketName; + + @RiakBucketType + String bucketType; + + @RiakUsermeta + Map metadata; + + @RiakIndex(name="email") + Set index; + + @RiakLinks + Collection links; + + @RiakVClock + VClock vclock; + + @RiakTombstone + boolean tombstone; + + @RiakContentType + String contentType; + + @RiakLastModified + Long lastModified; + + @RiakVTag + String vtag; + + @JsonProperty + String value; + +} \ No newline at end of file diff --git a/src/test/java/com/basho/riak/client/api/convert/RiakBeanSerializerModifierTest.java b/src/test/java/com/basho/riak/client/api/convert/RiakBeanSerializerModifierTest.java new file mode 100644 index 000000000..8872e0b16 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/convert/RiakBeanSerializerModifierTest.java @@ -0,0 +1,135 @@ +/* + * 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.convert; + +import com.basho.riak.client.api.convert.RiakJacksonModule; +import com.basho.riak.client.api.annotations.RiakKey; +import com.basho.riak.client.api.annotations.RiakUsermeta; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; + +/** + * @author russell + * + */ +@SuppressWarnings("unchecked") +public class RiakBeanSerializerModifierTest { + + @Test public void changePropertiesDropsRiakAnnotatedProperties() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new RiakJacksonModule()); + + RiakAnnotatedClass rac = new RiakAnnotatedClass(); + + String json = mapper.writeValueAsString(rac); + + @SuppressWarnings("unchecked") Map map = mapper.readValue(json, Map.class); + + assertEquals(2, map.size()); + assertTrue(map.containsKey("someField")); + assertTrue(map.containsKey("someOtherField")); + + RiakAnnotatedClassWithPublicFields racwpf = new RiakAnnotatedClassWithPublicFields(); + + json = mapper.writeValueAsString(racwpf); + + map = mapper.readValue(json, Map.class); + + assertEquals(2, map.size()); + assertTrue(map.containsKey("someField")); + assertTrue(map.containsKey("someOtherField")); + + // Test the combination of a Riak annotation and the Jackson @JsonProperty + RiakAnnotatedClassWithJsonProp racwjp = new RiakAnnotatedClassWithJsonProp(); + json = mapper.writeValueAsString(racwjp); + + map = mapper.readValue(json, Map.class); + + assertEquals(4, map.size()); + assertTrue(map.containsKey("keyField")); + assertTrue(map.containsKey("metaValueField")); + assertTrue(map.containsKey("someField")); + assertTrue(map.containsKey("someOtherField")); + + + } + + @SuppressWarnings("unused") private static final class RiakAnnotatedClass { + @RiakKey private String keyField = "key"; + @RiakUsermeta(key = "metaKey1") private String metaValueField = "ONE"; + @RiakUsermeta private Map usermeta = new HashMap(); + + private String someField = "TWO"; + private String someOtherField = "THREE"; + + public String getSomeField() { + return someField; + } + + public void setSomeField(String someField) { + this.someField = someField; + } + + public String getSomeOtherField() { + return someOtherField; + } + + public void setSomeOtherField(String someOtherField) { + this.someOtherField = someOtherField; + } + + public String getKeyField() { + return keyField; + } + + public String getMetaValueField() { + return metaValueField; + } + } + + @SuppressWarnings("unused") private static final class RiakAnnotatedClassWithPublicFields { + @RiakKey public String keyField = "key"; + @RiakUsermeta(key = "metaKey1") public String metaValueField = "ONE"; + @RiakUsermeta public Map usermeta = new HashMap(); + + public String someField = "TWO"; + public String someOtherField = "THREE"; + } + + @SuppressWarnings("unused") private static final class RiakAnnotatedClassWithJsonProp { + @JsonProperty + @RiakKey private String keyField = "key"; + @JsonProperty + @RiakUsermeta(key = "metaKey1") public String metaValueField = "ONE"; + @RiakUsermeta public Map usermeta = new HashMap(); + + public String someField = "TWO"; + public String someOtherField = "THREE"; + + public String getKeyField() { + return keyField; + } + + } + +} diff --git a/src/test/java/com/basho/riak/client/api/convert/StringConverterTest.java b/src/test/java/com/basho/riak/client/api/convert/StringConverterTest.java new file mode 100644 index 000000000..e8a4f4447 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/convert/StringConverterTest.java @@ -0,0 +1,68 @@ +/* + * 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.convert; + +import com.basho.riak.client.api.convert.Converter; +import com.basho.riak.client.api.convert.StringConverter; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; +import static org.junit.Assert.*; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class StringConverterTest +{ + + @Test + public void producesRiakObject() + { + String foo = "some value"; + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, "bucket"); + StringConverter converter = new StringConverter(); + + + Converter.OrmExtracted orm = converter.fromDomain(foo, ns, BinaryValue.create("key")); + + assertNotNull(orm.getRiakObject()); + RiakObject obj = orm.getRiakObject(); + assertEquals(foo, obj.getValue().toString()); + assertEquals("text/plain", obj.getContentType()); + assertEquals(ns, orm.getNamespace()); + assertEquals("key", orm.getKey().toString()); + } + + @Test + public void producesString() + { + String value = "some value"; + RiakObject obj = new RiakObject() + .setValue(BinaryValue.create(value)); + StringConverter converter = new StringConverter(); + + String result = converter.toDomain(obj, null); + + assertEquals(value, result); + + + } + +} diff --git a/src/test/java/com/basho/riak/client/api/convert/reflection/AnnotationUtilTest.java b/src/test/java/com/basho/riak/client/api/convert/reflection/AnnotationUtilTest.java new file mode 100644 index 000000000..154083ae8 --- /dev/null +++ b/src/test/java/com/basho/riak/client/api/convert/reflection/AnnotationUtilTest.java @@ -0,0 +1,2650 @@ +/* + * 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.convert.reflection; + +import com.basho.riak.client.api.convert.reflection.AnnotationUtil; +import com.basho.riak.client.api.annotations.RiakBucketName; +import com.basho.riak.client.api.annotations.RiakContentType; +import com.basho.riak.client.api.annotations.RiakKey; +import com.basho.riak.client.api.annotations.RiakTombstone; +import com.basho.riak.client.api.annotations.RiakLastModified; +import com.basho.riak.client.api.annotations.RiakVClock; +import com.basho.riak.client.api.annotations.RiakLinks; +import com.basho.riak.client.api.annotations.RiakVTag; +import com.basho.riak.client.api.annotations.RiakBucketType; +import com.basho.riak.client.api.annotations.RiakIndex; +import com.basho.riak.client.api.annotations.RiakUsermeta; +import com.basho.riak.client.api.cap.BasicVClock; +import com.basho.riak.client.api.cap.VClock; +import com.basho.riak.client.core.query.UserMetadata.RiakUserMetadata; +import com.basho.riak.client.core.query.indexes.IndexType; +import com.basho.riak.client.core.query.indexes.LongIntIndex; +import com.basho.riak.client.core.query.indexes.RawIndex; +import com.basho.riak.client.core.query.indexes.RiakIndexes; +import com.basho.riak.client.core.query.indexes.StringBinIndex; +import com.basho.riak.client.core.query.links.RiakLink; +import com.basho.riak.client.core.util.BinaryValue; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author Brian Roach + */ +public class AnnotationUtilTest +{ + @Test + public void getKeyStringField() + { + final String expected = "aKey"; + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + pojo.key = expected; + + assertEquals(BinaryValue.create(expected), AnnotationUtil.getKey(pojo)); + assertEquals(BinaryValue.create(expected), AnnotationUtil.getKey(pojo, null)); + } + + @Test + public void getKeyByteField() + { + final byte[] expected = "aKey".getBytes(); + final PojoWithAnnotatedByteFields pojo = new PojoWithAnnotatedByteFields(); + pojo.key = expected; + + assertEquals(BinaryValue.create(expected), AnnotationUtil.getKey(pojo)); + assertEquals(BinaryValue.create(expected), AnnotationUtil.getKey(pojo, null)); + } + + + @Test + public void setKeyStringField() + { + final BinaryValue expected = BinaryValue.create("aKey"); + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + AnnotationUtil.setKey(pojo, expected); + + assertNotNull(pojo.key); + assertEquals(expected.toString(), pojo.key); + } + + @Test + public void setKeyByteField() + { + final BinaryValue expected = BinaryValue.create("aKey"); + final PojoWithAnnotatedByteFields pojo = new PojoWithAnnotatedByteFields(); + AnnotationUtil.setKey(pojo, expected); + + assertNotNull(pojo.key); + assertArrayEquals(expected.getValue(), pojo.key); + } + + @Test + public void getKeyStringMethod() + { + final String expected = "aKey"; + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + pojo.setKey(expected); + + assertEquals(BinaryValue.create(expected), AnnotationUtil.getKey(pojo)); + assertEquals(BinaryValue.create(expected), AnnotationUtil.getKey(pojo, null)); + } + + @Test + public void getKeyByteMethod() + { + final byte[] expected = "aKey".getBytes(); + final PojoWithAnnotatedByteMethods pojo = new PojoWithAnnotatedByteMethods(); + pojo.setKey(expected); + + assertEquals(BinaryValue.create(expected), AnnotationUtil.getKey(pojo)); + assertEquals(BinaryValue.create(expected), AnnotationUtil.getKey(pojo, null)); + } + + @Test + public void setKeyByteMethod() + { + final byte[] expected = "aKey".getBytes(); + final PojoWithAnnotatedByteMethods pojo = new PojoWithAnnotatedByteMethods(); + AnnotationUtil.setKey(pojo, BinaryValue.create(expected)); + + assertNotNull(pojo.getKey()); + assertArrayEquals(expected, pojo.getKey()); + } + + @Test + public void setKeyStringMethod() + { + final BinaryValue expected = BinaryValue.create("aKey"); + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + AnnotationUtil.setKey(pojo, expected); + + assertNotNull(pojo.getKey()); + assertEquals(expected.toString(), pojo.getKey()); + } + + @Test(expected = RuntimeException.class) + public void getNonStringKeyField() + { + final Object o = new Object() { + @RiakKey + Date key; + }; + + AnnotationUtil.getKey(o); + } + + @Test(expected = RuntimeException.class) + public void getNonStringKeyGetter() + { + final Object o = new Object() { + @RiakKey + Date getKey() { return null; } + + @RiakKey + void setKey(String key) {} + + }; + + AnnotationUtil.getKey(o); + } + + @Test(expected = RuntimeException.class) + public void getNonStringKeySetter() + { + final Object o = new Object() { + @RiakKey + String getKey() { return null; } + + @RiakKey + void setKey(Date key) {} + + }; + + AnnotationUtil.setKey(o, BinaryValue.create("Some Date")); + } + + @Test + public void noKeyFieldOrMethod() + { + final Object o = new Object(); + + assertNull(AnnotationUtil.getKey(o)); + assertNotNull(AnnotationUtil.getKey(o, BinaryValue.create("default"))); + AnnotationUtil.setKey(o, BinaryValue.create("default")); // should do nothing + } + + @Test + public void getNullKeyField() + { + final String expected = null; + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + pojo.key = expected; + + assertNull(AnnotationUtil.getKey(pojo)); + assertNotNull(AnnotationUtil.getKey(pojo, BinaryValue.create("default"))); + } + + @Test + public void nullKeyMethod() + { + final String expected = null; + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + pojo.setKey(expected); + + assertNull(AnnotationUtil.getKey(pojo)); + assertNotNull(AnnotationUtil.getKey(pojo, BinaryValue.create("default"))); + } + + @Test + public void getBucketNameStringField() + { + final String expected = "aBucket"; + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + pojo.bucketName = expected; + + assertEquals(BinaryValue.create(expected), AnnotationUtil.getBucketName(pojo)); + assertEquals(BinaryValue.create(expected), AnnotationUtil.getBucketName(pojo, null)); + } + + @Test + public void getBucketNameByteField() + { + byte[] expected = "aBucket".getBytes(); + final PojoWithAnnotatedByteFields pojo = new PojoWithAnnotatedByteFields(); + pojo.bucketName = expected; + + assertEquals(BinaryValue.create(expected), AnnotationUtil.getBucketName(pojo)); + assertEquals(BinaryValue.create(expected), AnnotationUtil.getBucketName(pojo, null)); + + } + + @Test + public void setBucketNameStringField() + { + final BinaryValue expected = BinaryValue.create("aBucket"); + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + AnnotationUtil.setBucketName(pojo, expected); + + assertNotNull(pojo.bucketName); + assertEquals(expected.toString(), pojo.bucketName); + } + + @Test + public void setBucketNameByteField() + { + final BinaryValue expected = BinaryValue.create("aBucket"); + final PojoWithAnnotatedByteFields pojo = new PojoWithAnnotatedByteFields(); + AnnotationUtil.setBucketName(pojo, expected); + + assertNotNull(pojo.bucketName); + assertArrayEquals(expected.getValue(), pojo.bucketName); + } + + @Test + public void getBucketNameStringMethod() + { + final String expected = "aBucket"; + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + pojo.setBucketName(expected); + + assertEquals(BinaryValue.create(expected), AnnotationUtil.getBucketName(pojo)); + assertEquals(BinaryValue.create(expected), AnnotationUtil.getBucketName(pojo, null)); + } + + @Test + public void getBucketNameByteMethod() + { + byte[] expected = "aBucket".getBytes(); + final PojoWithAnnotatedByteMethods pojo = new PojoWithAnnotatedByteMethods(); + pojo.setBucketName(expected); + + assertEquals(BinaryValue.create(expected), AnnotationUtil.getBucketName(pojo)); + assertEquals(BinaryValue.create(expected), AnnotationUtil.getBucketName(pojo, null)); + } + + @Test + public void setBucketNameStringMethod() + { + final BinaryValue expected = BinaryValue.create("aBucket"); + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + AnnotationUtil.setBucketName(pojo, expected); + + assertNotNull(pojo.bucketName); + assertEquals(expected.toString(), pojo.bucketName); + } + + @Test + public void setBucketNameByteMethod() + { + byte[] expected = "aBucket".getBytes(); + final PojoWithAnnotatedByteMethods pojo = new PojoWithAnnotatedByteMethods(); + AnnotationUtil.setBucketName(pojo, BinaryValue.create(expected)); + + assertNotNull(pojo.bucketName); + assertArrayEquals(expected, pojo.bucketName); + + } + + @Test(expected = RuntimeException.class) + public void getIllegalBucketNameField() + { + final Object o = new Object() { + @RiakBucketName + Date bucketName; + }; + + AnnotationUtil.getBucketName(o); + } + + @Test(expected = RuntimeException.class) + public void illegalBucketNameGetter() + { + final Object o = new Object() { + @RiakBucketName + Date getKey() { return null; } + + @RiakBucketName + void setKey(String key) {} + + }; + + AnnotationUtil.getBucketName(o); + } + + @Test(expected = RuntimeException.class) + public void illegalBucketNameSetter() + { + final Object o = new Object() { + @RiakBucketName + String getBucketName() { return null; } + + @RiakBucketName + void setBucketName(Date key) {} + + }; + + AnnotationUtil.setBucketName(o, BinaryValue.create("Some Date")); + } + + @Test + public void noBucketNameFieldOrMethod() + { + final Object o = new Object() + { + private final String bucketName = "tomatoes"; + }; + + assertNull(AnnotationUtil.getBucketName(o)); + assertNotNull(AnnotationUtil.getBucketName(o, BinaryValue.create("default"))); + AnnotationUtil.setBucketName(o, BinaryValue.create("default")); // should do nothing + } + + @Test + public void nullBucketNameField() + { + final String expected = null; + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + pojo.bucketName = expected; + + assertNull(AnnotationUtil.getBucketName(pojo)); + assertNotNull(AnnotationUtil.getBucketName(pojo, BinaryValue.create("default"))); + } + + @Test + public void nullBucketNameMethod() + { + final String expected = null; + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + pojo.setBucketName(expected); + + assertNull(AnnotationUtil.getBucketName(pojo)); + assertNotNull(AnnotationUtil.getBucketName(pojo, BinaryValue.create("default"))); + } + + @Test + public void getBucketTypeStringField() + { + final String expected = "aBucketType"; + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + pojo.bucketType = expected; + + assertEquals(BinaryValue.create(expected), AnnotationUtil.getBucketType(pojo)); + assertEquals(BinaryValue.create(expected), AnnotationUtil.getBucketType(pojo, null)); + } + + @Test + public void getBucketTypeByteField() + { + byte[] expected = "aBucketType".getBytes(); + final PojoWithAnnotatedByteFields pojo = new PojoWithAnnotatedByteFields(); + pojo.bucketType = expected; + + assertEquals(BinaryValue.create(expected), AnnotationUtil.getBucketType(pojo)); + assertEquals(BinaryValue.create(expected), AnnotationUtil.getBucketType(pojo, null)); + + } + + @Test + public void setBucketTypeStringField() + { + final BinaryValue expected = BinaryValue.create("aBucketType"); + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + AnnotationUtil.setBucketType(pojo, expected); + + assertNotNull(pojo.bucketType); + assertEquals(expected.toString(), pojo.bucketType); + } + + @Test + public void setBucketTypeByteField() + { + final BinaryValue expected = BinaryValue.create("aBucketType"); + final PojoWithAnnotatedByteFields pojo = new PojoWithAnnotatedByteFields(); + AnnotationUtil.setBucketType(pojo, expected); + + assertNotNull(pojo.bucketType); + assertArrayEquals(expected.getValue(), pojo.bucketType); + } + + @Test + public void getBucketTypeStringMethod() + { + final String expected = "aBucketType"; + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + pojo.setBucketType(expected); + + assertEquals(BinaryValue.create(expected), AnnotationUtil.getBucketType(pojo)); + assertEquals(BinaryValue.create(expected), AnnotationUtil.getBucketType(pojo, null)); + } + + @Test + public void getBucketTypeByteMethod() + { + byte[] expected = "aBucketType".getBytes(); + final PojoWithAnnotatedByteMethods pojo = new PojoWithAnnotatedByteMethods(); + pojo.setBucketType(expected); + + assertEquals(BinaryValue.create(expected), AnnotationUtil.getBucketType(pojo)); + assertEquals(BinaryValue.create(expected), AnnotationUtil.getBucketType(pojo, null)); + } + + @Test + public void setBucketTypeStringMethod() + { + final BinaryValue expected = BinaryValue.create("aBucketType"); + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + AnnotationUtil.setBucketType(pojo, expected); + + assertNotNull(pojo.bucketType); + assertEquals(expected.toString(), pojo.bucketType); + } + + @Test + public void setBucketTypeByteMethod() + { + byte[] expected = "aBucketType".getBytes(); + final PojoWithAnnotatedByteMethods pojo = new PojoWithAnnotatedByteMethods(); + AnnotationUtil.setBucketType(pojo, BinaryValue.create(expected)); + + assertNotNull(pojo.bucketType); + assertArrayEquals(expected, pojo.bucketType); + + } + + @Test(expected = RuntimeException.class) + public void getIllegalBucketTypeField() + { + final Object o = new Object() { + @RiakBucketType + Date bucketType; + }; + + AnnotationUtil.getBucketType(o); + } + + @Test(expected = RuntimeException.class) + public void illegalBucketTypeGetter() + { + final Object o = new Object() { + @RiakBucketType + Date getType() { return null; } + + @RiakBucketType + void setType(String key) {} + + }; + + AnnotationUtil.getBucketType(o); + } + + @Test(expected = RuntimeException.class) + public void illegalBucketTypeSetter() + { + final Object o = new Object() { + @RiakBucketType + String getBucketType() { return null; } + + @RiakBucketType + void setBucketType(Date key) {} + + }; + + AnnotationUtil.setBucketType(o, BinaryValue.create("Some Date")); + } + + @Test + public void noBucketTypeFieldOrMethod() + { + final Object o = new Object() + { + private final String bucketType = "tomatoes"; + }; + + assertNull(AnnotationUtil.getBucketType(o)); + assertNotNull(AnnotationUtil.getBucketType(o, BinaryValue.create("default"))); + AnnotationUtil.setBucketType(o, BinaryValue.create("default")); // should do nothing + } + + @Test + public void nullBucketTypeField() + { + final String expected = null; + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + pojo.bucketType = expected; + + assertNull(AnnotationUtil.getBucketType(pojo)); + assertNotNull(AnnotationUtil.getBucketType(pojo, BinaryValue.create("default"))); + } + + @Test + public void nullBucketTypeMethod() + { + final String expected = null; + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + pojo.setBucketType(expected); + + assertNull(AnnotationUtil.getBucketType(pojo)); + assertNotNull(AnnotationUtil.getBucketType(pojo, BinaryValue.create("default"))); + } + + @Test + public void getVClockField() + { + final byte[] clockBytes = "a85hYGBgzGDKBVIcypz/fvo/2e2UwZTImMfKwHIy/SRfFgA=".getBytes(); + final VClock expected = new BasicVClock(clockBytes); + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + pojo.vclock = expected; + + assertEquals(expected, AnnotationUtil.getVClock(pojo)); + assertEquals(expected, AnnotationUtil.getVClock(pojo, null)); + } + + @Test + public void getVClockByteField() + { + final byte[] expected = "a85hYGBgzGDKBVIcypz/fvo/2e2UwZTImMfKwHIy/SRfFgA=".getBytes(); + final PojoWithAnnotatedByteFields pojo = new PojoWithAnnotatedByteFields(); + pojo.vclock = expected; + + assertEquals(new BasicVClock(expected), AnnotationUtil.getVClock(pojo)); + assertEquals(new BasicVClock(expected), AnnotationUtil.getVClock(pojo, null)); + + } + + @Test + public void setVClockField() + { + final byte[] clockBytes = "a85hYGBgzGDKBVIcypz/fvo/2e2UwZTImMfKwHIy/SRfFgA=".getBytes(); + final VClock expected = new BasicVClock(clockBytes); + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + AnnotationUtil.setVClock(pojo, expected); + + assertNotNull(pojo.vclock); + assertEquals(expected, pojo.vclock); + } + + @Test + public void setVClockByteField() + { + final byte[] expected = "a85hYGBgzGDKBVIcypz/fvo/2e2UwZTImMfKwHIy/SRfFgA=".getBytes(); + final PojoWithAnnotatedByteFields pojo = new PojoWithAnnotatedByteFields(); + AnnotationUtil.setVClock(pojo, new BasicVClock(expected)); + + assertNotNull(pojo.vclock); + assertArrayEquals(expected, pojo.vclock); + } + + @Test + public void getVClockMethod() + { + final byte[] clockBytes = "a85hYGBgzGDKBVIcypz/fvo/2e2UwZTImMfKwHIy/SRfFgA=".getBytes(); + final VClock expected = new BasicVClock(clockBytes); + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + pojo.setVClock(expected); + + assertEquals(expected, AnnotationUtil.getVClock(pojo)); + assertEquals(expected, AnnotationUtil.getVClock(pojo, null)); + } + + @Test + public void getVClockByteMethod() + { + final byte[] clockBytes = "a85hYGBgzGDKBVIcypz/fvo/2e2UwZTImMfKwHIy/SRfFgA=".getBytes(); + final VClock expected = new BasicVClock(clockBytes); + final PojoWithAnnotatedByteMethods pojo = new PojoWithAnnotatedByteMethods(); + pojo.setVClock(clockBytes); + + assertEquals(expected, AnnotationUtil.getVClock(pojo)); + assertEquals(expected, AnnotationUtil.getVClock(pojo, null)); + } + + @Test + public void setVClockMethod() + { + final byte[] clockBytes = "a85hYGBgzGDKBVIcypz/fvo/2e2UwZTImMfKwHIy/SRfFgA=".getBytes(); + final VClock expected = new BasicVClock(clockBytes); + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + AnnotationUtil.setVClock(pojo, expected); + + assertNotNull(pojo.vClock); + assertEquals(expected, pojo.vClock); + } + + @Test + public void setVClockByteMethod() + { + final byte[] clockBytes = "a85hYGBgzGDKBVIcypz/fvo/2e2UwZTImMfKwHIy/SRfFgA=".getBytes(); + final VClock expected = new BasicVClock(clockBytes); + final PojoWithAnnotatedByteMethods pojo = new PojoWithAnnotatedByteMethods(); + AnnotationUtil.setVClock(pojo, expected); + + assertNotNull(pojo.vclock); + assertArrayEquals(clockBytes, pojo.vclock); + + } + + @Test(expected = RuntimeException.class) + public void getIllegalVClockField() + { + final Object o = new Object() { + @RiakVClock + Date vclock; + }; + + AnnotationUtil.getVClock(o); + } + + @Test(expected = RuntimeException.class) + public void illegalVClockGetter() + { + final Object o = new Object() { + @RiakVClock + Date getVClock() { return null; } + + @RiakVClock + void setVClock(VClock key) {} + + }; + + AnnotationUtil.getVClock(o); + } + + @Test(expected = RuntimeException.class) + public void illegalVClockSetter() + { + final Object o = new Object() { + @RiakVClock + VClock getVClock() { return null; } + + @RiakVClock + void setVClock(Date key) {} + + }; + final byte[] clockBytes = "a85hYGBgzGDKBVIcypz/fvo/2e2UwZTImMfKwHIy/SRfFgA=".getBytes(); + final VClock expected = new BasicVClock(clockBytes); + AnnotationUtil.setVClock(o, expected); + } + + @Test + public void noVClockFieldOrMethod() + { + final Object o = new Object(); + + final byte[] clockBytes = "a85hYGBgzGDKBVIcypz/fvo/2e2UwZTImMfKwHIy/SRfFgA=".getBytes(); + final VClock expected = new BasicVClock(clockBytes); + assertNull(AnnotationUtil.getVClock(o)); + assertNotNull(AnnotationUtil.getVClock(o, expected)); + AnnotationUtil.setVClock(o, expected); // should do nothing + } + + @Test + public void nullVClockField() + { + final VClock expected = null; + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + pojo.vclock = expected; + + final byte[] clockBytes = "a85hYGBgzGDKBVIcypz/fvo/2e2UwZTImMfKwHIy/SRfFgA=".getBytes(); + assertNull(AnnotationUtil.getVClock(pojo)); + assertNotNull(AnnotationUtil.getVClock(pojo, new BasicVClock(clockBytes) )); + } + + @Test + public void nullVClockMethod() + { + final VClock expected = null; + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + pojo.setVClock(expected); + + assertNull(AnnotationUtil.getVClock(pojo)); + final byte[] clockBytes = "a85hYGBgzGDKBVIcypz/fvo/2e2UwZTImMfKwHIy/SRfFgA=".getBytes(); + assertNotNull(AnnotationUtil.getVClock(pojo, new BasicVClock(clockBytes))); + } + + + @Test + public void getTombstoneField() + { + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + pojo.tombstone = true; + assertTrue(AnnotationUtil.getTombstone(pojo)); + } + + @Test + public void getTombstoneMethod() + { + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + pojo.setTombstone(true); + assertTrue(AnnotationUtil.getTombstone(pojo)); + } + + @Test + public void noTombstoneFieldOrMethod() + { + final Object o = new Object(); + + assertNull(AnnotationUtil.getTombstone(o)); + } + + @Test + public void illegalTombstoneFieldType() + { + final Object o = new Object() + { + @RiakTombstone + private final String domainProperty = null; + + }; + + try + { + boolean tombstone = AnnotationUtil.getTombstone(o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + } + + @Test + public void illegalTombstoneGetterType() + { + final Object o = new Object() + { + @RiakTombstone + public String getTombstone() + { + return null; + } + + @RiakVClock + public void setTombstone(Boolean v) + {} + + }; + + try + { + boolean tombstone = AnnotationUtil.getTombstone(o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + } + + @Test + public void illegalTombstoneSetterType() + { + final Object o = new Object() + { + @RiakTombstone + public Boolean getTombstone() + { + return null; + } + + @RiakVClock + public void setTombstone(String v) + {} + + }; + + try + { + boolean tombstone = AnnotationUtil.getTombstone(o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + } + + @Test + public void setTombstoneField() + { + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + + AnnotationUtil.setTombstone(pojo, true); + assertEquals(pojo.tombstone, true); + + final PojoWithAnnotatedByteFields pojo2 = new PojoWithAnnotatedByteFields(); + + AnnotationUtil.setTombstone(pojo2, true); + assertEquals(pojo2.tombstone, true); + } + + @Test + public void setTombstoneMethod() + { + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + + AnnotationUtil.setTombstone(pojo, true); + assertTrue(pojo.getTombstone()); + + final PojoWithAnnotatedByteMethods pojo2 = new PojoWithAnnotatedByteMethods(); + + AnnotationUtil.setTombstone(pojo2, true); + assertTrue(pojo2.getTombstone()); + + } + + @Test + public void populateIndexFields() + { + HashSet langs = new HashSet(Arrays.asList("c","erlang","java")); + HashSet longs = new HashSet(Arrays.asList(4L,9L,12L)); + HashSet bytes = new HashSet(); + + for (Integer i = 0; i < 5; i++) + { + bytes.add(BinaryValue.create(i.toString().getBytes())); + } + + RiakIndexes rIndexes = new RiakIndexes(); + + rIndexes.getIndex(StringBinIndex.named("favorite_languages")).add(langs); + rIndexes.getIndex(StringBinIndex.named("lucky_language")).add(langs); + rIndexes.getIndex(LongIntIndex.named("longs")).add(longs); + rIndexes.getIndex(LongIntIndex.named("lucky_long")).add(longs); + rIndexes.getIndex(LongIntIndex.named("lucky_longlong")).add(longs); + rIndexes.getIndex(RawIndex.named("raw", IndexType.INT)).add(bytes); + rIndexes.getIndex(RawIndex.named("set_raw", IndexType.INT)).add(bytes); + rIndexes.getIndex(RawIndex.named("raw", IndexType.BIN)).add(bytes); + rIndexes.getIndex(RawIndex.named("set_raw", IndexType.BIN)).add(bytes); + + PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + + AnnotationUtil.populateIndexes(rIndexes, pojo); + + assertNotNull("Expected bin index field (Set languages) to be populated", pojo.languages); + assertNotNull("Expected bin index field (String luckyLanguage) to be populated", pojo.luckyLanguage); + assertNotNull("Expected int index field (Set longs) to be populated", pojo.longs); + assertTrue("Expected int index field (long luckyLong) to be populated", pojo.luckyLong != 0); + assertNotNull("Expected int index field (Long luckyLongLong) to be populated", pojo.luckyLongLong); + assertNotNull("Expected int index field (byte[] rawInt) to be populated", pojo.rawInt); + assertNotNull("Expected bin index field (byte[] rawBin) to be populated", pojo.rawBin); + assertNotNull("Expected int index field (Set rawInts) to be populated", pojo.rawInts); + assertNotNull("Expected bin index field (Set rawBins) to be populated", pojo.rawBins); + + assertEquals(langs.size(), pojo.languages.size()); + assertEquals(langs.iterator().next(), pojo.luckyLanguage); + assertEquals(longs.size(), pojo.longs.size()); + assertEquals(longs.iterator().next().longValue(), pojo.luckyLong); + assertEquals(longs.iterator().next(), pojo.luckyLongLong); + assertEquals(bytes.size(), pojo.rawInts.size()); + assertEquals(bytes.size(), pojo.rawBins.size()); + } + + @Test + public void getIndexFields() + { + PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + pojo.languages = new HashSet(Arrays.asList("c","erlang","java")); + pojo.longs = new HashSet(Arrays.asList(4L,9L,12L)); + pojo.luckyLanguage = pojo.languages.iterator().next(); + pojo.luckyLong = pojo.longs.iterator().next(); + pojo.luckyLongLong = pojo.longs.iterator().next(); + + pojo.rawBins = new HashSet(); + pojo.rawInts = new HashSet(); + + for (Integer i = 0; i < 5; i++) + { + byte[] bytes = i.toString().getBytes(); + pojo.rawBins.add(bytes); + pojo.rawInts.add(bytes); + } + + pojo.rawInt = pojo.rawInts.iterator().next(); + pojo.rawBin = pojo.rawBins.iterator().next(); + + RiakIndexes rIndexes = new RiakIndexes(); + AnnotationUtil.getIndexes(rIndexes, pojo); + + assertEquals("Expected RiakIndexes BinIndex (favorite_languages) to be populated", + rIndexes.getIndex(StringBinIndex.named("favorite_languages")).size(), + pojo.languages.size()); + assertEquals("Expected RiakIndexes BinIndex (lucky_language) to be populated", + rIndexes.getIndex(StringBinIndex.named("lucky_language")).size(), + 1); + assertEquals("Expected RiakIndexes BinIndex (set_raw) to be pupulated", + rIndexes.getIndex(RawIndex.named("set_raw", IndexType.BIN)).size(), + pojo.rawBins.size()); + assertEquals("Expected RiakIndexes BinIndex (raw) to be pupulated", + rIndexes.getIndex(RawIndex.named("raw", IndexType.BIN)).size(), + 1); + + + assertEquals("Expected RiakIndexes IntIndex (longs) to be populated", + rIndexes.getIndex(LongIntIndex.named("longs")).size(), + pojo.longs.size()); + assertEquals("Expected RiakIndexes IntIndex (lucky_long) to be populated", + rIndexes.getIndex(LongIntIndex.named("lucky_long")).size(), + 1); + assertEquals("Expected RiakIndexes IntIndex (lucky_longlong) to be populated", + rIndexes.getIndex(LongIntIndex.named("lucky_longlong")).size(), + 1); + assertEquals("Expected RiakIndexes IntIndex (set_raw) to be pupulated", + rIndexes.getIndex(RawIndex.named("set_raw", IndexType.INT)).size(), + pojo.rawInts.size()); + assertEquals("Expected RiakIndexes IntIndex (raw) to be pupulated", + rIndexes.getIndex(RawIndex.named("raw", IndexType.INT)).size(), + 1); + + + assertTrue(rIndexes.getIndex(StringBinIndex.named("favorite_languages")).values().containsAll(pojo.languages)); + assertEquals(rIndexes.getIndex(StringBinIndex.named("lucky_language")).iterator().next(), + pojo.languages.iterator().next()); + + assertTrue(rIndexes.getIndex(LongIntIndex.named("longs")).values().containsAll(pojo.longs)); + assertEquals(rIndexes.getIndex(LongIntIndex.named("lucky_long")).iterator().next(), + pojo.longs.iterator().next()); + assertEquals(rIndexes.getIndex(LongIntIndex.named("lucky_longlong")).iterator().next(), + pojo.longs.iterator().next()); + assertArrayEquals(rIndexes.getIndex(RawIndex.named("raw", IndexType.INT)).iterator().next().getValue(), + pojo.rawInt); + + } + + @Test + public void populateIndexMethods() + { + HashSet strings = new HashSet(Arrays.asList("c","erlang","java")); + HashSet longs = new HashSet(Arrays.asList(4L,9L,12L)); + HashSet bytes = new HashSet(); + + for (Integer i = 0; i < 5; i++) + { + bytes.add(BinaryValue.create(i.toString().getBytes())); + } + + RiakIndexes rIndexes = new RiakIndexes(); + + rIndexes.getIndex(StringBinIndex.named("strings")).add(strings); + rIndexes.getIndex(StringBinIndex.named("string")).add(strings); + rIndexes.getIndex(LongIntIndex.named("longs")).add(longs); + rIndexes.getIndex(LongIntIndex.named("long")).add(longs); + rIndexes.getIndex(LongIntIndex.named("longlong")).add(longs); + rIndexes.getIndex(RawIndex.named("raw", IndexType.INT)).add(bytes); + rIndexes.getIndex(RawIndex.named("set_raw", IndexType.INT)).add(bytes); + rIndexes.getIndex(RawIndex.named("raw", IndexType.BIN)).add(bytes); + rIndexes.getIndex(RawIndex.named("set_raw", IndexType.BIN)).add(bytes); + + PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + + AnnotationUtil.populateIndexes(rIndexes, pojo); + + assertNotNull("Expected bin index field (Set strings) to be populated", pojo.getStrings()); + assertNotNull("Expected bin index field (String string) to be populated", pojo.getString()); + assertNotNull("Expected int index field (Set longs) to be populated", pojo.getLongs()); + assertTrue("Expected int index field (long long) to be populated", pojo.getLong() != 0); + assertNotNull("Expected int index field (Long longlong) to be populated", pojo.getLongLong()); + + assertNotNull("Expected bin index field (Set rawBins) to be populated", pojo.getRawBinsIndex()); + assertNotNull("Expected int index field (Set rawInts) to be populated", pojo.getRawIntsIndex()); + assertNotNull("Expected bin index field (byte[] rawBin) to be populated", pojo.getRawBinIndex()); + assertNotNull("Expected int index field (byte[] rawInt) to be populated", pojo.getRawIntIndex()); + + assertEquals(strings.size(), pojo.getStrings().size()); + assertEquals(strings.iterator().next(), pojo.getString()); + assertEquals(longs.size(), pojo.getLongs().size()); + assertEquals(longs.iterator().next().longValue(), pojo.getLong()); + assertEquals(longs.iterator().next(), pojo.getLongLong()); + assertEquals(bytes.size(), pojo.getRawBinsIndex().size() ); + assertEquals(bytes.size(), pojo.getRawIntsIndex().size() ); + } + + @Test + public void getIndexMethods() + { + PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + pojo.setStrings(new HashSet(Arrays.asList("c","erlang","java"))); + pojo.setLongs(new HashSet(Arrays.asList(4L,9L,12L))); + pojo.setString(pojo.getStrings().iterator().next()); + pojo.setLong(pojo.getLongs().iterator().next()); + pojo.setLongLong(pojo.longs.iterator().next()); + + pojo.rawBins = new HashSet(); + pojo.rawInts = new HashSet(); + + for (Integer i = 0; i < 5; i++) + { + byte[] bytes = i.toString().getBytes(); + pojo.rawBins.add(bytes); + pojo.rawInts.add(bytes); + } + + pojo.rawInt = pojo.rawInts.iterator().next(); + pojo.rawBin = pojo.rawBins.iterator().next(); + + RiakIndexes rIndexes = new RiakIndexes(); + AnnotationUtil.getIndexes(rIndexes, pojo); + + assertEquals("Expected RiakIndexes BinIndex (strings) to be populated", + rIndexes.getIndex(StringBinIndex.named("strings")).size(), + pojo.getStrings().size()); + assertEquals("Expected RiakIndexes BinIndex (string) to be populated", + rIndexes.getIndex(StringBinIndex.named("string")).size(), + 1); + assertEquals("Expected RiakIndexes BinIndex (set_raw) to be pupulated", + rIndexes.getIndex(RawIndex.named("set_raw", IndexType.BIN)).size(), + pojo.rawBins.size()); + assertEquals("Expected RiakIndexes BinIndex (raw) to be pupulated", + rIndexes.getIndex(RawIndex.named("raw", IndexType.BIN)).size(), + 1); + + assertEquals("Expected RiakIndexes IntIndex (longs) to be populated", + rIndexes.getIndex(LongIntIndex.named("longs")).size(), + pojo.longs.size()); + assertEquals("Expected RiakIndexes IntIndex (long) to be populated", + rIndexes.getIndex(LongIntIndex.named("long")).size(), + 1); + assertEquals("Expected RiakIndexes IntIndex (longlong) to be populated", + rIndexes.getIndex(LongIntIndex.named("longlong")).size(), + 1); + assertEquals("Expected RiakIndexes IntIndex (set_raw) to be pupulated", + rIndexes.getIndex(RawIndex.named("set_raw", IndexType.INT)).size(), + pojo.rawInts.size()); + assertEquals("Expected RiakIndexes IntIndex (raw) to be pupulated", + rIndexes.getIndex(RawIndex.named("raw", IndexType.INT)).size(), + 1); + + assertTrue(rIndexes.getIndex(StringBinIndex.named("strings")).values().containsAll(pojo.getStrings())); + assertEquals(rIndexes.getIndex(StringBinIndex.named("string")).iterator().next(), + pojo.getStrings().iterator().next()); + + assertTrue(rIndexes.getIndex(LongIntIndex.named("longs")).values().containsAll(pojo.getLongs())); + assertEquals(rIndexes.getIndex(LongIntIndex.named("long")).iterator().next(), + pojo.getLongs().iterator().next()); + assertEquals(rIndexes.getIndex(LongIntIndex.named("longlong")).iterator().next(), + pojo.getLongs().iterator().next()); + + } + + @Test + public void illegalRiakIndexFieldType() + { + final RiakIndexes rIndexes = new RiakIndexes(); + + Object o = new Object() + { + @RiakIndex(name="whatever") + private final Boolean domainProperty = null; + + }; + + try + { + AnnotationUtil.populateIndexes(rIndexes, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + o = new Object() + { + @RiakIndex(name="whatever") + private final Set domainProperty = null; + + }; + + try + { + AnnotationUtil.populateIndexes(rIndexes, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + + } + + @Test + public void illegalRiakIndexSetterType() + { + final RiakIndexes rIndexes = new RiakIndexes(); + + Object o = new Object() + { + @RiakIndex(name="whatever") + public void setIndex(Boolean b) + {} + + @RiakIndex(name="whatever") + public Set getIndex() + { + return null; + } + + }; + + try + { + AnnotationUtil.populateIndexes(rIndexes, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + o = new Object() + { + @RiakIndex(name="whatever") + public void setIndex(Set index) + {} + + @RiakIndex(name="whatever") + public Set getIndex() + { + return null; + } + + }; + + try + { + AnnotationUtil.populateIndexes(rIndexes, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + } + + @Test + public void illegalRiakIndexGetterType() + { + final RiakIndexes rIndexes = new RiakIndexes(); + + Object o = new Object() + { + @RiakIndex(name="whatever") + public void setIndex(Long b) + {} + + @RiakIndex(name="whatever") + public boolean getIndex() + { + return true; + } + + }; + + try + { + AnnotationUtil.populateIndexes(rIndexes, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + o = new Object() + { + @RiakIndex(name="whatever") + public void setIndex(Set index) + {} + + @RiakIndex(name="whatever") + public Set getIndex() + { + return null; + } + + }; + + try + { + AnnotationUtil.populateIndexes(rIndexes, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + } + + + @Test + public void missingIndexNameInAnnotation() + { + final RiakIndexes rIndexes = new RiakIndexes(); + + Object o = new Object() + { + @RiakIndex + public void setIndex(Set index) + {} + + @RiakIndex + public Set getIndex() + { + return null; + } + + }; + + try + { + AnnotationUtil.populateIndexes(rIndexes, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + o = new Object() + { + @RiakIndex + private String index = null; + + }; + + try + { + AnnotationUtil.populateIndexes(rIndexes, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + } + + @Test + public void rawIndexWithoutFullName() + { + final RiakIndexes rIndexes = new RiakIndexes(); + + Object o = new Object() + { + @RiakIndex(name="raw") + public void setIndex(Set index) + {} + + @RiakIndex(name="raw_int") + public Set getIndex() + { + return null; + } + + }; + + try + { + AnnotationUtil.populateIndexes(rIndexes, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + o = new Object() + { + @RiakIndex(name="raw") + private byte[] index = null; + + }; + + try + { + AnnotationUtil.populateIndexes(rIndexes, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + } + + @Test + public void annotatedFieldNotInRiak() + { + PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + RiakIndexes rIndex = new RiakIndexes(); + + AnnotationUtil.populateIndexes(rIndex, pojo); + + assertNotNull(pojo.languages); + assertTrue(pojo.languages.isEmpty()); + assertNotNull(pojo.longs); + assertTrue(pojo.longs.isEmpty()); + + // Long/String will be null, long will be zero + assertNull(pojo.luckyLongLong); + assertNull(pojo.luckyLanguage); + assertEquals(pojo.luckyLong, 0); + + } + + @Test + public void annotatedMethodNotInRiak() + { + PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + RiakIndexes rIndex = new RiakIndexes(); + + AnnotationUtil.populateIndexes(rIndex, pojo); + + assertNotNull(pojo.getStrings()); + assertTrue(pojo.getStrings().isEmpty()); + assertNotNull(pojo.getLongs()); + assertTrue(pojo.getLongs().isEmpty()); + + // Long/String will be null, long will be zero + assertNull(pojo.getLongLong()); + assertNull(pojo.getString()); + assertEquals(pojo.getLong(), 0); + + } + + @Test + public void noAnnotatedFieldOrMethodForIndex() + { + PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + RiakIndexes rIndex = new RiakIndexes(); + rIndex.getIndex(StringBinIndex.named("no_annotation")).add("value"); + + AnnotationUtil.populateIndexes(rIndex, pojo); // should do nothing + + rIndex = new RiakIndexes(); + AnnotationUtil.getIndexes(rIndex, pojo); + assertFalse(rIndex.hasIndex(StringBinIndex.named("no_annotation"))); + } + + @Test + public void annotatedIndexFieldIsNull() + { + PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + RiakIndexes rIndex = new RiakIndexes(); + AnnotationUtil.getIndexes(rIndex, pojo); + + // The indexes should be created, but be empty + assertFalse(rIndex.isEmpty()); + assertEquals(9, rIndex.size()); + + assertTrue(rIndex.hasIndex(StringBinIndex.named("favorite_languages"))); + assertTrue(rIndex.getIndex(StringBinIndex.named("favorite_languages")).isEmpty()); + assertTrue(rIndex.hasIndex(LongIntIndex.named("longs"))); + assertTrue(rIndex.getIndex(LongIntIndex.named("longs")).isEmpty()); + assertTrue(rIndex.hasIndex(StringBinIndex.named("lucky_language"))); + assertTrue(rIndex.getIndex(StringBinIndex.named("lucky_language")).isEmpty()); + assertTrue(rIndex.hasIndex(LongIntIndex.named("lucky_longlong"))); + assertTrue(rIndex.getIndex(LongIntIndex.named("lucky_longlong")).isEmpty()); + + // TODO: chuck long support + assertTrue(rIndex.hasIndex(LongIntIndex.named("lucky_long"))); + assertEquals(1, rIndex.getIndex(LongIntIndex.named("lucky_long")).size()); + + } + + @Test + public void annotatedIndexMethodIsNull() + { + PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + RiakIndexes rIndex = new RiakIndexes(); + AnnotationUtil.getIndexes(rIndex, pojo); + + // The indexes should be created, but be empty + assertFalse(rIndex.isEmpty()); + assertEquals(9, rIndex.size()); + + assertTrue(rIndex.hasIndex(StringBinIndex.named("strings"))); + assertTrue(rIndex.getIndex(StringBinIndex.named("strings")).isEmpty()); + + assertTrue(rIndex.hasIndex(LongIntIndex.named("longs"))); + assertTrue(rIndex.getIndex(LongIntIndex.named("longs")).isEmpty()); + assertTrue(rIndex.hasIndex(StringBinIndex.named("string"))); + assertTrue(rIndex.getIndex(StringBinIndex.named("string")).isEmpty()); + assertTrue(rIndex.hasIndex(LongIntIndex.named("longlong"))); + assertTrue(rIndex.getIndex(LongIntIndex.named("longlong")).isEmpty()); + + // TODO: chuck long support + assertTrue(rIndex.hasIndex(LongIntIndex.named("long"))); + assertEquals(1, rIndex.getIndex(LongIntIndex.named("long")).size()); + + } + + @Test + public void getRiakLinksField() + { + Collection links = new LinkedList(); + RiakLink link = new RiakLink("bucket", "key", "tag"); + links.add(link); + link = new RiakLink("bucket", "key2", "tag"); + links.add(link); + PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + pojo.links = links; + + com.basho.riak.client.core.query.links.RiakLinks riakLinks = + new com.basho.riak.client.core.query.links.RiakLinks(); + + AnnotationUtil.getLinks(riakLinks, pojo); + assertFalse(riakLinks.isEmpty()); + assertTrue(riakLinks.getLinks().containsAll(links)); + } + + @Test + public void getRiakLinksMethod() + { + Collection links = new LinkedList(); + RiakLink link = new RiakLink("bucket", "key", "tag"); + links.add(link); + link = new RiakLink("bucket", "key2", "tag"); + links.add(link); + PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + pojo.setLinks(links); + + com.basho.riak.client.core.query.links.RiakLinks riakLinks = + new com.basho.riak.client.core.query.links.RiakLinks(); + + AnnotationUtil.getLinks(riakLinks, pojo); + assertFalse(riakLinks.isEmpty()); + assertTrue(riakLinks.getLinks().containsAll(links)); + } + + @Test + public void setRiakLinksField() + { + com.basho.riak.client.core.query.links.RiakLinks riakLinks = + new com.basho.riak.client.core.query.links.RiakLinks(); + RiakLink link = new RiakLink("bucket", "key", "tag"); + riakLinks.addLink(link); + link = new RiakLink("bucket", "key2", "tag"); + riakLinks.addLink(link); + + PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + AnnotationUtil.populateLinks(riakLinks, pojo); + + assertNotNull(pojo.links); + assertEquals(riakLinks.size(), pojo.links.size()); + assertTrue(pojo.links.containsAll(riakLinks.getLinks())); + } + + @Test + public void setRiakLinksMethod() + { + com.basho.riak.client.core.query.links.RiakLinks riakLinks = + new com.basho.riak.client.core.query.links.RiakLinks(); + RiakLink link = new RiakLink("bucket", "key", "tag"); + riakLinks.addLink(link); + link = new RiakLink("bucket", "key2", "tag"); + riakLinks.addLink(link); + + PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + AnnotationUtil.populateLinks(riakLinks, pojo); + + assertNotNull(pojo.getLinks()); + assertEquals(riakLinks.size(), pojo.getLinks().size()); + assertTrue(pojo.getLinks().containsAll(riakLinks.getLinks())); + } + + @Test + public void illegalRiakLinksFieldType() + { + com.basho.riak.client.core.query.links.RiakLinks riakLinks = + new com.basho.riak.client.core.query.links.RiakLinks(); + Object o = new Object() + { + @RiakLinks + private final String domainProperty = null; + + }; + + try + { + AnnotationUtil.getLinks(riakLinks, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + o = new Object() + { + @RiakLinks + private final Collection domainProperty = null; + + }; + + try + { + AnnotationUtil.getLinks(riakLinks, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + } + + @Test + public void illegalRiakLinksGetterType() + { + com.basho.riak.client.core.query.links.RiakLinks riakLinks = + new com.basho.riak.client.core.query.links.RiakLinks(); + + Object o = new Object() + { + @RiakLinks + public String getLinks() + { + return null; + } + + @RiakLinks + public void setLinks(Collection links) {} + + }; + + try + { + AnnotationUtil.getLinks(riakLinks, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + o = new Object() + { + @RiakLinks + public Collection getLinks() + { + return null; + } + + @RiakLinks + public void setLinks(Collection links) {} + + }; + + try + { + AnnotationUtil.getLinks(riakLinks, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + } + + @Test + public void illegalRiakLinksSetterType() + { + com.basho.riak.client.core.query.links.RiakLinks riakLinks = + new com.basho.riak.client.core.query.links.RiakLinks(); + + Object o = new Object() + { + @RiakLinks + public Collection getLinks() + { + return null; + } + + @RiakLinks + public void setLinks(String links) {} + + }; + + try + { + AnnotationUtil.getLinks(riakLinks, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + o = new Object() + { + @RiakLinks + public Collection getLinks() + { + return null; + } + + @RiakLinks + public void setLinks(Collection links) {} + + }; + + try + { + AnnotationUtil.getLinks(riakLinks, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + } + + @Test + public void annotatedLinksFieldIsNull() + { + PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + com.basho.riak.client.core.query.links.RiakLinks riakLinks = + new com.basho.riak.client.core.query.links.RiakLinks(); + + AnnotationUtil.getLinks(riakLinks, pojo); + + assertTrue(riakLinks.isEmpty()); + } + + @Test + public void annotatedLinksMethodIsNull() + { + PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + com.basho.riak.client.core.query.links.RiakLinks riakLinks = + new com.basho.riak.client.core.query.links.RiakLinks(); + + AnnotationUtil.getLinks(riakLinks, pojo); + + assertTrue(riakLinks.isEmpty()); + } + + @Test + public void noAnnotatedLinksFieldOrMethod() + { + com.basho.riak.client.core.query.links.RiakLinks riakLinks = + new com.basho.riak.client.core.query.links.RiakLinks(); + Object o = new Object(); + + RiakLink link = new RiakLink("bucket", "key", "tag"); + riakLinks.addLink(link); + link = new RiakLink("bucket", "key2", "tag"); + riakLinks.addLink(link); + AnnotationUtil.populateLinks(riakLinks, o); // should do nothing + + riakLinks.removeAllLinks(); + AnnotationUtil.getLinks(riakLinks, o); + + assertTrue(riakLinks.isEmpty()); + } + + @Test public void getUsermetaField() + { + final String userMetaItemOne = "userMetaItemOne"; + final Map userMetaData = new HashMap() {{ + put("key1", "value1"); + put("key2", "value2"); + put("key3", "value3"); + }}; + + PojoWithAnnotatedFields obj = new PojoWithAnnotatedFields(); + obj.usermeta = userMetaData; + obj.metaItemOne = userMetaItemOne; + + RiakUserMetadata meta = new RiakUserMetadata(); + AnnotationUtil.getUsermetaData(meta, obj); + + for (String key : userMetaData.keySet()) + { + assertTrue(meta.containsKey(key)); + assertEquals(meta.get(key), userMetaData.get(key)); + } + + assertTrue(meta.containsKey(META_KEY_ONE)); + assertEquals(userMetaItemOne, meta.get(META_KEY_ONE)); + } + + @Test + public void getUserMetaMethod() + { + final String userMetaItemOne = "userMetaItemOne"; + final Map userMetaData = new HashMap() {{ + put("key1", "value1"); + put("key2", "value2"); + put("key3", "value3"); + }}; + + PojoWithAnnotatedMethods obj = new PojoWithAnnotatedMethods(); + obj.setUsermeta(userMetaData); + obj.setMetaItemOne(userMetaItemOne); + + RiakUserMetadata meta = new RiakUserMetadata(); + AnnotationUtil.getUsermetaData(meta, obj); + + for (String key : userMetaData.keySet()) + { + assertTrue(meta.containsKey(key)); + assertEquals(meta.get(key), userMetaData.get(key)); + } + + assertTrue(meta.containsKey(META_KEY_ONE)); + assertEquals(userMetaItemOne, meta.get(META_KEY_ONE)); + } + + @Test + public void setUserMetaField() + { + RiakUserMetadata meta = new RiakUserMetadata(); + meta.put("key1", "value1"); + meta.put("key2", "value2"); + meta.put("key3", "value3"); + meta.put(META_KEY_ONE, "userMetaItemOne"); + + PojoWithAnnotatedFields obj = new PojoWithAnnotatedFields(); + AnnotationUtil.populateUsermeta(meta, obj); + + assertNotNull(obj.usermeta); + // The single annotated field is removed + assertEquals(3, obj.usermeta.size()); + + for (String key : obj.usermeta.keySet()) + { + assertTrue(meta.containsKey(key)); + assertEquals(meta.get(key), obj.usermeta.get(key)); + } + + assertNotNull(obj.metaItemOne); + assertEquals("userMetaItemOne", obj.metaItemOne); + + } + + @Test + public void setUserMetaMethod() + { + RiakUserMetadata meta = new RiakUserMetadata(); + meta.put("key1", "value1"); + meta.put("key2", "value2"); + meta.put("key3", "value3"); + meta.put(META_KEY_ONE, "userMetaItemOne"); + + PojoWithAnnotatedMethods obj = new PojoWithAnnotatedMethods(); + AnnotationUtil.populateUsermeta(meta, obj); + + assertNotNull(obj.usermeta); + // The single annotated field is removed + assertEquals(3, obj.usermeta.size()); + + for (String key : obj.usermeta.keySet()) + { + assertTrue(meta.containsKey(key)); + assertEquals(meta.get(key), obj.usermeta.get(key)); + } + + assertNotNull(obj.metaItemOne); + assertEquals("userMetaItemOne", obj.metaItemOne); + + } + + @Test + public void illegalUserMetaFieldType() + { + RiakUserMetadata meta = new RiakUserMetadata(); + + Object o = new Object() { + @RiakUsermeta + Boolean meta; + }; + + try + { + AnnotationUtil.populateUsermeta(meta, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + o = new Object() { + @RiakUsermeta + Map meta; + }; + + try + { + AnnotationUtil.populateUsermeta(meta, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + o = new Object() { + @RiakUsermeta(key="metaKey") + Long meta; + }; + + try + { + AnnotationUtil.populateUsermeta(meta, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + } + + @Test + public void illegalUserMetaGetterType() + { + RiakUserMetadata meta = new RiakUserMetadata(); + + Object o = new Object() { + @RiakUsermeta + public Boolean getMeta() + { + return true; + }; + + @RiakUsermeta + public void setMeta(Map meta) {} + }; + + try + { + AnnotationUtil.populateUsermeta(meta, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + o = new Object() { + @RiakUsermeta + public Map getMeta() + { + return null; + }; + + @RiakUsermeta + public void setMeta(Map meta) {} + }; + + try + { + AnnotationUtil.populateUsermeta(meta, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + o = new Object() { + @RiakUsermeta(key="metaKey") + public Long getMeta() + { + return 0L; + }; + @RiakUsermeta(key="metaKey") + public void setMeta(String meta) {} + }; + + try + { + AnnotationUtil.populateUsermeta(meta, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + } + + @Test + public void illegalUserMetaSetterType() + { + RiakUserMetadata meta = new RiakUserMetadata(); + + Object o = new Object() { + @RiakUsermeta + public Map getMeta() + { + return null; + }; + + @RiakUsermeta + public void setMeta(Boolean meta) {} + }; + + try + { + AnnotationUtil.populateUsermeta(meta, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + + o = new Object() { + @RiakUsermeta + public Map getMeta() + { + return null; + }; + + @RiakUsermeta + public void setMeta(Map meta) {} + }; + + try + { + AnnotationUtil.populateUsermeta(meta, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + o = new Object() { + @RiakUsermeta(key="metaKey") + public String getMeta() + { + return null; + }; + @RiakUsermeta(key="metaKey") + public void setMeta(Long meta) {} + }; + + try + { + AnnotationUtil.populateUsermeta(meta, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + } + + + @Test + public void singleMetaAnnotationMissingKey() + { + RiakUserMetadata meta = new RiakUserMetadata(); + + Object o = new Object() { + @RiakUsermeta + String meta; + }; + + try + { + AnnotationUtil.populateUsermeta(meta, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + + o = new Object() { + @RiakUsermeta + public String getSingleMeta() + { + return ""; + } + + @RiakUsermeta(key="key") + public void setSingleMeta(String value) {} + }; + + try + { + AnnotationUtil.populateUsermeta(meta, o); + fail("Excepted IllegalArgumentException to be thrown"); + } + catch (RuntimeException e) + { + assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + } + } + + @Test + public void annotatedMetaFieldIsNull() + { + PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + RiakUserMetadata meta = new RiakUserMetadata(); + AnnotationUtil.getUsermetaData(meta, pojo); + + assertTrue(meta.isEmpty()); + + } + + @Test + public void annotatedMetaMethodIsNull() + { + PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + RiakUserMetadata meta = new RiakUserMetadata(); + AnnotationUtil.getUsermetaData(meta, pojo); + + assertTrue(meta.isEmpty()); + } + + @Test + public void riakMetaEmptyFields() + { + PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + RiakUserMetadata meta = new RiakUserMetadata(); + AnnotationUtil.populateUsermeta(meta, pojo); + + assertNotNull(pojo.usermeta); + // Single entries should be null + assertNull(pojo.metaItemOne); + } + + @Test + public void riakMetaEmptyMethods() + { + PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + RiakUserMetadata meta = new RiakUserMetadata(); + AnnotationUtil.populateUsermeta(meta, pojo); + + assertNotNull(pojo.getUsermeta()); + // Single entries should be null + assertNull(pojo.getMetaItemOne()); + } + + @Test + public void getContentTypeField() + { + final String expected = "application/octet-stream"; + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + pojo.contentType = expected; + + assertEquals(expected, AnnotationUtil.getContentType(pojo)); + assertEquals(expected, AnnotationUtil.getContentType(pojo, null)); + } + + @Test + public void setContentTypeField() + { + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + final String expected = "application/octet-stream"; + AnnotationUtil.setContentType(pojo, expected); + + assertNotNull(pojo.contentType); + assertEquals(expected.toString(), pojo.contentType); + } + + @Test + public void getContentTypeMethod() + { + final String expected = "application/octet-stream"; + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + pojo.setContentType(expected); + + assertEquals(expected, AnnotationUtil.getContentType(pojo)); + assertEquals(expected, AnnotationUtil.getContentType(pojo, null)); + } + + @Test + public void setContentTypeMethod() + { + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + final String expected = "application/octet-stream"; + AnnotationUtil.setContentType(pojo, expected); + + assertNotNull(pojo.getContentType()); + assertEquals(expected, pojo.getContentType()); + } + + @Test(expected = RuntimeException.class) + public void getNonStringContentTypeField() + { + final Object o = new Object() { + @RiakContentType + Date key; + }; + + AnnotationUtil.getContentType(o); + } + + @Test(expected = RuntimeException.class) + public void getNonStringContentTypeGetter() + { + final Object o = new Object() { + @RiakContentType + Date getKey() { return null; } + + @RiakContentType + void setKey(String key) {} + + }; + + AnnotationUtil.getContentType(o); + } + + @Test(expected = RuntimeException.class) + public void getNonStringContentTypeSetter() + { + final Object o = new Object() { + @RiakContentType + String getKey() { return null; } + + @RiakContentType + void setKey(Date key) {} + + }; + + AnnotationUtil.getContentType(o); + } + + @Test + public void noContentTypeFieldOrMethod() + { + final Object o = new Object(); + + assertNull(AnnotationUtil.getContentType(o)); + assertNotNull(AnnotationUtil.getContentType(o, "default")); + AnnotationUtil.setContentType(o, "default"); // should do nothing + } + + @Test + public void getNullContentTypeField() + { + final String expected = null; + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + pojo.contentType = expected; + + assertNull(AnnotationUtil.getContentType(pojo)); + assertNotNull(AnnotationUtil.getContentType(pojo, "default")); + } + + @Test + public void getNullContentTypeMethod() + { + final String expected = null; + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + pojo.setContentType(expected); + + assertNull(AnnotationUtil.getContentType(pojo)); + assertNotNull(AnnotationUtil.getContentType(pojo, "default")); + } + + @Test + public void setVTagField() + { + final String expected = "vtag"; + final PojoWithAnnotatedFields pojo = new PojoWithAnnotatedFields(); + AnnotationUtil.setVTag(pojo, expected); + assertEquals(expected, pojo.vtag); + assertEquals(expected, pojo.vtag); + } + + @Test + public void setVTagMethod() + { + final String expected = "vtag"; + final PojoWithAnnotatedMethods pojo = new PojoWithAnnotatedMethods(); + AnnotationUtil.setVTag(pojo, expected); + assertEquals(expected, pojo.vtag); + assertEquals(expected, pojo.vtag); + } + + @Test(expected = RuntimeException.class) + public void getNonStringVTagField() + { + final Object o = new Object() { + @RiakVTag + Date key; + }; + + AnnotationUtil.setVTag(o, null); + } + + @Test(expected = RuntimeException.class) + public void getNonStringVTagSetter() + { + final Object o = new Object() { + @RiakVTag + public void setVTag(Date vtag) {} + }; + + AnnotationUtil.setVTag(o, null); + } + + protected static final String META_KEY_ONE = "metaKeyOne"; + + protected static final class PojoWithAnnotatedFields + { + @RiakKey + String key; + + @RiakBucketName + String bucketName; + + @RiakBucketType + String bucketType; + + @RiakVClock + VClock vclock; + + @RiakUsermeta(key = META_KEY_ONE) + String metaItemOne; + + @RiakUsermeta + Map usermeta; + + @RiakIndex(name = "favorite_languages") + public Set languages; + + @RiakIndex(name = "lucky_language") + public String luckyLanguage; + + @RiakIndex(name = "lucky_long") + public long luckyLong; + + @RiakIndex(name = "lucky_longlong") + public Long luckyLongLong; + + @RiakIndex(name = "longs") + public Set longs; + + @RiakIndex(name = "raw_int") + public byte[] rawInt; + + @RiakIndex(name = "raw_bin") + public byte[] rawBin; + + @RiakIndex(name = "set_raw_bin") + public Set rawBins; + + @RiakIndex(name = "set_raw_int") + public Set rawInts; + + @RiakTombstone + boolean tombstone; + + @RiakLinks + Collection links; + + @RiakContentType + String contentType; + + @RiakVTag + String vtag; + + @RiakLastModified + Long lastModified; + + + } + + protected static final class PojoWithAnnotatedByteFields + { + @RiakKey + byte[] key; + + @RiakBucketName + byte[] bucketName; + + @RiakBucketType + byte[] bucketType; + + @RiakVClock + byte[] vclock; + + @RiakTombstone + Boolean tombstone; + } + + + protected static final class PojoWithAnnotatedMethods + { + private String key; + private String bucketName; + private String bucketType; + private Map usermeta; + private String metaItemOne; + private Set longs; + private Set strings; + private long myLong; + private Long myLongLong; + private String myString; + private VClock vClock; + private boolean tombstone; + private Collection links; + private String contentType; + private String vtag; + private long lastModified; + private byte[] rawInt; + private byte[] rawBin; + private Set rawInts; + private Set rawBins; + + @RiakKey + public String getKey() + { + return this.key; + } + + @RiakKey + public void setKey(String key) + { + this.key = key; + } + + @RiakBucketName + public String getBucketName() + { + return bucketName; + } + + @RiakBucketName + public void setBucketName(String bucketName) + { + this.bucketName = bucketName; + } + + @RiakBucketType + public String getBucketType() + { + return bucketType; + } + + @RiakBucketType + public void setBucketType(String bucketType) + { + this.bucketType = bucketType; + } + + @RiakVClock + public VClock getVClock() + { + return this.vClock; + } + + @RiakVClock + public void setVClock(VClock vclock) + { + this.vClock = vclock; + } + + @RiakUsermeta + public Map getUsermeta() + { + return this.usermeta; + } + + @RiakUsermeta + public void setUsermeta(Map usermeta) + { + this.usermeta = usermeta; + } + + @RiakUsermeta(key = META_KEY_ONE) + public String getMetaItemOne() + { + return this.metaItemOne; + } + + @RiakUsermeta(key = META_KEY_ONE) + public void setMetaItemOne(String item) + { + this.metaItemOne = item; + } + + @RiakIndex(name = "longs") + public Set getLongs() { + return longs; + } + + @RiakIndex(name = "longs") + public void setLongs(Set longs) { + this.longs = longs; + } + + @RiakIndex(name = "strings") + public Set getStrings() { + return strings; + } + + @RiakIndex(name = "strings") + public void setStrings(Set strings) { + this.strings = strings; + } + + @RiakIndex(name = "long") + public long getLong() { + return myLong; + } + + @RiakIndex(name = "long") + public void setLong(long l) { + this.myLong = l; + } + + @RiakIndex(name = "longlong") + public Long getLongLong() { + return myLongLong; + } + + @RiakIndex(name = "longlong") + public void setLongLong(Long l) { + this.myLongLong = l; + } + + @RiakIndex(name = "string") + public String getString() { + return myString; + } + + @RiakIndex(name = "string") + public void setString(String s) { + this.myString = s; + } + + @RiakIndex(name ="raw_int") + public void setRawIntIndex(byte[] bytes) + { + this.rawInt = bytes; + } + + @RiakIndex(name ="raw_int") + public byte[] getRawIntIndex() + { + return rawInt; + } + + @RiakIndex(name ="set_raw_int") + public void setRawIntsIndex(Set bytes) + { + this.rawInts = bytes; + } + + @RiakIndex(name ="set_raw_int") + public Set getRawIntsIndex() + { + return rawInts; + } + + @RiakIndex(name ="raw_bin") + public void setRawBinIndex(byte[] bytes) + { + this.rawBin = bytes; + } + + @RiakIndex(name ="raw_bin") + public byte[] getRawBinIndex() + { + return rawBin; + } + + @RiakIndex(name ="set_raw_bin") + public void setRawBinssIndex(Set bytes) + { + this.rawBins = bytes; + } + + @RiakIndex(name ="set_raw_bin") + public Set getRawBinsIndex() + { + return rawBins; + } + + + @RiakTombstone + public Boolean getTombstone() { + return this.tombstone; + } + + @RiakTombstone + public void setTombstone(Boolean tombstone) { + this.tombstone = tombstone; + } + + @RiakLinks + public Collection getLinks() + { + return this.links; + } + + @RiakLinks + public void setLinks(Collection links) + { + this.links = links; + } + + @RiakContentType + public String getContentType() + { + return contentType; + } + + @RiakContentType + public void setContentType(String contentType) + { + this.contentType = contentType; + } + + @RiakVTag + public void setVTag(String vtag) + { + this.vtag = vtag; + } + + @RiakLastModified + public void setLastModified(Long lastModified) + { + this.lastModified = lastModified; + } + } + + protected static final class PojoWithAnnotatedByteMethods + { + private byte[] key; + private byte[] bucketName; + private byte[] bucketType; + private byte[] vclock; + private boolean tombstone; + + @RiakKey + public byte[] getKey() + { + return this.key; + } + + @RiakKey + public void setKey(byte[] key) + { + this.key = key; + } + + @RiakBucketName + public byte[] getBucketName() + { + return bucketName; + } + + @RiakBucketName + public void setBucketName(byte[] bucketName) + { + this.bucketName = bucketName; + } + + @RiakBucketType + public void setBucketType(byte[] bucketType) + { + this.bucketType = bucketType; + } + + @RiakBucketType + public byte[] getBucketType() + { + return bucketType; + } + + @RiakVClock + public void setVClock(byte[] vclock) + { + this.vclock = vclock; + } + + @RiakVClock + public byte[] getVClock() + { + return vclock; + } + + @RiakTombstone + public boolean getTombstone() { + return this.tombstone; + } + + @RiakTombstone + public void setTombstone(boolean tombstone) { + this.tombstone = tombstone; + } + + } + +} diff --git a/src/test/java/com/basho/riak/client/core/DefaultNodeManagerTest.java b/src/test/java/com/basho/riak/client/core/DefaultNodeManagerTest.java new file mode 100644 index 000000000..399225182 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/DefaultNodeManagerTest.java @@ -0,0 +1,172 @@ +/* + * 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.core; + +import java.util.LinkedList; +import java.util.List; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.*; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + + +/** + * + * @author Brian Roach + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest(FutureOperation.class) +public class DefaultNodeManagerTest +{ + private List mockNodes; + + @Before + public void setUp() + { + mockNodes = new LinkedList(); + for (int i = 0; i < 5; i++) + { + RiakNode mock = mock(RiakNode.class); + mockNodes.add(mock); + } + } + + + @Test + public void init() + { + DefaultNodeManager nodeManager = new DefaultNodeManager(); + nodeManager.init(mockNodes); + + List nodes = Whitebox.getInternalState(nodeManager, "healthy"); + assertEquals(mockNodes.size(), nodes.size()); + } + + @Test + public void executeOnNodeSuccess() + { + FutureOperation operation = PowerMockito.mock(FutureOperation.class); + DefaultNodeManager nodeManager = new DefaultNodeManager(); + doReturn(false).when(mockNodes.get(0)).execute(operation); + doReturn(true).when(mockNodes.get(1)).execute(operation); + nodeManager.init(mockNodes); + nodeManager.executeOnNode(operation, null); + verify(mockNodes.get(0)).execute(operation); + verify(mockNodes.get(1)).execute(operation); + verify(mockNodes.get(2), never()).execute(operation); + verify(mockNodes.get(3), never()).execute(operation); + verify(mockNodes.get(4), never()).execute(operation); + } + + @Test + public void executeOnNodeFailure() + { + FutureOperation operation = PowerMockito.mock(FutureOperation.class); + DefaultNodeManager nodeManager = new DefaultNodeManager(); + nodeManager.init(mockNodes); + nodeManager.executeOnNode(operation, null); + for (int i = 0; i < mockNodes.size(); i++) + { + verify(mockNodes.get(i)).execute(operation); + } + verify(operation).setException(argThat(new IsException())); + } + + @Test + public void removeUnhealthyNode() + { + DefaultNodeManager nodeManager = new DefaultNodeManager(); + nodeManager.init(mockNodes); + nodeManager.nodeStateChanged(mockNodes.get(0), RiakNode.State.HEALTH_CHECKING); + List healthy = Whitebox.getInternalState(nodeManager, "healthy"); + List unhealthy = Whitebox.getInternalState(nodeManager, "unhealthy"); + assertEquals(mockNodes.size() - 1, healthy.size()); + assertEquals(1, unhealthy.size()); + assertEquals(mockNodes.get(0), unhealthy.get(0)); + } + + @Test + public void restoreHealthyNode() + { + DefaultNodeManager nodeManager = new DefaultNodeManager(); + nodeManager.init(mockNodes); + nodeManager.nodeStateChanged(mockNodes.get(0), RiakNode.State.HEALTH_CHECKING); + nodeManager.nodeStateChanged(mockNodes.get(0), RiakNode.State.RUNNING); + List healthy = Whitebox.getInternalState(nodeManager, "healthy"); + List unhealthy = Whitebox.getInternalState(nodeManager, "unhealthy"); + assertEquals(mockNodes.size(), healthy.size()); + assertEquals(0, unhealthy.size()); + assertTrue(healthy.contains(mockNodes.get(0))); + } + + @Test + public void removeShutdownNode() + { + DefaultNodeManager nodeManager = new DefaultNodeManager(); + nodeManager.init(mockNodes); + nodeManager.nodeStateChanged(mockNodes.get(0), RiakNode.State.SHUTDOWN); + List healthy = Whitebox.getInternalState(nodeManager, "healthy"); + List unhealthy = Whitebox.getInternalState(nodeManager, "unhealthy"); + assertEquals(mockNodes.size() - 1, healthy.size()); + assertEquals(0, unhealthy.size()); + assertTrue(!healthy.contains(mockNodes.get(0))); + } + + @Test + public void removeNode() + { + DefaultNodeManager nodeManager = new DefaultNodeManager(); + nodeManager.init(mockNodes); + nodeManager.removeNode(mockNodes.get(0)); + List healthy = Whitebox.getInternalState(nodeManager, "healthy"); + List unhealthy = Whitebox.getInternalState(nodeManager, "unhealthy"); + assertEquals(mockNodes.size() - 1, healthy.size()); + assertEquals(0, unhealthy.size()); + verify(mockNodes.get(0)).removeStateListener(nodeManager); + verify(mockNodes.get(0)).shutdown(); + } + + @Test + public void addNode() + { + DefaultNodeManager nodeManager = new DefaultNodeManager(); + nodeManager.init(mockNodes); + RiakNode newNode = mock(RiakNode.class); + nodeManager.addNode(newNode); + List healthy = Whitebox.getInternalState(nodeManager, "healthy"); + List unhealthy = Whitebox.getInternalState(nodeManager, "unhealthy"); + assertEquals(mockNodes.size() + 1, healthy.size()); + } + + private class IsException extends ArgumentMatcher + { + @Override + public boolean matches(Object argument) + { + return argument instanceof Exception; + } + + } + +} diff --git a/src/test/java/com/basho/riak/client/core/FixtureTest.java b/src/test/java/com/basho/riak/client/core/FixtureTest.java new file mode 100644 index 000000000..fc3b6ae03 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/FixtureTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013 BashoTechnologies 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.core; + +import com.basho.riak.client.core.fixture.NetworkTestFixture; +import java.io.IOException; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +/** + * + * @author Brian Roach + */ +public class FixtureTest +{ + static NetworkTestFixture fixture; + static int startingPort = 5000; + + @BeforeClass + public static void installFixture() throws IOException + { + fixture = new NetworkTestFixture(startingPort); + new Thread(fixture).start(); + } + + @AfterClass + public static void teardownFixture() throws IOException + { + fixture.shutdown(); + } +} diff --git a/src/test/java/com/basho/riak/client/core/FutureOperationTest.java b/src/test/java/com/basho/riak/client/core/FutureOperationTest.java new file mode 100644 index 000000000..5081e4061 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/FutureOperationTest.java @@ -0,0 +1,412 @@ +/* + * Copyright 2013 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.core; + +import com.google.protobuf.Message; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + + +/** + * @author Brian Roach + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({FutureOperation.class, RiakMessage.class}) +public class FutureOperationTest +{ + @Test + public void setsRetrier() throws Exception + { + FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); + OperationRetrier retrier = mock(OperationRetrier.class); + + operation.setRetrier(retrier, 1); + OperationRetrier r = Whitebox.getInternalState(operation, "retrier"); + assertEquals(r, retrier); + } + + @Test + public void notifiesRetrier() + { + final int NUM_TRIES = 2; + + FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); + OperationRetrier retrier = mock(OperationRetrier.class); + RiakMessage response = mock(RiakMessage.class); + + operation.setRetrier(retrier, NUM_TRIES); + operation.setException(new Exception()); + verify(retrier).operationFailed(operation, NUM_TRIES - 1); + + operation.setResponse(response); + verify(retrier).operationComplete(operation, NUM_TRIES - 2); + } + + @Test + public void exceptionWithNoRemainingRetriesIsDone() + { + final int NUM_TRIES = 1; + + FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); + OperationRetrier retrier = mock(OperationRetrier.class); + + operation.setRetrier(retrier, NUM_TRIES); + operation.setException(new Exception()); + verify(retrier).operationFailed(operation, 0); + assertTrue(operation.isDone()); + + } + + @Test + public void exceptionWithRemainingRetriesIsNotDone() + { + final int NUM_TRIES = 2; + + FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); + OperationRetrier retrier = mock(OperationRetrier.class); + operation.setRetrier(retrier, NUM_TRIES); + operation.setException(new Exception()); + assertFalse(operation.isDone()); + } + + @Test + public void resultWithRemainingRetriesIsDone() + { + final int NUM_TRIES = 3; + + FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); + OperationRetrier retrier = mock(OperationRetrier.class); + RiakMessage response = PowerMockito.mock(RiakMessage.class); + + operation.setRetrier(retrier, NUM_TRIES); + operation.setResponse(response); + verify(retrier).operationComplete(operation, NUM_TRIES - 1); + assertTrue(operation.isDone()); + } + + @Test + public void isDoneAllowsGet() throws InterruptedException, ExecutionException + { + final int NUM_TRIES = 3; + + FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); + OperationRetrier retrier = mock(OperationRetrier.class); + RiakMessage response = PowerMockito.mock(RiakMessage.class); + + operation.setRetrier(retrier, NUM_TRIES); + operation.setResponse(response); + verify(retrier).operationComplete(operation, NUM_TRIES - 1); + assertTrue(operation.isDone()); + assertNotNull(operation.get()); + } + + @Test(expected=TimeoutException.class) + public void notDoneBlocksGet() throws InterruptedException, ExecutionException, TimeoutException + { + final int NUM_TRIES = 3; + + FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); + OperationRetrier retrier = mock(OperationRetrier.class); + + operation.setRetrier(retrier, NUM_TRIES); + operation.setException(new Exception()); + assertFalse(operation.isDone()); + operation.get(10, TimeUnit.MILLISECONDS); + + } + + @Test + public void notifiesListenersAfterSuccess() + { + + FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); + RiakMessage response = PowerMockito.mock(RiakMessage.class); + + final AtomicBoolean called = new AtomicBoolean(false); + operation.addListener(new RiakFutureListener() + { + @Override + public void handle(RiakFuture f) + { + called.set(true); + } + }); + + operation.setResponse(response); + + assertTrue(operation.isDone()); + assertTrue(called.get()); + + } + + @Test + public void notifiesListenersAfterStreamingSuccess() + { + + FutureOperation operation = PowerMockito.spy(new StreamingFutureOperationImpl(2)); + RiakMessage response = PowerMockito.mock(RiakMessage.class); + + final AtomicInteger called = new AtomicInteger(0); + operation.addListener(new RiakFutureListener() + { + @Override + public void handle(RiakFuture f) + { + called.incrementAndGet(); + } + }); + + operation.setResponse(response); + operation.setResponse(response); + + assertTrue(operation.isDone()); + assertEquals(1, called.get()); + + } + + @Test + public void notifiesLisetnersAfterFailure() + { + + FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); + + final AtomicBoolean called = new AtomicBoolean(false); + operation.addListener(new RiakFutureListener() + { + @Override + public void handle(RiakFuture f) + { + called.set(true); + } + }); + + operation.setException(new Exception()); + + assertTrue(operation.isDone()); + assertTrue(called.get()); + + } + + @Test + public void notifiesLisetnersAfterStreamingFailure() + { + + FutureOperation operation = PowerMockito.spy(new StreamingFutureOperationImpl(3)); + RiakMessage response = PowerMockito.mock(RiakMessage.class); + + final AtomicInteger called = new AtomicInteger(0); + operation.addListener(new RiakFutureListener() + { + @Override + public void handle(RiakFuture f) + { + called.incrementAndGet(); + } + }); + + operation.setResponse(response); + operation.setException(new Exception()); + + assertEquals(1, called.get()); + assertTrue(operation.isDone()); + + } + + @Test + public void notifiesOnAddAfterComplete() + { + + FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); + RiakMessage response = PowerMockito.mock(RiakMessage.class); + + operation.setResponse(response); + + final AtomicBoolean called = new AtomicBoolean(false); + operation.addListener(new RiakFutureListener() + { + @Override + public void handle(RiakFuture f) + { + called.set(true); + } + }); + + assertTrue(operation.isDone()); + assertTrue(called.get()); + + } + + @Test + public void notifiesOnAddAfterStreamingComplete() + { + + StreamingFutureOperationImpl operation = PowerMockito.spy(new StreamingFutureOperationImpl(2)); + RiakMessage response = PowerMockito.mock(RiakMessage.class); + + operation.setResponse(response); + assertFalse(operation.isDone()); + + operation.setResponse(response); + assertTrue(operation.isDone()); + + } + + @Test + public void removedListenersDoNotGetCalled() + { + + FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); + RiakMessage response = PowerMockito.mock(RiakMessage.class); + + final AtomicBoolean called = new AtomicBoolean(false); + RiakFutureListener listener = new RiakFutureListener() + { + @Override + public void handle(RiakFuture f) + { + called.set(true); + } + }; + + operation.addListener(listener); + operation.removeListener(listener); + operation.setResponse(response); + + assertTrue(operation.isDone()); + assertFalse(called.get()); + + } + + @Test(expected = IllegalStateException.class) + public void canOnlySetSuccessOnce() + { + FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); + RiakMessage response = PowerMockito.mock(RiakMessage.class); + + operation.setResponse(response); + operation.setResponse(response); + + } + + @Test(expected = IllegalStateException.class) + public void canOnlySetFailureOnce() + { + FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); + + operation.setException(new Exception()); + operation.setException(new Exception()); + + } + + @Test(expected = IllegalStateException.class) + public void canOnlySetSuccessOrFailure() + { + FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); + RiakMessage response = PowerMockito.mock(RiakMessage.class); + + operation.setResponse(response); + operation.setException(new Exception()); + + } + + private class FutureOperationImpl extends FutureOperation + { + public FutureOperationImpl() + { + } + + @Override + protected String convert(List rawResponse) + { + return "Fake!"; + } + + @Override + protected RiakMessage createChannelMessage() + { + return new RiakMessage((byte) 0, new byte[0]); + } + + @Override + protected Message decode(RiakMessage rawMessage) + { + return null; + } + + @Override + public Void getQueryInfo() + { + return null; + } + } + + private class StreamingFutureOperationImpl extends FutureOperation + { + private int tries; + + public StreamingFutureOperationImpl(int tries) + { + this.tries = tries; + } + + @Override + protected String convert(List rawResponse) + { + return "Fake!"; + } + + @Override + protected RiakMessage createChannelMessage() + { + return new RiakMessage((byte) 0, new byte[0]); + } + + @Override + protected boolean done(Message rawMessage) + { + return --tries <= 0; + } + + @Override + protected Message decode(RiakMessage rawMessage) + { + return null; + } + + @Override + public Void getQueryInfo() + { + return null; + } + } + + +} diff --git a/src/test/java/com/basho/riak/client/core/RiakClusterFixtureTest.java b/src/test/java/com/basho/riak/client/core/RiakClusterFixtureTest.java new file mode 100644 index 000000000..14100e6df --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/RiakClusterFixtureTest.java @@ -0,0 +1,211 @@ +/* + * 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.core; + +import com.basho.riak.client.core.fixture.NetworkTestFixture; +import com.basho.riak.client.core.operations.FetchOperation; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import java.io.IOException; +import java.net.UnknownHostException; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import org.junit.After; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; + + +/** + * + * @author Brian Roach + */ +public class RiakClusterFixtureTest +{ + private NetworkTestFixture[] fixtures; + + + @Before + public void setUp() throws IOException + { + fixtures = new NetworkTestFixture[3]; + for (int i = 0, j = 5000; i < 3; i++, j += 1000) + { + fixtures[i] = new NetworkTestFixture(j); + new Thread(fixtures[i]).start(); + } + } + + @After + public void tearDown() throws IOException + { + for (int i = 0; i < fixtures.length; i++) + { + fixtures[i].shutdown(); + } + } + + @Test + public void operationSuccess() throws UnknownHostException, InterruptedException, ExecutionException + { + List list = new LinkedList(); + + for (int i = 5000; i < 8000; i += 1000) + { + RiakNode.Builder builder = new RiakNode.Builder() + .withMinConnections(10) + .withRemotePort(i + NetworkTestFixture.PB_FULL_WRITE_STAY_OPEN); + list.add(builder.build()); + } + + RiakCluster cluster = new RiakCluster.Builder(list).build(); + cluster.start(); + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, "test_bucket"); + Location location = new Location(ns, "test_key2"); + + FetchOperation operation = + new FetchOperation.Builder(location).build(); + + cluster.execute(operation); + + try + { + FetchOperation.Response response = operation.get(); + assertEquals(response.getObjectList().get(0).getValue().toString(), "This is a value!"); + assertTrue(!response.isNotFound()); + } + catch(InterruptedException e) + { + + } + + cluster.shutdown().get(); + + } + + @Test + public void operationFail() throws UnknownHostException, ExecutionException, InterruptedException + { + List list = new LinkedList(); + + for (int i = 5000; i < 8000; i += 1000) + { + RiakNode.Builder builder = new RiakNode.Builder() + .withMinConnections(10) + .withRemotePort(i + NetworkTestFixture.ACCEPT_THEN_CLOSE); + list.add(builder.build()); + } + + RiakCluster cluster = new RiakCluster.Builder(list).build(); + + cluster.start(); + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, "test_bucket"); + Location location = new Location(ns, "test_key2"); + + FetchOperation operation = + new FetchOperation.Builder(location) + .build(); + + cluster.execute(operation); + + try + { + operation.await(); + assertFalse(operation.isSuccess()); + assertNotNull(operation.cause()); + } + finally + { + cluster.shutdown().get(); + } + } + + @Test + public void testStateListener() throws UnknownHostException, InterruptedException, ExecutionException + { + List list = new LinkedList(); + + for (int i = 5000; i < 8000; i += 1000) + { + RiakNode.Builder builder = new RiakNode.Builder() + .withMinConnections(10) + .withRemotePort(i + NetworkTestFixture.PB_FULL_WRITE_STAY_OPEN); + list.add(builder.build()); + } + + RiakCluster cluster = new RiakCluster.Builder(list).build(); + + StateListener listener = new StateListener(); + cluster.registerNodeStateListener(listener); + + cluster.start(); + + // Yeah, yeah, fragile ... whatever + Thread.sleep(3000); + + cluster.shutdown().get(); + + + // Upon registering the initial node state of each node should be sent. + assertEquals(3, listener.stateCreated); + // All three nodes should go through all three states and notify. + assertEquals(3, listener.stateRunning); + assertEquals(3, listener.stateShuttingDown); + assertEquals(3, listener.stateShutdown); + + + } + + public static class StateListener implements NodeStateListener + { + + public int stateCreated; + public int stateRunning; + public int stateShuttingDown; + public int stateShutdown; + + @Override + public void nodeStateChanged(RiakNode node, RiakNode.State state) + { + switch(state) + { + case CREATED: + stateCreated++; + break; + case RUNNING: + stateRunning++; + break; + case SHUTTING_DOWN: + stateShuttingDown++; + break; + case SHUTDOWN: + stateShutdown++; + break; + default: + break; + } + } + + } + + +} diff --git a/src/test/java/com/basho/riak/client/core/RiakClusterTest.java b/src/test/java/com/basho/riak/client/core/RiakClusterTest.java new file mode 100644 index 000000000..dcc7e7f88 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/RiakClusterTest.java @@ -0,0 +1,120 @@ +/* + * 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.core; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import java.net.UnknownHostException; +import java.util.concurrent.LinkedBlockingQueue; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.*; + + + +/** + * + * @author Brian Roach + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest(FutureOperation.class) +public class RiakClusterTest +{ + @Test + public void builderCreatesCluster() throws UnknownHostException + { + RiakNode.Builder nodeBuilder = new RiakNode.Builder(); + RiakCluster cluster = new RiakCluster.Builder(nodeBuilder.build()).build(); + assertTrue(!cluster.getNodes().isEmpty()); + } + + @Test + public void addNodeToCluster() throws UnknownHostException + { + NodeManager nodeManager = mock(NodeManager.class); + RiakNode node = mock(RiakNode.class); + RiakNode.Builder nodeBuilder = spy(new RiakNode.Builder()); + doReturn(node).when(nodeBuilder).build(); + + RiakCluster cluster = new RiakCluster.Builder(nodeBuilder.build()).withNodeManager(nodeManager).build(); + cluster.addNode(nodeBuilder.build()); + assertEquals(2, cluster.getNodes().size()); + verify(nodeManager).addNode(node); + } + + @Test + public void removeNodeFromCluster() throws UnknownHostException + { + NodeManager nodeManager = mock(NodeManager.class); + RiakNode node = mock(RiakNode.class); + RiakNode.Builder nodeBuilder = spy(new RiakNode.Builder()); + doReturn(node).when(nodeBuilder).build(); + doReturn(true).when(nodeManager).removeNode(node); + + RiakCluster cluster = new RiakCluster.Builder(nodeBuilder.build()).withNodeManager(nodeManager).build(); + assertTrue(cluster.removeNode(node)); + verify(nodeManager).removeNode(node); + assertEquals(0, cluster.getNodes().size()); + } + + @Test + public void allNodesShutdownStopsCluster() throws UnknownHostException + { + NodeManager nodeManager = mock(NodeManager.class); + RiakNode node = mock(RiakNode.class); + RiakNode.Builder nodeBuilder = spy(new RiakNode.Builder()); + doReturn(node).when(nodeBuilder).build(); + doReturn(true).when(nodeManager).removeNode(node); + + RiakCluster cluster = new RiakCluster.Builder(nodeBuilder.build()).withNodeManager(nodeManager).build(); + cluster.nodeStateChanged(node, RiakNode.State.SHUTDOWN); + RiakCluster.State state = Whitebox.getInternalState(cluster, "state"); + assertEquals(state, RiakCluster.State.SHUTDOWN); + } + + @Test + @SuppressWarnings("unchecked") + public void clusterExecutesOperation() throws UnknownHostException + { + NodeManager nodeManager = mock(NodeManager.class); + FutureOperation operation = PowerMockito.mock(FutureOperation.class); + RiakNode node = mock(RiakNode.class); + RiakNode.Builder nodeBuilder = spy(new RiakNode.Builder()); + doReturn(node).when(nodeBuilder).build(); + doReturn(true).when(node).execute(operation); + + RiakCluster cluster = new RiakCluster.Builder(nodeBuilder.build()).withNodeManager(nodeManager).build(); + Whitebox.setInternalState(cluster, "state", RiakCluster.State.RUNNING); + cluster.execute(operation); + assertEquals(1, cluster.inFlightCount()); + verify(nodeManager).executeOnNode(operation, null); + cluster.operationComplete(operation, 2); + assertEquals(0, cluster.inFlightCount()); + + cluster.execute(operation); + cluster.operationFailed(operation, 1); + LinkedBlockingQueue retryQueue = Whitebox.getInternalState(cluster, "retryQueue"); + assertEquals(1, retryQueue.size()); + + + } +} diff --git a/src/test/java/com/basho/riak/client/core/RiakNodeFixtureTest.java b/src/test/java/com/basho/riak/client/core/RiakNodeFixtureTest.java new file mode 100644 index 000000000..64d7a45dd --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/RiakNodeFixtureTest.java @@ -0,0 +1,323 @@ +/* + * 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.core; + +import com.basho.riak.client.core.RiakNode.State; +import com.basho.riak.client.core.fixture.NetworkTestFixture; +import com.basho.riak.client.core.operations.FetchOperation; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import io.netty.channel.Channel; +import org.junit.Test; +import org.powermock.reflect.Whitebox; + +import java.io.IOException; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public class RiakNodeFixtureTest extends FixtureTest +{ + + @Test + public void closedConnectionsTriggerHealthCheck() throws UnknownHostException, InterruptedException, Exception + { + RiakNode node = new RiakNode.Builder() + .withRemotePort(startingPort + NetworkTestFixture.ACCEPT_THEN_CLOSE) + .withMinConnections(10) + .build(); + StateListener listener = new StateListener(RiakNode.State.HEALTH_CHECKING); + node.start(); + node.addStateListener(listener); + assertTrue(listener.get(10)); + node.shutdown().get(); + + + } + + @Test + public void failedConnectionsTriggerHealthCheck() throws UnknownHostException, InterruptedException, Exception + { + RiakNode node = new RiakNode.Builder() + .withRemotePort(startingPort + NetworkTestFixture.NO_LISTENER) + .withMinConnections(10) + .withConnectionTimeout(10) + .build(); + + StateListener listener = new StateListener(RiakNode.State.HEALTH_CHECKING); + node.addStateListener(listener); + node.start(); + assertTrue(listener.get(10)); + node.shutdown().get(); + } + + @Test + public void operationFailuresTriggerHealthCheck() throws UnknownHostException, InterruptedException, Exception + { + RiakNode node = + new RiakNode.Builder() + .withRemoteAddress("127.0.0.1") + .withRemotePort(startingPort + NetworkTestFixture.PB_FULL_WRITE_ERROR_STAY_OPEN) + .build(); + + StateListener listener = new StateListener(RiakNode.State.HEALTH_CHECKING); + node.addStateListener(listener); + node.start(); + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, "test_bucket"); + Location location = new Location(ns, "test_key2"); + + for (int i = 0; i < 6; i++) + { + FetchOperation operation = + new FetchOperation.Builder(location) + .build(); + + boolean accepted = node.execute(operation); + assertTrue(accepted); + operation.await(); + assertFalse(operation.isSuccess()); + } + + assertTrue(listener.get(10)); + node.shutdown().get(); + } + + @Test + public void idleConnectionsAreRemoved() throws UnknownHostException, InterruptedException, Exception + { + RiakNode node = new RiakNode.Builder() + .withRemotePort(startingPort + NetworkTestFixture.PB_FULL_WRITE_STAY_OPEN) + .withMinConnections(10) + .withIdleTimeout(1000) + .build(); + + node.start(); + + // The node has 10 connections that should never be reaped. We want to make + // it create 2 more by checking out 12, then return them all. The extra 2 + // should get reaped. + + List channelList = new LinkedList(); + for (int i = 0; i < 12; i++) + { + channelList.add((Channel)Whitebox.invokeMethod(node, "getConnection", new Object[0])); + } + + for (Channel c : channelList) + { + Whitebox.invokeMethod(node, "returnConnection", c); + } + + LinkedBlockingDeque available = Whitebox.getInternalState(node, "available"); + assertEquals(available.size(), 12); + + Thread.sleep(10000); + + assertEquals(available.size(), 10); + + node.shutdown().get(); + + } + + @Test + public void nodeGoingDown() throws UnknownHostException, IOException, InterruptedException, ExecutionException + { + RiakNode node = new RiakNode.Builder() + .withRemotePort(startingPort + NetworkTestFixture.PB_FULL_WRITE_STAY_OPEN) + .withMinConnections(10) + .withIdleTimeout(1000) + .build(); + + StateListener listener = new StateListener(RiakNode.State.HEALTH_CHECKING); + node.start(); + node.addStateListener(listener); + + try + { + fixture.shutdown(); + assertTrue(listener.get(10)); + assertEquals(node.getNodeState(), State.HEALTH_CHECKING); + + node.shutdown().get(); + } + finally + { + fixture = new NetworkTestFixture(startingPort); + new Thread(fixture).start(); + } + } + + @Test + public void nodeRecovery() throws UnknownHostException, IOException, InterruptedException, ExecutionException + { + RiakNode node = new RiakNode.Builder() + .withRemotePort(startingPort + NetworkTestFixture.PB_FULL_WRITE_STAY_OPEN) + .withMinConnections(10) + .withIdleTimeout(1000) + .build(); + + StateListener listener = new StateListener(RiakNode.State.HEALTH_CHECKING); + node.start(); + node.addStateListener(listener); + + try + { + fixture.shutdown(); + + assertTrue(listener.get(10)); + node.removeStateListener(listener); + listener = new StateListener(RiakNode.State.RUNNING); + node.addStateListener(listener); + + } + finally + { + fixture = new NetworkTestFixture(startingPort); + new Thread(fixture).start(); + } + + assertTrue(listener.get(10)); + assertEquals(node.getNodeState(), State.RUNNING); + node.shutdown().get(); + } + + @Test + public void operationSuccess() throws UnknownHostException, InterruptedException, ExecutionException + { + RiakNode node = + new RiakNode.Builder() + .withRemoteAddress("127.0.0.1") + .withRemotePort(startingPort + NetworkTestFixture.PB_FULL_WRITE_STAY_OPEN) + .build(); + + node.start(); + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, "test_bucket"); + Location location = new Location(ns, "test_key2"); + + FetchOperation operation = + new FetchOperation.Builder(location) + .build(); + + boolean accepted = node.execute(operation); + assertTrue(accepted); + FetchOperation.Response response = operation.get(); + assertEquals(response.getObjectList().get(0).getValue().toString(), "This is a value!"); + assertTrue(!response.isNotFound()); + node.shutdown().get(); + } + + @Test + public void operationFail() throws UnknownHostException, InterruptedException, ExecutionException + { + RiakNode node = + new RiakNode.Builder() + .withRemoteAddress("127.0.0.1") + .withRemotePort(startingPort + NetworkTestFixture.ACCEPT_THEN_CLOSE) + .build(); + node.start(); + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, "test_bucket"); + Location location = new Location(ns, "test_key2"); + + FetchOperation operation = + new FetchOperation.Builder(location) + .build(); + + boolean accepted = node.execute(operation); + operation.await(); + assertFalse(operation.isSuccess()); + assertNotNull(operation.cause()); + node.shutdown().get(); + } + + @Test + public void nodeChangesStateOnPoolState() throws UnknownHostException, IOException, InterruptedException, ExecutionException + { + RiakNode node = + new RiakNode.Builder() + .withRemoteAddress("127.0.0.1") + .withMinConnections(10) + .withRemotePort(startingPort + NetworkTestFixture.PB_FULL_WRITE_STAY_OPEN) + .build(); + + node.start(); + + StateListener listener = new StateListener(RiakNode.State.HEALTH_CHECKING); + node.addStateListener(listener); + + try + { + fixture.shutdown(); + assertTrue(listener.get(10)); + assertEquals(RiakNode.State.HEALTH_CHECKING, node.getNodeState()); + } + finally + { + fixture = new NetworkTestFixture(startingPort); + new Thread(fixture).start(); + } + + node.shutdown().get(); + + } + + public static class StateListener implements NodeStateListener + { + private final CountDownLatch latch = new CountDownLatch(1); + private final EnumSet expected; + + + public StateListener(RiakNode.State... expected) + { + this.expected = EnumSet.copyOf(Arrays.asList(expected)); + } + + @Override + public void nodeStateChanged(RiakNode node, State state) + { + expected.remove(state); + if (expected.size() == 0) + { + latch.countDown(); + } + } + + public boolean get(int timeout) throws InterruptedException + { + return latch.await(timeout, TimeUnit.SECONDS); + } + + } + +} diff --git a/src/test/java/com/basho/riak/client/core/RiakNodeTest.java b/src/test/java/com/basho/riak/client/core/RiakNodeTest.java new file mode 100644 index 000000000..cab00cbd6 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/RiakNodeTest.java @@ -0,0 +1,395 @@ +/* + * 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.core; + +import com.basho.riak.client.core.RiakNode.State; +import com.google.protobuf.Message; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelPipeline; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import java.net.UnknownHostException; +import java.util.Deque; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import static com.jayway.awaitility.Awaitility.await; +import static com.jayway.awaitility.Awaitility.fieldIn; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + + +/** + * @author Brian Roach + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bootstrap.class, FutureOperation.class, RiakMessage.class}) + +public class RiakNodeTest +{ + @Test + public void builderProducesDefaultNode() throws UnknownHostException + { + RiakNode node = new RiakNode.Builder().build(); + + assertEquals(node.getRemoteAddress(), RiakNode.Builder.DEFAULT_REMOTE_ADDRESS); + assertEquals(node.getPort(), RiakNode.Builder.DEFAULT_REMOTE_PORT); + assertEquals(node.getNodeState(), State.CREATED); + assertEquals(node.getMaxConnections(), Integer.MAX_VALUE); + assertEquals(node.getConnectionTimeout(), RiakNode.Builder.DEFAULT_CONNECTION_TIMEOUT); + assertEquals(node.getIdleTimeout(), RiakNode.Builder.DEFAULT_IDLE_TIMEOUT); + assertEquals(node.getMinConnections(), RiakNode.Builder.DEFAULT_MIN_CONNECTIONS); + assertEquals(node.availablePermits(), Integer.MAX_VALUE); + } + + @Test + public void builderProducesCorrectNode() throws UnknownHostException + { + final int IDLE_TIMEOUT = 2000; + final int CONNECTION_TIMEOUT = 2001; + final int MIN_CONNECTIONS = 2002; + final int MAX_CONNECTIONS = 2003; + final int PORT = 2004; + final int READ_TIMEOUT = 2005; + final String REMOTE_ADDRESS = "localhost"; + final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor(); + final Bootstrap BOOTSTRAP = PowerMockito.spy(new Bootstrap()); + + doReturn(BOOTSTRAP).when(BOOTSTRAP).clone(); + + RiakNode node = new RiakNode.Builder() + .withIdleTimeout(IDLE_TIMEOUT) + .withConnectionTimeout(CONNECTION_TIMEOUT) + .withMinConnections(MIN_CONNECTIONS) + .withMaxConnections(MAX_CONNECTIONS) + .withRemotePort(PORT) + .withRemoteAddress(REMOTE_ADDRESS) + .withExecutor(EXECUTOR) + .withBootstrap(BOOTSTRAP) + .build(); + + assertEquals(node.getRemoteAddress(), REMOTE_ADDRESS); + + assertEquals(node.getNodeState(), RiakNode.State.CREATED); + assertEquals(node.getMaxConnections(), MAX_CONNECTIONS); + assertEquals(node.getConnectionTimeout(), CONNECTION_TIMEOUT); + assertEquals(node.getIdleTimeout(), IDLE_TIMEOUT); + assertEquals(node.getMinConnections(), MIN_CONNECTIONS); + assertEquals(node.getRemoteAddress(), REMOTE_ADDRESS); + assertEquals(node.availablePermits(), MAX_CONNECTIONS); + assertEquals(node.getPort(), PORT); + + } + + @Test + public void nodeRegistersListeners() throws UnknownHostException + { + RiakNode node = new RiakNode.Builder().build(); + NodeStateListener listener = mock(NodeStateListener.class); + node.addStateListener(listener); + boolean removed = node.removeStateListener(listener); + assertTrue(removed); + } + + @Test + public void nodeNotifiesListeners() throws UnknownHostException, Exception + { + RiakNode node = new RiakNode.Builder().build(); + NodeStateListener listener = mock(NodeStateListener.class); + node.addStateListener(listener); + Whitebox.invokeMethod(node, "notifyStateListeners", new Object[0]); + verify(listener).nodeStateChanged(node, RiakNode.State.CREATED); + } + + @Test + public void nodeStartsMinConnections() throws InterruptedException, UnknownHostException + { + final int MIN_CONNECTIONS = 5; + + ChannelFuture future = mock(ChannelFuture.class); + Channel c = mock(Channel.class); + Bootstrap bootstrap = PowerMockito.spy(new Bootstrap()); + + doReturn(future).when(c).closeFuture(); + doReturn(true).when(c).isOpen(); + doReturn(future).when(future).await(); + doReturn(true).when(future).isSuccess(); + doReturn(c).when(future).channel(); + doReturn(future).when(bootstrap).connect(); + doReturn(bootstrap).when(bootstrap).clone(); + + RiakNode node = new RiakNode.Builder() + .withBootstrap(bootstrap) + .withMinConnections(MIN_CONNECTIONS) + .build(); + node.start(); + Deque available = Whitebox.getInternalState(node, "available"); + assertEquals(MIN_CONNECTIONS, available.size()); + assertEquals(node.getNodeState(), State.RUNNING); + } + + @Test + public void NodeRespectsMax() throws InterruptedException, UnknownHostException, Exception + { + final int MAX_CONNECTIONS = 2; + + ChannelFuture future = mock(ChannelFuture.class); + Channel c = mock(Channel.class); + Bootstrap bootstrap = PowerMockito.spy(new Bootstrap()); + + doReturn(future).when(c).closeFuture(); + doReturn(true).when(c).isOpen(); + doReturn(future).when(future).await(); + doReturn(true).when(future).isSuccess(); + doReturn(c).when(future).channel(); + doReturn(future).when(bootstrap).connect(); + doReturn(bootstrap).when(bootstrap).clone(); + + RiakNode node = new RiakNode.Builder() + .withBootstrap(bootstrap) + .withMaxConnections(MAX_CONNECTIONS) + .build(); + node.start(); + + for (int i = 0; i < MAX_CONNECTIONS; i++) + { + assertNotNull(Whitebox.invokeMethod(node, "getConnection", new Object[0])); + } + + assertNull(Whitebox.invokeMethod(node, "getConnection", new Object[0])); + assertEquals(0, node.availablePermits()); + + node.setMaxConnections(MAX_CONNECTIONS + 1); + assertNotNull(Whitebox.invokeMethod(node, "getConnection", new Object[0])); + assertEquals(0, node.availablePermits()); + } + + @Test + public void channelsReturnedCorrectly() throws InterruptedException, UnknownHostException, Exception + { + final int MAX_CONNECTIONS = 1; + + ChannelFuture future = mock(ChannelFuture.class); + Channel c = mock(Channel.class); + Bootstrap bootstrap = PowerMockito.spy(new Bootstrap()); + + doReturn(future).when(c).closeFuture(); + doReturn(true).when(c).isOpen(); + doReturn(future).when(future).await(); + doReturn(true).when(future).isSuccess(); + doReturn(c).when(future).channel(); + doReturn(future).when(bootstrap).connect(); + doReturn(bootstrap).when(bootstrap).clone(); + + RiakNode node = new RiakNode.Builder() + .withBootstrap(bootstrap) + .withMaxConnections(MAX_CONNECTIONS) + .build(); + node.start(); + + assertNotNull(Whitebox.invokeMethod(node, "getConnection", new Object[0])); + assertNull(Whitebox.invokeMethod(node, "getConnection", new Object[0])); + Whitebox.invokeMethod(node, "returnConnection", c); + Deque available = Whitebox.getInternalState(node, "available"); + assertEquals(1, available.size()); + assertNotNull(Whitebox.invokeMethod(node, "getConnection", new Object[0])); + } + + @Test + public void healthCheckChangesState() + throws InterruptedException, UnknownHostException, Exception + { + ChannelFuture future = mock(ChannelFuture.class); + Channel c = mock(Channel.class); + Bootstrap bootstrap = PowerMockito.spy(new Bootstrap()); + + doReturn(future).when(c).closeFuture(); + doReturn(true).when(c).isOpen(); + doReturn(future).when(future).await(); + doReturn(false).when(future).isSuccess(); + doReturn(c).when(future).channel(); + + doReturn(future).when(bootstrap).connect(); + doReturn(bootstrap).when(bootstrap).clone(); + + RiakNode node = new RiakNode.Builder() + .withBootstrap(bootstrap) + .build(); + + for (int i = 0; i < 5; i++) + { + ChannelFutureListener listener = Whitebox.getInternalState(node, "inAvailableCloseListener", RiakNode.class); + listener.operationComplete(future); + } + + NodeStateListener listener = mock(NodeStateListener.class); + node.addStateListener(listener); + Whitebox.setInternalState(node, "state", State.RUNNING); + Whitebox.invokeMethod(node, "checkHealth", new Object[0]); + verify(listener).nodeStateChanged(node, State.HEALTH_CHECKING); + } + + @Test + public void idleReaperTest() throws InterruptedException, UnknownHostException, Exception + { + + ChannelFuture future = mock(ChannelFuture.class); + Channel c = mock(Channel.class); + Bootstrap bootstrap = PowerMockito.spy(new Bootstrap()); + + doReturn(future).when(c).closeFuture(); + doReturn(true).when(c).isOpen(); + doReturn(future).when(future).await(); + doReturn(true).when(future).isSuccess(); + doReturn(c).when(future).channel(); + + doReturn(future).when(bootstrap).connect(); + doReturn(bootstrap).when(bootstrap).clone(); + + RiakNode node = new RiakNode.Builder() + .withBootstrap(bootstrap) + .withMinConnections(1) + .withIdleTimeout(1) + .build(); + + node.start(); + Channel[] channelArray = new Channel[6]; + for (int i = 0; i < 6; i++) + { + channelArray[i] = Whitebox.invokeMethod(node, "getConnection", new Object[0]); + assertNotNull(channelArray[i]); + } + + for (Channel channel : channelArray) + { + Whitebox.invokeMethod(node, "returnConnection", channel); + } + + Deque available = Whitebox.getInternalState(node, "available"); + assertEquals(6, available.size()); + Thread.sleep(10); + Whitebox.invokeMethod(node, "reapIdleConnections", new Object[0]); + assertEquals(1, available.size()); + } + + @Test + public void nodeExecutesOperation() throws InterruptedException, UnknownHostException + { + Channel channel = mock(Channel.class); + ChannelPipeline channelPipeline = mock(ChannelPipeline.class); + ChannelFuture future = mock(ChannelFuture.class); + FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); + RiakMessage response = PowerMockito.mock(RiakMessage.class); + Bootstrap bootstrap = PowerMockito.spy(new Bootstrap()); + + doReturn(future).when(channel).closeFuture(); + doReturn(true).when(channel).isOpen(); + doReturn(channelPipeline).when(channel).pipeline(); + doReturn(future).when(channel).writeAndFlush(operation); + doReturn(future).when(future).await(); + doReturn(true).when(future).isSuccess(); + doReturn(channel).when(future).channel(); + doReturn(future).when(bootstrap).connect(); + doReturn(bootstrap).when(bootstrap).clone(); + + RiakNode node = new RiakNode.Builder().withBootstrap(bootstrap).build(); + node.start(); + boolean accepted = node.execute(operation); + assertTrue(accepted); + verify(channel).writeAndFlush(operation); + verify(operation).setLastNode(node); + assertEquals(1, node.getNumInProgress()); + + node.onSuccess(channel, response); + assertEquals(0, node.getNumInProgress()); + verify(operation).isDone(); + } + + @Test + public void nodeFailsOperation() throws InterruptedException, UnknownHostException + { + Channel channel = mock(Channel.class); + ChannelPipeline channelPipeline = mock(ChannelPipeline.class); + ChannelFuture future = mock(ChannelFuture.class); + FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); + Throwable t = mock(Throwable.class); + Bootstrap bootstrap = PowerMockito.spy(new Bootstrap()); + + doReturn(future).when(channel).closeFuture(); + doReturn(true).when(channel).isOpen(); + doReturn(channelPipeline).when(channel).pipeline(); + doReturn(future).when(channel).writeAndFlush(operation); + doReturn(future).when(future).await(); + doReturn(true).when(future).isSuccess(); + doReturn(channel).when(future).channel(); + doReturn(future).when(bootstrap).connect(); + doReturn(bootstrap).when(bootstrap).clone(); + + RiakNode node = new RiakNode.Builder().withBootstrap(bootstrap).build(); + node.start(); + boolean accepted = node.execute(operation); + assertTrue(accepted); + verify(channel).writeAndFlush(operation); + verify(operation).setLastNode(node); + Map inProgressMap = Whitebox.getInternalState(node, "inProgressMap"); + assertEquals(1, inProgressMap.size()); + node.onException(channel, t); + await().atMost(500, TimeUnit.MILLISECONDS).until(fieldIn(operation).ofType(Throwable.class).andWithName("exception"), equalTo(t)); + } + + private class FutureOperationImpl extends FutureOperation + { + + @Override + protected String convert(List rawResponse) + { + return "value"; + } + + @Override + protected Message decode(RiakMessage rawMessage) + { + return null; + } + + @Override + protected RiakMessage createChannelMessage() + { + return new RiakMessage((byte) 0, new byte[0]); + } + + @Override + public Void getQueryInfo() + { + return null; + } + + + } + +} diff --git a/src/test/java/com/basho/riak/client/core/converters/CrdtResponseConverterTest.java b/src/test/java/com/basho/riak/client/core/converters/CrdtResponseConverterTest.java new file mode 100644 index 000000000..026a85477 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/converters/CrdtResponseConverterTest.java @@ -0,0 +1,199 @@ +/* + * 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.core.converters; + +import com.basho.riak.client.core.query.crdt.types.RiakSet; +import com.basho.riak.client.core.query.crdt.types.RiakDatatype; +import com.basho.riak.client.core.query.crdt.types.RiakMap; +import com.basho.riak.client.core.query.crdt.types.RiakCounter; +import com.basho.riak.client.core.query.crdt.types.RiakRegister; +import com.basho.riak.client.core.query.crdt.types.RiakFlag; +import com.basho.riak.client.core.util.BinaryValue; +import com.basho.riak.protobuf.RiakDtPB; +import com.google.protobuf.ByteString; +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + +public class CrdtResponseConverterTest +{ + + @Test + public void testDtUpdateRespCounter() + { + + RiakDtPB.DtUpdateResp resp = RiakDtPB.DtUpdateResp.newBuilder() + .setCounterValue(1) + .build(); + + CrdtResponseConverter converter = new CrdtResponseConverter(); + + RiakDatatype element = converter.convert(resp); + + assertTrue(element.isCounter()); + assertEquals((Long) 1L, element.getAsCounter().view()); + + } + + @Test + public void testDtUpdateRespSet() + { + + Set values = new HashSet(); + values.add(ByteString.copyFromUtf8("1")); + values.add(ByteString.copyFromUtf8("2")); + values.add(ByteString.copyFromUtf8("3")); + + Set wrappedValues = new HashSet(); + wrappedValues.add(BinaryValue.create("1")); + wrappedValues.add(BinaryValue.create("2")); + wrappedValues.add(BinaryValue.create("3")); + + RiakDtPB.DtUpdateResp resp = RiakDtPB.DtUpdateResp.newBuilder() + .addAllSetValue(values) + .build(); + + CrdtResponseConverter converter = new CrdtResponseConverter(); + + RiakDatatype element = converter.convert(resp); + + assertTrue(element.isSet()); + assertEquals(wrappedValues, element.getAsSet().view()); + + } + + @Test + public void testDtUpdateRespMap() + { + + Set setValues = new HashSet(); + setValues.add(ByteString.copyFromUtf8("1")); + setValues.add(ByteString.copyFromUtf8("2")); + setValues.add(ByteString.copyFromUtf8("3")); + + Set wrappedSetValues = new HashSet(); + wrappedSetValues.add(BinaryValue.create("1")); + wrappedSetValues.add(BinaryValue.create("2")); + wrappedSetValues.add(BinaryValue.create("3")); + + final long counterValue = 1; + + final boolean flagValue = true; + + BinaryValue registerValue = BinaryValue.create("stuff"); + + BinaryValue counterKey = BinaryValue.create("1"); + BinaryValue setKey = BinaryValue.create("2"); + BinaryValue mapKey = BinaryValue.create("3"); + BinaryValue registerKey = BinaryValue.create("4"); + BinaryValue flagKey = BinaryValue.create("5"); + + RiakDtPB.DtUpdateResp resp = RiakDtPB.DtUpdateResp.newBuilder() + .addMapValue(RiakDtPB.MapEntry.newBuilder() + .setField(RiakDtPB.MapField.newBuilder() + .setType(RiakDtPB.MapField.MapFieldType.COUNTER) + .setName(ByteString.copyFrom(counterKey.unsafeGetValue()))) + .setCounterValue(counterValue)) + .addMapValue(RiakDtPB.MapEntry.newBuilder() + .setField(RiakDtPB.MapField.newBuilder() + .setName(ByteString.copyFrom(setKey.unsafeGetValue())) + .setType(RiakDtPB.MapField.MapFieldType.SET)) + .addAllSetValue(setValues)) + .addMapValue(RiakDtPB.MapEntry.newBuilder() + .setField(RiakDtPB.MapField.newBuilder() + .setName(ByteString.copyFrom(mapKey.unsafeGetValue())) + .setType(RiakDtPB.MapField.MapFieldType.MAP))) + .addMapValue(RiakDtPB.MapEntry.newBuilder() + .setField(RiakDtPB.MapField.newBuilder() + .setName(ByteString.copyFrom(registerKey.unsafeGetValue())) + .setType(RiakDtPB.MapField.MapFieldType.REGISTER)) + .setRegisterValue(ByteString.copyFrom(registerValue.unsafeGetValue()))) + .addMapValue(RiakDtPB.MapEntry.newBuilder() + .setField(RiakDtPB.MapField.newBuilder() + .setName(ByteString.copyFrom(flagKey.unsafeGetValue())) + .setType(RiakDtPB.MapField.MapFieldType.FLAG)) + .setFlagValue(flagValue)) + .build(); + + CrdtResponseConverter converter = new CrdtResponseConverter(); + + RiakDatatype element = converter.convert(resp); + + assertTrue(element.isMap()); + + RiakMap crdtMap = element.getAsMap(); + + assertTrue(crdtMap.get(counterKey).get(0).isCounter()); + assertTrue(crdtMap.get(setKey).get(0).isSet()); + assertTrue(crdtMap.get(mapKey).get(0).isMap()); + assertTrue(crdtMap.get(registerKey).get(0).isRegister()); + assertTrue(crdtMap.get(flagKey).get(0).isFlag()); + + RiakCounter riakCounter = crdtMap.get(counterKey).get(0).getAsCounter(); + assertEquals((Long) counterValue, riakCounter.view()); + + RiakSet crdtSet = crdtMap.get(setKey).get(0).getAsSet(); + assertEquals(wrappedSetValues, crdtSet.view()); + + // the asMap doesn't have any values + + RiakRegister crdtRegister = crdtMap.get(registerKey).get(0).getAsRegister(); + assertEquals(registerValue, crdtRegister.getValue()); + + RiakFlag crdtFlag = crdtMap.get(flagKey).get(0).getAsFlag(); + assertEquals(flagValue, crdtFlag.getEnabled()); + } + + @Test + public void testDtUpdateRespNestedMap() + { + BinaryValue mapKey = BinaryValue.create("key"); + + RiakDtPB.DtUpdateResp resp = RiakDtPB.DtUpdateResp.newBuilder() + .addMapValue(RiakDtPB.MapEntry.newBuilder() + .setField(RiakDtPB.MapField.newBuilder() + .setName(ByteString.copyFrom(mapKey.unsafeGetValue())) + .setType(RiakDtPB.MapField.MapFieldType.MAP)) + .addMapValue(RiakDtPB.MapEntry.newBuilder() + .setField(RiakDtPB.MapField.newBuilder() + .setName(ByteString.copyFrom(mapKey.unsafeGetValue())) + .setType(RiakDtPB.MapField.MapFieldType.MAP)) + .addMapValue(RiakDtPB.MapEntry.newBuilder() + .setField(RiakDtPB.MapField.newBuilder() + .setName(ByteString.copyFrom(mapKey.unsafeGetValue())) + .setType(RiakDtPB.MapField.MapFieldType.MAP))))) + .build(); + + CrdtResponseConverter converter = new CrdtResponseConverter(); + + RiakDatatype element = converter.convert(resp); + + assertTrue(element.isMap()); + RiakMap map = element.getAsMap(); + assertTrue(map.get(mapKey).get(0).isMap()); + map = map.get(mapKey).get(0).getAsMap(); + assertTrue(map.get(mapKey).get(0).isMap()); + map = map.get(mapKey).get(0).getAsMap(); + assertTrue(map.get(mapKey).get(0).isMap()); + + + } + +} diff --git a/src/test/java/com/basho/riak/client/core/fixture/AcceptReadPartialWriteStayOpen.java b/src/test/java/com/basho/riak/client/core/fixture/AcceptReadPartialWriteStayOpen.java new file mode 100644 index 000000000..d6a241bf3 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/fixture/AcceptReadPartialWriteStayOpen.java @@ -0,0 +1,54 @@ +/* + * 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.core.fixture; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.WritableByteChannel; +import java.nio.charset.Charset; +import java.util.Arrays; + +/** + * + * @author Brian Roach + */ +public class AcceptReadPartialWriteStayOpen extends Acceptor +{ + public AcceptReadPartialWriteStayOpen(ServerSocketChannel server) + { + super(server); + } + + @Override + Acceptor duplicate() + { + return new AcceptReadPartialWriteStayOpen(server); + } + + @Override + boolean writePb(SelectionKey key) throws IOException + { + ByteBuffer bb = ByteBuffer.allocate((pbMessage.getSerializedSize() / 2) + 5); + bb.putInt((pbMessage.getSerializedSize() / 2) + 1); + bb.put(pbCode); + bb.put(Arrays.copyOfRange(pbMessage.toByteArray(), 0, pbMessage.getSerializedSize() / 2)); + ((WritableByteChannel)key.channel()).write(bb); + return false; + } + +} diff --git a/src/test/java/com/basho/riak/client/core/fixture/AcceptReadPartialWriteThenClose.java b/src/test/java/com/basho/riak/client/core/fixture/AcceptReadPartialWriteThenClose.java new file mode 100644 index 000000000..36d56929c --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/fixture/AcceptReadPartialWriteThenClose.java @@ -0,0 +1,55 @@ +/* + * 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.core.fixture; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.WritableByteChannel; +import java.nio.charset.Charset; +import java.util.Arrays; + +/** + * + * @author Brian Roach + */ +public class AcceptReadPartialWriteThenClose extends Acceptor +{ + + public AcceptReadPartialWriteThenClose(ServerSocketChannel server) + { + super(server); + } + + @Override + Acceptor duplicate() + { + return new AcceptReadPartialWriteThenClose(server); + } + + @Override + boolean writePb(SelectionKey key) throws IOException + { + ByteBuffer bb = ByteBuffer.allocate((pbMessage.getSerializedSize() / 2) + 5); + bb.putInt((pbMessage.getSerializedSize() / 2) + 1); + bb.put(pbCode); + bb.put(Arrays.copyOfRange(pbMessage.toByteArray(), 0, pbMessage.getSerializedSize() / 2)); + ((WritableByteChannel)key.channel()).write(bb); + return true; + } + +} diff --git a/src/test/java/com/basho/riak/client/core/fixture/AcceptReadThenClose.java b/src/test/java/com/basho/riak/client/core/fixture/AcceptReadThenClose.java new file mode 100644 index 000000000..21e86b67a --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/fixture/AcceptReadThenClose.java @@ -0,0 +1,47 @@ +/* + * 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.core.fixture; + +import java.io.IOException; +import java.nio.channels.SelectionKey; +import java.nio.channels.ServerSocketChannel; + +/** + * + * @author Brian Roach + */ +public class AcceptReadThenClose extends Acceptor +{ + public AcceptReadThenClose(ServerSocketChannel server) + { + super(server); + } + + @Override + Acceptor duplicate() + { + return new AcceptReadThenClose(server); + } + + @Override + boolean writePb(SelectionKey key) throws IOException + { + return true; + } + + + +} diff --git a/src/test/java/com/basho/riak/client/core/fixture/AcceptReadWriteErrorStayOpen.java b/src/test/java/com/basho/riak/client/core/fixture/AcceptReadWriteErrorStayOpen.java new file mode 100644 index 000000000..aa1d1ad2f --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/fixture/AcceptReadWriteErrorStayOpen.java @@ -0,0 +1,53 @@ +/* + * 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.core.fixture; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.WritableByteChannel; + +/** + * + * @author Brian Roach + */ +public class AcceptReadWriteErrorStayOpen extends Acceptor +{ + public AcceptReadWriteErrorStayOpen(ServerSocketChannel server) + { + super(server); + } + + @Override + boolean writePb(SelectionKey key) throws IOException + { + ByteBuffer bb = ByteBuffer.allocate(pbErrorMsg.getSerializedSize() + 5); + bb.putInt(pbErrorMsg.getSerializedSize() + 1); + bb.put(pbErrorMsgCode); + bb.put(pbErrorMsg.toByteArray()); + bb.flip(); + ((WritableByteChannel)key.channel()).write(bb); + return false; + } + + @Override + Acceptor duplicate() + { + return new AcceptReadWriteErrorStayOpen(server); + } +} diff --git a/src/test/java/com/basho/riak/client/core/fixture/AcceptReadWriteStayOpen.java b/src/test/java/com/basho/riak/client/core/fixture/AcceptReadWriteStayOpen.java new file mode 100644 index 000000000..f1bd2d5f5 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/fixture/AcceptReadWriteStayOpen.java @@ -0,0 +1,52 @@ +/* + * 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.core.fixture; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.WritableByteChannel; + +/** + * + * @author Brian Roach + */ +public class AcceptReadWriteStayOpen extends Acceptor +{ + public AcceptReadWriteStayOpen(ServerSocketChannel server) + { + super(server); + } + + @Override + boolean writePb(SelectionKey key) throws IOException + { + ByteBuffer bb = ByteBuffer.allocate(pbMessage.getSerializedSize() + 5); + bb.putInt(pbMessage.getSerializedSize() + 1); + bb.put(pbCode); + bb.put(pbMessage.toByteArray()); + bb.flip(); + ((WritableByteChannel)key.channel()).write(bb); + return false; + } + + @Override + Acceptor duplicate() + { + return new AcceptReadWriteStayOpen(server); + } +} diff --git a/src/test/java/com/basho/riak/client/core/fixture/AcceptReadWriteThenClose.java b/src/test/java/com/basho/riak/client/core/fixture/AcceptReadWriteThenClose.java new file mode 100644 index 000000000..e4c5d6b12 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/fixture/AcceptReadWriteThenClose.java @@ -0,0 +1,53 @@ +/* + * 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.core.fixture; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.WritableByteChannel; +import java.nio.charset.Charset; + +/** + * + * @author Brian Roach + */ +public class AcceptReadWriteThenClose extends Acceptor +{ + public AcceptReadWriteThenClose(ServerSocketChannel server) + { + super(server); + } + + @Override + Acceptor duplicate() + { + return new AcceptReadWriteThenClose(server); + } + + @Override + boolean writePb(SelectionKey key) throws IOException + { + ByteBuffer bb = ByteBuffer.allocate(pbMessage.getSerializedSize() + 5); + bb.putInt(pbMessage.getSerializedSize() + 1); + bb.put(pbCode); + bb.put(pbMessage.toByteArray()); + ((WritableByteChannel)key.channel()).write(bb); + return true; + } + +} diff --git a/src/test/java/com/basho/riak/client/core/fixture/AcceptThenClose.java b/src/test/java/com/basho/riak/client/core/fixture/AcceptThenClose.java new file mode 100644 index 000000000..095925cc7 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/fixture/AcceptThenClose.java @@ -0,0 +1,49 @@ +/* + * 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.core.fixture; + +import java.io.IOException; +import java.nio.channels.SelectionKey; +import java.nio.channels.ServerSocketChannel; + +/** + * + * @author Brian Roach + */ +public class AcceptThenClose extends Acceptor +{ + + public AcceptThenClose(ServerSocketChannel server) + { + super(server); + this.type = AcceptorType.ACCEPT_THEN_CLOSE; + } + + @Override + Acceptor duplicate() + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + boolean writePb(SelectionKey key) throws IOException + { + throw new UnsupportedOperationException("Not supported yet."); + } + + + +} diff --git a/src/test/java/com/basho/riak/client/core/fixture/Acceptor.java b/src/test/java/com/basho/riak/client/core/fixture/Acceptor.java new file mode 100644 index 000000000..c6b3ec181 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/fixture/Acceptor.java @@ -0,0 +1,112 @@ +/* + * 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.core.fixture; + +import com.basho.riak.protobuf.RiakKvPB; +import com.basho.riak.protobuf.RiakPB; +import com.google.protobuf.ByteString; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.ServerSocketChannel; + +/** + * + * @author Brian Roach + */ +abstract class Acceptor +{ + public static final int DEFAULT_BUFFER_SIZE = 4096; + enum AcceptorType { ACCEPT_THEN_READ, ACCEPT_THEN_CLOSE } + + protected AcceptorType type = AcceptorType.ACCEPT_THEN_READ; + protected final ServerSocketChannel server; + protected final ByteBuffer readBuffer; + + protected final byte pbCode; + protected final RiakKvPB.RpbGetResp pbMessage; + protected final byte pbErrorMsgCode; + protected final RiakPB.RpbErrorResp pbErrorMsg; + + public Acceptor(ServerSocketChannel server) + { + this.server = server; + this.readBuffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE); + + RiakKvPB.RpbContent content = RiakKvPB.RpbContent.newBuilder() + .setValue(ByteString.copyFromUtf8("This is a value!")) + .setVtag(ByteString.copyFromUtf8("garbage")) + .build(); + + pbMessage = RiakKvPB.RpbGetResp.newBuilder().addContent(content).setVclock(ByteString.copyFromUtf8("garbage")).build(); + pbErrorMsg = RiakPB.RpbErrorResp.newBuilder().setErrcode(0).setErrmsg(ByteString.copyFromUtf8("Riak Error")).build(); + pbCode = (byte)10; + pbErrorMsgCode = (byte)0; + } + + final AcceptorType getType() + { + return type; + } + + final ServerSocketChannel getServerSocketChannel() + { + return server; + } + + void handle(SelectionKey key) throws IOException + { + if (((ReadableByteChannel)key.channel()).read(readBuffer) == -1) + { + throw new IOException("Read on closed key"); + } + + boolean closeAfterWrite; + readBuffer.flip(); + if (readBuffer.remaining() > 4) + { + int length = readBuffer.getInt(); + if (readBuffer.remaining() == length) + { + readBuffer.clear(); + closeAfterWrite = writePb(key); + if (closeAfterWrite) + { + key.channel().close(); + key.cancel(); + } + } + else + { + readBuffer.position(readBuffer.limit()); + readBuffer.limit(readBuffer.capacity()); + } + } + else + { + readBuffer.position(readBuffer.limit()); + readBuffer.limit(readBuffer.capacity()); + } + + + + } + + abstract Acceptor duplicate(); + abstract boolean writePb(SelectionKey key) throws IOException; + +} diff --git a/src/test/java/com/basho/riak/client/core/fixture/NetworkTestFixture.java b/src/test/java/com/basho/riak/client/core/fixture/NetworkTestFixture.java new file mode 100644 index 000000000..f4c677048 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/fixture/NetworkTestFixture.java @@ -0,0 +1,222 @@ +/* + * 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.core.fixture; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.StandardSocketOptions; +import java.nio.channels.ClosedSelectorException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.Iterator; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * + * This test fixture is to allow for specific network conditions to + * be created and test that the client core reacts appropriately. + * + * + * @author Brian Roach + * @since 2.0 + */ +public class NetworkTestFixture implements Runnable +{ + public static int ACCEPT_THEN_CLOSE = 0; + + public static int PB_CLOSE_BEFORE_WRITE = 1; + public static int PB_FULL_WRITE_THEN_CLOSE = 2; + public static int PB_PARTIAL_WRITE_THEN_CLOSE = 3; + public static int PB_FULL_WRITE_STAY_OPEN = 4; + public static int PB_PARTIAL_WRITE_STAY_OPEN = 5; + public static int PB_FULL_WRITE_ERROR_STAY_OPEN = 6; + public static int NO_LISTENER = 7; + + private final Selector selector; + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + + public NetworkTestFixture() throws IOException + { + this(5000); + } + + public NetworkTestFixture(int startingPort) throws IOException + { + selector = Selector.open(); + + ServerSocketChannel server = ServerSocketChannel.open(); + server.setOption(StandardSocketOptions.SO_REUSEADDR, true); + server.socket().bind(new InetSocketAddress("127.0.0.1", startingPort + ACCEPT_THEN_CLOSE)); + server.configureBlocking(false); + server.register(selector, SelectionKey.OP_ACCEPT); + SelectionKey key = server.keyFor(selector); + key.attach(new AcceptThenClose(server)); + + server = ServerSocketChannel.open(); + server.setOption(StandardSocketOptions.SO_REUSEADDR, true); + server.socket().bind(new InetSocketAddress("127.0.0.1", startingPort + PB_CLOSE_BEFORE_WRITE )); + server.configureBlocking(false); + server.register(selector, SelectionKey.OP_ACCEPT); + key = server.keyFor(selector); + key.attach(new AcceptReadThenClose(server)); + + server = ServerSocketChannel.open(); + server.setOption(StandardSocketOptions.SO_REUSEADDR, true); + server.socket().bind(new InetSocketAddress("127.0.0.1", startingPort + PB_FULL_WRITE_THEN_CLOSE )); + server.configureBlocking(false); + server.register(selector, SelectionKey.OP_ACCEPT); + key = server.keyFor(selector); + key.attach(new AcceptReadWriteThenClose(server)); + + server = ServerSocketChannel.open(); + server.setOption(StandardSocketOptions.SO_REUSEADDR, true); + server.socket().bind(new InetSocketAddress("127.0.0.1", startingPort + PB_PARTIAL_WRITE_THEN_CLOSE)); + server.configureBlocking(false); + server.register(selector, SelectionKey.OP_ACCEPT); + key = server.keyFor(selector); + key.attach(new AcceptReadPartialWriteThenClose(server)); + + server = ServerSocketChannel.open(); + server.setOption(StandardSocketOptions.SO_REUSEADDR, true); + server.socket().bind(new InetSocketAddress("127.0.0.1", startingPort + PB_FULL_WRITE_STAY_OPEN )); + server.configureBlocking(false); + server.register(selector, SelectionKey.OP_ACCEPT); + key = server.keyFor(selector); + key.attach(new AcceptReadWriteStayOpen(server)); + + server = ServerSocketChannel.open(); + server.setOption(StandardSocketOptions.SO_REUSEADDR, true); + server.socket().bind(new InetSocketAddress("127.0.0.1", startingPort + PB_PARTIAL_WRITE_STAY_OPEN)); + server.configureBlocking(false); + server.register(selector, SelectionKey.OP_ACCEPT); + key = server.keyFor(selector); + key.attach(new AcceptReadPartialWriteStayOpen(server)); + + server = ServerSocketChannel.open(); + server.setOption(StandardSocketOptions.SO_REUSEADDR, true); + server.socket().bind(new InetSocketAddress("127.0.0.1", startingPort + PB_FULL_WRITE_ERROR_STAY_OPEN )); + server.configureBlocking(false); + server.register(selector, SelectionKey.OP_ACCEPT); + key = server.keyFor(selector); + key.attach(new AcceptReadWriteErrorStayOpen(server)); + } + + @Override + public void run() + { + OUTER: + while(true) + { + try + { + lock.readLock().lock(); + if (selector.isOpen()) + { + try + { + selector.select(1000); + for (Iterator i = selector.selectedKeys().iterator(); i.hasNext();) + { + SelectionKey key = i.next(); + i.remove(); + + if (key.isAcceptable()) + { + Acceptor h = (Acceptor) key.attachment(); + SocketChannel client; + switch(h.type) + { + case ACCEPT_THEN_READ: + client = h.getServerSocketChannel().accept(); + client.configureBlocking(false); + client.socket().setTcpNoDelay(true); + client.register(selector, SelectionKey.OP_READ); + SelectionKey clientKey = client.keyFor(selector); + clientKey.attach(h.duplicate()); + break; + case ACCEPT_THEN_CLOSE: + client = h.getServerSocketChannel().accept(); + Thread.sleep(100); + client.close(); + break; + } + } + + if (key.isReadable()) { + Acceptor h = (Acceptor) key.attachment(); + try + { + h.handle(key); + } + catch (IOException e) + { + key.channel().close(); + key.cancel(); + } + } + + } + } + catch (IOException ex) + { + ex.printStackTrace(); + } + catch (ClosedSelectorException e) + { + // no op + } + catch (InterruptedException ex) + { + ex.printStackTrace(); + } + } + else + { + break OUTER; + } + } + finally + { + lock.readLock().unlock(); + } + } + } + + public void shutdown() throws IOException + { + try + { + lock.writeLock().lock(); + if (selector.isOpen()) + { + for (Iterator i = selector.keys().iterator(); i.hasNext();) + { + SelectionKey key = i.next(); + key.cancel(); + key.channel().close(); + } + + selector.close(); + } + } + finally + { + lock.writeLock().unlock(); + } + } +} diff --git a/src/test/java/com/basho/riak/client/core/netty/RiakMessageCodecTest.java b/src/test/java/com/basho/riak/client/core/netty/RiakMessageCodecTest.java new file mode 100644 index 000000000..93a35bce5 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/netty/RiakMessageCodecTest.java @@ -0,0 +1,91 @@ +/* + * 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.core.netty; + +import com.basho.riak.client.core.RiakMessage; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; +import static org.mockito.Mockito.*; +import org.powermock.reflect.Whitebox; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public class RiakMessageCodecTest +{ + final private int SIZE_DATA = 30; + private byte[] data; + final private int SIZE_LENGTH = 4; + final private int SIZE_CODE = 1; + private byte code = 1; + private ByteBuf buffer; + private ChannelHandlerContext mockContext; + + @Before + public void setUp() throws Exception + { + code = 1; + data = new byte[SIZE_DATA]; + Arrays.fill(data, (byte)1); + + RiakMessage pbMessage = new RiakMessage(code, data); + RiakMessageCodec codec = new RiakMessageCodec(); + + mockContext = mock(ChannelHandlerContext.class); + buffer = Unpooled.buffer(); + Whitebox.invokeMethod(codec, "encode", mockContext, pbMessage, buffer); + } + + @Test + public void encode() throws Exception + { + assertTrue(buffer.isReadable()); + assertEquals(buffer.readableBytes(), SIZE_DATA + SIZE_LENGTH + SIZE_CODE); + + int encodedLength = buffer.readInt(); + byte encodedCode = buffer.readByte(); + byte[] encodedData = new byte[SIZE_DATA]; + + buffer.readBytes(encodedData); + + assertEquals(encodedCode, code); + assertEquals(encodedLength, SIZE_DATA + SIZE_CODE); + assertArrayEquals(data, encodedData); + } + + @Test + public void decode() throws Exception + { + RiakMessageCodec codec = new RiakMessageCodec(); + List outList = new ArrayList(); + Whitebox.invokeMethod(codec, "decode", mockContext, buffer, outList); + RiakMessage message = (RiakMessage) outList.get(0); + assertEquals(code, message.getCode()); + assertArrayEquals(data, message.getData()); + + } +} diff --git a/src/test/java/com/basho/riak/client/core/netty/RiakResponseHandlerTest.java b/src/test/java/com/basho/riak/client/core/netty/RiakResponseHandlerTest.java new file mode 100644 index 000000000..166db6256 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/netty/RiakResponseHandlerTest.java @@ -0,0 +1,86 @@ +/* + * 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.core.netty; + +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.RiakResponseListener; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPipeline; +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Mockito.*; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + + + +/** + * + * @author Brian Roach + * @since 2.0 + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest(RiakMessage.class) +public class RiakResponseHandlerTest +{ + private RiakResponseHandler handler; + private ChannelHandlerContext mockContext; + private Channel mockChannel; + private ChannelPipeline mockPipeline; + private RiakResponseListener mockListener; + + @Before + public void setUp() + { + mockContext = mock(ChannelHandlerContext.class); + mockChannel = mock(Channel.class); + doReturn(mockChannel).when(mockContext).channel(); + mockPipeline = mock(ChannelPipeline.class); + doReturn(mockPipeline).when(mockChannel).pipeline(); + mockListener = mock(RiakResponseListener.class); + handler = new RiakResponseHandler(mockListener); + } + + @Test + public void registersListener() + { + RiakResponseListener listener = Whitebox.getInternalState(handler, "listener"); + assertEquals(listener, mockListener); + } + + @Test + public void notifiesListenerOnException() throws Exception + { + handler.exceptionCaught(mockContext, null); + verify(mockListener).onException(mockChannel, null); + } + + @Test + public void notifiesListenerOnComplete() throws Exception + { + RiakMessage message = PowerMockito.mock(RiakMessage.class); + doReturn((byte)10).when(message).getCode(); + handler.channelRead(mockContext, message); + + verify(mockListener).onSuccess(mockChannel, message); + } + +} diff --git a/src/test/java/com/basho/riak/client/core/operations/DtUpdateOperationTest.java b/src/test/java/com/basho/riak/client/core/operations/DtUpdateOperationTest.java new file mode 100644 index 000000000..03c16e1cd --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/operations/DtUpdateOperationTest.java @@ -0,0 +1,174 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.query.crdt.ops.MapOp; +import com.basho.riak.client.core.query.crdt.ops.RegisterOp; +import com.basho.riak.client.core.query.crdt.ops.SetOp; +import com.basho.riak.client.core.query.crdt.ops.CounterOp; +import com.basho.riak.client.core.query.crdt.ops.FlagOp; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.util.BinaryValue; +import com.basho.riak.protobuf.RiakDtPB; +import com.google.protobuf.ByteString; +import org.junit.Test; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + +public class DtUpdateOperationTest +{ + private final Namespace namespace = new Namespace("type", "bucket"); + + + @Test + public void testGetCounterOp() + { + + final long counterValue = 1; + CounterOp op = new CounterOp(counterValue); + DtUpdateOperation.Builder operation = new DtUpdateOperation.Builder(namespace); + RiakDtPB.CounterOp counterOp = operation.getCounterOp(op); + + assertEquals(counterValue, counterOp.getIncrement()); + + } + + @Test + public void testGetSetOp() + { + + BinaryValue addition = BinaryValue.create("1"); + BinaryValue removal = BinaryValue.create("2"); + + SetOp op = new SetOp() + .add(addition) + .remove(removal); + + DtUpdateOperation.Builder operation = new DtUpdateOperation.Builder(namespace); + RiakDtPB.SetOp setOp = operation.getSetOp(op); + + BinaryValue serializedAddition = BinaryValue.unsafeCreate(setOp.getAdds(0).toByteArray()); + BinaryValue serializedRemoval = BinaryValue.unsafeCreate(setOp.getRemoves(0).toByteArray()); + + assertEquals(addition, serializedAddition); + assertEquals(removal, serializedRemoval); + + } + + @Test + public void testGetFlagOp() + { + + final boolean enabled = true; + + FlagOp op = new FlagOp(enabled); + + DtUpdateOperation.Builder operation = new DtUpdateOperation.Builder(namespace); + RiakDtPB.MapUpdate.FlagOp flagOp = operation.getFlagOp(op); + + assertEquals(RiakDtPB.MapUpdate.FlagOp.ENABLE, flagOp); + + } + + @Test + public void testGetRegisterOp() + { + + final BinaryValue registerValue = BinaryValue.create("value"); + + RegisterOp op = new RegisterOp(registerValue); + + DtUpdateOperation.Builder operation = new DtUpdateOperation.Builder(namespace); + ByteString registerOp = operation.getRegisterOp(op); + + ByteString serializedValue = ByteString.copyFrom(registerValue.unsafeGetValue()); + assertEquals(serializedValue, registerOp); + } + + @Test + public void testGetMapOpUpdates() + { + + BinaryValue counterKey = BinaryValue.create("counter"); + BinaryValue setKey = BinaryValue.create("set"); + BinaryValue flagKey = BinaryValue.create("flag"); + BinaryValue registerKey = BinaryValue.create("register"); + BinaryValue mapKey = BinaryValue.create("map"); + + BinaryValue setAddValue = BinaryValue.create("1"); + + BinaryValue registerValue = BinaryValue.create("value"); + + BinaryValue mapAddValue = BinaryValue.create("value"); + + MapOp op = new MapOp() + .update(counterKey, new CounterOp(1)) + .update(setKey, new SetOp().add(setAddValue)) + .update(flagKey, new FlagOp(true)) + .update(registerKey, new RegisterOp(registerValue)) + .update(mapKey, new MapOp()); + + DtUpdateOperation.Builder operation = new DtUpdateOperation.Builder(namespace); + RiakDtPB.MapOp mapOp = operation.getMapOp(op); + + assertTrue(mapOp.getUpdatesCount() == 5); + + } + + @Test + public void testGetMapOpUpdateNestedMaps() + { + + BinaryValue key1 = BinaryValue.create("key1"); + MapOp op1 = new MapOp().update(key1, new CounterOp(1)); + + BinaryValue key2 = BinaryValue.create("key2"); + MapOp op2 = new MapOp().update(key2, op1); + + BinaryValue key3 = BinaryValue.create("key3"); + MapOp op3 = new MapOp().update(key3, op2); + + DtUpdateOperation.Builder operation = new DtUpdateOperation.Builder(namespace); + RiakDtPB.MapOp mapOp = operation.getMapOp(op3); + + assertTrue(mapOp.getUpdatesCount() == 1); + assertTrue(mapOp.getRemovesCount() == 0); + + //op3 + RiakDtPB.MapUpdate update = mapOp.getUpdates(0); + assertTrue(update.hasMapOp()); + + RiakDtPB.MapOp serializedMapOp = update.getMapOp(); + assertTrue(serializedMapOp.getUpdatesCount() == 1); + + //op2 + update = serializedMapOp.getUpdates(0); + assertTrue(update.hasMapOp()); + + serializedMapOp = update.getMapOp(); + assertTrue(serializedMapOp.getUpdatesCount() == 1); + + //op1 + update = serializedMapOp.getUpdates(0); + assertTrue(update.hasCounterOp()); + + RiakDtPB.CounterOp serializedCounterOp = update.getCounterOp(); + assertEquals(1, serializedCounterOp.getIncrement()); + + } + +} diff --git a/src/test/java/com/basho/riak/client/core/operations/StoreOperationTest.java b/src/test/java/com/basho/riak/client/core/operations/StoreOperationTest.java new file mode 100644 index 000000000..34f4887b3 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/operations/StoreOperationTest.java @@ -0,0 +1,75 @@ +/* + * 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.core.operations; + +import com.basho.riak.client.core.RiakMessage; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.query.indexes.LongIntIndex; +import com.basho.riak.client.core.query.indexes.RiakIndexes; +import com.basho.riak.client.core.query.links.RiakLink; +import com.basho.riak.client.core.util.BinaryValue; +import com.basho.riak.protobuf.RiakMessageCodes; +import com.basho.riak.protobuf.RiakKvPB; +import com.google.protobuf.InvalidProtocolBufferException; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static junit.framework.Assert.assertTrue; + +public class StoreOperationTest +{ + + @Test + public void testStoreOperationCreateChannelMessage() throws InvalidProtocolBufferException + { + + byte[] expectedValue = new byte[]{'O', '_', 'o'}; + + Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, "bucket"); + BinaryValue key = BinaryValue.create("key".getBytes()); + + RiakObject ro = new RiakObject(); + + List links = new ArrayList(); + links.add(new RiakLink("bucket", "key", "tag")); + ro.getLinks().addLinks(links); + + RiakIndexes indexes = ro.getIndexes(); + LongIntIndex longIndex = indexes.getIndex(LongIntIndex.named("dave")); + longIndex.add(42L); + + ro.setValue(BinaryValue.unsafeCreate(expectedValue)); + + Location location = new Location(ns, key); + StoreOperation operation = + new StoreOperation.Builder(location) + .withContent(ro) + .build(); + + RiakMessage rm = operation.createChannelMessage(); + + assertTrue(rm.getCode() == RiakMessageCodes.MSG_PutReq); + RiakKvPB.RpbPutReq req = RiakKvPB.RpbPutReq.parseFrom(rm.getData()); + assertTrue(Arrays.equals(req.getContent().getValue().toByteArray(), expectedValue)); + + } + +} diff --git a/src/test/java/com/basho/riak/client/core/operations/itest/ITestBase.java b/src/test/java/com/basho/riak/client/core/operations/itest/ITestBase.java new file mode 100644 index 000000000..815526f56 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/operations/itest/ITestBase.java @@ -0,0 +1,280 @@ +/* + * 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.core.operations.itest; + +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.RiakFutureListener; +import com.basho.riak.client.core.RiakNode; +import com.basho.riak.client.core.operations.DeleteOperation; +import com.basho.riak.client.core.operations.ListKeysOperation; +import com.basho.riak.client.core.operations.ResetBucketPropsOperation; +import com.basho.riak.client.core.operations.YzFetchIndexOperation; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.util.BinaryValue; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.UnknownHostException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public abstract class ITestBase +{ + protected static RiakCluster cluster; + protected static boolean testYokozuna; + protected static boolean test2i; + protected static boolean testBucketType; + protected static boolean testCrdt; + protected static boolean legacyRiakSearch; + protected static boolean security; + protected static BinaryValue bucketName; + protected static BinaryValue counterBucketType; + protected static BinaryValue setBucketType; + protected static BinaryValue mapBucketType; + protected static BinaryValue bucketType; + protected static BinaryValue yokozunaBucketType; + protected static String overrideCert; + + @BeforeClass + public static void setUp() throws UnknownHostException, FileNotFoundException, CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException + { + bucketName = BinaryValue.unsafeCreate("ITestBase".getBytes()); + + /** + * Riak security. + * + * If you want to test SSL/AUTH you need to: + * 1) configure riak with the certs included in test/resources and + * 2) create a user "tester" with the password "tester" + * 3) grant all permissions to tester on any: + * riak-admin security grant riak_kv.get,riak_kv.put,riak_kv.delete,riak_kv.index,riak_kv.list_keys,riak_kv.list_buckets,riak_core.get_bucket,riak_core.set_bucket,riak_core.get_bucket_type,riak_core.set_bucket_type ON ANY TO tester + * + * You can override the default cert included in resources using -Dcom.basho.riak.secutiry.cacert=file + */ + security = Boolean.parseBoolean(System.getProperty("com.basho.riak.security")); + overrideCert = System.getProperty("com.basho.riak.security.cacert"); + + /** + * Yokozuna. + * + * You need to create a bucket type in Riak for YZ: + * + * riak-admin bucket-type create jvtest_yz_search '{"props":{}}' + * riak-admin bucket-type activate jvtest_yz_search + */ + yokozunaBucketType = BinaryValue.create("jvtest_yz_search"); + testYokozuna = Boolean.parseBoolean(System.getProperty("com.basho.riak.yokozuna")); + + /** + * Bucket type + * + * you must create the type 'jvtest_test_type' to use this: + * + * riak-admin bucket-type create jvtest_test_type '{"props":{}}' + * riak-admin bucket-type activate jvtest_test_type + */ + testBucketType = Boolean.parseBoolean(System.getProperty("com.basho.riak.buckettype")); + bucketType = BinaryValue.unsafeCreate("jvtest_test_type".getBytes()); + + /** + * Secondary indexes + * + * The backend must be 'leveldb' in riak config to us this + */ + test2i = Boolean.parseBoolean(System.getProperty("com.basho.riak.2i")); + + + legacyRiakSearch = Boolean.parseBoolean(System.getProperty("com.basho.riak.riakSearch")); + + + /** + * In order to run the CRDT itests you must first manually + * create the following bucket types in your riak instance + * with the corresponding bucket properties. + * + * riak-admin bucket-type create jvtest_maps '{"props":{"allow_mult":true, "datatype": "map"}}' + * riak-admin bucket-type create jvtest_sets '{"props":{"allow_mult":true, "datatype": "set"}}' + * riak-admin bucket-type create jvtest_counters '{"props":{"allow_mult":true, "datatype": "counter"}}' + * riak-admin bucket-type activate jvtest_maps + * riak-admin bucket-type activate jvtest_sets + * riak-admin bucket-type activate jvtest_counters + */ + counterBucketType = BinaryValue.create("jvtest_counters"); + setBucketType = BinaryValue.create("jvtest_sets"); + mapBucketType = BinaryValue.create("jvtest_maps"); + testCrdt = Boolean.parseBoolean(System.getProperty("com.basho.riak.crdt")); + + RiakNode.Builder builder = new RiakNode.Builder() + .withMinConnections(10); + + if (security) + { + InputStream in; + if (overrideCert != null) + { + File f = new File(overrideCert); + in = new FileInputStream(f); + } + else + { + in = + Thread.currentThread().getContextClassLoader() + .getResourceAsStream("cacert.pem"); + } + + CertificateFactory cFactory = CertificateFactory.getInstance("X.509"); + X509Certificate caCert = (X509Certificate) cFactory.generateCertificate(in); + in.close(); + + KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + ks.load(null, "password".toCharArray()); + ks.setCertificateEntry("mycert", caCert); + + builder.withAuth("tester", "tester", ks); + } + + + + cluster = new RiakCluster.Builder(builder.build()).build(); + cluster.start(); + } + + @Before + public void beforeTest() throws InterruptedException, ExecutionException + { + resetAndEmptyBucket(bucketName); + if (testBucketType) + { + resetAndEmptyBucket(new Namespace(bucketType, bucketName)); + } + } + + @AfterClass + public static void tearDown() throws InterruptedException, ExecutionException + { + resetAndEmptyBucket(bucketName); + cluster.shutdown().get(); + } + + public static void resetAndEmptyBucket(BinaryValue name) throws InterruptedException, ExecutionException + { + resetAndEmptyBucket(new Namespace(Namespace.DEFAULT_BUCKET_TYPE, name.toString())); + + } + + protected static void resetAndEmptyBucket(Namespace namespace) throws InterruptedException, ExecutionException + { + ListKeysOperation.Builder keysOpBuilder = new ListKeysOperation.Builder(namespace); + + ListKeysOperation keysOp = keysOpBuilder.build(); + cluster.execute(keysOp); + List keyList = keysOp.get().getKeys(); + final int totalKeys = keyList.size(); + final Semaphore semaphore = new Semaphore(10); + final CountDownLatch latch = new CountDownLatch(1); + + RiakFutureListener listener = new RiakFutureListener() { + + private final AtomicInteger received = new AtomicInteger(); + + @Override + public void handle(RiakFuture f) + { + try + { + f.get(); + } + catch (InterruptedException ex) + { + throw new RuntimeException(ex); + } + catch (ExecutionException ex) + { + throw new RuntimeException(ex); + } + + semaphore.release(); + received.incrementAndGet(); + if (received.intValue() == totalKeys) + { + latch.countDown(); + } + } + + }; + + for (BinaryValue k : keyList) + { + Location location = new Location(namespace, k); + DeleteOperation.Builder delOpBuilder = new DeleteOperation.Builder(location); + DeleteOperation delOp = delOpBuilder.build(); + delOp.addListener(listener); + semaphore.acquire(); + cluster.execute(delOp); + } + + if (!keyList.isEmpty()) + { + latch.await(); + } + + ResetBucketPropsOperation.Builder resetOpBuilder = + new ResetBucketPropsOperation.Builder(namespace); + + ResetBucketPropsOperation resetOp = resetOpBuilder.build(); + cluster.execute(resetOp); + resetOp.get(); + + } + + public static boolean assureIndexExists(String indexName) throws InterruptedException + { + for (int x = 0; x < 5; x++) + { + Thread.sleep(2000); + YzFetchIndexOperation fetch = new YzFetchIndexOperation.Builder().withIndexName(indexName).build(); + cluster.execute(fetch); + fetch.await(); + if (fetch.isSuccess()) + { + return true; + } + } + + return false; + } +} diff --git a/src/test/java/com/basho/riak/client/core/operations/itest/ITestBucketProperties.java b/src/test/java/com/basho/riak/client/core/operations/itest/ITestBucketProperties.java new file mode 100644 index 000000000..fbad602c3 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/operations/itest/ITestBucketProperties.java @@ -0,0 +1,177 @@ +/* + * 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.core.operations.itest; + +import com.basho.riak.client.api.cap.Quorum; +import com.basho.riak.client.core.operations.FetchBucketPropsOperation; +import com.basho.riak.client.core.operations.StoreBucketPropsOperation; +import static com.basho.riak.client.core.operations.itest.ITestBase.bucketName; +import static com.basho.riak.client.core.operations.itest.ITestBase.testBucketType; +import com.basho.riak.client.core.query.BucketProperties; +import com.basho.riak.client.core.query.Namespace; +import java.util.concurrent.ExecutionException; +import static org.junit.Assert.*; +import org.junit.Assume; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class ITestBucketProperties extends ITestBase +{ + + @Test + public void testFetchDefaultBucketProps() throws InterruptedException, ExecutionException + { + Namespace namespace = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + BucketProperties props = fetchBucketProps(namespace); + assertTrue(props.hasNVal()); + assertTrue(props.hasAllowMulti()); + assertTrue(props.hasBasicQuorum()); + assertTrue(props.hasBigVClock()); + assertTrue(props.hasChashKeyFunction()); + assertTrue(props.hasLinkwalkFunction()); + assertTrue(props.hasDw()); + assertTrue(props.hasLastWriteWins()); + assertTrue(props.hasLinkwalkFunction()); + assertTrue(props.hasNotFoundOk()); + assertTrue(props.hasOldVClock()); + assertTrue(props.hasPr()); + assertTrue(props.hasPw()); + assertTrue(props.hasR()); + assertTrue(props.hasLegacyRiakSearchEnabled()); + assertTrue(props.hasRw()); + assertTrue(props.hasSmallVClock()); + assertTrue(props.hasW()); + assertTrue(props.hasYoungVClock()); + + assertFalse(props.hasBackend()); + assertFalse(props.hasPostcommitHooks()); + assertFalse(props.hasPrecommitHooks()); + assertFalse(props.hasSearchIndex()); + } + + @Test + public void testSetDefaultBucketProps() throws InterruptedException, ExecutionException + { + Namespace namespace = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + StoreBucketPropsOperation.Builder builder = + new StoreBucketPropsOperation.Builder(namespace) + .withAllowMulti(true) + .withNVal(4); + + storeBucketProps(builder); + + BucketProperties props = fetchBucketProps(namespace); + + assertEquals(props.getNVal(), Integer.valueOf(4)); + assertTrue(props.getAllowMulti()); + + } + + @Test + public void testResetBucketProps() throws InterruptedException, ExecutionException + { + Namespace namespace = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + StoreBucketPropsOperation.Builder builder = + new StoreBucketPropsOperation.Builder(namespace) + .withNVal(4) + .withR(1); + + storeBucketProps(builder); + BucketProperties props = fetchBucketProps(namespace); + + assertEquals(props.getNVal(), Integer.valueOf(4)); + assertEquals(props.getR().getIntValue(), 1); + + resetAndEmptyBucket(bucketName); + + props = fetchBucketProps(namespace); + assertEquals(props.getNVal(), Integer.valueOf(3)); + assertEquals(props.getR(), Quorum.quorumQuorum()); + + } + + @Test + public void testFetchBucketPropsFromType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(testBucketType); + Namespace namespace = new Namespace(bucketType, bucketName); + BucketProperties props = fetchBucketProps(namespace); + assertTrue(props.hasNVal()); + assertTrue(props.hasAllowMulti()); + assertTrue(props.hasBasicQuorum()); + assertTrue(props.hasBigVClock()); + assertTrue(props.hasChashKeyFunction()); + assertTrue(props.hasDw()); + assertTrue(props.hasLastWriteWins()); + assertTrue(props.hasNotFoundOk()); + assertTrue(props.hasOldVClock()); + assertTrue(props.hasPr()); + assertTrue(props.hasPw()); + assertTrue(props.hasR()); + assertTrue(props.hasLegacyRiakSearchEnabled()); + assertTrue(props.hasRw()); + assertTrue(props.hasSmallVClock()); + assertTrue(props.hasW()); + assertTrue(props.hasYoungVClock()); + + assertFalse(props.hasBackend()); + assertFalse(props.hasPostcommitHooks()); + assertFalse(props.hasPrecommitHooks()); + assertFalse(props.hasSearchIndex()); + } + + @Test + public void testSetBucketPropsInType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(testBucketType); + Namespace namespace = new Namespace(bucketType, bucketName); + StoreBucketPropsOperation.Builder builder = + new StoreBucketPropsOperation.Builder(namespace) + .withR(1) + .withNVal(4); + + storeBucketProps(builder); + BucketProperties props = fetchBucketProps(namespace); + + assertEquals(props.getNVal(), Integer.valueOf(4)); + assertEquals(props.getR().getIntValue(), 1); + + namespace = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + props = fetchBucketProps(namespace); + assertEquals(props.getNVal(), Integer.valueOf(3)); + } + + private BucketProperties fetchBucketProps(Namespace namespace) throws InterruptedException, ExecutionException + { + FetchBucketPropsOperation.Builder builder = new FetchBucketPropsOperation.Builder(namespace); + FetchBucketPropsOperation op = builder.build(); + cluster.execute(op); + return op.get().getBucketProperties(); + } + + private void storeBucketProps(StoreBucketPropsOperation.Builder builder) throws InterruptedException, ExecutionException + { + StoreBucketPropsOperation op = + builder.build(); + cluster.execute(op); + + op.get(); + } + +} diff --git a/src/test/java/com/basho/riak/client/core/operations/itest/ITestCrdtApi.java b/src/test/java/com/basho/riak/client/core/operations/itest/ITestCrdtApi.java new file mode 100644 index 000000000..846974954 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/operations/itest/ITestCrdtApi.java @@ -0,0 +1,161 @@ +/* + * 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.core.operations.itest; + +import com.basho.riak.client.core.query.crdt.types.RiakDatatype; +import com.basho.riak.client.core.query.crdt.types.RiakMap; +import com.basho.riak.client.core.query.crdt.types.RiakCounter; +import com.basho.riak.client.core.query.crdt.types.RiakRegister; +import com.basho.riak.client.core.query.crdt.types.RiakFlag; +import com.basho.riak.client.api.commands.datatypes.CounterUpdate; +import com.basho.riak.client.api.commands.datatypes.MapUpdate; +import com.basho.riak.client.api.commands.datatypes.RegisterUpdate; +import com.basho.riak.client.api.commands.datatypes.FlagUpdate; +import com.basho.riak.client.api.commands.datatypes.SetUpdate; +import com.basho.riak.client.core.operations.DtFetchOperation; +import com.basho.riak.client.core.operations.DtUpdateOperation; +import static com.basho.riak.client.core.operations.itest.ITestBase.bucketName; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.util.BinaryValue; +import org.junit.Assume; +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import static junit.framework.Assert.*; + +public class ITestCrdtApi extends ITestBase +{ + + @Test + public void simpleTest() throws ExecutionException, InterruptedException + { + Assume.assumeTrue(testCrdt); + /** + * Update some info about a user in a table of users + */ + + resetAndEmptyBucket(new Namespace(mapBucketType, bucketName)); + + // BinaryValues make it look messy, so define them all here. + final String numLogins = "logins"; + final String lastLoginTime = "last-login"; + final ByteBuffer nowBinary = ByteBuffer.allocate(8).putLong(System.currentTimeMillis()); + final byte[] now = nowBinary.array(); + final String username = "username"; + final String loggedIn = "logged-in"; + final String shoppingCart = "cart"; + + BinaryValue key = BinaryValue.create("user-info"); + + /* + + Data structure: + + bucket-type == asMap + -> "username" : asMap + -> "logins" : counter + -> "last-login" : register + -> "logged-in" : flag + -> "cart" : asSet + + */ + + // Build a shopping cart. We're buying the digits!!!! + ByteBuffer buffer = ByteBuffer.allocate(4); + SetUpdate favorites = new SetUpdate(); + for (int i = 0; i < 10; ++i) + { + buffer.putInt(i); + favorites.add(BinaryValue.create(buffer.array())); + buffer.rewind(); + } + + // Create an update for the user's values + MapUpdate userMapUpdate = new MapUpdate() + .update(numLogins, new CounterUpdate(1)) // counter + .update(lastLoginTime, new RegisterUpdate(now)) // register + .update(loggedIn, new FlagUpdate(true)) // flag + .update(shoppingCart, favorites); // asSet + + // Now create an update for the user's entry + MapUpdate userEntryUpdate = new MapUpdate() + .update(username, userMapUpdate); + + Location location = new Location(new Namespace(mapBucketType, bucketName), key); + DtUpdateOperation update = + new DtUpdateOperation.Builder(location) + .withOp(userEntryUpdate.getOp()) + .build(); + + cluster.execute(update); + update.get(); + + + DtFetchOperation fetch = new DtFetchOperation.Builder(location) + .build(); + cluster.execute(fetch); + + // users + RiakDatatype element = fetch.get().getCrdtElement(); + assertNotNull(element); + assertTrue(element.isMap()); + RiakMap usersMap = element.getAsMap(); + + // username + List usernameElement = usersMap.get(BinaryValue.create(username)); + assertNotNull(usernameElement); + assertEquals(1, usernameElement.size()); + assertTrue(usernameElement.get(0).isMap()); + RiakMap usernameMap = usernameElement.get(0).getAsMap(); + + // logins - counter + List numLoginsElement = usernameMap.get(BinaryValue.create(numLogins)); + assertNotNull(numLoginsElement); + assertEquals(1, numLoginsElement.size()); + assertTrue(numLoginsElement.get(0).isCounter()); + RiakCounter numLoginsCounter = numLoginsElement.get(0).getAsCounter(); + assertEquals((Long) 1L, numLoginsCounter.view()); + + // last-login - register + List lastLoginTimeElement = usernameMap.get(BinaryValue.create(lastLoginTime)); + assertNotNull(lastLoginTimeElement); + assertEquals(1, lastLoginTimeElement.size()); + assertTrue(lastLoginTimeElement.get(0).isRegister()); + RiakRegister lastLoginTimeRegister = lastLoginTimeElement.get(0).getAsRegister(); + assertTrue(Arrays.equals(now, lastLoginTimeRegister.getValue().getValue())); + + // logged-in - flag + List loggedInElement = usernameMap.get(BinaryValue.create(loggedIn)); + assertNotNull(loggedInElement); + assertEquals(1, loggedInElement.size()); + assertTrue(loggedInElement.get(0).isFlag()); + RiakFlag loggedInFlag = loggedInElement.get(0).getAsFlag(); + assertEquals(true, loggedInFlag.getEnabled()); + + // cart - asSet + List shoppingCartElement = usernameMap.get(BinaryValue.create(shoppingCart)); + assertNotNull(shoppingCartElement); + assertEquals(1, shoppingCartElement.size()); + assertTrue(shoppingCartElement.get(0).isSet()); + + } + +} diff --git a/src/test/java/com/basho/riak/client/core/operations/itest/ITestDeleteOperation.java b/src/test/java/com/basho/riak/client/core/operations/itest/ITestDeleteOperation.java new file mode 100644 index 000000000..35b9fca2d --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/operations/itest/ITestDeleteOperation.java @@ -0,0 +1,91 @@ +/* + * 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.core.operations.itest; + +import com.basho.riak.client.core.operations.DeleteOperation; +import com.basho.riak.client.core.operations.FetchOperation; +import com.basho.riak.client.core.operations.StoreOperation; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; +import java.util.concurrent.ExecutionException; +import static org.junit.Assert.*; +import static org.junit.Assume.assumeTrue; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class ITestDeleteOperation extends ITestBase +{ + @Test + public void testDeleteObjectDefaultType() throws InterruptedException, ExecutionException + { + testDeleteObject(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testDeleteObjectTestType() throws InterruptedException, ExecutionException + { + assumeTrue(testBucketType); + testDeleteObject(bucketType.toString()); + } + + private void testDeleteObject(String bucketType) throws InterruptedException, ExecutionException + { + final BinaryValue key = BinaryValue.unsafeCreate("my_key".getBytes()); + final String value = "{\"value\":\"value\"}"; + + RiakObject rObj = new RiakObject().setValue(BinaryValue.unsafeCreate(value.getBytes())); + + Location location = new Location(new Namespace(bucketType, bucketName.toString()), key); + StoreOperation storeOp = + new StoreOperation.Builder(location) + .withContent(rObj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + + FetchOperation fetchOp = + new FetchOperation.Builder(location).build(); + + + cluster.execute(fetchOp); + FetchOperation.Response response = fetchOp.get(); + RiakObject rObj2 = response.getObjectList().get(0); + + assertEquals(rObj.getValue(), rObj2.getValue()); + + DeleteOperation delOp = + new DeleteOperation.Builder(location) + .withVclock(rObj2.getVClock()).build(); + cluster.execute(delOp); + delOp.get(); + + fetchOp = + new FetchOperation.Builder(location).build(); + + cluster.execute(fetchOp); + response = fetchOp.get(); + assertTrue(response.isNotFound()); + assertTrue(response.getObjectList().isEmpty()); + + + } +} diff --git a/src/test/java/com/basho/riak/client/core/operations/itest/ITestDtUpdateOperation.java b/src/test/java/com/basho/riak/client/core/operations/itest/ITestDtUpdateOperation.java new file mode 100644 index 000000000..707440175 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/operations/itest/ITestDtUpdateOperation.java @@ -0,0 +1,525 @@ +/* + * 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.core.operations.itest; + +import com.basho.riak.client.core.query.crdt.types.RiakSet; +import com.basho.riak.client.core.query.crdt.types.RiakDatatype; +import com.basho.riak.client.core.query.crdt.types.RiakMap; +import com.basho.riak.client.core.query.crdt.types.RiakCounter; +import com.basho.riak.client.core.query.crdt.types.RiakFlag; +import com.basho.riak.client.core.query.crdt.types.RiakRegister; +import com.basho.riak.client.core.query.crdt.ops.MapOp; +import com.basho.riak.client.core.query.crdt.ops.RegisterOp; +import com.basho.riak.client.core.query.crdt.ops.SetOp; +import com.basho.riak.client.core.query.crdt.ops.CounterOp; +import com.basho.riak.client.core.query.crdt.ops.FlagOp; +import com.basho.riak.client.core.operations.DtFetchOperation; +import com.basho.riak.client.core.operations.DtUpdateOperation; +import static com.basho.riak.client.core.operations.itest.ITestBase.bucketName; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.util.BinaryValue; +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; + +import static junit.framework.Assert.*; +import static org.junit.Assume.assumeTrue; + +public class ITestDtUpdateOperation extends ITestBase +{ + + private RiakCounter fetchCounter(BinaryValue type, BinaryValue bucket, BinaryValue key) + throws ExecutionException, InterruptedException + { + Location location = new Location(new Namespace(type, bucket), key); + DtFetchOperation fetch = new DtFetchOperation.Builder(location).build(); + cluster.execute(fetch); + DtFetchOperation.Response response = fetch.get(); + RiakDatatype element = response.getCrdtElement(); + + assertNotNull(element); + assertTrue(element.isCounter()); + + return element.getAsCounter(); + } + + private RiakSet fetchSet(BinaryValue type, BinaryValue bucket, BinaryValue key) + throws ExecutionException, InterruptedException + { + Location location = new Location(new Namespace(type, bucket), key); + DtFetchOperation fetch = new DtFetchOperation.Builder(location).build(); + + cluster.execute(fetch); + DtFetchOperation.Response response = fetch.get(); + RiakDatatype element = response.getCrdtElement(); + + assertNotNull(element); + assertTrue(element.isSet()); + + return element.getAsSet(); + } + + private RiakMap fetchMap(BinaryValue type, BinaryValue bucket, BinaryValue key) + throws ExecutionException, InterruptedException + { + Location location = new Location(new Namespace(type, bucket), key); + DtFetchOperation fetch = new DtFetchOperation.Builder(location).build(); + + cluster.execute(fetch); + DtFetchOperation.Response response = fetch.get(); + RiakDatatype element = response.getCrdtElement(); + + assertNotNull(element); + assertTrue(element.isMap()); + + return element.getAsMap(); + } + + @Test + public void testCrdtCounter() throws ExecutionException, InterruptedException + { + + assumeTrue(testCrdt); + + final long iterations = 1; + + BinaryValue key = BinaryValue.create("key"); + + resetAndEmptyBucket(new Namespace(counterBucketType, bucketName)); + + RiakCounter counter = fetchCounter(counterBucketType, bucketName, key); + assertEquals((Long) 0L, counter.view()); + + Location location = new Location(new Namespace(counterBucketType, bucketName), key); + for (int i = 0; i < iterations; ++i) + { + DtUpdateOperation update = + new DtUpdateOperation.Builder(location) + .withOp(new CounterOp(1)) + .build(); + + cluster.execute(update); + update.get(); + } + + counter = fetchCounter(counterBucketType, bucketName, key); + assertEquals((Long) iterations, counter.view()); + + for (int i = 0; i < iterations; ++i) + { + DtUpdateOperation update = + new DtUpdateOperation.Builder(location) + .withOp(new CounterOp(-1)) + .build(); + + cluster.execute(update); + update.get(); + } + + counter = fetchCounter(counterBucketType, bucketName, key); + assertEquals((Long) 0L, counter.view()); + + resetAndEmptyBucket(new Namespace(counterBucketType, bucketName)); + + } + + @Test + public void testCrdtSet() throws ExecutionException, InterruptedException + { + + assumeTrue(testCrdt); + + final int iterations = 1; + + BinaryValue key = BinaryValue.create("key"); + + resetAndEmptyBucket(new Namespace(setBucketType, bucketName)); + + RiakSet set = fetchSet(setBucketType, bucketName, key); + assertTrue(set.view().isEmpty()); + + Set testValues = new HashSet(iterations); + Location location = new Location(new Namespace(setBucketType, bucketName), key); + BinaryValue ctx = null; + for (int i = 0; i < iterations; ++i) + { + ByteBuffer buff = (ByteBuffer) ByteBuffer.allocate(8).putInt(i).rewind(); + BinaryValue wrapped = BinaryValue.create(buff.array()); + testValues.add(wrapped); + + DtUpdateOperation update = + new DtUpdateOperation.Builder(location) + .withOp(new SetOp().add(wrapped)) + .withReturnBody(true) + .build(); + + cluster.execute(update); + DtUpdateOperation.Response resp = update.get(); + ctx = resp.getContext(); + set = resp.getCrdtElement().getAsSet(); + } + + assertEquals(iterations, set.view().size()); + assertEquals(testValues, set.view()); + + + for (BinaryValue setElement : testValues) + { + + DtUpdateOperation update = + new DtUpdateOperation.Builder(location) + .withOp(new SetOp().remove(setElement)) + .withContext(ctx) + .build(); + + cluster.execute(update); + update.get(); + + } + + set = fetchSet(setBucketType, bucketName, key); + assertTrue(set.view().isEmpty()); + + resetAndEmptyBucket(new Namespace(setBucketType, bucketName)); + + } + + @Test + public void testCrdtSetInterleved() throws ExecutionException, InterruptedException + { + + assumeTrue(testCrdt); + + final int iterations = 1; + + BinaryValue key = BinaryValue.create("key"); + + resetAndEmptyBucket(new Namespace(setBucketType, bucketName)); + + RiakSet set = fetchSet(setBucketType, bucketName, key); + assertTrue(set.view().isEmpty()); + + Set testValues = new HashSet(iterations); + Location location = new Location(new Namespace(setBucketType, bucketName), key); + for (int i = 0; i < iterations; ++i) + { + ByteBuffer buff = (ByteBuffer) ByteBuffer.allocate(8).putInt(i).rewind(); + BinaryValue wrapped = BinaryValue.create(buff.array()); + testValues.add(wrapped); + + DtUpdateOperation add = + new DtUpdateOperation.Builder(location) + .withOp(new SetOp().add(wrapped)) + .withReturnBody(true) + .build(); + + cluster.execute(add); + DtUpdateOperation.Response resp = add.get(); + + DtUpdateOperation delete = + new DtUpdateOperation.Builder(location) + .withOp(new SetOp().remove(wrapped)) + .withContext(resp.getContext()) + .build(); + + cluster.execute(delete); + delete.get(); + } + + set = fetchSet(setBucketType, bucketName, key); + assertTrue(set.view().isEmpty()); + + resetAndEmptyBucket(new Namespace(setBucketType, bucketName)); + + } + + @Test + public void testCrdtMap() throws ExecutionException, InterruptedException + { + + assumeTrue(testCrdt); + + BinaryValue key = BinaryValue.create("key"); + + resetAndEmptyBucket(new Namespace(mapBucketType, bucketName)); + + RiakMap map = fetchMap(mapBucketType, bucketName, key); + + assertTrue(map.view().isEmpty()); + + Location location = new Location(new Namespace(mapBucketType, bucketName), key); + BinaryValue setValue = BinaryValue.create("value"); + BinaryValue mapKey = BinaryValue.create("set"); + + DtUpdateOperation update = + new DtUpdateOperation.Builder(location) + .withOp(new MapOp().update(mapKey, new SetOp().add(setValue))) + .build(); + + cluster.execute(update); + update.get(); + + map = fetchMap(mapBucketType, bucketName, key); + assertEquals(1, map.view().size()); + assertNotNull(map.view().get(mapKey)); + assertEquals(1, map.view().get(mapKey).size()); + assertTrue(map.view().get(mapKey).get(0).isSet()); + RiakSet set = map.view().get(mapKey).get(0).getAsSet(); + assertTrue(set.view().contains(setValue)); + + + mapKey = BinaryValue.create("counter"); + + update = new DtUpdateOperation.Builder(location) + .withOp(new MapOp().update(mapKey, new CounterOp(1))) + .build(); + + cluster.execute(update); + update.get(); + + map = fetchMap(mapBucketType, bucketName, key); + assertEquals(2, map.view().size()); + assertNotNull(map.view().get(mapKey)); + assertEquals(1, map.view().get(mapKey).size()); + assertTrue(map.view().get(mapKey).get(0).isCounter()); + RiakCounter counter = map.view().get(mapKey).get(0).getAsCounter(); + assertEquals((Long) 1L, counter.view()); + + + mapKey = BinaryValue.create("flag"); + + update = + new DtUpdateOperation.Builder(location) + .withOp(new MapOp().update(mapKey, new FlagOp(true))) + .build(); + + cluster.execute(update); + update.get(); + + map = fetchMap(mapBucketType, bucketName, key); + assertEquals(3, map.view().size()); + assertNotNull(map.view().get(mapKey)); + assertEquals(1, map.view().get(mapKey).size()); + assertTrue(map.view().get(mapKey).get(0).isFlag()); + RiakFlag flag = map.view().get(mapKey).get(0).getAsFlag(); + assertTrue(flag.getEnabled()); + + + mapKey = BinaryValue.create("register"); + + update = new DtUpdateOperation.Builder(location) + .withOp(new MapOp().update(mapKey, new RegisterOp(mapKey))) + .build(); + + cluster.execute(update); + update.get(); + + map = fetchMap(mapBucketType, bucketName, key); + assertEquals(4, map.view().size()); + assertNotNull(map.view().get(mapKey)); + assertEquals(1, map.view().get(mapKey).size()); + RiakRegister register = map.view().get(mapKey).get(0).getAsRegister(); + assertEquals(mapKey, register.getValue()); + + + mapKey = BinaryValue.create("map"); + + update = new DtUpdateOperation.Builder(location) + .withOp(new MapOp().update(mapKey, new MapOp().update(mapKey, new FlagOp(false)))) + .build(); + + cluster.execute(update); + update.get(); + + map = fetchMap(mapBucketType, bucketName, key); + Map> mapView = map.view(); + assertEquals(5, mapView.size()); + + assertTrue(mapView.containsKey(mapKey)); + assertNotNull(map.view().get(mapKey)); + assertEquals(1, map.view().get(mapKey).size()); + assertTrue(mapView.get(mapKey).get(0).isMap()); + RiakMap nestedMap = mapView.get(mapKey).get(0).getAsMap(); + Map> nestedMapView = nestedMap.view(); + assertEquals(1, nestedMapView.size()); + + assertTrue(nestedMapView.containsKey(mapKey)); + assertNotNull(map.view().get(mapKey)); + assertEquals(1, map.view().get(mapKey).size()); + assertTrue(nestedMapView.get(mapKey).get(0).isFlag()); + RiakFlag nestedFlag = nestedMapView.get(mapKey).get(0).getAsFlag(); + assertFalse(nestedFlag.getEnabled()); + + resetAndEmptyBucket(new Namespace(mapBucketType, bucketName)); + + } + + @Test + public void testComplexMapUpdate() throws InterruptedException, ExecutionException + { + assumeTrue(testCrdt); + testComplexMapUpdate(false); + } + + @Test + public void testComplexMapUpdateWithReturnBody() throws InterruptedException, ExecutionException + { + // This test will currently fail as returnbody is broken in some cases in Riak. + assumeTrue(testCrdt); + testComplexMapUpdate(true); + } + + + private void testComplexMapUpdate(boolean returnBody) throws InterruptedException, ExecutionException + { + /* + Data structure: + + Riak key = "user-info" + Crdt Map + -> "Bob" : map + -> "logins" : counter + -> "last-login" : register + -> "logged-in" : flag + -> "cart" : set + + */ + + BinaryValue key = BinaryValue.create("user-info2"); + BinaryValue username = BinaryValue.create("Bob"); + BinaryValue logins = BinaryValue.create("logins"); + BinaryValue lastLogin = BinaryValue.create("last-login"); + BinaryValue loggedIn = BinaryValue.create("logged-in"); + BinaryValue cartContents = BinaryValue.create("cart"); + + + MapOp outerMap = new MapOp(); + MapOp innerMap = new MapOp(); + + ByteBuffer nowBinary = ByteBuffer.allocate(8).putLong(System.currentTimeMillis()); + byte[] now = nowBinary.array(); + + CounterOp counterOp = new CounterOp(1); + RegisterOp registerOp = new RegisterOp(BinaryValue.create(now)); + FlagOp flagOp = new FlagOp(false); + SetOp setOp = new SetOp() + .add(BinaryValue.create("Item 1")) + .add(BinaryValue.create("Item 2")); + + innerMap.update(logins, counterOp) + .update(lastLogin, registerOp) + .update(loggedIn, flagOp) + .update(cartContents, setOp); + + outerMap.update(username, innerMap); + + Namespace ns = new Namespace(mapBucketType, bucketName); + Location loc = new Location(ns, key); + + DtUpdateOperation update = new DtUpdateOperation.Builder(loc) + .withOp(outerMap) + .withReturnBody(returnBody) + .build(); + + cluster.execute(update); + + update.await(); + + if (!update.isSuccess()) + { + fail("Update operation failed: " + update.cause().toString()); + } + + RiakMap map; + if (returnBody) + { + DtUpdateOperation.Response response = update.get(); + assertNotNull(response); + assertTrue(response.hasCrdtElement()); + assertTrue(response.hasContext()); + RiakDatatype dt = response.getCrdtElement(); + assertTrue(dt.isMap()); + map = dt.getAsMap(); + } + else + { + map = fetchMap(mapBucketType, bucketName, key); + } + + + map = map.getMap(username); + assertNotNull(map); + RiakCounter counter = map.getCounter(logins); + assertNotNull(counter); + RiakRegister register = map.getRegister(lastLogin); + assertNotNull(register); + RiakFlag flag = map.getFlag(loggedIn); + assertNotNull(flag); + RiakSet set = map.getSet(cartContents); + assertNotNull(set); + + resetAndEmptyBucket(new Namespace(mapBucketType, bucketName)); + + } + + @Test + public void testSimpleMap() throws InterruptedException, ExecutionException + { + assumeTrue(testCrdt); + + BinaryValue key = BinaryValue.create("simple-map"); + BinaryValue mapKey = BinaryValue.create("set"); + Namespace ns = new Namespace(mapBucketType, bucketName); + Location loc = new Location(ns, key); + + SetOp setOp = new SetOp() + .add(BinaryValue.create("Item 1")) + .add(BinaryValue.create("Item 2")); + + + MapOp op = new MapOp().update(mapKey, setOp); + + DtUpdateOperation update = new DtUpdateOperation.Builder(loc) + .withOp(op) + .withReturnBody(true) + .build(); + + cluster.execute(update); + + update.await(); + + assertTrue(update.isSuccess()); + DtUpdateOperation.Response response = update.get(); + assertNotNull(response); + assertTrue(response.hasCrdtElement()); + assertTrue(response.hasContext()); + RiakDatatype dt = response.getCrdtElement(); + assertTrue(dt.isMap()); + RiakMap map = dt.getAsMap(); + + resetAndEmptyBucket(new Namespace(mapBucketType, bucketName)); + + + + } + +} diff --git a/src/test/java/com/basho/riak/client/core/operations/itest/ITestFetchOperation.java b/src/test/java/com/basho/riak/client/core/operations/itest/ITestFetchOperation.java new file mode 100644 index 000000000..1184ed9c7 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/operations/itest/ITestFetchOperation.java @@ -0,0 +1,166 @@ +/* + * 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.core.operations.itest; + +import com.basho.riak.client.core.operations.FetchOperation; +import com.basho.riak.client.core.operations.StoreBucketPropsOperation; +import com.basho.riak.client.core.operations.StoreOperation; +import static com.basho.riak.client.core.operations.itest.ITestBase.bucketName; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; +import java.util.List; +import java.util.concurrent.ExecutionException; +import static org.junit.Assert.*; +import static org.junit.Assume.assumeTrue; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class ITestFetchOperation extends ITestBase +{ + + @Test + public void testFetchOpNotFoundDefaultType() throws InterruptedException, ExecutionException + { + testFetchOpNotFound(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testFetchOpNotFoundTestType() throws InterruptedException, ExecutionException + { + assumeTrue(testBucketType); + testFetchOpNotFound(bucketType.toString()); + } + + private void testFetchOpNotFound(String bucketType) throws InterruptedException, ExecutionException + { + final BinaryValue key = BinaryValue.unsafeCreate("my_key_1".getBytes()); + final String value = "{\"value\":\"value\"}"; + Namespace ns = new Namespace(bucketType, bucketName.toString()); + Location location = new Location(ns, key); + FetchOperation fetchOp = + new FetchOperation.Builder(location).build(); + + cluster.execute(fetchOp); + FetchOperation.Response response = fetchOp.get(); + assertTrue(response.isNotFound()); + assertTrue(response.getObjectList().isEmpty()); + + } + + @Test + public void testFetchOpNoSiblingsDefaultType() throws InterruptedException, ExecutionException + { + testFetchOpNoSiblings(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testFetchOpNoSiblingsTestType() throws InterruptedException, ExecutionException + { + assumeTrue(testBucketType); + testFetchOpNoSiblings(bucketType.toString()); + } + + private void testFetchOpNoSiblings(String bucketType) throws InterruptedException, ExecutionException + { + final BinaryValue key = BinaryValue.unsafeCreate("my_key_2".getBytes()); + final String value = "{\"value\":\"value\"}"; + + RiakObject rObj = new RiakObject().setValue(BinaryValue.create(value)); + Namespace ns = new Namespace(bucketType, bucketName.toString()); + Location location = new Location(ns, key); + StoreOperation storeOp = + new StoreOperation.Builder(location) + .withContent(rObj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + + FetchOperation fetchOp = + new FetchOperation.Builder(location).build(); + + cluster.execute(fetchOp); + FetchOperation.Response response = fetchOp.get(); + assertFalse(response.isNotFound()); + List objectList = response.getObjectList(); + assertEquals(1, objectList.size()); + RiakObject ro = objectList.get(0); + assertEquals(ro.getValue().toString(), value); + + } + + @Test + public void testFetchOpWithSiblingsDefaultType() throws InterruptedException, ExecutionException + { + testFetchOpWithSiblings(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testFetchOpWithSiblingsTestType() throws InterruptedException, ExecutionException + { + assumeTrue(testBucketType); + testFetchOpWithSiblings(bucketType.toString()); + } + + private void testFetchOpWithSiblings(String bucketType) throws InterruptedException, ExecutionException + { + final BinaryValue key = BinaryValue.unsafeCreate("my_key_3".getBytes()); + final String value = "{\"value\":\"value\"}"; + + Namespace namespace = new Namespace(bucketType, bucketName.toString()); + StoreBucketPropsOperation op = + new StoreBucketPropsOperation.Builder(namespace) + .withAllowMulti(true) + .build(); + cluster.execute(op); + op.get(); + + RiakObject rObj = new RiakObject().setValue(BinaryValue.create(value)); + Location location = new Location(namespace, key); + StoreOperation storeOp = + new StoreOperation.Builder(location) + .withContent(rObj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + + storeOp = + new StoreOperation.Builder(location) + .withContent(rObj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + + FetchOperation fetchOp = + new FetchOperation.Builder(location).build(); + + cluster.execute(fetchOp); + FetchOperation.Response response = fetchOp.get(); + assertTrue(response.getObjectList().size() > 1); + + RiakObject ro = response.getObjectList().get(0); + assertEquals(ro.getValue().toString(), value); + + } + +} diff --git a/src/test/java/com/basho/riak/client/core/operations/itest/ITestListBucketsOperation.java b/src/test/java/com/basho/riak/client/core/operations/itest/ITestListBucketsOperation.java new file mode 100644 index 000000000..bf8fa683c --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/operations/itest/ITestListBucketsOperation.java @@ -0,0 +1,170 @@ +/* + * 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.core.operations.itest; + +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.RiakFutureListener; +import com.basho.riak.client.core.operations.ListBucketsOperation; +import com.basho.riak.client.core.operations.StoreOperation; +import static com.basho.riak.client.core.operations.itest.ITestBase.bucketName; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicInteger; +import static org.junit.Assert.*; +import static org.junit.Assume.assumeTrue; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class ITestListBucketsOperation extends ITestBase +{ + @Test + public void testListBucketsDefaultType() throws InterruptedException, ExecutionException + { + testListBuckets(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testListBucketsTestType() throws InterruptedException, ExecutionException + { + assumeTrue(testBucketType); + testListBuckets(bucketType.toString()); + } + + private void testListBuckets(String bucketType) throws InterruptedException, ExecutionException + { + // Empty buckets do not show up + final BinaryValue key = BinaryValue.unsafeCreate("my_key".getBytes()); + final String value = "{\"value\":\"value\"}"; + + RiakObject rObj = new RiakObject().setValue(BinaryValue.create(value)); + Location location = new Location(new Namespace(bucketType, bucketName.toString()), key); + StoreOperation storeOp = + new StoreOperation.Builder(location) + .withContent(rObj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + + ListBucketsOperation listOp = new ListBucketsOperation.Builder() + .withBucketType(BinaryValue.createFromUtf8(bucketType)) + .build(); + cluster.execute(listOp); + List bucketList = listOp.get().getBuckets(); + assertTrue(bucketList.size() > 0); + + boolean found = false; + for (BinaryValue baw : bucketList) + { + if (baw.toString().equals(bucketName.toString())) + { + found = true; + } + } + + assertTrue(found); + } + + @Test + public void testLargeBucketListDefaultType() throws InterruptedException, ExecutionException + { + testLargeBucketList(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testLargeBucketListTestType() throws InterruptedException, ExecutionException + { + assumeTrue(testBucketType); + testLargeBucketList(bucketType.toString()); + } + + private void testLargeBucketList(String bucketType) throws InterruptedException, ExecutionException + { + final BinaryValue key = BinaryValue.unsafeCreate("my_key".getBytes()); + final String value = "{\"value\":\"value\"}"; + final Semaphore semaphore = new Semaphore(10); + final CountDownLatch latch = new CountDownLatch(1); + + RiakFutureListener listener = + new RiakFutureListener() { + + private final AtomicInteger received = new AtomicInteger(); + + @Override + public void handle(RiakFuture f) + { + try + { + f.get(); + } + catch (InterruptedException ex) + { + throw new RuntimeException(ex); + } + catch (ExecutionException ex) + { + throw new RuntimeException(ex); + } + + semaphore.release(); + received.incrementAndGet(); + if (received.intValue() == 1000) + { + latch.countDown(); + } + } + }; + + for (int i = 0; i < 1000; i++) + { + Namespace ns = new Namespace(bucketType, bucketName.toString() + i); + RiakObject rObj = new RiakObject().setValue(BinaryValue.create(value)); + Location location = new Location(ns, key); + StoreOperation storeOp = + new StoreOperation.Builder(location) + .withContent(rObj) + .build(); + storeOp.addListener(listener); + semaphore.acquire(); + cluster.execute(storeOp); + } + + latch.await(); + + ListBucketsOperation listOp = new ListBucketsOperation.Builder() + .withBucketType(BinaryValue.createFromUtf8(bucketType)) + .build(); + cluster.execute(listOp); + List bucketList = listOp.get().getBuckets(); + assertTrue(bucketList.size() >= 1000); + + for (int i = 0; i < 1000; i++) + { + Namespace ns = new Namespace(bucketType, bucketName.toString() + i); + resetAndEmptyBucket(ns); + } + + } +} diff --git a/src/test/java/com/basho/riak/client/core/operations/itest/ITestListKeysOperation.java b/src/test/java/com/basho/riak/client/core/operations/itest/ITestListKeysOperation.java new file mode 100644 index 000000000..1815ea48b --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/operations/itest/ITestListKeysOperation.java @@ -0,0 +1,182 @@ +/* + * 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.core.operations.itest; + +import com.basho.riak.client.core.RiakFuture; +import com.basho.riak.client.core.RiakFutureListener; +import com.basho.riak.client.core.operations.ListKeysOperation; +import com.basho.riak.client.core.operations.StoreOperation; +import static com.basho.riak.client.core.operations.itest.ITestBase.bucketName; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicInteger; +import static org.junit.Assert.*; +import static org.junit.Assume.assumeTrue; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class ITestListKeysOperation extends ITestBase +{ + + @Test + public void testListNoKeysDefaultType() throws InterruptedException, ExecutionException + { + testListNoKeys(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testListNoKeysTestType() throws InterruptedException, ExecutionException + { + assumeTrue(testBucketType); + testListNoKeys(bucketType.toString()); + } + + private void testListNoKeys(String bucketType) throws InterruptedException, ExecutionException + { + Namespace ns = new Namespace(bucketType, bucketName.toString() + "_1"); + ListKeysOperation klistOp = new ListKeysOperation.Builder(ns).build(); + cluster.execute(klistOp); + List kList = klistOp.get().getKeys(); + assertTrue(kList.isEmpty()); + resetAndEmptyBucket(ns); + } + + @Test + public void testListKeyDefaultType() throws InterruptedException, ExecutionException + { + testListKey(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testListKeyTestType() throws InterruptedException, ExecutionException + { + assumeTrue(testBucketType); + testListKey(bucketType.toString()); + } + + private void testListKey(String bucketType) throws InterruptedException, ExecutionException + { + final Namespace ns = new Namespace(bucketType, bucketName.toString() + "_2"); + final BinaryValue key = BinaryValue.unsafeCreate("my_key".getBytes()); + final String value = "{\"value\":\"value\"}"; + + RiakObject rObj = new RiakObject().setValue(BinaryValue.create(value)); + Location location = new Location(ns, key); + StoreOperation storeOp = + new StoreOperation.Builder(location) + .withContent(rObj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + + ListKeysOperation klistOp = new ListKeysOperation.Builder(ns).build(); + cluster.execute(klistOp); + List kList = klistOp.get().getKeys(); + + assertEquals(kList.size(), 1); + assertEquals(kList.get(0), key); + resetAndEmptyBucket(ns); + + } + + @Test + public void testLargeKeyListDefaultType() throws InterruptedException, ExecutionException + { + testLargeKeyList(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testLargeKeyListTestType() throws InterruptedException, ExecutionException + { + assumeTrue(testBucketType); + testLargeKeyList(bucketType.toString()); + } + + private void testLargeKeyList(String bucketType) throws InterruptedException, ExecutionException + { + final String baseKey = "my_key"; + final String value = "{\"value\":\"value\"}"; + final Namespace ns = new Namespace(bucketType, bucketName.toString() + "_3"); + final Semaphore semaphore = new Semaphore(10); + final CountDownLatch latch = new CountDownLatch(1); + + RiakFutureListener listener = + new RiakFutureListener() { + + private final int expected = 1000; + private final AtomicInteger received = new AtomicInteger(); + + @Override + public void handle(RiakFuture f) + { + try + { + f.get(); + semaphore.release(); + received.incrementAndGet(); + + if (expected == received.intValue()) + { + ListKeysOperation klistOp = new ListKeysOperation.Builder(ns).build(); + cluster.execute(klistOp); + List kList; + kList = klistOp.get().getKeys(); + assertEquals(kList.size(), 1000); + latch.countDown(); + } + } + catch (InterruptedException ex) + { + throw new RuntimeException(ex); + } + catch (ExecutionException ex) + { + throw new RuntimeException(ex); + } + + } + }; + + for (int i = 0; i < 1000; i++) + { + semaphore.acquire(); + BinaryValue key = BinaryValue.unsafeCreate((baseKey + i).getBytes()); + RiakObject rObj = new RiakObject().setValue(BinaryValue.create(value)); + Location location = new Location(ns, key); + StoreOperation storeOp = + new StoreOperation.Builder(location) + .withContent(rObj) + .build(); + + storeOp.addListener(listener); + cluster.execute(storeOp); + } + + latch.await(); + ITestBase.resetAndEmptyBucket(ns); + + } +} diff --git a/src/test/java/com/basho/riak/client/core/operations/itest/ITestMapReduceOperation.java b/src/test/java/com/basho/riak/client/core/operations/itest/ITestMapReduceOperation.java new file mode 100644 index 000000000..e676c59ae --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/operations/itest/ITestMapReduceOperation.java @@ -0,0 +1,143 @@ +/* + * 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.core.operations.itest; + +import com.basho.riak.client.core.operations.MapReduceOperation; +import com.basho.riak.client.core.operations.StoreOperation; +import static com.basho.riak.client.core.operations.itest.ITestBase.bucketName; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import org.junit.Test; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public class ITestMapReduceOperation extends ITestBase +{ + @Test + public void testBasicMRDefaultType() throws InterruptedException, ExecutionException, IOException + { + // This will currently fail as the 4 arity input in broken in Riak. Specifying + // "default" doesn't work. I've worked around this in the User API. + testBasicMR(new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString())); + } + + @Test + public void testBasicMRTestType() throws InterruptedException, ExecutionException, IOException + { + assumeTrue(testBucketType); + testBasicMR(new Namespace(bucketType, bucketName)); + } + + private void testBasicMR(Namespace namespace) throws InterruptedException, ExecutionException, IOException + { + RiakObject obj = new RiakObject(); + + obj.setValue(BinaryValue.create("Alice was beginning to get very tired of sitting by her sister on the " + + "bank, and of having nothing to do: once or twice she had peeped into the " + + "book her sister was reading, but it had no pictures or conversations in " + + "it, 'and what is the use of a book,' thought Alice 'without pictures or " + + "conversation?'")); + Location location = new Location(namespace, BinaryValue.unsafeCreate("p1".getBytes())); + StoreOperation storeOp = + new StoreOperation.Builder(location) + .withContent(obj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + + obj.setValue(BinaryValue.create("So she was considering in her own mind (as well as she could, for the " + + "hot day made her feel very sleepy and stupid), whether the pleasure " + + "of making a daisy-chain would be worth the trouble of getting up and " + + "picking the daisies, when suddenly a White Rabbit with pink eyes ran " + + "close by her.")); + + location = new Location(namespace, BinaryValue.unsafeCreate("p2".getBytes())); + storeOp = + new StoreOperation.Builder(location) + .withContent(obj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + + obj.setValue(BinaryValue.create("The rabbit-hole went straight on like a tunnel for some way, and then " + + "dipped suddenly down, so suddenly that Alice had not a moment to think " + + "about stopping herself before she found herself falling down a very deep " + + "well.")); + location = new Location(namespace, BinaryValue.unsafeCreate("p3".getBytes())); + storeOp = + new StoreOperation.Builder(location) + .withContent(obj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + + String bName = namespace.getBucketNameAsString(); + String bType = namespace.getBucketTypeAsString(); + + String query = "{\"inputs\":[[\"" + bName + "\",\"p1\",\"\",\"" + bType + "\"]," + + "[\"" + bName + "\",\"p2\",\"\",\"" + bType + "\"]," + + "[\"" + bName + "\",\"p3\",\"\",\"" + bType + "\"]]," + + "\"query\":[{\"map\":{\"language\":\"javascript\",\"source\":\"" + + "function(v) {var m = v.values[0].data.toLowerCase().match(/\\w*/g); var r = [];" + + "for(var i in m) {if(m[i] != '') {var o = {};o[m[i]] = 1;r.push(o);}}return r;}" + + "\"}},{\"reduce\":{\"language\":\"javascript\",\"source\":\"" + + "function(v) {var r = {};for(var i in v) {for(var w in v[i]) {if(w in r) r[w] += v[i][w];" + + "else r[w] = v[i][w];}}return [r];}\"}}]}"; + + MapReduceOperation mrOp = + new MapReduceOperation.Builder(BinaryValue.unsafeCreate(query.getBytes())) + .build(); + + cluster.execute(mrOp); + mrOp.await(); + assertTrue(mrOp.isSuccess()); + ArrayNode resultList = mrOp.get().getResults().get(1); + + // The query should return one result which is a JSON array containing a + // single JSON object that is a asSet of word counts. + assertEquals(resultList.size(), 1); + + String json = resultList.get(0).toString(); + ObjectMapper oMapper = new ObjectMapper(); + @SuppressWarnings("unchecked") + Map resultMap = oMapper.readValue(json, Map.class); + + assertNotNull(resultMap.containsKey("the")); + assertEquals(Integer.valueOf(8),resultMap.get("the")); + + resetAndEmptyBucket(namespace); + + } +} diff --git a/src/test/java/com/basho/riak/client/core/operations/itest/ITestPingOperation.java b/src/test/java/com/basho/riak/client/core/operations/itest/ITestPingOperation.java new file mode 100644 index 000000000..c966fedd3 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/operations/itest/ITestPingOperation.java @@ -0,0 +1,66 @@ +/* + * 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.core.operations.itest; + +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.RiakNode; +import com.basho.riak.client.core.operations.PingOperation; +import java.net.UnknownHostException; +import java.util.concurrent.ExecutionException; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class ITestPingOperation extends ITestBase +{ + @Test + public void theMachinethatGoesPing() throws InterruptedException, ExecutionException + { + PingOperation ping = new PingOperation(); + cluster.execute(ping); + + ping.await(); + assertTrue(ping.isSuccess()); + } + + @Test + public void theMachineThatDoesntGoPing() throws UnknownHostException, InterruptedException + { + RiakNode.Builder builder = new RiakNode.Builder() + .withRemotePort(10001) + .withConnectionTimeout(500) + .withMinConnections(10); + + RiakCluster cluster = new RiakCluster.Builder(builder.build()).build(); + cluster.start(); + + PingOperation ping = new PingOperation(); + cluster.execute(ping); + ping.await(); + + assertFalse(ping.isSuccess()); + assertNotNull(ping.cause()); + + cluster.shutdown(); + + } +} diff --git a/src/test/java/com/basho/riak/client/core/operations/itest/ITestSearchOperation.java b/src/test/java/com/basho/riak/client/core/operations/itest/ITestSearchOperation.java new file mode 100644 index 000000000..a9ec188a7 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/operations/itest/ITestSearchOperation.java @@ -0,0 +1,185 @@ +/* + * 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.core.operations.itest; + +import com.basho.riak.client.core.operations.SearchOperation; +import com.basho.riak.client.core.operations.StoreBucketPropsOperation; +import com.basho.riak.client.core.operations.StoreOperation; +import com.basho.riak.client.core.operations.YzDeleteIndexOperation; +import com.basho.riak.client.core.operations.YzPutIndexOperation; +import static com.basho.riak.client.core.operations.itest.ITestBase.bucketName; +import static com.basho.riak.client.core.operations.itest.ITestBase.cluster; +import static com.basho.riak.client.core.operations.itest.ITestBase.testYokozuna; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.query.search.YokozunaIndex; +import com.basho.riak.client.core.util.BinaryValue; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import static org.junit.Assert.*; +import org.junit.Assume; +import org.junit.Test; + +/** + * + * @author Brian Roach + */ +public class ITestSearchOperation extends ITestBase +{ + @Test + public void testSimpleSearch() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(legacyRiakSearch); + + Namespace namespace = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString()); + StoreBucketPropsOperation op = + new StoreBucketPropsOperation.Builder(namespace) + .withLegacyRiakSearchEnabled(true) + .build(); + + cluster.execute(op); + op.get(); + + prepSearch(bucketType, bucketName); + + SearchOperation searchOp = new SearchOperation.Builder(bucketName, "Alice*").build(); + + cluster.execute(searchOp); + SearchOperation.Response result = searchOp.get(); + + assertEquals(result.numResults(), 2); + for (Map> map : result.getAllResults()) + { + assertFalse(map.isEmpty()); + assertEquals(map.size(), 2); // id and value fields + } + } + + @Test + public void testYokozunaSearch() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(testYokozuna); + + // First we have to create an index and attach it to a bucket + // and the 'default' bucket type can't be used for search + + BinaryValue searchBucket = BinaryValue.create("search_bucket"); + YokozunaIndex index = new YokozunaIndex("test_index"); + YzPutIndexOperation putOp = new YzPutIndexOperation.Builder(index).build(); + + cluster.execute(putOp); + putOp.await(); + + assertTrue(putOp.isSuccess()); + + assureIndexExists("test_index"); + + Namespace namespace = new Namespace(yokozunaBucketType, searchBucket); + StoreBucketPropsOperation propsOp = + new StoreBucketPropsOperation.Builder(namespace) + .withSearchIndex("test_index") + .build(); + cluster.execute(propsOp); + propsOp.await(); + + if (!propsOp.isSuccess()) + { + assertTrue(propsOp.cause().toString(), propsOp.isSuccess()); + } + + Thread.sleep(5000); + + prepSearch(yokozunaBucketType, searchBucket); + + Thread.sleep(5000); + + SearchOperation searchOp = new SearchOperation.Builder(BinaryValue.create("test_index"), "multi_ss:t*").build(); + + cluster.execute(searchOp); + searchOp.await(); + assertTrue(searchOp.isSuccess()); + SearchOperation.Response result = searchOp.get(); + for (Map> map : result.getAllResults()) + { + assertFalse(map.isEmpty()); + assertEquals(7, map.size()); // [_yz_rk, _yz_rb, multi_ss, content_s, _yz_rt, score, _yz_id] + assertTrue(map.containsKey("multi_ss")); + assertEquals(2, map.get("multi_ss").size()); + } + + resetAndEmptyBucket(namespace); + + YzDeleteIndexOperation delOp = new YzDeleteIndexOperation.Builder("test_index").build(); + cluster.execute(delOp); + delOp.await(); + assertTrue(delOp.isSuccess()); + + } + + private void prepSearch(BinaryValue searchBucketType, BinaryValue searchBucket) throws InterruptedException, ExecutionException + { + + RiakObject obj = new RiakObject().setContentType("application/json"); + + obj.setValue(BinaryValue.create("{ \"content_s\":\"Alice was beginning to get very tired of sitting by her sister on the " + + "bank, and of having nothing to do: once or twice she had peeped into the " + + "book her sister was reading, but it had no pictures or conversations in " + + "it, 'and what is the use of a book,' thought Alice 'without pictures or " + + "conversation?'\"}")); + + Namespace namespace = new Namespace(searchBucketType, searchBucket); + Location location = new Location(namespace, BinaryValue.unsafeCreate("p1".getBytes())); + StoreOperation storeOp = + new StoreOperation.Builder(location) + .withContent(obj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + + obj.setValue(BinaryValue.create("{ \"content_s\":\"So she was considering in her own mind (as well as she could, for the " + + "hot day made her feel very sleepy and stupid), whether the pleasure " + + "of making a daisy-chain would be worth the trouble of getting up and " + + "picking the daisies, when suddenly a White Rabbit with pink eyes ran " + + "close by her.\", \"multi_ss\":[\"this\",\"that\"]}")); + + + location = new Location(namespace, BinaryValue.unsafeCreate("p2".getBytes())); + storeOp = + new StoreOperation.Builder(location) + .withContent(obj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + + obj.setValue(BinaryValue.create("{ \"content_s\":\"The rabbit-hole went straight on like a tunnel for some way, and then " + + "dipped suddenly down, so suddenly that Alice had not a moment to think " + + "about stopping herself before she found herself falling down a very deep " + + "well.\"}")); + + location = new Location(namespace, BinaryValue.unsafeCreate("p3".getBytes())); + storeOp = + new StoreOperation.Builder(location) + .withContent(obj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + } +} diff --git a/src/test/java/com/basho/riak/client/core/operations/itest/ITestSecondaryIndexQueryOp.java b/src/test/java/com/basho/riak/client/core/operations/itest/ITestSecondaryIndexQueryOp.java new file mode 100644 index 000000000..c19a40420 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/operations/itest/ITestSecondaryIndexQueryOp.java @@ -0,0 +1,605 @@ +/* + * 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.core.operations.itest; + +import com.basho.riak.client.core.operations.SecondaryIndexQueryOperation; +import com.basho.riak.client.core.operations.SecondaryIndexQueryOperation.Response.Entry; +import com.basho.riak.client.core.operations.StoreOperation; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.query.indexes.LongIntIndex; +import com.basho.riak.client.core.query.indexes.StringBinIndex; +import com.basho.riak.client.core.util.BinaryValue; + +import java.util.concurrent.ExecutionException; +import junit.framework.Assert; +import org.junit.Assume; +import static org.junit.Assert.*; +import org.junit.Test; + +/** + * + * @author Brian Roach + * @author Alex Moore + */ +public class ITestSecondaryIndexQueryOp extends ITestBase +{ + @Test + public void testSingleQuerySingleResponseDefaultType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + testSingleQuerySingleResponse(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testSingleQuerySingleResponseTestType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + Assume.assumeTrue(testBucketType); + testSingleQuerySingleResponse(bucketType.toString()); + } + + private void testSingleQuerySingleResponse(String bucketType) throws InterruptedException, ExecutionException + { + + String indexName = "test_index"; + String keyBase = "my_key"; + String value = "value"; + Namespace ns = new Namespace(bucketType, bucketName.toString()); + + setupIndexTestData(ns, indexName, keyBase, value); + + SecondaryIndexQueryOperation.Query query = + new SecondaryIndexQueryOperation.Query.Builder(ns, BinaryValue.unsafeCreate((indexName + "_int").getBytes())) + .withIndexKey(BinaryValue.unsafeCreate(String.valueOf(5L).getBytes())) + .build(); + + SecondaryIndexQueryOperation queryOp = + new SecondaryIndexQueryOperation.Builder(query) + .build(); + + cluster.execute(queryOp); + SecondaryIndexQueryOperation.Response response = queryOp.get(); + + assertEquals(1, response.getEntryList().size()); + assertFalse(response.getEntryList().get(0).hasIndexKey()); + assertEquals(response.getEntryList().get(0).getObjectKey().toString(), keyBase + "5"); + + query = + new SecondaryIndexQueryOperation.Query.Builder(ns, BinaryValue.unsafeCreate((indexName + "_int").getBytes())) + .withIndexKey(BinaryValue.unsafeCreate(String.valueOf(5L).getBytes())) + .withReturnKeyAndIndex(true) + .build(); + queryOp = + new SecondaryIndexQueryOperation.Builder(query) + .build(); + + cluster.execute(queryOp); + response = queryOp.get(); + + assertEquals(1, response.getEntryList().size()); + assertTrue(response.getEntryList().get(0).hasIndexKey()); + assertEquals(response.getEntryList().get(0).getIndexKey(), BinaryValue.unsafeCreate("5".getBytes())); + assertEquals(response.getEntryList().get(0).getObjectKey().toString(), keyBase + "5"); + } + + @Test + public void testSingleQueryMultipleResponseDefaultType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + testSingleQueryMultipleResponse(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testSingleQueryMultipleResponseTestType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + Assume.assumeTrue(testBucketType); + testSingleQueryMultipleResponse(bucketType.toString()); + } + + private void testSingleQueryMultipleResponse(String bucketType) throws InterruptedException, ExecutionException + { + + String indexName = "test_index"; + String keyBase = "my_key"; + String value = "value"; + Namespace ns = new Namespace(bucketType, bucketName.toString()); + + for (long i = 0; i < 100; i++) + { + RiakObject obj = new RiakObject().setValue(BinaryValue.create(value)); + + obj.getIndexes().getIndex(LongIntIndex.named(indexName)).add(5L); + Location location = new Location(ns, BinaryValue.unsafeCreate((keyBase + i).getBytes())); + StoreOperation storeOp = + new StoreOperation.Builder(location) + .withContent(obj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + } + + SecondaryIndexQueryOperation.Query query = + new SecondaryIndexQueryOperation.Query.Builder(ns, BinaryValue.unsafeCreate((indexName + "_int").getBytes())) + .withIndexKey(BinaryValue.unsafeCreate(String.valueOf(5L).getBytes())) + .withPaginationSort(true) + .build(); + + SecondaryIndexQueryOperation queryOp = + new SecondaryIndexQueryOperation.Builder(query) + .build(); + + cluster.execute(queryOp); + SecondaryIndexQueryOperation.Response response = queryOp.get(); + + assertEquals(100, response.getEntryList().size()); + assertFalse(response.getEntryList().get(0).hasIndexKey()); + assertEquals(response.getEntryList().get(0).getObjectKey().toString(), keyBase + "0"); + + query = + new SecondaryIndexQueryOperation.Query.Builder(ns, BinaryValue.unsafeCreate((indexName + "_int").getBytes())) + .withIndexKey(BinaryValue.unsafeCreate(String.valueOf(5L).getBytes())) + .withPaginationSort(true) + .withReturnKeyAndIndex(true) + .build(); + + queryOp = + new SecondaryIndexQueryOperation.Builder(query) + .build(); + + cluster.execute(queryOp); + response = queryOp.get(); + + assertEquals(100, response.getEntryList().size()); + assertTrue(response.getEntryList().get(0).hasIndexKey()); + assertEquals(response.getEntryList().get(0).getIndexKey(), BinaryValue.unsafeCreate("5".getBytes())); + assertEquals(response.getEntryList().get(0).getObjectKey().toString(), keyBase + "0"); + + } + + @Test + public void testRangeQueryDefaultType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + testRangeQuery(Namespace.DEFAULT_BUCKET_TYPE); + + } + + @Test + public void testRangeQueryTestType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + Assume.assumeTrue(testBucketType); + testRangeQuery(bucketType.toString()); + + } + + private void testRangeQuery(String bucketType) throws InterruptedException, ExecutionException + { + + String indexName = "test_index"; + String keyBase = "my_key"; + String value = "value"; + Namespace ns = new Namespace(bucketType, bucketName.toString()); + + setupIndexTestData(ns, indexName, keyBase, value); + + + SecondaryIndexQueryOperation.Query query = + new SecondaryIndexQueryOperation.Query.Builder(ns, BinaryValue.unsafeCreate((indexName + "_int").getBytes())) + .withRangeStart(BinaryValue.unsafeCreate(String.valueOf(5L).getBytes())) + .withRangeEnd(BinaryValue.unsafeCreate(String.valueOf(20L).getBytes())) + .withPaginationSort(true) + .build(); + + SecondaryIndexQueryOperation queryOp = + new SecondaryIndexQueryOperation.Builder(query) + .build(); + + cluster.execute(queryOp); + SecondaryIndexQueryOperation.Response response = queryOp.get(); + + assertEquals(16, response.getEntryList().size()); + assertFalse(response.getEntryList().get(0).hasIndexKey()); + assertEquals(response.getEntryList().get(0).getObjectKey().toString(), keyBase + "5"); + + query = + new SecondaryIndexQueryOperation.Query.Builder(ns, BinaryValue.unsafeCreate((indexName + "_int").getBytes())) + .withRangeStart(BinaryValue.unsafeCreate(String.valueOf(5L).getBytes())) + .withRangeEnd(BinaryValue.unsafeCreate(String.valueOf(20L).getBytes())) + .withReturnKeyAndIndex(true) + .withPaginationSort(true) + .build(); + + queryOp = + new SecondaryIndexQueryOperation.Builder(query) + .build(); + + cluster.execute(queryOp); + response = queryOp.get(); + assertEquals(16, response.getEntryList().size()); + assertTrue(response.getEntryList().get(0).hasIndexKey()); + assertEquals(response.getEntryList().get(0).getIndexKey(), BinaryValue.unsafeCreate("5".getBytes())); + assertEquals(response.getEntryList().get(0).getObjectKey().toString(), keyBase + "5"); + } + + @Test + public void testNoSortWithNoPagingDefaultType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + testNoSortWithNoPaging(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testNoSortWithNoPagingTestType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + Assume.assumeTrue(testBucketType); + testNoSortWithNoPaging(bucketType.toString()); + } + + private void testNoSortWithNoPaging(String bucketType) throws InterruptedException, ExecutionException + { + + String indexName = "test_index_pagination"; + String value = "value"; + Namespace ns = new Namespace(bucketType, bucketName.toString()); + + setupIndexTestData(ns, indexName, "", value); + + SecondaryIndexQueryOperation.Query query = + new SecondaryIndexQueryOperation.Query.Builder(ns, BinaryValue.unsafeCreate((indexName + "_int").getBytes())) + .withRangeStart(BinaryValue.unsafeCreate(String.valueOf(0L).getBytes())) + .withRangeEnd(BinaryValue.unsafeCreate(String.valueOf(100L).getBytes())) + .withPaginationSort(false) + .build(); + + SecondaryIndexQueryOperation queryOp = + new SecondaryIndexQueryOperation.Builder(query) + .build(); + + cluster.execute(queryOp); + SecondaryIndexQueryOperation.Response response = queryOp.get(); + + assertEquals(100, response.getEntryList().size()); + } + + @Test + public void testSortWithNoPagingDefaultType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + testSortWithNoPaging(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testSortWithNoPagingTestType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + Assume.assumeTrue(testBucketType); + testSortWithNoPaging(bucketType.toString()); + } + + private void testSortWithNoPaging(String bucketType) throws InterruptedException, ExecutionException + { + + String indexName = "test_index_pagination"; + String value = "value"; + Namespace ns = new Namespace(bucketType, bucketName.toString()); + + setupIndexTestData(ns, indexName, "", value); + + + SecondaryIndexQueryOperation.Query query = + new SecondaryIndexQueryOperation.Query.Builder(ns, BinaryValue.unsafeCreate((indexName + "_int").getBytes())) + .withRangeStart(BinaryValue.unsafeCreate(String.valueOf(0L).getBytes())) + .withRangeEnd(BinaryValue.unsafeCreate(String.valueOf(100L).getBytes())) + .withPaginationSort(true) + .build(); + + SecondaryIndexQueryOperation queryOp = + new SecondaryIndexQueryOperation.Builder(query) + .build(); + + cluster.execute(queryOp); + SecondaryIndexQueryOperation.Response response = queryOp.get(); + + assertEquals(100, response.getEntryList().size()); + + AssertLongObjectsInOrder(response); + } + + @Test + public void testNoSortWithPagingDefaultType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + testNoSortWithPaging(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testNoSortWithPagingTestType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + Assume.assumeTrue(testBucketType); + testNoSortWithPaging(bucketType.toString()); + } + + private void testNoSortWithPaging(String bucketType) throws InterruptedException, ExecutionException + { + + String indexName = "test_index_pagination"; + String value = "value"; + Namespace ns = new Namespace(bucketType, bucketName.toString()); + + setupIndexTestData(ns, indexName, "", value); + + try + { + SecondaryIndexQueryOperation.Query query = + new SecondaryIndexQueryOperation.Query.Builder(ns, BinaryValue.unsafeCreate((indexName + "_int").getBytes())) + .withRangeStart(BinaryValue.unsafeCreate(String.valueOf(0L).getBytes())) + .withRangeEnd(BinaryValue.unsafeCreate(String.valueOf(100L).getBytes())) + .withPaginationSort(false) + .withMaxResults(20) + .build(); + + SecondaryIndexQueryOperation queryOp = + new SecondaryIndexQueryOperation.Builder(query) + .build(); + + fail("Didn't throw IllegalArgumentException"); + } + catch(IllegalArgumentException ex) { + assertNotNull(ex); + } + } + + @Test + public void testSortWithPagingDefaultType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + testSortWithPaging(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testSortWithPagingTestType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + Assume.assumeTrue(testBucketType); + testSortWithPaging(Namespace.DEFAULT_BUCKET_TYPE); + } + + private void testSortWithPaging(String bucketType) throws InterruptedException, ExecutionException + { + String indexName = "test_index_pagination"; + String value = "value"; + Namespace ns = new Namespace(bucketType, bucketName.toString()); + + setupIndexTestData(ns, indexName, "", value); + + SecondaryIndexQueryOperation.Query query = + new SecondaryIndexQueryOperation.Query.Builder(ns, BinaryValue.unsafeCreate((indexName + "_int").getBytes())) + .withRangeStart(BinaryValue.unsafeCreate(String.valueOf(0L).getBytes())) + .withRangeEnd(BinaryValue.unsafeCreate(String.valueOf(100L).getBytes())) + .withPaginationSort(true) + .withMaxResults(20) + .build(); + + SecondaryIndexQueryOperation queryOp = + new SecondaryIndexQueryOperation.Builder(query) + .build(); + + cluster.execute(queryOp); + SecondaryIndexQueryOperation.Response response = queryOp.get(); + + assertEquals(20, response.getEntryList().size()); + + AssertLongObjectsInOrder(response); + } + + @Test + public void testRegexTermFilterDefaultType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + testRegexTermFilter(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testRegexTermFilterTestType() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + Assume.assumeTrue(testBucketType); + testRegexTermFilter(bucketType.toString()); + } + + private void testRegexTermFilter(String bucketType) throws InterruptedException, ExecutionException + { + String indexName = "test_index_regex"; + String value = "value"; + Namespace ns = new Namespace(bucketType, bucketName.toString()); + + for (long i = 0; i < 20; i++) + { + RiakObject obj = new RiakObject().setValue(BinaryValue.create(value)); + + obj.getIndexes().getIndex(StringBinIndex.named(indexName)).add("foo" + String.format("%02d", i)); + + + Location location = new Location(ns, BinaryValue.unsafeCreate(Long.toString(i).getBytes())); + StoreOperation storeOp = + new StoreOperation.Builder(location) + .withContent(obj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + } + + + SecondaryIndexQueryOperation.Query query = + new SecondaryIndexQueryOperation.Query.Builder(ns, BinaryValue.unsafeCreate((indexName + "_bin").getBytes())) + .withRangeStart(BinaryValue.unsafeCreate("foo00".getBytes())) + .withRangeEnd(BinaryValue.unsafeCreate("foo19".getBytes())) + .withRegexTermFilter(BinaryValue.unsafeCreate("2".getBytes())) + .withReturnKeyAndIndex(true) + .withPaginationSort(true) + .build(); + + SecondaryIndexQueryOperation queryOp = + new SecondaryIndexQueryOperation.Builder(query) + .build(); + + cluster.execute(queryOp); + SecondaryIndexQueryOperation.Response response = queryOp.get(); + + assertEquals(2, response.getEntryList().size()); + assertEquals(response.getEntryList().get(0).getIndexKey(), BinaryValue.unsafeCreate("foo02".getBytes())); + assertEquals(response.getEntryList().get(0).getObjectKey().toString(), "2"); + assertEquals(response.getEntryList().get(1).getIndexKey(), BinaryValue.unsafeCreate("foo12".getBytes())); + assertEquals(response.getEntryList().get(1).getObjectKey().toString(), "12"); + } + + @Test + public void testExceptionThrownWhenUsingRegexFilterOnIntIndexesDefaultType() + { + Assume.assumeTrue(test2i); + testExceptionThrownWhenUsingRegexFilterOnIntIndexes(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testExceptionThrownWhenUsingRegexFilterOnIntIndexesTestType() + { + Assume.assumeTrue(test2i); + Assume.assumeTrue(testBucketType); + testExceptionThrownWhenUsingRegexFilterOnIntIndexes(bucketType.toString()); + } + + private void testExceptionThrownWhenUsingRegexFilterOnIntIndexes(String bucketType) + { + + try { + Namespace ns = new Namespace(bucketType, bucketName.toString()); + + SecondaryIndexQueryOperation.Query query = + new SecondaryIndexQueryOperation.Query.Builder(ns, BinaryValue.unsafeCreate(("foo_int").getBytes())) + .withRangeStart(BinaryValue.unsafeCreate("0".getBytes())) + .withRangeEnd(BinaryValue.unsafeCreate("100".getBytes())) + .withRegexTermFilter(BinaryValue.unsafeCreate("2".getBytes())) + .build(); + + new SecondaryIndexQueryOperation.Builder(query) + .build(); + + fail("Didn't throw IllegalArgumentException"); + } + catch(IllegalArgumentException ex) { + assertNotNull(ex); + } + } + + @Test + public void testBucketIndexHack() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + String indexName = "test_index_bucket"; + String keyBase = "my_key"; + String value = "value"; + + Namespace ns = new Namespace(bucketName.toString()); + + setupIndexTestData(ns, indexName, keyBase, value); + + SecondaryIndexQueryOperation.Query query = + new SecondaryIndexQueryOperation.Query.Builder(ns, BinaryValue.unsafeCreate("$bucket".getBytes())) + .withIndexKey(bucketName) + .withReturnKeyAndIndex(true) + .build(); + + SecondaryIndexQueryOperation queryOp = + new SecondaryIndexQueryOperation.Builder(query) + .build(); + + cluster.execute(queryOp); + SecondaryIndexQueryOperation.Response response = queryOp.get(); + + Assert.assertTrue(response.getEntryList().size() == 100); + + + } + + @Test + public void testKeyIndexHack() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(test2i); + String indexName = "test_index_bucket"; + String keyBase = "my_key"; + String value = "value"; + + Namespace ns = new Namespace(bucketName.toString()); + + setupIndexTestData(ns, indexName, keyBase, value); + + SecondaryIndexQueryOperation.Query query = + new SecondaryIndexQueryOperation.Query.Builder(ns, BinaryValue.unsafeCreate("$key".getBytes())) + .withRangeStart(BinaryValue.create("my_key10")) + .withRangeEnd(BinaryValue.create("my_key19")) + .withReturnKeyAndIndex(true) + .build(); + + SecondaryIndexQueryOperation queryOp = + new SecondaryIndexQueryOperation.Builder(query) + .build(); + + cluster.execute(queryOp); + SecondaryIndexQueryOperation.Response response = queryOp.get(); + + Assert.assertTrue(response.getEntryList().size() == 10); + + + } + + private void setupIndexTestData(Namespace ns, String indexName, String keyBase, String value) + throws InterruptedException, ExecutionException + { + for (long i = 0; i < 100; i++) + { + RiakObject obj = new RiakObject().setValue(BinaryValue.create(value)); + + obj.getIndexes().getIndex(LongIntIndex.named(indexName)).add(i); + + Location location = new Location(ns, keyBase + i); + StoreOperation storeOp = + new StoreOperation.Builder(location) + .withContent(obj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + } + } + + private void AssertLongObjectsInOrder(SecondaryIndexQueryOperation.Response response) { + Long previousKey = Long.parseLong(response.getEntryList().get(0).getObjectKey().toString()); + for (int j = 1; j < response.getEntryList().size(); j++) { + Long currentKey = Long.parseLong(response.getEntryList().get(j).getObjectKey().toString()); + assertTrue(previousKey <= currentKey); + previousKey = currentKey; + } + } +} + diff --git a/src/test/java/com/basho/riak/client/core/operations/itest/ITestStoreOperation.java b/src/test/java/com/basho/riak/client/core/operations/itest/ITestStoreOperation.java new file mode 100644 index 000000000..ecbe0b56e --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/operations/itest/ITestStoreOperation.java @@ -0,0 +1,135 @@ +/* + * 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.core.operations.itest; + +import com.basho.riak.client.core.operations.FetchOperation; +import com.basho.riak.client.core.operations.StoreBucketPropsOperation; +import com.basho.riak.client.core.operations.StoreOperation; +import static com.basho.riak.client.core.operations.itest.ITestBase.bucketName; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; +import java.util.concurrent.ExecutionException; +import static org.junit.Assert.*; +import static org.junit.Assume.assumeTrue; +import org.junit.Test; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public class ITestStoreOperation extends ITestBase +{ + final private BinaryValue key = BinaryValue.unsafeCreate("my_key".getBytes()); + final private String value = "{\"value\":\"some value\"}"; + + @Test + public void testSimpleStoreDefaultType() throws InterruptedException, ExecutionException + { + testSimpleStore(Namespace.DEFAULT_BUCKET_TYPE); + } + + @Test + public void testSimpleStoreTestType() throws InterruptedException, ExecutionException + { + assumeTrue(testBucketType); + testSimpleStore(bucketType.toString()); + } + + private void testSimpleStore(String bucketType) throws InterruptedException, ExecutionException + { + + RiakObject obj = new RiakObject().setValue(BinaryValue.create(value)); + Namespace ns = new Namespace(bucketType, bucketName.toString()); + Location location = new Location(ns, key); + StoreOperation storeOp = + new StoreOperation.Builder(location) + .withContent(obj) + .build(); + + cluster.execute(storeOp); + storeOp.get(); + + FetchOperation fetchOp = + new FetchOperation.Builder(location).build(); + + cluster.execute(fetchOp); + RiakObject obj2 = fetchOp.get().getObjectList().get(0); + + assertEquals(obj.getValue(), obj2.getValue()); + + } + + @Test + public void testStoreWithVClockAndReturnbodyDefaultType() throws InterruptedException, ExecutionException + { + assumeTrue(testBucketType); + testStoreWithVClockAndReturnbody(bucketType.toString()); + } + + @Test + public void testStoreWithVClockAndReturnbodyTestType() throws InterruptedException, ExecutionException + { + testStoreWithVClockAndReturnbody(Namespace.DEFAULT_BUCKET_TYPE); + } + + private void testStoreWithVClockAndReturnbody(String bucketType) throws InterruptedException, ExecutionException + { + // Enable allow_multi, store a new item, then do a read/modify/write + // using the vclock + + Namespace ns = new Namespace(bucketType, bucketName.toString() + "_1"); + StoreBucketPropsOperation op = + new StoreBucketPropsOperation.Builder(ns) + .withAllowMulti(true) + .build(); + cluster.execute(op); + op.get(); + + RiakObject obj = new RiakObject().setValue(BinaryValue.create(value)); + Location location = new Location(ns, key); + + StoreOperation storeOp = + new StoreOperation.Builder(location) + .withContent(obj) + .withReturnBody(true) + .build(); + + cluster.execute(storeOp); + StoreOperation.Response response = storeOp.get(); + obj = response.getObjectList().get(0); + + assertNotNull(obj.getVClock()); + + obj.setValue(BinaryValue.create("changed")); + + storeOp = new StoreOperation.Builder(location) + .withContent(obj) + .withReturnBody(true) + .build(); + + cluster.execute(storeOp); + response = storeOp.get(); + obj = response.getObjectList().get(0); + + assertEquals(obj.getValue().toString(), "changed"); + + resetAndEmptyBucket(ns); + } + +} diff --git a/src/test/java/com/basho/riak/client/core/operations/itest/ITestYzAdminOperations.java b/src/test/java/com/basho/riak/client/core/operations/itest/ITestYzAdminOperations.java new file mode 100644 index 000000000..f8f59ff9c --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/operations/itest/ITestYzAdminOperations.java @@ -0,0 +1,159 @@ +/* + * 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.core.operations.itest; + +import com.basho.riak.client.core.operations.YzDeleteIndexOperation; +import com.basho.riak.client.core.operations.YzFetchIndexOperation; +import com.basho.riak.client.core.operations.YzGetSchemaOperation; +import com.basho.riak.client.core.operations.YzPutIndexOperation; +import com.basho.riak.client.core.operations.YzPutSchemaOperation; +import com.basho.riak.client.core.query.search.YokozunaIndex; +import com.basho.riak.client.core.query.search.YokozunaSchema; +import java.util.List; +import java.util.concurrent.ExecutionException; +import static org.junit.Assert.*; +import org.junit.Assume; +import org.junit.Ignore; +import org.junit.Test; + +/** + * + * @author Brian Roach + * @since 2.0 + */ +public class ITestYzAdminOperations extends ITestBase +{ + + // This is asSet to ignore as I've found a problem with this schema being accepted as + // valid but then causing problems later if you try to create an index. Need to talk to Z. + // After that I can expand the unit tests. As is though, they show the networky + // bits work. + @Ignore + @Test + public void testStoreandFetchSchema() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(testYokozuna); + YokozunaSchema yzSchema = new YokozunaSchema("test_schema", + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "_yz_id" + + "" + + "" + + "" + + "" + + ""); + + + YzPutSchemaOperation putOp = new YzPutSchemaOperation.Builder(yzSchema).build(); + cluster.execute(putOp); + putOp.get(); + + YzGetSchemaOperation getOp = + new YzGetSchemaOperation.Builder("test_schema").build(); + + cluster.execute(getOp); + YokozunaSchema yzSchema2 = getOp.get().getSchema(); + + assertEquals(yzSchema.getContent(), yzSchema2.getContent()); + + + } + + @Test + public void fetchAndStoreDefaultSchema() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(testYokozuna); + YzGetSchemaOperation getOp = + new YzGetSchemaOperation.Builder("_yz_default").build(); + + cluster.execute(getOp); + YokozunaSchema yzSchema = getOp.get().getSchema(); + + assertNotNull(yzSchema.getName()); + assertNotNull(yzSchema.getContent()); + + YzPutSchemaOperation putOp = new YzPutSchemaOperation.Builder(yzSchema).build(); + cluster.execute(putOp); + putOp.get(); + + } + + @Test + public void testStoreAndFetchIndex() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(testYokozuna); + YokozunaIndex index = new YokozunaIndex("test_index"); + YzPutIndexOperation putOp = new YzPutIndexOperation.Builder(index).build(); + + cluster.execute(putOp); + putOp.get(); + + assertTrue("Index not created", assureIndexExists("test_index")); + + + YzFetchIndexOperation fetchOp = + new YzFetchIndexOperation.Builder().withIndexName("test_index") + .build(); + + cluster.execute(fetchOp); + fetchOp.await(); + if (!fetchOp.isSuccess()) + { + assertTrue(fetchOp.cause().toString(), fetchOp.isSuccess()); + } + + List indexList = fetchOp.get().getIndexes(); + + assertFalse(indexList.isEmpty()); + index = indexList.get(0); + assertEquals(index.getSchema(), "_yz_default"); + + YzDeleteIndexOperation delOp = new YzDeleteIndexOperation.Builder("test_index").build(); + cluster.execute(delOp); + delOp.await(); + assertTrue(delOp.isSuccess()); + + } + + @Test + public void testDeleteIndex() throws InterruptedException, ExecutionException + { + Assume.assumeTrue(testYokozuna); + YokozunaIndex index = new YokozunaIndex("test_index5"); + YzPutIndexOperation putOp = new YzPutIndexOperation.Builder(index).build(); + + cluster.execute(putOp); + putOp.get(); + + assertTrue("Index not created", assureIndexExists("test_index5")); + + YzDeleteIndexOperation delOp = + new YzDeleteIndexOperation.Builder("test_index5").build(); + cluster.execute(delOp); + delOp.get(); + + } + +} diff --git a/src/test/java/com/basho/riak/client/core/query/indexes/IndexTest.java b/src/test/java/com/basho/riak/client/core/query/indexes/IndexTest.java new file mode 100644 index 000000000..5e0b97d9e --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/query/indexes/IndexTest.java @@ -0,0 +1,66 @@ +/* + * 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.core.query.indexes; + +import com.basho.riak.client.core.query.indexes.IndexType; +import junit.framework.Assert; +import org.junit.Test; + +public class IndexTest +{ + + @Test(expected = IllegalArgumentException.class) + public void testInvalidIndexName() + { + + IndexType.typeFromFullname("notavaildname"); + + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidIndexTypeExtension() + { + + IndexType.typeFromFullname("notavalidname_notavalidextension"); + + } + + @Test + public void correctSuffixes() + { + Assert.assertEquals(IndexType.BIN.suffix(), "_bin"); + Assert.assertEquals(IndexType.INT.suffix(), "_int"); + Assert.assertEquals(IndexType.BUCKET.suffix(), ""); + Assert.assertEquals(IndexType.KEY.suffix(), ""); + } + + @Test + public void testValidIndexTypeExtensions() { + + IndexType indexType = IndexType.typeFromFullname("indexname_int"); + Assert.assertTrue(indexType.equals(IndexType.INT)); + + IndexType indexType1 = IndexType.typeFromFullname("indexname_bin"); + Assert.assertTrue(indexType1.equals(IndexType.BIN)); + + IndexType indexType2 = IndexType.typeFromFullname("$bucket"); + Assert.assertTrue(indexType2.equals(IndexType.BUCKET)); + + IndexType indexType3 = IndexType.typeFromFullname("$key"); + Assert.assertTrue(indexType3.equals(IndexType.KEY)); + + } +} diff --git a/src/test/java/com/basho/riak/client/core/query/indexes/LongIntIndexTest.java b/src/test/java/com/basho/riak/client/core/query/indexes/LongIntIndexTest.java new file mode 100644 index 000000000..2c201ba23 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/query/indexes/LongIntIndexTest.java @@ -0,0 +1,50 @@ +/* + * 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.core.query.indexes; + +import com.basho.riak.client.core.query.indexes.LongIntIndex; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + + +/** + * This only tests the conversions. RiakIndex is fully exercised via the RawIndexTest + * @author Brian Roach + */ +public class LongIntIndexTest +{ + @Test + public void storeAndRetrieveLong() + { + LongIntIndex index = new LongIntIndex.Name("index_name").createIndex(); + Set longSet = new HashSet(Arrays.asList(Long.MAX_VALUE, Long.MIN_VALUE)); + + index.add(longSet); + + assertEquals(index.size(), 2); + assertTrue(index.hasValue(Long.MIN_VALUE)); + assertTrue(index.hasValue(Long.MAX_VALUE)); + + Set longSet2 = index.values(); + assertTrue(longSet.containsAll(longSet2)); + + } + +} diff --git a/src/test/java/com/basho/riak/client/core/query/indexes/RawIndexTest.java b/src/test/java/com/basho/riak/client/core/query/indexes/RawIndexTest.java new file mode 100644 index 000000000..7355e3cc6 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/query/indexes/RawIndexTest.java @@ -0,0 +1,259 @@ +/* + * 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.core.query.indexes; + +import com.basho.riak.client.core.query.indexes.IndexType; +import com.basho.riak.client.core.query.indexes.RawIndex; +import com.basho.riak.client.core.util.BinaryValue; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +/** + * Note that the tests here exercise all the RiakIndex abstract methods + * @author Brian Roach + */ +public class RawIndexTest +{ + + + @Test + public void builderCreatesIndex() + { + RawIndex.Name builder = new RawIndex.Name("index_name", IndexType.INT); + RawIndex index = builder.createIndex(); + assertEquals(index.getName(), "index_name"); + assertEquals(index.getType(), IndexType.INT); + assertEquals(index.getFullname(), "index_name" + IndexType.INT.suffix()); + + builder = new RawIndex.Name("index_name", IndexType.BIN); + index = builder.createIndex(); + assertEquals(index.getName(), "index_name"); + assertEquals(index.getType(), IndexType.BIN); + assertEquals(index.getFullname(), "index_name" + IndexType.BIN.suffix()); + + } + + @Test + public void stripSuffix() + { + RawIndex.Name builder = new RawIndex.Name("index_name_int", IndexType.INT); + RawIndex index = builder.createIndex(); + assertEquals(index.getName(), "index_name"); + } + + @Test + public void addValue() + { + RawIndex.Name builder = new RawIndex.Name("index_name", IndexType.BIN); + RawIndex index = builder.createIndex(); + byte[] array = "value".getBytes(); + BinaryValue baw = BinaryValue.unsafeCreate(array); + + assertEquals(index.size(), 0); + index.add(baw); + assertEquals(index.size(), 1); + assertTrue(index.hasValue(baw)); + } + + @Test + public void addValues() + { + RawIndex.Name builder = new RawIndex.Name("index_name", IndexType.BIN); + RawIndex index = builder.createIndex(); + + List values = new LinkedList(); + for (int i = 0; i < 5; i++) + { + values.add(BinaryValue.unsafeCreate(("value" + i).getBytes())); + } + + index.add(values); + + assertEquals(index.size(), 5); + + for (BinaryValue baw : values) + { + assertTrue(index.hasValue(baw)); + } + } + + @Test + public void removeValue() + { + RawIndex.Name builder = new RawIndex.Name("index_name", IndexType.BIN); + RawIndex index = builder.createIndex(); + BinaryValue baw = null; + for (int i = 0; i < 3; i++) + { + baw = BinaryValue.unsafeCreate(("value" + i).getBytes()); + index.add(baw); + } + + assertEquals(index.size(), 3); + assertTrue(index.hasValue(baw)); + index.remove(baw); + assertFalse(index.hasValue(baw)); + assertEquals(index.size(), 2); + } + + @Test + public void removeValues() + { + RawIndex.Name builder = new RawIndex.Name("index_name", IndexType.BIN); + RawIndex index = builder.createIndex(); + + List values = new LinkedList(); + for (int i = 0; i < 5; i++) + { + values.add(BinaryValue.unsafeCreate(("value" + i).getBytes())); + } + + index.add(values); + + assertEquals(index.size(), 5); + BinaryValue baw = values.remove(0); + index.remove(values); + assertEquals(index.size(), 1); + assertTrue(index.hasValue(baw)); + for (BinaryValue b : values ) + { + assertFalse(index.hasValue(b)); + } + } + + @Test + public void noDuplicates() + { + RawIndex.Name builder = new RawIndex.Name("index_name", IndexType.BIN); + RawIndex index = builder.createIndex(); + + for (int i = 0; i < 5; i++) + { + index.add(BinaryValue.unsafeCreate("value".getBytes())); + } + + assertEquals(index.size(), 1); + + } + + @Test + public void wrap() + { + RawIndex.Name builder = new RawIndex.Name("index_name", IndexType.BIN); + RawIndex index = builder.createIndex(); + + List values = new LinkedList(); + for (int i = 0; i < 5; i++) + { + values.add(BinaryValue.unsafeCreate(("value" + i).getBytes())); + } + + index.add(values); + + builder = new RawIndex.Name("index_name", IndexType.BIN); + RawIndex index2 = builder.wrap(index).createIndex(); + + assertEquals(index, index2); + assertNotSame(index, index2); + assertTrue(index2.hasValue(values.get(0))); + index.remove(values.get(0)); + assertFalse(index2.hasValue(values.get(0))); + + } + + @Test + public void copy() + { + RawIndex.Name builder = new RawIndex.Name("index_name", IndexType.BIN); + RawIndex index = builder.createIndex(); + + List values = new LinkedList(); + for (int i = 0; i < 5; i++) + { + values.add(BinaryValue.unsafeCreate(("value" + i).getBytes())); + } + + index.add(values); + + builder = new RawIndex.Name("index_name", IndexType.BIN); + RawIndex index2 = builder.copyFrom(index).createIndex(); + + assertEquals(index, index2); + assertNotSame(index, index2); + assertTrue(index2.hasValue(values.get(0))); + index.remove(values.get(0)); + assertTrue(index2.hasValue(values.get(0))); + } + + @Test + public void iterator() + { + RawIndex.Name builder = new RawIndex.Name("index_name", IndexType.BIN); + RawIndex index = builder.createIndex(); + + List values = new LinkedList(); + for (int i = 0; i < 5; i++) + { + values.add(BinaryValue.unsafeCreate(("value" + i).getBytes())); + } + + index.add(values); + + for (BinaryValue b : index) + { + assertTrue(index.hasValue(b)); + } + + Iterator i = index.iterator(); + + while (i.hasNext()) + { + BinaryValue b = i.next(); + i.remove(); + assertFalse(index.hasValue(b)); + } + } + + @Test + public void values() + { + RawIndex.Name builder = new RawIndex.Name("index_name", IndexType.BIN); + RawIndex index = builder.createIndex(); + + List values = new LinkedList(); + for (int i = 0; i < 5; i++) + { + values.add(BinaryValue.unsafeCreate(("value" + i).getBytes())); + } + + index.add(values); + + Set valueSet = index.values(); + + for (BinaryValue b : values) + { + assertTrue(valueSet.contains(b)); + } + } + +} diff --git a/src/test/java/com/basho/riak/client/core/query/indexes/RiakIndexesTest.java b/src/test/java/com/basho/riak/client/core/query/indexes/RiakIndexesTest.java new file mode 100644 index 000000000..2bbdb6f61 --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/query/indexes/RiakIndexesTest.java @@ -0,0 +1,165 @@ +/* + * 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.core.query.indexes; + +import com.basho.riak.client.core.query.indexes.StringBinIndex; +import com.basho.riak.client.core.query.indexes.LongIntIndex; +import com.basho.riak.client.core.query.indexes.IndexType; +import com.basho.riak.client.core.query.indexes.RiakIndexes; +import com.basho.riak.client.core.query.indexes.RawIndex; +import com.basho.riak.client.core.util.BinaryValue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; + + +/** + * + * @author Brian Roach + */ +public class RiakIndexesTest +{ + private RiakIndexes indexes; + + @Before + public void setup() + { + indexes = new RiakIndexes(); + } + + @Test + public void createIndex() + { + assertTrue(indexes.isEmpty()); + LongIntIndex index = indexes.getIndex(new LongIntIndex.Name("foo")); + assertFalse(indexes.isEmpty()); + assertEquals(indexes.size(), 1); + assertTrue(indexes.hasIndex(new LongIntIndex.Name("foo"))); + } + + @Test + public void addToIndex() + { + assertTrue(indexes.isEmpty()); + + indexes.getIndex(new LongIntIndex.Name("my_index")).add(4L); + + assertTrue(indexes.hasIndex(new LongIntIndex.Name("my_index"))); + + LongIntIndex lii = indexes.getIndex(new LongIntIndex.Name("my_index")); + assertTrue(lii.hasValue(4L)); + + RawIndex rri = indexes.getIndex(new RawIndex.Name("my_index", IndexType.INT)); + assertTrue(rri.hasValue(BinaryValue.unsafeCreate(String.valueOf(4L).getBytes()))); + + assertEquals(indexes.size(), 1); + + } + + @Test + public void removeIndex() + { + assertTrue(indexes.isEmpty()); + StringBinIndex stringIndex = indexes.getIndex(new StringBinIndex.Name("bar")); + LongIntIndex longIndex = indexes.getIndex(new LongIntIndex.Name("foo")); + + LongIntIndex removedLongIndex = indexes.removeIndex(new LongIntIndex.Name("foo")); + assertEquals(longIndex, removedLongIndex); + assertEquals(indexes.size(), 1); + } + + @Test + public void getIndex() + { + indexes.getIndex(new LongIntIndex.Name("foo")).add(Long.MIN_VALUE); + LongIntIndex index = indexes.getIndex(new LongIntIndex.Name("foo")); + assertEquals(index.getName(), "foo"); + assertTrue(index.hasValue(Long.MIN_VALUE)); + } + + @Test + public void size() + { + assertTrue(indexes.isEmpty()); + for (int i = 0; i < 5; i++) + { + indexes.getIndex(new StringBinIndex.Name("name" + i)); + } + assertEquals(indexes.size(), 5); + indexes.removeIndex(new StringBinIndex.Name("name0")); + assertEquals(indexes.size(), 4); + indexes.removeAllIndexes(); + assertEquals(indexes.size(), 0); + } + + @Test + public void empty() + { + assertTrue(indexes.isEmpty()); + for (int i = 0; i < 2; i++) + { + indexes.getIndex(new StringBinIndex.Name("name" + i)); + } + assertFalse(indexes.isEmpty()); + indexes.removeIndex(new StringBinIndex.Name("name0")); + assertFalse(indexes.isEmpty()); + indexes.removeIndex(new StringBinIndex.Name("name1")); + assertTrue(indexes.isEmpty()); + indexes.getIndex(new LongIntIndex.Name("foo")); + assertFalse(indexes.isEmpty()); + indexes.removeAllIndexes(); + assertTrue(indexes.isEmpty()); + } + + @Test + public void indexTypesAreDifferent() + { + assertTrue(indexes.isEmpty()); + indexes.getIndex(new LongIntIndex.Name("foo")).add(Long.MIN_VALUE); + indexes.getIndex(new StringBinIndex.Name("foo")).add("value"); + assertEquals(indexes.size(), 2); + + StringBinIndex stringIndex = indexes.getIndex(new StringBinIndex.Name("foo")); + assertEquals(stringIndex.size(), 1); + assertTrue(stringIndex.hasValue("value")); + + LongIntIndex longIndex = indexes.getIndex(new LongIntIndex.Name("foo")); + assertEquals(stringIndex.size(), 1); + assertTrue(longIndex.hasValue(Long.MIN_VALUE)); + } + + @Test + public void wrapping() + { + RawIndex index = indexes.getIndex(new RawIndex.Name("foo", IndexType.BIN)); + BinaryValue baw = BinaryValue.unsafeCreate("value".getBytes()); + index.add(baw); + + StringBinIndex wrapper = indexes.getIndex(new StringBinIndex.Name("foo")); + assertNotSame(index, wrapper); + assertEquals(index, wrapper); + assertTrue(wrapper.hasValue("value")); + + wrapper.remove("value"); + + index = indexes.getIndex(new RawIndex.Name("foo", IndexType.BIN)); + assertFalse(index.hasValue(baw)); + } + +} diff --git a/src/test/java/com/basho/riak/client/core/query/indexes/StringBinIndexTest.java b/src/test/java/com/basho/riak/client/core/query/indexes/StringBinIndexTest.java new file mode 100644 index 000000000..8d0e317fc --- /dev/null +++ b/src/test/java/com/basho/riak/client/core/query/indexes/StringBinIndexTest.java @@ -0,0 +1,48 @@ +/* + * 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.core.query.indexes; + +import com.basho.riak.client.core.query.indexes.StringBinIndex; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +/** + * This only tests the conversions. RiakIndex is fully exercised via the RawIndexTest + * @author Brian Roach + */ +public class StringBinIndexTest +{ + @Test + public void storeAndRetrieveString() + { + StringBinIndex index = new StringBinIndex.Name("index_name").createIndex(); + Set stringSet = new HashSet(Arrays.asList("value1", "value2")); + + index.add(stringSet); + + assertEquals(index.size(), 2); + assertTrue(index.hasValue("value1")); + assertTrue(index.hasValue("value2")); + + Set stringSet2 = index.values(); + assertTrue(stringSet.containsAll(stringSet2)); + + } +} diff --git a/src/test/java/com/basho/riak/client/itest/ITestBasic.java b/src/test/java/com/basho/riak/client/itest/ITestBasic.java deleted file mode 100644 index e6b53056c..000000000 --- a/src/test/java/com/basho/riak/client/itest/ITestBasic.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * 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.itest; - -import static com.basho.riak.client.Hosts.RIAK_URL; -import static com.basho.riak.client.itest.Utils.*; -import static org.junit.Assert.*; - -import java.util.UUID; - -import org.junit.Test; - -import com.basho.riak.client.RiakBucketInfo; -import com.basho.riak.client.RiakClient; -import com.basho.riak.client.RiakLink; -import com.basho.riak.client.RiakObject; -import com.basho.riak.client.plain.PlainClient; -import com.basho.riak.client.plain.RiakIOException; -import com.basho.riak.client.plain.RiakResponseException; -import com.basho.riak.client.request.RequestMeta; -import com.basho.riak.client.response.BucketResponse; -import com.basho.riak.client.response.FetchResponse; -import com.basho.riak.client.response.HttpResponse; -import com.basho.riak.client.response.StoreResponse; -import com.basho.riak.client.util.Constants; - -/** - * Basic exercises such as store, fetch, and modify objects for the Riak client. - * Assumes Riak is reachable at {@link com.basho.riak.client.Hosts#RIAK_URL }. - * @see com.basho.riak.client.Hosts#RIAK_URL - */ -public class ITestBasic { - - @Test public void store_fetch_modify() { - final RiakClient c = new RiakClient(RIAK_URL); - final String VALUE1 = "value1"; - final String VALUE2 = "value2"; - final RiakLink LINK = new RiakLink("bucket", "key", "tag"); - final String USERMETA_KEY = "usermeta"; - final String USERMETA_VALUE = "value"; - final String BUCKET = "store_fetch_modify"; - final String KEY = "key"; - - // Set bucket schema to return siblings - RiakBucketInfo bucketInfo = new RiakBucketInfo(); - bucketInfo.setAllowMult(true); - bucketInfo.setNVal(3); - assertSuccess(c.setBucketSchema(BUCKET, bucketInfo)); - - // Make sure object doesn't exist - assertSuccess(c.delete(BUCKET, KEY, WRITE_3_REPLICAS())); - - FetchResponse fetchresp = c.fetch(BUCKET, KEY); - assertEquals(404, fetchresp.getStatusCode()); - - // Store a new object - RiakObject o = new RiakObject(BUCKET, KEY, VALUE1.getBytes()); - StoreResponse storeresp = c.store(o, WRITE_3_REPLICAS()); - assertSuccess(storeresp); - - // Retrieve it back - fetchresp = c.fetch(BUCKET, KEY); - assertSuccess(fetchresp); - assertTrue(fetchresp.hasObject()); - assertEquals(VALUE1, fetchresp.getObject().getValue()); - assertFalse(fetchresp.getObject().hasLinks()); - assertFalse(fetchresp.getObject().hasUsermeta()); - - // Modify and store it - o = fetchresp.getObject(); - o.setValue(VALUE2); - o.addLink(LINK); - o.addUsermetaItem(USERMETA_KEY, USERMETA_VALUE); - storeresp = c.store(o, WRITE_3_REPLICAS()); - assertSuccess(storeresp); - - // Validate modification happened - fetchresp = c.fetch(BUCKET, KEY); - assertSuccess(fetchresp); - assertTrue(fetchresp.hasObject()); - assertFalse(fetchresp.hasSiblings()); - assertEquals(VALUE2, fetchresp.getObject().getValue()); - assertEquals(1, fetchresp.getObject().numLinks()); - assertTrue(fetchresp.getObject().hasLink(LINK)); - assertEquals(USERMETA_VALUE, fetchresp.getObject().getUsermetaItem(USERMETA_KEY)); - } - - @Test public void test_bucket_schema() { - final RiakClient c = new RiakClient(RIAK_URL); - final String BUCKET = UUID.randomUUID().toString(); - final String KEY1 = UUID.randomUUID().toString(); - final String KEY2 = UUID.randomUUID().toString(); - final String KEY3 = UUID.randomUUID().toString(); - final String CHASH_MOD = "riak_core_util"; - final String CHASH_FUN = "chash_bucketonly_keyfun"; - - // Add a few objects - assertSuccess(c.store(new RiakObject(BUCKET, KEY1, "v".getBytes()), WRITE_3_REPLICAS())); - assertSuccess(c.store(new RiakObject(BUCKET, KEY2, "v".getBytes()), WRITE_3_REPLICAS())); - assertSuccess(c.store(new RiakObject(BUCKET, KEY3, "v".getBytes()), WRITE_3_REPLICAS())); - - // Get the current bucket schema and contents - BucketResponse bucketresp = c.listBucket(BUCKET); - assertSuccess(bucketresp); - assertTrue(bucketresp.hasBucketInfo()); - RiakBucketInfo bucketInfo = bucketresp.getBucketInfo(); - int nval = bucketInfo.getNVal(); - - // Verify that contents are correct - assertTrue("Should contain key1: " + bucketInfo.getKeys(), bucketInfo.getKeys().contains(KEY1)); - assertTrue("Should contain key2: " + bucketInfo.getKeys(), bucketInfo.getKeys().contains(KEY2)); - assertTrue("Should contain key3: " + bucketInfo.getKeys(), bucketInfo.getKeys().contains(KEY3)); - - // Change some properties - bucketInfo.setNVal(nval + 1); - bucketInfo.setCHashFun(CHASH_MOD, CHASH_FUN); - assertSuccess(c.setBucketSchema(BUCKET, bucketInfo)); - - // Verify that properties stuck - bucketresp = c.getBucketSchema(BUCKET); - assertSuccess(bucketresp); - assertTrue(bucketresp.hasBucketInfo()); - bucketInfo = bucketresp.getBucketInfo(); - assertEquals(nval + 1, bucketInfo.getNVal().intValue()); - - // Clear out the objects we're testing with - assertSuccess(c.delete(BUCKET, KEY1)); - assertSuccess(c.delete(BUCKET, KEY2)); - assertSuccess(c.delete(BUCKET, KEY3)); - } - - @Test public void deleteQuorumIsApplied() { - final RiakClient c = new RiakClient(RIAK_URL); - - final String bucket = UUID.randomUUID().toString(); - final String key = UUID.randomUUID().toString(); - final byte[] value = "value".getBytes(); - - RiakBucketInfo bucketInfo = new RiakBucketInfo(); - bucketInfo.setNVal(3); - - c.setBucketSchema(bucket, bucketInfo); - RiakObject o = new RiakObject(bucket, key, value); - - final RequestMeta rm = WRITE_3_REPLICAS(); - - StoreResponse storeresp = c.store(o, rm); - assertSuccess(storeresp); - - HttpResponse deleteResponse = c.delete(bucket, key, RequestMeta.deleteParams(4)); - - assertEquals(500, deleteResponse.getStatusCode()); - assertTrue(deleteResponse.getBodyAsString().contains("n_val_violation")); - } - - @Test public void fetchNonExistant() { - final RiakClient c = new RiakClient(RIAK_URL); - final String BUCKET = UUID.randomUUID().toString(); - final String KEY = UUID.randomUUID().toString(); - - FetchResponse fetchresp = c.fetch(BUCKET, KEY); - assertEquals(404, fetchresp.getStatusCode()); - assertNull(fetchresp.getObject()); - } - - @Test public void fetchNonExistant_plainClient() throws RiakIOException, RiakResponseException { - final RiakClient c = new RiakClient(RIAK_URL); - PlainClient client = new PlainClient(c); - final String BUCKET = UUID.randomUUID().toString(); - final String KEY = UUID.randomUUID().toString(); - - RiakObject o = client.fetch(BUCKET, KEY); - assertNull(o); - } - - @Test public void storeWithReturnBody() { - final RiakClient c = new RiakClient(RIAK_URL); - final String bucket = UUID.randomUUID().toString(); - final String key = UUID.randomUUID().toString(); - final byte[] value = "value".getBytes(); - - RiakObject o = new RiakObject(bucket, key, value); - StoreResponse storeresp = c.store(o, WRITE_3_REPLICAS()); - assertSuccess(storeresp); - - assertTrue(storeresp.hasObject()); - assertArrayEquals(value, storeresp.getObject().getValueAsBytes()); - assertFalse(storeresp.hasSiblings()); - assertEquals(0, storeresp.getSiblings().size()); - } - - @Test public void storeWithReturnBodyAndSiblings() throws InterruptedException { - final RiakClient c = new RiakClient(RIAK_URL); - - final String bucket = UUID.randomUUID().toString(); - final String key = UUID.randomUUID().toString(); - final byte[] value = "value".getBytes(); - final byte[] newValue = "new_value".getBytes(); - - RiakBucketInfo bucketInfo = new RiakBucketInfo(); - bucketInfo.setAllowMult(true); - - c.setBucketSchema(bucket, bucketInfo); - RiakObject o = new RiakObject(bucket, key, value); - - final RequestMeta rm = WRITE_3_REPLICAS().setHeader(Constants.QP_RETURN_BODY, "true"); - - StoreResponse storeresp = c.store(o, rm); - assertSuccess(storeresp); - - o = new RiakObject(bucket, key, newValue); - storeresp = c.store(o, rm); - - assertSuccess(storeresp); - assertTrue(storeresp.hasObject()); - assertTrue(storeresp.hasSiblings()); - assertEquals(2, storeresp.getSiblings().size()); - } -} diff --git a/src/test/java/com/basho/riak/client/itest/ITestDataLoad.java b/src/test/java/com/basho/riak/client/itest/ITestDataLoad.java deleted file mode 100644 index 55a8e6e6f..000000000 --- a/src/test/java/com/basho/riak/client/itest/ITestDataLoad.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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.itest; - - -import static com.basho.riak.client.Hosts.RIAK_URL; -import static org.junit.Assert.*; - -import java.util.Random; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Before; -import org.junit.Test; - -import com.basho.riak.client.RiakClient; -import com.basho.riak.client.RiakObject; - -/** - * Assumes Riak is reachable at {@link com.basho.riak.client.Hosts#RIAK_URL }. - * @see com.basho.riak.client.Hosts#RIAK_URL - */ -public class ITestDataLoad { - - final String BUCKET = "test_data_load"; - final int NUM_VALUES = 10; - final int VALUE_LENGTH = 512; - - byte data[][] = new byte[NUM_VALUES][VALUE_LENGTH]; - - @Before public void setup() { - for (int i = 0; i < 10; i++) { - new Random().nextBytes(data[i]); - } - } - - @Test public void concurrent_data_load() throws InterruptedException { - - final int NUM_THREADS = 5; - final int NUM_OBJECTS = 200; - final Thread[] threads = new Thread[NUM_THREADS]; - final AtomicInteger idx = new AtomicInteger(0); - - for (int i = 0; i < threads.length; i++) { - threads[i] = new Thread(new Runnable() { - - public void run() { - RiakClient riak = new RiakClient(RIAK_URL); - Random rnd = new Random(); - for (int i = 0; i < NUM_OBJECTS / NUM_THREADS; i++) { - String key = "data-load-" + idx.getAndIncrement(); - String value = new String(data[rnd.nextInt(NUM_VALUES)]); - RiakObject o = riak.fetch(BUCKET, key).getObject(); - if (o == null) { - o = new RiakObject(riak, BUCKET, key, value.getBytes()); - } else { - o.setValue(value); - } - Utils.assertSuccess(o.store()); - } - } - }); - threads[i].start(); - } - - for (Thread thread : threads) { - thread.join(); - } - } - - @Test public void vclock_doesnt_explode_using_single_client() { - final RiakClient riak = new RiakClient(RIAK_URL); - final String bucket = UUID.randomUUID().toString(); - final String key = "test-vclock-size"; - final RiakObject o = new RiakObject(riak, bucket, key); - final Random rnd = new Random(); - - Utils.assertSuccess(o.store()); - - String originalVclock = o.getVclock(); - - for (int i = 0; i < 15; i++) { - o.fetch(); - o.setValue(data[rnd.nextInt(NUM_VALUES)]); - Utils.assertSuccess(o.store()); - } - - assertEquals(originalVclock.length(), o.getVclock().length()); - o.delete(Utils.WRITE_3_REPLICAS()); - } -} \ No newline at end of file diff --git a/src/test/java/com/basho/riak/client/itest/ITestMapReduce.java b/src/test/java/com/basho/riak/client/itest/ITestMapReduce.java deleted file mode 100644 index f5e10bdf7..000000000 --- a/src/test/java/com/basho/riak/client/itest/ITestMapReduce.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.itest; - -import static com.basho.riak.client.Hosts.RIAK_URL; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.httpclient.HttpException; -import org.json.JSONArray; -import org.json.JSONException; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; - -import com.basho.riak.client.RiakClient; -import com.basho.riak.client.RiakLink; -import com.basho.riak.client.RiakObject; -import com.basho.riak.client.mapreduce.ErlangFunction; -import com.basho.riak.client.mapreduce.JavascriptFunction; -import com.basho.riak.client.request.MapReduceBuilder; -import com.basho.riak.client.response.MapReduceResponse; - -/** - * Exercises map/reduce features of the Riak client. - * Assumes Riak is reachable at {@link com.basho.riak.client.Hosts#RIAK_URL }. - * @see com.basho.riak.client.Hosts#RIAK_URL - */ -public class ITestMapReduce { - - public static String BUCKET_NAME = "mr_test_java"; - public static int TEST_ITEMS = 200; - - @BeforeClass - public static void setup() { - RiakClient c = new RiakClient(RIAK_URL); - for(int i = 0; i < TEST_ITEMS; i++) { - RiakObject object = new RiakObject(BUCKET_NAME, "java_" + Integer.toString(i)); - object.setContentType("text/plain"); - object.setValue(Integer.toString(i)); - if (i < TEST_ITEMS - 1) { - RiakLink link = new RiakLink(BUCKET_NAME, "java_" + Integer.toString(i + 1), "test"); - List links = new ArrayList(1); - links.add(link); - object.setLinks(links); - } - c.store(object); - } - - } - - @AfterClass - public static void teardown() { - RiakClient c = new RiakClient(RIAK_URL); - for(int i = 0; i < TEST_ITEMS; i++) { - c.delete(BUCKET_NAME, "java_" + Integer.toString(i)); - } - } - - @Test public void doLinkMapReduce() throws HttpException, IOException, JSONException { - RiakClient c = new RiakClient(RIAK_URL); - MapReduceResponse response = c.mapReduceOverBucket(BUCKET_NAME) - .link(BUCKET_NAME, "test", false) - .map(JavascriptFunction.named("Riak.mapValuesJson"), false) - .reduce(new ErlangFunction("riak_kv_mapreduce", "reduce_sort"), true) - .submit(); - assertTrue(response.isSuccess()); - JSONArray results = response.getResults(); - assertEquals(TEST_ITEMS - 1, results.length()); - } - - @Test public void doErlangMapReduce() throws HttpException, IOException, JSONException { - RiakClient c = new RiakClient(RIAK_URL); - MapReduceBuilder builder = new MapReduceBuilder(c); - builder.setBucket(BUCKET_NAME); - builder.map(new ErlangFunction("riak_kv_mapreduce", "map_object_value"), false); - builder.reduce(new ErlangFunction("riak_kv_mapreduce", "reduce_string_to_integer"), false); - builder.reduce(new ErlangFunction("riak_kv_mapreduce", "reduce_sort"), true); - MapReduceResponse response = builder.submit(); - assertTrue(response.isSuccess()); - JSONArray results = response.getResults(); - assertEquals(TEST_ITEMS, results.length()); - assertEquals(0, results.getInt(0)); - assertEquals(73, results.getInt(73)); - assertEquals(197, results.getInt(197)); - } - - @Test public void doJavascriptMapReduce() throws HttpException, IOException, JSONException { - RiakClient c = new RiakClient(RIAK_URL); - MapReduceBuilder builder = new MapReduceBuilder(c); - builder.setBucket(BUCKET_NAME); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), false); - builder.reduce(JavascriptFunction.named("Riak.reduceNumericSort"), true); - MapReduceResponse response = builder.submit(); - assertTrue(response.isSuccess()); - JSONArray results = response.getResults(); - assertEquals(TEST_ITEMS, results.length()); - assertEquals(0, results.getInt(0)); - assertEquals(73, results.getInt(73)); - assertEquals(197, results.getInt(197)); - } -} diff --git a/src/test/java/com/basho/riak/client/itest/ITestStreaming.java b/src/test/java/com/basho/riak/client/itest/ITestStreaming.java deleted file mode 100644 index 7fc63bfe8..000000000 --- a/src/test/java/com/basho/riak/client/itest/ITestStreaming.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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.itest; - -import static com.basho.riak.client.Hosts.RIAK_URL; -import static com.basho.riak.client.itest.Utils.*; -import static org.junit.Assert.*; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Random; - -import org.junit.Test; - -import com.basho.riak.client.RiakBucketInfo; -import com.basho.riak.client.RiakClient; -import com.basho.riak.client.RiakObject; -import com.basho.riak.client.response.BucketResponse; -import com.basho.riak.client.response.FetchResponse; -import com.basho.riak.client.util.ClientUtils; -import com.basho.riak.client.util.Constants; - -/** - * Assumes Riak is reachable at {@link com.basho.riak.client.Hosts#RIAK_URL }. - * @see com.basho.riak.client.Hosts#RIAK_URL - */ -public class ITestStreaming { - - @Test public void stream_keys() { - final RiakClient c = new RiakClient(RIAK_URL); - final String BUCKET = "test_stream_keys"; - final int NUM_KEYS = 20; - - // Add objects - for (int i = 0; i < NUM_KEYS; i++) { - assertSuccess(c.store(new RiakObject(BUCKET, "key" + Integer.toString(i), "v".getBytes()), - WRITE_3_REPLICAS())); - } - - // Iterate over keys in bucket - BucketResponse r = c.streamBucket(BUCKET); - assertSuccess(r); - - List keys = new ArrayList(); - for (String key : r.getBucketInfo().getKeys()) { - keys.add(key); - } - for (int i = 0; i < NUM_KEYS; i++) { - assertTrue("Should contain key" + i + ": " + keys, keys.contains("key" + i)); - } - - r.close(); - - // Clear out the objects we're testing with - for (int i = 0; i < NUM_KEYS; i++) { - assertSuccess(c.delete(BUCKET, "key" + Integer.toString(i))); - } - } - - @Test public void stream_object() throws IOException { - final RiakClient c = new RiakClient(RIAK_URL); - final String BUCKET = "test_stream_object"; - final String KEY = "key"; - final byte[] bytes = new byte[512]; - new Random().nextBytes(bytes); - - // Add object - assertSuccess(c.store(new RiakObject(BUCKET, KEY, bytes), WRITE_3_REPLICAS())); - - // Stream the object back - FetchResponse r = c.stream(BUCKET, KEY); - assertSuccess(r); - - assertTrue(r.isStreamed()); - assertNotNull(r.getStream()); - - ByteArrayOutputStream os = new ByteArrayOutputStream(512); - ClientUtils.copyStream(r.getStream(), os); - assertArrayEquals(bytes, os.toByteArray()); - - r.close(); - } - - @Test public void stream_siblings() throws IOException { - final RiakClient c = new RiakClient(RIAK_URL); - final String BUCKET = "test_stream_siblings"; - final String KEY = "key"; - final byte[][] bytes = new byte[][] { new byte[512], new byte[512], new byte[512] }; - new Random().nextBytes(bytes[0]); - new Random().nextBytes(bytes[1]); - new Random().nextBytes(bytes[2]); - - // Enable storing siblings in this bucket - BucketResponse bucketresp = c.listBucket(BUCKET); - assertSuccess(bucketresp); - assertTrue(bucketresp.hasBucketInfo()); - RiakBucketInfo bucketInfo = bucketresp.getBucketInfo(); - bucketInfo.setAllowMult(true); - assertSuccess(c.setBucketSchema(BUCKET, bucketInfo)); - - // Clean out any previous entry - c.delete(BUCKET, KEY, WRITE_3_REPLICAS()); - - // Add conflicting objects - c.setClientId("foo-"); - assertSuccess(c.store(new RiakObject(BUCKET, KEY, bytes[0]), WRITE_3_REPLICAS().setQueryParam(Constants.QP_RETURN_BODY, "false"))); - - c.setClientId("bar-"); - assertSuccess(c.store(new RiakObject(BUCKET, KEY, bytes[1]), WRITE_3_REPLICAS().setQueryParam(Constants.QP_RETURN_BODY, "false"))); - - c.setClientId("baz-"); - assertSuccess(c.store(new RiakObject(BUCKET, KEY, bytes[2]), WRITE_3_REPLICAS().setQueryParam(Constants.QP_RETURN_BODY, "false"))); - - // Stream the object back - FetchResponse r = c.stream(BUCKET, KEY); - assertEquals(300, r.getStatusCode()); - - assertTrue(r.isStreamed()); - assertNotNull(r.getStream()); - - assertTrue(r.hasSiblings()); - for (Iterator i = r.getSiblings().iterator(); i.hasNext(); ) { - RiakObject o = i.next(); - ByteArrayOutputStream os = new ByteArrayOutputStream(512); - ClientUtils.copyStream(o.getValueStream(), os); - byte[] out = os.toByteArray(); - boolean found = false; - for (int j = 0; j < bytes.length; j++) { - found = true; - for (int k = 0; k < bytes[j].length; k++) { - if (bytes[j][k] != out[k]) { - found = false; - break; - } - } - if (found) { - break; - } - } - assertTrue("Sibling value does not match any expected input", found); - } - - r.close(); - } -} diff --git a/src/test/java/com/basho/riak/client/itest/ITestWalk.java b/src/test/java/com/basho/riak/client/itest/ITestWalk.java deleted file mode 100644 index 6be536576..000000000 --- a/src/test/java/com/basho/riak/client/itest/ITestWalk.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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.itest; - -import static com.basho.riak.client.Hosts.RIAK_URL; -import static com.basho.riak.client.itest.Utils.*; -import static org.junit.Assert.*; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Test; - -import com.basho.riak.client.RiakClient; -import com.basho.riak.client.RiakLink; -import com.basho.riak.client.RiakObject; -import com.basho.riak.client.response.WalkResponse; - -/** - * Assumes Riak is reachable at {@link com.basho.riak.client.Hosts#RIAK_URL }. - * @see com.basho.riak.client.Hosts#RIAK_URL - */ -public class ITestWalk { - - @Test - public void test_walk() { - final RiakClient c = new RiakClient(RIAK_URL); - final String BUCKET = "test_walk"; - final String ROOT = "root"; - final String LEAF1 = "leaf1"; - final String LEAF2 = "leaf2"; - final String EXCLUDED_LEAF = "excluded_leaf"; - final byte[] INCLUDED_VALUE = "included".getBytes(); - final byte[] EXCLUDED_VALUE = "excluded".getBytes(); - final String TAG_INCLUDE = "tag_include"; - final String TAG_EXCLUDE = "tag_exclude"; - - // Clear out the objects we're testing with - assertSuccess(c.delete(BUCKET, ROOT)); - assertSuccess(c.delete(BUCKET, LEAF1)); - assertSuccess(c.delete(BUCKET, LEAF2)); - assertSuccess(c.delete(BUCKET, EXCLUDED_LEAF)); - - // Add a few objects - RiakObject leaf1 = new RiakObject(BUCKET, LEAF1, INCLUDED_VALUE); - RiakObject leaf2 = new RiakObject(BUCKET, LEAF2, INCLUDED_VALUE); - RiakObject excludedLeaf = new RiakObject(BUCKET, EXCLUDED_LEAF, EXCLUDED_VALUE); - RiakObject root = new RiakObject(c, BUCKET, ROOT) - .addLink(new RiakLink(BUCKET, LEAF1, TAG_INCLUDE)) - .addLink(new RiakLink(BUCKET, LEAF2, TAG_INCLUDE)) - .addLink(new RiakLink(BUCKET, EXCLUDED_LEAF, TAG_EXCLUDE)); - assertSuccess(c.store(root, WRITE_3_REPLICAS())); - assertSuccess(c.store(leaf1, WRITE_3_REPLICAS())); - assertSuccess(c.store(leaf2, WRITE_3_REPLICAS())); - assertSuccess(c.store(excludedLeaf, WRITE_3_REPLICAS())); - - // Perform walk - WalkResponse walkresp = root.walk(BUCKET, TAG_INCLUDE).run(); - assertSuccess(walkresp); - assertTrue(walkresp.hasSteps()); - assertEquals(1, walkresp.getSteps().size()); - assertEquals(2, walkresp.getSteps().get(0).size()); - - // Verify expected only linked to objects are returned - List> steps = walkresp.getSteps(); - List keys = new ArrayList(); - for (List step : steps) { - for (RiakObject object : step) { - keys.add(object.getKey()); - assertArrayEquals(INCLUDED_VALUE, object.getValueAsBytes()); - } - } - assertTrue(keys.contains(LEAF1)); - assertTrue(keys.contains(LEAF2)); - } -} diff --git a/src/test/java/com/basho/riak/client/itest/Utils.java b/src/test/java/com/basho/riak/client/itest/Utils.java deleted file mode 100644 index 6df56b107..000000000 --- a/src/test/java/com/basho/riak/client/itest/Utils.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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.itest; - -import static org.junit.Assert.*; - -import org.apache.commons.httpclient.URIException; - -import com.basho.riak.client.request.RequestMeta; -import com.basho.riak.client.response.HttpResponse; - -public class Utils { - - public static RequestMeta WRITE_3_REPLICAS() { return RequestMeta.writeParams(3, 3); } - - public static void assertSuccess(HttpResponse response) { - if (!response.isSuccess()) { - StringBuilder msg = new StringBuilder("Failed "); - msg.append(response.getHttpMethod().getName()).append(" "); - try { - msg.append(response.getHttpMethod().getURI().toString()); - } catch (URIException e) { - msg.append(response.getHttpMethod().getPath()); - } - msg.append(" -- ") - .append(response.getHttpMethod().getStatusLine()).append("; ") - .append("Response headers: ").append(response.getHttpHeaders().toString()).append("; ") - .append("Response body: ").append(new String(response.getBody())); - fail(msg.toString()); - } - } - -} diff --git a/src/test/java/com/basho/riak/client/mapreduce/TestMapReduceBuilder.java b/src/test/java/com/basho/riak/client/mapreduce/TestMapReduceBuilder.java deleted file mode 100644 index 3d4c53aab..000000000 --- a/src/test/java/com/basho/riak/client/mapreduce/TestMapReduceBuilder.java +++ /dev/null @@ -1,503 +0,0 @@ -/* - * 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.mapreduce; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.junit.Test; - -import com.basho.riak.client.RiakObject; -import com.basho.riak.client.mapreduce.filter.BetweenFilter; -import com.basho.riak.client.mapreduce.filter.EndsWithFilter; -import com.basho.riak.client.mapreduce.filter.EqualToFilter; -import com.basho.riak.client.mapreduce.filter.FloatToStringFilter; -import com.basho.riak.client.mapreduce.filter.GreaterThanFilter; -import com.basho.riak.client.mapreduce.filter.GreaterThanOrEqualFilter; -import com.basho.riak.client.mapreduce.filter.IntToStringFilter; -import com.basho.riak.client.mapreduce.filter.LessThanFilter; -import com.basho.riak.client.mapreduce.filter.LessThanOrEqualFilter; -import com.basho.riak.client.mapreduce.filter.LogicalAndFilter; -import com.basho.riak.client.mapreduce.filter.LogicalFilterGroup; -import com.basho.riak.client.mapreduce.filter.LogicalNotFilter; -import com.basho.riak.client.mapreduce.filter.LogicalOrFilter; -import com.basho.riak.client.mapreduce.filter.MatchFilter; -import com.basho.riak.client.mapreduce.filter.NotEqualToFilter; -import com.basho.riak.client.mapreduce.filter.SetMemberFilter; -import com.basho.riak.client.mapreduce.filter.SimilarToFilter; -import com.basho.riak.client.mapreduce.filter.StartsWithFilter; -import com.basho.riak.client.mapreduce.filter.StringToFloatFilter; -import com.basho.riak.client.mapreduce.filter.StringToIntFilter; -import com.basho.riak.client.mapreduce.filter.ToLowerFilter; -import com.basho.riak.client.mapreduce.filter.ToUpperFilter; -import com.basho.riak.client.mapreduce.filter.TokenizeFilter; -import com.basho.riak.client.mapreduce.filter.UrlDecodeFilter; -import com.basho.riak.client.request.MapReduceBuilder; -import com.basho.riak.test.util.JSONEquals; - -public class TestMapReduceBuilder { - - @Test public void canStoreBucket() { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("foo"); - assertEquals(builder.getBucket(), "foo"); - } - - @Test public void canStoreObjects() { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.addRiakObject("foo", "bar"); - Map> objects = builder.getRiakObjects(); - assertEquals(objects.get("foo").size(), 1); - assertTrue(objects.get("foo").contains("bar")); - // Verify duplicates are not added - builder.addRiakObject("foo", "bar"); - assertEquals(1, builder.getRiakObjects().get("foo").size()); - } - - @Test public void canRemoveObjects() { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.addRiakObject("foo", "bar"); - builder.addRiakObject("foo", "baz"); - assertEquals(2, builder.getRiakObjects().get("foo").size()); - builder.removeRiakObject("foo", "bar"); - assertEquals(1, builder.getRiakObjects().get("foo").size()); - } - - @Test public void nullParamClearsObject() { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.addRiakObject("foo", "bar"); - builder.setRiakObjects((Map>) null); - assertEquals(0, builder.getRiakObjects().size()); - - builder.addRiakObject("foo", "bar"); - builder.setRiakObjects((Collection) null); - assertEquals(0, builder.getRiakObjects().size()); - } - - @Test public void extractsRiakObjectInfo() { - MapReduceBuilder builder = new MapReduceBuilder(); - List riakObjects = Arrays.asList(new RiakObject[] { new RiakObject("foo", "bar"), - new RiakObject("foo", "baz")}); - builder.setRiakObjects(riakObjects); - - Map> objects = builder.getRiakObjects(); - assertEquals(objects.get("foo").size(), 2); - assertTrue(objects.get("foo").contains("bar")); - assertTrue(objects.get("foo").contains("baz")); - } - - @Test(expected=IllegalStateException.class) - public void canUseOnlyObjects() { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.addRiakObject("foo", "bar"); - builder.setBucket("wubba"); - } - - @Test(expected=IllegalStateException.class) - public void canUseOnlyBucket() { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.addRiakObject("foo", "bar"); - } - - @Test public void canBuildJSMapOnlyJobWithBucket() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), true); - - JSONObject expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":" + - "[{\"map\":{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":true}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - - } - - @Test public void canBuildJSMapReduceJobWithBucket() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(JavascriptFunction.anon("function(v) { return [v]; }"), false); - builder.reduce(JavascriptFunction.named("Riak.reduceMin"), true); - - JSONObject expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"map\":{\"source\":" + - "\"function(v) { return [v]; }\",\"language\":\"javascript\",\"keep\":false}}," + - "{\"reduce\":{\"name\":\"Riak.reduceMin\",\"language\":\"javascript\",\"keep\":true}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - - @Test public void canBuildErlangMapOnlyJobWithBucket() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(new ErlangFunction("foo", "bar"), true); - - JSONObject expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"map\":{\"module\":\"foo\"," + - "\"language\":\"erlang\",\"keep\":true,\"function\":\"bar\"}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @Test public void canBuildErlangMapReduceJobWithBucket() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(new ErlangFunction("foo", "bar"), false); - builder.reduce(new ErlangFunction("baz", "quux"), true); - - JSONObject expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"map\":{\"module\":\"foo\"," + - "\"language\":\"erlang\",\"keep\":false,\"function\":\"bar\"}}," + - "{\"reduce\":{\"module\":\"baz\",\"language\":\"erlang\",\"keep\":true,\"function\":\"quux\"}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @Test public void canBuildJSMapOnlyJobWithObjects() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.addRiakObject("first", "key1"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), true); - - - JSONObject expected = new JSONObject(); - JSONArray inputs = new JSONArray(); - inputs.put(new String[] {"first", "key1"}); - expected.put("inputs", inputs); - JSONArray query = new JSONArray("[{\"map\":{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":true}}]"); - expected.put("query", query); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @Test public void canBuildJSMapReduceJobWithObjects() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.addRiakObject("first", "key2"); - builder.map(JavascriptFunction.anon("function(v) { return [v]; }"), false); - builder.reduce(JavascriptFunction.named("Riak.reduceMin"), true); - - - JSONObject expected = new JSONObject(); - JSONArray inputs = new JSONArray(); - inputs.put(new String[] {"first", "key2"}); - expected.put("inputs", inputs); - JSONArray query = new JSONArray("[{\"map\":{\"source\":" + - "\"function(v) { return [v]; }\",\"language\":\"javascript\",\"keep\":false}}," + - "{\"reduce\":{\"name\":\"Riak.reduceMin\",\"language\":\"javascript\",\"keep\":true}}]"); - expected.put("query", query); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - - @Test public void canBuildErlangMapOnlyJobWithObjects() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.addRiakObject("first", "key1"); - builder.addRiakObject("second", "key1"); - builder.map(new ErlangFunction("foo", "bar"), true); - - JSONObject expected = new JSONObject(); - JSONArray inputs = new JSONArray(); - inputs.put(new String[] {"first", "key1"}); - inputs.put(new String[] {"second", "key1"}); - expected.put("inputs", inputs); - JSONArray query = new JSONArray("[{\"map\":{\"module\":\"foo\",\"language\":\"erlang\",\"keep\":true,\"function\":\"bar\"}}]"); - expected.put("query", query); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @Test public void canBuildErlangMapReduceJobWitObjects() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(new ErlangFunction("foo", "bar"), false); - builder.reduce(new ErlangFunction("baz", "quux"), true); - - JSONObject expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"map\":{\"module\":\"foo\"," + - "\"language\":\"erlang\",\"keep\":false,\"function\":\"bar\"}}," + - "{\"reduce\":{\"module\":\"baz\",\"language\":\"erlang\",\"keep\":true,\"function\":\"quux\"}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @Test public void canBuildLinkMapReduceJob() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.link("foo", false); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), true); - - JSONObject expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"link\":{\"bucket\":\"foo\",\"keep\":false}}," + - "{\"map\":{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":true}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @SuppressWarnings("unchecked") @Test public void canBuildMapJobWithPhaseArg() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), "123", true); - - JSONObject expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"map\":{\"arg\":\"123\"," + - "\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":true}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - - builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), 123, true); - - expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"map\":{\"arg\":123," + - "\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":true}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - - builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - List args = new ArrayList(2); - args.add(123); - args.add("testing"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), args, true); - - expected = new JSONObject("{\"inputs\":\"wubba\"}"); - - JSONObject mapSpec = new JSONObject("{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":true}"); - mapSpec.put("arg", (Object)Arrays.asList(123, "testing")); - JSONObject map = new JSONObject(); - map.put("map", mapSpec); - JSONArray query = new JSONArray(); - query.put(map); - - expected.put("query", query); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @SuppressWarnings("unchecked") @Test public void canBuildMapReduceJobWithPhaseArg() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), false); - builder.reduce(JavascriptFunction.named("Riak.reduceSort"), "testing", true); - - JSONObject expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"map\":{\"name\":\"Riak.mapValuesJson\"," + - "\"language\":\"javascript\",\"keep\":false}},{\"reduce\":{\"arg\":\"testing\"," + - "\"name\":\"Riak.reduceSort\",\"language\":\"javascript\",\"keep\":true}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - - builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), false); - builder.reduce(JavascriptFunction.named("Riak.reduceSort"), 123, true); - - expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"map\":{\"name\":\"Riak.mapValuesJson\"," + - "\"language\":\"javascript\",\"keep\":false}},{\"reduce\":{\"arg\":123," + - "\"name\":\"Riak.reduceSort\",\"language\":\"javascript\",\"keep\":true}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - - - List args = new ArrayList(2); - args.add(123); - args.add("testing"); - builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), false); - builder.reduce(new ErlangFunction("riak_kv_mapreduce", "reduce_sort"), args, true); - - expected = new JSONObject("{\"inputs\":\"wubba\"}"); - JSONObject map = new JSONObject("{\"map\":{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":false}}"); - - JSONObject reduceSpec = new JSONObject("{\"module\":\"riak_kv_mapreduce\",\"language\":\"erlang\",\"keep\":true,\"function\":\"reduce_sort\"}"); - reduceSpec.put("arg", (Object)Arrays.asList(123, "testing")); - JSONObject reduce = new JSONObject(); - reduce.put("reduce", reduceSpec); - - JSONArray query = new JSONArray(); - query.put(map); - query.put(reduce); - expected.put("query", query); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - /* - * This test should always include all available key filters to ensure they render correctly. This also - * implicitly verifies overall key_filter construction. - */ - @Test public void canBuildMapJobWithKeyFilters() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), false); - List setMembers = new ArrayList(); - setMembers.add("member1"); - setMembers.add("member2"); - setMembers.add("member3"); - builder.keyFilter(new BetweenFilter(22, 93)) - .keyFilter(new EndsWithFilter("apples")) - .keyFilter(new EqualToFilter(36)) - .keyFilter(new FloatToStringFilter()) - .keyFilter(new GreaterThanFilter(72)) - .keyFilter(new GreaterThanOrEqualFilter(72)) - .keyFilter(new IntToStringFilter()) - .keyFilter(new LessThanFilter(42)) - .keyFilter(new LessThanOrEqualFilter(43)) - .keyFilter(new MatchFilter("fun.*")) - .keyFilter(new NotEqualToFilter(90)) - .keyFilter(new SetMemberFilter(setMembers)) - .keyFilter(new SimilarToFilter("valeu", 2)) - .keyFilter(new StartsWithFilter("super")) - .keyFilter(new StringToFloatFilter()) - .keyFilter(new StringToIntFilter()) - .keyFilter(new TokenizeFilter("-", 3)) - .keyFilter(new ToLowerFilter()) - .keyFilter(new ToUpperFilter()) - .keyFilter(new UrlDecodeFilter()); - - JSONObject expected = new JSONObject("{\"inputs\":{\"key_filters\":[[\"between\",22,93],[\"ends_with\",\"apples\"],[\"eq\",36],[\"float_to_string\"],[\"greater_than\",72],[\"greater_than_eq\",72],[\"int_to_string\"],[\"less_than\",42],[\"less_than_eq\",43],[\"matches\",\"fun.*\"],[\"neq\",90],[\"set_member\",\"member1\",\"member2\",\"member3\"],[\"similar_to\",\"valeu\",2],[\"starts_with\",\"super\"],[\"string_to_float\"],[\"string_to_int\"],[\"tokenize\",\"-\",3],[\"to_lower\"],[\"to_upper\"],[\"urldecode\"]],\"bucket\":\"wubba\"},\"query\":[{\"map\":{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":false}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - /* - * Test logical and, or, not - */ - - @Test public void canBuildMapJobWithBuilderLogicalOrKeyFilters() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba1"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), false); - builder.keyFilter(new IntToStringFilter()) - .keyFilter(new LogicalOrFilter().add(new BetweenFilter("apples", "bananas")) - .add(new GreaterThanFilter(36))) - .keyFilter(new BetweenFilter(12.5, 36.8)); - - JSONObject expected = new JSONObject("{\"inputs\":{\"key_filters\":[[\"int_to_string\"],[\"or\",[\"between\",\"apples\",\"bananas\"],[\"greater_than\",36]],[\"between\",12.5,36.8]],\"bucket\":\"wubba1\"},\"query\":[{\"map\":{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":false}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @Test public void canBuildMapJobWithVarArgLogicalOrKeyFilters() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba1"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), false); - builder.keyFilter(new IntToStringFilter()) - .keyFilter(new LogicalOrFilter( - new BetweenFilter("apples", "bananas"), new GreaterThanFilter(36))) - .keyFilter(new BetweenFilter(12.5, 36.8)); - - JSONObject expected = new JSONObject("{\"inputs\":{\"key_filters\":[[\"int_to_string\"],[\"or\",[\"between\",\"apples\",\"bananas\"],[\"greater_than\",36]],[\"between\",12.5,36.8]],\"bucket\":\"wubba1\"},\"query\":[{\"map\":{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":false}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @Test public void canBuildMapJobWithBuilderLogicalAndKeyFilters() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba1"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), false); - builder.keyFilter(new IntToStringFilter()) - .keyFilter(new LogicalAndFilter().add(new BetweenFilter("apples", "bananas")) - .add(new GreaterThanFilter(36))) - .keyFilter(new BetweenFilter(12.5, 36.8)); - - JSONObject expected = new JSONObject("{\"inputs\":{\"key_filters\":[[\"int_to_string\"],[\"and\",[\"between\",\"apples\",\"bananas\"],[\"greater_than\",36]],[\"between\",12.5,36.8]],\"bucket\":\"wubba1\"},\"query\":[{\"map\":{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":false}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @Test public void canBuildMapJobWithVarArgLogicalAndKeyFilters() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba1"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), false); - builder.keyFilter(new IntToStringFilter()) - .keyFilter(new LogicalAndFilter( - new BetweenFilter("apples", "bananas"), new GreaterThanFilter(36))) - .keyFilter(new BetweenFilter(12.5, 36.8)); - - JSONObject expected = new JSONObject("{\"inputs\":{\"key_filters\":[[\"int_to_string\"],[\"and\",[\"between\",\"apples\",\"bananas\"],[\"greater_than\",36]],[\"between\",12.5,36.8]],\"bucket\":\"wubba1\"},\"query\":[{\"map\":{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":false}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @Test public void canBuildMapJobWithBuilderLogicalNotKeyFilters() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba1"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), false); - builder.keyFilter(new IntToStringFilter()) - .keyFilter(new LogicalNotFilter().add(new BetweenFilter("apples", "bananas")) - .add(new GreaterThanFilter(36))) - .keyFilter(new BetweenFilter(12.5, 36.8)); - - JSONObject expected = new JSONObject("{\"inputs\":{\"key_filters\":[[\"int_to_string\"],[\"not\",[\"between\",\"apples\",\"bananas\"],[\"greater_than\",36]],[\"between\",12.5,36.8]],\"bucket\":\"wubba1\"},\"query\":[{\"map\":{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":false}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - /* - * Test embedded LogicalFilterGroup (var args variation) - */ - - @Test public void canBuildMapJobWithVarArgLogicalNotKeyFilters() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba1"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), false); - builder.keyFilter(new IntToStringFilter(), - new LogicalNotFilter(new BetweenFilter("apples", "bananas"), - new GreaterThanFilter(36)), - new BetweenFilter(12.5, 36.8)); - - JSONObject expected = new JSONObject("{\"inputs\":{\"key_filters\":[[\"int_to_string\"],[\"not\",[\"between\",\"apples\",\"bananas\"],[\"greater_than\",36]],[\"between\",12.5,36.8]],\"bucket\":\"wubba1\"},\"query\":[{\"map\":{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":false}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - /* - * Test nested logical and, or, not - */ - - @Test public void canBuildMapJobWithCompoundLogicalKeyFilters() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba1"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), false); - builder.keyFilter(new LogicalOrFilter( - new LogicalOrFilter( - new LogicalFilterGroup( - new StringToIntFilter(), - new GreaterThanOrEqualFilter(98)), - new LogicalFilterGroup( - new StringToIntFilter(), - new LessThanFilter(2))), - new LogicalAndFilter( - new LogicalFilterGroup( - new StringToIntFilter(), - new BetweenFilter(18, 36)), - new LogicalFilterGroup( - new StringToIntFilter(), - new GreaterThanFilter(0))), - new LogicalNotFilter( - new LogicalFilterGroup( - new StringToIntFilter(), - new EqualToFilter(12))))); - - JSONObject expected = new JSONObject("{\"inputs\":{\"key_filters\":[[\"or\",[\"or\",[[\"string_to_int\"],[\"greater_than_eq\",98]],[[\"string_to_int\"],[\"less_than\",2]]],[\"and\",[[\"string_to_int\"],[\"between\",18,36]],[[\"string_to_int\"],[\"greater_than\",0]]],[\"not\",[[\"string_to_int\"],[\"eq\",12]]]]],\"bucket\":\"wubba1\"},\"query\":[{\"map\":{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":false}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - -} \ No newline at end of file diff --git a/src/test/java/com/basho/riak/client/mapreduce/TestMapReduceFunctions.java b/src/test/java/com/basho/riak/client/mapreduce/TestMapReduceFunctions.java deleted file mode 100644 index 455e46d08..000000000 --- a/src/test/java/com/basho/riak/client/mapreduce/TestMapReduceFunctions.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.mapreduce; - -import static org.junit.Assert.assertEquals; - -import org.json.JSONException; -import org.json.JSONObject; -import org.junit.Test; - -public class TestMapReduceFunctions { - - @Test public void erlangFunction_generatesCorrectJson() throws JSONException { - ErlangFunction f = new ErlangFunction("testing", "doit"); - JSONObject json = f.toJson(); - - assertEquals("testing", json.get("module")); - assertEquals("erlang", json.get("language")); - assertEquals("doit", json.get("function")); - //assertEquals("{\"module\":\"testing\",\"language\":\"erlang\",\"function\":\"doit\"}", json); - } - - @Test public void namedJSFunction_generatesCorrectJson() throws JSONException { - JavascriptFunction f = JavascriptFunction.named("Riak.mapValuesJson"); - String json = f.toJson().toString(); - assertEquals("{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\"}", json); - } - - @Test public void anonJSFunction_generatesCorrectJson() throws JSONException { - JavascriptFunction f = JavascriptFunction.anon("function(v) { return [v]; }"); - String json = f.toJson().toString(); - assertEquals("{\"source\":\"function(v) { return [v]; }\",\"language\":\"javascript\"}", json); - } - - @Test public void anonJSFunction_escapesQuotes() throws JSONException { - JavascriptFunction f = JavascriptFunction.anon("function(v) { return [{\"value\": v}]; }"); - String json = f.toJson().toString(); - assertEquals("{\"source\":\"function(v) { return [{\\\"value\\\": v}]; }\",\"language\":\"javascript\"}", json); - } - -} diff --git a/src/test/java/com/basho/riak/client/plain/TestConvertToCheckedExceptions.java b/src/test/java/com/basho/riak/client/plain/TestConvertToCheckedExceptions.java deleted file mode 100644 index 71744eeb1..000000000 --- a/src/test/java/com/basho/riak/client/plain/TestConvertToCheckedExceptions.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.plain; - -import org.junit.Test; - -import com.basho.riak.client.response.RiakIORuntimeException; -import com.basho.riak.client.response.RiakResponseRuntimeException; - -public class TestConvertToCheckedExceptions { - - ConvertToCheckedExceptions impl = new ConvertToCheckedExceptions(); - - @Test(expected=RiakIOException.class) public void translates_io_runtime_exception_to_checked() throws RiakIOException { - impl.handle(new RiakIORuntimeException()); - } - @Test(expected=RiakResponseException.class) public void translates_response_runtime_exception_to_checked() throws RiakResponseException { - impl.handle(new RiakResponseRuntimeException(null)); - } -} \ No newline at end of file diff --git a/src/test/java/com/basho/riak/client/plain/TestPlainClient.java b/src/test/java/com/basho/riak/client/plain/TestPlainClient.java deleted file mode 100644 index 56bad1aea..000000000 --- a/src/test/java/com/basho/riak/client/plain/TestPlainClient.java +++ /dev/null @@ -1,370 +0,0 @@ -/* - * 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.plain; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import static org.mockito.Mockito.*; -import static org.junit.Assert.*; - -import com.basho.riak.client.RiakBucketInfo; -import com.basho.riak.client.RiakClient; -import com.basho.riak.client.RiakObject; -import com.basho.riak.client.request.RequestMeta; -import com.basho.riak.client.response.BucketResponse; -import com.basho.riak.client.response.FetchResponse; -import com.basho.riak.client.response.HttpResponse; -import com.basho.riak.client.response.StoreResponse; -import com.basho.riak.client.response.StreamHandler; -import com.basho.riak.client.response.WalkResponse; - -public class TestPlainClient { - - // The basic status codes in HTTP 1.1 - private static final int HTTP_STATUSES[] = {100, 101, 199, 200, 201, 202, 203, 204, 205, 206, 299, 300, 301, 302, 303, 304, 305, 306, 307, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 499, 500, 501, 502, 503, 504, 505, 599}; - - final String bucket = "bucket"; - final String key = "key"; - final String walkSpec = "walkSpec"; - - @Mock RiakBucketInfo bucketInfo; - @Mock RiakClient mockRiakClient; - @Mock RiakObject object; - @Mock RequestMeta meta; - @Mock StreamHandler handler; - PlainClient impl; - - @Before public void setup() { - MockitoAnnotations.initMocks(this); - impl = new PlainClient(mockRiakClient); - } - - @Test public void installs_convert_to_checked_exception_handler() { - verify(mockRiakClient).setExceptionHandler(isA(ConvertToCheckedExceptions.class)); - } - - @Test public void methods_defer_to_impl() throws RiakIOException, RiakResponseException, IOException { - final HttpResponse mockHttpResponse = mock(HttpResponse.class); - when(mockRiakClient.setBucketSchema(bucket, bucketInfo, meta)).thenReturn(mockHttpResponse); - when(mockHttpResponse.getStatusCode()).thenReturn(204); - impl.setBucketSchema(bucket, bucketInfo, meta); - verify(mockRiakClient).setBucketSchema(bucket, bucketInfo, meta); - - final BucketResponse mockBucketResponse = mock(BucketResponse.class); - when(mockRiakClient.listBucket(bucket, meta)).thenReturn(mockBucketResponse); - when(mockBucketResponse.getStatusCode()).thenReturn(200); - impl.listBucket(bucket, meta); - verify(mockRiakClient).listBucket(bucket, meta); - - final StoreResponse mockStoreResponse = mock(StoreResponse.class); - when(mockRiakClient.store(object, meta)).thenReturn(mockStoreResponse); - when(mockStoreResponse.getStatusCode()).thenReturn(200); - impl.store(object, meta); - verify(mockRiakClient).store(object, meta); - - final FetchResponse mockFetchResponse = mock(FetchResponse.class); - when(mockRiakClient.fetchMeta(bucket, key, meta)).thenReturn(mockFetchResponse); - when(mockFetchResponse.getStatusCode()).thenReturn(200); - when(mockFetchResponse.hasObject()).thenReturn(true); - impl.fetchMeta(bucket, key, meta); - verify(mockRiakClient).fetchMeta(bucket, key, meta); - - when(mockRiakClient.fetch(bucket, key, meta)).thenReturn(mockFetchResponse); - when(mockFetchResponse.getStatusCode()).thenReturn(200); - when(mockFetchResponse.hasObject()).thenReturn(true); - impl.fetch(bucket, key, meta); - verify(mockRiakClient).fetch(bucket, key, meta); - - reset(mockRiakClient); - when(mockRiakClient.fetch(bucket, key, meta)).thenReturn(mockFetchResponse); - when(mockFetchResponse.getStatusCode()).thenReturn(200); - when(mockFetchResponse.hasObject()).thenReturn(true); - impl.fetchAll(bucket, key, meta); - verify(mockRiakClient).fetch(bucket, key, meta); - - impl.stream(bucket, key, handler, meta); - verify(mockRiakClient).stream(bucket, key, handler, meta); - - when(mockRiakClient.delete(bucket, key, meta)).thenReturn(mockHttpResponse); - when(mockHttpResponse.getStatusCode()).thenReturn(204); - impl.delete(bucket, key, meta); - verify(mockRiakClient).delete(bucket, key, meta); - - final WalkResponse mockWalkResponse = mock(WalkResponse.class); - when(mockRiakClient.walk(bucket, key, walkSpec, meta)).thenReturn(mockWalkResponse); - when(mockWalkResponse.getStatusCode()).thenReturn(200); - when(mockWalkResponse.hasSteps()).thenReturn(true); - impl.walk(bucket, key, walkSpec, meta); - verify(mockRiakClient).walk(bucket, key, walkSpec, meta); - } - - @Test public void setBucketSchema_throws_except_for_204(){ - HttpResponse mockResponse = mock(HttpResponse.class); - - for (int i : HTTP_STATUSES) { - when(mockResponse.getStatusCode()).thenReturn(i); - when(mockRiakClient.setBucketSchema(bucket, bucketInfo, meta)).thenReturn(mockResponse); - boolean threw = true; - try { - impl.setBucketSchema(bucket, bucketInfo, meta); - threw = false; - } catch (RiakIOException e) { - } catch (RiakResponseException e) { - } - assertTrue("Wrong behavior for status " + i, throwsForAllStatusesExcept(new int[] { 204 }, i, threw)); - } - } - - @Test public void listBucket_throws_except_for_200() { - BucketResponse mockResponse = mock(BucketResponse.class); - for (int i : HTTP_STATUSES) { - when(mockResponse.getStatusCode()).thenReturn(i); - when(mockRiakClient.listBucket(bucket, meta)).thenReturn(mockResponse); - boolean threw = true; - try { - impl.listBucket(bucket, meta); - threw = false; - } catch (RiakIOException e) { - } catch (RiakResponseException e) { - } - assertTrue("Wrong behavior for status " + i, throwsForAllStatusesExcept(new int[] { 200 }, i, threw)); - } - } - - @Test public void store_throws_except_for_200_and_204() { - StoreResponse mockResponse = mock(StoreResponse.class); - for (int i : HTTP_STATUSES) { - when(mockResponse.getStatusCode()).thenReturn(i); - when(mockRiakClient.store(object, meta)).thenReturn(mockResponse); - boolean threw = true; - try { - impl.store(object, meta); - threw = false; - } catch (RiakIOException e) { - } catch (RiakResponseException e) { - } - assertTrue("Wrong behavior for status " + i, throwsForAllStatusesExcept(new int[] { 200, 204 }, i, threw)); - } - } - - @Test public void fetchMeta_throws_except_for_200_304_or_404() { - FetchResponse mockResponse = mock(FetchResponse.class); - for (int i : HTTP_STATUSES) { - when(mockResponse.getStatusCode()).thenReturn(i); - when(mockResponse.hasObject()).thenReturn(true); - when(mockRiakClient.fetchMeta(bucket, key, meta)).thenReturn(mockResponse); - boolean threw = true; - try { - impl.fetchMeta(bucket, key, meta); - threw = false; - } catch (RiakIOException e) { - } catch (RiakResponseException e) { - } - assertTrue("Wrong behavior for status " + i, throwsForAllStatusesExcept(new int[] { 200, 304, 404 }, i, threw)); - } - } - - @Test public void fetchMeta_returns_null_on_404() throws RiakIOException, RiakResponseException { - final FetchResponse mockResponse = mock(FetchResponse.class); - - when(mockResponse.getStatusCode()).thenReturn(404); - when(mockRiakClient.fetchMeta(bucket, key, meta)).thenReturn(mockResponse); - - assertNull(impl.fetchMeta(bucket, key, meta)); - } - - @Test(expected=RiakResponseException.class) public void fetchMeta_throws_if_metadata_not_returned() throws RiakIOException, RiakResponseException { - final FetchResponse mockResponse = mock(FetchResponse.class); - - when(mockResponse.getStatusCode()).thenReturn(200); - when(mockResponse.hasObject()).thenReturn(false); - when(mockRiakClient.fetchMeta(bucket, key, meta)).thenReturn(mockResponse); - - impl.fetchMeta(bucket, key, meta); - } - - @Test public void fetch_throws_except_for_200_304_or_404() { - FetchResponse mockResponse = mock(FetchResponse.class); - for (int i : HTTP_STATUSES) { - when(mockResponse.getStatusCode()).thenReturn(i); - when(mockResponse.hasObject()).thenReturn(true); - when(mockRiakClient.fetch(bucket, key, meta)).thenReturn(mockResponse); - boolean threw = true; - try { - impl.fetch(bucket, key, meta); - threw = false; - } catch (RiakIOException e) { - } catch (RiakResponseException e) { - } - assertTrue("Wrong behavior for status " + i, throwsForAllStatusesExcept(new int[] { 200, 304, 404 }, i, threw)); - } - } - - @Test public void fetch_returns_null_on_404() throws RiakIOException, RiakResponseException { - final FetchResponse mockResponse = mock(FetchResponse.class); - - when(mockResponse.getStatusCode()).thenReturn(404); - when(mockRiakClient.fetch(bucket, key, meta)).thenReturn(mockResponse); - - assertNull(impl.fetch(bucket, key, meta)); - } - - @Test(expected=RiakResponseException.class) public void fetch_throws_if_object_not_returned() throws RiakIOException, RiakResponseException { - final FetchResponse mockResponse = mock(FetchResponse.class); - - when(mockResponse.getStatusCode()).thenReturn(200); - when(mockResponse.hasObject()).thenReturn(false); - when(mockRiakClient.fetch(bucket, key, meta)).thenReturn(mockResponse); - - impl.fetch(bucket, key, meta); - } - - @Test public void fetchAll_throws_except_for_200_304_or_404() { - FetchResponse mockResponse = mock(FetchResponse.class); - for (int i : HTTP_STATUSES) { - when(mockResponse.getStatusCode()).thenReturn(i); - when(mockResponse.hasObject()).thenReturn(true); - when(mockRiakClient.fetch(bucket, key, meta)).thenReturn(mockResponse); - boolean threw = true; - try { - impl.fetchAll(bucket, key, meta); - threw = false; - } catch (RiakIOException e) { - } catch (RiakResponseException e) { - } - assertTrue("Wrong behavior for status " + i, throwsForAllStatusesExcept(new int[] { 200, 304, 404 }, i, threw)); - } - } - - @Test public void fetchAll_returns_null_on_404() throws RiakIOException, RiakResponseException { - final FetchResponse mockResponse = mock(FetchResponse.class); - - when(mockResponse.getStatusCode()).thenReturn(404); - when(mockRiakClient.fetch(bucket, key, meta)).thenReturn(mockResponse); - - assertNull(impl.fetchAll(bucket, key, meta)); - } - - @Test public void fetchAll_returns_siblings_if_exists() throws RiakIOException, RiakResponseException { - final FetchResponse mockResponse = mock(FetchResponse.class); - final List siblings = new ArrayList(); - - when(mockResponse.getStatusCode()).thenReturn(200); - when(mockResponse.hasSiblings()).thenReturn(true); - when(mockResponse.getSiblings()).thenAnswer(new Answer>() { - public List answer(InvocationOnMock invocation) throws Throwable { - return siblings; - } - }); - when(mockRiakClient.fetch(bucket, key, meta)).thenReturn(mockResponse); - - assertSame(siblings, impl.fetchAll(bucket, key, meta)); - } - - @Test public void fetchAll_returns_object_if_no_siblings() throws RiakIOException, RiakResponseException { - final FetchResponse mockResponse = mock(FetchResponse.class); - - when(mockResponse.getStatusCode()).thenReturn(200); - when(mockResponse.hasObject()).thenReturn(true); - when(mockResponse.hasSiblings()).thenReturn(false); - when(mockResponse.getObject()).thenReturn(object); - when(mockRiakClient.fetch(bucket, key, meta)).thenReturn(mockResponse); - - Collection siblings = impl.fetchAll(bucket, key, meta); - - assertEquals(1, siblings.size()); - assertSame(object, siblings.iterator().next()); - } - - @Test(expected=RiakResponseException.class) public void fetchAll_throws_if_object_not_returned() throws RiakIOException, RiakResponseException { - final FetchResponse mockResponse = mock(FetchResponse.class); - - when(mockResponse.getStatusCode()).thenReturn(200); - when(mockResponse.hasObject()).thenReturn(false); - when(mockRiakClient.fetch(bucket, key, meta)).thenReturn(mockResponse); - - impl.fetchAll(bucket, key, meta); - } - - @Test public void delete_throws_except_for_204_and_404() { - HttpResponse mockResponse = mock(HttpResponse.class); - for (int i : HTTP_STATUSES) { - when(mockResponse.getStatusCode()).thenReturn(i); - when(mockRiakClient.delete(bucket, key, meta)).thenReturn(mockResponse); - boolean threw = true; - try { - impl.delete(bucket, key, meta); - threw = false; - } catch (RiakIOException e) { - } catch (RiakResponseException e) { - } - assertTrue("Wrong behavior for status " + i, throwsForAllStatusesExcept(new int[] { 204, 404 }, i, threw)); - } - } - - @Test public void walk_throws_except_for_200_and_404() { - WalkResponse mockResponse = mock(WalkResponse.class); - for (int i : HTTP_STATUSES) { - when(mockResponse.getStatusCode()).thenReturn(i); - when(mockResponse.hasSteps()).thenReturn(true); - when(mockRiakClient.walk(bucket, key, walkSpec, meta)).thenReturn(mockResponse); - boolean threw = true; - try { - impl.walk(bucket, key, walkSpec, meta); - threw = false; - } catch (RiakIOException e) { - } catch (RiakResponseException e) { - } - assertTrue("Wrong behavior for status " + i, throwsForAllStatusesExcept(new int[] { 200, 404 }, i, threw)); - } - } - - @Test public void walk_returns_null_on_404() throws RiakIOException, RiakResponseException { - final WalkResponse mockResponse = mock(WalkResponse.class); - - when(mockResponse.getStatusCode()).thenReturn(404); - when(mockRiakClient.walk(bucket, key, walkSpec, meta)).thenReturn(mockResponse); - - assertNull(impl.walk(bucket, key, walkSpec, meta)); - } - - @Test(expected=RiakResponseException.class) public void walk_throws_if_steps_not_returned() throws RiakIOException, RiakResponseException { - final WalkResponse mockResponse = mock(WalkResponse.class); - - when(mockResponse.getStatusCode()).thenReturn(200); - when(mockResponse.hasSteps()).thenReturn(false); - when(mockRiakClient.walk(bucket, key, walkSpec, meta)).thenReturn(mockResponse); - - impl.walk(bucket, key, walkSpec, meta); - } - - private boolean throwsForAllStatusesExcept(int[] okStatus, int status, boolean threw) { - boolean ok = false; - for (int s : okStatus) { - if (status == s) - ok = true; - } - return (ok && !threw) || (!ok && threw); - } -} \ No newline at end of file diff --git a/src/test/java/com/basho/riak/client/request/TestRequestMeta.java b/src/test/java/com/basho/riak/client/request/TestRequestMeta.java deleted file mode 100644 index bd5482dbb..000000000 --- a/src/test/java/com/basho/riak/client/request/TestRequestMeta.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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.request; - -import static org.junit.Assert.*; - -import org.junit.Test; - -public class TestRequestMeta { - - @Test public void readParams_sets_r_query_parameter() { - final int R = 10; - RequestMeta impl = RequestMeta.readParams(R); - assertEquals(Integer.toString(R), impl.getQueryParam("r")); - } - - @Test public void writeParams_sets_non_null_w_and_dw_query_parameter() { - final int W = 10; - final int DW = 11; - RequestMeta impl = RequestMeta.writeParams(W, DW); - assertEquals(Integer.toString(W), impl.getQueryParam("w")); - assertEquals(Integer.toString(DW), impl.getQueryParam("dw")); - - impl = RequestMeta.writeParams(W, null); - assertEquals(Integer.toString(W), impl.getQueryParam("w")); - assertEquals(null, impl.getQueryParam("dw")); - - impl = RequestMeta.writeParams(W, DW); - assertEquals(Integer.toString(W), impl.getQueryParam("w")); - assertEquals(Integer.toString(DW), impl.getQueryParam("dw")); - } - - @Test public void deleteParams_sets_rw_query_parameter() { - final int RW = 10; - RequestMeta impl = RequestMeta.deleteParams(RW); - assertEquals(Integer.toString(RW), impl.getQueryParam("rw")); - } - - @Test public void headers_are_returned_by_get_headers() { - final String HEADER_KEY = "key"; - final String HEADER_VALUE = "value"; - - RequestMeta impl = new RequestMeta(); - impl.setHeader(HEADER_KEY, HEADER_VALUE); - assertEquals(HEADER_VALUE, impl.getHeader(HEADER_KEY)); - assertEquals(HEADER_VALUE, impl.getHeaders().get(HEADER_KEY)); - } - - @Test public void query_params_are_returned_by_get_query_params() { - final String QP_KEY = "key"; - final String QP_VALUE = "value"; - - RequestMeta impl = new RequestMeta(); - impl.setQueryParam(QP_KEY, QP_VALUE); - assertEquals(QP_VALUE, impl.getQueryParam(QP_KEY)); - assertTrue(impl.getQueryParams().contains(QP_KEY + "=" + QP_VALUE)); - } - - @Test public void query_parameters_string_contains_formatted_parameters() { - RequestMeta impl = new RequestMeta(); - impl.setQueryParam("p1", "v1"); - impl.setQueryParam("p2", "v2"); - impl.setQueryParam("p3", "v3"); - impl.getQueryParams().matches("(^p1=v1|&p1=v1).*"); // param at beginning of string or preceded by ampersand - impl.getQueryParams().matches("(^p2=v2|&p2=v2).*"); - impl.getQueryParams().matches("(^p3=v3|&p3=v3).*"); - } - - @Test public void no_leading_or_trailing_ampersands_for_query_parameters_string() { - RequestMeta impl = new RequestMeta(); - impl.setQueryParam("p1", "v1"); - impl.setQueryParam("p2", "v2"); - impl.setQueryParam("p3", "v3"); - assertFalse(impl.getQueryParams().startsWith("&")); - assertFalse(impl.getQueryParams().endsWith("&")); - } -} diff --git a/src/test/java/com/basho/riak/client/response/TestBucketResponse.java b/src/test/java/com/basho/riak/client/response/TestBucketResponse.java deleted file mode 100644 index 82b8b05e2..000000000 --- a/src/test/java/com/basho/riak/client/response/TestBucketResponse.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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.response; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Collection; -import java.util.Iterator; - -import org.json.JSONException; -import org.junit.Test; - -public class TestBucketResponse { - - final String TEXT_BODY = - "{\"props\":" + - "{\"name\":\"b\"," + - "\"allow_mult\":false," + - "\"big_vclock\":50," + - "\"chash_keyfun\":" + - "{\"mod\":\"riak_util\"," + - "\"fun\":\"chash_std_keyfun\"}," + - "\"linkfun\":" + - "{\"mod\":\"jiak_object\"," + - "\"fun\":\"mapreduce_linkfun\"}," + - "\"n_val\":3," + - "\"old_vclock\":86400," + - "\"small_vclock\":10," + - "\"young_vclock\":20}," + - "\"keys\":" + - "[\"j\",\"k\",\"l\"]}"; - final byte[] BODY = TEXT_BODY.getBytes(); - final InputStream STREAM = new ByteArrayInputStream( - ("{\"props\":" + - "{\"name\":\"b\"," + - "\"allow_mult\":false," + - "\"big_vclock\":50," + - "\"chash_keyfun\":" + - "{\"mod\":\"riak_util\"," + - "\"fun\":\"chash_std_keyfun\"}," + - "\"linkfun\":" + - "{\"mod\":\"jiak_object\"," + - "\"fun\":\"mapreduce_linkfun\"}," + - "\"n_val\":3," + - "\"old_vclock\":86400," + - "\"small_vclock\":10," + - "\"young_vclock\":20}}" + - "{\"keys\":[\"j\"]}{\"keys\":[]}{\"keys\":[]}{\"keys\":[\"k\",\"l\"]}") - .getBytes()); - - @Test public void doesnt_throw_on_null_impl() throws JSONException, IOException { - new BucketResponse(null); - } - - @Test public void parses_schema_field() throws JSONException, IOException { - HttpResponse mockHttpResponse = mock(HttpResponse.class); - when(mockHttpResponse.getBody()).thenReturn(BODY); - when(mockHttpResponse.getBodyAsString()).thenReturn(TEXT_BODY); - when(mockHttpResponse.isSuccess()).thenReturn(true); - - BucketResponse impl = new BucketResponse(mockHttpResponse); - - assertEquals(false, impl.getBucketInfo().getAllowMult()); - assertEquals(3, impl.getBucketInfo().getNVal().intValue()); - assertEquals("riak_util:chash_std_keyfun", impl.getBucketInfo().getCHashFun()); - assertEquals("jiak_object:mapreduce_linkfun", impl.getBucketInfo().getLinkFun()); - } - - @Test public void parses_keys_field() throws Exception { - HttpResponse mockHttpResponse = mock(HttpResponse.class); - when(mockHttpResponse.getBody()).thenReturn(BODY); - when(mockHttpResponse.getBodyAsString()).thenReturn(TEXT_BODY); - when(mockHttpResponse.isSuccess()).thenReturn(true); - - BucketResponse impl = new BucketResponse(mockHttpResponse); - Collection keys = impl.getBucketInfo().getKeys(); - - assertEquals(3, keys.size()); - assertTrue(keys.contains("j")); - assertTrue(keys.contains("k")); - assertTrue(keys.contains("l")); - } - - @Test public void parses_streamed_schema_field() throws JSONException, IOException { - HttpResponse mockHttpResponse = mock(HttpResponse.class); - when(mockHttpResponse.getStream()).thenReturn(STREAM); - when(mockHttpResponse.isStreamed()).thenReturn(true); - when(mockHttpResponse.isSuccess()).thenReturn(true); - - BucketResponse impl = new BucketResponse(mockHttpResponse); - - assertEquals(false, impl.getBucketInfo().getAllowMult()); - assertEquals(3, impl.getBucketInfo().getNVal().intValue()); - assertEquals("riak_util:chash_std_keyfun", impl.getBucketInfo().getCHashFun()); - assertEquals("jiak_object:mapreduce_linkfun", impl.getBucketInfo().getLinkFun()); - } - - @Test public void parses_streamed_keys() throws Exception { - HttpResponse mockHttpResponse = mock(HttpResponse.class); - when(mockHttpResponse.getStream()).thenReturn(STREAM); - when(mockHttpResponse.isStreamed()).thenReturn(true); - when(mockHttpResponse.isSuccess()).thenReturn(true); - - BucketResponse impl = new BucketResponse(mockHttpResponse); - Collection keys = impl.getBucketInfo().getKeys(); - - Iterator key = keys.iterator(); - assertEquals("j", key.next()); - assertEquals("k", key.next()); - assertEquals("l", key.next()); - } - - @Test public void returns_empty_keys_list_if_keys_element_not_in_response() throws JSONException, IOException { - final byte[] body = "{\"props\": {\"name\":\"b\"}}".getBytes(); - HttpResponse mockHttpResponse = mock(HttpResponse.class); - when(mockHttpResponse.getBody()).thenReturn(body); - when(mockHttpResponse.getBodyAsString()).thenReturn(new String(body)); - when(mockHttpResponse.isSuccess()).thenReturn(true); - - BucketResponse impl = new BucketResponse(mockHttpResponse); - - assertNotNull(impl.getBucketInfo().getKeys()); - assertTrue(impl.getBucketInfo().getKeys().isEmpty()); - } -} diff --git a/src/test/java/com/basho/riak/client/response/TestDefaultHttpResponse.java b/src/test/java/com/basho/riak/client/response/TestDefaultHttpResponse.java deleted file mode 100644 index 06d6cb79d..000000000 --- a/src/test/java/com/basho/riak/client/response/TestDefaultHttpResponse.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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.response; - -import static org.mockito.Mockito.*; -import static org.junit.Assert.*; - -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.DeleteMethod; -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.commons.httpclient.methods.HeadMethod; -import org.junit.Test; - -public class TestDefaultHttpResponse { - - DefaultHttpResponse impl; - - @Test public void status_2xx_300_and_304_success_for_head() { - HeadMethod head = new HeadMethod(); - - for (int i = 200; i < 300; i++) { - impl = new DefaultHttpResponse(null, null, i, null, null, null, head); - assertTrue(impl.isSuccess()); - } - - impl = new DefaultHttpResponse(null, null, 300, null, null, null, head); - assertTrue(impl.isSuccess()); - - impl = new DefaultHttpResponse(null, null, 304, null, null, null, head); - assertTrue(impl.isSuccess()); - } - @Test public void status_2xx_300_and_304_success_for_get() { - GetMethod get = new GetMethod(); - - for (int i = 200; i < 300; i++) { - impl = new DefaultHttpResponse(null, null, i, null, null, null, get); - assertTrue(impl.isSuccess()); - } - - impl = new DefaultHttpResponse(null, null, 300, null, null, null, get); - assertTrue(impl.isSuccess()); - - impl = new DefaultHttpResponse(null, null, 304, null, null, null, get); - assertTrue(impl.isSuccess()); - } - @Test public void status_2xx_and_404_success_for_delete() { - DeleteMethod delete = new DeleteMethod(); - - for (int i = 200; i < 300; i++) { - impl = new DefaultHttpResponse(null, null, i, null, null, null, delete); - assertTrue(impl.isSuccess()); - } - - impl = new DefaultHttpResponse(null, null, 404, null, null, null, delete); - assertTrue(impl.isSuccess()); - } - @Test public void status_4xx_is_error() { - for (int i = 400; i < 500; i++) { - impl = new DefaultHttpResponse(null, null, i, null, null, null, null); - assertTrue(impl.isError()); - } - } - @Test public void status_5xx_is_error() { - for (int i = 500; i < 600; i++) { - impl = new DefaultHttpResponse(null, null, i, null, null, null, null); - assertTrue(impl.isError()); - } - } - @Test public void status_less_than_100_is_error() { - for (int i = -1; i < 100; i++) { - impl = new DefaultHttpResponse(null, null, i, null, null, null, null); - assertTrue(impl.isError()); - } - } - @Test public void null_http_method_with_200_is_still_success() { - impl = new DefaultHttpResponse(null, null, 200, null, null, null, null); - assertTrue(impl.isSuccess()); - } - @Test public void headers_are_never_null() { - impl = new DefaultHttpResponse(null, null, 200, null, null, null, null); - assertNotNull(impl.getHttpHeaders()); - } - - @Test public void close_closes_underlying_http_connection() { - HttpMethod mockHttpMethod = mock(HttpMethod.class); - impl = new DefaultHttpResponse(null, null, 200, null, null, null, mockHttpMethod); - impl.close(); - verify(mockHttpMethod).releaseConnection(); - } -} diff --git a/src/test/java/com/basho/riak/client/response/TestFetchResponse.java b/src/test/java/com/basho/riak/client/response/TestFetchResponse.java deleted file mode 100644 index 31e419dd5..000000000 --- a/src/test/java/com/basho/riak/client/response/TestFetchResponse.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * 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.response; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import org.apache.commons.httpclient.HttpMethod; -import org.json.JSONException; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import com.basho.riak.client.RiakClient; -import com.basho.riak.client.RiakObject; -import com.basho.riak.client.util.ClientUtils; -import com.basho.riak.client.util.Constants; - -public class TestFetchResponse { - - final String BUCKET = "bucket"; - final String KEY = "key"; - final Map SINGLE_HEADERS = new HashMap(); - final String SINGLE_BODY = "foo"; - final Map SIBLING_HEADERS = new HashMap(); - - final String CONTENT_LENGTH = "3"; - - @Mock HttpResponse mockHttpResponse; - @Mock HttpMethod mockHttpMethod; - @Mock RiakClient mockRiakClient; - String SIBLING_BODY; - - @Before public void setup() { - MockitoAnnotations.initMocks(this); - - SINGLE_HEADERS.put("X-Riak-Vclock".toLowerCase(), "a85hYGBgzGDKBVIsDPKZOzKYEhnzWBlaJyw9wpcFAA=="); - SINGLE_HEADERS.put("Vary".toLowerCase(), "Accept-Encoding"); - SINGLE_HEADERS.put("Server".toLowerCase(), "MochiWeb/1.1 WebMachine/1.5.1 (hack the charles gibson)"); - SINGLE_HEADERS.put("Link".toLowerCase(), "; riaktag=\"next\", ; rel=\"up\""); - SINGLE_HEADERS.put("Last-Modified".toLowerCase(), "Tue, 22 Dec 2009 18:48:37 GMT"); - SINGLE_HEADERS.put("ETag".toLowerCase(), "4d5y9wqQK2Do0RK5ezwCJD"); - SINGLE_HEADERS.put("Date".toLowerCase(), "Tue, 22 Dec 2009 19:06:47 GMT"); - SINGLE_HEADERS.put("Content-Type".toLowerCase(), "text/plain"); - SINGLE_HEADERS.put("Content-Length".toLowerCase(), CONTENT_LENGTH); - - SIBLING_HEADERS.put("X-Riak-Vclock".toLowerCase(), "a85hYGBgzmDKBVIsDPKZOzKYEhnzWBlaJyw9wgcVZtWdug4q/GgGXJitOYmh6u0rZIksAA=="); - SIBLING_HEADERS.put("Vary".toLowerCase(), "Accept, Accept-Encoding"); - SIBLING_HEADERS.put("Server".toLowerCase(), "MochiWeb/1.1 WebMachine/1.5.1 (hack the charles gibson)"); - SIBLING_HEADERS.put("Date".toLowerCase(), "Tue, 22 Dec 2009 19:24:21 GMT"); - SIBLING_HEADERS.put("Content-Type".toLowerCase(), "multipart/mixed; boundary=1MFeoR33D8Jdz3uUa9SQI7H8XCb"); - SIBLING_HEADERS.put("Content-Length".toLowerCase(), "407"); - - SIBLING_BODY = "\r\n" + - "--1MFeoR33D8Jdz3uUa9SQI7H8XCb\r\n" + - "Content-Type: text/plain\r\n" + - "Link: ; rel=\"up\", ; riaktag=\"next\"\r\n" + - "Etag: 55SrI4GjdnGfyuShLBWjuf\r\n" + - "Last-Modified: Tue, 22 Dec 2009 19:24:18 GMT\r\n" + - "\r\n" + - "bar\r\n" + - "--1MFeoR33D8Jdz3uUa9SQI7H8XCb\r\n" + - "Content-Type: application/octect-stream\r\n" + - "Link: ; rel=\"up\"\r\n" + - "Etag: 4d5y9wqQK2Do0RK5ezwCJD\r\n" + - "Last-Modified: Tue, 22 Dec 2009 18:48:37 GMT\r\n" + - "X-Riak-Meta-Test: value\r\n" + - "\r\n" + - "foo\r\n" + - "--1MFeoR33D8Jdz3uUa9SQI7H8XCb--\r\n"; - } - - @Test public void doesnt_throw_on_null_impl() throws JSONException { - new FetchResponse(null, null); - } - - @Test public void parses_single_object() throws JSONException { - when(mockHttpResponse.getBucket()).thenReturn(BUCKET); - when(mockHttpResponse.getKey()).thenReturn(KEY); - when(mockHttpResponse.getHttpHeaders()).thenReturn(SINGLE_HEADERS); - when(mockHttpResponse.getBody()).thenReturn(SINGLE_BODY.getBytes()); - when(mockHttpResponse.isSuccess()).thenReturn(true); - - FetchResponse impl = new FetchResponse(mockHttpResponse, mockRiakClient); - - assertTrue(impl.hasObject()); - assertSame(mockRiakClient, impl.getObject().getRiakClient()); - assertEquals(BUCKET, impl.getObject().getBucket()); - assertEquals(KEY, impl.getObject().getKey()); - assertEquals("text/plain", impl.getObject().getContentType()); - assertEquals(SINGLE_BODY, impl.getObject().getValue()); - assertEquals("Tue, 22 Dec 2009 18:48:37 GMT", impl.getObject().getLastmod()); - assertEquals(1, impl.getObject().numLinks()); - assertFalse(impl.getObject().hasUsermeta()); - assertEquals("a85hYGBgzGDKBVIsDPKZOzKYEhnzWBlaJyw9wpcFAA==", impl.getObject().getVclock()); - assertEquals("4d5y9wqQK2Do0RK5ezwCJD", impl.getObject().getVtag()); - } - - @Test public void parses_sibling_objects() throws JSONException { - when(mockHttpResponse.getBucket()).thenReturn(BUCKET); - when(mockHttpResponse.getKey()).thenReturn(KEY); - when(mockHttpResponse.getHttpHeaders()).thenReturn(SIBLING_HEADERS); - when(mockHttpResponse.getBody()).thenReturn(SIBLING_BODY.getBytes()); - when(mockHttpResponse.getBodyAsString()).thenReturn(SIBLING_BODY); - when(mockHttpResponse.getStatusCode()).thenReturn(300); - - FetchResponse impl = new FetchResponse(mockHttpResponse, mockRiakClient); - assertTrue(impl.hasSiblings()); - - Iterator siblings = impl.getSiblings().iterator(); - - RiakObject o; - o = siblings.next(); - assertSame(mockRiakClient, o.getRiakClient()); - assertEquals(BUCKET, o.getBucket()); - assertEquals(KEY, o.getKey()); - assertEquals("text/plain", o.getContentType()); - assertEquals("bar", o.getValue()); - assertEquals(1, o.numLinks()); - assertFalse(o.hasUsermeta()); - assertEquals("Tue, 22 Dec 2009 19:24:18 GMT", o.getLastmod()); - assertEquals("a85hYGBgzmDKBVIsDPKZOzKYEhnzWBlaJyw9wgcVZtWdug4q/GgGXJitOYmh6u0rZIksAA==", o.getVclock()); - assertEquals("55SrI4GjdnGfyuShLBWjuf", o.getVtag()); - - o = siblings.next(); - assertSame(mockRiakClient, o.getRiakClient()); - assertEquals(BUCKET, o.getBucket()); - assertEquals(KEY, o.getKey()); - assertEquals("application/octect-stream", o.getContentType()); - assertEquals("foo", o.getValue()); - assertEquals("Tue, 22 Dec 2009 18:48:37 GMT", o.getLastmod()); - assertFalse(o.hasLinks()); - assertEquals(1, o.numUsermetaItems()); - assertEquals("value", o.getUsermetaItem("test")); - assertEquals("a85hYGBgzmDKBVIsDPKZOzKYEhnzWBlaJyw9wgcVZtWdug4q/GgGXJitOYmh6u0rZIksAA==", o.getVclock()); - assertEquals("4d5y9wqQK2Do0RK5ezwCJD", o.getVtag()); - } - - @Test public void chooses_one_object_when_siblings_exist() throws JSONException { - when(mockHttpResponse.getBucket()).thenReturn(BUCKET); - when(mockHttpResponse.getKey()).thenReturn(KEY); - when(mockHttpResponse.getHttpHeaders()).thenReturn(SIBLING_HEADERS); - when(mockHttpResponse.getBody()).thenReturn(SIBLING_BODY.getBytes()); - when(mockHttpResponse.getBodyAsString()).thenReturn(SIBLING_BODY); - when(mockHttpResponse.getStatusCode()).thenReturn(300); - - FetchResponse impl = new FetchResponse(mockHttpResponse, mockRiakClient); - - assertTrue(impl.hasObject()); - assertTrue(impl.getSiblings().contains(impl.getObject())); - } - - @Test public void stores_value_stream_on_2xx_if_httpresponse_body_is_null() throws IOException { - final Long contentLength = new Long(100); - final InputStream mockInputStream = mock(InputStream.class); - - SINGLE_HEADERS.put(Constants.HDR_CONTENT_LENGTH, contentLength.toString()); - - when(mockHttpResponse.isSuccess()).thenReturn(true); - when(mockHttpResponse.getHttpHeaders()).thenReturn(SINGLE_HEADERS); - when(mockHttpResponse.isStreamed()).thenReturn(true); - when(mockHttpResponse.getStream()).thenReturn(mockInputStream); - - FetchResponse impl = new FetchResponse(mockHttpResponse, mockRiakClient); - - assertTrue(impl.hasObject()); - assertSame(mockInputStream, impl.getObject().getValueStream()); - assertEquals(contentLength, impl.getObject().getValueStreamLength()); - } - - @Test public void returns_streamed_collection_on_streaming_300_response() throws IOException { - final ByteArrayInputStream is = new ByteArrayInputStream(SIBLING_BODY.getBytes()); - - when(mockHttpResponse.getStatusCode()).thenReturn(300); - when(mockHttpResponse.getBucket()).thenReturn(BUCKET); - when(mockHttpResponse.getKey()).thenReturn(KEY); - when(mockHttpResponse.getHttpHeaders()).thenReturn(SIBLING_HEADERS); - when(mockHttpResponse.isStreamed()).thenReturn(true); - when(mockHttpResponse.getStream()).thenReturn(is); - - FetchResponse impl = new FetchResponse(mockHttpResponse, mockRiakClient); - assertTrue(impl.hasSiblings()); - assertTrue(impl.getSiblings() instanceof StreamedSiblingsCollection); - - Iterator siblings = impl.getSiblings().iterator(); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - - RiakObject o; - o = siblings.next(); - ClientUtils.copyStream(o.getValueStream(), os); - - assertSame(mockRiakClient, o.getRiakClient()); - assertEquals(BUCKET, o.getBucket()); - assertEquals(KEY, o.getKey()); - assertEquals("text/plain", o.getContentType()); - assertEquals("bar", os.toString()); - assertEquals(1, o.numLinks()); - assertFalse(o.hasUsermeta()); - assertEquals("Tue, 22 Dec 2009 19:24:18 GMT", o.getLastmod()); - assertEquals("a85hYGBgzmDKBVIsDPKZOzKYEhnzWBlaJyw9wgcVZtWdug4q/GgGXJitOYmh6u0rZIksAA==", o.getVclock()); - assertEquals("55SrI4GjdnGfyuShLBWjuf", o.getVtag()); - - o = siblings.next(); - os.reset(); - ClientUtils.copyStream(o.getValueStream(), os); - assertSame(mockRiakClient, o.getRiakClient()); - assertEquals(BUCKET, o.getBucket()); - assertEquals(KEY, o.getKey()); - assertEquals("application/octect-stream", o.getContentType()); - assertEquals("foo", os.toString()); - assertEquals("Tue, 22 Dec 2009 18:48:37 GMT", o.getLastmod()); - assertFalse(o.hasLinks()); - assertEquals(1, o.numUsermetaItems()); - assertEquals("value", o.getUsermetaItem("test")); - assertEquals("a85hYGBgzmDKBVIsDPKZOzKYEhnzWBlaJyw9wgcVZtWdug4q/GgGXJitOYmh6u0rZIksAA==", o.getVclock()); - assertEquals("4d5y9wqQK2Do0RK5ezwCJD", o.getVtag()); - } - - @Test public void does_not_close_stream_on_streaming_300_response() throws IOException { - final ByteArrayInputStream is = new ByteArrayInputStream(SIBLING_BODY.getBytes()); - - when(mockHttpResponse.getStatusCode()).thenReturn(300); - when(mockHttpResponse.getBucket()).thenReturn(BUCKET); - when(mockHttpResponse.getKey()).thenReturn(KEY); - when(mockHttpResponse.getHttpHeaders()).thenReturn(SIBLING_HEADERS); - when(mockHttpResponse.isStreamed()).thenReturn(true); - when(mockHttpResponse.getStream()).thenReturn(is); - - new FetchResponse(mockHttpResponse, mockRiakClient); - - verify(mockHttpResponse, never()).close(); - } - - @Test public void returns_content_length_for_head() { - when(mockHttpResponse.getBucket()).thenReturn(BUCKET); - when(mockHttpResponse.getKey()).thenReturn(KEY); - when(mockHttpResponse.getHttpHeaders()).thenReturn(SINGLE_HEADERS); - when(mockHttpResponse.isSuccess()).thenReturn(true); - - FetchResponse impl = new FetchResponse(mockHttpResponse, mockRiakClient); - - assertSame(mockRiakClient, impl.getObject().getRiakClient()); - assertEquals(BUCKET, impl.getObject().getBucket()); - assertEquals(KEY, impl.getObject().getKey()); - assertEquals("text/plain", impl.getObject().getContentType()); - - Long contentLength = impl.getObject().getValueStreamLength(); - assertNotNull("Content-Length should not be null", contentLength); - assertEquals(CONTENT_LENGTH, contentLength.toString()); - assertEquals("Tue, 22 Dec 2009 18:48:37 GMT", impl.getObject().getLastmod()); - assertEquals(1, impl.getObject().numLinks()); - assertFalse(impl.getObject().hasUsermeta()); - assertEquals("a85hYGBgzGDKBVIsDPKZOzKYEhnzWBlaJyw9wpcFAA==", impl.getObject().getVclock()); - assertEquals("4d5y9wqQK2Do0RK5ezwCJD", impl.getObject().getVtag()); - } -} \ No newline at end of file diff --git a/src/test/java/com/basho/riak/client/response/TestHttpResponseDecorator.java b/src/test/java/com/basho/riak/client/response/TestHttpResponseDecorator.java deleted file mode 100644 index 341acf58a..000000000 --- a/src/test/java/com/basho/riak/client/response/TestHttpResponseDecorator.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.response; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.commons.httpclient.HttpMethod; -import org.json.JSONException; -import org.junit.Test; - -public class TestHttpResponseDecorator { - - @Test public void delegates_http_response_methods_to_impl() throws JSONException { - - final String BUCKET = "bucket"; - final String KEY = "key"; - final byte[] BODY = "body".getBytes(); - final int STATUS_CODE = 1; - final Map HTTP_HEADERS = new HashMap(); - final HttpMethod HTTP_METHOD = mock(HttpMethod.class); - final boolean IS_SUCCESS = true; - final boolean IS_ERROR = true; - - HttpResponse mockHttpResponse = mock(HttpResponse.class); - when(mockHttpResponse.getBucket()).thenReturn(BUCKET); - when(mockHttpResponse.getKey()).thenReturn(KEY); - when(mockHttpResponse.getBody()).thenReturn(BODY); - when(mockHttpResponse.getStatusCode()).thenReturn(STATUS_CODE); - when(mockHttpResponse.getHttpHeaders()).thenReturn(HTTP_HEADERS); - when(mockHttpResponse.getHttpMethod()).thenReturn(HTTP_METHOD); - when(mockHttpResponse.isSuccess()).thenReturn(IS_SUCCESS); - when(mockHttpResponse.isError()).thenReturn(IS_ERROR); - - HttpResponseDecorator impl = new HttpResponseDecorator(mockHttpResponse); - - assertEquals(BUCKET, impl.getBucket()); - assertEquals(KEY, impl.getKey()); - assertEquals(BODY, impl.getBody()); - assertEquals(STATUS_CODE, impl.getStatusCode()); - assertSame(HTTP_HEADERS, impl.getHttpHeaders()); - assertSame(HTTP_METHOD, impl.getHttpMethod()); - assertEquals(IS_SUCCESS, impl.isSuccess()); - assertEquals(IS_ERROR, impl.isError()); - - verify(mockHttpResponse).getBucket(); - verify(mockHttpResponse).getKey(); -// verify(mockHttpResponse).getBody(); - verify(mockHttpResponse).getStatusCode(); - verify(mockHttpResponse).getHttpHeaders(); - verify(mockHttpResponse).getHttpMethod(); - verify(mockHttpResponse).isSuccess(); - verify(mockHttpResponse).isError(); - } -} diff --git a/src/test/java/com/basho/riak/client/response/TestStoreResponse.java b/src/test/java/com/basho/riak/client/response/TestStoreResponse.java deleted file mode 100644 index 2d8be5bcd..000000000 --- a/src/test/java/com/basho/riak/client/response/TestStoreResponse.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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.response; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import java.util.HashMap; -import java.util.Map; - -import org.json.JSONException; -import org.junit.Test; - -public class TestStoreResponse { - - @Test public void doesnt_throw_on_null_impl() throws JSONException { - new StoreResponse(null); - } - - @Test public void parses_meta_headers() { - final FetchResponse mockFetchResponse = mock(FetchResponse.class); - final Map HTTP_HEADERS = new HashMap(); - HTTP_HEADERS.put("X-Riak-Vclock".toLowerCase(), "a85hYGBgzGDKBVIsDPKZOzKYEhnzWBlaJyw9wpcFAA=="); - HTTP_HEADERS.put("Last-Modified".toLowerCase(), "Tue, 22 Dec 2009 18:48:37 GMT"); - HTTP_HEADERS.put("ETag".toLowerCase(), "4d5y9wqQK2Do0RK5ezwCJD"); - - when(mockFetchResponse.getHttpHeaders()).thenReturn(HTTP_HEADERS); - when(mockFetchResponse.isSuccess()).thenReturn(true); - - StoreResponse impl = new StoreResponse(mockFetchResponse); - - assertEquals("a85hYGBgzGDKBVIsDPKZOzKYEhnzWBlaJyw9wpcFAA==", impl.getVclock()); - assertEquals("Tue, 22 Dec 2009 18:48:37 GMT", impl.getLastmod()); - assertEquals("4d5y9wqQK2Do0RK5ezwCJD", impl.getVtag()); - } - - @Test public void delegates_multiimpl_to_bodyresponse() { - final FetchResponse mockFetchResponse = mock(FetchResponse.class); - StoreResponse impl = new StoreResponse(mockFetchResponse); - - impl.hasObject(); - impl.getObject(); - impl.hasSiblings(); - impl.getSiblings(); - - verify(mockFetchResponse, times(1)).hasObject(); - verify(mockFetchResponse, times(1)).getObject(); - verify(mockFetchResponse, times(1)).hasSiblings(); - verify(mockFetchResponse, times(1)).getSiblings(); - - } -} diff --git a/src/test/java/com/basho/riak/client/response/TestStreamedKeysCollection.java b/src/test/java/com/basho/riak/client/response/TestStreamedKeysCollection.java deleted file mode 100644 index 226c3dc0e..000000000 --- a/src/test/java/com/basho/riak/client/response/TestStreamedKeysCollection.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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.response; - -import static org.junit.Assert.*; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.Iterator; - -import org.json.JSONTokener; -import org.junit.Test; - -public class TestStreamedKeysCollection { - - StreamedKeysCollection impl; - - @Test public void gets_all_keys() { - final String keys = "{\"keys\":[\"key1\"]}{\"keys\":[]}{\"keys\":[]}{\"keys\":[\"key2\",\"key3\"]}{\"keys\":[]}"; - final InputStream stream = new ByteArrayInputStream(keys.getBytes()); - impl = new StreamedKeysCollection(new JSONTokener(new InputStreamReader(stream))); - Iterator iter = impl.iterator(); - - // cause all keys to be cached - assertEquals(3, impl.size()); - assertEquals("key1", iter.next()); - assertEquals("key2", iter.next()); - assertEquals("key3", iter.next()); - } - - @Test public void iterator_iterates_all_keys() { - final String keys = "{\"keys\":[\"key1\"]}{\"keys\":[]}{\"keys\":[]}{\"keys\":[\"key2\",\"key3\"]}{\"keys\":[]}"; - final InputStream stream = new ByteArrayInputStream(keys.getBytes()); - StreamedKeysCollection impl = new StreamedKeysCollection(new JSONTokener(new InputStreamReader(stream))); - - int i = 0; - String expected[] = {"key1", "key2", "key3"}; - for (String key : impl) { - assertEquals(expected[i++], key); - } - } - - @Test public void reads_an_input_array() { - final String keys = "[\"key1\", \"key2\"]"; - final InputStream stream = new ByteArrayInputStream(keys.getBytes()); - StreamedKeysCollection impl = new StreamedKeysCollection(new JSONTokener(new InputStreamReader(stream))); - Iterator iter = impl.iterator(); - - assertEquals("key1", iter.next()); - assertEquals("key2", iter.next()); - } - - @Test public void cacheNext_finds_first_embedded_array() { - final String keys = "{\"keys\":[\"key1\",\"key2\",\"key3\"]}"; - final InputStream stream = new ByteArrayInputStream(keys.getBytes()); - StreamedKeysCollection impl = new StreamedKeysCollection(new JSONTokener(new InputStreamReader(stream))); - - assertEquals("key1", impl.iterator().next()); - } - - @Test public void finds_next_array() { - final String keys = "{\"keys\":[\"key1\"]}{\"j\": 1, \"k\": \"v\", \"l\": [ ]}[\"key2\", \"key3\"]"; - final InputStream stream = new ByteArrayInputStream(keys.getBytes()); - StreamedKeysCollection impl = new StreamedKeysCollection(new JSONTokener(new InputStreamReader(stream))); - Iterator iter = impl.iterator(); - - assertEquals("key1", iter.next()); - assertEquals("key2", iter.next()); - assertEquals("key3", iter.next()); - } - - @Test public void cache_next_returns_false_after_calling_close_backend() { - final String keys = "[\"key1\", \"key2\"]"; - final InputStream stream = new ByteArrayInputStream(keys.getBytes()); - - impl = new StreamedKeysCollection(new JSONTokener(new InputStreamReader(stream))); - assertTrue(impl.cacheNext()); - - impl.closeBackend(); - assertFalse(impl.cacheNext()); - - assertEquals("key1", impl.iterator().next()); - } -} diff --git a/src/test/java/com/basho/riak/client/response/TestStreamedSiblingsCollection.java b/src/test/java/com/basho/riak/client/response/TestStreamedSiblingsCollection.java deleted file mode 100644 index aebb2b82b..000000000 --- a/src/test/java/com/basho/riak/client/response/TestStreamedSiblingsCollection.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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.response; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import com.basho.riak.client.RiakClient; -import com.basho.riak.client.RiakLink; -import com.basho.riak.client.RiakObject; -import com.basho.riak.client.util.Multipart; -import com.basho.riak.client.util.StreamedMultipart; - -public class TestStreamedSiblingsCollection { - - @Mock RiakClient mockRiak; - @Mock StreamedMultipart mockMultipart; - @Mock Multipart.Part mockPart; - final String BUCKET = "bucket"; - final String KEY = "key"; - final Map headers = new HashMap(); - final Map partHeaders = new HashMap(); - - StreamedSiblingsCollection impl; - - @Before public void setup() { - MockitoAnnotations.initMocks(this); - when(mockMultipart.getHeaders()).thenReturn(headers); - when(mockPart.getHeaders()).thenReturn(partHeaders); - impl = new StreamedSiblingsCollection(mockRiak, BUCKET, KEY, mockMultipart); - } - - @Test public void extracts_vclock_from_parent_document() { - final String vclock = "a85hYGBgzWDKBVJszUkM2xfyZjAlMuaxMmQ90j/KB5FgScvP18UinJRYhV24CIsw0HTG4McxyBJZAA=="; - headers.put("x-riak-vclock", vclock); - when(mockMultipart.next()).thenReturn(mockPart); - RiakObject o = impl.iterator().next(); - assertNotNull(o); - assertEquals(vclock, o.getVclock()); - } - - @Test public void subpart_location_header_overrides_bucket_and_key() { - partHeaders.put("location", "/riak/new_bucket/new_key"); - when(mockMultipart.next()).thenReturn(mockPart); - RiakObject o = impl.iterator().next(); - assertNotNull(o); - assertEquals("new_bucket", o.getBucket()); - assertEquals("new_key", o.getKey()); - } - - @Test public void reads_subpart_content_type() { - partHeaders.put("content-type", "content type"); - when(mockMultipart.next()).thenReturn(mockPart); - RiakObject o = impl.iterator().next(); - assertNotNull(o); - assertEquals("content type", o.getContentType()); - } - - @Test public void reads_subpart_last_modified() { - partHeaders.put("last-modified", "lastmod"); - when(mockMultipart.next()).thenReturn(mockPart); - RiakObject o = impl.iterator().next(); - assertNotNull(o); - assertEquals("lastmod", o.getLastmod()); - } - - @Test public void reads_subpart_etag() { - partHeaders.put("etag", "etag"); - when(mockMultipart.next()).thenReturn(mockPart); - RiakObject o = impl.iterator().next(); - assertNotNull(o); - assertEquals("etag", o.getVtag()); - } - - @Test public void reads_subpart_usermeta() { - partHeaders.put("x-riak-meta-foo", "bar"); - when(mockMultipart.next()).thenReturn(mockPart); - RiakObject o = impl.iterator().next(); - assertNotNull(o); - assertEquals("bar", o.getUsermetaItem("foo")); - } - - @Test public void reads_subpart_links() { - partHeaders.put("link", "; riaktag=foo"); - when(mockMultipart.next()).thenReturn(mockPart); - RiakObject o = impl.iterator().next(); - assertNotNull(o); - assertTrue(o.hasLink(new RiakLink("other_bucket", "other_key", "foo"))); - } - - @Test public void reads_subpart_stream() { - final InputStream mockInputStream = mock(InputStream.class); - when(mockPart.getStream()).thenReturn(mockInputStream); - when(mockMultipart.next()).thenReturn(mockPart); - RiakObject o = impl.iterator().next(); - assertNotNull(o); - assertSame(mockInputStream, o.getValueStream()); - } - - @Test public void returns_empty_collection_when_no_parts() { - assertEquals(0, impl.size()); - assertFalse(impl.iterator().hasNext()); - assertNull(impl.iterator().next()); - } - - @Test(expected=RiakIORuntimeException.class) public void throws_riak_io_runtime_exception_on_communication_error() { - IOException e = new IOException(); - when(mockMultipart.next()).thenThrow(new RuntimeException(e)); - impl.iterator().next(); - } - -} diff --git a/src/test/java/com/basho/riak/client/response/TestWalkResponse.java b/src/test/java/com/basho/riak/client/response/TestWalkResponse.java deleted file mode 100644 index aeb4ee60b..000000000 --- a/src/test/java/com/basho/riak/client/response/TestWalkResponse.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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.response; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import java.util.HashMap; -import java.util.Map; - -import org.json.JSONException; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import com.basho.riak.client.RiakClient; -import com.basho.riak.client.response.HttpResponse; -import com.basho.riak.client.response.RiakResponseRuntimeException; - -public class TestWalkResponse { - - final String BUCKET = "bucket"; - final String KEY = "key"; - final Map HEADERS = new HashMap(); - - @Mock HttpResponse mockHttpResponse; - @Mock RiakClient mockRiakClient; - - @Before public void setup() { - HEADERS.put("Content-Type".toLowerCase(), "multipart/mixed; boundary=BCVLGEKnH0gY7KsH5nW3xnzhYbU"); - - MockitoAnnotations.initMocks(this); - - when(mockHttpResponse.getBucket()).thenReturn(BUCKET); - when(mockHttpResponse.getKey()).thenReturn(KEY); - when(mockHttpResponse.getHttpHeaders()).thenReturn(HEADERS); - } - - @Test public void doesnt_throw_on_null_impl() throws JSONException { - new WalkResponse(null, null); - } - - @Test public void returns_empty_list_on_no_content() { - when(mockHttpResponse.getBody()).thenReturn("".getBytes()); - when(mockHttpResponse.isSuccess()).thenReturn(true); - - WalkResponse impl = new WalkResponse(mockHttpResponse, mockRiakClient); - - assertFalse(impl.hasSteps()); - assertEquals(0, impl.getSteps().size()); - } - - @Test public void parses_walk_steps() { - final String BODY = "\r\n" + "--BCVLGEKnH0gY7KsH5nW3xnzhYbU\r\n" - + "Content-Type: multipart/mixed; boundary=7Ymillu08Tqzwb9Cm6Bs8OewFd5\r\n" - + "\r\n" - + "--7Ymillu08Tqzwb9Cm6Bs8OewFd5\r\n" - + "X-Riak-Vclock: vclock1\r\n" - + "Location: /riak/b/k1\r\n" - + "\r\n" - + "foo\r\n" - + "--7Ymillu08Tqzwb9Cm6Bs8OewFd5\r\n" - + "X-Riak-Vclock: vclock2\r\n" - + "Location: /riak/b/k2\r\n" - + "\r\n" - + "bar\r\n" - + "--7Ymillu08Tqzwb9Cm6Bs8OewFd5--\r\n" - + "\r\n" - + "--BCVLGEKnH0gY7KsH5nW3xnzhYbU--\r\n"; - - when(mockHttpResponse.getBody()).thenReturn(BODY.getBytes()); - when(mockHttpResponse.getBodyAsString()).thenReturn(BODY); - when(mockHttpResponse.isSuccess()).thenReturn(true); - - WalkResponse impl = new WalkResponse(mockHttpResponse, mockRiakClient); - assertTrue(impl.hasSteps()); - assertEquals(1, impl.getSteps().size()); - - assertSame(mockRiakClient, impl.getSteps().get(0).get(0).getRiakClient()); - assertEquals("b", impl.getSteps().get(0).get(0).getBucket()); - assertEquals("k1", impl.getSteps().get(0).get(0).getKey()); - assertEquals("foo", impl.getSteps().get(0).get(0).getValue()); - assertEquals("vclock1", impl.getSteps().get(0).get(0).getVclock()); - - assertSame(mockRiakClient, impl.getSteps().get(0).get(1).getRiakClient()); - assertEquals("b", impl.getSteps().get(0).get(1).getBucket()); - assertEquals("k2", impl.getSteps().get(0).get(1).getKey()); - assertEquals("bar", impl.getSteps().get(0).get(1).getValue()); - assertEquals("vclock2", impl.getSteps().get(0).get(1).getVclock()); - } - - @Test(expected = RiakResponseRuntimeException.class) public void throws_on_invalid_subpart_content_type() { - final String BODY = "\r\n" - + "--BCVLGEKnH0gY7KsH5nW3xnzhYbU\r\n" - + "Content-Type: text/plain\r\n" - + "\r\n" - + "--7Ymillu08Tqzwb9Cm6Bs8OewFd5\r\n" - + "\r\n" - + "--7Ymillu08Tqzwb9Cm6Bs8OewFd5--\r\n" - + "\r\n" - + "--BCVLGEKnH0gY7KsH5nW3xnzhYbU--\r\n"; - - when(mockHttpResponse.getBody()).thenReturn(BODY.getBytes()); - when(mockHttpResponse.getBodyAsString()).thenReturn(BODY); - when(mockHttpResponse.isSuccess()).thenReturn(true); - - new WalkResponse(mockHttpResponse, mockRiakClient); - } - - // WalkResponse uses Multipart.parse, so we can rely on TestMultipart - // to validate multipart parsing works. -} diff --git a/src/test/java/com/basho/riak/client/util/TestBranchableInputStream.java b/src/test/java/com/basho/riak/client/util/TestBranchableInputStream.java deleted file mode 100644 index 472db940b..000000000 --- a/src/test/java/com/basho/riak/client/util/TestBranchableInputStream.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * 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.util; - -import static org.junit.Assert.*; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Random; - -import org.junit.Test; - -import com.basho.riak.client.util.BranchableInputStream.InputStreamBranch; -import com.basho.riak.client.util.BranchableInputStream.LinkedChunk; - -public class TestBranchableInputStream { - - BranchableInputStream impl; - - @Test public void behaves_as_standard_input_stream_for_short_inputs() throws IOException { - byte[] bytes = "short buffer".getBytes(); - ByteArrayInputStream is = new ByteArrayInputStream(bytes); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - impl = new BranchableInputStream(is); - ClientUtils.copyStream(impl, os); - - assertArrayEquals(bytes, os.toByteArray()); - } - - @Test public void behaves_as_standard_input_stream_for_long_inputs() throws IOException { - byte[] bytes = new byte[BranchableInputStream.DEFAULT_BASE_CHUNK_SIZE * 5]; - new Random().nextBytes(bytes); - ByteArrayInputStream is = new ByteArrayInputStream(bytes); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - impl = new BranchableInputStream(is); - ClientUtils.copyStream(impl, os); - - assertArrayEquals(bytes, os.toByteArray()); - } - - @Test public void read_array_works() throws IOException { - byte[] bytes = new byte[100]; - byte[] copy = new byte[100]; - new Random().nextBytes(bytes); - ByteArrayInputStream is = new ByteArrayInputStream(bytes); - - impl = new BranchableInputStream(is); - impl.read(copy); - - assertArrayEquals(bytes, copy); - } - - @Test public void all_branches_read_data() throws IOException { - byte[] bytes = new byte[100]; - new Random().nextBytes(bytes); - ByteArrayInputStream is = new ByteArrayInputStream(bytes); - InputStreamBranch[] branches = new InputStreamBranch[5]; - impl = new BranchableInputStream(is); - - for (int i = 0; i < 5; i++) { - branches[i] = (InputStreamBranch) impl.branch(); - int c = impl.read(); - assertEquals(bytes[i], (byte) c); - } - - for (int i = 0; i < 5; i++) { - for (int j = i; j < bytes.length; j++) { - assertEquals(bytes[j], (byte) branches[i].read()); - } - } - - assertEquals(-1, impl.read()); - } - - @Test public void read_position_updates_when_reading() throws IOException { - byte[] bytes = "012345678901234567890123456789".getBytes(); - ByteArrayInputStream is = new ByteArrayInputStream(bytes); - impl = new BranchableInputStream(is); - - for (int i = 0; i < 20; i++) { - assertEquals(i, impl.pos); - impl.read(); - } - } - - @Test public void primary_and_branch_positions_updates_when_reading_from_branch() throws IOException { - byte[] bytes = "012345678901234567890123456789".getBytes(); - ByteArrayInputStream is = new ByteArrayInputStream(bytes); - InputStreamBranch[] branches = new InputStreamBranch[5]; - impl = new BranchableInputStream(is); - - - for (int i = 0; i < 5; i++) { - branches[i] = (InputStreamBranch) impl.branch(); - impl.read(); - } - - for (int i = 0; i < 5; i++) { - for (int j = i; j < 20; j++) { - assertEquals(j, branches[i].pos); - branches[i].read(); - } - } - - assertEquals(20, impl.pos); - } - - @Test public void no_data_loss_when_branches_span_chunks() throws IOException { - byte[] bytes = new byte[BranchableInputStream.DEFAULT_BASE_CHUNK_SIZE * 5]; - new Random().nextBytes(bytes); - ByteArrayInputStream is = new ByteArrayInputStream(bytes); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - impl = new BranchableInputStream(is); - - InputStream[] branches = new InputStreamBranch[2]; - branches[0] = impl.branch(); - - for (int i = 0; i < BranchableInputStream.DEFAULT_BASE_CHUNK_SIZE * 3; i++) { - impl.read(); - } - - branches[1] = impl.branch(); - for (int i = 0; i < BranchableInputStream.DEFAULT_BASE_CHUNK_SIZE; i++) { - branches[1].read(); - } - - assertEquals(BranchableInputStream.DEFAULT_BASE_CHUNK_SIZE * 4, impl.pos); - - os.reset(); - ClientUtils.copyStream(branches[0], os); - assertArrayEquals(bytes, os.toByteArray()); - - os.reset(); - ClientUtils.copyStream(branches[1], os); - - byte[] expected = new byte[BranchableInputStream.DEFAULT_BASE_CHUNK_SIZE * 5 - BranchableInputStream.DEFAULT_BASE_CHUNK_SIZE * 4]; - - System.arraycopy(bytes, BranchableInputStream.DEFAULT_BASE_CHUNK_SIZE * 4, expected, 0, BranchableInputStream.DEFAULT_BASE_CHUNK_SIZE * 5 - BranchableInputStream.DEFAULT_BASE_CHUNK_SIZE * 4); - - assertArrayEquals(expected, os.toByteArray()); - - assertEquals(-1, impl.read()); - } - - @Test public void chunk_sizes_increase_exponentially() throws IOException { - byte[] bytes = new byte[BranchableInputStream.DEFAULT_BASE_CHUNK_SIZE * 5]; - new Random().nextBytes(bytes); - ByteArrayInputStream is = new ByteArrayInputStream(bytes); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - - impl = new BranchableInputStream(is); - LinkedChunk firstChunk = impl.lastChunk; - InputStream branch = impl.branch(); - ClientUtils.copyStream(branch, os); - - assertEquals(0, firstChunk.buf.length); - assertEquals(BranchableInputStream.DEFAULT_BASE_CHUNK_SIZE, firstChunk.next().buf.length); - assertEquals(BranchableInputStream.DEFAULT_BASE_CHUNK_SIZE*2, firstChunk.next().next().buf.length); - assertEquals(BranchableInputStream.DEFAULT_BASE_CHUNK_SIZE*4, firstChunk.next().next().next().buf.length); - - assertTrue(firstChunk.full()); - assertTrue(firstChunk.next().full()); - assertTrue(firstChunk.next().next().full()); - assertFalse(firstChunk.next().next().next().full()); - - assertNull(firstChunk.next().next().next().next()); - } -} diff --git a/src/test/java/com/basho/riak/client/util/TestClientHelper.java b/src/test/java/com/basho/riak/client/util/TestClientHelper.java deleted file mode 100644 index 188ea34e5..000000000 --- a/src/test/java/com/basho/riak/client/util/TestClientHelper.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * 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.util; - -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; - -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpException; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.DeleteMethod; -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.commons.httpclient.methods.HeadMethod; -import org.apache.commons.httpclient.methods.PostMethod; -import org.apache.commons.httpclient.methods.PutMethod; -import org.json.JSONObject; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import com.basho.riak.client.RiakConfig; -import com.basho.riak.client.RiakObject; -import com.basho.riak.client.request.RequestMeta; -import com.basho.riak.client.response.HttpResponse; -import com.basho.riak.client.response.RiakExceptionHandler; -import com.basho.riak.client.response.RiakIORuntimeException; -import com.basho.riak.client.response.RiakResponseRuntimeException; -import com.basho.riak.client.response.StreamHandler; - -public class TestClientHelper { - - final RiakConfig config = new RiakConfig(); - final String bucket = "bucket"; - final String key = "key"; - final String walkSpec = "walkSpec"; - final String mrJob = "mrJob"; - final String clientId = "test"; - final RequestMeta meta = new RequestMeta(); - - @Mock HttpClient mockHttpClient; - @Mock JSONObject schema; - @Mock RiakObject object; - ClientHelper impl; - - @Before public void setup() { - MockitoAnnotations.initMocks(this); - - when(object.getBucket()).thenReturn(bucket); - when(object.getKey()).thenReturn(key); - - impl = new ClientHelper(config, clientId); - impl.setHttpClient(mockHttpClient); - } - - @Test public void client_helper_uses_passed_in_client_id() throws UnsupportedEncodingException { - assertEquals(clientId, new String(impl.getClientId())); - } - - @Test public void client_helper_generates_client_id_if_null() { - impl = new ClientHelper(config, null); - assertNotNull(impl.getClientId()); - assertEquals(4, impl.getClientId().length); - } - - @Test public void fetch_defaults_to_not_streaming() { - impl = spy(impl); - impl.fetch(bucket, key, meta); - verify(impl).fetch(bucket, key, meta, false); - } - - @Test public void setBucketSchema_PUTs_to_bucket_URL() throws HttpException, IOException { - when(mockHttpClient.executeMethod(any(HttpMethod.class))).thenAnswer(pathVerifier("/" + bucket)); - impl.setBucketSchema(bucket, schema, meta); - verify(mockHttpClient).executeMethod(any(PutMethod.class)); - } - - @Test public void getBucketSchema_GETs_bucket_URL() throws HttpException, IOException { - when(mockHttpClient.executeMethod(any(HttpMethod.class))).thenAnswer(pathVerifier("/" + bucket)); - impl.getBucketSchema(bucket, meta); - verify(mockHttpClient).executeMethod(any(GetMethod.class)); - } - - @Test public void getBucketSchema_adds_no_keys_qp() throws HttpException, IOException { - RequestMeta meta = spy(new RequestMeta()); - impl = spy(impl); - impl.getBucketSchema(bucket, meta); - verify(meta).setQueryParam(Constants.QP_KEYS, Constants.NO_KEYS); - verify(impl).executeMethod(eq(bucket), anyString(), any(GetMethod.class), same(meta), eq(false)); - } - - @Test public void listBucket_GETs_bucket_URL() throws HttpException, IOException { - when(mockHttpClient.executeMethod(any(HttpMethod.class))).thenAnswer(pathVerifier("/" + bucket)); - impl.listBucket(bucket, meta, false); - verify(mockHttpClient).executeMethod(any(GetMethod.class)); - } - - @Test public void listBucket_adds_keys_qp_when_streaming_response() { - RequestMeta meta = spy(new RequestMeta()); - impl = spy(impl); - impl.listBucket(bucket, meta, true); - verify(meta).setQueryParam(Constants.QP_KEYS, Constants.STREAM_KEYS); - verify(impl).executeMethod(eq(bucket), anyString(), any(GetMethod.class), same(meta), eq(true)); - } - - @Test public void listBucket_add_keys_equals_true_qp_if_not_streaming_response() { - RequestMeta meta = spy(new RequestMeta()); - impl = spy(impl); - impl.listBucket(bucket, meta, false); - verify(meta).setQueryParam(eq(Constants.QP_KEYS), eq(Constants.INCLUDE_KEYS)); - verify(impl).executeMethod(eq(bucket), anyString(), any(GetMethod.class), same(meta), eq(false)); - } - - @Test public void store_PUTs_object_URL() throws HttpException, IOException { - when(mockHttpClient.executeMethod(any(HttpMethod.class))).thenAnswer(pathVerifier("/" + bucket + "/" + key)); - impl.store(object, meta); - verify(mockHttpClient).executeMethod(any(PutMethod.class)); - } - - @Test public void store_sets_client_id() { - impl.store(object, meta); - assertEquals(ClientUtils.encodeClientId(clientId), meta.getClientId()); - } - - @Test public void store_doesnt_overwrite_client_id() { - meta.setClientId("clientId"); - impl.store(object, meta); - assertEquals("clientId", meta.getClientId()); - } - - @Test public void store_sets_connection_header() { - impl.store(object, meta); - assertEquals("keep-alive", meta.getHeader(Constants.HDR_CONNECTION)); - } - - @Test public void store_doesnt_overwrite_connection_header() { - meta.setHeader(Constants.HDR_CONNECTION, "connection"); - impl.store(object, meta); - assertEquals("connection", meta.getHeader(Constants.HDR_CONNECTION)); - } - - @Test public void fetchMeta_HEADs_object_URL() throws HttpException, IOException { - when(mockHttpClient.executeMethod(any(HttpMethod.class))).thenAnswer(pathVerifier("/" + bucket + "/" + key)); - impl.fetchMeta(bucket, key, meta); - verify(mockHttpClient).executeMethod(any(HeadMethod.class)); - } - - @Test public void fetchMeta_adds_default_R_value() { - impl.fetchMeta(bucket, key, meta); - assertEquals(Integer.toString(Constants.DEFAULT_R), meta.getQueryParam(Constants.QP_R)); - } - - @Test public void fetch_GETs_object_URL() throws HttpException, IOException { - when(mockHttpClient.executeMethod(any(HttpMethod.class))).thenAnswer(pathVerifier("/" + bucket + "/" + key)); - impl.fetch(bucket, key, meta); - verify(mockHttpClient).executeMethod(any(GetMethod.class)); - } - - @Test public void fetch_adds_default_R_value() { - impl.fetch(bucket, key, meta); - assertEquals(Integer.toString(Constants.DEFAULT_R), meta.getQueryParam(Constants.QP_R)); - } - - @Test public void stream_GETs_object_URL() throws IOException { - when(mockHttpClient.executeMethod(any(HttpMethod.class))).thenAnswer(pathVerifier("/" + bucket + "/" + key)); - impl.stream(bucket, key, mock(StreamHandler.class), meta); - verify(mockHttpClient).executeMethod(any(GetMethod.class)); - } - - @Test public void stream_adds_default_R_value() throws IOException { - impl.stream(bucket, key, mock(StreamHandler.class), meta); - assertEquals(Integer.toString(Constants.DEFAULT_R), meta.getQueryParam(Constants.QP_R)); - } - - @Test public void delete_DELETEs_object_URL() throws HttpException, IOException { - when(mockHttpClient.executeMethod(any(HttpMethod.class))).thenAnswer(pathVerifier("/" + bucket + "/" + key)); - impl.delete(bucket, key, meta); - verify(mockHttpClient).executeMethod(any(DeleteMethod.class)); - } - - @Test public void walk_GETs_object_URL() throws HttpException, IOException { - when(mockHttpClient.executeMethod(any(HttpMethod.class))).thenAnswer(pathVerifier("/" + bucket + "/" + key + "/" + walkSpec)); - impl.walk(bucket, key, walkSpec, meta); - verify(mockHttpClient).executeMethod(any(GetMethod.class)); - } - - @Test public void mapreduce_POSTs_to_mapred_URL() throws HttpException, IOException { - when(mockHttpClient.executeMethod(any(HttpMethod.class))).thenAnswer(pathVerifier("/mapred")); - impl.mapReduce(mrJob, meta); - verify(mockHttpClient).executeMethod(any(PostMethod.class)); - } - - @Test public void all_methods_add_query_params() throws HttpException, IOException { - - impl.setBucketSchema(bucket, schema, meta); - impl.listBucket(bucket, meta, false); - impl.store(object, meta); - impl.fetchMeta(bucket, key, meta); - impl.fetch(bucket, key, meta); - impl.delete(bucket, key, meta); - impl.walk(bucket, key, walkSpec, meta); - impl.mapReduce(mrJob, meta); - } - - @Test public void execute_method_adds_headers() throws HttpException, IOException { - HttpMethod mockHttpMethod = mock(HttpMethod.class); - meta.setHeader("p", "v"); - - impl.executeMethod(null, null, mockHttpMethod, meta); - - verify(mockHttpClient).executeMethod(mockHttpMethod); - verify(mockHttpMethod).setRequestHeader("p", "v"); - } - - @Test public void execute_method_adds_query_params() throws HttpException, IOException { - HttpMethod mockHttpMethod = mock(HttpMethod.class); - meta.setQueryParam("p", "v"); - - impl.executeMethod(null, null, mockHttpMethod, meta); - - verify(mockHttpClient).executeMethod(mockHttpMethod); - verify(mockHttpMethod).setQueryString(contains("p=v")); - } - - @Test public void execute_method_without_stream_response_closes_connection() throws IOException { - HttpMethod mockHttpMethod = mock(HttpMethod.class); - - impl.executeMethod(null, null, mockHttpMethod, meta); - - verify(mockHttpMethod).releaseConnection(); - } - - @Test public void execute_method_with_stream_response_returns_null_body() throws IOException { - HttpMethod mockHttpMethod = mock(HttpMethod.class); - - HttpResponse r = impl.executeMethod(null, null, mockHttpMethod, meta, true); - - assertNull(r.getBody()); - } - - @Test public void execute_method_with_stream_response_doesnt_consume_stream_or_close_connection() throws IOException { - HttpMethod mockHttpMethod = mock(HttpMethod.class); - - impl.executeMethod(null, null, mockHttpMethod, meta, true); - - verify(mockHttpMethod, never()).getResponseBody(); - verify(mockHttpMethod, never()).getResponseBodyAsString(); - verify(mockHttpMethod, never()).releaseConnection(); - } - - @Test public void execute_method_defers_exceptions_to_toss() throws HttpException, IOException { - HttpMethod mockHttpMethod = mock(HttpMethod.class); - - impl = spy(impl); - doReturn(null).when(impl).toss(any(RiakIORuntimeException.class)); - when(mockHttpClient.executeMethod(any(HttpMethod.class))).thenThrow(new IOException()).thenThrow(new HttpException()); - - impl.executeMethod(null, null, mockHttpMethod, meta); - impl.executeMethod(null, null, mockHttpMethod, meta); - - verify(impl, times(2)).toss(any(RiakIORuntimeException.class)); - } - - @Test(expected=RiakIORuntimeException.class) public void toss_throws_io_exception_if_no_exception_handler_installed() { - impl.toss(new RiakIORuntimeException()); - } - @Test(expected=RiakResponseRuntimeException.class) public void toss_throws_response_exception_if_no_exception_handler_installed() { - impl.toss(new RiakResponseRuntimeException(null)); - } - - @Test public void toss_doesnt_throw_if_exception_handler_installed() { - impl.setExceptionHandler(mock(RiakExceptionHandler.class)); - impl.toss(new RiakIORuntimeException()); - impl.toss(new RiakResponseRuntimeException(null)); - } - - private Answer pathVerifier(final String pathSuffix) { - return new Answer() { - public Integer answer(InvocationOnMock invocation) throws Throwable { - String path = ((HttpMethod) invocation.getArguments()[0]).getURI().getPath(); - assertTrue("URL path should end with " + pathSuffix + " but was '" + path + "'", path.endsWith(pathSuffix) || path.endsWith(pathSuffix + "?")); - return 0; - } - }; - } -} diff --git a/src/test/java/com/basho/riak/client/util/TestClientUtils.java b/src/test/java/com/basho/riak/client/util/TestClientUtils.java deleted file mode 100644 index b84fd8405..000000000 --- a/src/test/java/com/basho/riak/client/util/TestClientUtils.java +++ /dev/null @@ -1,385 +0,0 @@ -/* - * 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.util; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import java.io.UnsupportedEncodingException; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpMethodRetryHandler; -import org.apache.commons.httpclient.params.HttpClientParams; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import com.basho.riak.client.RiakClient; -import com.basho.riak.client.RiakConfig; -import com.basho.riak.client.RiakLink; -import com.basho.riak.client.RiakObject; - -public class TestClientUtils { - - @Mock RiakClient mockRiakClient; - - RiakConfig config = new RiakConfig(); - - @Test public void newHttpClient_uses_configs_http_client_if_exists() { - MockitoAnnotations.initMocks(this); - - HttpClient httpClient = new HttpClient(); - config.setHttpClient(httpClient); - assertSame(httpClient, ClientUtils.newHttpClient(config)); - } - - @Test public void newHttpClient_sets_max_total_connections() { - final int maxConnections = 11; - config.setMaxConnections(maxConnections); - - HttpClient httpClient = ClientUtils.newHttpClient(config); - - assertEquals(maxConnections, httpClient.getHttpConnectionManager().getParams().getMaxTotalConnections()); - } - - @Test public void newHttpClient_sets_connection_timeout() { - final int timeout = 11; - config.setTimeout(timeout); - - HttpClient httpClient = ClientUtils.newHttpClient(config); - - assertEquals(timeout, httpClient.getParams().getConnectionManagerTimeout()); - assertEquals(timeout, httpClient.getParams().getSoTimeout()); - assertEquals(timeout, httpClient.getHttpConnectionManager().getParams().getConnectionTimeout()); - } - - @Test public void newHttpClient_sets_retry_handler() { - final HttpMethodRetryHandler handler = mock(HttpMethodRetryHandler.class); - config.setRetryHandler(handler); - - HttpClient httpClient = ClientUtils.newHttpClient(config); - - assertSame(handler, httpClient.getParams().getParameter(HttpClientParams.RETRY_HANDLER)); - } - - @Test public void makeURI_url_encodes_bucket() { - String url = ClientUtils.makeURI(config, "/"); - assertTrue("Expected bucket to be encoded as %2F in URL" + url, url.endsWith("/%2F")); - - } - - @Test public void makeURI_url_encodes_key() { - String url = ClientUtils.makeURI(config, "b", "/"); - assertTrue("Expected key to be encoded as %2F in URL: " + url, url.endsWith("/b/%2F")); - } - - @Test public void makeURI_prepends_slash_to_extra_if_extra_is_a_path_component() { - String url = ClientUtils.makeURI(config, "b", "k", "path_component"); - assertTrue("Expected a slash before path_component in URL: " + url, url.endsWith("/path_component")); - } - - @Test public void getPathFromURL_masks_full_url() { - String path = ClientUtils.getPathFromUrl("http://host.com:10000/path/to/object"); - assertEquals("/path/to/object", path); - } - - @Test public void getPathFromURL_masks_portless_url() { - String path = ClientUtils.getPathFromUrl("http://host.com/path/to/object"); - assertEquals("/path/to/object", path); - } - - @Test public void getPathFromURL_masks_schemeless_url() { - String path = ClientUtils.getPathFromUrl("host.com:10000/path/to/object"); - assertEquals("/path/to/object", path); - } - - @Test public void getPathFromURL_returns_path_in_path_only_url() { - String path = ClientUtils.getPathFromUrl("/path/to/object"); - assertEquals("/path/to/object", path); - } - - @Test public void getPathFromURL_handles_empty_url() { - String path = ClientUtils.getPathFromUrl(""); - assertEquals("", path); - } - - @Test public void getPathFromURL_handles_null() { - String path = ClientUtils.getPathFromUrl(null); - assertNull(path); - } - - @Test public void unquote_string_removes_surrounding_quotes() { - String s = ClientUtils.unquoteString("\"string\""); - assertFalse("Starting quote should be removed", s.startsWith("\"")); - assertFalse("Trailing quote should be removed", s.endsWith("\"")); - } - - @Test public void unquote_string_unescapes_blackslash_escaped_chars() { - String s = ClientUtils.unquoteString("\"\\\\\\a\\n\""); - assertEquals("\\an", s); - } - - @Test public void unquote_string_handles_trailing_blackslash() { - String s = ClientUtils.unquoteString("\"\\\\\\a\\n\\\""); - assertEquals("\\an\\", s); - } - - @Test public void unquote_string_handles_unquoted_string() { - String s = ClientUtils.unquoteString("\\\\\\a\\n"); - assertEquals("\\an", s); - } - - @Test public void json_opt_string_handles_any_type_and_nulls_without_throwing() throws JSONException { - // optString() is used by jsonObjectAsMap and jsonArrayAsList with the - // assumption that - // it won't throw. Just making sure... - JSONObject jsonObject = new JSONObject(); - JSONArray jsonArray = new JSONArray(); - - assertEquals("", jsonObject.optString("k")); - assertEquals("", jsonArray.optString(0)); - - jsonObject.put("k", new JSONArray()); - jsonArray.put(0, new JSONArray()); - assertEquals("[]", jsonObject.optString("k")); - assertEquals("[]", jsonArray.optString(0)); - - jsonObject.put("k", new JSONObject()); - jsonArray.put(0, new JSONObject()); - assertEquals("{}", jsonObject.optString("k")); - assertEquals("{}", jsonArray.optString(0)); - - jsonObject.put("k", 12); - jsonArray.put(0, 12); - assertEquals("12", jsonObject.optString("k")); - assertEquals("12", jsonArray.optString(0)); - - jsonObject.put("k", true); - jsonArray.put(0, true); - assertEquals("true", jsonObject.optString("k")); - assertEquals("true", jsonArray.optString(0)); - } - - @Test public void json_object_as_map_puts_all_fields_using_opt_string() throws JSONException { - JSONObject json = spy(new JSONObject().put("k1", "v1").put("k2", "v2").put("k3", "v3")); - Map map = ClientUtils.jsonObjectAsMap(json); - - verify(json).optString("k1"); - verify(json).optString("k2"); - verify(json).optString("k3"); - - assertEquals("v1", map.get("k1")); - assertEquals("v2", map.get("k2")); - assertEquals("v3", map.get("k3")); - } - - @Test public void json_array_as_list_adds_all_elements_using_opt_string() { - JSONArray json = spy(new JSONArray().put("v1").put("v2").put("v3")); - List list = ClientUtils.jsonArrayAsList(json); - - verify(json).optString(0); - verify(json).optString(1); - verify(json).optString(2); - - assertEquals("v1", list.get(0)); - assertEquals("v2", list.get(1)); - assertEquals("v3", list.get(2)); - } - - @Test public void parses_null_or_empty_link_header() { - Collection links; - - links = ClientUtils.parseLinkHeader(null); - assertNotNull(links); - assertTrue(links.isEmpty()); - - ClientUtils.parseLinkHeader(""); - assertNotNull(links); - assertTrue(links.isEmpty()); - } - - @Test public void parses_links_from_header() { - // Rely on LinkHeader tests to verify that link parsing actually works - // for other cases (single link, multiple links, malformed, etc) - List links = ClientUtils.parseLinkHeader("; riaktag=t, ; riaktag=\"abc\""); - assertEquals(2, links.size()); - assertEquals(new RiakLink("link", "1", "t"), links.get(0)); - assertEquals(new RiakLink("link", "2", "abc"), links.get(1)); - } - - @Test public void parse_usermeta_handles_null_or_empty_header_set() { - Map usermeta; - - usermeta = ClientUtils.parseUsermeta(null); - assertNotNull(usermeta); - assertTrue(usermeta.isEmpty()); - - usermeta = ClientUtils.parseUsermeta(new HashMap()); - assertNotNull(usermeta); - assertTrue(usermeta.isEmpty()); - } - - @Test public void parse_usermeta_returns_correct_headers() { - @SuppressWarnings("serial") Map usermeta = new HashMap() { - { - put("h1", "v1"); - put("h2", "v2"); - put("X-Riak-Meta-Test", "v"); - } - }; - - usermeta = ClientUtils.parseUsermeta(usermeta); - assertEquals(1, usermeta.size()); - assertEquals("v", usermeta.get("Test")); - } - - @Test public void parse_usermeta_is_case_insensitive() { - @SuppressWarnings("serial") Map usermeta = new HashMap() { - { - put("X-Riak-Meta-Test", "v"); - put("x-riak-meta-Test2", "v2"); - } - }; - - usermeta = ClientUtils.parseUsermeta(usermeta); - assertEquals(2, usermeta.size()); - assertEquals("v", usermeta.get("Test")); - assertEquals("v2", usermeta.get("Test2")); - } - - @Test public void parse_multipart_handles_null_params() { - List objects = ClientUtils.parseMultipart(null, null, null, null, null); - assertNotNull(objects); - } - - @Test public void parse_multipart_returns_correct_riak_and_bucket_and_key() { - Map headers = new HashMap(); - headers.put("content-type", "multipart/mixed; boundary=boundary"); - headers.put("x-riak-vclock", "vclock"); - String body = "\r\n--boundary\r\n" + "\r\n" + "--boundary--"; - - List objects = ClientUtils.parseMultipart(mockRiakClient, "b", "k", headers, body.getBytes()); - assertEquals(1, objects.size()); - assertSame(mockRiakClient, objects.get(0).getRiakClient()); - assertEquals("b", objects.get(0).getBucket()); - assertEquals("k", objects.get(0).getKey()); - } - - @Test public void parse_multipart_returns_all_header_metadata_for_object() { - Map headers = new HashMap(); - headers.put("content-type", "multipart/mixed; boundary=boundary"); - headers.put("x-riak-vclock", "vclock"); - String body = "\r\n--boundary\r\n" + "Content-Type: text/plain\r\n" + "Last-Modified: lastmod\r\n" - + "Link: ; riaktag=t\r\n" + "ETag: vtag\r\n" + "X-Riak-Meta-Test: value\r\n" + "\r\n" - + "--boundary--"; - - List objects = ClientUtils.parseMultipart(mockRiakClient, "b", "k", headers, body.getBytes()); - - assertEquals(1, objects.get(0).numLinks()); - assertTrue(objects.get(0).hasLink(new RiakLink("b", "l", "t"))); - - assertEquals(1, objects.get(0).numUsermetaItems()); - assertEquals("value", objects.get(0).getUsermetaItem("test")); - - assertEquals("text/plain", objects.get(0).getContentType()); - assertEquals("vclock", objects.get(0).getVclock()); - assertEquals("lastmod", objects.get(0).getLastmod()); - assertEquals("vtag", objects.get(0).getVtag()); - } - - @Test public void parse_multipart_returns_value() { - Map headers = new HashMap(); - headers.put("content-type", "multipart/mixed; boundary=boundary"); - headers.put("x-riak-vclock", "vclock"); - String body = "\r\n--boundary\r\n" + "\r\n" + "foo\r\n" + "--boundary--"; - - List objects = ClientUtils.parseMultipart(mockRiakClient, "b", "k", headers, body.getBytes()); - assertEquals("foo", objects.get(0).getValue()); - } - - // Rely on Multipart tests to verify that multipart parsing actually works - - @Test public void join_handles_null_array() { - assertNull(ClientUtils.join(null, null)); - } - - @Test public void join_handles_empty_array() { - assertNull(ClientUtils.join(new String[0], null)); - } - - @Test public void join_handles_one_element_array() { - assertEquals("x", ClientUtils.join(new String[] {"x"}, ",")); - } - - @Test public void join_handles_one_multielement_array() { - assertEquals("x,y,z", ClientUtils.join(new String[] {"x", "y", "z"}, ",")); - } - - @Test public void join_handles_one_multicharacter_delimiter() { - assertEquals("x//y//z", ClientUtils.join(new String[] {"x", "y", "z"}, "//")); - } - - @Test(expected=IllegalArgumentException.class) public void encode_client_id_throws_on_null() { - ClientUtils.encodeClientId((byte[]) null); - } - - @Test(expected=IllegalArgumentException.class) public void encode_client_id_throws_on_too_short_input() { - ClientUtils.encodeClientId("id"); - } - - @Test public void encode_client_id_encodes_first_4_bytes() { - String id = "clientid"; - String encoded = ClientUtils.encodeClientId(id); - - byte[] decoded; - try { - decoded = Base64.decodeBase64(encoded.getBytes("UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException("UTF-8 support required in JVM"); - } - assertEquals(4, decoded.length); - assertEquals(id.getBytes()[0], decoded[0]); - assertEquals(id.getBytes()[1], decoded[1]); - assertEquals(id.getBytes()[2], decoded[2]); - assertEquals(id.getBytes()[3], decoded[3]); - } - - @Test public void random_client_id_returns_4_encoded_bytes() { - String encoded = ClientUtils.randomClientId(); - byte[] decoded; - try { - decoded = Base64.decodeBase64(encoded.getBytes("UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException("UTF-8 support required in JVM"); - } - assertEquals(4, decoded.length); - } - - @Test public void random_client_id_returns_different_ids() { - String id1 = ClientUtils.randomClientId(); - String id2 = ClientUtils.randomClientId(); - assertNotNull(id1); - assertNotNull(id2); - assertFalse(id1.equals(id2)); - } - -} diff --git a/src/test/java/com/basho/riak/client/util/TestCollectionWrapper.java b/src/test/java/com/basho/riak/client/util/TestCollectionWrapper.java deleted file mode 100644 index b540d2827..000000000 --- a/src/test/java/com/basho/riak/client/util/TestCollectionWrapper.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.util; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Random; - -import org.junit.Before; -import org.junit.Test; - -public class TestCollectionWrapper { - - final static int MAX_ELS = 10; - List els = new ArrayList(); - CollectionWrapper impl; - - @Before public void setup() { - impl = spy(new CollectionWrapperStub()); - } - - @Test public void add_adds_to_collection() { - final String el = "element"; - impl.add(el); - assertSame(el, impl.iterator().next()); - } - - @Test public void iterator_returns_all_elements() { - Iterator iter = impl.iterator(); - for (int i = 0; i < MAX_ELS; i++) { - String s = iter.next(); - assertEquals(els.get(i), s); - } - assertArrayEquals(els.toArray(), impl.toArray()); - } - - @Test public void to_array_returns_all_elements() { - Object[] s = impl.toArray(); - assertArrayEquals(els.toArray(), s); - } - - class CollectionWrapperStub extends CollectionWrapper { - @Override protected boolean cacheNext() { - if (els.size() >= MAX_ELS) - return false; - byte[] bytes = new byte[10]; - new Random().nextBytes(bytes); - String el = new String(bytes); - els.add(el); - impl.cache(el); - return true; - } - @Override protected void closeBackend() { } - } -} diff --git a/src/test/java/com/basho/riak/client/util/TestLinkHeader.java b/src/test/java/com/basho/riak/client/util/TestLinkHeader.java deleted file mode 100644 index ec7e5dfed..000000000 --- a/src/test/java/com/basho/riak/client/util/TestLinkHeader.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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.util; - -import static org.junit.Assert.*; - -import java.util.Map; - -import org.junit.Test; - -public class TestLinkHeader { - - @Test public void parses_null_and_empty_headers() { - String header = null; - Map> links = LinkHeader.parse(header); - assertEquals(0, links.size()); - - header = ""; - links = LinkHeader.parse(header); - assertEquals(0, links.size()); - - header = " "; - links = LinkHeader.parse(header); - assertEquals(0, links.size()); - } - - @Test public void ignores_malformed_header_components() { - final String header = ", ;>>abc, "; - Map> links = LinkHeader.parse(header); - assertEquals(2, links.size()); - assertNotNull(links.get("/res")); - assertNotNull(links.get("/res2")); - } - - @Test public void ignores_empty_elements() { - final String header = ",; param=value, ,, ,"; - Map> links = LinkHeader.parse(header); - - assertEquals(2, links.size()); - - assertNotNull(links.get("/res")); - assertEquals(1, links.get("/res").keySet().size()); - assertEquals("value", links.get("/res").get("param")); - - assertNotNull(links.get("/res2")); - assertEquals(0, links.get("/res2").keySet().size()); - } - - @Test public void parses_one_link_with_one_parameter() { - final String header = "; param=value"; - Map> links = LinkHeader.parse(header); - assertEquals(1, links.size()); - assertNotNull(links.get("/res")); - assertEquals("value", links.get("/res").get("param")); - } - - @Test public void parses_multiple_links() { - final String header = "; param=value, ; param2=value2, ; param3=value3"; - Map> links = LinkHeader.parse(header); - assertEquals(3, links.size()); - assertNotNull(links.get("/res")); - assertNotNull(links.get("/res2")); - assertNotNull(links.get("/another/res")); - assertEquals("value", links.get("/res").get("param")); - assertEquals("value2", links.get("/res2").get("param2")); - assertEquals("value3", links.get("/another/res").get("param3")); - } - - @Test public void handles_varying_whitespace() { - final String header = ";param=value;param2=value2,; param3=value3; param4=value4, "; - Map> links = LinkHeader.parse(header); - assertEquals(3, links.size()); - assertNotNull(links.get("/res")); - assertNotNull(links.get("/res2")); - assertNotNull(links.get("/another/res")); - assertEquals("value", links.get("/res").get("param")); - assertEquals("value2", links.get("/res").get("param2")); - assertEquals("value3", links.get("/res2").get("param3")); - assertEquals("value4", links.get("/res2").get("param4")); - assertEquals(0, links.get("/another/res").keySet().size()); - } - - @Test public void handles_malformed_links() { - String header = "; param=value"; - Map> links = LinkHeader.parse(header); - assertEquals(0, links.size()); - - header = "<>/res>; param=value"; - links = LinkHeader.parse(header); - assertEquals(0, links.size()); - - header = "<; param=value"; - links = LinkHeader.parse(header); - assertEquals(0, links.size()); - } - - @Test public void parses_link_with_no_parameters() { - String header = ""; - Map> links = LinkHeader.parse(header); - assertEquals(1, links.size()); - assertEquals(0, links.get("/res").keySet().size()); - } - - @Test public void parses_link_with_multiple_parameters() { - String header = "; param1=value1; param2=value2; param3=value3"; - Map> links = LinkHeader.parse(header); - assertEquals(1, links.size()); - assertEquals(3, links.get("/res").keySet().size()); - assertEquals("value1", links.get("/res").get("param1")); - assertEquals("value2", links.get("/res").get("param2")); - assertEquals("value3", links.get("/res").get("param3")); - } - - @Test public void parses_link_with_quoted_parameter() { - String header = "; param1=\"\\\"\\value1\\\"\""; // backslash escaped quote and 'v' in quoted string - Map> links = LinkHeader.parse(header); - assertEquals(1, links.size()); - assertEquals("\"value1\"", links.get("/res").get("param1")); - } - - @Test public void maps_valueless_parameter_to_null() { - final String header = "; param"; - Map> links = LinkHeader.parse(header); - - assertTrue(links.get("/res").keySet().contains("param")); - assertEquals(null, links.get("/res").get("param")); - } - - @Test public void ignores_links_with_malformed_parameters() { - String header = "; param=, "; - Map> links = LinkHeader.parse(header); - assertEquals(1, links.size()); - assertNotNull(links.get("/res2")); - - header = "; @, "; - links = LinkHeader.parse(header); - assertEquals(1, links.size()); - assertNotNull(links.get("/res2")); - - header = "; \"param\", "; - links = LinkHeader.parse(header); - assertEquals(1, links.size()); - assertNotNull(links.get("/res2")); -} -} diff --git a/src/test/java/com/basho/riak/client/util/TestMultipart.java b/src/test/java/com/basho/riak/client/util/TestMultipart.java deleted file mode 100644 index fd36dbf3d..000000000 --- a/src/test/java/com/basho/riak/client/util/TestMultipart.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * 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.util; - -import org.junit.Test; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -public class TestMultipart { - - @Test public void null_result_if_not_multipart_message() { - Map headers = new HashMap(); - headers.put("content-type", "text/plain"); - String body = "abc"; - assertNull(Multipart.parse(headers, body.getBytes())); - } - - @Test public void null_result_if_not_content_type_missing_boundary_parameter() { - Map headers = new HashMap(); - headers.put("content-type", "multipart/mixed"); - String body = "\r\n--boundary\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "subpart\r\n" + "--boundary--"; - - assertNull(Multipart.parse(headers, body.getBytes())); - } - - @Test public void parses_multipart_with_1_empty_part() { - Map headers = new HashMap(); - headers.put("content-type", "multipart/mixed; boundary=boundary"); - String body = "\r\n--boundary\r\n" + "\r\n" + "--boundary--"; - - List parts = Multipart.parse(headers, body.getBytes()); - assertEquals(1, parts.size()); - assertEquals(0, parts.get(0).getHeaders().size()); - assertEquals("", parts.get(0).getBodyAsString()); - } - - @Test public void parses_multipart_with_1_part() { - Map headers = new HashMap(); - headers.put("content-type", "multipart/mixed; boundary=boundary"); - String body = "\r\n--boundary\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "subpart\r\n" + "--boundary--"; - - List parts = Multipart.parse(headers, body.getBytes()); - assertEquals(1, parts.size()); - assertEquals(1, parts.get(0).getHeaders().size()); - assertEquals("text/plain", parts.get(0).getHeaders().get(Constants.HDR_CONTENT_TYPE)); - assertEquals("subpart", parts.get(0).getBodyAsString()); - } - - @Test public void parses_multipart_with_invalid_utf8_binary_part() { - Map headers = new HashMap(); - headers.put("content-type", "multipart/mixed; boundary=boundary"); - - /* 00000000 0d 0a 2d 2d 62 6f 75 6e 64 61 72 79 0d 0a 43 6f |..--boundary..Co| - * 00000010 6e 74 65 6e 74 2d 54 79 70 65 3a 20 74 65 78 74 |ntent-Type: text| - * 00000020 2f 70 6c 61 69 6e 0d 0a 0d 0a f0 28 8c 28 0d 0a |/plain.....(.(..| - * 00000030 2d 2d 62 6f 75 6e 64 61 72 79 2d 2d |--boundary--| - * 0000003c - */ - byte[] body = new byte[]{ - 13, 10, 45, 45, 98, 111, 117, 110, 100, 97, 114, 121, 13, 10, 67, 111, - 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 32, 116, 101, 120, - 116, 47, 112, 108, 97, 105, 110, 13, 10, 13, 10, - -16, 40, -116, 40, // <- invalid UTF8 sequence, valid binary data - 13, 10, 45, 45, 98, 111, 117, 110, 100, 97, 114, 121, 45, 45 - }; - - byte[] invalidUtf8 = new byte[]{-16, 40, -116, 40}; - - List parts = Multipart.parse(headers, body); - assertEquals(1, parts.size()); - assertEquals(1, parts.get(0).getHeaders().size()); - assertEquals("text/plain", parts.get(0).getHeaders().get(Constants.HDR_CONTENT_TYPE)); - assertArrayEquals(invalidUtf8, parts.get(0).getBody()); - } - - @Test public void parses_multipart_with_multiple_parts() { - Map headers = new HashMap(); - headers.put("content-type", "multipart/mixed; boundary=boundary"); - String body = "\r\n--boundary\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "part1\r\n" + "--boundary\r\n" + "\r\n" - + "part2\r\n" + "--boundary--"; - - List parts = Multipart.parse(headers, body.getBytes()); - assertEquals(2, parts.size()); - assertEquals("part1", parts.get(0).getBodyAsString()); - assertEquals("part2", parts.get(1).getBodyAsString()); - } - - @Test public void parses_multipart_subpart() { - Map headers = new HashMap(); - headers.put("content-type", "multipart/mixed; boundary=boundary"); - String body = "\r\n--boundary\r\n" + "Content-Type: multipart/mixed; boundary=5hgaasMxj1NIcoxJBpWd4j9IuaW\r\n" + "\r\n" - + "--5hgaasMxj1NIcoxJBpWd4j9IuaW\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" - + "subpart1\r\n" + "--5hgaasMxj1NIcoxJBpWd4j9IuaW\r\n" + "Content-Type: application/octet-stream\r\n" - + "\r\n" + "subpart2\r\n" + "--5hgaasMxj1NIcoxJBpWd4j9IuaW--\r\n" + "\r\n" + "--boundary--\r\n"; - - List parts = Multipart.parse(headers, body.getBytes()); - assertEquals(1, parts.size()); - - List subparts = Multipart.parse(parts.get(0).getHeaders(), parts.get(0).getBody()); - assertEquals(2, subparts.size()); - assertEquals("subpart1", subparts.get(0).getBodyAsString()); - assertEquals("subpart2", subparts.get(1).getBodyAsString()); - } - - @Test public void parses_subpart_headers_and_body() { - Map headers = new HashMap(); - headers.put("content-type", "multipart/mixed; boundary=\"boundary\""); - String PART_BODY = "this part has multiple" + "lines of text\r\n"; - String body = "\r\n--boundary\r\n" + "X-Riak-Vclock: a85hYGBgzGDKBVIsLOLmazKYEhnzWBkmzFt4hC8LAA==\r\n" + - "Location: /riak/test/key\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" + PART_BODY + - "\r\n--boundary--"; - - List parts = Multipart.parse(headers, body.getBytes()); - assertEquals(3, parts.get(0).getHeaders().size()); - assertEquals("a85hYGBgzGDKBVIsLOLmazKYEhnzWBkmzFt4hC8LAA==", - parts.get(0).getHeaders().get(Constants.HDR_VCLOCK)); - assertEquals("/riak/test/key", parts.get(0).getHeaders().get(Constants.HDR_LOCATION)); - assertEquals("application/octet-stream", parts.get(0).getHeaders().get(Constants.HDR_CONTENT_TYPE)); - assertEquals(PART_BODY, parts.get(0).getBodyAsString()); - } - - @Test public void handles_quoted_boundary() { - Map headers = new HashMap(); - headers.put("content-type", "multipart/mixed; boundary=\"\\\\\\x\\\"\""); // boundary - // is - // the - // string: - // \x" - String body = "\r\n--\\x\"\r\n" + "\r\n" + "part\r\n" + "--\\x\"--"; - - List parts = Multipart.parse(headers, body.getBytes()); - assertEquals(1, parts.size()); - assertEquals(0, parts.get(0).getHeaders().size()); - assertEquals("part", parts.get(0).getBodyAsString()); - } -} diff --git a/src/test/java/com/basho/riak/client/util/TestOneTokenInputStream.java b/src/test/java/com/basho/riak/client/util/TestOneTokenInputStream.java deleted file mode 100644 index 018cda3b4..000000000 --- a/src/test/java/com/basho/riak/client/util/TestOneTokenInputStream.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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.util; - -import static org.junit.Assert.*; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; - -import org.junit.Test; - -public class TestOneTokenInputStream { - - OneTokenInputStream impl; - - @Test public void stream_ends_at_delimiter_in_middle_of_stream() throws IOException { - String delim = "\r\n--boundary"; - String part1 = "abcdefghijklmnop"; - String part2 = "qrstuvwxyz"; - String body = - part1 + - delim + "\r\n" + - part2; - InputStream stream = new ByteArrayInputStream(body.getBytes()); - impl = new OneTokenInputStream(stream, delim); - - ByteArrayOutputStream os = new ByteArrayOutputStream(); - ClientUtils.copyStream(impl, os); - assertEquals(part1, os.toString()); - } - - @Test public void no_content_if_stream_starts_with_delimiter() throws IOException { - String delim = "\r\n--boundary"; - String body = delim + "abcdef"; - InputStream stream = new ByteArrayInputStream(body.getBytes()); - impl = new OneTokenInputStream(stream, delim); - - assertEquals(-1, impl.read()); - } - - @Test public void stream_ends_at_delimiter_at_end_of_stream() throws IOException { - String delim = "\r\n--boundary"; - String part1 = "abcdefghijklmnop"; - String body = part1 + delim; - InputStream stream = new ByteArrayInputStream(body.getBytes()); - impl = new OneTokenInputStream(stream, delim); - - ByteArrayOutputStream os = new ByteArrayOutputStream(); - ClientUtils.copyStream(impl, os); - assertEquals(part1, os.toString()); - } - - @Test public void stream_ends_at_end_of_stream_when_no_delimiter() throws IOException { - String delim = "\r\n--boundary"; - String part1 = "abcdefghijklmnop"; - String body = part1; - InputStream stream = new ByteArrayInputStream(body.getBytes()); - impl = new OneTokenInputStream(stream, delim); - - ByteArrayOutputStream os = new ByteArrayOutputStream(); - ClientUtils.copyStream(impl, os); - assertEquals(part1, os.toString()); - } -} diff --git a/src/test/java/com/basho/riak/client/util/TestStreamedMultipart.java b/src/test/java/com/basho/riak/client/util/TestStreamedMultipart.java deleted file mode 100644 index a13c20009..000000000 --- a/src/test/java/com/basho/riak/client/util/TestStreamedMultipart.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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.util; - -import static org.junit.Assert.*; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Test; - -import com.basho.riak.client.util.Multipart.Part; - -public class TestStreamedMultipart { - - @SuppressWarnings("serial") final Map headers = new HashMap() {{ - put("content-type", "multipart/mixed; boundary=\"boundary\""); - }}; - final String body = - "\r\n" + - "--boundary\r\n" + - "X-Riak-Vclock: a85hYGBgzGDKBVIsLOLmazKYEhnzWBkmzFt4hC8LAA==\r\n" + - "Location: /riak/test/key\r\n" + - "Content-Type: application/octet-stream\r\n" + - "\r\n" + - "foo\r\n" + - "--boundary\r\n" + - "Content-Type: text/plain\r\n" + - "Header: value1\r\n" + - "\r\n" + - "multiple lines of\n" + - "text in this part\n" + - "\r\n" + - "--boundary with some trailing text on the same line\r\n" + - "Content-Type: text/csv\r\n" + - "Header: value2\r\n" + - "\r\n" + - "baz,\r\n" + - "--boundary--\r\n" + - "postlude\n" + - "\r\n"; - InputStream stream = new ByteArrayInputStream(body.getBytes()); - - StreamedMultipart impl; - - @Test public void no_constructor_exception_on_valid_input() throws EOFException, IOException { - String body = - "preamble\r\n" + - "--boundary\r\n" + - "message\r\n" + - "--boundary--"; - stream = new ByteArrayInputStream(body.getBytes()); - - new StreamedMultipart(headers, stream); - } - - @Test public void no_constructor_exception_when_no_preamble() throws EOFException, IOException { - String body = - "--boundary\r\n" + - "message\r\n" + - "--boundary--"; - stream = new ByteArrayInputStream(body.getBytes()); - - new StreamedMultipart(headers, stream); - } - - @Test (expected=EOFException.class) public void throws_when_initial_boundary_is_missing() throws EOFException, IOException { - String body = - "--wrong\r\n" + - "message\r\n" + - "--wrong--"; - stream = new ByteArrayInputStream(body.getBytes()); - - new StreamedMultipart(headers, stream); - } - - @Test public void finds_all_parts() throws EOFException, IOException { - impl = new StreamedMultipart(headers, stream); - assertNotNull(impl.next()); - assertNotNull(impl.next()); - assertNotNull(impl.next()); - assertNull(impl.next()); - } - - @Test public void correctly_parses_part_headers() throws EOFException, IOException { - Map partHeaders; - impl = new StreamedMultipart(headers, stream); - - partHeaders = impl.next().getHeaders(); - assertEquals(3, partHeaders.keySet().size()); - assertEquals("a85hYGBgzGDKBVIsLOLmazKYEhnzWBkmzFt4hC8LAA==", partHeaders.get("x-riak-vclock")); - assertEquals("/riak/test/key", partHeaders.get("location")); - assertEquals("application/octet-stream", partHeaders.get("content-type")); - - partHeaders = impl.next().getHeaders(); - assertEquals(2, partHeaders.keySet().size()); - assertEquals("text/plain", partHeaders.get("content-type")); - assertEquals("value1", partHeaders.get("header")); - - partHeaders = impl.next().getHeaders(); - assertEquals(2, partHeaders.keySet().size()); - assertEquals("text/csv", partHeaders.get("content-type")); - assertEquals("value2", partHeaders.get("header")); - - assertNull(impl.next()); - } - - @Test public void correctly_positions_body_stream_when_all_part_bodies_are_consumed() throws EOFException, IOException { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - impl = new StreamedMultipart(headers, stream); - - os.reset(); - ClientUtils.copyStream(impl.next().getStream(), os); - assertEquals(os.toString(), "foo"); - - os.reset(); - ClientUtils.copyStream(impl.next().getStream(), os); - assertEquals("multiple lines of\ntext in this part\n", os.toString()); - - os.reset(); - ClientUtils.copyStream(impl.next().getStream(), os); - assertEquals("baz,", os.toString()); - - assertNull(impl.next()); - } - - @Test public void correctly_positions_body_stream_when_parts_are_skipped() throws EOFException, IOException { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - impl = new StreamedMultipart(headers, stream); - - Part[] parts = new Part[] { impl.next(), impl.next(), impl.next() }; - assertNull(impl.next()); - - os.reset(); - ClientUtils.copyStream(parts[2].getStream(), os); - assertEquals("baz,", os.toString()); - - os.reset(); - ClientUtils.copyStream(parts[1].getStream(), os); - assertEquals("multiple lines of\ntext in this part\n", os.toString()); - - os.reset(); - ClientUtils.copyStream(parts[0].getStream(), os); - assertEquals("foo", os.toString()); - - } -} diff --git a/src/test/java/com/basho/riak/pbc/TestBucketProperties.java b/src/test/java/com/basho/riak/pbc/TestBucketProperties.java deleted file mode 100644 index be3a11393..000000000 --- a/src/test/java/com/basho/riak/pbc/TestBucketProperties.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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.pbc; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import com.basho.riak.pbc.RPB.RpbBucketProps; -import com.basho.riak.pbc.RPB.RpbBucketProps.Builder; -import com.basho.riak.pbc.RPB.RpbGetBucketResp; - -/** - * @author russell - * - */ -public class TestBucketProperties { - - private static final int N_VAL = 2; - - private BucketProperties bucketProperties; - - @Test public void allFieldsAvailableAndSet() { - final Builder rpbBucketPropsBuilder = RpbBucketProps.newBuilder().setAllowMult(true).setNVal(N_VAL); - final RpbGetBucketResp bucketResponse = RpbGetBucketResp.newBuilder().setProps(rpbBucketPropsBuilder).build(); - - bucketProperties = new BucketProperties(); - bucketProperties.init(bucketResponse); - - assertTrue("allow mult should be true", bucketProperties.getAllowMult()); - assertEquals("nVal incorrect", Integer.valueOf(N_VAL), bucketProperties.getNValue()); - } - - @Test public void noResponseDefaultValues() { - final RpbGetBucketResp bucketResponse = RpbGetBucketResp.getDefaultInstance(); - - bucketProperties = new BucketProperties(); - bucketProperties.init(bucketResponse); - - assertNull("allow mult should be null", bucketProperties.getAllowMult()); - assertNull("nVal should be null", bucketProperties.getNValue()); - } - - @Test public void emptyResponseDefaultValues() { - final RpbBucketProps rpbBucketProps = RpbBucketProps.getDefaultInstance(); - final RpbGetBucketResp bucketResponse = RpbGetBucketResp.newBuilder().setProps(rpbBucketProps).build(); - - bucketProperties = new BucketProperties(); - bucketProperties.init(bucketResponse); - - assertNull("allow mult should be null", bucketProperties.getAllowMult()); - assertNull("nVal should be null", bucketProperties.getNValue()); - } - - @Test public void build() { - bucketProperties = new BucketProperties(); - bucketProperties.allowMult(true); - bucketProperties.nValue(N_VAL); - - final RpbBucketProps rbpBucketProps = bucketProperties.build(); - - assertEquals("built allow multi incorrect", true, rbpBucketProps.getAllowMult()); - assertEquals("nVal incorrect", N_VAL, rbpBucketProps.getNVal()); - } - - @Test public void equalsHashCode() { - final Builder rpbBucketPropsBuilder = RpbBucketProps.newBuilder().setAllowMult(true).setNVal(N_VAL); - final RpbGetBucketResp bucketResponse = RpbGetBucketResp.newBuilder().setProps(rpbBucketPropsBuilder).build(); - - bucketProperties = new BucketProperties(); - bucketProperties.init(bucketResponse); - - assertTrue(bucketProperties.equals(bucketProperties)); - assertFalse(bucketProperties.equals(null)); - assertFalse(bucketProperties.equals("not a bucket properties")); - - final BucketProperties equalProperties = new BucketProperties(); - equalProperties.init(bucketResponse); - - assertTrue(equalProperties.equals(bucketProperties)); - assertTrue(bucketProperties.equals(equalProperties)); - assertEquals(equalProperties.hashCode(), bucketProperties.hashCode()); - - final BucketProperties nullProperties = new BucketProperties(); - nullProperties.init(RpbGetBucketResp.getDefaultInstance()); - - assertFalse(bucketProperties.equals(nullProperties)); - assertFalse(nullProperties.equals(bucketProperties)); - assertFalse(nullProperties.hashCode() == bucketProperties.hashCode()); - - final Builder builder = RpbBucketProps.newBuilder().setAllowMult(false).setNVal(1); - final RpbGetBucketResp response = RpbGetBucketResp.newBuilder().setProps(builder).build(); - - final BucketProperties differentProperties = new BucketProperties(); - differentProperties.init(response); - assertFalse(bucketProperties.equals(differentProperties)); - assertFalse(differentProperties.equals(bucketProperties)); - assertFalse(differentProperties.hashCode() == bucketProperties.hashCode()); - } - -} diff --git a/src/test/java/com/basho/riak/pbc/TestRequestMeta.java b/src/test/java/com/basho/riak/pbc/TestRequestMeta.java deleted file mode 100644 index f51a8ffe8..000000000 --- a/src/test/java/com/basho/riak/pbc/TestRequestMeta.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.pbc; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -/** - * @author russell - * - */ -public class TestRequestMeta { - - @Test - public void builderIsPopulatedFromSetterValues() { - final int dw = 2; - final int w = 3; - final boolean returnBody = true; - final String contentType = "application/json"; - final RPB.RpbPutReq.Builder builder = RPB.RpbPutReq.newBuilder(); - - IRequestMeta requestMeta = new RequestMeta(); - requestMeta.dw(dw).w(w).returnBody(returnBody).contentType(contentType); - - assertEquals("Content type value does not match", contentType, requestMeta.getContentType().toStringUtf8()); - - requestMeta.preparePut(builder); - - assertEquals(dw, builder.getDw()); - assertEquals(w, builder.getW()); - assertEquals(returnBody, builder.getReturnBody()); - } - - @Test - public void nullsGetPrimativeDefaults() { - final RPB.RpbPutReq.Builder builder = RPB.RpbPutReq.newBuilder(); - - IRequestMeta requestMeta = new RequestMeta(); - - requestMeta.preparePut(builder); - - assertEquals(0, builder.getDw()); - assertEquals(0, builder.getW()); - assertEquals(false, builder.getReturnBody()); - } -} \ No newline at end of file diff --git a/src/test/java/com/basho/riak/pbc/TestRiakLink.java b/src/test/java/com/basho/riak/pbc/TestRiakLink.java deleted file mode 100644 index b5bb36031..000000000 --- a/src/test/java/com/basho/riak/pbc/TestRiakLink.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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.pbc; - -import static com.basho.riak.test.util.ExpectedValues.BS_BUCKET; -import static com.basho.riak.test.util.ExpectedValues.BS_KEY; -import static com.basho.riak.test.util.ExpectedValues.BS_TAG; -import static com.basho.riak.test.util.ExpectedValues.BUCKET; -import static com.basho.riak.test.util.ExpectedValues.KEY; -import static com.basho.riak.test.util.ExpectedValues.TAG; -import static com.basho.riak.test.util.ExpectedValues.rpbLinks; -import static org.junit.Assert.assertEquals; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.junit.Test; - -import com.basho.riak.pbc.RPB.RpbLink; -import com.google.protobuf.ByteString; - -/** - * @author russell - * - */ -public class TestRiakLink { - - @Test public void fromRpbLink() { - final RPB.RpbLink rpbLink = RPB.RpbLink.newBuilder().setBucket(BS_BUCKET).setKey(BS_KEY).setTag(BS_TAG).build(); - final RiakLink link = new RiakLink(rpbLink); - - assertBasicValues(link); - } - - @Test public void fromByteStrings() { - final RiakLink link = new RiakLink(BS_BUCKET, BS_KEY, BS_TAG); - - assertBasicValues(link); - } - - @Test public void fromStrings() { - final RiakLink link = new RiakLink(BUCKET, KEY, TAG); - - assertBasicValues(link); - } - - @Test - public void buildsRpbLinkFromState() { - final RiakLink link = new RiakLink(BS_BUCKET, BS_KEY, BS_TAG); - final RpbLink rpbLink = link.build(); - - assertEquals(BS_BUCKET, rpbLink.getBucket()); - assertEquals(BS_KEY, rpbLink.getKey()); - assertEquals(BS_TAG, rpbLink.getTag()); - } - - private static void assertBasicValues(final RiakLink riakLink) { - assertEquals(BS_BUCKET, riakLink.getBucket()); - assertEquals(BS_KEY, riakLink.getKey()); - assertEquals(BS_TAG, riakLink.getTag()); - } - - @Test - public void decode() { - final int numLinks = 10; - final List rpbLinks = rpbLinks(numLinks); - - final List decoded = RiakLink.decode(rpbLinks); - - assertEquals(rpbLinks.size(), decoded.size()); - - Set counters = new HashSet(); - - for(RiakLink link : decoded) { - String[] bucketNCnt = splitByteString(link.getBucket()); - String[] keyNCnt = splitByteString(link.getKey()); - String[] tagNCnt = splitByteString(link.getTag()); - - assertEquals(BUCKET, bucketNCnt[0]); - assertEquals(KEY, keyNCnt[0]); - assertEquals(TAG, tagNCnt[0]); - - counters.add(Integer.parseInt(bucketNCnt[1])); - } - assertEquals(numLinks, counters.size()); - } - - private String[] splitByteString(final ByteString bs) { - return bs.toStringUtf8().split("_"); - } - -} diff --git a/src/test/java/com/basho/riak/pbc/TestRiakObject.java b/src/test/java/com/basho/riak/pbc/TestRiakObject.java deleted file mode 100644 index c6cf4b26e..000000000 --- a/src/test/java/com/basho/riak/pbc/TestRiakObject.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * 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.pbc; - -import static com.basho.riak.test.util.ExpectedValues.*; -import static org.junit.Assert.*; - -import java.util.UUID; - -import org.junit.Test; - -import com.basho.riak.pbc.RPB.RpbContent; - - -/** - * @author russell - * - */ -public class TestRiakObject { - - @Test public void constuctFromStrings() { - final RiakObject riakObject = new RiakObject(BUCKET, KEY, CONTENT); - assertBasicValues(riakObject); - } - - @Test public void constuctFromByteStrings() { - final RiakObject riakObject = new RiakObject(BS_BUCKET , BS_KEY, BS_CONTENT); - assertBasicValues(riakObject); - } - - @Test public void constuctFromStringsAndBytes() { - final RiakObject riakObject = new RiakObject(BUCKET, KEY, CONTENT.getBytes()); - assertBasicValues(riakObject); - } - - @Test public void constuctFromByteStringsWithVclock() { - final RiakObject riakObject = new RiakObject(BS_VCLOCK, BS_BUCKET, BS_KEY, BS_CONTENT); - assertBasicValues(riakObject, true); - - } - - - @Test public void fromRpbContent() { - final String[] userMetaKeys = {"MetaKey1", "MetaKey2"}; - final String[] userMetaValues = {"MetaValue1", "MetaValue2"}; - - final RpbContent.Builder contentBuilder = RpbContent.newBuilder(); - - contentBuilder.setContentType(BS_JSON_CTYPE); - contentBuilder.setCharset(BS_UTF8_CHARSET); - contentBuilder.setValue(BS_CONTENT); - contentBuilder.setContentEncoding(BS_GZIP_ENC); - contentBuilder.setVtag(BS_VTAG); - contentBuilder.addAllLinks(rpbLinks(10)); - contentBuilder.setLastMod(LAST_MOD); - contentBuilder.setLastModUsecs(LAST_MOD_USEC); - contentBuilder.addAllUsermeta(rpbPairs(userMetaKeys, userMetaValues)); - - final RpbContent content = contentBuilder.build(); - - final RiakObject riakObject = new RiakObject(BS_VCLOCK, BS_BUCKET, BS_KEY, content); - - assertBasicValues(riakObject, true); - - assertEquals(content, riakObject.buildContent()); - } - - - @Test public void addLink() { - final RiakObject riakObject = new RiakObject(BUCKET, KEY, CONTENT); - final RpbContent.Builder contentBuilder = RpbContent.newBuilder(); - - contentBuilder.addAllLinks(rpbLinks(2)).setValue(BS_CONTENT); - - final RpbContent content = contentBuilder.build(); - - riakObject.addLink(concatToByteString(TAG, 0), concatToByteString(BUCKET, 0), concatToByteString(KEY, 0)); - riakObject.addLink(TAG + "_1", BUCKET + "_1", KEY + "_1"); - - assertEquals(content, riakObject.buildContent()); - } - - @Test public void setContenType() { - final RiakObject riakObject = new RiakObject(BUCKET, KEY, CONTENT); - final RpbContent.Builder contentBuilder = RpbContent.newBuilder(); - - contentBuilder.setValue(BS_CONTENT).setContentType(BS_JSON_CTYPE); - - riakObject.setContentType(JSON_CTYPE); - - assertEquals(contentBuilder.build(), riakObject.buildContent()); - } - - private static void assertBasicValues(final RiakObject riakObject) { - assertBasicValues(riakObject, false); - } - - private static void assertBasicValues(final RiakObject riakObject, boolean hasVClock) { - assertEquals(BUCKET, riakObject.getBucket()); - assertEquals(BS_BUCKET, riakObject.getBucketBS()); - assertEquals(KEY, riakObject.getKey()); - assertEquals(BS_KEY, riakObject.getKeyBS()); - assertEquals(BS_CONTENT, riakObject.getValue()); - - if(hasVClock) { - assertEquals(BS_VCLOCK, riakObject.getVclock()); - } else { - assertNull("Expected null vclock", riakObject.getVclock()); - } - } - - @Test public void modifyLinksAndBuildContentConcurrently() throws InterruptedException { - final RiakObject riakObject = new RiakObject(BS_VCLOCK, BS_BUCKET, BS_KEY, BS_CONTENT); - final int cnt = 20; - - Thread[] threads = new Thread[cnt]; - - for (int i = 0; i < cnt; i++) { - threads[i] = new Thread(new Runnable() { - - public void run() { - String bucket = UUID.randomUUID().toString(); - String key = UUID.randomUUID().toString(); - String tag = UUID.randomUUID().toString(); - int cnt = 0; - while (true) { - riakObject.addLink(bucket + cnt, key + cnt, tag + cnt); - cnt++; - try { - Thread.sleep(10); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - } - }); - threads[i].setDaemon(true); - threads[i].start(); - } - - Thread.sleep(500); - - riakObject.buildContent(); - } - -} diff --git a/src/test/java/com/basho/riak/pbc/itest/ITestBasic.java b/src/test/java/com/basho/riak/pbc/itest/ITestBasic.java deleted file mode 100644 index d2bdac1f7..000000000 --- a/src/test/java/com/basho/riak/pbc/itest/ITestBasic.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * 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.pbc.itest; - -import static com.basho.riak.client.Hosts.RIAK_HOST; -import static com.basho.riak.client.Hosts.RIAK_PORT; -import static com.google.protobuf.ByteString.copyFromUtf8; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.Map; -import java.util.UUID; -import java.util.prefs.Preferences; - -import org.junit.Test; - -import com.basho.riak.client.util.Constants; -import com.basho.riak.pbc.BucketProperties; -import com.basho.riak.pbc.RiakClient; -import com.basho.riak.pbc.RiakObject; -import com.google.protobuf.ByteString; - -/** - * Assumes Riak is reachable at {@link com.basho.riak.client.Hosts#RIAK_HOST }. - * @author russell - * @see com.basho.riak.client.Hosts#RIAK_HOST - */ -public class ITestBasic { - - private static final String BUCKET = "__itest_java_pbc__"; - - /* - * PING - */ - - @Test public void ping() throws Exception { - final RiakClient c = new RiakClient(RIAK_HOST, RIAK_PORT); - c.ping(); - } - - /* - * BUCKET PROPS - */ - - @Test public void bucketProperties() throws Exception { - final RiakClient c = new RiakClient(RIAK_HOST, RIAK_PORT); - - BucketProperties bucketInfo = new BucketProperties(); - bucketInfo.allowMult(true); - bucketInfo.nValue(2); - - c.setBucketProperties(copyFromUtf8(BUCKET), bucketInfo); - - assertEquals(bucketInfo, c.getBucketProperties(copyFromUtf8(BUCKET))); - - //change them - bucketInfo.allowMult(false); - bucketInfo.nValue(3); - - c.setBucketProperties(copyFromUtf8(BUCKET), bucketInfo); - - assertEquals(bucketInfo, c.getBucketProperties(copyFromUtf8(BUCKET))); - } - - /* - * CLIENT ID - */ - - @Test public void prepareClientId() throws Exception { - Preferences prefs = Preferences.userNodeForPackage(RiakClient.class); - prefs.clear(); - - String clid = prefs.get("client_id", null); - assertNull(clid); - - final RiakClient c = new RiakClient(RIAK_HOST, RIAK_PORT); - c.prepareClientID(); - - clid = prefs.get("client_id", null); - assertNotNull(clid); - } - - @Test public void clientId() throws Exception { - final String clientId = "HUPA_PUPA"; - final RiakClient c = new RiakClient(RIAK_HOST, RIAK_PORT); - - c.setClientID(clientId); - assertEquals(clientId.substring(0, Constants.RIAK_CLIENT_ID_LENGTH), c.getClientID()); - } - - @Test public void clientIdExpiredConnection() throws Exception { - final String clientId = "HUPA_PUPA"; - final RiakClient c = new RiakClient(RIAK_HOST, RIAK_PORT); - - c.setClientID(clientId); - - Thread.sleep(1500); - - assertEquals(clientId.substring(0, Constants.RIAK_CLIENT_ID_LENGTH), c.getClientID()); - } - - @Test public void tooShortClientId() throws Exception { - final String clientId = "HUP"; - final RiakClient c = new RiakClient(RIAK_HOST, RIAK_PORT); - - try { - c.setClientID(clientId); - fail("Expected IllegalArgumentException"); - }catch(IllegalArgumentException e) { - //NO-OP - } - } - - @Test public void tooLongClientId() throws Exception { - final String clientId = "HUPA_PUPA"; - final RiakClient c = new RiakClient(RIAK_HOST, RIAK_PORT); - - c.setClientID(copyFromUtf8(clientId)); - assertEquals(clientId.substring(0, Constants.RIAK_CLIENT_ID_LENGTH), c.getClientID()); - } - - /* - * SERVER INFO - */ - - @Test public void getServerInfo() throws Exception { - final RiakClient c = new RiakClient(RIAK_HOST, RIAK_PORT); - - final Map serverInfo = c.getServerInfo(); - - assertTrue(serverInfo.containsKey("node")); - assertTrue(serverInfo.containsKey("server_version")); - } - - /* - * LIST BUCKETS - */ - @Test public void listBuckets() throws Exception { - final String bucket = UUID.randomUUID().toString(); - final String key = UUID.randomUUID().toString(); - final String content = "content"; - final RiakClient c = new RiakClient(RIAK_HOST, RIAK_PORT); - - c.store(new RiakObject(bucket, key, content)); - - final ByteString[] buckets = c.listBuckets(); - - boolean testBucketPresent = false; - - for(ByteString b : buckets) { - if(b.toStringUtf8().equals(bucket)) { - testBucketPresent = true; - break; - } - } - - assertTrue(testBucketPresent); - - c.delete(bucket, key); - } - - /* - * STORE RETRIEVE DELETE etc - */ - - @Test public void storeAndRetrieve() throws Exception { - final String key = "key1"; - final String content = "value1"; - final String updatedContent = "updatedValue1"; - final String nonExistantKey = "non_existant_key"; - final RiakClient c = new RiakClient(RIAK_HOST, RIAK_PORT); - c.prepareClientID(); - RiakObject o = new RiakObject(BUCKET, key, content); - - //empty bucket - for(ByteString k : c.listKeys(copyFromUtf8(BUCKET))) { - c.delete(BUCKET, k.toStringUtf8()); - } - - c.store(o); - RiakObject[] fetched = c.fetch(BUCKET, key); - - assertEquals(1, fetched.length); - o = fetched[0]; - assertEquals(content, o.getValue().toStringUtf8()); - - final RiakObject o2 = new RiakObject(o.getVclock(), o.getBucketBS(), o.getKeyBS(), copyFromUtf8(updatedContent)); - c.store(o2); - - fetched = c.fetch(BUCKET, key, 2); - - assertEquals(1, fetched.length); - assertEquals(updatedContent, fetched[0].getValue().toStringUtf8()); - - //fetch absent object - c.delete(BUCKET, nonExistantKey, 2); - fetched = c.fetch(BUCKET, nonExistantKey); - - assertTrue(fetched.length == 0); - } -} diff --git a/src/test/java/com/basho/riak/pbc/itest/ITestDataLoad.java b/src/test/java/com/basho/riak/pbc/itest/ITestDataLoad.java deleted file mode 100644 index 66f4fb0a8..000000000 --- a/src/test/java/com/basho/riak/pbc/itest/ITestDataLoad.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * 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.pbc.itest; - -import static com.basho.riak.client.Hosts.RIAK_HOST; -import static com.basho.riak.client.Hosts.RIAK_PORT; -import static com.google.protobuf.ByteString.copyFrom; -import static com.google.protobuf.ByteString.copyFromUtf8; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; -import java.util.List; -import java.util.Random; -import java.util.UUID; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Before; -import org.junit.Test; - -import com.basho.riak.pbc.RequestMeta; -import com.basho.riak.pbc.RiakClient; -import com.basho.riak.pbc.RiakObject; -import com.basho.riak.test.util.ExpectedValues; -import com.google.protobuf.ByteString; - -/** - * Assumes Riak is reachable at {@link com.basho.riak.client.Hosts#RIAK_HOST }. - * @see com.basho.riak.client.Hosts#RIAK_HOST - */ -public class ITestDataLoad { - - private static final String BUCKET = "__itest_java_pbc_dataload__"; - final int NUM_VALUES = 10; - final int VALUE_LENGTH = 512; - - byte data[][] = new byte[NUM_VALUES][VALUE_LENGTH]; - - @Before public void setup() { - for (int i = 0; i < 10; i++) { - new Random().nextBytes(data[i]); - } - } - - @Test public void multiDataLoad() throws Exception { - final String bucket = UUID.randomUUID().toString(); - final int NUM_OBJECTS = 200; - int idx = 0; - final RiakClient riak = new RiakClient(RIAK_HOST); - riak.setClientID("PMDL"); - final RiakObject[] objects = new RiakObject[NUM_OBJECTS]; - final RequestMeta meta = new RequestMeta(); - meta.w(3); - meta.dw(2); - - Random rnd = new Random(); - for (int i = 0; i < NUM_OBJECTS; i++) { - String key = "data-load-" + idx; - String value = new String(data[rnd.nextInt(NUM_VALUES)]); - RiakObject o = new RiakObject(bucket, key, value); - objects[i] = o; - idx++; - } - - final List vclocks = Arrays.asList(riak.store(objects, meta)); - - assertEquals(NUM_OBJECTS, vclocks.size()); - - for (RiakObject o : objects) { - RiakObject[] fetched = riak.fetch(bucket, o.getKey()); - assertEquals(1, fetched.length); - RiakObject o2 = fetched[0]; - assertEquals("No match for " + o.getKey(), o.getValue().toStringUtf8(), o2.getValue().toStringUtf8()); - assertTrue("Vclock for " +o2.getKey() + " not present in vclocks array " + o2.getVclock(), vclocks.contains(o2.getVclock())); - } - - for(ByteString key : riak.listKeys(copyFromUtf8(bucket))) { - riak.delete(bucket, key.toStringUtf8()); - } - } - - @Test public void concurrentDataLoad() throws Exception { - final int NUM_THREADS = 5; - final int NUM_OBJECTS = 200; - - final CountDownLatch startLatch = new CountDownLatch(1); - final CountDownLatch endLatch = new CountDownLatch(NUM_THREADS); - - final Thread[] threads = new Thread[NUM_THREADS]; - final AtomicInteger idx = new AtomicInteger(0); - - final RiakClient riak = new RiakClient(RIAK_HOST); - - for (int i = 0; i < threads.length; i++) { - threads[i] = new Thread(new Runnable() { - - public void run() { - try { - startLatch.await(); - - Random rnd = new Random(); - for (int i = 0; i < NUM_OBJECTS / NUM_THREADS; i++) { - String key = "data-load-" + idx.getAndIncrement(); - String value = new String(data[rnd.nextInt(NUM_VALUES)]); - RiakObject[] objects = riak.fetch(BUCKET, key); - RiakObject o = null; - if (objects.length == 0) { - o = new RiakObject(BUCKET, key, value); - } else { - o = new RiakObject(objects[0].getVclock(), objects[0].getBucketBS(), - objects[0].getKeyBS(), copyFromUtf8(value)); - } - - RiakObject result = riak.store(o, new RequestMeta().w(2).returnBody(true))[0]; - assertEquals(o.getBucket(), result.getBucket()); - assertEquals(o.getKey(), result.getKey()); - assertEquals(o.getValue(), result.getValue()); - } - endLatch.countDown(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - }); - threads[i].start(); - } - - startLatch.countDown(); - - endLatch.await(); - } - - @Test public void vclockDoesntExplodeUsingSingleClient() throws Exception { - final RiakClient c = new RiakClient(RIAK_HOST); - c.setClientID("TVCS"); - final Random rnd = new Random(); - final String bucket = UUID.randomUUID().toString(); - final String key = "pbc-test-vclock-size"; - - RiakObject o = new RiakObject(bucket, key, ExpectedValues.CONTENT); - - ByteString originalVclock = c.store(o, new RequestMeta().w(3).returnBody(true))[0].getVclock(); - - for (int i = 0; i < 15; i++) { - o = c.fetch(o.getBucketBS(), o.getKeyBS())[0]; - o = new RiakObject(o.getVclock(), o.getBucketBS(), o.getKeyBS(), copyFrom(data[rnd.nextInt(NUM_VALUES)])); - c.store(o); - } - - assertEquals(originalVclock.size(), o.getVclock().size()); - c.delete(bucket, key); - } -} \ No newline at end of file diff --git a/src/test/java/com/basho/riak/pbc/itest/ITestMapReduce.java b/src/test/java/com/basho/riak/pbc/itest/ITestMapReduce.java deleted file mode 100644 index 8fcd2afce..000000000 --- a/src/test/java/com/basho/riak/pbc/itest/ITestMapReduce.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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.pbc.itest; - -import static com.basho.riak.client.Hosts.RIAK_HOST; -import static com.basho.riak.client.Hosts.RIAK_PORT; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.io.IOException; - -import org.apache.commons.httpclient.HttpException; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; - -import com.basho.riak.pbc.MapReduceResponseSource; -import com.basho.riak.pbc.RiakClient; -import com.basho.riak.pbc.RiakObject; -import com.basho.riak.pbc.mapreduce.ErlangFunction; -import com.basho.riak.pbc.mapreduce.JavascriptFunction; -import com.basho.riak.pbc.mapreduce.MapReduceBuilder; -import com.basho.riak.pbc.mapreduce.MapReduceResponse; - -/** - * Exercises map/reduce features of the Riak client. - * Assumes Riak is reachable at {@link com.basho.riak.client.Hosts#RIAK_HOST }. - * @see com.basho.riak.client.Hosts#RIAK_HOST - */ -public class ITestMapReduce { - - private static final String KEY_PREFIX = "pbc_java_"; - private static final String TAG = "test"; - public static String BUCKET = "pbc_mr_test_java"; - public static int TEST_ITEMS = 200; - - @BeforeClass public static void setup() throws Exception { - final RiakClient c = new RiakClient(RIAK_HOST); - for (int i = 0; i < TEST_ITEMS; i++) { - RiakObject object = new RiakObject(BUCKET, KEY_PREFIX + Integer.toString(i), Integer.toString(i)); - object.setContentType("text/plain"); - - if (i < TEST_ITEMS - 1) { - object.addLink(TAG, BUCKET, KEY_PREFIX + Integer.toString(i + 1)); - } - c.store(object); - } - } - - @AfterClass public static void teardown() throws Exception { - final RiakClient c = new RiakClient(RIAK_HOST); - - for (int i = 0; i < TEST_ITEMS; i++) { - c.delete(BUCKET, KEY_PREFIX + Integer.toString(i)); - } - } - - @Test public void doLinkMapReduce() throws HttpException, IOException, JSONException { - final RiakClient c = new RiakClient(RIAK_HOST); - - MapReduceBuilder mrb = new MapReduceBuilder(c) - .setBucket(BUCKET).link(BUCKET, TAG, false) - .map(JavascriptFunction.named("Riak.mapValuesJson"), false) - .reduce(new ErlangFunction("riak_kv_mapreduce", "reduce_sort"), true); - - MapReduceResponseSource response = mrb.submit(); - - assertTrue(response.hasNext()); - - MapReduceResponse mrr = response.next(); - JSONArray results = mrr.getJSON(); - assertEquals(TEST_ITEMS - 1, results.length()); - } - - @Test public void doErlangMapReduce() throws HttpException, IOException, JSONException { - RiakClient c = new RiakClient(RIAK_HOST); - MapReduceBuilder builder = new MapReduceBuilder(c); - builder.setBucket(BUCKET); - builder.map(new ErlangFunction("riak_kv_mapreduce", "map_object_value"), false); - builder.reduce(new ErlangFunction("riak_kv_mapreduce", "reduce_string_to_integer"), false); - builder.reduce(new ErlangFunction("riak_kv_mapreduce", "reduce_sort"), true); - MapReduceResponseSource response = builder.submit(); - assertTrue(response.hasNext()); - JSONArray results = response.next().getJSON(); - assertEquals(TEST_ITEMS, results.length()); - assertEquals(0, results.getInt(0)); - assertEquals(73, results.getInt(73)); - assertEquals(197, results.getInt(197)); - } - - @Test public void doJavascriptMapReduce() throws HttpException, IOException, JSONException { - RiakClient c = new RiakClient(RIAK_HOST); - MapReduceBuilder builder = new MapReduceBuilder(c); - builder.setBucket(BUCKET); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), false); - builder.reduce(JavascriptFunction.named("Riak.reduceNumericSort"), true); - MapReduceResponseSource response = builder.submit(); - assertTrue(response.hasNext()); - JSONArray results = response.next().getJSON(); - assertEquals(TEST_ITEMS, results.length()); - assertEquals(0, results.getInt(0)); - assertEquals(73, results.getInt(73)); - assertEquals(197, results.getInt(197)); - } - - @Test public void doJavascriptMapReduceFromJSON() throws HttpException, IOException, JSONException { - RiakClient c = new RiakClient(RIAK_HOST); - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket(BUCKET); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), false); - builder.reduce(JavascriptFunction.named("Riak.reduceNumericSort"), true); - JSONObject mrJob = builder.toJSON(); - - MapReduceResponseSource response = c.mapReduce(mrJob); - assertTrue(response.hasNext()); - JSONArray results = response.next().getJSON(); - assertEquals(TEST_ITEMS, results.length()); - assertEquals(0, results.getInt(0)); - assertEquals(73, results.getInt(73)); - assertEquals(197, results.getInt(197)); - } -} diff --git a/src/test/java/com/basho/riak/pbc/itest/Utils.java b/src/test/java/com/basho/riak/pbc/itest/Utils.java deleted file mode 100644 index b65828ad6..000000000 --- a/src/test/java/com/basho/riak/pbc/itest/Utils.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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.pbc.itest; - -import static org.junit.Assert.*; - -import org.apache.commons.httpclient.URIException; - -import com.basho.riak.client.request.RequestMeta; -import com.basho.riak.client.response.HttpResponse; - -public class Utils { - - public static RequestMeta WRITE_3_REPLICAS() { return RequestMeta.writeParams(3, 3); } - - public static void assertSuccess(HttpResponse response) { - if (!response.isSuccess()) { - StringBuilder msg = new StringBuilder("Failed "); - msg.append(response.getHttpMethod().getName()).append(" "); - try { - msg.append(response.getHttpMethod().getURI().toString()); - } catch (URIException e) { - msg.append(response.getHttpMethod().getPath()); - } - msg.append(" -- ") - .append(response.getHttpMethod().getStatusLine()).append("; ") - .append("Response headers: ").append(response.getHttpHeaders().toString()).append("; ") - .append("Response body: ").append(response.getBody()); - fail(msg.toString()); - } - } - -} diff --git a/src/test/java/com/basho/riak/pbc/mapreduce/TestMapReduceBuilder.java b/src/test/java/com/basho/riak/pbc/mapreduce/TestMapReduceBuilder.java deleted file mode 100644 index 97443056d..000000000 --- a/src/test/java/com/basho/riak/pbc/mapreduce/TestMapReduceBuilder.java +++ /dev/null @@ -1,323 +0,0 @@ -/* - * 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.pbc.mapreduce; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.junit.Test; - -import com.basho.riak.pbc.RiakObject; -import com.basho.riak.test.util.JSONEquals; - -public class TestMapReduceBuilder { - - @Test public void canStoreBucket() { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("foo"); - assertEquals(builder.getBucket(), "foo"); - } - - @Test public void canStoreObjects() { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.addRiakObject("foo", "bar"); - Map> objects = builder.getRiakObjects(); - assertEquals(objects.get("foo").size(), 1); - assertTrue(objects.get("foo").contains("bar")); - // Verify duplicates are not added - builder.addRiakObject("foo", "bar"); - assertEquals(1, builder.getRiakObjects().get("foo").size()); - } - - @Test public void canRemoveObjects() { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.addRiakObject("foo", "bar"); - builder.addRiakObject("foo", "baz"); - assertEquals(2, builder.getRiakObjects().get("foo").size()); - builder.removeRiakObject("foo", "bar"); - assertEquals(1, builder.getRiakObjects().get("foo").size()); - } - - @Test public void nullParamClearsObject() { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.addRiakObject("foo", "bar"); - builder.setRiakObjects((Map>) null); - assertEquals(0, builder.getRiakObjects().size()); - - builder.addRiakObject("foo", "bar"); - builder.setRiakObjects((Collection) null); - assertEquals(0, builder.getRiakObjects().size()); - } - - @Test public void extractsRiakObjectInfo() { - MapReduceBuilder builder = new MapReduceBuilder(); - List riakObjects = Arrays.asList(new RiakObject[] { new RiakObject("foo", "bar", "v"), - new RiakObject("foo", "baz", "v")}); - builder.setRiakObjects(riakObjects); - - Map> objects = builder.getRiakObjects(); - assertEquals(objects.get("foo").size(), 2); - assertTrue(objects.get("foo").contains("bar")); - assertTrue(objects.get("foo").contains("baz")); - } - - @Test(expected=IllegalStateException.class) - public void canUseOnlyObjects() { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.addRiakObject("foo", "bar"); - builder.setBucket("wubba"); - } - - @Test(expected=IllegalStateException.class) - public void canUseOnlyBucket() { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.addRiakObject("foo", "bar"); - } - - @Test(expected=IllegalStateException.class) - public void canUseOnlyBucket_collection() { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - List riakObjects = Arrays.asList(new RiakObject[] { new RiakObject("foo", "bar", "v"), - new RiakObject("foo", "baz", "v")}); - builder.setRiakObjects(riakObjects); - } - - @Test public void canBuildJSMapOnlyJobWithBucket() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), true); - - JSONObject expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":" + - "[{\"map\":{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":true}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - - } - - @Test public void canBuildJSMapReduceJobWithBucket() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(JavascriptFunction.anon("function(v) { return [v]; }"), false); - builder.reduce(JavascriptFunction.named("Riak.reduceMin"), true); - - JSONObject expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"map\":{\"source\":" + - "\"function(v) { return [v]; }\",\"language\":\"javascript\",\"keep\":false}}," + - "{\"reduce\":{\"name\":\"Riak.reduceMin\",\"language\":\"javascript\",\"keep\":true}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - - @Test public void canBuildErlangMapOnlyJobWithBucket() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(new ErlangFunction("foo", "bar"), true); - - JSONObject expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"map\":{\"module\":\"foo\"," + - "\"language\":\"erlang\",\"keep\":true,\"function\":\"bar\"}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @Test public void canBuildErlangMapReduceJobWithBucket() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(new ErlangFunction("foo", "bar"), false); - builder.reduce(new ErlangFunction("baz", "quux"), true); - - JSONObject expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"map\":{\"module\":\"foo\"," + - "\"language\":\"erlang\",\"keep\":false,\"function\":\"bar\"}}," + - "{\"reduce\":{\"module\":\"baz\",\"language\":\"erlang\",\"keep\":true,\"function\":\"quux\"}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @Test public void canBuildJSMapOnlyJobWithObjects() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.addRiakObject("first", "key1"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), true); - - - JSONObject expected = new JSONObject(); - JSONArray inputs = new JSONArray(); - inputs.put(new String[] {"first", "key1"}); - expected.put("inputs", inputs); - JSONArray query = new JSONArray("[{\"map\":{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":true}}]"); - expected.put("query", query); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @Test public void canBuildJSMapReduceJobWithObjects() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.addRiakObject("first", "key2"); - builder.map(JavascriptFunction.anon("function(v) { return [v]; }"), false); - builder.reduce(JavascriptFunction.named("Riak.reduceMin"), true); - - - JSONObject expected = new JSONObject(); - JSONArray inputs = new JSONArray(); - inputs.put(new String[] {"first", "key2"}); - expected.put("inputs", inputs); - JSONArray query = new JSONArray("[{\"map\":{\"source\":" + - "\"function(v) { return [v]; }\",\"language\":\"javascript\",\"keep\":false}}," + - "{\"reduce\":{\"name\":\"Riak.reduceMin\",\"language\":\"javascript\",\"keep\":true}}]"); - expected.put("query", query); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - - @Test public void canBuildErlangMapOnlyJobWithObjects() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.addRiakObject("first", "key1"); - builder.addRiakObject("second", "key1"); - builder.map(new ErlangFunction("foo", "bar"), true); - - JSONObject expected = new JSONObject(); - JSONArray inputs = new JSONArray(); - inputs.put(new String[] {"first", "key1"}); - inputs.put(new String[] {"second", "key1"}); - expected.put("inputs", inputs); - JSONArray query = new JSONArray("[{\"map\":{\"module\":\"foo\",\"language\":\"erlang\",\"keep\":true,\"function\":\"bar\"}}]"); - expected.put("query", query); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @Test public void canBuildErlangMapReduceJobWitObjects() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(new ErlangFunction("foo", "bar"), false); - builder.reduce(new ErlangFunction("baz", "quux"), true); - - JSONObject expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"map\":{\"module\":\"foo\"," + - "\"language\":\"erlang\",\"keep\":false,\"function\":\"bar\"}}," + - "{\"reduce\":{\"module\":\"baz\",\"language\":\"erlang\",\"keep\":true,\"function\":\"quux\"}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @Test public void canBuildLinkMapReduceJob() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.link("foo", false); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), true); - - JSONObject expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"link\":{\"bucket\":\"foo\",\"keep\":false}}," + - "{\"map\":{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":true}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @SuppressWarnings("unchecked") @Test public void canBuildMapJobWithPhaseArg() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), "123", true); - - JSONObject expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"map\":{\"arg\":\"123\"," + - "\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":true}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - - builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), 123, true); - - expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"map\":{\"arg\":123," + - "\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":true}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - - builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - List args = new ArrayList(2); - args.add(123); - args.add("testing"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), args, true); - - expected = new JSONObject("{\"inputs\":\"wubba\"}"); - - JSONObject mapSpec = new JSONObject("{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":true}"); - mapSpec.put("arg", (Object)Arrays.asList(123, "testing")); - JSONObject map = new JSONObject(); - map.put("map", mapSpec); - JSONArray query = new JSONArray(); - query.put(map); - - expected.put("query", query); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } - - @SuppressWarnings("unchecked") @Test public void canBuildMapReduceJobWithPhaseArg() throws JSONException { - MapReduceBuilder builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), false); - builder.reduce(JavascriptFunction.named("Riak.reduceSort"), "testing", true); - - JSONObject expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"map\":{\"name\":\"Riak.mapValuesJson\"," + - "\"language\":\"javascript\",\"keep\":false}},{\"reduce\":{\"arg\":\"testing\"," + - "\"name\":\"Riak.reduceSort\",\"language\":\"javascript\",\"keep\":true}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - - builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), false); - builder.reduce(JavascriptFunction.named("Riak.reduceSort"), 123, true); - - expected = new JSONObject("{\"inputs\":\"wubba\",\"query\":[{\"map\":{\"name\":\"Riak.mapValuesJson\"," + - "\"language\":\"javascript\",\"keep\":false}},{\"reduce\":{\"arg\":123," + - "\"name\":\"Riak.reduceSort\",\"language\":\"javascript\",\"keep\":true}}]}"); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - - - List args = new ArrayList(2); - args.add(123); - args.add("testing"); - builder = new MapReduceBuilder(); - builder.setBucket("wubba"); - builder.map(JavascriptFunction.named("Riak.mapValuesJson"), false); - builder.reduce(new ErlangFunction("riak_kv_mapreduce", "reduce_sort"), args, true); - - expected = new JSONObject("{\"inputs\":\"wubba\"}"); - JSONObject map = new JSONObject("{\"map\":{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\",\"keep\":false}}"); - - JSONObject reduceSpec = new JSONObject("{\"module\":\"riak_kv_mapreduce\",\"language\":\"erlang\",\"keep\":true,\"function\":\"reduce_sort\"}"); - reduceSpec.put("arg", (Object)Arrays.asList(123, "testing")); - JSONObject reduce = new JSONObject(); - reduce.put("reduce", reduceSpec); - - JSONArray query = new JSONArray(); - query.put(map); - query.put(reduce); - expected.put("query", query); - - assertTrue("Generated JSON not as expected", JSONEquals.equals(expected, builder.toJSON())); - } -} \ No newline at end of file diff --git a/src/test/java/com/basho/riak/pbc/mapreduce/TestMapReduceFunctions.java b/src/test/java/com/basho/riak/pbc/mapreduce/TestMapReduceFunctions.java deleted file mode 100644 index 0a2d1f0f9..000000000 --- a/src/test/java/com/basho/riak/pbc/mapreduce/TestMapReduceFunctions.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.pbc.mapreduce; - -import static org.junit.Assert.*; - -import org.json.JSONException; -import org.json.JSONObject; -import org.junit.Test; - -import com.basho.riak.test.util.JSONEquals; - -public class TestMapReduceFunctions { - - @Test public void erlangFunction_generatesCorrectJson() throws JSONException { - ErlangFunction f = new ErlangFunction("testing", "doit"); - JSONObject json = f.toJson(); - - assertEquals("testing", json.get("module")); - assertEquals("erlang", json.get("language")); - assertEquals("doit", json.get("function")); - } - - @Test public void namedJSFunction_generatesCorrectJson() throws JSONException { - JavascriptFunction f = JavascriptFunction.named("Riak.mapValuesJson"); - assertTrue(JSONEquals.equals(new JSONObject("{\"name\":\"Riak.mapValuesJson\",\"language\":\"javascript\"}"), f.toJson())); - } - - @Test public void anonJSFunction_generatesCorrectJson() throws JSONException { - JavascriptFunction f = JavascriptFunction.anon("function(v) { return [v]; }"); - assertTrue(JSONEquals.equals(new JSONObject("{\"source\":\"function(v) { return [v]; }\",\"language\":\"javascript\"}"), f.toJson())); - } - - @Test public void anonJSFunction_escapesQuotes() throws JSONException { - JavascriptFunction f = JavascriptFunction.anon("function(v) { return [{\"value\": v}]; }"); - assertTrue(JSONEquals.equals(new JSONObject("{\"source\":\"function(v) { return [{\\\"value\\\": v}]; }\",\"language\":\"javascript\"}"), f.toJson())); - } - - @Test public void linkFunctionCTORStoresArgs() throws Exception { - LinkFunction f = new LinkFunction("bucket", "tag"); - assertTrue(JSONEquals.equals(new JSONObject("{\"tag\":\"tag\",\"bucket\":\"bucket\"}"), f.toJson())); - } -} diff --git a/src/test/java/com/basho/riak/test/util/ExpectedValues.java b/src/test/java/com/basho/riak/test/util/ExpectedValues.java deleted file mode 100644 index 8a7423025..000000000 --- a/src/test/java/com/basho/riak/test/util/ExpectedValues.java +++ /dev/null @@ -1,100 +0,0 @@ -/** - * - */ -package com.basho.riak.test.util; - -import static com.google.protobuf.ByteString.copyFromUtf8; - -import java.util.ArrayList; -import java.util.List; - -import com.basho.riak.client.util.Constants; -import com.basho.riak.pbc.RPB.RpbLink; -import com.basho.riak.pbc.RPB.RpbPair; -import com.google.protobuf.ByteString; - -/** - * Some constants for test values that crop up time and again. - * - * @author russell - * - */ -public final class ExpectedValues { - - public static final String BUCKET = "bucket"; - public static final String KEY = "key"; - public static final String CONTENT = "content"; - public static final String VCLOCK = "a85hYGBgymDKBVIsrLnh3BlMiYx5rAymfeeO8EGFWRLl30GF/00ACmcBAA=="; - public static final String TAG = "tag"; - public static final String JSON_CTYPE = Constants.CTYPE_JSON; - public static final String UTF8_CHARSET = "UTF-8"; - public static final String GZIP_ENC = "gzip"; - public static final String VTAG = "16vic4eU9ny46o4KPiDz1f"; - public static final int LAST_MOD = 1271442363; - public static final int LAST_MOD_USEC = 105696; - - public static final ByteString BS_BUCKET = copyFromUtf8(BUCKET); - public static final ByteString BS_KEY = copyFromUtf8(KEY); - public static final ByteString BS_CONTENT = copyFromUtf8(CONTENT); - public static final ByteString BS_VCLOCK = copyFromUtf8(VCLOCK); - public static final ByteString BS_TAG = copyFromUtf8(TAG); - public static final ByteString BS_JSON_CTYPE = copyFromUtf8(JSON_CTYPE); - public static final ByteString BS_UTF8_CHARSET = copyFromUtf8(UTF8_CHARSET); - public static final ByteString BS_GZIP_ENC = copyFromUtf8(GZIP_ENC); - public static final ByteString BS_VTAG = copyFromUtf8(VTAG); - - private ExpectedValues() {} - - /** - * Generate a number of RpbLinks, each link has the values - * {@link ExpectedValues#BUCKET} _ n, {@link ExpectedValues#KEY} _ n, {@link ExpectedValues#TAG} _ n - * @param num how many RpbLinks to generate - * @return List of RpbLinks - */ - public static List rpbLinks(int num) { - final int numLinks = num; - final List rpbLinks = new ArrayList(); - - for(int i=0; i < numLinks; i++) { - RpbLink.Builder builder = RpbLink.newBuilder() - .setBucket(concatToByteString(BUCKET, i)) - .setKey(concatToByteString(KEY, i)) - .setTag(concatToByteString(TAG, i)); - rpbLinks.add(builder.build()); - } - - return rpbLinks; - } - - - public static ByteString concatToByteString(String value, int counter) { - return ByteString.copyFromUtf8(value + "_" + counter); - } - - /** - * Create a List of RpbPairs from a pair of String arrays. - * keys and values must be of the same length. - * - * @param keys - * @param values - * @return List of RpbPairs one for each key, value pair - */ - public static List rpbPairs(String[] keys, String[] values) { - if(keys.length != values.length) { - throw new IllegalArgumentException("keys and values must be same length"); - } - - final List pairs = new ArrayList(); - - for(int i=0; i < keys.length; i++ ) { - RpbPair.Builder builder = RpbPair.newBuilder(); - builder.setKey(copyFromUtf8(keys[i])); - builder.setValue(copyFromUtf8(values[i])); - pairs.add(builder.build()); - } - - return pairs; - } - - -} diff --git a/src/test/java/com/basho/riak/test/util/JSONEquals.java b/src/test/java/com/basho/riak/test/util/JSONEquals.java deleted file mode 100644 index 7f3af7f3c..000000000 --- a/src/test/java/com/basho/riak/test/util/JSONEquals.java +++ /dev/null @@ -1,185 +0,0 @@ -/** - * - */ -package com.basho.riak.test.util; - -import java.util.Arrays; -import java.util.Iterator; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Since neither {@link JSONObject} nor {@link JSONArray} provide equals. - * - * Equals is needed since we can't guarantee order of elements in a JSONObject so we can't compare - * JSONObject.toString() with an expected String representation of a JSONObject. - * - * - * @author russell - * - */ -public class JSONEquals { - - private JSONEquals() {} - - /** - * Compare two {@link JSONObject}s for equality. - * - * Warning: This method assumes that the data structure is acyclical (just like {@link JSONObject#toString()} - * - * @param json1 {@link JSONObject} to compare with json2 - * @param json2 {@link JSONObject} to compare with json1 - * @return true if both objects contain exactly the same elements. - * @throws JSONException - */ - public static boolean equals(final JSONObject json1, final JSONObject json2) throws JSONException { - boolean equal = false; - - if(andNull(json1, json2)) { - equal = true; - } else if(xorNull(json1, json2)) { - equal = false; - } else if(json1.equals(json2)) { - equal = true; - } else if (json1.length() != json2.length()) { - equal = false; - } else { - equal = logicallyEqual(json1, json2); - } - - return equal; - } - - /** - * Is only one of the passed objects null? - * @param o1 - * @param o2 - * @return true if either o1 or o2 (and not both) are null - */ - private static boolean xorNull(Object o1, Object o2) { - return o1 == null ^ o2 == null; - } - - /** - * Are both the objects null? - * @param o1 - * @param o2 - * @return true if both o1 and o2 are null - */ - private static boolean andNull(Object o1, Object o2) { - return o1 == null && o2 == null; - } - - /** - * Tests the two {@link JSONObject}s for logical equality (Same elements, same values). - * - * Expects that 2 non null {@link JSONObject}s of the same size. - * @param json1 - * @param json2 - * @return true if both {@link JSONObject}s contain exactly the same elements and they are all equal. - * @throws JSONException - */ - private static boolean logicallyEqual(JSONObject json1, JSONObject json2) throws JSONException { - boolean equal = true; - @SuppressWarnings("rawtypes") Iterator iterator = json1.keys(); - - while(iterator.hasNext()){ - String key = (String)iterator.next(); - - if(!json2.has(key)) { - equal = false; - break; - } - - Object o1 = json1.get(key); - Object o2 = json2.get(key); - - if(!objectsEqual(o1, o2)) { - equal = false; - break; - } - } - - return equal; - } - - /** - * Tests if two objects are equal. If the objects are neither {@link JSONObject} nor {@link JSONArray} - * delegates to o1.equals(o2). - * - * @param o1 - * @param o2 - * @return true if the objects are equal, false otherwise - * @throws JSONException - */ - private static boolean objectsEqual(Object o1, Object o2) throws JSONException { - boolean equal = false; - - if(o1 instanceof JSONObject && o2 instanceof JSONObject) { - equal = JSONEquals.equals((JSONObject)o1, (JSONObject)o2); - } else if(o1 instanceof JSONArray && o2 instanceof JSONArray) { - equal = JSONEquals.equals((JSONArray)o1, (JSONArray)o2); - } else if (o1.getClass().isArray() && o2.getClass().isArray()) { - equal = Arrays.deepEquals((Object[]) o1, (Object[]) o2); - } else { - equal = o1.equals(o2); - } - return equal; - } - - /** - * Compare two {@link JSONArray}s for equality. - * - * Warning: This method assumes that the data structure is acyclical (just like {@link JSONArray#toString()} - * - * @param array1 {@link JSONArray} to compare with array2 - * @param array2 {@link JSONArray} to compare with array1 - * @return true if both objects contain exactly the same elements. - * @throws JSONException - */ - public static boolean equals(final JSONArray array1, final JSONArray array2) throws JSONException { - boolean equal = false; - - if(andNull(array1, array2)) { - equal = true; - } else if(xorNull(array1, array2)) { - equal = false; - } else if(array1.equals(array2)) { - equal = true; - } else if(array1.length() != array2.length()) { - equal = false; - } else { - equal = logicallyEqual(array1, array2); - } - - return equal; - } - - /** - * Tests the two {@link JSONArray}s for logical equality same values in same order. - * - * Expects that 2 non null {@link JSONArray}s of the same size. - * @param array1 - * @param array2 - * @return true if both {@link JSONArray}s contain exactly the same elements in the same order. - * @throws JSONException - */ - private static boolean logicallyEqual(JSONArray array1, JSONArray array2) throws JSONException { - boolean equal = true; - - for(int i=0; i < array1.length(); i++) { - Object o1 = array1.get(i); - Object o2 = array2.get(i); - - if(!objectsEqual(o1, o2)) { - equal = false; - break; - } - } - - return equal; - } - -} diff --git a/src/test/resources/cacert.pem b/src/test/resources/cacert.pem new file mode 100644 index 000000000..ecb610076 --- /dev/null +++ b/src/test/resources/cacert.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDxTCCAy6gAwIBAgIJAIxUkANeHGH0MA0GCSqGSIb3DQEBBAUAMIGeMRswGQYD +VQQKExJUaGUgU2FtcGxlIENvbXBhbnkxEzARBgNVBAsTCmNsaWVudCBkZXYxHTAb +BgkqhkiG9w0BCQEWDnJpYWtAYmFzaG8uY29tMRMwEQYDVQQHEwpNZXRyb3BvbGlz +MREwDwYDVQQIEwhOZXcgWW9yazELMAkGA1UEBhMCVVMxFjAUBgNVBAMTDUJhc2hv +IFJvb3QgQ0EwHhcNMTQwMjI4MjI0NDQ0WhcNMjQwMjI2MjI0NDQ0WjCBnjEbMBkG +A1UEChMSVGhlIFNhbXBsZSBDb21wYW55MRMwEQYDVQQLEwpjbGllbnQgZGV2MR0w +GwYJKoZIhvcNAQkBFg5yaWFrQGJhc2hvLmNvbTETMBEGA1UEBxMKTWV0cm9wb2xp +czERMA8GA1UECBMITmV3IFlvcmsxCzAJBgNVBAYTAlVTMRYwFAYDVQQDEw1CYXNo +byBSb290IENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCeT4HEI4n3wFP5 +vz/Hv3YQNpIWuEc8OjPuLSHjLCjYctp8qsSy1G14sJY5Sot15aFNfFzANRw+bAvs +RJWfMJ82txqIcbj4ChHg/qOrr+lTurygLfqEp+v/0/DhQt/Ul118PYADoorR7a5t +eC/O+UJ+e2pbndkuIvM4zTxz7zFfSQIDAQABo4IBBzCCAQMwDAYDVR0TBAUwAwEB +/zAdBgNVHQ4EFgQUHsffo7pozyaqq6vM3VmwO88lB6EwgdMGA1UdIwSByzCByIAU +Hsffo7pozyaqq6vM3VmwO88lB6GhgaSkgaEwgZ4xGzAZBgNVBAoTElRoZSBTYW1w +bGUgQ29tcGFueTETMBEGA1UECxMKY2xpZW50IGRldjEdMBsGCSqGSIb3DQEJARYO +cmlha0BiYXNoby5jb20xEzARBgNVBAcTCk1ldHJvcG9saXMxETAPBgNVBAgTCE5l +dyBZb3JrMQswCQYDVQQGEwJVUzEWMBQGA1UEAxMNQmFzaG8gUm9vdCBDQYIJAIxU +kANeHGH0MA0GCSqGSIb3DQEBBAUAA4GBAHi8OjJ8hDCzNNIEjgGF6Ttj5DM8/5mT +9SKgO9CuB1Sux0Frl9cPaFPpDT/YRgCOIBy7JIer2XbO3CO8knaZIhHikz4xiLIp +KUIm3XM+Ol+Pjsnu0Gh83oUHV4Vz/QQJgW4BILUV0PxHecvzXFxclhmFBuEm2H6/ +rDxZVAi5dFAJ +-----END CERTIFICATE----- diff --git a/src/test/resources/cert.pem b/src/test/resources/cert.pem new file mode 100644 index 000000000..86185a111 --- /dev/null +++ b/src/test/resources/cert.pem @@ -0,0 +1,49 @@ +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 1 (0x1) + Signature Algorithm: md5WithRSAEncryption + Issuer: O=The Sample Company, OU=client dev/emailAddress=riak@basho.com, L=Metropolis, ST=New York, C=US, CN=Basho Root CA + Validity + Not Before: Feb 28 22:46:14 2014 GMT + Not After : Feb 26 22:46:14 2024 GMT + Subject: C=US, ST=New York, O=The Sample Company, OU=rjc test, CN=localhost + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (1024 bit) + Modulus (1024 bit): + 00:d7:b8:f0:ac:c6:43:fc:df:9c:8a:64:e9:72:bf: + 22:b5:ca:bf:88:e2:4e:a8:3f:8b:d4:41:da:c7:21: + f3:fb:69:db:b4:40:4b:79:49:e8:59:fc:f4:f4:a6: + c4:76:db:70:d7:ca:77:64:f7:50:71:43:f5:73:b7: + 8e:ec:28:bd:3d:bc:95:f9:87:c0:c0:3a:5b:46:d7: + 38:3b:fc:06:0d:b1:20:a8:24:41:6d:d4:78:c7:50: + e6:46:4d:78:45:00:ab:6d:e1:32:d0:78:a2:36:68: + 32:ec:7a:93:2e:4a:a0:a2:79:55:35:2d:2d:35:09: + af:29:ac:2b:23:88:b3:00:ab + Exponent: 65537 (0x10001) + Signature Algorithm: md5WithRSAEncryption + 61:da:a4:cc:3d:24:1f:96:11:ff:09:5a:d0:a8:cb:71:a1:c4: + 0b:3f:9b:20:63:81:98:c6:37:da:2d:57:e8:7c:96:9d:21:21: + 01:e5:d9:34:dd:e8:bb:68:91:9a:6a:33:37:76:14:f6:9b:2b: + 56:d4:e3:8a:cf:fe:47:95:35:a9:57:96:eb:b8:7c:75:c4:be: + 3b:6a:9f:5c:3b:e4:61:5c:5f:67:cc:aa:dd:63:01:42:49:8b: + 5d:bb:e6:24:52:69:c0:71:6c:5c:1a:90:22:a4:db:7d:7b:db: + 4e:8d:e4:7a:e5:d5:af:2a:fa:1f:a5:22:d9:c8:d7:ea:d5:e1: + 96:7a +-----BEGIN CERTIFICATE----- +MIICcjCCAdsCAQEwDQYJKoZIhvcNAQEEBQAwgZ4xGzAZBgNVBAoTElRoZSBTYW1w +bGUgQ29tcGFueTETMBEGA1UECxMKY2xpZW50IGRldjEdMBsGCSqGSIb3DQEJARYO +cmlha0BiYXNoby5jb20xEzARBgNVBAcTCk1ldHJvcG9saXMxETAPBgNVBAgTCE5l +dyBZb3JrMQswCQYDVQQGEwJVUzEWMBQGA1UEAxMNQmFzaG8gUm9vdCBDQTAeFw0x +NDAyMjgyMjQ2MTRaFw0yNDAyMjYyMjQ2MTRaMGQxCzAJBgNVBAYTAlVTMREwDwYD +VQQIEwhOZXcgWW9yazEbMBkGA1UEChMSVGhlIFNhbXBsZSBDb21wYW55MREwDwYD +VQQLEwhyamMgdGVzdDESMBAGA1UEAxMJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQDXuPCsxkP835yKZOlyvyK1yr+I4k6oP4vUQdrHIfP7adu0 +QEt5SehZ/PT0psR223DXyndk91BxQ/Vzt47sKL09vJX5h8DAOltG1zg7/AYNsSCo +JEFt1HjHUOZGTXhFAKtt4TLQeKI2aDLsepMuSqCieVU1LS01Ca8prCsjiLMAqwID +AQABMA0GCSqGSIb3DQEBBAUAA4GBAGHapMw9JB+WEf8JWtCoy3GhxAs/myBjgZjG +N9otV+h8lp0hIQHl2TTd6LtokZpqMzd2FPabK1bU44rP/keVNalXluu4fHXEvjtq +n1w75GFcX2fMqt1jAUJJi1275iRSacBxbFwakCKk2317206N5Hrl1a8q+h+lItnI +1+rV4ZZ6 +-----END CERTIFICATE----- diff --git a/src/test/resources/key.pem b/src/test/resources/key.pem new file mode 100644 index 000000000..45029f2dd --- /dev/null +++ b/src/test/resources/key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQDXuPCsxkP835yKZOlyvyK1yr+I4k6oP4vUQdrHIfP7adu0QEt5 +SehZ/PT0psR223DXyndk91BxQ/Vzt47sKL09vJX5h8DAOltG1zg7/AYNsSCoJEFt +1HjHUOZGTXhFAKtt4TLQeKI2aDLsepMuSqCieVU1LS01Ca8prCsjiLMAqwIDAQAB +AoGAB93wuPxKAnDNLqbbdJnP1D1emslWAQDAQq9cISL12NdVcc9ecT/mv1FgRYPH +amY6SURS8lWqk1bdHqk1Bxu9Ou/3PCvWB7iMDRYGjvKjtdSnyMYCM4eBeuiYZ4Bl +0R160a42r8k514LVsdlX7ojC9WFyCl9z+g/7zPopYwrrgeECQQD6JhltfFhp7gvz +SWv/iopFoSAk9iKHjUWX65tR4PFpllpz/KyVcSC6mT4amQS6AZ4eri4bVbDjZwk7 +pTWO5yQzAkEA3MSxtw8cWEU1hshcmrmWqTyKKWRod3FagtqGDM1dF2kkdD3aM/6p +osKGau0bkOPEr4xE0i3msp1JJYFNRgB5qQJAISZdZkn9OYSHZuVK/6Fvc4zHwr4v +k0EggOKDQdWKiJCZShepE2Dbkn7uYP34R2IhfzVb/NldSLWF3ralI1v0SwJAakCo +n8hABiPZLTOFjlpALteaT22p8cK2oz/1QHgek8Pg7alPObMZheJFu3S+za8x3KOQ +A/BflhupNsnSnRxhcQJAWepp4Fxev+rZBNIZmX0MGaFSwGEnM1egpcKCLHcfdzj6 +xZs0kbxdAkf5o51XaaKczPgyY02XOIAjvRHuJq5dxQ== +-----END RSA PRIVATE KEY-----