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

Java tutorial

Introduction

Here is the source code for org.apache.accumulo.proxy.SimpleProxyIT.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 org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.accumulo.core.client.security.tokens.PasswordToken;
import org.apache.accumulo.core.conf.DefaultConfiguration;
import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.file.FileOperations;
import org.apache.accumulo.core.file.FileSKVWriter;
import org.apache.accumulo.core.iterators.DevNull;
import org.apache.accumulo.core.iterators.SortedKeyValueIterator;
import org.apache.accumulo.core.iterators.user.SummingCombiner;
import org.apache.accumulo.core.iterators.user.VersioningIterator;
import org.apache.accumulo.core.metadata.MetadataTable;
import org.apache.accumulo.core.util.ByteBufferUtil;
import org.apache.accumulo.core.util.UtilWaitThread;
import org.apache.accumulo.examples.simple.constraints.NumericValueConstraint;
import org.apache.accumulo.minicluster.MiniAccumuloCluster;
import org.apache.accumulo.minicluster.MiniAccumuloConfig;
import org.apache.accumulo.proxy.thrift.AccumuloProxy.Client;
import org.apache.accumulo.proxy.thrift.AccumuloSecurityException;
import org.apache.accumulo.proxy.thrift.ActiveCompaction;
import org.apache.accumulo.proxy.thrift.ActiveScan;
import org.apache.accumulo.proxy.thrift.BatchScanOptions;
import org.apache.accumulo.proxy.thrift.Column;
import org.apache.accumulo.proxy.thrift.ColumnUpdate;
import org.apache.accumulo.proxy.thrift.CompactionReason;
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.IteratorScope;
import org.apache.accumulo.proxy.thrift.IteratorSetting;
import org.apache.accumulo.proxy.thrift.Key;
import org.apache.accumulo.proxy.thrift.KeyValue;
import org.apache.accumulo.proxy.thrift.MutationsRejectedException;
import org.apache.accumulo.proxy.thrift.PartialKey;
import org.apache.accumulo.proxy.thrift.Range;
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.SystemPermission;
import org.apache.accumulo.proxy.thrift.TableExistsException;
import org.apache.accumulo.proxy.thrift.TableNotFoundException;
import org.apache.accumulo.proxy.thrift.TablePermission;
import org.apache.accumulo.proxy.thrift.TimeType;
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.util.PortUtils;
import org.apache.accumulo.test.functional.SlowIterator;
import org.apache.commons.io.FileUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TProtocolFactory;
import org.apache.thrift.server.TServer;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.rules.TestName;

/**
 * Call every method on the proxy and try to verify that it works.
 */
public class SimpleProxyIT {

    public static File macTestFolder = new File(
            System.getProperty("user.dir") + "/target/" + SimpleProxyIT.class.getName());

    private static MiniAccumuloCluster accumulo;
    private static String secret = "superSecret";
    private static Random random = new Random();
    private static TServer proxyServer;
    private static Thread thread;
    private static int proxyPort;
    private static org.apache.accumulo.proxy.thrift.AccumuloProxy.Client client;
    private static String principal = "root";

    private static Map<String, String> properties = new TreeMap<String, String>() {
        private static final long serialVersionUID = 1L;

        {
            put("password", secret);
        }
    };
    private static ByteBuffer creds = null;

    private static Class<? extends TProtocolFactory> protocolClass;

    static Class<? extends TProtocolFactory> getRandomProtocol() {
        List<Class<? extends TProtocolFactory>> protocolFactories = new ArrayList<Class<? extends TProtocolFactory>>();
        protocolFactories.add(org.apache.thrift.protocol.TJSONProtocol.Factory.class);
        protocolFactories.add(org.apache.thrift.protocol.TBinaryProtocol.Factory.class);
        protocolFactories.add(org.apache.thrift.protocol.TTupleProtocol.Factory.class);
        protocolFactories.add(org.apache.thrift.protocol.TCompactProtocol.Factory.class);

        return protocolFactories.get(random.nextInt(protocolFactories.size()));
    }

    private static final AtomicInteger tableCounter = new AtomicInteger(0);

    private static String makeTableName() {
        return "test" + tableCounter.getAndIncrement();
    }

    @Rule
    public TemporaryFolder tempFolder = new TemporaryFolder(macTestFolder);

    @Rule
    public TestName testName = new TestName();

    @BeforeClass
    public static void setupMiniCluster() throws Exception {
        FileUtils.deleteQuietly(macTestFolder);
        macTestFolder.mkdirs();
        MiniAccumuloConfig config = new MiniAccumuloConfig(macTestFolder, secret).setNumTservers(1);
        accumulo = new MiniAccumuloCluster(config);
        accumulo.start();

        Properties props = new Properties();
        props.put("instance", accumulo.getConfig().getInstanceName());
        props.put("zookeepers", accumulo.getZooKeepers());
        props.put("tokenClass", PasswordToken.class.getName());

        protocolClass = getRandomProtocol();

        proxyPort = PortUtils.getRandomFreePort();
        proxyServer = Proxy.createProxyServer(org.apache.accumulo.proxy.thrift.AccumuloProxy.class,
                org.apache.accumulo.proxy.ProxyServer.class, proxyPort, protocolClass, props);
        thread = new Thread() {
            @Override
            public void run() {
                proxyServer.serve();
            }
        };
        thread.start();
        while (!proxyServer.isServing())
            UtilWaitThread.sleep(100);
        client = new TestProxyClient("localhost", proxyPort, protocolClass.newInstance()).proxy();
        creds = client.login(principal, properties);
    }

    @Test(timeout = 10000)
    public void security() throws Exception {
        client.createLocalUser(creds, "user", s2bb(secret));
        ByteBuffer badLogin = client.login("user", properties);
        client.dropLocalUser(creds, "user");
        final String table = makeTableName();
        client.createTable(creds, table, false, TimeType.MILLIS);

        final IteratorSetting setting = new IteratorSetting(100, "slow", SlowIterator.class.getName(),
                Collections.singletonMap("sleepTime", "200"));

        try {
            client.addConstraint(badLogin, table, NumericValueConstraint.class.getName());
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.addSplits(badLogin, table, Collections.singleton(s2bb("1")));
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.clearLocatorCache(badLogin, table);
            fail("exception not thrown");
        } catch (TException ex) {
        }
        try {
            client.compactTable(badLogin, table, null, null, null, true, false);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.cancelCompaction(badLogin, table);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.createTable(badLogin, table, false, TimeType.MILLIS);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.deleteTable(badLogin, table);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.deleteRows(badLogin, table, null, null);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.tableExists(badLogin, table);
            fail("exception not thrown");
        } catch (TException ex) {
        }
        try {
            client.flushTable(badLogin, table, null, null, false);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.getLocalityGroups(badLogin, table);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.getMaxRow(badLogin, table, Collections.<ByteBuffer>emptySet(), null, false, null, false);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.getTableProperties(badLogin, table);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.listSplits(badLogin, table, 10000);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.listTables(badLogin);
            fail("exception not thrown");
        } catch (TException ex) {
        }
        try {
            client.listConstraints(badLogin, table);
            fail("exception not thrown");
        } catch (TException ex) {
        }
        try {
            client.mergeTablets(badLogin, table, null, null);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.offlineTable(badLogin, table, false);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.onlineTable(badLogin, table, false);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.removeConstraint(badLogin, table, 0);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.removeTableProperty(badLogin, table, Property.TABLE_FILE_MAX.getKey());
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.renameTable(badLogin, table, "someTableName");
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            Map<String, Set<String>> groups = new HashMap<String, Set<String>>();
            groups.put("group1", Collections.singleton("cf1"));
            groups.put("group2", Collections.singleton("cf2"));
            client.setLocalityGroups(badLogin, table, groups);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.setTableProperty(badLogin, table, Property.TABLE_FILE_MAX.getKey(), "0");
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.tableIdMap(badLogin);
            fail("exception not thrown");
        } catch (TException ex) {
        }
        try {
            client.getSiteConfiguration(badLogin);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.getSystemConfiguration(badLogin);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.getTabletServers(badLogin);
            fail("exception not thrown");
        } catch (TException ex) {
        }
        try {
            client.getActiveScans(badLogin, "fake");
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.getActiveCompactions(badLogin, "fakse");
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.removeProperty(badLogin, "table.split.threshold");
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.setProperty(badLogin, "table.split.threshold", "500M");
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.testClassLoad(badLogin, DevNull.class.getName(), SortedKeyValueIterator.class.getName());
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.authenticateUser(badLogin, "root", s2pp(secret));
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            HashSet<ByteBuffer> auths = new HashSet<ByteBuffer>(Arrays.asList(s2bb("A"), s2bb("B")));
            client.changeUserAuthorizations(badLogin, "stooge", auths);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.changeLocalUserPassword(badLogin, "stooge", s2bb(""));
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.createLocalUser(badLogin, "stooge", s2bb("password"));
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.dropLocalUser(badLogin, "stooge");
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.getUserAuthorizations(badLogin, "stooge");
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.grantSystemPermission(badLogin, "stooge", SystemPermission.CREATE_TABLE);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.grantTablePermission(badLogin, "root", table, TablePermission.WRITE);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.hasSystemPermission(badLogin, "stooge", SystemPermission.CREATE_TABLE);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.hasTablePermission(badLogin, "root", table, TablePermission.WRITE);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.listLocalUsers(badLogin);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.revokeSystemPermission(badLogin, "stooge", SystemPermission.CREATE_TABLE);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.revokeTablePermission(badLogin, "root", table, TablePermission.ALTER_TABLE);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.createScanner(badLogin, table, new ScanOptions());
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.createBatchScanner(badLogin, table, new BatchScanOptions());
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.updateAndFlush(badLogin, table, new HashMap<ByteBuffer, List<ColumnUpdate>>());
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.createWriter(badLogin, table, new WriterOptions());
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.attachIterator(badLogin, "slow", setting, EnumSet.allOf(IteratorScope.class));
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.checkIteratorConflicts(badLogin, table, setting, EnumSet.allOf(IteratorScope.class));
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            final String TABLE_TEST = makeTableName();
            client.cloneTable(badLogin, table, TABLE_TEST, false, null, null);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.exportTable(badLogin, table, "/tmp");
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.importTable(badLogin, "testify", "/tmp");
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.getIteratorSetting(badLogin, table, "foo", IteratorScope.SCAN);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.listIterators(badLogin, table);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.removeIterator(badLogin, table, "name", EnumSet.allOf(IteratorScope.class));
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.splitRangeByTablets(badLogin, table, client.getRowRange(ByteBuffer.wrap("row".getBytes())), 10);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            File importDir = tempFolder.newFolder("importDir");
            File failuresDir = tempFolder.newFolder("failuresDir");
            client.importDirectory(badLogin, table, importDir.getAbsolutePath(), failuresDir.getAbsolutePath(),
                    true);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.pingTabletServer(badLogin, "fake");
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.login("badUser", properties);
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.testTableClassLoad(badLogin, table, VersioningIterator.class.getName(),
                    SortedKeyValueIterator.class.getName());
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
        try {
            client.createConditionalWriter(badLogin, table, new ConditionalWriterOptions());
            fail("exception not thrown");
        } catch (AccumuloSecurityException ex) {
        }
    }

    @Test(timeout = 10000)
    public void tableNotFound() throws Exception {
        final String doesNotExist = "doesNotExists";
        try {
            client.addConstraint(creds, doesNotExist, NumericValueConstraint.class.getName());
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.addSplits(creds, doesNotExist, Collections.<ByteBuffer>emptySet());
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        final IteratorSetting setting = new IteratorSetting(100, "slow", SlowIterator.class.getName(),
                Collections.singletonMap("sleepTime", "200"));
        try {
            client.attachIterator(creds, doesNotExist, setting, EnumSet.allOf(IteratorScope.class));
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.cancelCompaction(creds, doesNotExist);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.checkIteratorConflicts(creds, doesNotExist, setting, EnumSet.allOf(IteratorScope.class));
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.clearLocatorCache(creds, doesNotExist);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            final String TABLE_TEST = makeTableName();
            client.cloneTable(creds, doesNotExist, TABLE_TEST, false, null, null);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.compactTable(creds, doesNotExist, null, null, null, true, false);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.createBatchScanner(creds, doesNotExist, new BatchScanOptions());
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.createScanner(creds, doesNotExist, new ScanOptions());
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.createWriter(creds, doesNotExist, new WriterOptions());
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.deleteRows(creds, doesNotExist, null, null);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.deleteTable(creds, doesNotExist);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.exportTable(creds, doesNotExist, "/tmp");
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.flushTable(creds, doesNotExist, null, null, false);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.getIteratorSetting(creds, doesNotExist, "foo", IteratorScope.SCAN);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.getLocalityGroups(creds, doesNotExist);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.getMaxRow(creds, doesNotExist, Collections.<ByteBuffer>emptySet(), null, false, null, false);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.getTableProperties(creds, doesNotExist);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.grantTablePermission(creds, "root", doesNotExist, TablePermission.WRITE);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.hasTablePermission(creds, "root", doesNotExist, TablePermission.WRITE);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            File importDir = tempFolder.newFolder("importDir");
            File failuresDir = tempFolder.newFolder("failuresDir");
            client.importDirectory(creds, doesNotExist, importDir.getAbsolutePath(), failuresDir.getAbsolutePath(),
                    true);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.listConstraints(creds, doesNotExist);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.listSplits(creds, doesNotExist, 10000);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.mergeTablets(creds, doesNotExist, null, null);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.offlineTable(creds, doesNotExist, false);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.onlineTable(creds, doesNotExist, false);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.removeConstraint(creds, doesNotExist, 0);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.removeIterator(creds, doesNotExist, "name", EnumSet.allOf(IteratorScope.class));
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.removeTableProperty(creds, doesNotExist, Property.TABLE_FILE_MAX.getKey());
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.renameTable(creds, doesNotExist, "someTableName");
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.revokeTablePermission(creds, "root", doesNotExist, TablePermission.ALTER_TABLE);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.setTableProperty(creds, doesNotExist, Property.TABLE_FILE_MAX.getKey(), "0");
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.splitRangeByTablets(creds, doesNotExist, client.getRowRange(ByteBuffer.wrap("row".getBytes())),
                    10);
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.updateAndFlush(creds, doesNotExist, new HashMap<ByteBuffer, List<ColumnUpdate>>());
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.getDiskUsage(creds, Collections.singleton(doesNotExist));
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.testTableClassLoad(creds, doesNotExist, VersioningIterator.class.getName(),
                    SortedKeyValueIterator.class.getName());
            fail("exception not thrown");
        } catch (TableNotFoundException ex) {
        }
        try {
            client.createConditionalWriter(creds, doesNotExist, new ConditionalWriterOptions());
        } catch (TableNotFoundException ex) {
        }
    }

    @Test(timeout = 10000)
    public void testExists() throws Exception {
        client.createTable(creds, "ett1", false, TimeType.MILLIS);
        client.createTable(creds, "ett2", false, TimeType.MILLIS);
        try {
            client.createTable(creds, "ett1", false, TimeType.MILLIS);
            fail("exception not thrown");
        } catch (TableExistsException tee) {
        }
        try {
            client.renameTable(creds, "ett1", "ett2");
            fail("exception not thrown");
        } catch (TableExistsException tee) {
        }
        try {
            client.cloneTable(creds, "ett1", "ett2", false, new HashMap<String, String>(), new HashSet<String>());
            fail("exception not thrown");
        } catch (TableExistsException tee) {
        }
    }

    @Test(timeout = 10000)
    public void testUnknownScanner() throws Exception {
        final String TABLE_TEST = makeTableName();

        client.createTable(creds, TABLE_TEST, true, TimeType.MILLIS);

        String scanner = client.createScanner(creds, TABLE_TEST, null);
        assertFalse(client.hasNext(scanner));
        client.closeScanner(scanner);

        try {
            client.hasNext(scanner);
            fail("exception not thrown");
        } catch (UnknownScanner us) {
        }

        try {
            client.closeScanner(scanner);
            fail("exception not thrown");
        } catch (UnknownScanner us) {
        }

        try {
            client.nextEntry("99999999");
            fail("exception not thrown");
        } catch (UnknownScanner us) {
        }
        try {
            client.nextK("99999999", 6);
            fail("exception not thrown");
        } catch (UnknownScanner us) {
        }
        try {
            client.hasNext("99999999");
            fail("exception not thrown");
        } catch (UnknownScanner us) {
        }
        try {
            client.hasNext(UUID.randomUUID().toString());
            fail("exception not thrown");
        } catch (UnknownScanner us) {
        }
    }

    @Test(timeout = 10000)
    public void testUnknownWriter() throws Exception {
        final String TABLE_TEST = makeTableName();

        client.createTable(creds, TABLE_TEST, true, TimeType.MILLIS);

        String writer = client.createWriter(creds, TABLE_TEST, null);
        client.update(writer, mutation("row0", "cf", "cq", "value"));
        client.flush(writer);
        client.update(writer, mutation("row2", "cf", "cq", "value2"));
        client.closeWriter(writer);

        // this is a oneway call, so it does not throw exceptions
        client.update(writer, mutation("row2", "cf", "cq", "value2"));

        try {
            client.flush(writer);
            fail("exception not thrown");
        } catch (UnknownWriter uw) {
        }
        try {
            client.flush("99999");
            fail("exception not thrown");
        } catch (UnknownWriter uw) {
        }
        try {
            client.flush(UUID.randomUUID().toString());
            fail("exception not thrown");
        } catch (UnknownWriter uw) {
        }
        try {
            client.closeWriter("99999");
            fail("exception not thrown");
        } catch (UnknownWriter uw) {
        }
    }

    @Test(timeout = 10000)
    public void testDelete() throws Exception {
        final String TABLE_TEST = makeTableName();

        client.createTable(creds, TABLE_TEST, true, TimeType.MILLIS);
        client.updateAndFlush(creds, TABLE_TEST, mutation("row0", "cf", "cq", "value"));

        assertScan(new String[][] { { "row0", "cf", "cq", "value" } }, TABLE_TEST);

        ColumnUpdate upd = new ColumnUpdate(s2bb("cf"), s2bb("cq"));
        upd.setDeleteCell(true);
        Map<ByteBuffer, List<ColumnUpdate>> delete = Collections.singletonMap(s2bb("row0"),
                Collections.singletonList(upd));

        client.updateAndFlush(creds, TABLE_TEST, delete);

        assertScan(new String[][] {}, TABLE_TEST);
    }

    @Test(timeout = 10000)
    public void testInstanceOperations() throws Exception {
        int tservers = 0;
        for (String tserver : client.getTabletServers(creds)) {
            client.pingTabletServer(creds, tserver);
            tservers++;
        }
        assertTrue(tservers > 0);

        // get something we know is in the site config
        Map<String, String> cfg = client.getSiteConfiguration(creds);
        assertTrue(cfg.get("instance.dfs.dir").startsWith(macTestFolder.getPath()));

        // set a property in zookeeper
        client.setProperty(creds, "table.split.threshold", "500M");

        // check that we can read it
        for (int i = 0; i < 5; i++) {
            cfg = client.getSystemConfiguration(creds);
            if ("500M".equals(cfg.get("table.split.threshold")))
                break;
            UtilWaitThread.sleep(200);
        }
        assertEquals("500M", cfg.get("table.split.threshold"));

        // unset the setting, check that it's not what it was
        client.removeProperty(creds, "table.split.threshold");
        for (int i = 0; i < 5; i++) {
            cfg = client.getSystemConfiguration(creds);
            if (!"500M".equals(cfg.get("table.split.threshold")))
                break;
            UtilWaitThread.sleep(200);
        }
        assertNotEquals("500M", cfg.get("table.split.threshold"));

        // try to load some classes via the proxy
        assertTrue(client.testClassLoad(creds, DevNull.class.getName(), SortedKeyValueIterator.class.getName()));
        assertFalse(client.testClassLoad(creds, "foo.bar", SortedKeyValueIterator.class.getName()));

        // create a table that's very slow, so we can look for scans/compactions
        client.createTable(creds, "slow", true, TimeType.MILLIS);
        IteratorSetting setting = new IteratorSetting(100, "slow", SlowIterator.class.getName(),
                Collections.singletonMap("sleepTime", "200"));
        client.attachIterator(creds, "slow", setting, EnumSet.allOf(IteratorScope.class));
        client.updateAndFlush(creds, "slow", mutation("row", "cf", "cq", "value"));
        client.updateAndFlush(creds, "slow", mutation("row2", "cf", "cq", "value"));
        client.updateAndFlush(creds, "slow", mutation("row3", "cf", "cq", "value"));
        client.updateAndFlush(creds, "slow", mutation("row4", "cf", "cq", "value"));

        // scan
        Thread t = new Thread() {
            @Override
            public void run() {
                String scanner;
                try {
                    Client client2 = new TestProxyClient("localhost", proxyPort, protocolClass.newInstance())
                            .proxy();
                    scanner = client2.createScanner(creds, "slow", null);
                    client2.nextK(scanner, 10);
                    client2.closeScanner(scanner);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        };
        t.start();
        // look for the scan
        List<ActiveScan> scans = Collections.emptyList();
        loop: for (int i = 0; i < 100; i++) {
            for (String tserver : client.getTabletServers(creds)) {
                scans = client.getActiveScans(creds, tserver);
                if (!scans.isEmpty())
                    break loop;
                UtilWaitThread.sleep(10);
            }
        }
        t.join();
        assertFalse(scans.isEmpty());
        ActiveScan scan = scans.get(0);
        assertEquals("root", scan.getUser());
        assertTrue(ScanState.RUNNING.equals(scan.getState()) || ScanState.QUEUED.equals(scan.getState()));
        assertEquals(ScanType.SINGLE, scan.getType());
        assertEquals("slow", scan.getTable());
        Map<String, String> map = client.tableIdMap(creds);
        assertEquals(map.get("slow"), scan.getExtent().tableId);
        assertTrue(scan.getExtent().endRow == null);
        assertTrue(scan.getExtent().prevEndRow == null);

        // start a compaction
        t = new Thread() {
            @Override
            public void run() {
                try {
                    Client client2 = new TestProxyClient("localhost", proxyPort, protocolClass.newInstance())
                            .proxy();
                    client2.compactTable(creds, "slow", null, null, null, true, true);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        };
        t.start();

        // try to catch it in the act
        List<ActiveCompaction> compactions = Collections.emptyList();
        loop2: for (int i = 0; i < 100; i++) {
            for (String tserver : client.getTabletServers(creds)) {
                compactions = client.getActiveCompactions(creds, tserver);
                if (!compactions.isEmpty())
                    break loop2;
            }
            UtilWaitThread.sleep(10);
        }
        t.join();
        // verify the compaction information
        assertFalse(compactions.isEmpty());
        ActiveCompaction c = compactions.get(0);
        assertEquals(map.get("slow"), c.getExtent().tableId);
        assertTrue(c.inputFiles.isEmpty());
        assertEquals(CompactionType.MINOR, c.getType());
        assertEquals(CompactionReason.USER, c.getReason());
        assertEquals("", c.localityGroup);
        assertTrue(c.outputFile.contains("default_tablet"));
    }

    @Test
    public void testSecurityOperations() throws Exception {
        final String TABLE_TEST = makeTableName();

        // check password
        assertTrue(client.authenticateUser(creds, "root", s2pp(secret)));
        assertFalse(client.authenticateUser(creds, "root", s2pp("")));

        // create a user
        client.createLocalUser(creds, "stooge", s2bb("password"));
        // change auths
        Set<String> users = client.listLocalUsers(creds);
        assertEquals(new HashSet<String>(Arrays.asList("root", "stooge")), users);
        HashSet<ByteBuffer> auths = new HashSet<ByteBuffer>(Arrays.asList(s2bb("A"), s2bb("B")));
        client.changeUserAuthorizations(creds, "stooge", auths);
        List<ByteBuffer> update = client.getUserAuthorizations(creds, "stooge");
        assertEquals(auths, new HashSet<ByteBuffer>(update));

        // change password
        client.changeLocalUserPassword(creds, "stooge", s2bb(""));
        assertTrue(client.authenticateUser(creds, "stooge", s2pp("")));

        // check permission failure
        @SuppressWarnings("serial")
        ByteBuffer stooge = client.login("stooge", new TreeMap<String, String>() {
            {
                put("password", "");
            }
        });

        try {
            client.createTable(stooge, "fail", true, TimeType.MILLIS);
            fail("should not create the table");
        } catch (AccumuloSecurityException ex) {
            assertFalse(client.listTables(creds).contains("fail"));
        }
        // grant permissions and test
        assertFalse(client.hasSystemPermission(creds, "stooge", SystemPermission.CREATE_TABLE));
        client.grantSystemPermission(creds, "stooge", SystemPermission.CREATE_TABLE);
        assertTrue(client.hasSystemPermission(creds, "stooge", SystemPermission.CREATE_TABLE));
        client.createTable(stooge, "success", true, TimeType.MILLIS);
        client.listTables(creds).contains("succcess");

        // revoke permissions
        client.revokeSystemPermission(creds, "stooge", SystemPermission.CREATE_TABLE);
        assertFalse(client.hasSystemPermission(creds, "stooge", SystemPermission.CREATE_TABLE));
        try {
            client.createTable(stooge, "fail", true, TimeType.MILLIS);
            fail("should not create the table");
        } catch (AccumuloSecurityException ex) {
            assertFalse(client.listTables(creds).contains("fail"));
        }
        // create a table to test table permissions
        client.createTable(creds, TABLE_TEST, true, TimeType.MILLIS);
        // denied!
        try {
            String scanner = client.createScanner(stooge, TABLE_TEST, null);
            client.nextK(scanner, 100);
            fail("stooge should not read table test");
        } catch (AccumuloSecurityException ex) {
        }
        // grant
        assertFalse(client.hasTablePermission(creds, "stooge", TABLE_TEST, TablePermission.READ));
        client.grantTablePermission(creds, "stooge", TABLE_TEST, TablePermission.READ);
        assertTrue(client.hasTablePermission(creds, "stooge", TABLE_TEST, TablePermission.READ));
        String scanner = client.createScanner(stooge, TABLE_TEST, null);
        client.nextK(scanner, 10);
        client.closeScanner(scanner);
        // revoke
        client.revokeTablePermission(creds, "stooge", TABLE_TEST, TablePermission.READ);
        assertFalse(client.hasTablePermission(creds, "stooge", TABLE_TEST, TablePermission.READ));
        try {
            scanner = client.createScanner(stooge, TABLE_TEST, null);
            client.nextK(scanner, 100);
            fail("stooge should not read table test");
        } catch (AccumuloSecurityException ex) {
        }

        // delete user
        client.dropLocalUser(creds, "stooge");
        users = client.listLocalUsers(creds);
        assertEquals(1, users.size());

    }

    @Test
    public void testBatchWriter() throws Exception {
        final String TABLE_TEST = makeTableName();

        client.createTable(creds, TABLE_TEST, true, TimeType.MILLIS);
        client.addConstraint(creds, TABLE_TEST, NumericValueConstraint.class.getName());

        WriterOptions writerOptions = new WriterOptions();
        writerOptions.setLatencyMs(10000);
        writerOptions.setMaxMemory(2);
        writerOptions.setThreads(1);
        writerOptions.setTimeoutMs(100000);

        String batchWriter = client.createWriter(creds, TABLE_TEST, writerOptions);
        client.update(batchWriter, mutation("row1", "cf", "cq", "x"));
        client.update(batchWriter, mutation("row1", "cf", "cq", "x"));
        try {
            client.flush(batchWriter);
            fail("constraint did not fire");
        } catch (MutationsRejectedException ex) {
        }
        try {
            client.closeWriter(batchWriter);
            fail("constraint did not fire");
        } catch (MutationsRejectedException e) {
        }

        client.removeConstraint(creds, TABLE_TEST, 2);

        assertScan(new String[][] {}, TABLE_TEST);

        UtilWaitThread.sleep(2000);

        writerOptions = new WriterOptions();
        writerOptions.setLatencyMs(10000);
        writerOptions.setMaxMemory(3000);
        writerOptions.setThreads(1);
        writerOptions.setTimeoutMs(100000);

        batchWriter = client.createWriter(creds, TABLE_TEST, writerOptions);

        client.update(batchWriter, mutation("row1", "cf", "cq", "x"));
        client.flush(batchWriter);
        client.closeWriter(batchWriter);

        assertScan(new String[][] { { "row1", "cf", "cq", "x" } }, TABLE_TEST);

        client.deleteTable(creds, TABLE_TEST);
    }

    @Test
    public void testTableOperations() throws Exception {
        final String TABLE_TEST = makeTableName();

        client.createTable(creds, TABLE_TEST, true, TimeType.MILLIS);
        // constraints
        client.addConstraint(creds, TABLE_TEST, NumericValueConstraint.class.getName());
        assertEquals(2, client.listConstraints(creds, TABLE_TEST).size());

        UtilWaitThread.sleep(2000);

        client.updateAndFlush(creds, TABLE_TEST, mutation("row1", "cf", "cq", "123"));

        try {
            client.updateAndFlush(creds, TABLE_TEST, mutation("row1", "cf", "cq", "x"));
            fail("constraint did not fire");
        } catch (MutationsRejectedException ex) {
        }

        client.removeConstraint(creds, TABLE_TEST, 2);

        UtilWaitThread.sleep(2000);

        assertEquals(1, client.listConstraints(creds, TABLE_TEST).size());

        client.updateAndFlush(creds, TABLE_TEST, mutation("row1", "cf", "cq", "x"));
        assertScan(new String[][] { { "row1", "cf", "cq", "x" } }, TABLE_TEST);
        // splits, merge
        client.addSplits(creds, TABLE_TEST,
                new HashSet<ByteBuffer>(Arrays.asList(s2bb("a"), s2bb("m"), s2bb("z"))));
        List<ByteBuffer> splits = client.listSplits(creds, TABLE_TEST, 1);
        assertEquals(Arrays.asList(s2bb("m")), splits);
        client.mergeTablets(creds, TABLE_TEST, null, s2bb("m"));
        splits = client.listSplits(creds, TABLE_TEST, 10);
        assertEquals(Arrays.asList(s2bb("m"), s2bb("z")), splits);
        client.mergeTablets(creds, TABLE_TEST, null, null);
        splits = client.listSplits(creds, TABLE_TEST, 10);
        List<ByteBuffer> empty = Collections.emptyList();
        assertEquals(empty, splits);
        // iterators
        client.deleteTable(creds, TABLE_TEST);
        client.createTable(creds, TABLE_TEST, true, TimeType.MILLIS);
        HashMap<String, String> options = new HashMap<String, String>();
        options.put("type", "STRING");
        options.put("columns", "cf");
        IteratorSetting setting = new IteratorSetting(10, TABLE_TEST, SummingCombiner.class.getName(), options);
        client.attachIterator(creds, TABLE_TEST, setting, EnumSet.allOf(IteratorScope.class));
        for (int i = 0; i < 10; i++) {
            client.updateAndFlush(creds, TABLE_TEST, mutation("row1", "cf", "cq", "1"));
        }
        assertScan(new String[][] { { "row1", "cf", "cq", "10" } }, TABLE_TEST);
        try {
            client.checkIteratorConflicts(creds, TABLE_TEST, setting, EnumSet.allOf(IteratorScope.class));
            fail("checkIteratorConflicts did not throw an exception");
        } catch (Exception ex) {
        }
        client.deleteRows(creds, TABLE_TEST, null, null);
        client.removeIterator(creds, TABLE_TEST, "test", EnumSet.allOf(IteratorScope.class));
        String expected[][] = new String[10][];
        for (int i = 0; i < 10; i++) {
            client.updateAndFlush(creds, TABLE_TEST, mutation("row" + i, "cf", "cq", "" + i));
            expected[i] = new String[] { "row" + i, "cf", "cq", "" + i };
            client.flushTable(creds, TABLE_TEST, null, null, true);
        }
        assertScan(expected, TABLE_TEST);
        // clone
        final String TABLE_TEST2 = makeTableName();
        client.cloneTable(creds, TABLE_TEST, TABLE_TEST2, true, null, null);
        assertScan(expected, TABLE_TEST2);
        client.deleteTable(creds, TABLE_TEST2);

        // don't know how to test this, call it just for fun
        client.clearLocatorCache(creds, TABLE_TEST);

        // compact
        client.compactTable(creds, TABLE_TEST, null, null, null, true, true);
        assertEquals(1, countFiles(TABLE_TEST));
        assertScan(expected, TABLE_TEST);

        // get disk usage
        client.cloneTable(creds, TABLE_TEST, TABLE_TEST2, true, null, null);
        Set<String> tablesToScan = new HashSet<String>();
        tablesToScan.add(TABLE_TEST);
        tablesToScan.add(TABLE_TEST2);
        tablesToScan.add("foo");
        client.createTable(creds, "foo", true, TimeType.MILLIS);
        List<DiskUsage> diskUsage = (client.getDiskUsage(creds, tablesToScan));
        assertEquals(2, diskUsage.size());
        assertEquals(1, diskUsage.get(0).getTables().size());
        assertEquals(2, diskUsage.get(1).getTables().size());
        client.compactTable(creds, TABLE_TEST2, null, null, null, true, true);
        diskUsage = (client.getDiskUsage(creds, tablesToScan));
        assertEquals(3, diskUsage.size());
        assertEquals(1, diskUsage.get(0).getTables().size());
        assertEquals(1, diskUsage.get(1).getTables().size());
        assertEquals(1, diskUsage.get(2).getTables().size());
        client.deleteTable(creds, "foo");
        client.deleteTable(creds, TABLE_TEST2);

        // export/import
        File dir = tempFolder.newFolder("test");
        File destDir = tempFolder.newFolder("test_dest");
        client.offlineTable(creds, TABLE_TEST, false);
        client.exportTable(creds, TABLE_TEST, dir.getAbsolutePath());
        // copy files to a new location
        FileSystem fs = FileSystem.get(new Configuration());
        FSDataInputStream is = fs.open(new Path(dir + "/distcp.txt"));
        BufferedReader r = new BufferedReader(new InputStreamReader(is));
        while (true) {
            String line = r.readLine();
            if (line == null)
                break;
            Path srcPath = new Path(line);
            FileUtils.copyFile(new File(srcPath.toUri().getPath()), new File(destDir, srcPath.getName()));
        }
        client.deleteTable(creds, TABLE_TEST);
        client.importTable(creds, "testify", destDir.getAbsolutePath());
        assertScan(expected, "testify");
        client.deleteTable(creds, "testify");

        try {
            // ACCUMULO-1558 a second import from the same dir should fail, the first import moved the files
            client.importTable(creds, "testify2", destDir.getAbsolutePath());
            fail();
        } catch (Exception e) {
        }

        assertFalse(client.listTables(creds).contains("testify2"));

        // Locality groups
        client.createTable(creds, "test", true, TimeType.MILLIS);
        Map<String, Set<String>> groups = new HashMap<String, Set<String>>();
        groups.put("group1", Collections.singleton("cf1"));
        groups.put("group2", Collections.singleton("cf2"));
        client.setLocalityGroups(creds, "test", groups);
        assertEquals(groups, client.getLocalityGroups(creds, "test"));
        // table properties
        Map<String, String> orig = client.getTableProperties(creds, "test");
        client.setTableProperty(creds, "test", "table.split.threshold", "500M");
        Map<String, String> update = client.getTableProperties(creds, "test");
        assertEquals(update.get("table.split.threshold"), "500M");
        client.removeTableProperty(creds, "test", "table.split.threshold");
        update = client.getTableProperties(creds, "test");
        assertEquals(orig, update);
        // rename table
        Map<String, String> tables = client.tableIdMap(creds);
        client.renameTable(creds, "test", "bar");
        Map<String, String> tables2 = client.tableIdMap(creds);
        assertEquals(tables.get("test"), tables2.get("bar"));
        // table exists
        assertTrue(client.tableExists(creds, "bar"));
        assertFalse(client.tableExists(creds, "test"));
        // bulk import
        String filename = dir + "/bulk/import/rfile.rf";
        FileSKVWriter writer = FileOperations.getInstance().openWriter(filename, fs, fs.getConf(),
                DefaultConfiguration.getInstance());
        writer.startDefaultLocalityGroup();
        writer.append(new org.apache.accumulo.core.data.Key(new Text("a"), new Text("b"), new Text("c")),
                new Value("value".getBytes()));
        writer.close();
        fs.mkdirs(new Path(dir + "/bulk/fail"));
        client.importDirectory(creds, "bar", dir + "/bulk/import", dir + "/bulk/fail", true);
        String scanner = client.createScanner(creds, "bar", null);
        ScanResult more = client.nextK(scanner, 100);
        client.closeScanner(scanner);
        assertEquals(1, more.results.size());
        ByteBuffer maxRow = client.getMaxRow(creds, "bar", null, null, false, null, false);
        assertEquals(s2bb("a"), maxRow);

        assertFalse(client.testTableClassLoad(creds, "bar", "abc123", SortedKeyValueIterator.class.getName()));
        assertTrue(client.testTableClassLoad(creds, "bar", VersioningIterator.class.getName(),
                SortedKeyValueIterator.class.getName()));
    }

    private Condition newCondition(String cf, String cq) {
        return new Condition(new Column(s2bb(cf), s2bb(cq), s2bb("")));
    }

    private Condition newCondition(String cf, String cq, String val) {
        return newCondition(cf, cq).setValue(s2bb(val));
    }

    private Condition newCondition(String cf, String cq, long ts, String val) {
        return newCondition(cf, cq).setValue(s2bb(val)).setTimestamp(ts);
    }

    private ColumnUpdate newColUpdate(String cf, String cq, String val) {
        return new ColumnUpdate(s2bb(cf), s2bb(cq)).setValue(s2bb(val));
    }

    private ColumnUpdate newColUpdate(String cf, String cq, long ts, String val) {
        return new ColumnUpdate(s2bb(cf), s2bb(cq)).setTimestamp(ts).setValue(s2bb(val));
    }

    private void assertScan(String[][] expected, String table) throws Exception {
        String scid = client.createScanner(creds, table, new ScanOptions());
        ScanResult keyValues = client.nextK(scid, expected.length + 1);

        assertEquals(expected.length, keyValues.results.size());
        assertFalse(keyValues.more);

        for (int i = 0; i < keyValues.results.size(); i++) {
            checkKey(expected[i][0], expected[i][1], expected[i][2], expected[i][3], keyValues.results.get(i));
        }

        client.closeScanner(scid);
    }

    @Test
    public void testConditionalWriter() throws Exception {
        final String TABLE_TEST = makeTableName();

        client.createTable(creds, TABLE_TEST, true, TimeType.MILLIS);

        client.addConstraint(creds, TABLE_TEST, NumericValueConstraint.class.getName());

        String cwid = client.createConditionalWriter(creds, TABLE_TEST, new ConditionalWriterOptions());

        Map<ByteBuffer, ConditionalUpdates> updates = new HashMap<ByteBuffer, ConditionalUpdates>();

        updates.put(s2bb("00345"), new ConditionalUpdates(Arrays.asList(newCondition("meta", "seq")),
                Arrays.asList(newColUpdate("meta", "seq", 10, "1"), newColUpdate("data", "img", "73435435"))));

        Map<ByteBuffer, ConditionalStatus> results = client.updateRowsConditionally(cwid, updates);

        assertEquals(1, results.size());
        assertEquals(ConditionalStatus.ACCEPTED, results.get(s2bb("00345")));

        assertScan(new String[][] { { "00345", "data", "img", "73435435" }, { "00345", "meta", "seq", "1" } },
                TABLE_TEST);

        // test not setting values on conditions
        updates.clear();

        updates.put(s2bb("00345"), new ConditionalUpdates(Arrays.asList(newCondition("meta", "seq")),
                Arrays.asList(newColUpdate("meta", "seq", "2"))));
        updates.put(s2bb("00346"), new ConditionalUpdates(Arrays.asList(newCondition("meta", "seq")),
                Arrays.asList(newColUpdate("meta", "seq", "1"))));

        results = client.updateRowsConditionally(cwid, updates);

        assertEquals(2, results.size());
        assertEquals(ConditionalStatus.REJECTED, results.get(s2bb("00345")));
        assertEquals(ConditionalStatus.ACCEPTED, results.get(s2bb("00346")));

        assertScan(new String[][] { { "00345", "data", "img", "73435435" }, { "00345", "meta", "seq", "1" },
                { "00346", "meta", "seq", "1" } }, TABLE_TEST);

        // test setting values on conditions
        updates.clear();

        updates.put(s2bb("00345"), new ConditionalUpdates(Arrays.asList(newCondition("meta", "seq", "1")),
                Arrays.asList(newColUpdate("meta", "seq", 20, "2"), newColUpdate("data", "img", "567890"))));

        updates.put(s2bb("00346"), new ConditionalUpdates(Arrays.asList(newCondition("meta", "seq", "2")),
                Arrays.asList(newColUpdate("meta", "seq", "3"))));

        results = client.updateRowsConditionally(cwid, updates);

        assertEquals(2, results.size());
        assertEquals(ConditionalStatus.ACCEPTED, results.get(s2bb("00345")));
        assertEquals(ConditionalStatus.REJECTED, results.get(s2bb("00346")));

        assertScan(new String[][] { { "00345", "data", "img", "567890" }, { "00345", "meta", "seq", "2" },
                { "00346", "meta", "seq", "1" } }, TABLE_TEST);

        // test setting timestamp on condition to a non-existant version
        updates.clear();

        updates.put(s2bb("00345"), new ConditionalUpdates(Arrays.asList(newCondition("meta", "seq", 10, "2")),
                Arrays.asList(newColUpdate("meta", "seq", 30, "3"), newColUpdate("data", "img", "1234567890"))));

        results = client.updateRowsConditionally(cwid, updates);

        assertEquals(1, results.size());
        assertEquals(ConditionalStatus.REJECTED, results.get(s2bb("00345")));

        assertScan(new String[][] { { "00345", "data", "img", "567890" }, { "00345", "meta", "seq", "2" },
                { "00346", "meta", "seq", "1" } }, TABLE_TEST);

        // test setting timestamp to an existing version

        updates.clear();

        updates.put(s2bb("00345"), new ConditionalUpdates(Arrays.asList(newCondition("meta", "seq", 20, "2")),
                Arrays.asList(newColUpdate("meta", "seq", 30, "3"), newColUpdate("data", "img", "1234567890"))));

        results = client.updateRowsConditionally(cwid, updates);

        assertEquals(1, results.size());
        assertEquals(ConditionalStatus.ACCEPTED, results.get(s2bb("00345")));

        assertScan(new String[][] { { "00345", "data", "img", "1234567890" }, { "00345", "meta", "seq", "3" },
                { "00346", "meta", "seq", "1" } }, TABLE_TEST);

        // run test w/ condition that has iterators
        // following should fail w/o iterator
        client.updateAndFlush(creds, TABLE_TEST,
                Collections.singletonMap(s2bb("00347"), Arrays.asList(newColUpdate("data", "count", "1"))));
        client.updateAndFlush(creds, TABLE_TEST,
                Collections.singletonMap(s2bb("00347"), Arrays.asList(newColUpdate("data", "count", "1"))));
        client.updateAndFlush(creds, TABLE_TEST,
                Collections.singletonMap(s2bb("00347"), Arrays.asList(newColUpdate("data", "count", "1"))));

        updates.clear();
        updates.put(s2bb("00347"), new ConditionalUpdates(Arrays.asList(newCondition("data", "count", "3")),
                Arrays.asList(newColUpdate("data", "img", "1234567890"))));

        results = client.updateRowsConditionally(cwid, updates);

        assertEquals(1, results.size());
        assertEquals(ConditionalStatus.REJECTED, results.get(s2bb("00347")));

        assertScan(new String[][] { { "00345", "data", "img", "1234567890" }, { "00345", "meta", "seq", "3" },
                { "00346", "meta", "seq", "1" }, { "00347", "data", "count", "1" } }, TABLE_TEST);

        // following test w/ iterator setup should succeed
        Condition iterCond = newCondition("data", "count", "3");
        Map<String, String> props = new HashMap<String, String>();
        props.put("type", "STRING");
        props.put("columns", "data:count");
        IteratorSetting is = new IteratorSetting(1, "sumc", SummingCombiner.class.getName(), props);
        iterCond.setIterators(Arrays.asList(is));

        updates.clear();
        updates.put(s2bb("00347"), new ConditionalUpdates(Arrays.asList(iterCond),
                Arrays.asList(newColUpdate("data", "img", "1234567890"))));

        results = client.updateRowsConditionally(cwid, updates);

        assertEquals(1, results.size());
        assertEquals(ConditionalStatus.ACCEPTED, results.get(s2bb("00347")));

        assertScan(new String[][] { { "00345", "data", "img", "1234567890" }, { "00345", "meta", "seq", "3" },
                { "00346", "meta", "seq", "1" }, { "00347", "data", "count", "1" },
                { "00347", "data", "img", "1234567890" } }, TABLE_TEST);

        // test a mutation that violated a constraint
        updates.clear();
        updates.put(s2bb("00347"), new ConditionalUpdates(Arrays.asList(newCondition("data", "img", "1234567890")),
                Arrays.asList(newColUpdate("data", "count", "A"))));

        results = client.updateRowsConditionally(cwid, updates);

        assertEquals(1, results.size());
        assertEquals(ConditionalStatus.VIOLATED, results.get(s2bb("00347")));

        assertScan(new String[][] { { "00345", "data", "img", "1234567890" }, { "00345", "meta", "seq", "3" },
                { "00346", "meta", "seq", "1" }, { "00347", "data", "count", "1" },
                { "00347", "data", "img", "1234567890" } }, TABLE_TEST);

        // run test with two conditions
        // both conditions should fail
        updates.clear();
        updates.put(s2bb("00347"), new ConditionalUpdates(
                Arrays.asList(newCondition("data", "img", "565"), newCondition("data", "count", "2")),
                Arrays.asList(newColUpdate("data", "count", "3"), newColUpdate("data", "img", "0987654321"))));

        results = client.updateRowsConditionally(cwid, updates);

        assertEquals(1, results.size());
        assertEquals(ConditionalStatus.REJECTED, results.get(s2bb("00347")));

        assertScan(new String[][] { { "00345", "data", "img", "1234567890" }, { "00345", "meta", "seq", "3" },
                { "00346", "meta", "seq", "1" }, { "00347", "data", "count", "1" },
                { "00347", "data", "img", "1234567890" } }, TABLE_TEST);

        // one condition should fail
        updates.clear();
        updates.put(s2bb("00347"), new ConditionalUpdates(
                Arrays.asList(newCondition("data", "img", "1234567890"), newCondition("data", "count", "2")),
                Arrays.asList(newColUpdate("data", "count", "3"), newColUpdate("data", "img", "0987654321"))));

        results = client.updateRowsConditionally(cwid, updates);

        assertEquals(1, results.size());
        assertEquals(ConditionalStatus.REJECTED, results.get(s2bb("00347")));

        assertScan(new String[][] { { "00345", "data", "img", "1234567890" }, { "00345", "meta", "seq", "3" },
                { "00346", "meta", "seq", "1" }, { "00347", "data", "count", "1" },
                { "00347", "data", "img", "1234567890" } }, TABLE_TEST);

        // one condition should fail
        updates.clear();
        updates.put(s2bb("00347"), new ConditionalUpdates(
                Arrays.asList(newCondition("data", "img", "565"), newCondition("data", "count", "1")),
                Arrays.asList(newColUpdate("data", "count", "3"), newColUpdate("data", "img", "0987654321"))));

        results = client.updateRowsConditionally(cwid, updates);

        assertEquals(1, results.size());
        assertEquals(ConditionalStatus.REJECTED, results.get(s2bb("00347")));

        assertScan(new String[][] { { "00345", "data", "img", "1234567890" }, { "00345", "meta", "seq", "3" },
                { "00346", "meta", "seq", "1" }, { "00347", "data", "count", "1" },
                { "00347", "data", "img", "1234567890" } }, TABLE_TEST);

        // both conditions should succeed

        ConditionalStatus result = client.updateRowConditionally(creds, TABLE_TEST, s2bb("00347"),
                new ConditionalUpdates(
                        Arrays.asList(newCondition("data", "img", "1234567890"),
                                newCondition("data", "count", "1")),
                        Arrays.asList(newColUpdate("data", "count", "3"),
                                newColUpdate("data", "img", "0987654321"))));

        assertEquals(ConditionalStatus.ACCEPTED, result);

        assertScan(new String[][] { { "00345", "data", "img", "1234567890" }, { "00345", "meta", "seq", "3" },
                { "00346", "meta", "seq", "1" }, { "00347", "data", "count", "3" },
                { "00347", "data", "img", "0987654321" } }, TABLE_TEST);

        client.closeConditionalWriter(cwid);
        try {
            client.updateRowsConditionally(cwid, updates);
            fail("conditional writer not closed");
        } catch (UnknownWriter uk) {
        }

        // run test with colvis
        client.createLocalUser(creds, "cwuser", s2bb("bestpasswordever"));
        client.changeUserAuthorizations(creds, "cwuser", Collections.singleton(s2bb("A")));
        client.grantTablePermission(creds, "cwuser", TABLE_TEST, TablePermission.WRITE);
        client.grantTablePermission(creds, "cwuser", TABLE_TEST, TablePermission.READ);

        ByteBuffer cwuCreds = client.login("cwuser", Collections.singletonMap("password", "bestpasswordever"));

        cwid = client.createConditionalWriter(cwuCreds, TABLE_TEST,
                new ConditionalWriterOptions().setAuthorizations(Collections.singleton(s2bb("A"))));

        updates.clear();
        updates.put(s2bb("00348"),
                new ConditionalUpdates(Arrays.asList(new Condition(new Column(s2bb("data"), s2bb("c"), s2bb("A")))),
                        Arrays.asList(newColUpdate("data", "seq", "1"),
                                newColUpdate("data", "c", "1").setColVisibility(s2bb("A")))));
        updates.put(s2bb("00349"),
                new ConditionalUpdates(Arrays.asList(new Condition(new Column(s2bb("data"), s2bb("c"), s2bb("B")))),
                        Arrays.asList(newColUpdate("data", "seq", "1"))));

        results = client.updateRowsConditionally(cwid, updates);

        assertEquals(2, results.size());
        assertEquals(ConditionalStatus.ACCEPTED, results.get(s2bb("00348")));
        assertEquals(ConditionalStatus.INVISIBLE_VISIBILITY, results.get(s2bb("00349")));

        assertScan(new String[][] { { "00345", "data", "img", "1234567890" }, { "00345", "meta", "seq", "3" },
                { "00346", "meta", "seq", "1" }, { "00347", "data", "count", "3" },
                { "00347", "data", "img", "0987654321" }, { "00348", "data", "seq", "1" } }, TABLE_TEST);

        updates.clear();

        updates.clear();
        updates.put(s2bb("00348"),
                new ConditionalUpdates(
                        Arrays.asList(
                                new Condition(new Column(s2bb("data"), s2bb("c"), s2bb("A"))).setValue(s2bb("0"))),
                        Arrays.asList(newColUpdate("data", "seq", "2"),
                                newColUpdate("data", "c", "2").setColVisibility(s2bb("A")))));

        results = client.updateRowsConditionally(cwid, updates);

        assertEquals(1, results.size());
        assertEquals(ConditionalStatus.REJECTED, results.get(s2bb("00348")));

        assertScan(new String[][] { { "00345", "data", "img", "1234567890" }, { "00345", "meta", "seq", "3" },
                { "00346", "meta", "seq", "1" }, { "00347", "data", "count", "3" },
                { "00347", "data", "img", "0987654321" }, { "00348", "data", "seq", "1" } }, TABLE_TEST);

        updates.clear();
        updates.put(s2bb("00348"),
                new ConditionalUpdates(
                        Arrays.asList(
                                new Condition(new Column(s2bb("data"), s2bb("c"), s2bb("A"))).setValue(s2bb("1"))),
                        Arrays.asList(newColUpdate("data", "seq", "2"),
                                newColUpdate("data", "c", "2").setColVisibility(s2bb("A")))));

        results = client.updateRowsConditionally(cwid, updates);

        assertEquals(1, results.size());
        assertEquals(ConditionalStatus.ACCEPTED, results.get(s2bb("00348")));

        assertScan(new String[][] { { "00345", "data", "img", "1234567890" }, { "00345", "meta", "seq", "3" },
                { "00346", "meta", "seq", "1" }, { "00347", "data", "count", "3" },
                { "00347", "data", "img", "0987654321" }, { "00348", "data", "seq", "2" } }, TABLE_TEST);

        client.closeConditionalWriter(cwid);
        try {
            client.updateRowsConditionally(cwid, updates);
            fail("conditional writer not closed");
        } catch (UnknownWriter uk) {
        }

        client.dropLocalUser(creds, "cwuser");

    }

    private void checkKey(String row, String cf, String cq, String val, KeyValue keyValue) {
        assertEquals(row, ByteBufferUtil.toString(keyValue.key.row));
        assertEquals(cf, ByteBufferUtil.toString(keyValue.key.colFamily));
        assertEquals(cq, ByteBufferUtil.toString(keyValue.key.colQualifier));
        assertEquals("", ByteBufferUtil.toString(keyValue.key.colVisibility));
        assertEquals(val, ByteBufferUtil.toString(keyValue.value));
    }

    // scan metadata for file entries for the given table
    private int countFiles(String table) throws Exception {
        Map<String, String> tableIdMap = client.tableIdMap(creds);
        String tableId = tableIdMap.get(table);
        Key start = new Key();
        start.row = s2bb(tableId + ";");
        Key end = new Key();
        end.row = s2bb(tableId + "<");
        end = client.getFollowing(end, PartialKey.ROW);
        ScanOptions opt = new ScanOptions();
        opt.range = new Range(start, true, end, false);
        opt.columns = Collections.singletonList(new ScanColumn(s2bb("file")));
        String scanner = client.createScanner(creds, MetadataTable.NAME, opt);
        int result = 0;
        while (true) {
            ScanResult more = client.nextK(scanner, 100);
            result += more.getResults().size();
            if (!more.more)
                break;
        }
        return result;
    }

    private Map<ByteBuffer, List<ColumnUpdate>> mutation(String row, String cf, String cq, String value) {
        ColumnUpdate upd = new ColumnUpdate(s2bb(cf), s2bb(cq));
        upd.setValue(value.getBytes());
        return Collections.singletonMap(s2bb(row), Collections.singletonList(upd));
    }

    private ByteBuffer s2bb(String cf) {
        return ByteBuffer.wrap(cf.getBytes());
    }

    private Map<String, String> s2pp(String cf) {
        Map<String, String> toRet = new TreeMap<String, String>();
        toRet.put("password", cf);
        return toRet;
    }

    static private ByteBuffer t2bb(Text t) {
        return ByteBuffer.wrap(t.getBytes());
    }

    @Test
    public void testGetRowRange() throws Exception {
        Range range = client.getRowRange(s2bb("xyzzy"));
        org.apache.accumulo.core.data.Range range2 = new org.apache.accumulo.core.data.Range(new Text("xyzzy"));
        assertEquals(0, range.start.row.compareTo(t2bb(range2.getStartKey().getRow())));
        assertEquals(0, range.stop.row.compareTo(t2bb(range2.getEndKey().getRow())));
        assertEquals(range.startInclusive, range2.isStartKeyInclusive());
        assertEquals(range.stopInclusive, range2.isEndKeyInclusive());
        assertEquals(0, range.start.colFamily.compareTo(t2bb(range2.getStartKey().getColumnFamily())));
        assertEquals(0, range.start.colQualifier.compareTo(t2bb(range2.getStartKey().getColumnQualifier())));
        assertEquals(0, range.stop.colFamily.compareTo(t2bb(range2.getEndKey().getColumnFamily())));
        assertEquals(0, range.stop.colQualifier.compareTo(t2bb(range2.getEndKey().getColumnQualifier())));
        assertEquals(range.start.timestamp, range.start.timestamp);
        assertEquals(range.stop.timestamp, range.stop.timestamp);
    }

    @AfterClass
    public static void tearDownMiniCluster() throws Exception {
        accumulo.stop();
    }
}