io.orchestrate.client.KvResource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of orchestrate-client Show documentation
Show all versions of orchestrate-client Show documentation
A high performance, asynchronous Java client to query the Orchestrate.io service.
The newest version!
/*
* Copyright 2014 the original author or authors.
*
* 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 io.orchestrate.client;
import io.orchestrate.client.jsonpatch.JsonPatch;
import lombok.NonNull;
import org.glassfish.grizzly.http.*;
import org.glassfish.grizzly.http.util.Header;
import org.glassfish.grizzly.http.util.HttpStatus;
import org.glassfish.grizzly.memory.ByteBufferWrapper;
import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.ByteBuffer;
import static io.orchestrate.client.Preconditions.checkArgument;
import static io.orchestrate.client.Preconditions.checkNotNull;
/**
* The resource for the KV features in the Orchestrate API.
*/
public class KvResource extends BaseResource {
/** The collection for the request. */
private final String collection;
/** The key for the request. */
private final String key;
/** Whether to store the object if no key already exists. */
private boolean ifAbsent;
/** The last known version of the stored object. */
private String objectRef;
/** Whether an update can act as an insert if the kv doesn't exist */
private boolean upsert;
/** The fully-qualified names of fields to select when filtering the result JSON */
private String withFields;
/** The fully-qualified names of fields to reject when filtering the result JSON */
private String withoutFields;
KvResource(final OrchestrateClient client,
final JacksonMapper mapper,
final String collection,
final String key) {
super(client, mapper);
assert (collection != null);
assert (collection.length() > 0);
assert (key != null);
assert (key.length() > 0);
this.collection = collection;
this.key = key;
this.ifAbsent = false;
this.objectRef = null;
this.withFields = null;
this.withoutFields = null;
}
/**
* Store an object by key to the Orchestrate service as part of a bulk operation.
*
* Usage:
*
* {@code
* client.bulk()
* .add(client.kv("someCollection", "key1").bulkPut(obj))
* .add(client.kv("someCollection", "key2").bulkPut(obj))
* .add(...)
* .done();
* }
*
*
* @param value The object to store.
* @return The bulk operation.
*
* @see #put(Object)
* @see Client#bulk()
*/
public BulkOperation bulkPut(final @NonNull Object value) {
return BulkOperation.forKvItem(collection, key, value);
}
/**
* {@link #delete(boolean)}.
* @return The prepared delete request.
*/
public OrchestrateRequest delete() {
return delete(Boolean.FALSE);
}
/**
* Purge a KV object from a collection in the Orchestrate.io service. This
* will permanently remove the record from the service, and cannot be
* undone.
*
* Usage:
*
* {@code
* boolean result =
* client.kv("someCollection", "someKey")
* .delete(Boolean.TRUE)
* .get();
* }
*
*
* @param purge If {@code true}, permanently delete the object AND its "ref"
* history from Orchestrate.
* @return The prepared delete request.
*/
public OrchestrateRequest delete(final boolean purge) {
checkArgument(!ifAbsent, "'ifAbsent' cannot be used in a DELETE request.");
final String uri = client.uri(collection, key);
final HttpRequestPacket.Builder builder = HttpRequestPacket.builder()
.method(Method.DELETE)
.uri(uri);
if (purge) {
builder.query("purge=true");
}
if (objectRef != null) {
builder.header(Header.IfMatch, "\"".concat(objectRef).concat("\""));
}
final HttpContent packet = builder.build().httpContentBuilder().build();
return new OrchestrateRequest(client, packet, new ResponseConverter() {
@Override
public Boolean from(final HttpContent response) throws IOException {
final int status = ((HttpResponsePacket) response.getHttpHeader()).getStatus();
return (status == HttpStatus.NO_CONTENT_204.getStatusCode());
}
});
}
/**
* {@link #get(Class)}.
* @param clazz Type information for marshalling objects at runtime.
* @param The type to deserialize the result to.
* @return This KV resource.
*/
public OrchestrateRequest> get(final Class clazz) {
return get(clazz, null);
}
/**
* Fetch an object by key from the Orchestrate service.
*
* Usage:
*
* {@code
* KvObject object =
* client.kv("someCollection", "someKey")
* .get(DomainObject.class)
* .get();
* }
*
*
* @param clazz Type information for marshalling objects at runtime.
* @param ref The version of the object to get.
* @param The type to deserialize the result to.
* @return This KV resource.
*/
public OrchestrateRequest> get(final @NonNull Class clazz, @Nullable final String ref) {
final String uri = ref != null ?
client.uri(collection, key, "refs", ref) :
client.uri(collection, key);
String query = "";
if (withFields != null) {
query = query.concat("&with_fields=").concat(client.encode(withFields));
}
if (withoutFields != null) {
query = query.concat("&without_fields=").concat(client.encode(withoutFields));
}
final HttpRequestPacket.Builder packetBuilder = HttpRequestPacket.builder()
.method(Method.GET)
.uri(uri);
if (!query.isEmpty()) {
packetBuilder.query(query);
}
final HttpContent packet = packetBuilder.build()
.httpContentBuilder()
.build();
return new OrchestrateRequest>(client, packet, new ResponseConverter>() {
@Override
public KvObject from(final HttpContent response) throws IOException {
final HttpHeader header = response.getHttpHeader();
final int status = ((HttpResponsePacket) header).getStatus();
if (status == 404) {
// maybe one day we can return an optional type
return null;
}
return toKvObject(response, collection, key, clazz);
}
});
}
/**
* Equivalent to {@code this.ifAbsent(Boolean.TRUE)}.
*
* @return This KV resource.
* @see #ifAbsent(boolean)
*/
public KvResource ifAbsent() {
return ifAbsent(Boolean.TRUE);
}
/**
* Whether to store the object if no key already exists.
*
* @param ifAbsent If {@code true}
* @return This KV resource.
*/
public KvResource ifAbsent(final boolean ifAbsent) {
checkArgument(!ifAbsent || objectRef == null, "'ifMatch' and 'ifAbsent' cannot be used together.");
this.ifAbsent = ifAbsent;
return this;
}
public KvResource upsert() {
return upsert(Boolean.TRUE);
}
public KvResource upsert(final boolean upsert) {
this.upsert = upsert;
return this;
}
/**
* The last known version of the stored object to match for the request to
* succeed.
*
* @param objectRef The last known version of the stored object.
* @return This KV resource.
*/
public KvResource ifMatch(final @NonNull String objectRef) {
checkArgument(!ifAbsent, "'ifMatch' and 'ifAbsent' cannot be used together.");
this.objectRef = objectRef;
return this;
}
/**
* Apply field-filtering to the result JSON, using this list of fully-qualified
* field names as a whitelist of fields to include.
*
*
* {@code
* KvObject object =
* client.kv("someCollection", "someKey")
* .withFields("value.name.first,value.name.last")
* .get(DomainObject.class)
* .get();
* }
*
*
* @param withFields The comma separated list of fully-qualified field names to select.
* @return This request.
*/
public KvResource withFields(final String withFields) {
this.withFields = checkNotNull(withFields, "withFields");
return this;
}
/**
* Apply field-filtering to the result JSON, using this list of fully-qualified
* field names as a blacklist of fields to exclude.
*
*
* {@code
* KvObject object =
* client.kv("someCollection", "someKey")
* .withoutFields("value.name.first,value.name.last")
* .get(DomainObject.class)
* .get();
* }
*
*
* @param withoutFields The comma separated list of fully-qualified field names to reject.
* @return This request.
*/
public KvResource withoutFields(final String withoutFields) {
this.withoutFields = checkNotNull(withoutFields, "withoutFields");
return this;
}
/**
* Store an object by key to the Orchestrate service.
*
* Usage:
*
* {@code
* DomainObject obj = new DomainObject();
* KvMetadata kvMetadata =
* client.kv("someCollection", "someKey")
* .put(obj)
* .get();
* }
*
*
* @param value The object to store.
* @return The prepared put request.
*/
public OrchestrateRequest put(final @NonNull Object value) {
final byte[] content = toJsonBytes(value);
final String uri = client.uri(collection, key);
final HttpRequestPacket.Builder httpHeaderBuilder = HttpRequestPacket.builder()
.method(Method.PUT)
.contentType("application/json")
.uri(uri);
if (objectRef != null) {
httpHeaderBuilder.header(Header.IfMatch, "\"".concat(objectRef).concat("\""));
} else if (ifAbsent) {
httpHeaderBuilder.header(Header.IfNoneMatch, "\"*\"");
}
httpHeaderBuilder.contentLength(content.length);
final HttpContent packet = httpHeaderBuilder.build()
.httpContentBuilder()
.content(new ByteBufferWrapper(ByteBuffer.wrap(content)))
.build();
return new OrchestrateRequest(client, packet, new ResponseConverter() {
@Override
public KvMetadata from(final HttpContent response) throws IOException {
final HttpHeader header = response.getHttpHeader();
final int status = ((HttpResponsePacket) header).getStatus();
if (status == 201) {
final String ref = header.getHeader(Header.ETag)
.replace("\"", "")
.replace("-gzip", "");
return new KvObject(collection, key, ref, null, mapper, null, null, null);
}
return null;
}
});
}
public OrchestrateRequest patch(JsonPatch patchOps) {
final byte[] content = toJsonBytes(patchOps.getOps());
final String uri = client.uri(collection, key);
final HttpRequestPacket.Builder httpHeaderBuilder = HttpRequestPacket.builder()
.method(Method.PATCH)
.contentType("application/json-patch+json")
.query("upsert=" + Boolean.toString(upsert))
.uri(uri);
if (objectRef != null) {
httpHeaderBuilder.header(Header.IfMatch, "\"".concat(objectRef).concat("\""));
} else if (ifAbsent) {
throw new IllegalStateException("Cannot perform an ifAbsent PATCH request.");
}
httpHeaderBuilder.contentLength(content.length);
final HttpContent packet = httpHeaderBuilder.build()
.httpContentBuilder()
.content(new ByteBufferWrapper(ByteBuffer.wrap(content)))
.build();
return new OrchestrateRequest(client, packet, new ResponseConverter() {
@Override
public KvMetadata from(final HttpContent response) throws IOException {
final HttpHeader header = response.getHttpHeader();
final int status = ((HttpResponsePacket) header).getStatus();
if (status == 201) {
final String ref = header.getHeader(Header.ETag)
.replace("\"", "")
.replace("-gzip", "");
return new KvObject(collection, key, ref, null, mapper, null, null, null);
}
return null;
}
});
}
public OrchestrateRequest merge(String jsonObject) {
final byte[] content = toJsonBytes(jsonObject);
final String uri = client.uri(collection, key);
final HttpRequestPacket.Builder httpHeaderBuilder = HttpRequestPacket.builder()
.method(Method.PATCH)
.contentType("application/merge-patch+json")
.query("upsert=" + Boolean.toString(upsert))
.uri(uri);
if (objectRef != null) {
httpHeaderBuilder.header(Header.IfMatch, "\"".concat(objectRef).concat("\""));
} else if (ifAbsent) {
throw new IllegalStateException("Cannot perform an ifAbsent PATCH request.");
}
httpHeaderBuilder.contentLength(content.length);
final HttpContent packet = httpHeaderBuilder.build()
.httpContentBuilder()
.content(new ByteBufferWrapper(ByteBuffer.wrap(content)))
.build();
return new OrchestrateRequest(client, packet, new ResponseConverter() {
@Override
public KvMetadata from(final HttpContent response) throws IOException {
final HttpHeader header = response.getHttpHeader();
final int status = ((HttpResponsePacket) header).getStatus();
if (status == 201) {
final String ref = header.getHeader(Header.ETag)
.replace("\"", "")
.replace("-gzip", "");
return new KvObject(collection, key, ref, null, mapper, null, null, null);
}
return null;
}
});
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy