
com.couchbase.client.java.CouchbaseAsyncBucket Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-client Show documentation
Show all versions of java-client Show documentation
The official Couchbase Java SDK
/**
* Copyright (C) 2014 Couchbase, Inc.
*
* 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 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 DEALING
* IN THE SOFTWARE.
*/
package com.couchbase.client.java;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import com.couchbase.client.core.ClusterFacade;
import com.couchbase.client.core.CouchbaseException;
import com.couchbase.client.core.lang.Tuple2;
import com.couchbase.client.core.logging.CouchbaseLogger;
import com.couchbase.client.core.logging.CouchbaseLoggerFactory;
import com.couchbase.client.core.message.cluster.CloseBucketRequest;
import com.couchbase.client.core.message.cluster.CloseBucketResponse;
import com.couchbase.client.core.message.kv.AppendRequest;
import com.couchbase.client.core.message.kv.AppendResponse;
import com.couchbase.client.core.message.kv.CounterRequest;
import com.couchbase.client.core.message.kv.CounterResponse;
import com.couchbase.client.core.message.kv.GetRequest;
import com.couchbase.client.core.message.kv.GetResponse;
import com.couchbase.client.core.message.kv.InsertRequest;
import com.couchbase.client.core.message.kv.InsertResponse;
import com.couchbase.client.core.message.kv.ObserveRequest;
import com.couchbase.client.core.message.kv.ObserveResponse;
import com.couchbase.client.core.message.kv.PrependRequest;
import com.couchbase.client.core.message.kv.PrependResponse;
import com.couchbase.client.core.message.kv.RemoveRequest;
import com.couchbase.client.core.message.kv.RemoveResponse;
import com.couchbase.client.core.message.kv.ReplaceRequest;
import com.couchbase.client.core.message.kv.ReplaceResponse;
import com.couchbase.client.core.message.kv.TouchRequest;
import com.couchbase.client.core.message.kv.TouchResponse;
import com.couchbase.client.core.message.kv.UnlockRequest;
import com.couchbase.client.core.message.kv.UnlockResponse;
import com.couchbase.client.core.message.kv.UpsertRequest;
import com.couchbase.client.core.message.kv.UpsertResponse;
import com.couchbase.client.core.message.observe.Observe;
import com.couchbase.client.core.message.search.SearchQueryRequest;
import com.couchbase.client.core.message.search.SearchQueryResponse;
import com.couchbase.client.core.message.view.ViewQueryRequest;
import com.couchbase.client.core.message.view.ViewQueryResponse;
import com.couchbase.client.deps.io.netty.buffer.ByteBuf;
import com.couchbase.client.java.bucket.AsyncBucketManager;
import com.couchbase.client.java.bucket.DefaultAsyncBucketManager;
import com.couchbase.client.java.bucket.ReplicaReader;
import com.couchbase.client.java.document.Document;
import com.couchbase.client.java.document.JsonDocument;
import com.couchbase.client.java.document.JsonLongDocument;
import com.couchbase.client.java.document.json.JsonArray;
import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.subdoc.AsyncLookupInBuilder;
import com.couchbase.client.java.subdoc.AsyncMutateInBuilder;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import com.couchbase.client.java.error.CASMismatchException;
import com.couchbase.client.java.error.CouchbaseOutOfMemoryException;
import com.couchbase.client.java.error.DocumentAlreadyExistsException;
import com.couchbase.client.java.error.DocumentDoesNotExistException;
import com.couchbase.client.java.error.DurabilityException;
import com.couchbase.client.java.error.RequestTooBigException;
import com.couchbase.client.java.error.TemporaryFailureException;
import com.couchbase.client.java.error.TemporaryLockFailureException;
import com.couchbase.client.java.query.AsyncN1qlQueryResult;
import com.couchbase.client.java.query.N1qlQuery;
import com.couchbase.client.java.query.Statement;
import com.couchbase.client.java.query.core.N1qlQueryExecutor;
import com.couchbase.client.java.repository.AsyncRepository;
import com.couchbase.client.java.repository.CouchbaseAsyncRepository;
import com.couchbase.client.java.search.SearchQueryResult;
import com.couchbase.client.java.search.SearchQueryRow;
import com.couchbase.client.java.search.query.SearchQuery;
import com.couchbase.client.java.transcoder.BinaryTranscoder;
import com.couchbase.client.java.transcoder.JacksonTransformers;
import com.couchbase.client.java.transcoder.JsonArrayTranscoder;
import com.couchbase.client.java.transcoder.JsonBooleanTranscoder;
import com.couchbase.client.java.transcoder.JsonDoubleTranscoder;
import com.couchbase.client.java.transcoder.JsonLongTranscoder;
import com.couchbase.client.java.transcoder.JsonStringTranscoder;
import com.couchbase.client.java.transcoder.JsonTranscoder;
import com.couchbase.client.java.transcoder.LegacyTranscoder;
import com.couchbase.client.java.transcoder.RawJsonTranscoder;
import com.couchbase.client.java.transcoder.SerializableTranscoder;
import com.couchbase.client.java.transcoder.StringTranscoder;
import com.couchbase.client.java.transcoder.Transcoder;
import com.couchbase.client.java.transcoder.subdoc.FragmentTranscoder;
import com.couchbase.client.java.transcoder.subdoc.JacksonFragmentTranscoder;
import com.couchbase.client.java.view.AsyncSpatialViewResult;
import com.couchbase.client.java.view.AsyncViewResult;
import com.couchbase.client.java.view.SpatialViewQuery;
import com.couchbase.client.java.view.ViewQuery;
import com.couchbase.client.java.view.ViewQueryResponseMapper;
import com.couchbase.client.java.view.ViewRetryHandler;
import rx.Observable;
import rx.functions.Func0;
import rx.functions.Func1;
public class CouchbaseAsyncBucket implements AsyncBucket {
private static final CouchbaseLogger LOGGER = CouchbaseLoggerFactory.getInstance(CouchbaseAsyncBucket.class);
private static final int COUNTER_NOT_EXISTS_EXPIRY = 0xffffffff;
public static final String CURRENT_BUCKET_IDENTIFIER = "#CURRENT_BUCKET#";
public static final JsonTranscoder JSON_OBJECT_TRANSCODER = new JsonTranscoder();
public static final JsonArrayTranscoder JSON_ARRAY_TRANSCODER = new JsonArrayTranscoder();
public static final JsonBooleanTranscoder JSON_BOOLEAN_TRANSCODER = new JsonBooleanTranscoder();
public static final JsonDoubleTranscoder JSON_DOUBLE_TRANSCODER = new JsonDoubleTranscoder();
public static final JsonLongTranscoder JSON_LONG_TRANSCODER = new JsonLongTranscoder();
public static final JsonStringTranscoder JSON_STRING_TRANSCODER = new JsonStringTranscoder();
public static final RawJsonTranscoder RAW_JSON_TRANSCODER = new RawJsonTranscoder();
public static final LegacyTranscoder LEGACY_TRANSCODER = new LegacyTranscoder();
public static final BinaryTranscoder BINARY_TRANSCODER = new BinaryTranscoder();
public static final StringTranscoder STRING_TRANSCODER = new StringTranscoder();
public static final SerializableTranscoder SERIALIZABLE_TRANSCODER = new SerializableTranscoder();
private final String bucket;
private final String password;
private final ClusterFacade core;
private final Map, Transcoder extends Document, ?>> transcoders;
//TODO this could be opened for customization like with transcoders
private final FragmentTranscoder subdocumentTranscoder = new JacksonFragmentTranscoder(JacksonTransformers.MAPPER);
private final AsyncBucketManager bucketManager;
private final CouchbaseEnvironment environment;
/** the bucket's {@link N1qlQueryExecutor}. Prefer using {@link #n1qlQueryExecutor()} since it allows mocking and testing */
private final N1qlQueryExecutor n1qlQueryExecutor;
private volatile boolean closed;
public CouchbaseAsyncBucket(final ClusterFacade core, final CouchbaseEnvironment environment, final String name,
final String password, final List> customTranscoders) {
bucket = name;
this.password = password;
this.core = core;
this.environment = environment;
this.closed = false;
transcoders = new ConcurrentHashMap, Transcoder extends Document, ?>>();
transcoders.put(JSON_OBJECT_TRANSCODER.documentType(), JSON_OBJECT_TRANSCODER);
transcoders.put(JSON_ARRAY_TRANSCODER.documentType(), JSON_ARRAY_TRANSCODER);
transcoders.put(JSON_BOOLEAN_TRANSCODER.documentType(), JSON_BOOLEAN_TRANSCODER);
transcoders.put(JSON_DOUBLE_TRANSCODER.documentType(), JSON_DOUBLE_TRANSCODER);
transcoders.put(JSON_LONG_TRANSCODER.documentType(), JSON_LONG_TRANSCODER);
transcoders.put(JSON_STRING_TRANSCODER.documentType(), JSON_STRING_TRANSCODER);
transcoders.put(RAW_JSON_TRANSCODER.documentType(), RAW_JSON_TRANSCODER);
transcoders.put(LEGACY_TRANSCODER.documentType(), LEGACY_TRANSCODER);
transcoders.put(BINARY_TRANSCODER.documentType(), BINARY_TRANSCODER);
transcoders.put(STRING_TRANSCODER.documentType(), STRING_TRANSCODER);
transcoders.put(SERIALIZABLE_TRANSCODER.documentType(), SERIALIZABLE_TRANSCODER);
for (Transcoder extends Document, ?> custom : customTranscoders) {
transcoders.put(custom.documentType(), custom);
}
bucketManager = DefaultAsyncBucketManager.create(bucket, password, core);
boolean n1qlPreparedEncodedPlanEnabled = "true".equalsIgnoreCase(System.getProperty(N1qlQueryExecutor.ENCODED_PLAN_ENABLED_PROPERTY, "true")); //active by default
n1qlQueryExecutor = new N1qlQueryExecutor(core, bucket, password, n1qlPreparedEncodedPlanEnabled);
}
@Override
public String name() {
return bucket;
}
@Override
public Observable core() {
return Observable.just(core);
}
@Override
public FragmentTranscoder subdocumentTranscoder() {
return subdocumentTranscoder;
}
/**
* Returns the underlying {@link N1qlQueryExecutor} used to perform N1QL queries.
*
* Handle with care since all additional checks that are normally performed by this library may be skipped (hence
* the protected visibility).
*/
protected N1qlQueryExecutor n1qlQueryExecutor() {
return this.n1qlQueryExecutor;
}
@Override
public CouchbaseEnvironment environment() {
return environment;
}
@Override
public Observable repository() {
return Observable.just((AsyncRepository) new CouchbaseAsyncRepository(this));
}
@Override
public Observable get(final String id) {
return get(id, JsonDocument.class);
}
@Override
@SuppressWarnings("unchecked")
public > Observable get(D document) {
return (Observable) get(document.id(), document.getClass());
}
@Override
@SuppressWarnings("unchecked")
public > Observable get(final String id, final Class target) {
return Observable.defer(new Func0>() {
@Override
public Observable call() {
return core.send(new GetRequest(id, bucket));
}
})
.filter(new Func1() {
@Override
public Boolean call(GetResponse response) {
if (response.status().isSuccess()) {
return true;
}
ByteBuf content = response.content();
if (content != null && content.refCnt() > 0) {
content.release();
}
switch(response.status()) {
case NOT_EXISTS:
return false;
case TEMPORARY_FAILURE:
case SERVER_BUSY:
throw new TemporaryFailureException();
case OUT_OF_MEMORY:
throw new CouchbaseOutOfMemoryException();
default:
throw new CouchbaseException(response.status().toString());
}
}
})
.map(new Func1() {
@Override
public D call(final GetResponse response) {
Transcoder, Object> transcoder = (Transcoder, Object>) transcoders.get(target);
return (D) transcoder.decode(id, response.content(), response.cas(), 0, response.flags(),
response.status());
}
});
}
@Override
public Observable exists(final String id) {
return Observable.defer(new Func0>() {
@Override
public Observable call() {
return core.send(new ObserveRequest(id, 0, true, (short) 0, bucket));
}
})
.map(new Func1() {
@Override
public Boolean call(ObserveResponse response) {
ByteBuf content = response.content();
if (content != null && content.refCnt() > 0) {
content.release();
}
ObserveResponse.ObserveStatus foundStatus = response.observeStatus();
if (foundStatus == ObserveResponse.ObserveStatus.FOUND_PERSISTED
|| foundStatus == ObserveResponse.ObserveStatus.FOUND_NOT_PERSISTED) {
return true;
}
return false;
}
});
}
@Override
public > Observable exists(D document) {
return exists(document.id());
}
@Override
public Observable getAndLock(String id, int lockTime) {
return getAndLock(id, lockTime, JsonDocument.class);
}
@Override
@SuppressWarnings("unchecked")
public > Observable getAndLock(D document, int lockTime) {
return (Observable) getAndLock(document.id(), lockTime, document.getClass());
}
@Override
@SuppressWarnings("unchecked")
public > Observable getAndLock(final String id, final int lockTime, final Class target) {
return Observable.defer(new Func0>() {
@Override
public Observable call() {
return core.send(new GetRequest(id, bucket, true, false, lockTime));
}
})
.filter(new Func1() {
@Override
public Boolean call(GetResponse response) {
if (response.status().isSuccess()) {
return true;
}
ByteBuf content = response.content();
if (content != null && content.refCnt() > 0) {
content.release();
}
switch (response.status()) {
case NOT_EXISTS:
return false;
case TEMPORARY_FAILURE:
throw new TemporaryLockFailureException();
case SERVER_BUSY:
throw new TemporaryFailureException();
case OUT_OF_MEMORY:
throw new CouchbaseOutOfMemoryException();
default:
throw new CouchbaseException(response.status().toString());
}
}
})
.map(new Func1() {
@Override
public D call(final GetResponse response) {
Transcoder, Object> transcoder = (Transcoder, Object>) transcoders.get(target);
return (D) transcoder.decode(id, response.content(), response.cas(), 0, response.flags(),
response.status());
}
});
}
@Override
public Observable getAndTouch(String id, int expiry) {
return getAndTouch(id, expiry, JsonDocument.class);
}
@Override
@SuppressWarnings("unchecked")
public > Observable getAndTouch(D document) {
return (Observable) getAndTouch(document.id(), document.expiry(), document.getClass());
}
@Override
@SuppressWarnings("unchecked")
public > Observable getAndTouch(final String id, final int expiry, final Class target) {
return Observable.defer(new Func0>() {
@Override
public Observable call() {
return core.send(new GetRequest(id, bucket, false, true, expiry));
}
})
.filter(new Func1() {
@Override
public Boolean call(GetResponse response) {
if (response.status().isSuccess()) {
return true;
}
ByteBuf content = response.content();
if (content != null && content.refCnt() > 0) {
content.release();
}
switch (response.status()) {
case NOT_EXISTS:
return false;
case TEMPORARY_FAILURE:
case SERVER_BUSY:
throw new TemporaryFailureException();
case OUT_OF_MEMORY:
throw new CouchbaseOutOfMemoryException();
default:
throw new CouchbaseException(response.status().toString());
}
}
})
.map(new Func1() {
@Override
public D call(final GetResponse response) {
Transcoder, Object> transcoder = (Transcoder, Object>) transcoders.get(target);
return (D) transcoder.decode(id, response.content(), response.cas(), 0, response.flags(),
response.status());
}
});
}
@Override
public Observable getFromReplica(final String id, final ReplicaMode type) {
return getFromReplica(id, type, JsonDocument.class);
}
@Override
@SuppressWarnings("unchecked")
public > Observable getFromReplica(final D document, final ReplicaMode type) {
return (Observable) getFromReplica(document.id(), type, document.getClass());
}
@Override
@SuppressWarnings("unchecked")
public > Observable getFromReplica(final String id, final ReplicaMode type,
final Class target) {
return ReplicaReader
.read(core, id, type, bucket)
.map(new Func1() {
@Override
public D call(final GetResponse response) {
Transcoder, Object> transcoder = (Transcoder, Object>) transcoders.get(target);
return (D) transcoder.decode(id, response.content(), response.cas(), 0, response.flags(),
response.status());
}
})
.cache(type.maxAffectedNodes());
}
@Override
@SuppressWarnings("unchecked")
public > Observable insert(final D document) {
final Transcoder, Object> transcoder = (Transcoder, Object>) transcoders.get(document.getClass());
return Observable.defer(new Func0>() {
@Override
public Observable call() {
Tuple2 encoded = transcoder.encode((Document