org.apache.accumulo.proxy.ProxyServer.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.accumulo.proxy.ProxyServer.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.accumulo.proxy;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import org.apache.accumulo.core.client.AccumuloException;
import org.apache.accumulo.core.client.AccumuloSecurityException;
import org.apache.accumulo.core.client.BatchScanner;
import org.apache.accumulo.core.client.BatchWriter;
import org.apache.accumulo.core.client.BatchWriterConfig;
import org.apache.accumulo.core.client.ClientConfiguration;
import org.apache.accumulo.core.client.ConditionalWriter;
import org.apache.accumulo.core.client.ConditionalWriter.Result;
import org.apache.accumulo.core.client.ConditionalWriterConfig;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.core.client.Instance;
import org.apache.accumulo.core.client.IteratorSetting;
import org.apache.accumulo.core.client.MutationsRejectedException;
import org.apache.accumulo.core.client.NamespaceExistsException;
import org.apache.accumulo.core.client.NamespaceNotEmptyException;
import org.apache.accumulo.core.client.NamespaceNotFoundException;
import org.apache.accumulo.core.client.Scanner;
import org.apache.accumulo.core.client.ScannerBase;
import org.apache.accumulo.core.client.TableExistsException;
import org.apache.accumulo.core.client.TableNotFoundException;
import org.apache.accumulo.core.client.ZooKeeperInstance;
import org.apache.accumulo.core.client.admin.ActiveCompaction;
import org.apache.accumulo.core.client.admin.ActiveScan;
import org.apache.accumulo.core.client.admin.CompactionConfig;
import org.apache.accumulo.core.client.admin.NewTableConfiguration;
import org.apache.accumulo.core.client.admin.TimeType;
import org.apache.accumulo.core.client.impl.Credentials;
import org.apache.accumulo.core.client.impl.Namespaces;
import org.apache.accumulo.core.client.impl.thrift.TableOperationExceptionType;
import org.apache.accumulo.core.client.impl.thrift.ThriftTableOperationException;
import org.apache.accumulo.core.client.security.SecurityErrorCode;
import org.apache.accumulo.core.client.security.tokens.AuthenticationToken;
import org.apache.accumulo.core.client.security.tokens.PasswordToken;
import org.apache.accumulo.core.data.Column;
import org.apache.accumulo.core.data.ConditionalMutation;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Mutation;
import org.apache.accumulo.core.data.PartialKey;
import org.apache.accumulo.core.data.Range;
import org.apache.accumulo.core.data.TabletId;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.iterators.IteratorUtil.IteratorScope;
import org.apache.accumulo.core.security.Authorizations;
import org.apache.accumulo.core.security.ColumnVisibility;
import org.apache.accumulo.core.security.NamespacePermission;
import org.apache.accumulo.core.security.SystemPermission;
import org.apache.accumulo.core.security.TablePermission;
import org.apache.accumulo.core.util.ByteBufferUtil;
import org.apache.accumulo.core.util.DeprecationUtil;
import org.apache.accumulo.core.util.TextUtil;
import org.apache.accumulo.proxy.thrift.AccumuloProxy;
import org.apache.accumulo.proxy.thrift.BatchScanOptions;
import org.apache.accumulo.proxy.thrift.ColumnUpdate;
import org.apache.accumulo.proxy.thrift.CompactionReason;
import org.apache.accumulo.proxy.thrift.CompactionStrategyConfig;
import org.apache.accumulo.proxy.thrift.CompactionType;
import org.apache.accumulo.proxy.thrift.Condition;
import org.apache.accumulo.proxy.thrift.ConditionalStatus;
import org.apache.accumulo.proxy.thrift.ConditionalUpdates;
import org.apache.accumulo.proxy.thrift.ConditionalWriterOptions;
import org.apache.accumulo.proxy.thrift.DiskUsage;
import org.apache.accumulo.proxy.thrift.Durability;
import org.apache.accumulo.proxy.thrift.KeyValue;
import org.apache.accumulo.proxy.thrift.KeyValueAndPeek;
import org.apache.accumulo.proxy.thrift.NoMoreEntriesException;
import org.apache.accumulo.proxy.thrift.ScanColumn;
import org.apache.accumulo.proxy.thrift.ScanOptions;
import org.apache.accumulo.proxy.thrift.ScanResult;
import org.apache.accumulo.proxy.thrift.ScanState;
import org.apache.accumulo.proxy.thrift.ScanType;
import org.apache.accumulo.proxy.thrift.UnknownScanner;
import org.apache.accumulo.proxy.thrift.UnknownWriter;
import org.apache.accumulo.proxy.thrift.WriterOptions;
import org.apache.accumulo.server.rpc.ThriftServerType;
import org.apache.accumulo.server.rpc.UGIAssumingProcessor;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.hadoop.io.Text;
import org.apache.thrift.TException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;

/**
 * Proxy Server exposing the Accumulo API via Thrift..
 *
 * @since 1.5
 */
public class ProxyServer implements AccumuloProxy.Iface {

    public static final Logger logger = LoggerFactory.getLogger(ProxyServer.class);
    public static final String RPC_ACCUMULO_PRINCIPAL_MISMATCH_MSG = "RPC principal did not match requested Accumulo principal";
    protected Instance instance;

    protected Class<? extends AuthenticationToken> tokenClass;

    static protected class ScannerPlusIterator {
        public ScannerBase scanner;
        public Iterator<Map.Entry<Key, Value>> iterator;
    }

    static protected class BatchWriterPlusProblem {
        public BatchWriter writer;
        public MutationsRejectedException exception = null;
    }

    static class CloseWriter implements RemovalListener<UUID, BatchWriterPlusProblem> {
        @Override
        public void onRemoval(RemovalNotification<UUID, BatchWriterPlusProblem> notification) {
            try {
                BatchWriterPlusProblem value = notification.getValue();
                if (value.exception != null)
                    throw value.exception;
                notification.getValue().writer.close();
            } catch (MutationsRejectedException e) {
                logger.warn("MutationsRejectedException", e);
            }
        }

        public CloseWriter() {
        }
    }

    static class CloseScanner implements RemovalListener<UUID, ScannerPlusIterator> {
        @Override
        public void onRemoval(RemovalNotification<UUID, ScannerPlusIterator> notification) {
            final ScannerBase base = notification.getValue().scanner;
            if (base instanceof BatchScanner) {
                final BatchScanner scanner = (BatchScanner) base;
                scanner.close();
            }
        }

        public CloseScanner() {
        }
    }

    public static class CloseConditionalWriter implements RemovalListener<UUID, ConditionalWriter> {
        @Override
        public void onRemoval(RemovalNotification<UUID, ConditionalWriter> notification) {
            notification.getValue().close();
        }
    }

    protected Cache<UUID, ScannerPlusIterator> scannerCache;
    protected Cache<UUID, BatchWriterPlusProblem> writerCache;
    protected Cache<UUID, ConditionalWriter> conditionalWriterCache;

    private final ThriftServerType serverType;

    public ProxyServer(Properties props) {

        String useMock = props.getProperty("useMockInstance");
        if (useMock != null && Boolean.parseBoolean(useMock))
            instance = DeprecationUtil.makeMockInstance(this.getClass().getName());
        else {
            ClientConfiguration clientConf;
            if (props.containsKey("clientConfigurationFile")) {
                String clientConfFile = props.getProperty("clientConfigurationFile");
                try {
                    clientConf = new ClientConfiguration(clientConfFile);
                } catch (ConfigurationException e) {
                    throw new RuntimeException(e);
                }
            } else {
                clientConf = ClientConfiguration.loadDefault();
            }
            instance = new ZooKeeperInstance(clientConf.withInstance(props.getProperty("instance"))
                    .withZkHosts(props.getProperty("zookeepers")));
        }

        try {
            String tokenProp = props.getProperty("tokenClass", PasswordToken.class.getName());
            tokenClass = Class.forName(tokenProp).asSubclass(AuthenticationToken.class);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }

        final String serverTypeStr = props.getProperty(Proxy.THRIFT_SERVER_TYPE, Proxy.THRIFT_SERVER_TYPE_DEFAULT);
        ThriftServerType tempServerType = Proxy.DEFAULT_SERVER_TYPE;
        if (!Proxy.THRIFT_SERVER_TYPE_DEFAULT.equals(serverTypeStr)) {
            tempServerType = ThriftServerType.get(serverTypeStr);
        }

        serverType = tempServerType;

        scannerCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).maximumSize(1000)
                .removalListener(new CloseScanner()).build();

        writerCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).maximumSize(1000)
                .removalListener(new CloseWriter()).build();

        conditionalWriterCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).maximumSize(1000)
                .removalListener(new CloseConditionalWriter()).build();
    }

    protected Connector getConnector(ByteBuffer login) throws Exception {
        String[] pair = ByteBufferUtil.toString(login).split(",", 2);
        if (instance.getInstanceID().equals(pair[0])) {
            Credentials creds = Credentials.deserialize(pair[1]);
            return instance.getConnector(creds.getPrincipal(), creds.getToken());
        } else {
            throw new org.apache.accumulo.core.client.AccumuloSecurityException(pair[0],
                    org.apache.accumulo.core.client.impl.thrift.SecurityErrorCode.INVALID_INSTANCEID);
        }
    }

    private void handleAccumuloException(AccumuloException e)
            throws org.apache.accumulo.proxy.thrift.TableNotFoundException,
            org.apache.accumulo.proxy.thrift.AccumuloException {
        if (e.getCause() instanceof ThriftTableOperationException) {
            ThriftTableOperationException ttoe = (ThriftTableOperationException) e.getCause();
            if (ttoe.type == TableOperationExceptionType.NOTFOUND) {
                throw new org.apache.accumulo.proxy.thrift.TableNotFoundException(e.toString());
            }
        }
        throw new org.apache.accumulo.proxy.thrift.AccumuloException(e.toString());
    }

    private void handleAccumuloSecurityException(AccumuloSecurityException e)
            throws org.apache.accumulo.proxy.thrift.TableNotFoundException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException {
        if (e.getSecurityErrorCode().equals(SecurityErrorCode.TABLE_DOESNT_EXIST))
            throw new org.apache.accumulo.proxy.thrift.TableNotFoundException(e.toString());
        throw new org.apache.accumulo.proxy.thrift.AccumuloSecurityException(e.toString());
    }

    private void handleExceptionTNF(Exception ex) throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            throw ex;
        } catch (AccumuloException e) {
            Throwable cause = e.getCause();
            if (null != cause && TableNotFoundException.class.equals(cause.getClass())) {
                throw new org.apache.accumulo.proxy.thrift.TableNotFoundException(cause.toString());
            }
            handleAccumuloException(e);
        } catch (AccumuloSecurityException e) {
            handleAccumuloSecurityException(e);
        } catch (TableNotFoundException e) {
            throw new org.apache.accumulo.proxy.thrift.TableNotFoundException(ex.toString());
        } catch (Exception e) {
            throw new org.apache.accumulo.proxy.thrift.AccumuloException(e.toString());
        }
    }

    private void handleExceptionTEE(Exception ex) throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException,
            org.apache.accumulo.proxy.thrift.TableExistsException, TException {
        try {
            throw ex;
        } catch (AccumuloException e) {
            handleAccumuloException(e);
        } catch (AccumuloSecurityException e) {
            handleAccumuloSecurityException(e);
        } catch (TableNotFoundException e) {
            throw new org.apache.accumulo.proxy.thrift.TableNotFoundException(ex.toString());
        } catch (TableExistsException e) {
            throw new org.apache.accumulo.proxy.thrift.TableExistsException(e.toString());
        } catch (Exception e) {
            throw new org.apache.accumulo.proxy.thrift.AccumuloException(e.toString());
        }
    }

    private void handleExceptionMRE(Exception ex) throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException,
            org.apache.accumulo.proxy.thrift.MutationsRejectedException, TException {
        try {
            throw ex;
        } catch (MutationsRejectedException e) {
            throw new org.apache.accumulo.proxy.thrift.MutationsRejectedException(ex.toString());
        } catch (AccumuloException e) {
            handleAccumuloException(e);
        } catch (AccumuloSecurityException e) {
            handleAccumuloSecurityException(e);
        } catch (TableNotFoundException e) {
            throw new org.apache.accumulo.proxy.thrift.TableNotFoundException(ex.toString());
        } catch (Exception e) {
            throw new org.apache.accumulo.proxy.thrift.AccumuloException(e.toString());
        }
    }

    private void handleExceptionNNF(Exception ex) throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.NamespaceNotFoundException, TException {
        try {
            throw ex;
        } catch (AccumuloException e) {
            Throwable cause = e.getCause();
            if (null != cause && NamespaceNotFoundException.class.equals(cause.getClass())) {
                throw new org.apache.accumulo.proxy.thrift.NamespaceNotFoundException(cause.toString());
            }
            handleAccumuloException(e);
        } catch (AccumuloSecurityException e) {
            handleAccumuloSecurityException(e);
        } catch (NamespaceNotFoundException e) {
            throw new org.apache.accumulo.proxy.thrift.NamespaceNotFoundException(ex.toString());
        } catch (Exception e) {
            throw new org.apache.accumulo.proxy.thrift.AccumuloException(e.toString());
        }
    }

    private void handleException(Exception ex) throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            throw ex;
        } catch (AccumuloException e) {
            throw new org.apache.accumulo.proxy.thrift.AccumuloException(e.toString());
        } catch (AccumuloSecurityException e) {
            throw new org.apache.accumulo.proxy.thrift.AccumuloSecurityException(e.toString());
        } catch (Exception e) {
            throw new org.apache.accumulo.proxy.thrift.AccumuloException(e.toString());
        }
    }

    @Override
    public int addConstraint(ByteBuffer login, String tableName, String constraintClassName)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {

        try {
            return getConnector(login).tableOperations().addConstraint(tableName, constraintClassName);
        } catch (Exception e) {
            handleExceptionTNF(e);
            return -1;
        }
    }

    @Override
    public void addSplits(ByteBuffer login, String tableName, Set<ByteBuffer> splits)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {

        try {
            SortedSet<Text> sorted = new TreeSet<Text>();
            for (ByteBuffer split : splits) {
                sorted.add(ByteBufferUtil.toText(split));
            }
            getConnector(login).tableOperations().addSplits(tableName, sorted);
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public void clearLocatorCache(ByteBuffer login, String tableName)
            throws org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            getConnector(login).tableOperations().clearLocatorCache(tableName);
        } catch (TableNotFoundException e) {
            throw new org.apache.accumulo.proxy.thrift.TableNotFoundException(e.toString());
        } catch (Exception e) {
            throw new TException(e.toString());
        }
    }

    @Override
    public void compactTable(ByteBuffer login, String tableName, ByteBuffer startRow, ByteBuffer endRow,
            List<org.apache.accumulo.proxy.thrift.IteratorSetting> iterators, boolean flush, boolean wait,
            CompactionStrategyConfig compactionStrategy)
            throws org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException,
            org.apache.accumulo.proxy.thrift.AccumuloException, TException {
        try {
            CompactionConfig compactionConfig = new CompactionConfig().setStartRow(ByteBufferUtil.toText(startRow))
                    .setEndRow(ByteBufferUtil.toText(endRow)).setIterators(getIteratorSettings(iterators))
                    .setFlush(flush).setWait(wait);

            if (compactionStrategy != null) {
                org.apache.accumulo.core.client.admin.CompactionStrategyConfig ccc = new org.apache.accumulo.core.client.admin.CompactionStrategyConfig(
                        compactionStrategy.getClassName());
                if (compactionStrategy.options != null)
                    ccc.setOptions(compactionStrategy.options);
                compactionConfig.setCompactionStrategy(ccc);
            }

            getConnector(login).tableOperations().compact(tableName, compactionConfig);
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public void cancelCompaction(ByteBuffer login, String tableName)
            throws org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException,
            org.apache.accumulo.proxy.thrift.AccumuloException, TException {

        try {
            getConnector(login).tableOperations().cancelCompaction(tableName);
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    private List<IteratorSetting> getIteratorSettings(
            List<org.apache.accumulo.proxy.thrift.IteratorSetting> iterators) {
        List<IteratorSetting> result = new ArrayList<IteratorSetting>();
        if (iterators != null) {
            for (org.apache.accumulo.proxy.thrift.IteratorSetting is : iterators) {
                result.add(getIteratorSetting(is));
            }
        }
        return result;
    }

    @Override
    public void createTable(ByteBuffer login, String tableName, boolean versioningIter,
            org.apache.accumulo.proxy.thrift.TimeType type)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableExistsException, TException {
        try {
            if (type == null)
                type = org.apache.accumulo.proxy.thrift.TimeType.MILLIS;

            NewTableConfiguration tConfig = new NewTableConfiguration()
                    .setTimeType(TimeType.valueOf(type.toString()));
            if (!versioningIter)
                tConfig = tConfig.withoutDefaultIterators();
            getConnector(login).tableOperations().create(tableName, tConfig);
        } catch (TableExistsException e) {
            throw new org.apache.accumulo.proxy.thrift.TableExistsException(e.toString());
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public void deleteTable(ByteBuffer login, String tableName)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            getConnector(login).tableOperations().delete(tableName);
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public void deleteRows(ByteBuffer login, String tableName, ByteBuffer startRow, ByteBuffer endRow)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            getConnector(login).tableOperations().deleteRows(tableName, ByteBufferUtil.toText(startRow),
                    ByteBufferUtil.toText(endRow));
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public boolean tableExists(ByteBuffer login, String tableName) throws TException {
        try {
            return getConnector(login).tableOperations().exists(tableName);
        } catch (Exception e) {
            throw new TException(e);
        }
    }

    @Override
    public void flushTable(ByteBuffer login, String tableName, ByteBuffer startRow, ByteBuffer endRow, boolean wait)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            getConnector(login).tableOperations().flush(tableName, ByteBufferUtil.toText(startRow),
                    ByteBufferUtil.toText(endRow), wait);
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public Map<String, Set<String>> getLocalityGroups(ByteBuffer login, String tableName)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            Map<String, Set<Text>> groups = getConnector(login).tableOperations().getLocalityGroups(tableName);
            Map<String, Set<String>> ret = new HashMap<String, Set<String>>();
            for (Entry<String, Set<Text>> entry : groups.entrySet()) {
                Set<String> value = new HashSet<String>();
                ret.put(entry.getKey(), value);
                for (Text val : entry.getValue()) {
                    value.add(val.toString());
                }
            }
            return ret;
        } catch (Exception e) {
            handleExceptionTNF(e);
            return null;
        }
    }

    @Override
    public ByteBuffer getMaxRow(ByteBuffer login, String tableName, Set<ByteBuffer> auths, ByteBuffer startRow,
            boolean startInclusive, ByteBuffer endRow, boolean endInclusive)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            Connector connector = getConnector(login);
            Text startText = ByteBufferUtil.toText(startRow);
            Text endText = ByteBufferUtil.toText(endRow);
            Authorizations auth;
            if (auths != null) {
                auth = getAuthorizations(auths);
            } else {
                auth = connector.securityOperations().getUserAuthorizations(connector.whoami());
            }
            Text max = connector.tableOperations().getMaxRow(tableName, auth, startText, startInclusive, endText,
                    endInclusive);
            return TextUtil.getByteBuffer(max);
        } catch (Exception e) {
            handleExceptionTNF(e);
            return null;
        }
    }

    @Override
    public Map<String, String> getTableProperties(ByteBuffer login, String tableName)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            Map<String, String> ret = new HashMap<String, String>();

            for (Map.Entry<String, String> entry : getConnector(login).tableOperations().getProperties(tableName)) {
                ret.put(entry.getKey(), entry.getValue());
            }
            return ret;
        } catch (Exception e) {
            handleExceptionTNF(e);
            return null;
        }
    }

    @Override
    public List<ByteBuffer> listSplits(ByteBuffer login, String tableName, int maxSplits)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            Collection<Text> splits = getConnector(login).tableOperations().listSplits(tableName, maxSplits);
            List<ByteBuffer> ret = new ArrayList<ByteBuffer>();
            for (Text split : splits) {
                ret.add(TextUtil.getByteBuffer(split));
            }
            return ret;
        } catch (Exception e) {
            handleExceptionTNF(e);
            return null;
        }
    }

    @Override
    public Set<String> listTables(ByteBuffer login) throws TException {
        try {
            return getConnector(login).tableOperations().list();
        } catch (Exception e) {
            throw new TException(e);
        }
    }

    @Override
    public Map<String, Integer> listConstraints(ByteBuffer login, String tableName)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {

        try {
            return getConnector(login).tableOperations().listConstraints(tableName);
        } catch (Exception e) {
            handleExceptionTNF(e);
            return null;
        }
    }

    @Override
    public void mergeTablets(ByteBuffer login, String tableName, ByteBuffer startRow, ByteBuffer endRow)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            getConnector(login).tableOperations().merge(tableName, ByteBufferUtil.toText(startRow),
                    ByteBufferUtil.toText(endRow));
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public void offlineTable(ByteBuffer login, String tableName, boolean wait)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            getConnector(login).tableOperations().offline(tableName, wait);
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public void onlineTable(ByteBuffer login, String tableName, boolean wait)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            getConnector(login).tableOperations().online(tableName, wait);
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public void removeConstraint(ByteBuffer login, String tableName, int constraint)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {

        try {
            getConnector(login).tableOperations().removeConstraint(tableName, constraint);
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public void removeTableProperty(ByteBuffer login, String tableName, String property)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            getConnector(login).tableOperations().removeProperty(tableName, property);
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public void renameTable(ByteBuffer login, String oldTableName, String newTableName)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException,
            org.apache.accumulo.proxy.thrift.TableExistsException, TException {
        try {
            getConnector(login).tableOperations().rename(oldTableName, newTableName);
        } catch (Exception e) {
            handleExceptionTEE(e);
        }
    }

    @Override
    public void setLocalityGroups(ByteBuffer login, String tableName, Map<String, Set<String>> groupStrings)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            Map<String, Set<Text>> groups = new HashMap<String, Set<Text>>();
            for (Entry<String, Set<String>> groupEntry : groupStrings.entrySet()) {
                groups.put(groupEntry.getKey(), new HashSet<Text>());
                for (String val : groupEntry.getValue()) {
                    groups.get(groupEntry.getKey()).add(new Text(val));
                }
            }
            getConnector(login).tableOperations().setLocalityGroups(tableName, groups);
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public void setTableProperty(ByteBuffer login, String tableName, String property, String value)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            getConnector(login).tableOperations().setProperty(tableName, property, value);
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public Map<String, String> tableIdMap(ByteBuffer login) throws TException {
        try {
            return getConnector(login).tableOperations().tableIdMap();
        } catch (Exception e) {
            throw new TException(e);
        }
    }

    @Override
    public List<DiskUsage> getDiskUsage(ByteBuffer login, Set<String> tables)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            List<org.apache.accumulo.core.client.admin.DiskUsage> diskUsages = getConnector(login).tableOperations()
                    .getDiskUsage(tables);
            List<DiskUsage> retUsages = new ArrayList<DiskUsage>();
            for (org.apache.accumulo.core.client.admin.DiskUsage diskUsage : diskUsages) {
                DiskUsage usage = new DiskUsage();
                usage.setTables(new ArrayList<String>(diskUsage.getTables()));
                usage.setUsage(diskUsage.getUsage());
                retUsages.add(usage);
            }
            return retUsages;
        } catch (Exception e) {
            handleExceptionTNF(e);
            return null;
        }
    }

    @Override
    public Map<String, String> getSiteConfiguration(ByteBuffer login)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            return getConnector(login).instanceOperations().getSiteConfiguration();
        } catch (Exception e) {
            handleException(e);
            return null;
        }
    }

    @Override
    public Map<String, String> getSystemConfiguration(ByteBuffer login)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            return getConnector(login).instanceOperations().getSystemConfiguration();
        } catch (Exception e) {
            handleException(e);
            return null;
        }
    }

    @Override
    public List<String> getTabletServers(ByteBuffer login) throws TException {
        try {
            return getConnector(login).instanceOperations().getTabletServers();
        } catch (Exception e) {
            throw new TException(e);
        }
    }

    @Override
    public List<org.apache.accumulo.proxy.thrift.ActiveScan> getActiveScans(ByteBuffer login, String tserver)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        List<org.apache.accumulo.proxy.thrift.ActiveScan> result = new ArrayList<org.apache.accumulo.proxy.thrift.ActiveScan>();
        try {
            List<ActiveScan> activeScans = getConnector(login).instanceOperations().getActiveScans(tserver);
            for (ActiveScan scan : activeScans) {
                org.apache.accumulo.proxy.thrift.ActiveScan pscan = new org.apache.accumulo.proxy.thrift.ActiveScan();
                pscan.client = scan.getClient();
                pscan.user = scan.getUser();
                pscan.table = scan.getTable();
                pscan.age = scan.getAge();
                pscan.idleTime = scan.getIdleTime();
                pscan.type = ScanType.valueOf(scan.getType().toString());
                pscan.state = ScanState.valueOf(scan.getState().toString());
                TabletId e = scan.getTablet();
                pscan.extent = new org.apache.accumulo.proxy.thrift.KeyExtent(e.getTableId().toString(),
                        TextUtil.getByteBuffer(e.getEndRow()), TextUtil.getByteBuffer(e.getPrevEndRow()));
                pscan.columns = new ArrayList<org.apache.accumulo.proxy.thrift.Column>();
                if (scan.getColumns() != null) {
                    for (Column c : scan.getColumns()) {
                        org.apache.accumulo.proxy.thrift.Column column = new org.apache.accumulo.proxy.thrift.Column();
                        column.setColFamily(c.getColumnFamily());
                        column.setColQualifier(c.getColumnQualifier());
                        column.setColVisibility(c.getColumnVisibility());
                        pscan.columns.add(column);
                    }
                }
                pscan.iterators = new ArrayList<org.apache.accumulo.proxy.thrift.IteratorSetting>();
                for (String iteratorString : scan.getSsiList()) {
                    String[] parts = iteratorString.split("[=,]");
                    if (parts.length == 3) {
                        String name = parts[0];
                        int priority = Integer.parseInt(parts[1]);
                        String classname = parts[2];
                        org.apache.accumulo.proxy.thrift.IteratorSetting settings = new org.apache.accumulo.proxy.thrift.IteratorSetting(
                                priority, name, classname, scan.getSsio().get(name));
                        pscan.iterators.add(settings);
                    }
                }
                pscan.authorizations = new ArrayList<ByteBuffer>();
                if (scan.getAuthorizations() != null) {
                    for (byte[] a : scan.getAuthorizations()) {
                        pscan.authorizations.add(ByteBuffer.wrap(a));
                    }
                }
                result.add(pscan);
            }
            return result;
        } catch (Exception e) {
            handleException(e);
            return null;
        }
    }

    @Override
    public List<org.apache.accumulo.proxy.thrift.ActiveCompaction> getActiveCompactions(ByteBuffer login,
            String tserver) throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {

        try {
            List<org.apache.accumulo.proxy.thrift.ActiveCompaction> result = new ArrayList<org.apache.accumulo.proxy.thrift.ActiveCompaction>();
            List<ActiveCompaction> active = getConnector(login).instanceOperations().getActiveCompactions(tserver);
            for (ActiveCompaction comp : active) {
                org.apache.accumulo.proxy.thrift.ActiveCompaction pcomp = new org.apache.accumulo.proxy.thrift.ActiveCompaction();
                pcomp.age = comp.getAge();
                pcomp.entriesRead = comp.getEntriesRead();
                pcomp.entriesWritten = comp.getEntriesWritten();
                TabletId e = comp.getTablet();
                pcomp.extent = new org.apache.accumulo.proxy.thrift.KeyExtent(e.getTableId().toString(),
                        TextUtil.getByteBuffer(e.getEndRow()), TextUtil.getByteBuffer(e.getPrevEndRow()));
                pcomp.inputFiles = new ArrayList<String>();
                if (comp.getInputFiles() != null) {
                    pcomp.inputFiles.addAll(comp.getInputFiles());
                }
                pcomp.localityGroup = comp.getLocalityGroup();
                pcomp.outputFile = comp.getOutputFile();
                pcomp.reason = CompactionReason.valueOf(comp.getReason().toString());
                pcomp.type = CompactionType.valueOf(comp.getType().toString());

                pcomp.iterators = new ArrayList<org.apache.accumulo.proxy.thrift.IteratorSetting>();
                if (comp.getIterators() != null) {
                    for (IteratorSetting setting : comp.getIterators()) {
                        org.apache.accumulo.proxy.thrift.IteratorSetting psetting = new org.apache.accumulo.proxy.thrift.IteratorSetting(
                                setting.getPriority(), setting.getName(), setting.getIteratorClass(),
                                setting.getOptions());
                        pcomp.iterators.add(psetting);
                    }
                }
                result.add(pcomp);
            }
            return result;
        } catch (Exception e) {
            handleException(e);
            return null;
        }
    }

    @Override
    public void removeProperty(ByteBuffer login, String property)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            getConnector(login).instanceOperations().removeProperty(property);
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public void setProperty(ByteBuffer login, String property, String value)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            getConnector(login).instanceOperations().setProperty(property, value);
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public boolean testClassLoad(ByteBuffer login, String className, String asTypeName)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            return getConnector(login).instanceOperations().testClassLoad(className, asTypeName);
        } catch (Exception e) {
            handleException(e);
            return false;
        }
    }

    @Override
    public boolean authenticateUser(ByteBuffer login, String user, Map<String, String> properties)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            return getConnector(login).securityOperations().authenticateUser(user, getToken(user, properties));
        } catch (Exception e) {
            handleException(e);
            return false;
        }
    }

    @Override
    public void changeUserAuthorizations(ByteBuffer login, String user, Set<ByteBuffer> authorizations)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            Set<String> auths = new HashSet<String>();
            for (ByteBuffer auth : authorizations) {
                auths.add(ByteBufferUtil.toString(auth));
            }
            getConnector(login).securityOperations().changeUserAuthorizations(user,
                    new Authorizations(auths.toArray(new String[0])));
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public void changeLocalUserPassword(ByteBuffer login, String user, ByteBuffer password)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            getConnector(login).securityOperations().changeLocalUserPassword(user, new PasswordToken(password));
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public void createLocalUser(ByteBuffer login, String user, ByteBuffer password)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            getConnector(login).securityOperations().createLocalUser(user, new PasswordToken(password));
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public void dropLocalUser(ByteBuffer login, String user)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            getConnector(login).securityOperations().dropLocalUser(user);
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public List<ByteBuffer> getUserAuthorizations(ByteBuffer login, String user)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            return getConnector(login).securityOperations().getUserAuthorizations(user).getAuthorizationsBB();
        } catch (Exception e) {
            handleException(e);
            return null;
        }
    }

    @Override
    public void grantSystemPermission(ByteBuffer login, String user,
            org.apache.accumulo.proxy.thrift.SystemPermission perm)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            getConnector(login).securityOperations().grantSystemPermission(user,
                    SystemPermission.getPermissionById((byte) perm.getValue()));
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public void grantTablePermission(ByteBuffer login, String user, String table,
            org.apache.accumulo.proxy.thrift.TablePermission perm)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            getConnector(login).securityOperations().grantTablePermission(user, table,
                    TablePermission.getPermissionById((byte) perm.getValue()));
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public boolean hasSystemPermission(ByteBuffer login, String user,
            org.apache.accumulo.proxy.thrift.SystemPermission perm)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            return getConnector(login).securityOperations().hasSystemPermission(user,
                    SystemPermission.getPermissionById((byte) perm.getValue()));
        } catch (Exception e) {
            handleException(e);
            return false;
        }
    }

    @Override
    public boolean hasTablePermission(ByteBuffer login, String user, String table,
            org.apache.accumulo.proxy.thrift.TablePermission perm)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            return getConnector(login).securityOperations().hasTablePermission(user, table,
                    TablePermission.getPermissionById((byte) perm.getValue()));
        } catch (Exception e) {
            handleExceptionTNF(e);
            return false;
        }
    }

    @Override
    public Set<String> listLocalUsers(ByteBuffer login) throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            return getConnector(login).securityOperations().listLocalUsers();
        } catch (Exception e) {
            handleException(e);
            return null;
        }
    }

    @Override
    public void revokeSystemPermission(ByteBuffer login, String user,
            org.apache.accumulo.proxy.thrift.SystemPermission perm)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            getConnector(login).securityOperations().revokeSystemPermission(user,
                    SystemPermission.getPermissionById((byte) perm.getValue()));
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public void revokeTablePermission(ByteBuffer login, String user, String table,
            org.apache.accumulo.proxy.thrift.TablePermission perm)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            getConnector(login).securityOperations().revokeTablePermission(user, table,
                    TablePermission.getPermissionById((byte) perm.getValue()));
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public void grantNamespacePermission(ByteBuffer login, String user, String namespaceName,
            org.apache.accumulo.proxy.thrift.NamespacePermission perm)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            getConnector(login).securityOperations().grantNamespacePermission(user, namespaceName,
                    NamespacePermission.getPermissionById((byte) perm.getValue()));
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public boolean hasNamespacePermission(ByteBuffer login, String user, String namespaceName,
            org.apache.accumulo.proxy.thrift.NamespacePermission perm)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            return getConnector(login).securityOperations().hasNamespacePermission(user, namespaceName,
                    NamespacePermission.getPermissionById((byte) perm.getValue()));
        } catch (Exception e) {
            handleException(e);
            return false;
        }
    }

    @Override
    public void revokeNamespacePermission(ByteBuffer login, String user, String namespaceName,
            org.apache.accumulo.proxy.thrift.NamespacePermission perm)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            getConnector(login).securityOperations().revokeNamespacePermission(user, namespaceName,
                    NamespacePermission.getPermissionById((byte) perm.getValue()));
        } catch (Exception e) {
            handleException(e);
        }
    }

    private Authorizations getAuthorizations(Set<ByteBuffer> authorizations) {
        List<String> auths = new ArrayList<String>();
        for (ByteBuffer bbauth : authorizations) {
            auths.add(ByteBufferUtil.toString(bbauth));
        }
        return new Authorizations(auths.toArray(new String[0]));
    }

    @Override
    public String createScanner(ByteBuffer login, String tableName, ScanOptions opts)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            Connector connector = getConnector(login);

            Authorizations auth;
            if (opts != null && opts.isSetAuthorizations()) {
                auth = getAuthorizations(opts.authorizations);
            } else {
                auth = connector.securityOperations().getUserAuthorizations(connector.whoami());
            }
            Scanner scanner = connector.createScanner(tableName, auth);

            if (opts != null) {
                if (opts.iterators != null) {
                    for (org.apache.accumulo.proxy.thrift.IteratorSetting iter : opts.iterators) {
                        IteratorSetting is = new IteratorSetting(iter.getPriority(), iter.getName(),
                                iter.getIteratorClass(), iter.getProperties());
                        scanner.addScanIterator(is);
                    }
                }
                org.apache.accumulo.proxy.thrift.Range prange = opts.range;
                if (prange != null) {
                    Range range = new Range(Util.fromThrift(prange.getStart()), prange.startInclusive,
                            Util.fromThrift(prange.getStop()), prange.stopInclusive);
                    scanner.setRange(range);
                }
                if (opts.columns != null) {
                    for (ScanColumn col : opts.columns) {
                        if (col.isSetColQualifier())
                            scanner.fetchColumn(ByteBufferUtil.toText(col.colFamily),
                                    ByteBufferUtil.toText(col.colQualifier));
                        else
                            scanner.fetchColumnFamily(ByteBufferUtil.toText(col.colFamily));
                    }
                }
            }

            UUID uuid = UUID.randomUUID();

            ScannerPlusIterator spi = new ScannerPlusIterator();
            spi.scanner = scanner;
            spi.iterator = scanner.iterator();
            scannerCache.put(uuid, spi);
            return uuid.toString();
        } catch (Exception e) {
            handleExceptionTNF(e);
            return null;
        }
    }

    @Override
    public String createBatchScanner(ByteBuffer login, String tableName, BatchScanOptions opts)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            Connector connector = getConnector(login);

            int threads = 10;
            Authorizations auth;
            if (opts != null && opts.isSetAuthorizations()) {
                auth = getAuthorizations(opts.authorizations);
            } else {
                auth = connector.securityOperations().getUserAuthorizations(connector.whoami());
            }
            if (opts != null && opts.threads > 0)
                threads = opts.threads;

            BatchScanner scanner = connector.createBatchScanner(tableName, auth, threads);

            if (opts != null) {
                if (opts.iterators != null) {
                    for (org.apache.accumulo.proxy.thrift.IteratorSetting iter : opts.iterators) {
                        IteratorSetting is = new IteratorSetting(iter.getPriority(), iter.getName(),
                                iter.getIteratorClass(), iter.getProperties());
                        scanner.addScanIterator(is);
                    }
                }

                ArrayList<Range> ranges = new ArrayList<Range>();

                if (opts.ranges == null) {
                    ranges.add(new Range());
                } else {
                    for (org.apache.accumulo.proxy.thrift.Range range : opts.ranges) {
                        Range aRange = new Range(
                                range.getStart() == null ? null : Util.fromThrift(range.getStart()), true,
                                range.getStop() == null ? null : Util.fromThrift(range.getStop()), false);
                        ranges.add(aRange);
                    }
                }
                scanner.setRanges(ranges);

                if (opts.columns != null) {
                    for (ScanColumn col : opts.columns) {
                        if (col.isSetColQualifier())
                            scanner.fetchColumn(ByteBufferUtil.toText(col.colFamily),
                                    ByteBufferUtil.toText(col.colQualifier));
                        else
                            scanner.fetchColumnFamily(ByteBufferUtil.toText(col.colFamily));
                    }
                }
            }

            UUID uuid = UUID.randomUUID();

            ScannerPlusIterator spi = new ScannerPlusIterator();
            spi.scanner = scanner;
            spi.iterator = scanner.iterator();
            scannerCache.put(uuid, spi);
            return uuid.toString();
        } catch (Exception e) {
            handleExceptionTNF(e);
            return null;
        }
    }

    private ScannerPlusIterator getScanner(String scanner) throws UnknownScanner {

        UUID uuid = null;
        try {
            uuid = UUID.fromString(scanner);
        } catch (IllegalArgumentException e) {
            throw new UnknownScanner(e.getMessage());
        }

        ScannerPlusIterator spi = scannerCache.getIfPresent(uuid);
        if (spi == null) {
            throw new UnknownScanner("Scanner never existed or no longer exists");
        }
        return spi;
    }

    @Override
    public boolean hasNext(String scanner) throws UnknownScanner, TException {
        ScannerPlusIterator spi = getScanner(scanner);

        return (spi.iterator.hasNext());
    }

    @Override
    public KeyValueAndPeek nextEntry(String scanner) throws NoMoreEntriesException, UnknownScanner,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {

        ScanResult scanResult = nextK(scanner, 1);
        if (scanResult.results.size() > 0) {
            return new KeyValueAndPeek(scanResult.results.get(0), scanResult.isMore());
        } else {
            throw new NoMoreEntriesException();
        }
    }

    @Override
    public ScanResult nextK(String scanner, int k) throws NoMoreEntriesException, UnknownScanner,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {

        // fetch the scanner
        ScannerPlusIterator spi = getScanner(scanner);
        Iterator<Map.Entry<Key, Value>> batchScanner = spi.iterator;
        // synchronized to prevent race conditions
        synchronized (batchScanner) {
            ScanResult ret = new ScanResult();
            ret.setResults(new ArrayList<KeyValue>());
            int numRead = 0;
            try {
                while (batchScanner.hasNext() && numRead < k) {
                    Map.Entry<Key, Value> next = batchScanner.next();
                    ret.addToResults(
                            new KeyValue(Util.toThrift(next.getKey()), ByteBuffer.wrap(next.getValue().get())));
                    numRead++;
                }
                ret.setMore(numRead == k);
            } catch (Exception ex) {
                closeScanner(scanner);
                throw new org.apache.accumulo.proxy.thrift.AccumuloSecurityException(ex.toString());
            }
            return ret;
        }
    }

    @Override
    public void closeScanner(String scanner) throws UnknownScanner, TException {
        UUID uuid = null;
        try {
            uuid = UUID.fromString(scanner);
        } catch (IllegalArgumentException e) {
            throw new UnknownScanner(e.getMessage());
        }

        try {
            if (scannerCache.asMap().remove(uuid) == null) {
                throw new UnknownScanner("Scanner never existed or no longer exists");
            }
        } catch (UnknownScanner e) {
            throw e;
        } catch (Exception e) {
            throw new TException(e.toString());
        }
    }

    @Override
    public void updateAndFlush(ByteBuffer login, String tableName, Map<ByteBuffer, List<ColumnUpdate>> cells)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException,
            org.apache.accumulo.proxy.thrift.MutationsRejectedException, TException {
        BatchWriterPlusProblem bwpe = null;
        try {
            bwpe = getWriter(login, tableName, null);
            addCellsToWriter(cells, bwpe);
            if (bwpe.exception != null)
                throw bwpe.exception;
            bwpe.writer.flush();
        } catch (Exception e) {
            handleExceptionMRE(e);
        } finally {
            if (null != bwpe) {
                try {
                    bwpe.writer.close();
                } catch (MutationsRejectedException e) {
                    handleExceptionMRE(e);
                }
            }
        }
    }

    private static final ColumnVisibility EMPTY_VIS = new ColumnVisibility();

    void addCellsToWriter(Map<ByteBuffer, List<ColumnUpdate>> cells, BatchWriterPlusProblem bwpe) {
        if (bwpe.exception != null)
            return;

        HashMap<Text, ColumnVisibility> vizMap = new HashMap<Text, ColumnVisibility>();

        for (Map.Entry<ByteBuffer, List<ColumnUpdate>> entry : cells.entrySet()) {
            Mutation m = new Mutation(ByteBufferUtil.toBytes(entry.getKey()));
            addUpdatesToMutation(vizMap, m, entry.getValue());
            try {
                bwpe.writer.addMutation(m);
            } catch (MutationsRejectedException mre) {
                bwpe.exception = mre;
            }
        }
    }

    private void addUpdatesToMutation(HashMap<Text, ColumnVisibility> vizMap, Mutation m, List<ColumnUpdate> cu) {
        for (ColumnUpdate update : cu) {
            ColumnVisibility viz = EMPTY_VIS;
            if (update.isSetColVisibility()) {
                viz = getCahcedCV(vizMap, update.getColVisibility());
            }
            byte[] value = new byte[0];
            if (update.isSetValue())
                value = update.getValue();
            if (update.isSetTimestamp()) {
                if (update.isSetDeleteCell() && update.isDeleteCell()) {
                    m.putDelete(update.getColFamily(), update.getColQualifier(), viz, update.getTimestamp());
                } else {
                    m.put(update.getColFamily(), update.getColQualifier(), viz, update.getTimestamp(), value);
                }
            } else {
                if (update.isSetDeleteCell() && update.isDeleteCell()) {
                    m.putDelete(new Text(update.getColFamily()), new Text(update.getColQualifier()), viz);
                } else {
                    m.put(new Text(update.getColFamily()), new Text(update.getColQualifier()), viz,
                            new Value(value));
                }
            }
        }
    }

    private static ColumnVisibility getCahcedCV(HashMap<Text, ColumnVisibility> vizMap, byte[] cv) {
        ColumnVisibility viz;
        Text vizText = new Text(cv);
        viz = vizMap.get(vizText);
        if (viz == null) {
            vizMap.put(vizText, viz = new ColumnVisibility(vizText));
        }
        return viz;
    }

    @Override
    public String createWriter(ByteBuffer login, String tableName, WriterOptions opts)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            BatchWriterPlusProblem writer = getWriter(login, tableName, opts);
            UUID uuid = UUID.randomUUID();
            writerCache.put(uuid, writer);
            return uuid.toString();
        } catch (Exception e) {
            handleExceptionTNF(e);
            return null;
        }
    }

    @Override
    public void update(String writer, Map<ByteBuffer, List<ColumnUpdate>> cells) throws TException {
        try {
            BatchWriterPlusProblem bwpe = getWriter(writer);
            addCellsToWriter(cells, bwpe);
        } catch (UnknownWriter e) {
            // just drop it, this is a oneway thrift call and throwing a TException seems to make all subsequent thrift calls fail
        }
    }

    @Override
    public void flush(String writer)
            throws UnknownWriter, org.apache.accumulo.proxy.thrift.MutationsRejectedException, TException {
        try {
            BatchWriterPlusProblem bwpe = getWriter(writer);
            if (bwpe.exception != null)
                throw bwpe.exception;
            bwpe.writer.flush();
        } catch (MutationsRejectedException e) {
            throw new org.apache.accumulo.proxy.thrift.MutationsRejectedException(e.toString());
        } catch (UnknownWriter uw) {
            throw uw;
        } catch (Exception e) {
            throw new TException(e);
        }
    }

    @Override
    public void closeWriter(String writer)
            throws UnknownWriter, org.apache.accumulo.proxy.thrift.MutationsRejectedException, TException {
        try {
            BatchWriterPlusProblem bwpe = getWriter(writer);
            if (bwpe.exception != null)
                throw bwpe.exception;
            bwpe.writer.close();
            writerCache.invalidate(UUID.fromString(writer));
        } catch (UnknownWriter uw) {
            throw uw;
        } catch (MutationsRejectedException e) {
            throw new org.apache.accumulo.proxy.thrift.MutationsRejectedException(e.toString());
        } catch (Exception e) {
            throw new TException(e);
        }
    }

    private BatchWriterPlusProblem getWriter(String writer) throws UnknownWriter {
        UUID uuid = null;
        try {
            uuid = UUID.fromString(writer);
        } catch (IllegalArgumentException iae) {
            throw new UnknownWriter(iae.getMessage());
        }

        BatchWriterPlusProblem bwpe = writerCache.getIfPresent(uuid);
        if (bwpe == null) {
            throw new UnknownWriter("Writer never existed or no longer exists");
        }
        return bwpe;
    }

    BatchWriterPlusProblem getWriter(ByteBuffer login, String tableName, WriterOptions opts) throws Exception {
        BatchWriterConfig cfg = new BatchWriterConfig();
        if (opts != null) {
            if (opts.maxMemory != 0)
                cfg.setMaxMemory(opts.maxMemory);
            if (opts.threads != 0)
                cfg.setMaxWriteThreads(opts.threads);
            if (opts.timeoutMs != 0)
                cfg.setTimeout(opts.timeoutMs, TimeUnit.MILLISECONDS);
            if (opts.latencyMs != 0)
                cfg.setMaxLatency(opts.latencyMs, TimeUnit.MILLISECONDS);
            if (opts.isSetDurability() && opts.durability != null) {
                cfg.setDurability(getDurability(opts.getDurability()));
            }
        }
        BatchWriterPlusProblem result = new BatchWriterPlusProblem();
        result.writer = getConnector(login).createBatchWriter(tableName, cfg);
        return result;
    }

    private org.apache.accumulo.core.client.Durability getDurability(Durability durability) {
        switch (durability) {
        case DEFAULT:
            return org.apache.accumulo.core.client.Durability.DEFAULT;
        case FLUSH:
            return org.apache.accumulo.core.client.Durability.FLUSH;
        case LOG:
            return org.apache.accumulo.core.client.Durability.LOG;
        case NONE:
            return org.apache.accumulo.core.client.Durability.NONE;
        case SYNC:
            return org.apache.accumulo.core.client.Durability.SYNC;
        }
        throw new IllegalArgumentException("Invalid durability value: " + durability.ordinal());
    }

    private IteratorSetting getIteratorSetting(org.apache.accumulo.proxy.thrift.IteratorSetting setting) {
        return new IteratorSetting(setting.priority, setting.name, setting.iteratorClass, setting.getProperties());
    }

    private IteratorScope getIteratorScope(org.apache.accumulo.proxy.thrift.IteratorScope scope) {
        return IteratorScope.valueOf(scope.toString().toLowerCase());
    }

    private EnumSet<IteratorScope> getIteratorScopes(Set<org.apache.accumulo.proxy.thrift.IteratorScope> scopes) {
        EnumSet<IteratorScope> scopes_ = EnumSet.noneOf(IteratorScope.class);
        for (org.apache.accumulo.proxy.thrift.IteratorScope scope : scopes) {
            scopes_.add(getIteratorScope(scope));
        }
        return scopes_;
    }

    private EnumSet<org.apache.accumulo.proxy.thrift.IteratorScope> getProxyIteratorScopes(
            Set<IteratorScope> scopes) {
        EnumSet<org.apache.accumulo.proxy.thrift.IteratorScope> scopes_ = EnumSet
                .noneOf(org.apache.accumulo.proxy.thrift.IteratorScope.class);
        for (IteratorScope scope : scopes) {
            scopes_.add(org.apache.accumulo.proxy.thrift.IteratorScope.valueOf(scope.toString().toUpperCase()));
        }
        return scopes_;
    }

    @Override
    public void attachIterator(ByteBuffer login, String tableName,
            org.apache.accumulo.proxy.thrift.IteratorSetting setting,
            Set<org.apache.accumulo.proxy.thrift.IteratorScope> scopes)
            throws org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            getConnector(login).tableOperations().attachIterator(tableName, getIteratorSetting(setting),
                    getIteratorScopes(scopes));
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public void checkIteratorConflicts(ByteBuffer login, String tableName,
            org.apache.accumulo.proxy.thrift.IteratorSetting setting,
            Set<org.apache.accumulo.proxy.thrift.IteratorScope> scopes)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            getConnector(login).tableOperations().checkIteratorConflicts(tableName, getIteratorSetting(setting),
                    getIteratorScopes(scopes));
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public void cloneTable(ByteBuffer login, String tableName, String newTableName, boolean flush,
            Map<String, String> propertiesToSet, Set<String> propertiesToExclude)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException,
            org.apache.accumulo.proxy.thrift.TableExistsException, TException {
        try {
            propertiesToExclude = propertiesToExclude == null ? new HashSet<String>() : propertiesToExclude;
            propertiesToSet = propertiesToSet == null ? new HashMap<String, String>() : propertiesToSet;

            getConnector(login).tableOperations().clone(tableName, newTableName, flush, propertiesToSet,
                    propertiesToExclude);
        } catch (Exception e) {
            handleExceptionTEE(e);
        }
    }

    @Override
    public void exportTable(ByteBuffer login, String tableName, String exportDir)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {

        try {
            getConnector(login).tableOperations().exportTable(tableName, exportDir);
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public void importTable(ByteBuffer login, String tableName, String importDir)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableExistsException, TException {

        try {
            getConnector(login).tableOperations().importTable(tableName, importDir);
        } catch (TableExistsException e) {
            throw new org.apache.accumulo.proxy.thrift.TableExistsException(e.toString());
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public org.apache.accumulo.proxy.thrift.IteratorSetting getIteratorSetting(ByteBuffer login, String tableName,
            String iteratorName, org.apache.accumulo.proxy.thrift.IteratorScope scope)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            IteratorSetting is = getConnector(login).tableOperations().getIteratorSetting(tableName, iteratorName,
                    getIteratorScope(scope));
            return new org.apache.accumulo.proxy.thrift.IteratorSetting(is.getPriority(), is.getName(),
                    is.getIteratorClass(), is.getOptions());
        } catch (Exception e) {
            handleExceptionTNF(e);
            return null;
        }
    }

    @Override
    public Map<String, Set<org.apache.accumulo.proxy.thrift.IteratorScope>> listIterators(ByteBuffer login,
            String tableName) throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            Map<String, EnumSet<IteratorScope>> iterMap = getConnector(login).tableOperations()
                    .listIterators(tableName);
            Map<String, Set<org.apache.accumulo.proxy.thrift.IteratorScope>> result = new HashMap<String, Set<org.apache.accumulo.proxy.thrift.IteratorScope>>();
            for (Map.Entry<String, EnumSet<IteratorScope>> entry : iterMap.entrySet()) {
                result.put(entry.getKey(), getProxyIteratorScopes(entry.getValue()));
            }
            return result;
        } catch (Exception e) {
            handleExceptionTNF(e);
            return null;
        }
    }

    @Override
    public void removeIterator(ByteBuffer login, String tableName, String iterName,
            Set<org.apache.accumulo.proxy.thrift.IteratorScope> scopes)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            getConnector(login).tableOperations().removeIterator(tableName, iterName, getIteratorScopes(scopes));
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public Set<org.apache.accumulo.proxy.thrift.Range> splitRangeByTablets(ByteBuffer login, String tableName,
            org.apache.accumulo.proxy.thrift.Range range, int maxSplits)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            Set<Range> ranges = getConnector(login).tableOperations().splitRangeByTablets(tableName,
                    getRange(range), maxSplits);
            Set<org.apache.accumulo.proxy.thrift.Range> result = new HashSet<org.apache.accumulo.proxy.thrift.Range>();
            for (Range r : ranges) {
                result.add(getRange(r));
            }
            return result;
        } catch (Exception e) {
            handleExceptionTNF(e);
            return null;
        }
    }

    private org.apache.accumulo.proxy.thrift.Range getRange(Range r) {
        return new org.apache.accumulo.proxy.thrift.Range(getProxyKey(r.getStartKey()), r.isStartKeyInclusive(),
                getProxyKey(r.getEndKey()), r.isEndKeyInclusive());
    }

    private org.apache.accumulo.proxy.thrift.Key getProxyKey(Key k) {
        if (k == null)
            return null;
        org.apache.accumulo.proxy.thrift.Key result = new org.apache.accumulo.proxy.thrift.Key(
                TextUtil.getByteBuffer(k.getRow()), TextUtil.getByteBuffer(k.getColumnFamily()),
                TextUtil.getByteBuffer(k.getColumnQualifier()), TextUtil.getByteBuffer(k.getColumnVisibility()));
        result.setTimestamp(k.getTimestamp());
        return result;
    }

    private Range getRange(org.apache.accumulo.proxy.thrift.Range range) {
        return new Range(Util.fromThrift(range.start), Util.fromThrift(range.stop));
    }

    @Override
    public void importDirectory(ByteBuffer login, String tableName, String importDir, String failureDir,
            boolean setTime) throws org.apache.accumulo.proxy.thrift.TableNotFoundException,
            org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            getConnector(login).tableOperations().importDirectory(tableName, importDir, failureDir, setTime);
        } catch (Exception e) {
            handleExceptionTNF(e);
        }
    }

    @Override
    public org.apache.accumulo.proxy.thrift.Range getRowRange(ByteBuffer row) throws TException {
        return getRange(new Range(ByteBufferUtil.toText(row)));
    }

    @Override
    public org.apache.accumulo.proxy.thrift.Key getFollowing(org.apache.accumulo.proxy.thrift.Key key,
            org.apache.accumulo.proxy.thrift.PartialKey part) throws TException {
        Key key_ = Util.fromThrift(key);
        PartialKey part_ = PartialKey.valueOf(part.toString());
        Key followingKey = key_.followingKey(part_);
        return getProxyKey(followingKey);
    }

    @Override
    public String systemNamespace() throws TException {
        return Namespaces.ACCUMULO_NAMESPACE;
    }

    @Override
    public String defaultNamespace() throws TException {
        return Namespaces.DEFAULT_NAMESPACE;
    }

    @Override
    public List<String> listNamespaces(ByteBuffer login) throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            return new LinkedList<>(getConnector(login).namespaceOperations().list());
        } catch (Exception e) {
            handleException(e);
            return null;
        }
    }

    @Override
    public boolean namespaceExists(ByteBuffer login, String namespaceName)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            return getConnector(login).namespaceOperations().exists(namespaceName);
        } catch (Exception e) {
            handleException(e);
            return false;
        }
    }

    @Override
    public void createNamespace(ByteBuffer login, String namespaceName)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.NamespaceExistsException, TException {
        try {
            getConnector(login).namespaceOperations().create(namespaceName);
        } catch (NamespaceExistsException e) {
            throw new org.apache.accumulo.proxy.thrift.NamespaceExistsException(e.toString());
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public void deleteNamespace(ByteBuffer login, String namespaceName)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.NamespaceNotFoundException,
            org.apache.accumulo.proxy.thrift.NamespaceNotEmptyException, TException {
        try {
            getConnector(login).namespaceOperations().delete(namespaceName);
        } catch (NamespaceNotFoundException e) {
            throw new org.apache.accumulo.proxy.thrift.NamespaceNotFoundException(e.toString());
        } catch (NamespaceNotEmptyException e) {
            throw new org.apache.accumulo.proxy.thrift.NamespaceNotEmptyException(e.toString());
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public void renameNamespace(ByteBuffer login, String oldNamespaceName, String newNamespaceName)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.NamespaceNotFoundException,
            org.apache.accumulo.proxy.thrift.NamespaceExistsException, TException {
        try {
            getConnector(login).namespaceOperations().rename(oldNamespaceName, newNamespaceName);
        } catch (NamespaceNotFoundException e) {
            throw new org.apache.accumulo.proxy.thrift.NamespaceNotFoundException(e.toString());
        } catch (NamespaceExistsException e) {
            throw new org.apache.accumulo.proxy.thrift.NamespaceExistsException(e.toString());
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public void setNamespaceProperty(ByteBuffer login, String namespaceName, String property, String value)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.NamespaceNotFoundException, TException {
        try {
            getConnector(login).namespaceOperations().setProperty(namespaceName, property, value);
        } catch (Exception e) {
            handleExceptionNNF(e);
        }
    }

    @Override
    public void removeNamespaceProperty(ByteBuffer login, String namespaceName, String property)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.NamespaceNotFoundException, TException {
        try {
            getConnector(login).namespaceOperations().removeProperty(namespaceName, property);
        } catch (Exception e) {
            handleExceptionNNF(e);
        }
    }

    @Override
    public Map<String, String> getNamespaceProperties(ByteBuffer login, String namespaceName)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.NamespaceNotFoundException, TException {
        try {
            Map<String, String> props = new HashMap<>();
            for (Map.Entry<String, String> entry : getConnector(login).namespaceOperations()
                    .getProperties(namespaceName)) {
                props.put(entry.getKey(), entry.getValue());
            }
            return props;
        } catch (Exception e) {
            handleExceptionNNF(e);
            return null;
        }
    }

    @Override
    public Map<String, String> namespaceIdMap(ByteBuffer login)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            return getConnector(login).namespaceOperations().namespaceIdMap();
        } catch (Exception e) {
            handleException(e);
            return null;
        }
    }

    @Override
    public void attachNamespaceIterator(ByteBuffer login, String namespaceName,
            org.apache.accumulo.proxy.thrift.IteratorSetting setting,
            Set<org.apache.accumulo.proxy.thrift.IteratorScope> scopes)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.NamespaceNotFoundException, TException {
        try {
            if (null != scopes && scopes.size() > 0) {
                getConnector(login).namespaceOperations().attachIterator(namespaceName, getIteratorSetting(setting),
                        getIteratorScopes(scopes));
            } else {
                getConnector(login).namespaceOperations().attachIterator(namespaceName,
                        getIteratorSetting(setting));
            }
        } catch (Exception e) {
            handleExceptionNNF(e);
        }
    }

    @Override
    public void removeNamespaceIterator(ByteBuffer login, String namespaceName, String name,
            Set<org.apache.accumulo.proxy.thrift.IteratorScope> scopes)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.NamespaceNotFoundException, TException {
        try {
            getConnector(login).namespaceOperations().removeIterator(namespaceName, name,
                    getIteratorScopes(scopes));
        } catch (Exception e) {
            handleExceptionNNF(e);
        }
    }

    @Override
    public org.apache.accumulo.proxy.thrift.IteratorSetting getNamespaceIteratorSetting(ByteBuffer login,
            String namespaceName, String name, org.apache.accumulo.proxy.thrift.IteratorScope scope)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.NamespaceNotFoundException, TException {
        try {
            IteratorSetting setting = getConnector(login).namespaceOperations().getIteratorSetting(namespaceName,
                    name, getIteratorScope(scope));
            return new org.apache.accumulo.proxy.thrift.IteratorSetting(setting.getPriority(), setting.getName(),
                    setting.getIteratorClass(), setting.getOptions());
        } catch (Exception e) {
            handleExceptionNNF(e);
            return null;
        }
    }

    @Override
    public Map<String, Set<org.apache.accumulo.proxy.thrift.IteratorScope>> listNamespaceIterators(ByteBuffer login,
            String namespaceName) throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.NamespaceNotFoundException, TException {
        try {
            Map<String, Set<org.apache.accumulo.proxy.thrift.IteratorScope>> namespaceIters = new HashMap<>();
            for (Map.Entry<String, EnumSet<IteratorScope>> entry : getConnector(login).namespaceOperations()
                    .listIterators(namespaceName).entrySet()) {
                namespaceIters.put(entry.getKey(), getProxyIteratorScopes(entry.getValue()));
            }
            return namespaceIters;
        } catch (Exception e) {
            handleExceptionNNF(e);
            return null;
        }
    }

    @Override
    public void checkNamespaceIteratorConflicts(ByteBuffer login, String namespaceName,
            org.apache.accumulo.proxy.thrift.IteratorSetting setting,
            Set<org.apache.accumulo.proxy.thrift.IteratorScope> scopes)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.NamespaceNotFoundException, TException {
        try {
            getConnector(login).namespaceOperations().checkIteratorConflicts(namespaceName,
                    getIteratorSetting(setting), getIteratorScopes(scopes));
        } catch (Exception e) {
            handleExceptionNNF(e);
        }
    }

    @Override
    public int addNamespaceConstraint(ByteBuffer login, String namespaceName, String constraintClassName)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.NamespaceNotFoundException, TException {
        try {
            return getConnector(login).namespaceOperations().addConstraint(namespaceName, constraintClassName);
        } catch (Exception e) {
            handleExceptionNNF(e);
            return -1;
        }
    }

    @Override
    public void removeNamespaceConstraint(ByteBuffer login, String namespaceName, int id)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.NamespaceNotFoundException, TException {
        try {
            getConnector(login).namespaceOperations().removeConstraint(namespaceName, id);
        } catch (Exception e) {
            handleExceptionNNF(e);
        }
    }

    @Override
    public Map<String, Integer> listNamespaceConstraints(ByteBuffer login, String namespaceName)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.NamespaceNotFoundException, TException {
        try {
            return getConnector(login).namespaceOperations().listConstraints(namespaceName);
        } catch (Exception e) {
            handleExceptionNNF(e);
            return null;
        }
    }

    @Override
    public boolean testNamespaceClassLoad(ByteBuffer login, String namespaceName, String className,
            String asTypeName) throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.NamespaceNotFoundException, TException {
        try {
            return getConnector(login).namespaceOperations().testClassLoad(namespaceName, className, asTypeName);
        } catch (Exception e) {
            handleExceptionNNF(e);
            return false;
        }
    }

    @Override
    public void pingTabletServer(ByteBuffer login, String tserver)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        try {
            getConnector(login).instanceOperations().ping(tserver);
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public ByteBuffer login(String principal, Map<String, String> loginProperties)
            throws org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {
        if (ThriftServerType.SASL == serverType) {
            String remoteUser = UGIAssumingProcessor.rpcPrincipal();
            if (null == remoteUser || !remoteUser.equals(principal)) {
                logger.error("Denying login from user " + remoteUser + " who attempted to log in as " + principal);
                throw new org.apache.accumulo.proxy.thrift.AccumuloSecurityException(
                        RPC_ACCUMULO_PRINCIPAL_MISMATCH_MSG);
            }
        }

        try {
            AuthenticationToken token = getToken(principal, loginProperties);
            ByteBuffer login = ByteBuffer
                    .wrap((instance.getInstanceID() + "," + new Credentials(principal, token).serialize())
                            .getBytes(UTF_8));
            getConnector(login); // check to make sure user exists
            return login;
        } catch (AccumuloSecurityException e) {
            logger.error("Failed to login", e);
            throw new org.apache.accumulo.proxy.thrift.AccumuloSecurityException(e.toString());
        } catch (Exception e) {
            logger.error("Failed to login", e);
            throw new TException(e);
        }
    }

    private AuthenticationToken getToken(String principal, Map<String, String> properties)
            throws AccumuloSecurityException, AccumuloException {
        AuthenticationToken.Properties props = new AuthenticationToken.Properties();
        props.putAllStrings(properties);
        AuthenticationToken token;
        try {
            token = tokenClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            logger.error("Error constructing authentication token", e);
            throw new AccumuloException(e);
        }
        token.init(props);
        return token;
    }

    @Override
    public boolean testTableClassLoad(ByteBuffer login, String tableName, String className, String asTypeName)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            return getConnector(login).tableOperations().testClassLoad(tableName, className, asTypeName);
        } catch (Exception e) {
            handleExceptionTNF(e);
            return false;
        }
    }

    @Override
    public String createConditionalWriter(ByteBuffer login, String tableName, ConditionalWriterOptions options)
            throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {
        try {
            ConditionalWriterConfig cwc = new ConditionalWriterConfig();
            if (options.getMaxMemory() != 0) {
                // TODO
            }
            if (options.isSetThreads() && options.getThreads() != 0)
                cwc.setMaxWriteThreads(options.getThreads());
            if (options.isSetTimeoutMs() && options.getTimeoutMs() != 0)
                cwc.setTimeout(options.getTimeoutMs(), TimeUnit.MILLISECONDS);
            if (options.isSetAuthorizations() && options.getAuthorizations() != null)
                cwc.setAuthorizations(getAuthorizations(options.getAuthorizations()));
            if (options.isSetDurability() && options.getDurability() != null)
                cwc.setDurability(getDurability(options.getDurability()));

            ConditionalWriter cw = getConnector(login).createConditionalWriter(tableName, cwc);

            UUID id = UUID.randomUUID();

            conditionalWriterCache.put(id, cw);

            return id.toString();
        } catch (Exception e) {
            handleExceptionTNF(e);
            return null;
        }
    }

    @Override
    public Map<ByteBuffer, ConditionalStatus> updateRowsConditionally(String conditionalWriter,
            Map<ByteBuffer, ConditionalUpdates> updates)
            throws UnknownWriter, org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException, TException {

        ConditionalWriter cw = conditionalWriterCache.getIfPresent(UUID.fromString(conditionalWriter));

        if (cw == null) {
            throw new UnknownWriter();
        }

        try {
            HashMap<Text, ColumnVisibility> vizMap = new HashMap<Text, ColumnVisibility>();

            ArrayList<ConditionalMutation> cmuts = new ArrayList<ConditionalMutation>(updates.size());
            for (Entry<ByteBuffer, ConditionalUpdates> cu : updates.entrySet()) {
                ConditionalMutation cmut = new ConditionalMutation(ByteBufferUtil.toBytes(cu.getKey()));

                for (Condition tcond : cu.getValue().conditions) {
                    org.apache.accumulo.core.data.Condition cond = new org.apache.accumulo.core.data.Condition(
                            tcond.column.getColFamily(), tcond.column.getColQualifier());

                    if (tcond.getColumn().getColVisibility() != null
                            && tcond.getColumn().getColVisibility().length > 0) {
                        cond.setVisibility(getCahcedCV(vizMap, tcond.getColumn().getColVisibility()));
                    }

                    if (tcond.isSetValue())
                        cond.setValue(tcond.getValue());

                    if (tcond.isSetTimestamp())
                        cond.setTimestamp(tcond.getTimestamp());

                    if (tcond.isSetIterators()) {
                        cond.setIterators(getIteratorSettings(tcond.getIterators())
                                .toArray(new IteratorSetting[tcond.getIterators().size()]));
                    }

                    cmut.addCondition(cond);
                }

                addUpdatesToMutation(vizMap, cmut, cu.getValue().updates);

                cmuts.add(cmut);
            }

            Iterator<Result> results = cw.write(cmuts.iterator());

            HashMap<ByteBuffer, ConditionalStatus> resultMap = new HashMap<ByteBuffer, ConditionalStatus>();

            while (results.hasNext()) {
                Result result = results.next();
                ByteBuffer row = ByteBuffer.wrap(result.getMutation().getRow());
                ConditionalStatus status = ConditionalStatus.valueOf(result.getStatus().name());
                resultMap.put(row, status);
            }

            return resultMap;
        } catch (Exception e) {
            handleException(e);
            return null;
        }
    }

    @Override
    public void closeConditionalWriter(String conditionalWriter) throws TException {
        ConditionalWriter cw = conditionalWriterCache.getIfPresent(UUID.fromString(conditionalWriter));
        if (cw != null) {
            cw.close();
            conditionalWriterCache.invalidate(UUID.fromString(conditionalWriter));
        }
    }

    @Override
    public ConditionalStatus updateRowConditionally(ByteBuffer login, String tableName, ByteBuffer row,
            ConditionalUpdates updates) throws org.apache.accumulo.proxy.thrift.AccumuloException,
            org.apache.accumulo.proxy.thrift.AccumuloSecurityException,
            org.apache.accumulo.proxy.thrift.TableNotFoundException, TException {

        String cwid = createConditionalWriter(login, tableName, new ConditionalWriterOptions());
        try {
            return updateRowsConditionally(cwid, Collections.singletonMap(row, updates)).get(row);
        } finally {
            closeConditionalWriter(cwid);
        }
    }
}