/*
* Copyright (c) 2013-2017 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.server;
import java.io.File;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.Executors;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.thrift.TException;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.server.TThreadPoolServer.Args;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;
import org.cliffc.high_scale_lib.NonBlockingHashMap;
import com.cinchapi.concourse.Constants;
import com.cinchapi.concourse.Link;
import com.cinchapi.concourse.Timestamp;
import com.cinchapi.concourse.annotate.Alias;
import com.cinchapi.concourse.annotate.Atomic;
import com.cinchapi.concourse.annotate.AutoRetry;
import com.cinchapi.concourse.annotate.Batch;
import com.cinchapi.concourse.annotate.HistoricalRead;
import com.cinchapi.concourse.annotate.VersionControl;
import com.cinchapi.concourse.lang.Language;
import com.cinchapi.concourse.lang.NaturalLanguage;
import com.cinchapi.concourse.lang.Parser;
import com.cinchapi.concourse.lang.PostfixNotationSymbol;
import com.cinchapi.concourse.security.AccessManager;
import com.cinchapi.concourse.server.http.HttpServer;
import com.cinchapi.concourse.server.io.FileSystem;
import com.cinchapi.concourse.server.jmx.ManagedOperation;
import com.cinchapi.concourse.server.management.ConcourseManagementService;
import com.cinchapi.concourse.server.plugin.PluginException;
import com.cinchapi.concourse.server.plugin.PluginManager;
import com.cinchapi.concourse.server.plugin.data.TObjectResultDataset;
import com.cinchapi.concourse.server.plugin.PluginRestricted;
import com.cinchapi.concourse.server.storage.AtomicOperation;
import com.cinchapi.concourse.server.storage.AtomicStateException;
import com.cinchapi.concourse.server.storage.BufferedStore;
import com.cinchapi.concourse.server.storage.AtomicSupport;
import com.cinchapi.concourse.server.storage.Engine;
import com.cinchapi.concourse.server.storage.Transaction;
import com.cinchapi.concourse.server.storage.TransactionStateException;
import com.cinchapi.concourse.server.upgrade.UpgradeTasks;
import com.cinchapi.concourse.shell.CommandLine;
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.DuplicateEntryException;
import com.cinchapi.concourse.thrift.InvalidArgumentException;
import com.cinchapi.concourse.thrift.Operator;
import com.cinchapi.concourse.thrift.ParseException;
import com.cinchapi.concourse.thrift.SecurityException;
import com.cinchapi.concourse.thrift.TCriteria;
import com.cinchapi.concourse.thrift.TObject;
import com.cinchapi.concourse.thrift.TransactionException;
import com.cinchapi.concourse.thrift.TransactionToken;
import com.cinchapi.concourse.thrift.Type;
import com.cinchapi.concourse.thrift.ConcourseService.Iface;
import com.cinchapi.concourse.time.Time;
import com.cinchapi.concourse.util.Convert;
import com.cinchapi.concourse.util.Environments;
import com.cinchapi.concourse.util.Logger;
import com.cinchapi.concourse.util.TMaps;
import com.cinchapi.concourse.util.Timestamps;
import com.cinchapi.concourse.util.Version;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gson.JsonParseException;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.matcher.Matchers;
import static com.cinchapi.concourse.server.GlobalState.*;
/**
* Accepts requests from clients to read and write data in Concourse. The server
* is configured with a {@code concourse.prefs} file.
*
* @author Jeff Nelson
*/
public class ConcourseServer extends BaseConcourseServer
implements ConcourseService.Iface {
/**
* Create a new {@link ConcourseServer} instance that uses the default port
* and storage locations or those defined in the accessible
* {@code concourse.prefs} file.
*
* Creates a new {@link ConcourseServer} for management running
* on {@link JMX_PORT} using {@code Thrift}
*
* @return {@link ConcourseServer}
* @throws TTransportException
*/
public static ConcourseServer create() throws TTransportException {
return create(CLIENT_PORT, BUFFER_DIRECTORY, DATABASE_DIRECTORY);
}
/**
* Create a new {@link ConcourseServer} instance that uses the specified
* port and storage locations.
* <p>
* In general, this factory should on be used by unit tests. Runtime
* construction of the server should be done using the
* {@link ConcourseServer#create()} method so that the preferences file is
* used.
* </p>
*
* @param port - the port on which to listen for client connections
* @param bufferStore - the location to store {@link Buffer} files
* @param dbStore - the location to store {@link Database} files
* @return {@link ConcourseServer}
* @throws TTransportException
*/
public static ConcourseServer create(int port, String bufferStore,
String dbStore) throws TTransportException {
Injector injector = Guice.createInjector(new ThriftModule());
ConcourseServer server = injector.getInstance(ConcourseServer.class);
server.init(port, bufferStore, dbStore);
return server;
}
/**
* Run the server...
*
* @param args
* @throws TTransportException
* @throws MalformedObjectNameException
* @throws NotCompliantMBeanException
* @throws MBeanRegistrationException
* @throws InstanceAlreadyExistsException
*/
public static void main(String... args) throws TTransportException,
MalformedObjectNameException, InstanceAlreadyExistsException,
MBeanRegistrationException, NotCompliantMBeanException {
// Run all the pending upgrade tasks
UpgradeTasks.runLatest();
// Ensure the application is properly configured
MemoryUsage heap = ManagementFactory.getMemoryMXBean()
.getHeapMemoryUsage();
if(heap.getInit() < MIN_HEAP_SIZE) {
System.err.println("Cannot initialize Concourse Server with "
+ "a heap smaller than " + MIN_HEAP_SIZE + " bytes");
System.exit(127);
}
// Create an instance of the server and all of its dependencies
final ConcourseServer server = ConcourseServer.create();
// Check if concourse is in inconsistent state.
if(GlobalState.SYSTEM_ID == null) {
throw new IllegalStateException(
"Concourse is in inconsistent state because "
+ "the System ID in the buffer and database directories are different");
}
// Start the server...
Thread serverThread = new Thread(new Runnable() {
@Override
public void run() {
try {
CommandLine.displayWelcomeBanner();
System.out.println("System ID: " + GlobalState.SYSTEM_ID);
server.start();
}
catch (TTransportException e) {
e.printStackTrace();
System.exit(-1);
}
}
}, "main");
serverThread.start();
// Prepare for graceful shutdown...
// NOTE: It may be necessary to run the Java VM with
// -Djava.net.preferIPv4Stack=true
final Thread shutdownThread = new Thread(new Runnable() {
@Override
public void run() {
try {
ServerSocket socket = new ServerSocket(SHUTDOWN_PORT);
socket.accept(); // block until a shutdown request is made
Logger.info("Shutdown request received");
server.stop();
socket.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
}, "Shutdown");
shutdownThread.setDaemon(true);
shutdownThread.start();
// "Warm up" the ANTLR parsing engine in the background
new Thread(new Runnable() {
@Override
public void run() {
NaturalLanguage.parseMicros("now");
}
}).start();
// Add a shutdown hook that launches the official {@link ShutdownRunner}
// in cases where the server process is directly killed (i.e. from the
// control script)
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
ShutdownRunner.main();
try {
shutdownThread.join();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
/**
* Return the appropriate collection for a result dataset, depending upon
* the execution thread.
*
* @return the result dataset collection
*/
private static Map<Long, Map<String, Set<TObject>>> emptyResultDataset() {
return (INVOCATION_THREAD_CLASS == Thread.currentThread().getClass())
? new TObjectResultDataset() : Maps.newLinkedHashMap();
}
/**
* Return the appropriate collection for a result dataset, depending upon
* the execution thread.
*
* @param capacity the initial capacity for the dataset collection
* @return the result dataset collection
*/
private static Map<Long, Map<String, Set<TObject>>> emptyResultDatasetWithCapacity(
int capacity) {
return (INVOCATION_THREAD_CLASS == Thread.currentThread().getClass())
? new TObjectResultDataset()
: TMaps.newLinkedHashMapWithCapacity(capacity);
}
/**
* Return {@code true} if adding {@code link} to {@code record} is valid.
* This method is used to enforce referential integrity (i.e. record cannot
* link to itself) before the data makes it way to the Engine.
*
* @param link
* @param record
* @return {@code true} if the link is valid
*/
private static boolean isValidLink(Link link, long record) {
return link.longValue() != record;
}
/**
* Contains the credentials used by the {@link #accessManager}. This file is
* typically located in the root of the server installation.
*/
private static final String ACCESS_FILE = ".access";
/**
* The minimum heap size required to run Concourse Server.
*/
private static final int MIN_HEAP_SIZE = 268435456; // 256 MB
/**
* The number of worker threads that Concourse Server uses.
*/
private static final int NUM_WORKER_THREADS = 100; // This may become
// configurable in a
// prefs file in a
// future release.
/**
* The AccessManager controls access to the server.
*/
private AccessManager accessManager;
/**
* The base location where the indexed buffer pages are stored.
*/
private String bufferStore;
/**
* The base location where the indexed database records are stored.
*/
private String dbStore;
/**
* A mapping from env to the corresponding Engine that controls all the
* logic for data storage and retrieval.
*/
private Map<String, Engine> engines;
/**
* A server for handling HTTP requests, if the {@code http_port} preference
* is configured.
*/
@Nullable
private HttpServer httpServer;
/**
* The Thrift server that handles all managed operations.
*/
private TServer mgmtServer;
/**
* The PluginManager seamlessly handles plugins that are running in separate
* JVMs.
*/
private PluginManager pluginManager;
/**
* The Thrift server controls the RPC protocol. Use
* https://github.com/m1ch1/mapkeeper/wiki/Thrift-Java-Servers-Compared for
* a reference.
*/
private TServer server;
/**
* The server maintains a collection of {@link Transaction} objects to
* ensure that client requests are properly routed. When the client makes a
* call to {@link #stage(AccessToken)}, a Transaction is started on the
* server and a {@link TransactionToken} is used for the client to reference
* that Transaction in future calls.
*/
private final Map<TransactionToken, Transaction> transactions = new NonBlockingHashMap<TransactionToken, Transaction>();
@Override
@ThrowsThriftExceptions
@PluginRestricted
public void abort(AccessToken creds, TransactionToken transaction,
String env) throws TException {
checkAccess(creds, transaction);
transactions.remove(transaction).abort();
}
@Override
@Atomic
@AutoRetry
@ThrowsThriftExceptions
public long addKeyValue(String key, TObject value, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
long record = 0;
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
record = Time.now();
Operations.addIfEmptyAtomic(key, value, record, atomic);
}
catch (AtomicStateException e) {
atomic = null;
}
}
return record;
}
@Override
@ThrowsThriftExceptions
public boolean addKeyValueRecord(String key, TObject value, long record,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
if(value.getType() != Type.LINK
|| isValidLink((Link) Convert.thriftToJava(value), record)) {
return ((BufferedStore) getStore(transaction, environment)).add(key,
value, record);
}
else {
return false;
}
}
@Override
@Atomic
@Batch
@ThrowsThriftExceptions
public Map<Long, Boolean> addKeyValueRecords(String key, TObject value,
List<Long> records, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Boolean> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (long record : records) {
result.put(record, atomic.add(key, value, record));
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
return result;
}
@Override
@VersionControl
@ThrowsThriftExceptions
public Map<Long, String> auditKeyRecord(String key, long record,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
return getStore(transaction, environment).audit(key, record);
}
@Override
@Alias
@VersionControl
@ThrowsThriftExceptions
public Map<Long, String> auditKeyRecordStart(String key, long record,
long start, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
return auditKeyRecordStartEnd(key, record, start, Time.NONE, creds,
transaction, environment);
}
@Override
@VersionControl
@ThrowsThriftExceptions
public Map<Long, String> auditKeyRecordStartEnd(String key, long record,
long start, long end, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<Long, String> base = store.audit(key, record);
Map<Long, String> result = TMaps
.newLinkedHashMapWithCapacity(base.size());
int index = Timestamps.findNearestSuccessorForTimestamp(base.keySet(),
start);
Entry<Long, String> entry = null;
for (int i = index; i < base.size(); ++i) {
entry = Iterables.get(base.entrySet(), i);
if(entry.getKey() >= end) {
break;
}
result.put(entry.getKey(), entry.getValue());
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, String> auditKeyRecordStartstr(String key, long record,
String start, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
return auditKeyRecordStart(key, record,
NaturalLanguage.parseMicros(start), creds, transaction,
environment);
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, String> auditKeyRecordStartstrEndstr(String key,
long record, String start, String end, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return auditKeyRecordStartEnd(key, record,
NaturalLanguage.parseMicros(start),
NaturalLanguage.parseMicros(end), creds, transaction,
environment);
}
@Override
@VersionControl
@ThrowsThriftExceptions
public Map<Long, String> auditRecord(long record, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return getStore(transaction, environment).audit(record);
}
@Override
@Alias
@VersionControl
@ThrowsThriftExceptions
public Map<Long, String> auditRecordStart(long record, long start,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
return auditRecordStartEnd(record, start, Time.NONE, creds, transaction,
environment);
}
@Override
@VersionControl
@ThrowsThriftExceptions
public Map<Long, String> auditRecordStartEnd(long record, long start,
long end, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<Long, String> base = store.audit(record);
Map<Long, String> result = TMaps
.newLinkedHashMapWithCapacity(base.size());
int index = Timestamps.findNearestSuccessorForTimestamp(base.keySet(),
start);
Entry<Long, String> entry = null;
for (int i = index; i < base.size(); ++i) {
entry = Iterables.get(base.entrySet(), i);
if(entry.getKey() >= end) {
break;
}
result.put(entry.getKey(), entry.getValue());
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, String> auditRecordStartstr(long record, String start,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
return auditRecordStart(record, NaturalLanguage.parseMicros(start),
creds, transaction, environment);
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, String> auditRecordStartstrEndstr(long record,
String start, String end, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return auditRecordStartEnd(record, NaturalLanguage.parseMicros(start),
NaturalLanguage.parseMicros(end), creds, transaction,
environment);
}
@Override
@ThrowsThriftExceptions
public TObject averageKey(String key, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number average = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
average = Operations.avgKeyAtomic(key, Time.NONE, atomic);
}
catch (AtomicStateException e) {
atomic = null;
average = 0;
}
}
return Convert.javaToThrift(average);
}
@Override
@ThrowsThriftExceptions
public TObject averageKeyCcl(String key, String ccl, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
try {
Queue<PostfixNotationSymbol> queue = Parser.toPostfixNotation(ccl);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number average = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
average = Operations.avgKeyRecordsAtomic(key, records,
Time.NONE, atomic);
}
catch (AtomicStateException e) {
average = 0;
atomic = null;
}
}
return Convert.javaToThrift(average);
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Override
@ThrowsThriftExceptions
public TObject averageKeyCclTime(String key, String ccl, long timestamp,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
try {
Queue<PostfixNotationSymbol> queue = Parser.toPostfixNotation(ccl);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number average = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
average = Operations.avgKeyRecordsAtomic(key, records,
timestamp, atomic);
}
catch (AtomicStateException e) {
average = 0;
atomic = null;
}
}
return Convert.javaToThrift(average);
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Override
@ThrowsThriftExceptions
public TObject averageKeyCriteria(String key, TCriteria criteria,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number average = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
average = Operations.avgKeyRecordsAtomic(key, records,
Time.NONE, atomic);
}
catch (AtomicStateException e) {
average = 0;
atomic = null;
}
}
return Convert.javaToThrift(average);
}
@Override
@ThrowsThriftExceptions
public TObject averageKeyCriteriaTime(String key, TCriteria criteria,
long timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number average = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
average = Operations.avgKeyRecordsAtomic(key, records,
timestamp, atomic);
}
catch (AtomicStateException e) {
average = 0;
atomic = null;
}
}
return Convert.javaToThrift(average);
}
@Override
@ThrowsThriftExceptions
public TObject averageKeyRecord(String key, long record, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number average = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
average = Operations.avgKeyRecordAtomic(key, record, Time.NONE,
atomic);
}
catch (AtomicStateException e) {
atomic = null;
average = 0;
}
}
return Convert.javaToThrift(average);
}
@Override
@ThrowsThriftExceptions
public TObject averageKeyRecords(String key, List<Long> records,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number average = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
average = Operations.avgKeyRecordsAtomic(key, records,
Time.NONE, atomic);
}
catch (AtomicStateException e) {
atomic = null;
average = 0;
}
}
return Convert.javaToThrift(average);
}
@Override
@ThrowsThriftExceptions
public TObject averageKeyRecordsTime(String key, List<Long> records,
long timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number average = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
average = Operations.avgKeyRecordsAtomic(key, records,
timestamp, atomic);
}
catch (AtomicStateException e) {
atomic = null;
average = 0;
}
}
return Convert.javaToThrift(average);
}
@Override
@ThrowsThriftExceptions
public TObject averageKeyRecordTime(String key, long record, long timestamp,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number average = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
average = Operations.avgKeyRecordAtomic(key, record, timestamp,
atomic);
}
catch (AtomicStateException e) {
atomic = null;
average = 0;
}
}
return Convert.javaToThrift(average);
}
@Override
@ThrowsThriftExceptions
public TObject averageKeyTime(String key, long timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws SecurityException, TransactionException, TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number average = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
average = Operations.avgKeyAtomic(key, timestamp, atomic);
}
catch (AtomicStateException e) {
atomic = null;
average = 0;
}
}
return Convert.javaToThrift(average);
}
@Override
@ThrowsThriftExceptions
public Map<TObject, Set<Long>> browseKey(String key, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
return getStore(transaction, environment).browse(key);
}
@Override
@Atomic
@Batch
@ThrowsThriftExceptions
public Map<String, Map<TObject, Set<Long>>> browseKeys(List<String> keys,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<String, Map<TObject, Set<Long>>> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (String key : keys) {
result.put(key, atomic.browse(key));
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
return result;
}
@Override
@Batch
@HistoricalRead
@ThrowsThriftExceptions
public Map<String, Map<TObject, Set<Long>>> browseKeysTime(
List<String> keys, long timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<String, Map<TObject, Set<Long>>> result = TMaps
.newLinkedHashMapWithCapacity(keys.size());
for (String key : keys) {
result.put(key, store.browse(key));
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<String, Map<TObject, Set<Long>>> browseKeysTimestr(
List<String> keys, String timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return browseKeysTime(keys, NaturalLanguage.parseMicros(timestamp),
creds, transaction, environment);
}
@Override
@HistoricalRead
@ThrowsThriftExceptions
public Map<TObject, Set<Long>> browseKeyTime(String key, long timestamp,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
return getStore(transaction, environment).browse(key, timestamp);
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<TObject, Set<Long>> browseKeyTimestr(String key,
String timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
return browseKeyTime(key, NaturalLanguage.parseMicros(timestamp), creds,
transaction, environment);
}
@Override
@Atomic
@AutoRetry
@ThrowsThriftExceptions
public Map<Long, Set<TObject>> chronologizeKeyRecord(String key,
long record, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
return store.chronologize(key, record, 0, Time.now());
}
@Override
@Alias
@AutoRetry
@ThrowsThriftExceptions
public Map<Long, Set<TObject>> chronologizeKeyRecordStart(String key,
long record, long start, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
return store.chronologize(key, record, start, Time.NONE);
}
@Override
@AutoRetry
@ThrowsThriftExceptions
public Map<Long, Set<TObject>> chronologizeKeyRecordStartEnd(String key,
long record, long start, long end, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
return store.chronologize(key, record, start, end);
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, Set<TObject>> chronologizeKeyRecordStartstr(String key,
long record, String start, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
return store.chronologize(key, record,
NaturalLanguage.parseMicros(start), Time.now());
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, Set<TObject>> chronologizeKeyRecordStartstrEndstr(
String key, long record, String start, String end,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
return store.chronologize(key, record,
NaturalLanguage.parseMicros(start),
NaturalLanguage.parseMicros(end));
}
@Override
@Atomic
@AutoRetry
@ThrowsThriftExceptions
public void clearKeyRecord(String key, long record, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Operations.clearKeyRecordAtomic(key, record, atomic);
}
catch (AtomicStateException e) {
atomic = null;
}
}
}
@Override
@AutoRetry
@Atomic
@Batch
@ThrowsThriftExceptions
public void clearKeyRecords(String key, List<Long> records,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (long record : records) {
Operations.clearKeyRecordAtomic(key, record, atomic);
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
}
@Override
@AutoRetry
@Atomic
@Batch
@ThrowsThriftExceptions
public void clearKeysRecord(List<String> keys, long record,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (String key : keys) {
Operations.clearKeyRecordAtomic(key, record, atomic);
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
}
@Override
@AutoRetry
@Atomic
@Batch
@ThrowsThriftExceptions
public void clearKeysRecords(List<String> keys, List<Long> records,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (long record : records) {
for (String key : keys) {
Operations.clearKeyRecordAtomic(key, record, atomic);
}
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
}
@Override
@Atomic
@AutoRetry
@ThrowsThriftExceptions
public void clearRecord(long record, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Operations.clearRecordAtomic(record, atomic);
}
catch (AtomicStateException e) {
atomic = null;
}
}
}
@Override
@AutoRetry
@Atomic
@Batch
@ThrowsThriftExceptions
public void clearRecords(List<Long> records, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (long record : records) {
Operations.clearRecordAtomic(record, atomic);
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
}
@Override
@ThrowsThriftExceptions
@PluginRestricted
public boolean commit(AccessToken creds, TransactionToken transaction,
String env) throws TException {
checkAccess(creds, transaction);
return transactions.remove(transaction).commit();
}
@Override
@ThrowsThriftExceptions
public Set<String> describeRecord(long record, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
return getStore(transaction, environment).describe(record);
}
@Override
@Atomic
@Batch
@ThrowsThriftExceptions
public Map<Long, Set<String>> describeRecords(List<Long> records,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Set<String>> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (long record : records) {
result.put(record, atomic.describe(record));
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
return result;
}
@Override
@Batch
@HistoricalRead
@ThrowsThriftExceptions
public Map<Long, Set<String>> describeRecordsTime(List<Long> records,
long timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Set<String>> result = TMaps
.newLinkedHashMapWithCapacity(records.size());
for (long record : records) {
result.put(record, store.describe(record, timestamp));
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, Set<String>> describeRecordsTimestr(List<Long> records,
String timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
return describeRecordsTime(records,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@HistoricalRead
@ThrowsThriftExceptions
public Set<String> describeRecordTime(long record, long timestamp,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
return getStore(transaction, environment).describe(record, timestamp);
}
@Override
@Alias
@ThrowsThriftExceptions
public Set<String> describeRecordTimestr(long record, String timestamp,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
return describeRecordTime(record,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@ThrowsThriftExceptions
public Map<Diff, Set<TObject>> diffKeyRecordStart(String key, long record,
long start, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
return diffKeyRecordStartEnd(key, record, start,
Timestamp.now().getMicros(), creds, transaction, environment);
}
@Override
@ThrowsThriftExceptions
public Map<Diff, Set<TObject>> diffKeyRecordStartEnd(String key,
long record, long start, long end, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Set<TObject> startValues = null;
Set<TObject> endValues = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
startValues = store.select(key, record, start);
endValues = store.select(key, record, end);
}
catch (AtomicStateException e) {
atomic = null;
}
}
Map<Diff, Set<TObject>> result = Maps.newHashMapWithExpectedSize(2);
Set<TObject> xor = Sets.symmetricDifference(startValues, endValues);
int expectedSize = xor.size() / 2;
Set<TObject> added = Sets.newHashSetWithExpectedSize(expectedSize);
Set<TObject> removed = Sets.newHashSetWithExpectedSize(expectedSize);
for (TObject current : xor) {
if(!startValues.contains(current))
added.add(current);
else {
removed.add(current);
}
}
if(!added.isEmpty()) {
result.put(Diff.ADDED, added);
}
if(!removed.isEmpty()) {
result.put(Diff.REMOVED, removed);
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Diff, Set<TObject>> diffKeyRecordStartstr(String key,
long record, String start, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return diffKeyRecordStart(key, record,
NaturalLanguage.parseMicros(start), creds, transaction,
environment);
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Diff, Set<TObject>> diffKeyRecordStartstrEndstr(String key,
long record, String start, String end, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return diffKeyRecordStartEnd(key, record,
NaturalLanguage.parseMicros(start),
NaturalLanguage.parseMicros(end), creds, transaction,
environment);
}
@Override
@ThrowsThriftExceptions
public Map<TObject, Map<Diff, Set<Long>>> diffKeyStart(String key,
long start, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
return diffKeyStartEnd(key, start, Timestamp.now().getMicros(), creds,
transaction, environment);
}
@Override
@ThrowsThriftExceptions
public Map<TObject, Map<Diff, Set<Long>>> diffKeyStartEnd(String key,
long start, long end, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Map<TObject, Set<Long>> startData = null;
Map<TObject, Set<Long>> endData = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
startData = store.browse(key, start);
endData = store.browse(key, end);
}
catch (AtomicStateException e) {
atomic = null;
}
}
Set<TObject> startValues = startData.keySet();
Set<TObject> endValues = endData.keySet();
Set<TObject> xor = Sets.symmetricDifference(startValues, endValues);
Set<TObject> intersection = startValues.size() < endValues.size()
? Sets.intersection(startValues, endValues)
: Sets.intersection(endValues, startValues);
Map<TObject, Map<Diff, Set<Long>>> result = TMaps
.newLinkedHashMapWithCapacity(xor.size() + intersection.size());
for (TObject value : xor) {
Map<Diff, Set<Long>> entry = Maps.newHashMapWithExpectedSize(1);
if(!startValues.contains(value)) {
entry.put(Diff.ADDED, endData.get(value));
}
else {
entry.put(Diff.REMOVED, endData.get(value));
}
result.put(value, entry);
}
for (TObject value : intersection) {
Set<Long> startRecords = startData.get(value);
Set<Long> endRecords = endData.get(value);
Set<Long> xorRecords = Sets.symmetricDifference(startRecords,
endRecords);
if(!xorRecords.isEmpty()) {
Set<Long> added = Sets
.newHashSetWithExpectedSize(xorRecords.size());
Set<Long> removed = Sets
.newHashSetWithExpectedSize(xorRecords.size());
for (Long record : xorRecords) {
if(!startRecords.contains(record)) {
added.add(record);
}
else {
removed.add(record);
}
}
Map<Diff, Set<Long>> entry = Maps.newHashMapWithExpectedSize(2);
if(!added.isEmpty()) {
entry.put(Diff.ADDED, added);
}
if(!removed.isEmpty()) {
entry.put(Diff.REMOVED, removed);
}
result.put(value, entry);
}
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<TObject, Map<Diff, Set<Long>>> diffKeyStartstr(String key,
String start, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
return diffKeyStart(key, NaturalLanguage.parseMicros(start), creds,
transaction, environment);
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<TObject, Map<Diff, Set<Long>>> diffKeyStartstrEndstr(String key,
String start, String end, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return diffKeyStartEnd(key, NaturalLanguage.parseMicros(start),
NaturalLanguage.parseMicros(end), creds, transaction,
environment);
}
@Override
@ThrowsThriftExceptions
public Map<String, Map<Diff, Set<TObject>>> diffRecordStart(long record,
long start, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
return diffRecordStartEnd(record, start, Timestamp.now().getMicros(),
creds, transaction, environment);
}
@Override
@ThrowsThriftExceptions
public Map<String, Map<Diff, Set<TObject>>> diffRecordStartEnd(long record,
long start, long end, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Map<String, Set<TObject>> startData = null;
Map<String, Set<TObject>> endData = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
startData = store.select(record, start);
endData = store.select(record, end);
}
catch (AtomicStateException e) {
atomic = null;
}
}
Set<String> startKeys = startData.keySet();
Set<String> endKeys = endData.keySet();
Set<String> xor = Sets.symmetricDifference(startKeys, endKeys);
Set<String> intersection = Sets.intersection(startKeys, endKeys);
Map<String, Map<Diff, Set<TObject>>> result = TMaps
.newLinkedHashMapWithCapacity(xor.size() + intersection.size());
for (String key : xor) {
Map<Diff, Set<TObject>> entry = Maps.newHashMapWithExpectedSize(1);
if(!startKeys.contains(key)) {
entry.put(Diff.ADDED, endData.get(key));
}
else {
entry.put(Diff.REMOVED, endData.get(key));
}
result.put(key, entry);
}
for (String key : intersection) {
Set<TObject> startValues = startData.get(key);
Set<TObject> endValues = endData.get(key);
Set<TObject> xorValues = Sets.symmetricDifference(startValues,
endValues);
if(!xorValues.isEmpty()) {
Set<TObject> added = Sets
.newHashSetWithExpectedSize(xorValues.size());
Set<TObject> removed = Sets
.newHashSetWithExpectedSize(xorValues.size());
for (TObject value : xorValues) {
if(!startValues.contains(value)) {
added.add(value);
}
else {
removed.add(value);
}
}
Map<Diff, Set<TObject>> entry = Maps
.newHashMapWithExpectedSize(2);
if(!added.isEmpty()) {
entry.put(Diff.ADDED, added);
}
if(!removed.isEmpty()) {
entry.put(Diff.REMOVED, removed);
}
result.put(key, entry);
}
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<String, Map<Diff, Set<TObject>>> diffRecordStartstr(long record,
String start, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
return diffRecordStart(record, NaturalLanguage.parseMicros(start),
creds, transaction, environment);
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<String, Map<Diff, Set<TObject>>> diffRecordStartstrEndstr(
long record, String start, String end, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return diffRecordStartEnd(record, NaturalLanguage.parseMicros(start),
NaturalLanguage.parseMicros(end), creds, transaction,
environment);
}
@Override
@ThrowsThriftExceptions
public Set<Long> findCcl(String ccl, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
try {
Queue<PostfixNotationSymbol> queue = Parser.toPostfixNotation(ccl);
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Operations.findAtomic(queue, stack, atomic);
}
catch (AtomicStateException e) {
atomic = null;
}
}
return Sets.newTreeSet(stack.pop());
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Override
@Atomic
@Batch
@ThrowsThriftExceptions
public Set<Long> findCriteria(TCriteria criteria, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Operations.findAtomic(queue, stack, atomic);
}
catch (AtomicStateException e) {
atomic = null;
}
}
return Sets.newTreeSet(stack.pop());
}
@Override
@Alias
@ThrowsThriftExceptions
public Set<Long> findKeyOperatorstrValues(String key, String operator,
List<TObject> values, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return findKeyOperatorValues(key, Convert.stringToOperator(operator),
values, creds, transaction, environment);
}
@Override
@Alias
@ThrowsThriftExceptions
public Set<Long> findKeyOperatorstrValuesTime(String key, String operator,
List<TObject> values, long timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return findKeyOperatorValuesTime(key,
Convert.stringToOperator(operator), values, timestamp, creds,
transaction, environment);
}
@Override
@Alias
@ThrowsThriftExceptions
public Set<Long> findKeyOperatorstrValuesTimestr(String key,
String operator, List<TObject> values, String timestamp,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
return findKeyOperatorstrValuesTime(key, operator, values,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@ThrowsThriftExceptions
public Set<Long> findKeyOperatorValues(String key, Operator operator,
List<TObject> values, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
TObject[] tValues = values.toArray(new TObject[values.size()]);
return getStore(transaction, environment).find(key, operator, tValues);
}
@Override
@HistoricalRead
@ThrowsThriftExceptions
public Set<Long> findKeyOperatorValuesTime(String key, Operator operator,
List<TObject> values, long timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
TObject[] tValues = values.toArray(new TObject[values.size()]);
return getStore(transaction, environment).find(timestamp, key, operator,
tValues);
}
@Override
@Alias
@ThrowsThriftExceptions
public Set<Long> findKeyOperatorValuesTimestr(String key, Operator operator,
List<TObject> values, String timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return findKeyOperatorValuesTime(key, operator, values,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@AutoRetry
@Atomic
@ThrowsThriftExceptions
public long findOrAddKeyValue(String key, TObject value, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Set<Long> records = Sets.newLinkedHashSetWithExpectedSize(1);
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
records.addAll(atomic.find(key, Operator.EQUALS, value));
if(records.isEmpty()) {
long record = Time.now();
Operations.addIfEmptyAtomic(key, value, record, atomic);
records.add(record);
}
}
catch (AtomicStateException e) {
records.clear();
atomic = null;
}
}
if(records.size() == 1) {
return Iterables.getOnlyElement(records);
}
else {
throw new DuplicateEntryException(
com.cinchapi.concourse.util.Strings.joinWithSpace("Found",
records.size(), "records that match", key, "=",
value));
}
}
@SuppressWarnings("unchecked")
@Override
@Atomic
@ThrowsThriftExceptions
public long findOrInsertCclJson(String ccl, String json, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
List<Multimap<String, Object>> objects = Lists
.newArrayList(Convert.jsonToJava(json));
AtomicSupport store = getStore(transaction, environment);
Set<Long> records = Sets.newLinkedHashSet();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Queue<PostfixNotationSymbol> queue = Parser
.toPostfixNotation(ccl);
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findOrInsertAtomic(records, objects, queue, stack,
atomic);
}
catch (AtomicStateException e) {
atomic = null;
records.clear();
}
}
if(records.size() == 1) {
return Iterables.getOnlyElement(records);
}
else {
throw new DuplicateEntryException(
com.cinchapi.concourse.util.Strings.joinWithSpace("Found",
records.size(), "records that match", ccl));
}
}
@SuppressWarnings("unchecked")
@Override
@Atomic
@ThrowsThriftExceptions
public long findOrInsertCriteriaJson(TCriteria criteria, String json,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
List<Multimap<String, Object>> objects = Lists
.newArrayList(Convert.jsonToJava(json));
AtomicSupport store = getStore(transaction, environment);
Set<Long> records = Sets.newLinkedHashSet();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findOrInsertAtomic(records, objects, queue, stack,
atomic);
}
catch (AtomicStateException e) {
atomic = null;
records.clear();
}
}
if(records.size() == 1) {
return Iterables.getOnlyElement(records);
}
else {
throw new DuplicateEntryException(
com.cinchapi.concourse.util.Strings.joinWithSpace("Found",
records.size(), "records that match", Language
.translateFromThriftCriteria(criteria)));
}
}
@Override
@ThrowsThriftExceptions
public Map<Long, Map<String, TObject>> getCcl(String ccl, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
try {
Queue<PostfixNotationSymbol> queue = Parser.toPostfixNotation(ccl);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, TObject>> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
Map<String, TObject> entry = TMaps
.newLinkedHashMapWithCapacity(
atomic.describe(record).size());
for (String key : atomic.describe(record)) {
try {
entry.put(key, Iterables
.getLast(atomic.select(key, record)));
}
catch (NoSuchElementException e) {
continue;
}
}
if(!entry.isEmpty()) {
result.put(record, entry);
}
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Override
@ThrowsThriftExceptions
public Map<Long, Map<String, TObject>> getCclTime(String ccl,
long timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
try {
Queue<PostfixNotationSymbol> queue = Parser.toPostfixNotation(ccl);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, TObject>> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
Map<String, TObject> entry = TMaps
.newLinkedHashMapWithCapacity(atomic
.describe(record, timestamp).size());
for (String key : atomic.describe(record, timestamp)) {
try {
entry.put(key, Iterables.getLast(
atomic.select(key, record, timestamp)));
}
catch (NoSuchElementException e) {
continue;
}
}
if(!entry.isEmpty()) {
result.put(record, entry);
}
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, Map<String, TObject>> getCclTimestr(String ccl,
String timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
return getCclTime(ccl, NaturalLanguage.parseMicros(timestamp), creds,
transaction, environment);
}
@Override
@ThrowsThriftExceptions
public Map<Long, Map<String, TObject>> getCriteria(TCriteria criteria,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, TObject>> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
Map<String, TObject> entry = TMaps
.newLinkedHashMapWithCapacity(
atomic.describe(record).size());
for (String key : atomic.describe(record)) {
try {
entry.put(key, Iterables
.getLast(atomic.select(key, record)));
}
catch (NoSuchElementException e) {
continue;
}
}
if(!entry.isEmpty()) {
result.put(record, entry);
}
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
@Override
@ThrowsThriftExceptions
public Map<Long, Map<String, TObject>> getCriteriaTime(TCriteria criteria,
long timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, TObject>> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
Map<String, TObject> entry = TMaps
.newLinkedHashMapWithCapacity(
atomic.describe(record, timestamp).size());
for (String key : atomic.describe(record, timestamp)) {
try {
entry.put(key, Iterables
.getLast(atomic.select(key, record)));
}
catch (NoSuchElementException e) {
continue;
}
}
if(!entry.isEmpty()) {
result.put(record, entry);
}
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, Map<String, TObject>> getCriteriaTimestr(
TCriteria criteria, String timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return getCriteriaTime(criteria, NaturalLanguage.parseMicros(timestamp),
creds, transaction, environment);
}
@Override
@ThrowsThriftExceptions
public Map<Long, TObject> getKeyCcl(String key, String ccl,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
try {
Queue<PostfixNotationSymbol> queue = Parser.toPostfixNotation(ccl);
AtomicSupport store = getStore(transaction, environment);
Map<Long, TObject> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
try {
result.put(record, Iterables
.getLast(atomic.select(key, record)));
}
catch (NoSuchElementException e) {
continue;
}
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Override
@ThrowsThriftExceptions
public Map<Long, TObject> getKeyCclTime(String key, String ccl,
long timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
try {
Queue<PostfixNotationSymbol> queue = Parser.toPostfixNotation(ccl);
AtomicSupport store = getStore(transaction, environment);
Map<Long, TObject> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
try {
result.put(record, Iterables.getLast(
atomic.select(key, record, timestamp)));
}
catch (NoSuchElementException e) {
continue;
}
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, TObject> getKeyCclTimestr(String key, String ccl,
String timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
return getKeyCclTime(key, ccl, NaturalLanguage.parseMicros(timestamp),
creds, transaction, environment);
}
@Override
@ThrowsThriftExceptions
public Map<Long, TObject> getKeyCriteria(String key, TCriteria criteria,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
AtomicSupport store = getStore(transaction, environment);
Map<Long, TObject> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
try {
result.put(record,
Iterables.getLast(atomic.select(key, record)));
}
catch (NoSuchElementException e) {
continue;
}
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
@Override
@ThrowsThriftExceptions
public Map<Long, TObject> getKeyCriteriaTime(String key, TCriteria criteria,
long timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
AtomicSupport store = getStore(transaction, environment);
Map<Long, TObject> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
try {
result.put(record, Iterables.getLast(
atomic.select(key, record, timestamp)));
}
catch (NoSuchElementException e) {
continue;
}
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, TObject> getKeyCriteriaTimestr(String key,
TCriteria criteria, String timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return getKeyCriteriaTime(key, criteria,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@ThrowsThriftExceptions
public TObject getKeyRecord(String key, long record, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
return Iterables.getLast(
getStore(transaction, environment).select(key, record),
TObject.NULL);
}
@Override
@Atomic
@Batch
@ThrowsThriftExceptions
public Map<Long, TObject> getKeyRecords(String key, List<Long> records,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<Long, TObject> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (long record : records) {
try {
result.put(record,
Iterables.getLast(atomic.select(key, record)));
}
catch (NoSuchElementException e) {
continue;
}
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
return result;
}
@Override
@Batch
@HistoricalRead
@ThrowsThriftExceptions
public Map<Long, TObject> getKeyRecordsTime(String key, List<Long> records,
long timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
Map<Long, TObject> result = TMaps
.newLinkedHashMapWithCapacity(records.size());
AtomicSupport store = getStore(transaction, environment);
for (long record : records) {
try {
result.put(record, Iterables
.getLast(store.select(key, record, timestamp)));
}
catch (NoSuchElementException e) {
continue;
}
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, TObject> getKeyRecordsTimestr(String key,
List<Long> records, String timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return getKeyRecordsTime(key, records,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@HistoricalRead
@ThrowsThriftExceptions
public TObject getKeyRecordTime(String key, long record, long timestamp,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
return Iterables.getLast(getStore(transaction, environment).select(key,
record, timestamp), TObject.NULL);
}
@Override
@Alias
@ThrowsThriftExceptions
public TObject getKeyRecordTimestr(String key, long record,
String timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
return getKeyRecordTime(key, record,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@ThrowsThriftExceptions
public Map<Long, Map<String, TObject>> getKeysCcl(List<String> keys,
String ccl, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
try {
Queue<PostfixNotationSymbol> queue = Parser.toPostfixNotation(ccl);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, TObject>> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
Map<String, TObject> entry = TMaps
.newLinkedHashMapWithCapacity(keys.size());
for (String key : keys) {
try {
entry.put(key, Iterables
.getLast(atomic.select(key, record)));
}
catch (NoSuchElementException e) {
continue;
}
}
if(!entry.isEmpty()) {
result.put(record, entry);
}
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Override
@ThrowsThriftExceptions
public Map<Long, Map<String, TObject>> getKeysCclTime(List<String> keys,
String ccl, long timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
try {
Queue<PostfixNotationSymbol> queue = Parser.toPostfixNotation(ccl);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, TObject>> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
Map<String, TObject> entry = TMaps
.newLinkedHashMapWithCapacity(keys.size());
for (String key : keys) {
try {
entry.put(key, Iterables.getLast(
atomic.select(key, record, timestamp)));
}
catch (NoSuchElementException e) {
continue;
}
}
if(!entry.isEmpty()) {
result.put(record, entry);
}
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, Map<String, TObject>> getKeysCclTimestr(List<String> keys,
String ccl, String timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return getKeysCclTime(keys, ccl, NaturalLanguage.parseMicros(timestamp),
creds, transaction, environment);
}
@Override
@ThrowsThriftExceptions
public Map<Long, Map<String, TObject>> getKeysCriteria(List<String> keys,
TCriteria criteria, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, TObject>> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
Map<String, TObject> entry = TMaps
.newLinkedHashMapWithCapacity(keys.size());
for (String key : keys) {
try {
entry.put(key, Iterables
.getLast(atomic.select(key, record)));
}
catch (NoSuchElementException e) {
continue;
}
}
if(!entry.isEmpty()) {
result.put(record, entry);
}
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
@Override
@ThrowsThriftExceptions
public Map<Long, Map<String, TObject>> getKeysCriteriaTime(
List<String> keys, TCriteria criteria, long timestamp,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, TObject>> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
Map<String, TObject> entry = TMaps
.newLinkedHashMapWithCapacity(keys.size());
for (String key : keys) {
try {
entry.put(key, Iterables.getLast(
atomic.select(key, record, timestamp)));
}
catch (NoSuchElementException e) {
continue;
}
}
if(!entry.isEmpty()) {
result.put(record, entry);
}
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, Map<String, TObject>> getKeysCriteriaTimestr(
List<String> keys, TCriteria criteria, String timestamp,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
return getKeysCriteriaTime(keys, criteria,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@Atomic
@Batch
@ThrowsThriftExceptions
public Map<String, TObject> getKeysRecord(List<String> keys, long record,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<String, TObject> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (String key : keys) {
try {
result.put(key,
Iterables.getLast(atomic.select(key, record)));
}
catch (NoSuchElementException e) {
continue;
}
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
return result;
}
@Override
@Atomic
@Batch
@ThrowsThriftExceptions
public Map<Long, Map<String, TObject>> getKeysRecords(List<String> keys,
List<Long> records, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, TObject>> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (long record : records) {
Map<String, TObject> entry = TMaps
.newLinkedHashMapWithCapacity(keys.size());
for (String key : keys) {
try {
entry.put(key, Iterables
.getLast(atomic.select(key, record)));
}
catch (NoSuchElementException e) {
continue;
}
}
if(!entry.isEmpty()) {
result.put(record, entry);
}
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
return result;
}
@Override
@Batch
@HistoricalRead
@ThrowsThriftExceptions
public Map<Long, Map<String, TObject>> getKeysRecordsTime(List<String> keys,
List<Long> records, long timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
Map<Long, Map<String, TObject>> result = TMaps
.newLinkedHashMapWithCapacity(records.size());
AtomicSupport store = getStore(transaction, environment);
for (long record : records) {
Map<String, TObject> entry = TMaps
.newLinkedHashMapWithCapacity(keys.size());
for (String key : keys) {
try {
entry.put(key, Iterables
.getLast(store.select(key, record, timestamp)));
}
catch (NoSuchElementException e) {
continue;
}
}
if(!entry.isEmpty()) {
result.put(record, entry);
}
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, Map<String, TObject>> getKeysRecordsTimestr(
List<String> keys, List<Long> records, String timestamp,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
return getKeysRecordsTime(keys, records,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@Batch
@HistoricalRead
@ThrowsThriftExceptions
public Map<String, TObject> getKeysRecordTime(List<String> keys,
long record, long timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
Map<String, TObject> result = TMaps
.newLinkedHashMapWithCapacity(keys.size());
AtomicSupport store = getStore(transaction, environment);
for (String key : keys) {
try {
result.put(key, Iterables
.getLast(store.select(key, record, timestamp)));
}
catch (NoSuchElementException e) {
continue;
}
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<String, TObject> getKeysRecordTimestr(List<String> keys,
long record, String timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return getKeysRecordTime(keys, record,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@ThrowsThriftExceptions
public String getServerEnvironment(AccessToken creds,
TransactionToken transaction, String env)
throws SecurityException, TException {
checkAccess(creds, transaction);
return Environments.sanitize(env);
}
@Override
@ManagedOperation
public String getServerVersion() {
return Version.getVersion(ConcourseServer.class).toString();
}
@Override
@Atomic
@Batch
@ThrowsThriftExceptions
public Set<Long> insertJson(String json, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
List<Multimap<String, Object>> objects = Convert.anyJsonToJava(json);
AtomicSupport store = getStore(transaction, environment);
Set<Long> records = Sets.newLinkedHashSet();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
List<DeferredWrite> deferred = Lists.newArrayList();
for (Multimap<String, Object> object : objects) {
long record;
if(object.containsKey(
Constants.JSON_RESERVED_IDENTIFIER_NAME)) {
// If the $id$ is specified in the JSON blob, insert the
// data into that record since no record(s) are provided
// as method parameters.
// WARNING: This means that doing the equivalent of
// `insert(jsonify(records))` will cause an infinite
// loop because this method will attempt to insert the
// data into the same records from which it was
// exported. Therefore, advise users to not export data
// with the $id and import that same data in the same
// environment.
record = ((Number) Iterables.getOnlyElement(object
.get(Constants.JSON_RESERVED_IDENTIFIER_NAME)))
.longValue();
}
else {
record = Time.now();
}
atomic.touch(record);
if(Operations.insertAtomic(object, record, atomic,
deferred)) {
records.add(record);
}
else {
throw AtomicStateException.RETRY;
}
}
Operations.insertDeferredAtomic(deferred, atomic);
}
catch (AtomicStateException e) {
atomic = null;
records.clear();
}
}
return records;
}
@Override
@Atomic
@ThrowsThriftExceptions
public boolean insertJsonRecord(String json, long record, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
try {
Multimap<String, Object> data = Convert.jsonToJava(json);
AtomicOperation atomic = store.startAtomicOperation();
List<DeferredWrite> deferred = Lists.newArrayList();
return Operations.insertAtomic(data, record, atomic, deferred)
&& Operations.insertDeferredAtomic(deferred, atomic)
&& atomic.commit();
}
catch (TransactionStateException e) {
throw new TransactionException();
}
catch (AtomicStateException e) {
return false;
}
}
@Override
@Atomic
@Batch
@ThrowsThriftExceptions
public Map<Long, Boolean> insertJsonRecords(String json, List<Long> records,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Multimap<String, Object> data = Convert.jsonToJava(json);
Map<Long, Boolean> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
List<DeferredWrite> deferred = Lists.newArrayList();
for (long record : records) {
result.put(record, Operations.insertAtomic(data, record,
atomic, deferred));
}
Operations.insertDeferredAtomic(deferred, atomic);
}
catch (AtomicStateException e) {
atomic = null;
}
}
return result;
}
@Override
@ThrowsThriftExceptions
public Set<Long> inventory(AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
return getStore(transaction, environment).getAllRecords();
}
@Override
@ThrowsThriftExceptions
public ComplexTObject invokePlugin(String id, String method,
List<ComplexTObject> params, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return pluginManager.invoke(id, method, params, creds, transaction,
environment);
}
@Override
@Atomic
@AutoRetry
@ThrowsThriftExceptions
public String jsonifyRecords(List<Long> records, boolean identifier,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
String json = "";
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
json = Operations.jsonify(records, 0L, identifier, atomic);
}
catch (AtomicStateException e) {
atomic = null;
}
}
return json;
}
@Override
@HistoricalRead
@ThrowsThriftExceptions
public String jsonifyRecordsTime(List<Long> records, long timestamp,
boolean identifier, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
return Operations.jsonify(records, timestamp, identifier,
getStore(transaction, environment));
}
@Override
@Alias
@ThrowsThriftExceptions
public String jsonifyRecordsTimestr(List<Long> records, String timestamp,
boolean identifier, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
return jsonifyRecordsTime(records,
NaturalLanguage.parseMicros(timestamp), identifier, creds,
transaction, environment);
}
/**
* A version of the login routine that handles the case when no environment
* has been specified. The is most common when authenticating a user for
* managed operations.
*
* @param username
* @param password
* @return the access token
* @throws TException
*/
@Override
@PluginRestricted
public AccessToken login(ByteBuffer username, ByteBuffer password)
throws TException {
return login(username, password, DEFAULT_ENVIRONMENT);
}
@Override
@PluginRestricted
public AccessToken login(ByteBuffer username, ByteBuffer password,
String environment) throws TException {
validate(username, password);
getEngine(environment);
return accessManager.getNewAccessToken(username);
}
@Override
@PluginRestricted
public void logout(AccessToken creds) throws TException {
logout(creds, null);
}
@Override
@PluginRestricted
public void logout(AccessToken creds, String environment)
throws TException {
checkAccess(creds, null);
accessManager.expireAccessToken(creds);
}
/**
* Return a service {@link AccessToken token} that can be used to
* authenticate and authorize non-user requests.
*
* @return the {@link AccessToken service token}
*/
@PluginRestricted
public AccessToken newServiceToken() {
return accessManager.getNewServiceToken();
}
@Override
@ThrowsThriftExceptions
public boolean pingRecord(long record, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
return Operations.ping(record, getStore(transaction, environment));
}
@Override
@Atomic
@Batch
@ThrowsThriftExceptions
public Map<Long, Boolean> pingRecords(List<Long> records, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Boolean> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (long record : records) {
result.put(record, Operations.ping(record, atomic));
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
return result;
}
@Override
@Atomic
@ThrowsThriftExceptions
public void reconcileKeyRecordValues(String key, long record,
Set<TObject> values, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Set<TObject> existingValues = store.select(key, record);
for (TObject existingValue : existingValues) {
if(!values.remove(existingValue)) {
atomic.remove(key, existingValue, record);
}
}
for (TObject value : values) {
atomic.add(key, value, record);
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
}
@Override
@ThrowsThriftExceptions
public boolean removeKeyValueRecord(String key, TObject value, long record,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
if(value.getType() != Type.LINK
|| isValidLink((Link) Convert.thriftToJava(value), record)) {
return ((BufferedStore) getStore(transaction, environment))
.remove(key, value, record);
}
else {
return false;
}
}
@Override
@Atomic
@Batch
@ThrowsThriftExceptions
public Map<Long, Boolean> removeKeyValueRecords(String key, TObject value,
List<Long> records, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Boolean> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (long record : records) {
result.put(record, atomic.remove(key, value, record));
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
return result;
}
@Override
@Atomic
@Batch
@VersionControl
@AutoRetry
@ThrowsThriftExceptions
public void revertKeyRecordsTime(String key, List<Long> records,
long timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (long record : records) {
Operations.revertAtomic(key, record, timestamp, atomic);
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
}
@Override
@Alias
@ThrowsThriftExceptions
public void revertKeyRecordsTimestr(String key, List<Long> records,
String timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
revertKeyRecordsTime(key, records,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@Atomic
@VersionControl
@AutoRetry
@ThrowsThriftExceptions
public void revertKeyRecordTime(String key, long record, long timestamp,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Operations.revertAtomic(key, record, timestamp, atomic);
}
catch (AtomicStateException e) {
atomic = null;
}
}
}
@Override
@Alias
@ThrowsThriftExceptions
public void revertKeyRecordTimestr(String key, long record,
String timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
revertKeyRecordTime(key, record, NaturalLanguage.parseMicros(timestamp),
creds, transaction, environment);
}
@Override
@Atomic
@Batch
@VersionControl
@AutoRetry
@ThrowsThriftExceptions
public void revertKeysRecordsTime(List<String> keys, List<Long> records,
long timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (long record : records) {
for (String key : keys) {
Operations.revertAtomic(key, record, timestamp, atomic);
}
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
}
@Override
@Alias
@ThrowsThriftExceptions
public void revertKeysRecordsTimestr(List<String> keys, List<Long> records,
String timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
revertKeysRecordsTime(keys, records,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@Atomic
@Batch
@VersionControl
@AutoRetry
@ThrowsThriftExceptions
public void revertKeysRecordTime(List<String> keys, long record,
long timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (String key : keys) {
Operations.revertAtomic(key, record, timestamp, atomic);
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
}
@Override
@Alias
@ThrowsThriftExceptions
public void revertKeysRecordTimestr(List<String> keys, long record,
String timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
revertKeysRecordTime(keys, record,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@ThrowsThriftExceptions
public Set<Long> search(String key, String query, AccessToken creds,
TransactionToken transaction, String env) throws TException {
checkAccess(creds, transaction);
return getStore(transaction, env).search(key, query);
}
@Override
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectCcl(String ccl,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
try {
Queue<PostfixNotationSymbol> queue = Parser.toPostfixNotation(ccl);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, Set<TObject>>> result = emptyResultDataset();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
Map<String, Set<TObject>> entry = TMaps
.newLinkedHashMapWithCapacity(
atomic.describe(record).size());
for (String key : atomic.describe(record)) {
entry.put(key, atomic.select(key, record));
}
result.put(record, entry);
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Override
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectCclTime(String ccl,
long timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
try {
Queue<PostfixNotationSymbol> queue = Parser.toPostfixNotation(ccl);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, Set<TObject>>> result = emptyResultDataset();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
Map<String, Set<TObject>> entry = TMaps
.newLinkedHashMapWithCapacity(atomic
.describe(record, timestamp).size());
for (String key : atomic.describe(record, timestamp)) {
entry.put(key,
atomic.select(key, record, timestamp));
}
result.put(record, entry);
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectCclTimestr(String ccl,
String timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
return selectCclTime(ccl, NaturalLanguage.parseMicros(timestamp), creds,
transaction, environment);
}
@Override
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectCriteria(
TCriteria criteria, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, Set<TObject>>> result = emptyResultDataset();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
Map<String, Set<TObject>> entry = TMaps
.newLinkedHashMapWithCapacity(
atomic.describe(record).size());
for (String key : atomic.describe(record)) {
entry.put(key, atomic.select(key, record));
}
result.put(record, entry);
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
@Override
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectCriteriaTime(
TCriteria criteria, long timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, Set<TObject>>> result = emptyResultDataset();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
Map<String, Set<TObject>> entry = TMaps
.newLinkedHashMapWithCapacity(
atomic.describe(record, timestamp).size());
for (String key : atomic.describe(record, timestamp)) {
entry.put(key, atomic.select(key, record, timestamp));
}
result.put(record, entry);
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectCriteriaTimestr(
TCriteria criteria, String timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return selectCriteriaTime(criteria,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@ThrowsThriftExceptions
public Map<Long, Set<TObject>> selectKeyCcl(String key, String ccl,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
try {
Queue<PostfixNotationSymbol> queue = Parser.toPostfixNotation(ccl);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Set<TObject>> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
result.put(record, atomic.select(key, record));
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Override
@ThrowsThriftExceptions
public Map<Long, Set<TObject>> selectKeyCclTime(String key, String ccl,
long timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
try {
Queue<PostfixNotationSymbol> queue = Parser.toPostfixNotation(ccl);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Set<TObject>> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
result.put(record,
atomic.select(key, record, timestamp));
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, Set<TObject>> selectKeyCclTimestr(String key, String ccl,
String timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
return selectKeyCclTime(key, ccl,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@ThrowsThriftExceptions
public Map<Long, Set<TObject>> selectKeyCriteria(String key,
TCriteria criteria, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Set<TObject>> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
result.put(record, atomic.select(key, record));
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
@Override
@ThrowsThriftExceptions
public Map<Long, Set<TObject>> selectKeyCriteriaTime(String key,
TCriteria criteria, long timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Set<TObject>> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
result.put(record, atomic.select(key, record, timestamp));
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, Set<TObject>> selectKeyCriteriaTimestr(String key,
TCriteria criteria, String timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return selectKeyCriteriaTime(key, criteria,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@ThrowsThriftExceptions
public Set<TObject> selectKeyRecord(String key, long record,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
return getStore(transaction, environment).select(key, record);
}
@Override
@Atomic
@Batch
@ThrowsThriftExceptions
public Map<Long, Set<TObject>> selectKeyRecords(String key,
List<Long> records, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Set<TObject>> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (long record : records) {
result.put(record, atomic.select(key, record));
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
return result;
}
@Override
@Atomic
@Batch
@HistoricalRead
@ThrowsThriftExceptions
public Map<Long, Set<TObject>> selectKeyRecordsTime(String key,
List<Long> records, long timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Set<TObject>> result = TMaps
.newLinkedHashMapWithCapacity(records.size());
for (long record : records) {
result.put(record, store.select(key, record, timestamp));
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, Set<TObject>> selectKeyRecordsTimestr(String key,
List<Long> records, String timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return selectKeyRecordsTime(key, records,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@HistoricalRead
@ThrowsThriftExceptions
public Set<TObject> selectKeyRecordTime(String key, long record,
long timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
return getStore(transaction, environment).select(key, record,
timestamp);
}
@Override
@Alias
@ThrowsThriftExceptions
public Set<TObject> selectKeyRecordTimestr(String key, long record,
String timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
return selectKeyRecordTime(key, record,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectKeysCcl(List<String> keys,
String ccl, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
try {
Queue<PostfixNotationSymbol> queue = Parser.toPostfixNotation(ccl);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, Set<TObject>>> result = emptyResultDataset();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
Map<String, Set<TObject>> entry = TMaps
.newLinkedHashMapWithCapacity(keys.size());
for (String key : keys) {
entry.put(key, atomic.select(key, record));
}
result.put(record, entry);
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Override
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectKeysCclTime(
List<String> keys, String ccl, long timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
try {
Queue<PostfixNotationSymbol> queue = Parser.toPostfixNotation(ccl);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, Set<TObject>>> result = emptyResultDataset();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
Map<String, Set<TObject>> entry = TMaps
.newLinkedHashMapWithCapacity(keys.size());
for (String key : keys) {
entry.put(key,
atomic.select(key, record, timestamp));
}
result.put(record, entry);
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectKeysCclTimestr(
List<String> keys, String ccl, String timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return selectKeysCclTime(keys, ccl,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectKeysCriteria(
List<String> keys, TCriteria criteria, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, Set<TObject>>> result = emptyResultDataset();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
Map<String, Set<TObject>> entry = TMaps
.newLinkedHashMapWithCapacity(keys.size());
for (String key : keys) {
entry.put(key, atomic.select(key, record));
}
result.put(record, entry);
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
@Override
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectKeysCriteriaTime(
List<String> keys, TCriteria criteria, long timestamp,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, Set<TObject>>> result = emptyResultDataset();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
for (long record : records) {
Map<String, Set<TObject>> entry = TMaps
.newLinkedHashMapWithCapacity(keys.size());
for (String key : keys) {
entry.put(key, atomic.select(key, record, timestamp));
}
result.put(record, entry);
}
}
catch (AtomicStateException e) {
result.clear();
atomic = null;
}
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectKeysCriteriaTimestr(
List<String> keys, TCriteria criteria, String timestamp,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
return selectKeysCriteriaTime(keys, criteria,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@Atomic
@Batch
@ThrowsThriftExceptions
public Map<String, Set<TObject>> selectKeysRecord(List<String> keys,
long record, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<String, Set<TObject>> result = Maps.newLinkedHashMap();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (String key : keys) {
result.put(key, atomic.select(key, record));
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
return result;
}
@Override
@Atomic
@Batch
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectKeysRecords(
List<String> keys, List<Long> records, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, Set<TObject>>> result = emptyResultDataset();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (long record : records) {
Map<String, Set<TObject>> entry = TMaps
.newLinkedHashMapWithCapacity(keys.size());
for (String key : keys) {
entry.put(key, atomic.select(key, record));
}
if(!entry.isEmpty()) {
result.put(record, entry);
}
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
return result;
}
@Override
@Batch
@HistoricalRead
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectKeysRecordsTime(
List<String> keys, List<Long> records, long timestamp,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, Set<TObject>>> result = emptyResultDatasetWithCapacity(
records.size());
for (long record : records) {
Map<String, Set<TObject>> entry = TMaps
.newLinkedHashMapWithCapacity(keys.size());
for (String key : keys) {
entry.put(key, store.select(key, record, timestamp));
}
if(!entry.isEmpty()) {
result.put(record, entry);
}
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectKeysRecordsTimestr(
List<String> keys, List<Long> records, String timestamp,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
return selectKeysRecordsTime(keys, records,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@Batch
@HistoricalRead
@ThrowsThriftExceptions
public Map<String, Set<TObject>> selectKeysRecordTime(List<String> keys,
long record, long timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<String, Set<TObject>> result = TMaps
.newLinkedHashMapWithCapacity(keys.size());
for (String key : keys) {
result.put(key, store.select(key, record, timestamp));
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<String, Set<TObject>> selectKeysRecordTimestr(List<String> keys,
long record, String timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return selectKeysRecordTime(keys, record,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@ThrowsThriftExceptions
public Map<String, Set<TObject>> selectRecord(long record,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
return getStore(transaction, environment).select(record);
}
@Override
@Atomic
@Batch
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectRecords(
List<Long> records, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, Set<TObject>>> result = emptyResultDataset();
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (long record : records) {
result.put(record, atomic.select(record));
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
return result;
}
@Override
@Batch
@HistoricalRead
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectRecordsTime(
List<Long> records, long timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
Map<Long, Map<String, Set<TObject>>> result = emptyResultDatasetWithCapacity(
records.size());
for (long record : records) {
result.put(record, store.select(record, timestamp));
}
return result;
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<Long, Map<String, Set<TObject>>> selectRecordsTimestr(
List<Long> records, String timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return selectRecordsTime(records,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Override
@HistoricalRead
@ThrowsThriftExceptions
public Map<String, Set<TObject>> selectRecordTime(long record,
long timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
return getStore(transaction, environment).select(record, timestamp);
}
@Override
@Alias
@ThrowsThriftExceptions
public Map<String, Set<TObject>> selectRecordTimestr(long record,
String timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
return selectRecordTime(record, NaturalLanguage.parseMicros(timestamp),
creds, transaction, environment);
}
@Override
@Alias
@ThrowsThriftExceptions
public long setKeyValue(String key, TObject value, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return addKeyValue(key, value, creds, transaction, environment);
}
@Override
@ThrowsThriftExceptions
public void setKeyValueRecord(String key, TObject value, long record,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
((BufferedStore) getStore(transaction, environment)).set(key, value,
record);
}
@Override
@Atomic
@Batch
@ThrowsThriftExceptions
public void setKeyValueRecords(String key, TObject value,
List<Long> records, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
for (long record : records) {
atomic.set(key, value, record);
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
}
@Override
@ThrowsThriftExceptions
@PluginRestricted
public TransactionToken stage(AccessToken creds, String env)
throws TException {
checkAccess(creds, null);
TransactionToken token = new TransactionToken(creds, Time.now());
Transaction transaction = getEngine(env).startTransaction();
transactions.put(token, transaction);
Logger.info("Started Transaction {}", transaction);
return token;
}
/**
* Start the server.
*
* @throws TTransportException
*/
@PluginRestricted
public void start() throws TTransportException {
for (Engine engine : engines.values()) {
engine.start();
}
httpServer.start();
pluginManager.start();
Thread mgmtThread = new Thread(() -> {
mgmtServer.serve();
}, "management-server");
mgmtThread.setDaemon(true);
mgmtThread.start();
System.out.println("The Concourse server has started");
server.serve();
}
/**
* Stop the server.
*/
@PluginRestricted
public void stop() {
if(server.isServing()) {
mgmtServer.stop();
server.stop();
pluginManager.stop();
httpServer.stop();
for (Engine engine : engines.values()) {
engine.stop();
}
System.out.println("The Concourse server has stopped");
}
}
@Override
@ThrowsThriftExceptions
public TObject sumKey(String key, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number sum = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
sum = Operations.sumKeyAtomic(key, Time.NONE, atomic);
}
catch (AtomicStateException e) {
atomic = null;
sum = 0;
}
}
return Convert.javaToThrift(sum);
}
@Override
@ThrowsThriftExceptions
public TObject sumKeyCcl(String key, String ccl, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
try {
Queue<PostfixNotationSymbol> queue = Parser.toPostfixNotation(ccl);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number sum = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
sum = Operations.sumKeyRecordsAtomic(key, records,
Time.NONE, atomic);
}
catch (AtomicStateException e) {
sum = 0;
atomic = null;
}
}
return Convert.javaToThrift(sum);
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Override
@ThrowsThriftExceptions
public TObject sumKeyCclTime(String key, String ccl, long timestamp,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
try {
Queue<PostfixNotationSymbol> queue = Parser.toPostfixNotation(ccl);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number sum = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
sum = Operations.sumKeyRecordsAtomic(key, records,
timestamp, atomic);
}
catch (AtomicStateException e) {
sum = 0;
atomic = null;
}
}
return Convert.javaToThrift(sum);
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Override
@ThrowsThriftExceptions
public TObject sumKeyCriteria(String key, TCriteria criteria,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number sum = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
sum = Operations.sumKeyRecordsAtomic(key, records, Time.NONE,
atomic);
}
catch (AtomicStateException e) {
sum = 0;
atomic = null;
}
}
return Convert.javaToThrift(sum);
}
@Override
@ThrowsThriftExceptions
public TObject sumKeyCriteriaTime(String key, TCriteria criteria,
long timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
Queue<PostfixNotationSymbol> queue = Operations
.convertCriteriaToQueue(criteria);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number sum = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Deque<Set<Long>> stack = new ArrayDeque<Set<Long>>();
Operations.findAtomic(queue, stack, atomic);
Set<Long> records = stack.pop();
sum = Operations.sumKeyRecordsAtomic(key, records, timestamp,
atomic);
}
catch (AtomicStateException e) {
sum = 0;
atomic = null;
}
}
return Convert.javaToThrift(sum);
}
@Override
@ThrowsThriftExceptions
public TObject sumKeyRecord(String key, long record, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number sum = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
sum = Operations.sumKeyRecordAtomic(key, record, Time.NONE,
atomic);
}
catch (AtomicStateException e) {
atomic = null;
sum = 0;
}
}
return Convert.javaToThrift(sum);
}
@Override
@ThrowsThriftExceptions
public TObject sumKeyRecords(String key, List<Long> records,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number sum = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
sum = Operations.sumKeyRecordsAtomic(key, records, Time.NONE,
atomic);
}
catch (AtomicStateException e) {
atomic = null;
sum = 0;
}
}
return Convert.javaToThrift(sum);
}
@Override
@ThrowsThriftExceptions
public TObject sumKeyRecordsTime(String key, List<Long> records,
long timestamp, AccessToken creds, TransactionToken transaction,
String environment) throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number sum = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
sum = Operations.sumKeyRecordsAtomic(key, records, timestamp,
atomic);
}
catch (AtomicStateException e) {
atomic = null;
sum = 0;
}
}
return Convert.javaToThrift(sum);
}
@Override
@ThrowsThriftExceptions
public TObject sumKeyRecordTime(String key, long record, long timestamp,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number sum = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
sum = Operations.sumKeyRecordAtomic(key, record, timestamp,
atomic);
}
catch (AtomicStateException e) {
atomic = null;
sum = 0;
}
}
return Convert.javaToThrift(sum);
}
@Override
@ThrowsThriftExceptions
public TObject sumKeyTime(String key, long timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws SecurityException, TransactionException, TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, environment);
AtomicOperation atomic = null;
Number sum = 0;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
sum = Operations.sumKeyAtomic(key, timestamp, atomic);
}
catch (AtomicStateException e) {
atomic = null;
sum = 0;
}
}
return Convert.javaToThrift(sum);
}
@Override
@ThrowsThriftExceptions
public long time(AccessToken creds, TransactionToken token,
String environment) throws TException {
return Time.now();
}
@Override
@ThrowsThriftExceptions
public long timePhrase(String phrase, AccessToken creds,
TransactionToken token, String environment) throws TException {
try {
return NaturalLanguage.parseMicros(phrase);
}
catch (Exception e) {
throw new ParseException(e.getMessage());
}
}
@Atomic
@Override
@ThrowsThriftExceptions
public boolean verifyAndSwap(String key, TObject expected, long record,
TObject replacement, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
try {
AtomicOperation atomic = getStore(transaction, environment)
.startAtomicOperation();
return atomic.remove(key, expected, record)
&& atomic.add(key, replacement, record) ? atomic.commit()
: false;
}
catch (TransactionStateException e) {
throw new TransactionException();
}
catch (AtomicStateException e) {
return false;
}
}
@Override
@ThrowsThriftExceptions
public boolean verifyKeyValueRecord(String key, TObject value, long record,
AccessToken creds, TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
return getStore(transaction, environment).verify(key, value, record);
}
@Override
@HistoricalRead
@ThrowsThriftExceptions
public boolean verifyKeyValueRecordTime(String key, TObject value,
long record, long timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
checkAccess(creds, transaction);
return getStore(transaction, environment).verify(key, value, record,
timestamp);
}
@Override
@Alias
@ThrowsThriftExceptions
public boolean verifyKeyValueRecordTimestr(String key, TObject value,
long record, String timestamp, AccessToken creds,
TransactionToken transaction, String environment)
throws TException {
return verifyKeyValueRecordTime(key, value, record,
NaturalLanguage.parseMicros(timestamp), creds, transaction,
environment);
}
@Atomic
@Override
@AutoRetry
@ThrowsThriftExceptions
public void verifyOrSet(String key, TObject value, long record,
AccessToken creds, TransactionToken transaction, String env)
throws TException {
checkAccess(creds, transaction);
AtomicSupport store = getStore(transaction, env);
AtomicOperation atomic = null;
while (atomic == null || !atomic.commit()) {
atomic = store.startAtomicOperation();
try {
Set<TObject> values = atomic.select(key, record);
for (TObject val : values) {
if(!val.equals(value)) {
atomic.remove(key, val, record);
}
}
if(!atomic.verify(key, value, record)) {
atomic.add(key, value, record);
}
}
catch (AtomicStateException e) {
atomic = null;
}
}
}
@Override
protected void checkAccess(AccessToken creds) throws TException {
checkAccess(creds, null);
}
@Override
protected AccessManager getAccessManager() {
return accessManager;
}
@Override
protected String getBufferStore() {
return bufferStore;
}
@Override
protected String getDbStore() {
return dbStore;
}
/**
* Return the {@link Engine} that is associated with {@code env}. If such an
* Engine does not exist, create a new one and add it to the collection.
*
* @param env
* @return the Engine
*/
protected Engine getEngine(String env) {
Engine engine = engines.get(env);
if(engine == null) {
env = Environments.sanitize(env);
return getEngineUnsafe(env);
}
return engine;
}
@Override
protected PluginManager getPluginManager() {
return pluginManager;
}
/**
* Check to make sure that {@code creds} and {@code transaction} are valid
* and are associated with one another.
*
* @param creds
* @param transaction
* @throws SecurityException
* @throws IllegalArgumentException
*/
private void checkAccess(AccessToken creds,
@Nullable TransactionToken transaction)
throws SecurityException, IllegalArgumentException {
if(!accessManager.isValidAccessToken(creds)) {
throw new SecurityException("Invalid access token");
}
Preconditions.checkArgument((transaction != null
&& transaction.getAccessToken().equals(creds)
&& transactions.containsKey(transaction))
|| transaction == null);
}
/**
* Return the {@link Engine} that is associated with the
* {@link Default#ENVIRONMENT}.
*
* @return the Engine
*/
private Engine getEngine() {
return getEngine(DEFAULT_ENVIRONMENT);
}
/**
* Return the {@link Engine} that is associated with {@code env} without
* performing any sanitization on the name. If such an Engine does not
* exist, create a new one and add it to the collection.
*/
private Engine getEngineUnsafe(String env) {
Engine engine = engines.get(env);
if(engine == null) {
engine = new Engine(bufferStore + File.separator + env,
dbStore + File.separator + env, env);
engine.start();
engines.put(env, engine);
}
return engine;
}
/**
* Return the correct store to use for a read/write operation depending upon
* the {@code env} whether the client has submitted a {@code transaction}
* token.
*
* @param transaction
* @param env
* @return the store to use
*/
private AtomicSupport getStore(TransactionToken transaction, String env) {
return transaction != null ? transactions.get(transaction)
: getEngine(env);
}
/**
* Initialize this instance. This method MUST always be called after
* constructing the instance.
*
* @param port - the port on which to listen for client connections
* @param bufferStore - the location to store {@link Buffer} files
* @param dbStore - the location to store {@link Database} files
* @throws TTransportException
*/
private void init(int port, String bufferStore, String dbStore)
throws TTransportException {
Preconditions.checkState(!bufferStore.equalsIgnoreCase(dbStore),
"Cannot store buffer and database files in the same directory. "
+ "Please check concourse.prefs.");
Preconditions.checkState(
!Strings.isNullOrEmpty(
Environments.sanitize(DEFAULT_ENVIRONMENT)),
"Cannot initialize "
+ "Concourse Server with a default environment of "
+ "'%s'. Please use a default environment name that "
+ "contains only alphanumeric characters.",
DEFAULT_ENVIRONMENT);
FileSystem.mkdirs(bufferStore);
FileSystem.mkdirs(dbStore);
FileSystem.lock(bufferStore);
FileSystem.lock(dbStore);
TServerSocket socket = new TServerSocket(port);
ConcourseService.Processor<Iface> processor = new ConcourseService.Processor<Iface>(
this);
Args args = new TThreadPoolServer.Args(socket);
args.processor(processor);
args.maxWorkerThreads(NUM_WORKER_THREADS);
args.executorService(Executors
.newCachedThreadPool(new ThreadFactoryBuilder().setDaemon(true)
.setNameFormat("Client Worker" + " %d").build()));
// CON-530: Set a lower timeout on the ExecutorService's termination to
// prevent the server from hanging because of active threads that have
// not yet been given a task but won't allow shutdown to proceed (i.e.
// clients from a ConnectionPool).
args.stopTimeoutVal(2);
this.server = new TThreadPoolServer(args);
this.bufferStore = bufferStore;
this.dbStore = dbStore;
this.engines = Maps.newConcurrentMap();
this.accessManager = AccessManager.create(ACCESS_FILE);
this.httpServer = GlobalState.HTTP_PORT > 0
? HttpServer.create(this, GlobalState.HTTP_PORT)
: HttpServer.disabled();
getEngine(); // load the default engine
this.pluginManager = new PluginManager(this,
GlobalState.CONCOURSE_HOME + File.separator + "plugins");
// Setup the management server
TServerSocket mgmtSocket = new TServerSocket(
GlobalState.MANAGEMENT_PORT);
TSimpleServer.Args mgmtArgs = new TSimpleServer.Args(mgmtSocket);
mgmtArgs.processor(new ConcourseManagementService.Processor<>(this));
this.mgmtServer = new TSimpleServer(mgmtArgs);
}
/**
* Validate that the {@code username} and {@code password} pair represent
* correct credentials. If not, throw a SecurityException.
*
* @param username
* @param password
* @throws SecurityException
*/
private void validate(ByteBuffer username, ByteBuffer password)
throws SecurityException {
if(!accessManager.isExistingUsernamePasswordCombo(username, password)) {
throw new SecurityException(
"Invalid username/password combination.");
}
}
/**
* A {@link DeferredWrite} is a wrapper around a key, value, and record.
* This is typically used by Concourse Server to gather certain writes
* during a batch operation that shouldn't be tried until the end.
*
* @author Jeff Nelson
*/
@Immutable
public static final class DeferredWrite {
// NOTE: This class does not define hashCode() or equals() because the
// defaults are the desired behaviour.
private final String key;
private final long record;
private final Object value;
/**
* Construct a new instance.
*
* @param key
* @param value
* @param record
*/
public DeferredWrite(String key, Object value, long record) {
this.key = key;
this.value = value;
this.record = record;
}
/**
* Return the key.
*
* @return the key
*/
public String getKey() {
return key;
}
/**
* Return the record.
*
* @return the record
*/
public long getRecord() {
return record;
}
/**
* Return the value.
*
* @return the value
*/
public Object getValue() {
return value;
}
}
/**
* A {@link MethodInterceptor} that delegates to the underlying annotated
* method, but catches specific exceptions and translates them to the
* appropriate Thrift counterparts.
*/
static class ThriftExceptionHandler implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
try {
return invocation.proceed();
}
catch (IllegalArgumentException e) {
throw new InvalidArgumentException(e.getMessage());
}
catch (AtomicStateException e) {
// If an AtomicStateException makes it here, then it must really
// be a TransactionStateException.
assert e.getClass() == TransactionStateException.class;
throw new TransactionException();
}
catch (java.lang.SecurityException e) {
throw new SecurityException(e.getMessage());
}
catch (IllegalStateException | JsonParseException e) {
// java.text.ParseException is checked, so internal server
// classes don't use it to indicate parse errors. Since most
// parsing using some sort of state machine, we've adopted the
// convention to throw IllegalStateExceptions whenever a parse
// error has occurred.
throw new ParseException(e.getMessage());
}
catch (PluginException e) {
throw new TException(e);
}
catch (TException e) {
// This clause may seem unnecessary, but some of the server
// methods manually throw TExceptions, so we need to catch them
// here and re-throw so that they don't get propagated as
// TTransportExceptions.
throw e;
}
catch (Throwable t) {
Logger.warn(
"The following exception occurred "
+ "but was not propagated to the client: {}",
t);
throw Throwables.propagate(t);
}
}
}
/**
* A {@link com.google.inject.Module Module} that configures AOP
* interceptors and injectors that handle Thrift specific needs.
*/
static class ThriftModule extends AbstractModule {
@Override
protected void configure() {
bindInterceptor(Matchers.subclassesOf(ConcourseServer.class),
Matchers.annotatedWith(ThrowsThriftExceptions.class),
new ThriftExceptionHandler());
}
}
/**
* Indicates that a {@link ConcourseServer server} method propagates certain
* Java exceptions to the client using analogous ones in the
* {@code com.cinchapi.concourse.thrift} package.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface ThrowsThriftExceptions {}
}