All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cinchapi.concourse.ConcourseThriftDriver Maven / Gradle / Ivy

/*
 * Copyright (c) 2013-2018 Cinchapi 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.cinchapi.concourse;

import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;

import javax.annotation.Nullable;

import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;

import com.cinchapi.common.base.CheckedExceptions;
import com.cinchapi.concourse.config.ConcourseClientPreferences;
import com.cinchapi.concourse.lang.BuildableState;
import com.cinchapi.concourse.lang.Criteria;
import com.cinchapi.concourse.lang.Language;
import com.cinchapi.concourse.security.ClientSecurity;
import com.cinchapi.concourse.thrift.AccessToken;
import com.cinchapi.concourse.thrift.ComplexTObject;
import com.cinchapi.concourse.thrift.ConcourseService;
import com.cinchapi.concourse.thrift.Diff;
import com.cinchapi.concourse.thrift.Operator;
import com.cinchapi.concourse.thrift.SecurityException;
import com.cinchapi.concourse.thrift.TObject;
import com.cinchapi.concourse.thrift.TransactionToken;
import com.cinchapi.concourse.util.ByteBuffers;
import com.cinchapi.concourse.util.Collections;
import com.cinchapi.concourse.util.Conversions;
import com.cinchapi.concourse.util.Convert;
import com.cinchapi.concourse.util.LinkNavigation;
import com.cinchapi.concourse.util.PrettyLinkedHashMap;
import com.cinchapi.concourse.util.PrettyLinkedTableMap;
import com.cinchapi.concourse.util.Transformers;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

/**
 * An implementation of the {@link Concourse} interface that interacts with the
 * server via Thrift's RPC protocols.
 * 
 * @author Jeff Nelson
 */
class ConcourseThriftDriver extends Concourse {

    private static String ENVIRONMENT;
    private static String PASSWORD;
    private static String SERVER_HOST;
    private static int SERVER_PORT;
    private static String USERNAME;
    static {
        // If there is a concourse_client.prefs file located in the working
        // directory, parse it and use its values as defaults.
        ConcourseClientPreferences config = ConcourseClientPreferences
                .fromCurrentWorkingDirectory();
        SERVER_HOST = config.getHost();
        SERVER_PORT = config.getPort();
        USERNAME = config.getUsername();
        PASSWORD = new String(config.getPassword());
        ENVIRONMENT = config.getEnvironment();

    }

    /**
     * The Thrift client that actually handles all RPC communication.
     */
    private final ConcourseService.Client client;

    /**
     * The client keeps a copy of its {@link AccessToken} and passes it to
     * the server for each remote procedure call. The client will
     * re-authenticate when necessary using the username/password read from
     * the prefs file.
     */
    private AccessToken creds = null;

    /**
     * The environment to which the client is connected.
     */
    private final String environment;

    /**
     * The host of the connection.
     */
    private final String host;

    /**
     * An encrypted copy of the password passed to the constructor.
     */
    private final ByteBuffer password;

    /**
     * The port of the connection.
     */
    private final int port;

    /**
     * Whenever the client starts a Transaction, it keeps a
     * {@link TransactionToken} so that the server can stage the changes in
     * the appropriate place.
     */
    private TransactionToken transaction = null;

    /**
     * An encrypted copy of the username passed to the constructor.
     */
    private final ByteBuffer username;

    /**
     * Create a new Client connection to the environment of the Concourse
     * Server described in {@code concourse_client.prefs} (or the default
     * environment and server if the prefs file does not exist) and return a
     * handler to facilitate database interaction.
     */
    public ConcourseThriftDriver() {
        this(ENVIRONMENT);
    }

    /**
     * Create a new Client connection to the specified {@code environment}
     * of the Concourse Server described in {@code concourse_client.prefs}
     * (or the default server if the prefs file does not exist) and return a
     * handler to facilitate database interaction.
     * 
     * @param environment
     */
    public ConcourseThriftDriver(String environment) {
        this(SERVER_HOST, SERVER_PORT, USERNAME, PASSWORD, environment);
    }

    /**
     * Create a new Client connection to the default environment of the
     * specified Concourse Server and return a handler to facilitate
     * database interaction.
     * 
     * @param host
     * @param port
     * @param username
     * @param password
     */
    public ConcourseThriftDriver(String host, int port, String username,
            String password) {
        this(host, port, username, password, "");
    }

    /**
     * Create a new Client connection to the specified {@code environment}
     * of the specified Concourse Server and return a handler to facilitate
     * database interaction.
     * 
     * @param host
     * @param port
     * @param username
     * @param password
     * @param environment
     */
    public ConcourseThriftDriver(String host, int port, String username,
            String password, String environment) {
        this.host = host;
        this.port = port;
        this.username = ClientSecurity.encrypt(username);
        this.password = ClientSecurity.encrypt(password);
        this.environment = environment;
        final TTransport transport = new TSocket(host, port);
        try {
            transport.open();
            TProtocol protocol = new TBinaryProtocol(transport);
            client = new ConcourseService.Client(protocol);
            authenticate();
            Runtime.getRuntime().addShutdownHook(new Thread("shutdown") {

                @Override
                public void run() {
                    if(transaction != null && transport.isOpen()) {
                        abort();
                        transport.close();
                    }
                }

            });
        }
        catch (TTransportException e) {
            throw new RuntimeException(
                    "Could not connect to the Concourse Server at " + host + ":"
                            + port);
        }
    }

    @Override
    public void abort() {
        execute(() -> {
            if(transaction != null) {
                final TransactionToken token = transaction;
                transaction = null;
                client.abort(creds, token, environment);
            }
            return null;
        });
    }

    @Override
    public  long add(String key, T value) {
        return execute(() -> {
            return client.addKeyValue(key, Convert.javaToThrift(value), creds,
                    transaction, environment);
        });
    }

    @Override
    public  Map add(String key, T value,
            Collection records) {
        return execute(() -> {
            Map raw = client.addKeyValueRecords(key,
                    Convert.javaToThrift(value),
                    Collections.toLongList(records), creds, transaction,
                    environment);
            Map pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record", "Successful");
            for (long record : records) {
                pretty.put(record, raw.get(record));
            }
            return pretty;
        });
    }

    @Override
    public  boolean add(String key, T value, long record) {
        return execute(() -> {
            return client.addKeyValueRecord(key, Convert.javaToThrift(value),
                    record, creds, transaction, environment);
        });
    }

    @Override
    public Map audit(long record) {
        return execute(() -> {
            Map audit = client.auditRecord(record, creds,
                    transaction, environment);
            return ((PrettyLinkedHashMap) Transformers
                    .transformMap(audit, Conversions.timestampToMicros()))
                            .setKeyName("DateTime").setValueName("Revision");
        });
    }

    @Override
    public Map audit(long record, Timestamp start) {
        return execute(() -> {
            Map audit;
            if(start.isString()) {
                audit = client.auditRecordStartstr(record, start.toString(),
                        creds, transaction, environment);
            }
            else {
                audit = client.auditRecordStart(record, start.getMicros(),
                        creds, transaction, environment);
            }
            return ((PrettyLinkedHashMap) Transformers
                    .transformMap(audit, Conversions.timestampToMicros()))
                            .setKeyName("DateTime").setValueName("Revision");
        });
    }

    @Override
    public Map audit(long record, Timestamp start,
            Timestamp end) {
        return execute(() -> {
            Map audit;
            if(start.isString()) {
                audit = client.auditRecordStartstrEndstr(record,
                        start.toString(), end.toString(), creds, transaction,
                        environment);
            }
            else {
                audit = client.auditRecordStartEnd(record, start.getMicros(),
                        end.getMicros(), creds, transaction, environment);
            }
            return ((PrettyLinkedHashMap) Transformers
                    .transformMap(audit, Conversions.timestampToMicros()))
                            .setKeyName("DateTime").setValueName("Revision");
        });
    }

    @Override
    public Map audit(String key, long record) {
        return execute(() -> {
            Map audit = client.auditKeyRecord(key, record, creds,
                    transaction, environment);
            return ((PrettyLinkedHashMap) Transformers
                    .transformMap(audit, Conversions.timestampToMicros()))
                            .setKeyName("DateTime").setValueName("Revision");
        });
    }

    @Override
    public Map audit(String key, long record,
            Timestamp start) {
        return execute(() -> {
            Map audit;
            if(start.isString()) {
                audit = client.auditKeyRecordStartstr(key, record,
                        start.toString(), creds, transaction, environment);
            }
            else {
                audit = client.auditKeyRecordStart(key, record,
                        start.getMicros(), creds, transaction, environment);
            }
            return ((PrettyLinkedHashMap) Transformers
                    .transformMap(audit, Conversions.timestampToMicros()))
                            .setKeyName("DateTime").setValueName("Revision");
        });
    }

    @Override
    public Map audit(String key, long record,
            Timestamp start, Timestamp end) {
        return execute(() -> {
            Map audit;
            if(start.isString()) {
                audit = client.auditKeyRecordStartstrEndstr(key, record,
                        start.toString(), end.toString(), creds, transaction,
                        environment);
            }
            else {
                audit = client.auditKeyRecordStartEnd(key, record,
                        start.getMicros(), end.getMicros(), creds, transaction,
                        environment);
            }
            return ((PrettyLinkedHashMap) Transformers
                    .transformMap(audit, Conversions.timestampToMicros()))
                            .setKeyName("DateTime").setValueName("Revision");
        });
    }

    @Override
    public Map>> browse(Collection keys) {
        return execute(() -> {
            Map>> raw = client.browseKeys(
                    Collections.toList(keys), creds, transaction, environment);
            Map>> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Key");
            for (Entry>> entry : raw
                    .entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapSet(entry.getValue(),
                                Conversions.thriftToJava(),
                                Conversions. none()));
            }
            return pretty;
        });
    }

    @Override
    public Map>> browse(Collection keys,
            Timestamp timestamp) {
        return execute(() -> {
            Map>> raw;
            if(timestamp.isString()) {
                raw = client.browseKeysTimestr(Collections.toList(keys),
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                raw = client.browseKeysTime(Collections.toList(keys),
                        timestamp.getMicros(), creds, transaction, environment);
            }
            Map>> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Key");
            for (Entry>> entry : raw
                    .entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapSet(entry.getValue(),
                                Conversions.thriftToJava(),
                                Conversions. none()));
            }
            return pretty;
        });
    }

    @Override
    public Map> browse(String key) {
        return execute(() -> {
            Map> raw = client.browseKey(key, creds,
                    transaction, environment);
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap(key, "Records");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(Convert.thriftToJava(entry.getKey()),
                        entry.getValue());
            }
            return pretty;
        });
    }

    @Override
    public Map> browse(String key, Timestamp timestamp) {
        return execute(() -> {
            Map> raw;
            if(timestamp.isString()) {
                raw = client.browseKeyTimestr(key, timestamp.toString(), creds,
                        transaction, environment);
            }
            else {
                raw = client.browseKeyTime(key, timestamp.getMicros(), creds,
                        transaction, environment);
            }
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap(key, "Records");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(Convert.thriftToJava(entry.getKey()),
                        entry.getValue());
            }
            return pretty;
        });
    }

    @Override
    public Map> chronologize(String key, long record) {
        return execute(() -> {
            Map> raw = client.chronologizeKeyRecord(key,
                    record, creds, transaction, environment);
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("DateTime", "Values");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(Timestamp.fromMicros(entry.getKey()),
                        Transformers.transformSetLazily(entry.getValue(),
                                Conversions.thriftToJava()));
            }
            return pretty;
        });
    }

    @Override
    public Map> chronologize(String key, long record,
            Timestamp start) {
        return execute(() -> {
            Map> raw;
            if(start.isString()) {
                raw = client.chronologizeKeyRecordStartstr(key, record,
                        start.toString(), creds, transaction, environment);
            }
            else {
                raw = client.chronologizeKeyRecordStart(key, record,
                        start.getMicros(), creds, transaction, environment);
            }
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("DateTime", "Values");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(Timestamp.fromMicros(entry.getKey()),
                        Transformers.transformSetLazily(entry.getValue(),
                                Conversions.thriftToJava()));
            }
            return pretty;
        });
    }

    @Override
    public Map> chronologize(String key, long record,
            Timestamp start, Timestamp end) {
        return execute(() -> {
            Map> raw;
            if(start.isString()) {
                raw = client.chronologizeKeyRecordStartstrEndstr(key, record,
                        start.toString(), end.toString(), creds, transaction,
                        environment);
            }
            else {
                raw = client.chronologizeKeyRecordStartEnd(key, record,
                        start.getMicros(), end.getMicros(), creds, transaction,
                        environment);
            }
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("DateTime", "Values");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(Timestamp.fromMicros(entry.getKey()),
                        Transformers.transformSetLazily(entry.getValue(),
                                Conversions.thriftToJava()));
            }
            return pretty;
        });
    }

    @Override
    public void clear(Collection records) {
        execute(() -> {
            client.clearRecords(Collections.toLongList(records), creds,
                    transaction, environment);
            return null;
        });
    }

    @Override
    public void clear(Collection keys, Collection records) {
        execute(() -> {
            client.clearKeysRecords(Collections.toList(keys),
                    Collections.toLongList(records), creds, transaction,
                    environment);
            return null;
        });
    }

    @Override
    public void clear(Collection keys, long record) {
        execute(() -> {
            client.clearKeysRecord(Collections.toList(keys), record, creds,
                    transaction, environment);
            return null;
        });
    }

    @Override
    public void clear(long record) {
        execute(() -> {
            client.clearRecord(record, creds, transaction, environment);
            return null;
        });
    }

    @Override
    public void clear(String key, Collection records) {
        execute(() -> {
            client.clearKeyRecords(key, Collections.toLongList(records), creds,
                    transaction, environment);
            return null;
        });
    }

    @Override
    public void clear(String key, long record) {
        execute(() -> {
            client.clearKeyRecord(key, record, creds, transaction, environment);
            return null;
        });
    }

    @Override
    public boolean commit() {
        return execute(() -> {
            final TransactionToken token = transaction;
            transaction = null;
            return token != null ? client.commit(creds, token, environment)
                    : false;
        });
    }

    @Override
    public Set describe() {
        return execute(() -> {
            return client.describe(creds, transaction, environment);
        });
    }

    @Override
    public Set describe(Timestamp timestamp) {
        return execute(() -> {
            if(timestamp.isString()) {
                return client.describeTimestr(timestamp.toString(), creds,
                        transaction, environment);
            }
            else {
                return client.describeTime(timestamp.getMicros(), creds,
                        transaction, environment);
            }
        });
    }

    @Override
    public Map> describe(Collection records) {
        return execute(() -> {
            Map> raw = client.describeRecords(
                    Collections.toLongList(records), creds, transaction,
                    environment);
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record", "Keys");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(), entry.getValue());
            }
            return pretty;
        });
    }

    @Override
    public Map> describe(Collection records,
            Timestamp timestamp) {
        return execute(() -> {
            Map> raw;
            if(timestamp.isString()) {
                raw = client.describeRecordsTimestr(
                        Collections.toLongList(records), timestamp.toString(),
                        creds, transaction, environment);
            }
            else {
                raw = client.describeRecordsTime(
                        Collections.toLongList(records), timestamp.getMicros(),
                        creds, transaction, environment);
            }
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record", "Keys");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(), entry.getValue());
            }
            return pretty;
        });
    }

    @Override
    public Set describe(long record) {
        return execute(() -> {
            Set result = client.describeRecord(record, creds,
                    transaction, environment);
            return result;
        });
    }

    @Override
    public Set describe(long record, Timestamp timestamp) {
        return execute(() -> {
            if(timestamp.isString()) {
                return client.describeRecordTimestr(record,
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                return client.describeRecordTime(record, timestamp.getMicros(),
                        creds, transaction, environment);
            }
        });
    }

    @Override
    public  Map>> diff(long record,
            Timestamp start) {
        return execute(() -> {
            Map>> raw;
            if(start.isString()) {
                raw = client.diffRecordStartstr(record, start.toString(), creds,
                        transaction, environment);
            }
            else {
                raw = client.diffRecordStart(record, start.getMicros(), creds,
                        transaction, environment);
            }
            PrettyLinkedTableMap> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap();
            pretty.setRowName("Key");
            for (Entry>> entry : raw
                    .entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapSet(entry.getValue(),
                                Conversions. none(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map>> diff(long record, Timestamp start,
            Timestamp end) {
        return execute(() -> {
            Map>> raw;
            if(start.isString()) {
                raw = client.diffRecordStartstrEndstr(record, start.toString(),
                        end.toString(), creds, transaction, environment);
            }
            else {
                raw = client.diffRecordStartEnd(record, start.getMicros(),
                        end.getMicros(), creds, transaction, environment);
            }
            PrettyLinkedTableMap> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap();
            pretty.setRowName("Key");
            for (Entry>> entry : raw
                    .entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapSet(entry.getValue(),
                                Conversions. none(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> diff(String key, long record,
            Timestamp start) {
        return execute(() -> {
            Map> raw;
            if(start.isString()) {
                raw = client.diffKeyRecordStartstr(key, record,
                        start.toString(), creds, transaction, environment);
            }
            else {
                raw = client.diffKeyRecordStart(key, record, start.getMicros(),
                        creds, transaction, environment);
            }
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Operation", "Value");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformSetLazily(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> diff(String key, long record, Timestamp start,
            Timestamp end) {
        return execute(() -> {
            Map> raw;
            if(start.isString()) {
                raw = client.diffKeyRecordStartstrEndstr(key, record,
                        start.toString(), end.toString(), creds, transaction,
                        environment);
            }
            else {
                raw = client.diffKeyRecordStartEnd(key, record,
                        start.getMicros(), end.getMicros(), creds, transaction,
                        environment);
            }
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Operation", "Value");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformSetLazily(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @SuppressWarnings("unchecked")
    @Override
    public  Map>> diff(String key, Timestamp start) {
        return execute(() -> {
            Map>> raw;
            if(start.isString()) {
                raw = client.diffKeyStartstr(key, start.toString(), creds,
                        transaction, environment);
            }
            else {
                raw = client.diffKeyStart(key, start.getMicros(), creds,
                        transaction, environment);
            }
            PrettyLinkedTableMap> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap();
            pretty.setRowName("Value");
            for (Entry>> entry : raw.entrySet()) {
                pretty.put((T) Convert.thriftToJava(entry.getKey()),
                        entry.getValue());
            }
            return pretty;
        });
    }

    @SuppressWarnings("unchecked")
    @Override
    public  Map>> diff(String key, Timestamp start,
            Timestamp end) {
        return execute(() -> {
            Map>> raw;
            if(start.isString()) {
                raw = client.diffKeyStartstrEndstr(key, start.toString(),
                        end.toString(), creds, transaction, environment);
            }
            else {
                raw = client.diffKeyStartEnd(key, start.getMicros(),
                        end.getMicros(), creds, transaction, environment);
            }
            PrettyLinkedTableMap> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap();
            pretty.setRowName("Value");
            for (Entry>> entry : raw.entrySet()) {
                pretty.put((T) Convert.thriftToJava(entry.getKey()),
                        entry.getValue());
            }
            return pretty;
        });
    }

    @Override
    public void exit() {
        try {
            client.logout(creds, environment);
            client.getInputProtocol().getTransport().close();
            client.getOutputProtocol().getTransport().close();
        }
        catch (com.cinchapi.concourse.thrift.SecurityException
                | TTransportException e) {
            // Handle corner case where the client is existing because of
            // (or after the occurrence of) a password change, which means
            // it can't perform a traditional logout. Its worth nothing that
            // we're okay with this scenario because a password change will
            // delete all previously issued tokens.
        }
        catch (Exception e) {
            throw CheckedExceptions.wrapAsRuntimeException(e);
        }
    }

    @Override
    public Set find(Criteria criteria) {
        return execute(() -> {
            return client.findCriteria(
                    Language.translateToThriftCriteria(criteria), creds,
                    transaction, environment);
        });
    }

    @Override
    public Set find(Object criteria) {
        if(criteria instanceof BuildableState) {
            return find(((BuildableState) criteria).build());
        }
        else {
            throw new IllegalArgumentException(
                    criteria + " is not a valid argument for the find method");
        }
    }

    @Override
    public Set find(String ccl) {
        return execute(() -> {
            return client.findCcl(ccl, creds, transaction, environment);
        });
    }

    @Override
    public Set find(String key, Object value) {
        return executeFind(key, Operator.EQUALS, value);
    }

    @Override
    public Set find(String key, Object value, Timestamp timestamp) {
        return executeFind(key, Operator.EQUALS, value, timestamp);
    }

    @Override
    public Set find(String key, Operator operator, Object value) {
        return executeFind(key, operator, value);
    }

    @Override
    public Set find(String key, Operator operator, Object value,
            Object value2) {
        return executeFind(key, operator, value, value2);
    }

    @Override
    public Set find(String key, Operator operator, Object value,
            Object value2, Timestamp timestamp) {
        return executeFind(timestamp, key, operator, value, value2);
    }

    @Override
    public Set find(String key, Operator operator, Object value,
            Timestamp timestamp) {
        return executeFind(timestamp, key, operator, value);
    }

    @Override
    public Set find(String key, String operator, Object value) {
        return executeFind(key, operator, value);
    }

    @Override
    public Set find(String key, String operator, Object value,
            Object value2) {
        return executeFind(key, operator, value, value2);
    }

    @Override
    public Set find(String key, String operator, Object value,
            Object value2, Timestamp timestamp) {
        return executeFind(timestamp, key, operator, value, value2);
    }

    @Override
    public Set find(String key, String operator, Object value,
            Timestamp timestamp) {
        return executeFind(timestamp, key, operator, value);
    }

    @Override
    public  long findOrAdd(String key, T value)
            throws DuplicateEntryException {
        return execute(() -> {
            return client.findOrAddKeyValue(key, Convert.javaToThrift(value),
                    creds, transaction, environment);
        });
    }

    @Override
    public long findOrInsert(Criteria criteria, String json)
            throws DuplicateEntryException {
        return execute(() -> {
            return client.findOrInsertCriteriaJson(
                    Language.translateToThriftCriteria(criteria), json, creds,
                    transaction, environment);
        });
    }

    @Override
    public long findOrInsert(String ccl, String json)
            throws DuplicateEntryException {
        return execute(() -> {
            return client.findOrInsertCclJson(ccl, json, creds, transaction,
                    environment);
        });
    }

    @Override
    public  Map> get(Collection keys,
            Collection records) {
        return execute(() -> {
            Map> raw = client.getKeysRecords(
                    Collections.toList(keys), Collections.toLongList(records),
                    creds, transaction, environment);
            Map> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapValues(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> get(Collection keys,
            Collection records, Timestamp timestamp) {
        return execute(() -> {
            Map> raw;
            if(timestamp.isString()) {
                raw = client.getKeysRecordsTimestr(Collections.toList(keys),
                        Collections.toLongList(records), timestamp.toString(),
                        creds, transaction, environment);
            }
            else {
                raw = client.getKeysRecordsTime(Collections.toList(keys),
                        Collections.toLongList(records), timestamp.getMicros(),
                        creds, transaction, environment);
            }
            Map> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapValues(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> get(Collection keys,
            Criteria criteria) {
        return execute(() -> {
            Map> raw = client.getKeysCriteria(
                    Collections.toList(keys),
                    Language.translateToThriftCriteria(criteria), creds,
                    transaction, environment);
            Map> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapValues(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> get(Collection keys,
            Criteria criteria, Timestamp timestamp) {
        return execute(() -> {
            Map> raw;
            if(timestamp.isString()) {
                raw = client.getKeysCriteriaTimestr(Collections.toList(keys),
                        Language.translateToThriftCriteria(criteria),
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                raw = client.getKeysCriteriaTime(Collections.toList(keys),
                        Language.translateToThriftCriteria(criteria),
                        timestamp.getMicros(), creds, transaction, environment);
            }
            Map> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapValues(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @SuppressWarnings("unchecked")
    @Override
    public  Map get(Collection keys, long record) {
        return execute(() -> {
            Map raw = client.getKeysRecord(
                    Collections.toList(keys), record, creds, transaction,
                    environment);
            Map pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Key", "Value");
            for (Entry entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        (T) Convert.thriftToJava(entry.getValue()));
            }
            return pretty;
        });
    }

    @SuppressWarnings("unchecked")
    @Override
    public  Map get(Collection keys, long record,
            Timestamp timestamp) {
        return execute(() -> {
            Map raw;
            if(timestamp.isString()) {
                raw = client.getKeysRecordTimestr(Collections.toList(keys),
                        record, timestamp.toString(), creds, transaction,
                        environment);
            }
            else {
                raw = client.getKeysRecordTime(Collections.toList(keys), record,
                        timestamp.getMicros(), creds, transaction, environment);
            }
            Map pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Key", "Value");
            for (Entry entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        (T) Convert.thriftToJava(entry.getValue()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> get(Collection keys,
            Object criteria) {
        if(criteria instanceof BuildableState) {
            return get(keys, ((BuildableState) criteria).build());
        }
        else {
            throw new IllegalArgumentException(
                    criteria + " is not a valid argument for the get method");
        }
    }

    @Override
    public  Map> get(Collection keys,
            Object criteria, Timestamp timestamp) {
        if(criteria instanceof BuildableState) {
            return get(keys, ((BuildableState) criteria).build(), timestamp);
        }
        else {
            throw new IllegalArgumentException(
                    criteria + " is not a valid argument for the get method");
        }
    }

    @Override
    public  Map> get(Collection keys,
            String ccl) {
        return execute(() -> {
            Map> raw = client.getKeysCcl(
                    Collections.toList(keys), ccl, creds, transaction,
                    environment);
            Map> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapValues(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> get(Collection keys,
            String ccl, Timestamp timestamp) {
        return execute(() -> {
            Map> raw;
            if(timestamp.isString()) {
                raw = client.getKeysCclTimestr(Collections.toList(keys), ccl,
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                raw = client.getKeysCclTime(Collections.toList(keys), ccl,
                        timestamp.getMicros(), creds, transaction, environment);
            }
            Map> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapValues(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> get(Criteria criteria) {
        return execute(() -> {
            Map> raw = client.getCriteria(
                    Language.translateToThriftCriteria(criteria), creds,
                    transaction, environment);
            Map> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapValues(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> get(Criteria criteria,
            Timestamp timestamp) {
        return execute(() -> {
            Map> raw;
            if(timestamp.isString()) {
                raw = client.getCriteriaTimestr(
                        Language.translateToThriftCriteria(criteria),
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                raw = client.getCriteriaTime(
                        Language.translateToThriftCriteria(criteria),
                        timestamp.getMicros(), creds, transaction, environment);
            }
            Map> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapValues(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> get(Object criteria) {
        if(criteria instanceof BuildableState) {
            return get(((BuildableState) criteria).build());
        }
        else {
            throw new IllegalArgumentException(
                    criteria + " is not a valid argument for the get method");
        }
    }

    @Override
    public  Map> get(Object criteria,
            Timestamp timestamp) {
        if(criteria instanceof BuildableState) {
            return get(((BuildableState) criteria).build(), timestamp);
        }
        else {
            throw new IllegalArgumentException(
                    criteria + " is not a valid argument for the get method");
        }
    }

    @Override
    public  Map> get(String ccl) {
        return execute(() -> {
            Map> raw = client.getCcl(ccl, creds,
                    transaction, environment);
            Map> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapValues(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @SuppressWarnings("unchecked")
    @Override
    public  Map get(String key, Collection records) {
        return execute(() -> {
            Map raw = client.getKeyRecords(key,
                    Collections.toLongList(records), creds, transaction,
                    environment);
            Map pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record", key);
            for (Entry entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        (T) Convert.thriftToJava(entry.getValue()));
            }
            return pretty;
        });
    }

    @SuppressWarnings("unchecked")
    @Override
    public  Map get(String key, Collection records,
            Timestamp timestamp) {
        return execute(() -> {
            Map raw;
            if(timestamp.isString()) {
                raw = client.getKeyRecordsTimestr(key,
                        Collections.toLongList(records), timestamp.toString(),
                        creds, transaction, environment);
            }
            else {
                raw = client.getKeyRecordsTime(key,
                        Collections.toLongList(records), timestamp.getMicros(),
                        creds, transaction, environment);
            }
            Map pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record", key);
            for (Entry entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        (T) Convert.thriftToJava(entry.getValue()));
            }
            return pretty;
        });
    }

    @SuppressWarnings("unchecked")
    @Override
    public  Map get(String key, Criteria criteria) {
        return execute(() -> {
            Map raw = client.getKeyCriteria(key,
                    Language.translateToThriftCriteria(criteria), creds,
                    transaction, environment);
            Map pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record", key);
            for (Entry entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        (T) Convert.thriftToJava(entry.getValue()));
            }
            return pretty;
        });
    }

    @SuppressWarnings("unchecked")
    @Override
    public  Map get(String key, Criteria criteria,
            Timestamp timestamp) {
        return execute(() -> {
            Map raw;
            if(timestamp.isString()) {
                raw = client.getKeyCriteriaTimestr(key,
                        Language.translateToThriftCriteria(criteria),
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                raw = client.getKeyCriteriaTime(key,
                        Language.translateToThriftCriteria(criteria),
                        timestamp.getMicros(), creds, transaction, environment);
            }
            Map pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record", key);
            for (Entry entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        (T) Convert.thriftToJava(entry.getValue()));
            }
            return pretty;
        });
    }

    @SuppressWarnings("unchecked")
    @Override
    public  T get(String key, long record) {
        return execute(() -> {
            TObject raw = client.getKeyRecord(key, record, creds, transaction,
                    environment);
            return raw == TObject.NULL ? null : (T) Convert.thriftToJava(raw);
        });
    }

    @SuppressWarnings("unchecked")
    @Override
    public  T get(String key, long record, Timestamp timestamp) {
        return execute(() -> {
            TObject raw;
            if(timestamp.isString()) {
                raw = client.getKeyRecordTimestr(key, record,
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                raw = client.getKeyRecordTime(key, record,
                        timestamp.getMicros(), creds, transaction, environment);
            }
            return raw == TObject.NULL ? null : (T) Convert.thriftToJava(raw);
        });
    }

    @Override
    public  Map get(String key, Object criteria) {
        if(criteria instanceof BuildableState) {
            return get(key, ((BuildableState) criteria).build());
        }
        else {
            throw new IllegalArgumentException(
                    criteria + " is not a valid argument for the get method");
        }
    }

    @Override
    public  Map get(String key, Object criteria,
            Timestamp timestamp) {
        if(criteria instanceof BuildableState) {
            return get(key, ((BuildableState) criteria).build(), timestamp);
        }
        else {
            throw new IllegalArgumentException(
                    criteria + " is not a valid argument for the get method");
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public  Map get(String key, String ccl) {
        return execute(() -> {
            Map raw = client.getKeyCcl(key, ccl, creds,
                    transaction, environment);
            Map pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record", key);
            for (Entry entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        (T) Convert.thriftToJava(entry.getValue()));
            }
            return pretty;
        });
    }

    @SuppressWarnings("unchecked")
    @Override
    public  Map get(String key, String ccl, Timestamp timestamp) {
        return execute(() -> {
            Map raw;
            if(timestamp.isString()) {
                raw = client.getKeyCclTimestr(key, ccl, timestamp.toString(),
                        creds, transaction, environment);
            }
            else {
                raw = client.getKeyCclTime(key, ccl, timestamp.getMicros(),
                        creds, transaction, environment);
            }
            Map pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record", key);
            for (Entry entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        (T) Convert.thriftToJava(entry.getValue()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> get(String ccl, Timestamp timestamp) {
        return execute(() -> {
            Map> raw;
            if(timestamp.isString()) {
                raw = client.getCclTimestr(ccl, timestamp.toString(), creds,
                        transaction, environment);
            }
            else {
                raw = client.getCclTime(ccl, timestamp.getMicros(), creds,
                        transaction, environment);
            }
            Map> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapValues(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public String getServerEnvironment() {
        return execute(() -> {
            return client.getServerEnvironment(creds, transaction, environment);
        });
    }

    @Override
    public String getServerVersion() {
        return execute(() -> {
            return client.getServerVersion();
        });
    }

    @Override
    public Set insert(String json) {
        return execute(() -> {
            return client.insertJson(json, creds, transaction, environment);
        });
    }

    @Override
    public Map insert(String json, Collection records) {
        return execute(() -> {
            return client.insertJsonRecords(json,
                    Collections.toLongList(records), creds, transaction,
                    environment);
        });
    }

    @Override
    public boolean insert(String json, long record) {
        return execute(() -> {
            return client.insertJsonRecord(json, record, creds, transaction,
                    environment);
        });
    }

    @Override
    public Set inventory() {
        return execute(() -> {
            return client.inventory(creds, transaction, environment);
        });
    }

    @Override
    public  T invokePlugin(String id, String method, Object... args) {
        return execute(() -> {
            List params = Lists
                    .newArrayListWithCapacity(args.length);
            for (Object arg : args) {
                params.add(ComplexTObject.fromJavaObject(arg));
            }
            ComplexTObject result = client.invokePlugin(id, method, params,
                    creds, transaction, environment);
            return result.getJavaObject();
        });
    }

    @Override
    public String jsonify(Collection records) {
        return jsonify(records, true);
    }

    @Override
    public String jsonify(Collection records, boolean includeId) {
        return execute(() -> {
            return client.jsonifyRecords(Collections.toLongList(records),
                    includeId, creds, transaction, environment);
        });
    }

    @Override
    public String jsonify(Collection records, Timestamp timestamp) {
        return jsonify(records, timestamp, true);
    }

    @Override
    public String jsonify(Collection records, Timestamp timestamp,
            boolean includeId) {
        return execute(() -> {
            if(timestamp.isString()) {
                return client.jsonifyRecordsTimestr(
                        Collections.toLongList(records), timestamp.toString(),
                        includeId, creds, transaction, environment);
            }
            else {
                return client.jsonifyRecordsTime(
                        Collections.toLongList(records), timestamp.getMicros(),
                        includeId, creds, transaction, environment);
            }
        });
    }

    @Override
    public String jsonify(long record) {
        return jsonify(java.util.Collections.singletonList(record), true);
    }

    @Override
    public String jsonify(long record, boolean includeId) {
        return jsonify(java.util.Collections.singletonList(record), includeId);
    }

    @Override
    public String jsonify(long record, Timestamp timestamp) {
        return jsonify(java.util.Collections.singletonList(record), timestamp,
                true);
    }

    @Override
    public String jsonify(long record, Timestamp timestamp, boolean includeId) {
        return jsonify(java.util.Collections.singletonList(record), timestamp,
                includeId);
    }

    @Override
    public Map link(String key, Collection destinations,
            long source) {
        Map result = PrettyLinkedHashMap
                .newPrettyLinkedHashMap("Record", "Result");
        for (long destination : destinations) {
            result.put(destination, link(key, destination, source));
        }
        return result;
    }

    @Override
    public boolean link(String key, long destination, long source) {
        return add(key, Link.to(destination), source);
    }

    @Override
    public  Map>> navigate(
            final Collection keys, final Collection records) {
        return execute(() -> {
            Map>> raw = client
                    .navigateKeysRecords(Collections.toList(keys),
                            Collections.toLongList(records), creds, transaction,
                            environment);
            return Thrift.transformRecordsKeysValues(raw);
        });
    }

    @Override
    public  Map>> navigate(
            final Collection keys, final Collection records,
            final Timestamp timestamp) {
        return execute(() -> {
            Map>> raw = client
                    .navigateKeysRecordsTime(Collections.toList(keys),
                            Collections.toLongList(records),
                            timestamp.getMicros(), creds, transaction,
                            environment);
            return Thrift.transformRecordsKeysValues(raw);
        });
    }

    @Override
    public  Map>> navigate(Collection keys,
            Criteria criteria) {
        return execute(() -> {
            Map>> raw = client
                    .navigateKeysCriteria(Collections.toList(keys),
                            Language.translateToThriftCriteria(criteria), creds,
                            transaction, environment);
            return Thrift.transformRecordsKeysValues(raw);
        });
    }

    @Override
    public  Map>> navigate(Collection keys,
            Criteria criteria, Timestamp timestamp) {
        return execute(() -> {
            Map>> raw;
            if(timestamp.isString()) {
                raw = client.navigateKeysCriteriaTimestr(
                        Collections.toList(keys),
                        Language.translateToThriftCriteria(criteria),
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                raw = client.navigateKeysCriteriaTime(Collections.toList(keys),
                        Language.translateToThriftCriteria(criteria),
                        timestamp.getMicros(), creds, transaction, environment);
            }
            return Thrift.transformRecordsKeysValues(raw);
        });
    }

    @Override
    public  Map>> navigate(
            final Collection keys, final long record) {
        return execute(() -> {
            Map>> raw = client
                    .navigateKeysRecord(Collections.toList(keys), record, creds,
                            transaction, environment);
            return Thrift.transformRecordsKeysValues(raw);
        });
    }

    @Override
    public  Map>> navigate(
            final Collection keys, final long record,
            final Timestamp timestamp) {
        return execute(() -> {
            Map>> raw;
            if(timestamp.isString()) {
                raw = client.navigateKeysRecordTimestr(Collections.toList(keys),
                        record, timestamp.toString(), creds, transaction,
                        environment);
            }
            else {
                raw = client.navigateKeysRecordTime(Collections.toList(keys),
                        record, timestamp.getMicros(), creds, transaction,
                        environment);
            }
            return Thrift.transformRecordsKeysValues(raw);
        });
    }

    @Override
    public  Map>> navigate(
            final Collection keys, final String ccl) {
        return execute(() -> {
            Map>> raw = client.navigateKeysCcl(
                    Collections.toList(keys), ccl, creds, transaction,
                    environment);
            return Thrift.transformRecordsKeysValues(raw);
        });
    }

    @Override
    public  Map>> navigate(
            final Collection keys, final String ccl,
            final Timestamp timestamp) {
        return execute(() -> {
            Map>> raw;
            if(timestamp.isString()) {
                raw = client.navigateKeysCclTimestr(Collections.toList(keys),
                        ccl, timestamp.toString(), creds, transaction,
                        environment);
            }
            else {
                raw = client.navigateKeysCclTime(Collections.toList(keys), ccl,
                        timestamp.getMicros(), creds, transaction, environment);
            }
            return Thrift.transformRecordsKeysValues(raw);
        });
    }

    @Override
    public  Map> navigate(final String key,
            final Collection records) {
        return execute(() -> {
            Map> raw = client.navigateKeyRecords(key,
                    Collections.toLongList(records), creds, transaction,
                    environment);
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record",
                            LinkNavigation.getNavigationSchemeDestination(key));
            return Thrift.transformRecordsValues(raw, pretty);
        });
    }

    @Override
    public  Map> navigate(final String key,
            final Collection records, final Timestamp timestamp) {
        return execute(() -> {
            Map> raw;
            if(timestamp.isString()) {
                raw = client.navigateKeyRecordsTimestr(key,
                        Collections.toLongList(records), timestamp.toString(),
                        creds, transaction, environment);
            }
            else {
                raw = client.navigateKeyRecordsTime(key,
                        Collections.toLongList(records), timestamp.getMicros(),
                        creds, transaction, environment);
            }
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record",
                            LinkNavigation.getNavigationSchemeDestination(key));
            return Thrift.transformRecordsValues(raw, pretty);
        });
    }

    @Override
    public  Map> navigate(final String key,
            final Criteria criteria) {
        return execute(() -> {
            Map> raw = client.navigateKeyCriteria(key,
                    Language.translateToThriftCriteria(criteria), creds,
                    transaction, environment);
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record",
                            LinkNavigation.getNavigationSchemeDestination(key));
            return Thrift.transformRecordsValues(raw, pretty);
        });
    }

    @Override
    public  Map> navigate(final String key,
            final Criteria criteria, final Timestamp timestamp) {
        return execute(() -> {
            Map> raw;
            if(timestamp.isString()) {
                raw = client.navigateKeyCriteriaTimestr(key,
                        Language.translateToThriftCriteria(criteria),
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                raw = client.navigateKeyCriteriaTime(key,
                        Language.translateToThriftCriteria(criteria),
                        timestamp.getMicros(), creds, transaction, environment);
            }
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record",
                            LinkNavigation.getNavigationSchemeDestination(key));
            return Thrift.transformRecordsValues(raw, pretty);
        });
    }

    @Override
    public  Map> navigate(final String key, final long record) {
        return execute(() -> {
            Map> raw = client.navigateKeyRecord(key, record,
                    creds, transaction, environment);
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record",
                            LinkNavigation.getNavigationSchemeDestination(key));
            return Thrift.transformRecordsValues(raw, pretty);
        });
    }

    @Override
    public  Map> navigate(final String key, final long record,
            final Timestamp timestamp) {
        return execute(() -> {
            Map> raw;
            if(timestamp.isString()) {
                raw = client.navigateKeyRecordTimestr(key, record,
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                raw = client.navigateKeyRecordTime(key, record,
                        timestamp.getMicros(), creds, transaction, environment);
            }
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record",
                            LinkNavigation.getNavigationSchemeDestination(key));
            return Thrift.transformRecordsValues(raw, pretty);
        });
    }

    @Override
    public  Map> navigate(final String key, final String ccl) {
        return execute(() -> {
            Map> raw = client.navigateKeyCcl(key, ccl, creds,
                    transaction, environment);
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record",
                            LinkNavigation.getNavigationSchemeDestination(key));
            return Thrift.transformRecordsValues(raw, pretty);
        });
    }

    @Override
    public  Map> navigate(final String key, final String ccl,
            final Timestamp timestamp) {
        return execute(() -> {
            Map> raw;
            if(timestamp.isString()) {
                raw = client.navigateKeyCclTimestr(key, ccl,
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                raw = client.navigateKeyCclTime(key, ccl, timestamp.getMicros(),
                        creds, transaction, environment);
            }
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record",
                            LinkNavigation.getNavigationSchemeDestination(key));
            return Thrift.transformRecordsValues(raw, pretty);
        });
    }

    @Override
    public Map ping(Collection records) {
        return execute(() -> {
            return client.pingRecords(Collections.toLongList(records), creds,
                    transaction, environment);
        });
    }

    @Override
    public boolean ping(long record) {
        return execute(() -> {
            return client.pingRecord(record, creds, transaction, environment);
        });
    }

    @Override
    public  void reconcile(String key, long record, Collection values) {
        execute(() -> {
            Set valueSet = Sets
                    .newHashSetWithExpectedSize(values.size());
            for (T value : values) {
                valueSet.add(Convert.javaToThrift(value));
            }
            client.reconcileKeyRecordValues(key, record, valueSet, creds,
                    transaction, environment);
            return null;
        });
    }

    @Override
    public  Map remove(String key, T value,
            Collection records) {
        return execute(() -> {
            Map raw = client.removeKeyValueRecords(key,
                    Convert.javaToThrift(value),
                    Collections.toLongList(records), creds, transaction,
                    environment);
            Map pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record", "Result");
            for (long record : records) {
                pretty.put(record, raw.get(record));
            }
            return pretty;
        });
    }

    @Override
    public  boolean remove(String key, T value, long record) {
        return execute(() -> {
            return client.removeKeyValueRecord(key, Convert.javaToThrift(value),
                    record, creds, transaction, environment);
        });
    }

    @Override
    public void revert(Collection keys, Collection records,
            Timestamp timestamp) {
        execute(() -> {
            if(timestamp.isString()) {
                client.revertKeysRecordsTimestr(Collections.toList(keys),
                        Collections.toLongList(records), timestamp.toString(),
                        creds, transaction, environment);
            }
            else {
                client.revertKeysRecordsTime(Collections.toList(keys),
                        Collections.toLongList(records), timestamp.getMicros(),
                        creds, transaction, environment);
            }
            return null;
        });
    }

    @Override
    public void revert(Collection keys, long record,
            Timestamp timestamp) {
        execute(() -> {
            if(timestamp.isString()) {
                client.revertKeysRecordTimestr(Collections.toList(keys), record,
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                client.revertKeysRecordTime(Collections.toList(keys), record,
                        timestamp.getMicros(), creds, transaction, environment);
            }
            return null;
        });
    }

    @Override
    public void revert(String key, Collection records,
            Timestamp timestamp) {
        execute(() -> {
            if(timestamp.isString()) {
                client.revertKeyRecordsTimestr(key,
                        Collections.toLongList(records), timestamp.toString(),
                        creds, transaction, environment);
            }
            else {
                client.revertKeyRecordsTime(key,
                        Collections.toLongList(records), timestamp.getMicros(),
                        creds, transaction, environment);
            }
            return null;
        });
    }

    @Override
    public void revert(String key, long record, Timestamp timestamp) {
        execute(() -> {
            if(timestamp.isString()) {
                client.revertKeyRecordTimestr(key, record, timestamp.toString(),
                        creds, transaction, environment);
            }
            else {
                client.revertKeyRecordTime(key, record, timestamp.getMicros(),
                        creds, transaction, environment);
            }
            return null;
        });
    }

    @Override
    public Set search(String key, String query) {
        return execute(() -> {
            return client.search(key, query, creds, transaction, environment);
        });
    }

    @Override
    public Map>> select(
            Collection records) {
        return execute(() -> {
            Map>> raw = client.selectRecords(
                    Collections.toLongList(records), creds, transaction,
                    environment);
            Map>> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry>> entry : raw
                    .entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapSet(entry.getValue(),
                                Conversions. none(),
                                Conversions.thriftToJava()));
            }
            return pretty;
        });
    }

    @Override
    public Map>> select(Collection records,
            Timestamp timestamp) {
        return execute(() -> {
            Map>> raw;
            if(timestamp.isString()) {
                raw = client.selectRecordsTimestr(
                        Collections.toLongList(records), timestamp.toString(),
                        creds, transaction, environment);
            }
            else {
                raw = client.selectRecordsTime(Collections.toLongList(records),
                        timestamp.getMicros(), creds, transaction, environment);
            }
            Map>> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry>> entry : raw
                    .entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapSet(entry.getValue(),
                                Conversions. none(),
                                Conversions.thriftToJava()));
            }
            return pretty;
        });
    }

    @Override
    public  Map>> select(Collection keys,
            Collection records) {
        return execute(() -> {
            Map>> raw = client.selectKeysRecords(
                    Collections.toList(keys), Collections.toLongList(records),
                    creds, transaction, environment);
            Map>> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry>> entry : raw
                    .entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapSet(entry.getValue(),
                                Conversions. none(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map>> select(Collection keys,
            Collection records, Timestamp timestamp) {
        return execute(() -> {
            Map>> raw;
            if(timestamp.isString()) {
                raw = client.selectKeysRecordsTimestr(Collections.toList(keys),
                        Collections.toLongList(records), timestamp.toString(),
                        creds, transaction, environment);
            }
            else {
                raw = client.selectKeysRecordsTime(Collections.toList(keys),
                        Collections.toLongList(records), timestamp.getMicros(),
                        creds, transaction, environment);
            }
            Map>> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry>> entry : raw
                    .entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapSet(entry.getValue(),
                                Conversions. none(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map>> select(Collection keys,
            Criteria criteria) {
        return execute(() -> {
            Map>> raw = client
                    .selectKeysCriteria(Collections.toList(keys),
                            Language.translateToThriftCriteria(criteria), creds,
                            transaction, environment);
            Map>> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry>> entry : raw
                    .entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapSet(entry.getValue(),
                                Conversions. none(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map>> select(Collection keys,
            Criteria criteria, Timestamp timestamp) {
        return execute(() -> {
            Map>> raw;
            if(timestamp.isString()) {
                raw = client.selectKeysCriteriaTimestr(Collections.toList(keys),
                        Language.translateToThriftCriteria(criteria),
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                raw = client.selectKeysCriteriaTime(Collections.toList(keys),
                        Language.translateToThriftCriteria(criteria),
                        timestamp.getMicros(), creds, transaction, environment);
            }
            Map>> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry>> entry : raw
                    .entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapSet(entry.getValue(),
                                Conversions. none(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> select(Collection keys,
            long record) {
        return execute(() -> {
            Map> raw = client.selectKeysRecord(
                    Collections.toList(keys), record, creds, transaction,
                    environment);
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Key", "Values");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformSetLazily(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> select(Collection keys, long record,
            Timestamp timestamp) {
        return execute(() -> {
            Map> raw;
            if(timestamp.isString()) {
                raw = client.selectKeysRecordTimestr(Collections.toList(keys),
                        record, timestamp.toString(), creds, transaction,
                        environment);
            }
            else {
                raw = client.selectKeysRecordTime(Collections.toList(keys),
                        record, timestamp.getMicros(), creds, transaction,
                        environment);
            }
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Key", "Values");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformSetLazily(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map>> select(Collection keys,
            Object criteria) {
        if(criteria instanceof BuildableState) {
            return select(keys, ((BuildableState) criteria).build());
        }
        else {
            throw new IllegalArgumentException(criteria
                    + " is not a valid argument for the select method");
        }
    }

    @Override
    public  Map>> select(Collection keys,
            Object criteria, Timestamp timestamp) {
        if(criteria instanceof BuildableState) {
            return select(keys, ((BuildableState) criteria).build(), timestamp);
        }
        else {
            throw new IllegalArgumentException(criteria
                    + " is not a valid argument for the select method");
        }
    }

    @Override
    public  Map>> select(Collection keys,
            String ccl) {
        return execute(() -> {
            Map>> raw = client.selectKeysCcl(
                    Collections.toList(keys), ccl, creds, transaction,
                    environment);
            Map>> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry>> entry : raw
                    .entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapSet(entry.getValue(),
                                Conversions. none(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map>> select(Collection keys,
            String ccl, Timestamp timestamp) {
        return execute(() -> {
            Map>> raw;
            if(timestamp.isString()) {
                raw = client.selectKeysCclTimestr(Collections.toList(keys), ccl,
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                raw = client.selectKeysCclTime(Collections.toList(keys), ccl,
                        timestamp.getMicros(), creds, transaction, environment);
            }
            Map>> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry>> entry : raw
                    .entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapSet(entry.getValue(),
                                Conversions. none(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map>> select(Criteria criteria) {
        return execute(() -> {
            Map>> raw = client.selectCriteria(
                    Language.translateToThriftCriteria(criteria), creds,
                    transaction, environment);
            Map>> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry>> entry : raw
                    .entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapSet(entry.getValue(),
                                Conversions. none(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map>> select(Criteria criteria,
            Timestamp timestamp) {
        return execute(() -> {
            Map>> raw;
            if(timestamp.isString()) {
                raw = client.selectCriteriaTimestr(
                        Language.translateToThriftCriteria(criteria),
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                raw = client.selectCriteriaTime(
                        Language.translateToThriftCriteria(criteria),
                        timestamp.getMicros(), creds, transaction, environment);
            }
            Map>> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry>> entry : raw
                    .entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapSet(entry.getValue(),
                                Conversions. none(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public Map> select(long record) {
        return execute(() -> {
            Map> raw = client.selectRecord(record, creds,
                    transaction, environment);
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Key", "Values");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(), Transformers.transformSetLazily(
                        entry.getValue(), Conversions.thriftToJava()));
            }
            return pretty;
        });
    }

    @Override
    public Map> select(long record, Timestamp timestamp) {
        return execute(() -> {
            Map> raw;
            if(timestamp.isString()) {
                raw = client.selectRecordTimestr(record, timestamp.toString(),
                        creds, transaction, environment);
            }
            else {
                raw = client.selectRecordTime(record, timestamp.getMicros(),
                        creds, transaction, environment);
            }
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Key", "Values");
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(), Transformers.transformSetLazily(
                        entry.getValue(), Conversions.thriftToJava()));
            }
            return pretty;
        });
    }

    @Override
    public  Map>> select(Object criteria) {
        if(criteria instanceof BuildableState) {
            return select(((BuildableState) criteria).build());
        }
        else {
            throw new IllegalArgumentException(
                    criteria + " is not a valid argument for the get method");
        }
    }

    @Override
    public  Map>> select(Object criteria,
            Timestamp timestamp) {
        if(criteria instanceof BuildableState) {
            return select(((BuildableState) criteria).build(), timestamp);
        }
        else {
            throw new IllegalArgumentException(
                    criteria + " is not a valid argument for the get method");
        }
    }

    @Override
    public  Map>> select(String ccl) {
        return execute(() -> {
            Map>> raw = client.selectCcl(ccl,
                    creds, transaction, environment);
            Map>> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry>> entry : raw
                    .entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapSet(entry.getValue(),
                                Conversions. none(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> select(String key, Collection records) {
        return execute(() -> {
            Map> raw = client.selectKeyRecords(key,
                    Collections.toLongList(records), creds, transaction,
                    environment);
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record", key);
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformSetLazily(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> select(String key, Collection records,
            Timestamp timestamp) {
        return execute(() -> {
            Map> raw;
            if(timestamp.isString()) {
                raw = client.selectKeyRecordsTimestr(key,
                        Collections.toLongList(records), timestamp.toString(),
                        creds, transaction, environment);
            }
            else {
                raw = client.selectKeyRecordsTime(key,
                        Collections.toLongList(records), timestamp.getMicros(),
                        creds, transaction, environment);
            }
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record", key);
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformSetLazily(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> select(String key, Criteria criteria) {
        return execute(() -> {
            Map> raw = client.selectKeyCriteria(key,
                    Language.translateToThriftCriteria(criteria), creds,
                    transaction, environment);
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record", key);
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformSetLazily(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> select(String key, Criteria criteria,
            Timestamp timestamp) {
        return execute(() -> {
            Map> raw;
            if(timestamp.isString()) {
                raw = client.selectKeyCriteriaTimestr(key,
                        Language.translateToThriftCriteria(criteria),
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                raw = client.selectKeyCriteriaTime(key,
                        Language.translateToThriftCriteria(criteria),
                        timestamp.getMicros(), creds, transaction, environment);
            }
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record", key);
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformSetLazily(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Set select(String key, long record) {
        return execute(() -> {
            Set values = client.selectKeyRecord(key, record, creds,
                    transaction, environment);
            return Transformers.transformSetLazily(values,
                    Conversions. thriftToJavaCasted());
        });
    }

    @Override
    public  Set select(String key, long record, Timestamp timestamp) {
        return execute(() -> {
            Set values;
            if(timestamp.isString()) {
                values = client.selectKeyRecordTimestr(key, record,
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                values = client.selectKeyRecordTime(key, record,
                        timestamp.getMicros(), creds, transaction, environment);
            }
            return Transformers.transformSetLazily(values,
                    Conversions. thriftToJavaCasted());
        });
    }

    @Override
    public  Map> select(String key, Object criteria) {
        if(criteria instanceof BuildableState) {
            return select(key, ((BuildableState) criteria).build());
        }
        else {
            throw new IllegalArgumentException(criteria
                    + " is not a valid argument for the select method");
        }
    }

    @Override
    public  Map> select(String key, Object criteria,
            Timestamp timestamp) {
        if(criteria instanceof BuildableState) {
            return select(key, ((BuildableState) criteria).build(), timestamp);
        }
        else {
            throw new IllegalArgumentException(criteria
                    + " is not a valid argument for the select method");
        }
    }

    @Override
    public  Map> select(String key, String ccl) {
        return execute(() -> {
            Map> raw = client.selectKeyCcl(key, ccl, creds,
                    transaction, environment);
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record", key);
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformSetLazily(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map> select(String key, String ccl,
            Timestamp timestamp) {
        return execute(() -> {
            Map> raw;
            if(timestamp.isString()) {
                raw = client.selectKeyCclTimestr(key, ccl, timestamp.toString(),
                        creds, transaction, environment);
            }
            else {
                raw = client.selectKeyCclTime(key, ccl, timestamp.getMicros(),
                        creds, transaction, environment);
            }
            Map> pretty = PrettyLinkedHashMap
                    .newPrettyLinkedHashMap("Record", key);
            for (Entry> entry : raw.entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformSetLazily(entry.getValue(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public  Map>> select(String ccl,
            Timestamp timestamp) {
        return execute(() -> {
            Map>> raw;
            if(timestamp.isString()) {
                raw = client.selectCclTimestr(ccl, timestamp.toString(), creds,
                        transaction, environment);
            }
            else {
                raw = client.selectCclTime(ccl, timestamp.getMicros(), creds,
                        transaction, environment);
            }
            Map>> pretty = PrettyLinkedTableMap
                    .newPrettyLinkedTableMap("Record");
            for (Entry>> entry : raw
                    .entrySet()) {
                pretty.put(entry.getKey(),
                        Transformers.transformMapSet(entry.getValue(),
                                Conversions. none(),
                                Conversions. thriftToJavaCasted()));
            }
            return pretty;
        });
    }

    @Override
    public void set(String key, Object value, Collection records) {
        execute(() -> {
            client.setKeyValueRecords(key, Convert.javaToThrift(value),
                    Collections.toLongList(records), creds, transaction,
                    environment);
            return null;
        });
    }

    @Override
    public  void set(String key, T value, long record) {
        execute(() -> {
            client.setKeyValueRecord(key, Convert.javaToThrift(value), record,
                    creds, transaction, environment);
            return null;
        });
    }

    @Override
    public void stage() throws TransactionException {
        execute(() -> {
            transaction = client.stage(creds, environment);
            return null;
        });
    }

    @Override
    public Timestamp time() {
        return execute(() -> {
            return Timestamp
                    .fromMicros(client.time(creds, transaction, environment));
        });
    }

    @Override
    public Timestamp time(String phrase) {
        return execute(() -> {
            return Timestamp.fromMicros(
                    client.timePhrase(phrase, creds, transaction, environment));
        });
    }

    @Override
    public String toString() {
        return "Connected to " + host + ":" + port + " as "
                + new String(ClientSecurity.decrypt(username).array());
    }

    @Override
    public boolean unlink(String key, long destination, long source) {
        return remove(key, Link.to(destination), source);
    }

    @Override
    public boolean verify(String key, Object value, long record) {
        return execute(() -> {
            return client.verifyKeyValueRecord(key, Convert.javaToThrift(value),
                    record, creds, transaction, environment);
        });
    }

    @Override
    public boolean verify(String key, Object value, long record,
            Timestamp timestamp) {
        return execute(() -> {
            if(timestamp.isString()) {
                return client.verifyKeyValueRecordTimestr(key,
                        Convert.javaToThrift(value), record,
                        timestamp.toString(), creds, transaction, environment);
            }
            else {
                return client.verifyKeyValueRecordTime(key,
                        Convert.javaToThrift(value), record,
                        timestamp.getMicros(), creds, transaction, environment);
            }
        });
    }

    @Override
    public boolean verifyAndSwap(String key, Object expected, long record,
            Object replacement) {
        return execute(() -> {
            return client.verifyAndSwap(key, Convert.javaToThrift(expected),
                    record, Convert.javaToThrift(replacement), creds,
                    transaction, environment);
        });
    }

    @Override
    public void verifyOrSet(String key, Object value, long record) {
        execute(() -> {
            client.verifyOrSet(key, Convert.javaToThrift(value), record, creds,
                    transaction, environment);
            return null;
        });
    }

    /**
     * Return the current {@link AccessToken}
     * 
     * @return the creds
     */
    AccessToken creds() {
        return creds;
    }

    /**
     * Return the environment to which the driver is connected.
     * 
     * @return the environment
     */
    String environment() {
        return environment;
    }

    /**
     * Execute the task defined in {@code callable}. This method contains
     * retry logic to handle cases when {@code creds} expires and must be
     * updated.
     * 
     * @param callable
     * @return the task result
     */
     T execute(Callable callable) {
        try {
            return callable.call();
        }
        catch (SecurityException e) {
            authenticate();
            return execute(callable);
        }
        catch (com.cinchapi.concourse.thrift.TransactionException e) {
            throw new TransactionException();
        }
        catch (com.cinchapi.concourse.thrift.DuplicateEntryException e) {
            throw new DuplicateEntryException(e);
        }
        catch (com.cinchapi.concourse.thrift.InvalidArgumentException e) {
            throw new InvalidArgumentException(e);
        }
        catch (com.cinchapi.concourse.thrift.ParseException e) {
            throw new ParseException(e);
        }
        catch (com.cinchapi.concourse.thrift.PermissionException e) {
            throw new PermissionException(e);
        }
        catch (com.cinchapi.concourse.thrift.ManagementException e) {
            throw new ManagementException(e);
        }
        catch (Exception e) {
            throw CheckedExceptions.wrapAsRuntimeException(e);
        }
    }

    /**
     * Return the thrift RPC client.
     * 
     * @return the {@link ConcourseService#Client}
     */
    ConcourseService.Client thrift() {
        return client;
    }

    /**
     * Return the current {@link TransactionToken}.
     * 
     * @return the transaction token
     */
    @Nullable
    TransactionToken transaction() {
        return transaction;
    }

    @Override
    protected Concourse copyConnection() {
        return new ConcourseThriftDriver(host, port,
                ByteBuffers.getString(ClientSecurity.decrypt(username)),
                ByteBuffers.getString(ClientSecurity.decrypt(password)),
                environment);
    }

    /**
     * Authenticate the {@link #username} and {@link #password} and populate
     * {@link #creds} with the appropriate AccessToken.
     */
    private void authenticate() {
        try {
            creds = client.login(ClientSecurity.decrypt(username),
                    ClientSecurity.decrypt(password), environment);
        }
        catch (TException e) {
            throw CheckedExceptions.wrapAsRuntimeException(e);
        }
    }

    /**
     * Perform an old-school/simple find operation where {@code key}
     * satisfied {@code operation} in relation to the specified
     * {@code values}.
     * 
     * @param key
     * @param operator
     * @param values
     * @return the records that match the criteria.
     */
    private Set executeFind(final String key, final Object operator,
            final Object... values) {
        final List tValues = Lists.transform(
                Lists.newArrayList(values), Conversions.javaToThrift());
        return execute(() -> {
            if(operator instanceof Operator) {
                return client.findKeyOperatorValues(key, (Operator) operator,
                        tValues, creds, transaction, environment);
            }
            else {
                return client.findKeyOperatorstrValues(key, operator.toString(),
                        tValues, creds, transaction, environment);
            }
        });
    }

    /**
     * Perform an old-school/simple find operation where {@code key}
     * satisfied {@code operation} in relation to the specified
     * {@code values} at {@code timestamp}.
     * 
     * @param key
     * @param operator
     * @param values
     * @param timestamp a {@link Timestamp} that represents the historical
     *            instant to use in the lookup – created from either a
     *            {@link Timestamp#fromString(String) natural language
     *            description} of a point in time (i.e. two weeks ago), OR
     *            the {@link Timestamp#fromMicros(long) number
     *            of microseconds} since the Unix epoch, OR
     *            a {@link Timestamp#fromJoda(org.joda.time.DateTime) Joda
     *            DateTime} object
     * @return the records that match the criteria.
     */
    private Set executeFind(final Timestamp timestamp, final String key,
            final Object operator, final Object... values) {
        final List tValues = Lists.transform(
                Lists.newArrayList(values), Conversions.javaToThrift());
        return execute(() -> {
            if(operator instanceof Operator) {
                return client.findKeyOperatorValuesTime(key,
                        (Operator) operator, tValues, timestamp.getMicros(),
                        creds, transaction, environment);
            }
            else {
                return client.findKeyOperatorstrValuesTime(key,
                        operator.toString(), tValues, timestamp.getMicros(),
                        creds, transaction, environment);
            }
        });
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy