Package com.sun.sgs.test.impl.service.data.store

Source Code of com.sun.sgs.test.impl.service.data.store.MyRunnable

/*
* Copyright 2007-2010 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
* --
*/

package com.sun.sgs.test.impl.service.data.store;

import com.sun.sgs.app.NameNotBoundException;
import com.sun.sgs.app.ObjectNotFoundException;
import com.sun.sgs.app.TransactionAbortedException;
import com.sun.sgs.app.TransactionException;
import com.sun.sgs.app.TransactionNotActiveException;
import com.sun.sgs.app.TransactionTimeoutException;
import com.sun.sgs.impl.kernel.AccessCoordinatorHandle;
import com.sun.sgs.impl.kernel.StandardProperties;
import com.sun.sgs.impl.service.data.store.DataStoreException;
import com.sun.sgs.impl.service.data.store.DataStoreImpl;
import com.sun.sgs.impl.service.data.store.DataStoreProfileProducer;
import com.sun.sgs.kernel.ComponentRegistry;
import com.sun.sgs.service.Transaction;
import com.sun.sgs.service.TransactionParticipant;
import com.sun.sgs.service.store.ClassInfoNotFoundException;
import com.sun.sgs.service.store.DataStore;
import com.sun.sgs.test.util.DummyProfileCoordinator;
import com.sun.sgs.test.util.DummyTransaction;
import com.sun.sgs.test.util.DummyTransaction.UsePrepareAndCommit;
import com.sun.sgs.test.util.DummyTransactionProxy;
import static com.sun.sgs.test.util.UtilProperties.createProperties;
import com.sun.sgs.tools.test.FilteredNameRunner;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Properties;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;

/*
* XXX: Test recovery of prepared transactions after a crash
* XXX: Test concurrent access
*/

/** Test the DataStoreImpl class */
@RunWith(FilteredNameRunner.class)
public class TestDataStoreImpl extends Assert {

    /** The name of the DataStoreImpl class. */
    private static final String DataStoreImplClassName =
  DataStoreImpl.class.getName();

    /** Directory used for database shared across multiple tests. */
    protected static final String dbDirectory =
  System.getProperty("java.io.tmpdir") + File.separator +
  "TestDataStoreImpl.db";

    /** The default basic test environment, or {@code null} if not set. */
    private static BasicDataStoreTestEnv defaultEnv = null;

    /** The basic test environment. */
    protected final BasicDataStoreTestEnv env;

    /** The transaction proxy. */
    protected final DummyTransactionProxy txnProxy;

    /** The access coordinator. */
    protected final AccessCoordinatorHandle accessCoordinator;

    /** The system registry. */
    protected final ComponentRegistry systemRegistry;

    /** An instance of the data store, to test. */
    protected static DataStore store;

    /** Default properties for creating the DataStore. */
    protected Properties props;

    /** An initial, open transaction. */
    protected DummyTransaction txn;

    /** The object ID of a newly created object. */
    protected long id;

    /** Creates the test. */
    public TestDataStoreImpl() {
  this(defaultEnv == null
       ? defaultEnv = new BasicDataStoreTestEnv(System.getProperties())
       : defaultEnv);
    }

    /** Creates the test using the specified basic test environment. */
    protected TestDataStoreImpl(BasicDataStoreTestEnv env) {
  this.env = env;
  txnProxy = env.txnProxy;
  accessCoordinator = env.accessCoordinator; 
  systemRegistry = env.systemRegistry;
   

    /** Make sure an empty version of the directory exists. */
    @BeforeClass
    public static void setUpBeforeClass() {
  cleanDirectory(dbDirectory);
    }

    /** Creates the data store and an object. */
    @Before
    public void setUp() throws Exception {
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY, 10000);
  props = getProperties();
  if (store == null) {
      store = createDataStore();
  }
  id = store.createObject(txn);
    }

    /** Abort the current transaction, if non-null, and shutdown the store. */
    @After
    public void tearDown() throws Exception {
  try {
      if (txn != null) {
    txn.abort(new RuntimeException("abort"));
      }
  } catch (RuntimeException e) {
      e.printStackTrace();
      throw e;
  } finally {
            txn = null;
  }
    }

    /* -- Tests -- */

    /* -- Test constructor -- */

    @Test
    public void testConstructorNullArg() throws Exception {
  try {
      createDataStore(null);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    /**
     * Tests that the {@code DataStore} correctly infers the database
     * subdirectory when only the root directory is provided.
     *
     * @throws Exception if an unexpected exception occurs
     */
    @Test
    public void testConstructorNoDirectory() throws Exception {
        String rootDir = createDirectory();
        File dataDir = new File(rootDir, "dsdb");
        if (!dataDir.mkdir()) {
            throw new RuntimeException("Failed to create sub-dir: " + dataDir);
        }
        Properties props = createProperties(
            StandardProperties.APP_NAME, "Foo",
            StandardProperties.APP_ROOT, rootDir);
        DataStore testStore = createDataStore(props);
        testStore.shutdown();
    }

    @Test
    public void testConstructorNoDirectoryNorRoot() throws Exception {
  Properties props = new Properties();
  try {
      createDataStore(props);
      fail("Expected IllegalArgumentException");
  } catch (IllegalArgumentException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testConstructorNonexistentDirectory() throws Exception {
        String directory = createDirectory();
        File dir = new File(directory);
        if (!dir.delete()) {
      throw new RuntimeException("Problem deleting directory: " + dir);
  }
  props.setProperty(
                DataStoreImplClassName + ".directory", directory);
        createDataStore(props);
        assertTrue(dir.exists());
    }

    @Test
    public void testConstructorDirectoryIsFile() throws Exception {
  String file = File.createTempFile("existing", "db").getPath();
  props.setProperty(
      DataStoreImplClassName + ".directory",
      file);
  try {
      createDataStore(props);
      fail("Expected DataStoreException");
  } catch (DataStoreException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testConstructorDirectoryNotWritable() throws Exception {
        String osName = System.getProperty("os.name", "unknown");
  /*
   * Can't seem to create a non-writable directory on Windows.
   * -tjb@sun.com (01/09/2007)
   */
        if (osName.startsWith("Windows")) {
            System.err.println("Skipping on " + osName);
            return;
        }
  String directory = createDirectory();
  props.setProperty(DataStoreImplClassName + ".directory", directory);
        File dir = new File(directory);
  dir.setReadOnly();
        if(dir.canWrite()) {
            System.err.println("Skipping with superuser");
            return;
        }
  try {
      createDataStore(props);
      fail("Expected DataStoreException");
  } catch (DataStoreException e) {
      System.err.println(e);
  }
    }

    /* -- Test createObject -- */

    @Test
    public void testCreateObjectNullTxn() {
  try {
      store.createObject(null);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    /* -- Unusual states -- */
    private final Action createObject = new Action() {
  void run() { store.createObject(txn); };
    };
    @Test
    public void testCreateObjectAborted() throws Exception {
  testAborted(createObject);
    }
    @Test
    public void testCreateObjectPreparedReadOnly() throws Exception {
  testPreparedReadOnly(createObject);
    }
    @Test
    public void testCreateObjectPreparedModified() throws Exception {
  testPreparedModified(createObject);
    }
    @Test
    public void testCreateObjectCommitted() throws Exception {
  testCommitted(createObject);
    }
    @Test
    public void testCreateObjectWrongTxn() throws Exception {
  testWrongTxn(createObject);
    }
    @Test
    public void testCreateObjectShuttingDownExistingTxn() throws Exception {
  testShuttingDownExistingTxn(createObject);
    }
    @Test
    public void testCreateObjectShuttingDownNewTxn() throws Exception {
  testShuttingDownNewTxn(createObject);
    }
    @Test
    public void testCreateObjectShutdown() throws Exception {
  testShutdown(createObject);
    }

    @Test
    public void testCreateObjectSuccess() throws Exception {
  assertTrue(id >= 0);
  assertTrue(
      txn.participants.contains(
    ((DataStoreProfileProducer) store).getDataStore()));
  long id2 = store.createObject(txn);
  assertTrue(id2 >= 0);
  assertTrue(id != id2);
  /*
   * Only setting the object causes the current transaction to contain
   * modifications!  -tjb@sun.com (10/18/2006)
   */
  assertTrue(txn.prepare());
    }

    @Test
    public void testCreateObjectMany() throws Exception {
  for (int i = 0; i < 10; i++) {
      if (i != 0) {
    txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
      }
      for (int j = 0; j < 200; j++) {
    store.createObject(txn);
      }
      txn.commit();
  }
  txn = null;
    }

    /* -- Test markForUpdate -- */

    @Test
    public void testMarkForUpdateNullTxn() {
  try {
      store.markForUpdate(null, 3);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testMarkForUpdateBadId() {
  try {
      store.markForUpdate(txn, -3);
      fail("Expected IllegalArgumentException");
  } catch (IllegalArgumentException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testMarkForUpdateNotFound() throws Exception {
  store.markForUpdate(txn, id);
    }

    /* -- Unusual states -- */
    private final Action markForUpdate = new Action() {
  void setUp() throws Exception {
      store.setObject(txn, id, new byte[] { 0 });
      long id2 = store.createObject(txn);
      store.setObject(txn, id2, new byte[] { 0 });
      txn.commit();
      txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
      store.getObject(txn, id2, false);
  }
  void run() { store.markForUpdate(txn, id); }
    };
    @Test
    public void testMarkForUpdateAborted() throws Exception {
  testAborted(markForUpdate);
    }
    @Test
    public void testMarkForUpdatePreparedReadOnly() throws Exception {
  testPreparedReadOnly(markForUpdate);
    }
    @Test
    public void testMarkForUpdatePreparedModified() throws Exception {
  testPreparedModified(markForUpdate);
    }
    @Test
    public void testMarkForUpdateCommitted() throws Exception {
  testCommitted(markForUpdate);
    }
    @Test
    public void testMarkForUpdateWrongTxn() throws Exception {
  testWrongTxn(markForUpdate);
    }
    @Test
    public void testMarkForUpdateShuttingDownExistingTxn() throws Exception {
  testShuttingDownExistingTxn(markForUpdate);
    }
    @Test
    public void testMarkForUpdateShuttingDownNewTxn() throws Exception {
  testShuttingDownNewTxn(markForUpdate);
    }
    @Test
    public void testMarkForUpdateShutdown() throws Exception {
  testShutdown(markForUpdate);
    }

    @Test
    public void testMarkForUpdateSuccess() throws Exception {
  store.setObject(txn, id, new byte[] { 0 });
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  store.markForUpdate(txn, id);
  store.markForUpdate(txn, id);
  assertTrue(
      txn.participants.contains(
    ((DataStoreProfileProducer) store).getDataStore()));
  /* Marking for update is not an update! */
  assertTrue(txn.prepare());
    }

    /* -- Test getObject -- */

    @Test
    public void testGetObjectNullTxn() {
  try {
      store.getObject(null, 3, false);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testGetObjectBadId() {
  try {
      store.getObject(txn, -3, false);
      fail("Expected IllegalArgumentException");
  } catch (IllegalArgumentException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testGetObjectNotFound() throws Exception {
  try {
      store.getObject(txn, id, false);
      fail("Expected ObjectNotFoundException");
  } catch (ObjectNotFoundException e) {
      System.err.println(e);
  }
    }

    /* -- Unusual states -- */
    private final Action getObject = new Action() {
  void setUp() throws Exception {
      store.setObject(txn, id, new byte[] { 0 });
      txn.commit();
      txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
      store.getObject(txn, id, false);
  }
  void run() { store.getObject(txn, id, false); };
    };
    @Test
    public void testGetObjectAborted() throws Exception {
  testAborted(getObject);
    }
    @Test
    public void testGetObjectPreparedReadOnly() throws Exception {
  testPreparedReadOnly(getObject);
    }
    @Test
    public void testGetObjectPreparedModified() throws Exception {
  testPreparedModified(getObject);
    }
    @Test
    public void testGetObjectCommitted() throws Exception {
  testCommitted(getObject);
    }
    @Test
    public void testGetObjectWrongTxn() throws Exception {
  testWrongTxn(getObject);
    }
    @Test
    public void testGetObjectShuttingDownExistingTxn() throws Exception {
  testShuttingDownExistingTxn(getObject);
    }
    @Test
    public void testGetObjectShuttingDownNewTxn() throws Exception {
  testShuttingDownNewTxn(getObject);
    }
    @Test
    public void testGetObjectShutdown() throws Exception {
  testShutdown(getObject);
    }

    @Test
    public void testGetObjectSuccess() throws Exception {
  byte[] data = { 1, 2 };
  store.setObject(txn, id, data);
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  byte[] result =  store.getObject(txn, id, false);
  assertTrue(
      txn.participants.contains(
    ((DataStoreProfileProducer) store).getDataStore()));
  assertTrue(Arrays.equals(data, result));
  store.getObject(txn, id, false);
  /* Getting for update is not an update! */
  assertTrue(txn.prepare());
    }

    /**
     * Test that we can store all values for the first data byte, since we're
     * now using that byte to mark placeholders.
     */
    @Test
    public void testGetObjectFirstByte() throws Exception {
  byte[] value = new byte[0];
  store.setObject(txn, id, value);
  assertSameBytes(value, store.getObject(txn, id, false));
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  assertSameBytes(value, store.getObject(txn, id, false));
  long id2 = store.createObject(txn);
  for (int i = 0; i < 256; i++) {
      value = new byte[] { (byte) i };
      byte[] value2 = new byte[] { (byte) i, (byte) 33 };
      store.setObject(txn, id, value);
      store.setObject(txn, id2, value2);
      assertSameBytes(value, store.getObject(txn, id, false));
      assertSameBytes(value2, store.getObject(txn, id2, false));
      txn.commit();
      txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
      assertSameBytes(value, store.getObject(txn, id, false));
      assertSameBytes(value2, store.getObject(txn, id2, false));
  }
    }

    /* -- Test setObject -- */

    @Test
    public void testSetObjectNullTxn() {
  byte[] data = { 0 };
  try {
      store.setObject(null, 3, data);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testSetObjectBadId() {
  byte[] data = { 0 };
  try {
      store.setObject(txn, -3, data);
      fail("Expected IllegalArgumentException");
  } catch (IllegalArgumentException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testSetObjectNullData() {
  try {
      store.setObject(txn, id, null);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testSetObjectEmptyData() throws Exception {
  byte[] data = { };
  store.setObject(txn, id, data);
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  byte[] result = store.getObject(txn, id, false);
  assertTrue(result.length == 0);
    }

    /* -- Unusual states -- */
    private final Action setObject = new Action() {
  void run() { store.setObject(txn, id, new byte[] { 0 }); }
    };
    @Test
    public void testSetObjectAborted() throws Exception {
  testAborted(setObject);
    }
    @Test
    public void testSetObjectPreparedReadOnly() throws Exception {
  testPreparedReadOnly(setObject);
    }
    @Test
    public void testSetObjectPreparedModified() throws Exception {
  testPreparedModified(setObject);
    }
    @Test
    public void testSetObjectCommitted() throws Exception {
  testCommitted(setObject);
    }
    @Test
    public void testSetObjectWrongTxn() throws Exception {
  testWrongTxn(setObject);
    }
    @Test
    public void testSetObjectShuttingDownExistingTxn() throws Exception {
  testShuttingDownExistingTxn(setObject);
    }
    @Test
    public void testSetObjectShuttingDownNewTxn() throws Exception {
  testShuttingDownNewTxn(setObject);
    }
    @Test
    public void testSetObjectShutdown() throws Exception {
  testShutdown(setObject);
    }

    @Test
    public void testSetObjectSuccess() throws Exception {
  byte[] data = { 1, 2 };
  store.setObject(txn, id, data);
  assertFalse(txn.prepare());
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  byte[] result = store.getObject(txn, id, false);
  assertTrue(Arrays.equals(data, result));
  byte[] newData = new byte[] { 3 };
  store.setObject(txn, id, newData);
  assertTrue(Arrays.equals(newData, store.getObject(txn, id, true)));
  txn.abort(new RuntimeException("abort"));
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  assertTrue(Arrays.equals(data, store.getObject(txn, id, true)));
  store.setObject(txn, id, newData);
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  assertTrue(Arrays.equals(newData, store.getObject(txn, id, true)));
    }

    /* -- Test setObjects -- */

    @Test
    public void testSetObjectsNullTxn() {
  long[] ids = { id };
  byte[][] dataArray = { { 0 } };
  try {
      store.setObjects(null, ids, dataArray);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testSetObjectsBadId() {
  long[] ids = { -3 };
  byte[][] dataArray = { { 0 } };
  try {
      store.setObjects(txn, ids, dataArray);
      fail("Expected IllegalArgumentException");
  } catch (IllegalArgumentException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testSetObjectsWrongLengths() {
  long[] ids = { id };
  byte[][] dataArray = { { 0 }, { 1 } };
  try {
      store.setObjects(txn, ids, dataArray);
      fail("Expected IllegalArgumentException");
  } catch (IllegalArgumentException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testSetObjectsNullOids() {
  byte[][] dataArray = { { 0 } };
  try {
      store.setObjects(txn, null, dataArray);
      fail("Expected NullArgumentException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testSetObjectsNullDataArray() {
  long[] ids = { id };
  try {
      store.setObjects(txn, ids, null);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testSetObjectsNullData() {
  long[] ids = { id };
  byte[][] dataArray = { null };
  try {
      store.setObjects(txn, ids, dataArray);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testSetObjectsEmptyData() throws Exception {
  long[] ids = { id };
  byte[][] dataArray = { { } };
  store.setObjects(txn, ids, dataArray);
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  byte[] result = store.getObject(txn, id, false);
  assertTrue(result.length == 0);
    }

    /* -- Unusual states -- */
    private final Action setObjects = new Action() {
  void run() {
      store.setObjects(txn, new long[] { id }, new byte[][] { { 0 } });
  }
    };
    @Test
    public void testSetObjectsAborted() throws Exception {
  testAborted(setObjects);
    }
    @Test
    public void testSetObjectsPreparedReadOnly() throws Exception {
  testPreparedReadOnly(setObjects);
    }
    @Test
    public void testSetObjectsPreparedModified() throws Exception {
  testPreparedModified(setObjects);
    }
    @Test
    public void testSetObjectsCommitted() throws Exception {
  testCommitted(setObjects);
    }
    @Test
    public void testSetObjectsWrongTxn() throws Exception {
  testWrongTxn(setObjects);
    }
    @Test
    public void testSetObjectsShuttingDownExistingTxn() throws Exception {
  testShuttingDownExistingTxn(setObjects);
    }
    @Test
    public void testSetObjectsShuttingDownNewTxn() throws Exception {
  testShuttingDownNewTxn(setObjects);
    }
    @Test
    public void testSetObjectsShutdown() throws Exception {
  testShutdown(setObjects);
    }

    @Test
    public void testSetObjectsSuccess() throws Exception {
  long[] ids = { id, store.createObject(txn) };
  byte[][] dataArray = { { 1, 2 }, { 3, 4, 5 } };
  store.setObjects(txn, ids, dataArray);
  assertFalse(txn.prepare());
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  for (int i = 0; i < ids.length; i++) {
      long id = ids[i];
      byte[] data = dataArray[i];
      byte[] result = store.getObject(txn, id, false);
      assertTrue(Arrays.equals(data, result));
      byte[] newData = new byte[] { (byte) i };
      store.setObjects(txn, new long[] { id }, new byte[][] { newData });
      assertTrue(Arrays.equals(newData, store.getObject(txn, id, true)));
      txn.abort(new RuntimeException("abort"));
      txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
      assertTrue(Arrays.equals(data, store.getObject(txn, id, true)));
      store.setObjects(txn, new long[] { id }, new byte[][] { newData });
      txn.commit();
      txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
      assertTrue(Arrays.equals(newData, store.getObject(txn, id, true)));
  }
    }

    /* -- Test removeObject -- */

    @Test
    public void testRemoveObjectNullTxn() {
  try {
      store.removeObject(null, 3);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testRemoveObjectBadId() {
  try {
      store.removeObject(txn, -3);
      fail("Expected IllegalArgumentException");
  } catch (IllegalArgumentException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testRemoveObjectNotFound() {
  try {
      store.removeObject(txn, id);
      fail("Expected ObjectNotFoundException");
  } catch (ObjectNotFoundException e) {
      System.err.println(e);
  }
    }

    /* -- Unusual states -- */
    private final Action removeObject = new Action() {
  void setUp() throws Exception {
      store.setObject(txn, id, new byte[] { 0 });
      long id2 = store.createObject(txn);
      store.setObject(txn, id2, new byte[] { 0 });
      txn.commit();
      txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
      store.getObject(txn, id2, false);
  }
  void run() { store.removeObject(txn, id); }
    };
    @Test
    public void testRemoveObjectAborted() throws Exception {
  testAborted(removeObject);
    }
    @Test
    public void testRemoveObjectPreparedReadOnly() throws Exception {
  testPreparedReadOnly(removeObject);
    }
    @Test
    public void testRemoveObjectPreparedModified() throws Exception {
  testPreparedModified(removeObject);
    }
    @Test
    public void testRemoveObjectCommitted() throws Exception {
  testCommitted(removeObject);
    }
    @Test
    public void testRemoveObjectWrongTxn() throws Exception {
  testWrongTxn(removeObject);
    }
    @Test
    public void testRemoveObjectShuttingDownExistingTxn() throws Exception {
  testShuttingDownExistingTxn(removeObject);
    }
    @Test
    public void testRemoveObjectShuttingDownNewTxn() throws Exception {
  testShuttingDownNewTxn(removeObject);
    }
    @Test
    public void testRemoveObjectShutdown() throws Exception {
  testShutdown(removeObject);
    }

    @Test
    public void testRemoveObjectSuccess() throws Exception {
  store.setObject(txn, id, new byte[] { 0 });
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  store.removeObject(txn, id);
  assertFalse(txn.prepare());
  txn.abort(new RuntimeException("abort"));
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  store.removeObject(txn, id);
  try {
      store.getObject(txn, id, false);
      fail("Expected ObjectNotFoundException");
  } catch (ObjectNotFoundException e) {
  }
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  try {
      store.getObject(txn, id, false);
      fail("Expected ObjectNotFoundException");
  } catch (ObjectNotFoundException e) {
  }
    }

    /* -- Test getBinding -- */

    @Test
    public void testGetBindingNullTxn() {
  try {
      store.getBinding(null, "foo");
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testGetBindingNullName() {
  try {
      store.getBinding(txn, null);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testGetBindingEmptyName() throws Exception {
  store.setObject(txn, id, new byte[] { 0 });
  store.setBinding(txn, "", id);
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  long result = store.getBinding(txn, "");
  assertEquals(id, result);
    }

    @Test
    public void testGetBindingNotFound() throws Exception {
  try {
      store.getBinding(txn, "unknown");
      fail("Expected NameNotBoundException");
  } catch (NameNotBoundException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testGetBindingObjectNotFound() throws Exception {
  store.setBinding(txn, "foo", id);
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  long result = store.getBinding(txn, "foo");
  assertEquals(id, result);
    }

    /* -- Unusual states -- */
    private final Action getBinding = new Action() {
  void setUp() throws Exception {
      store.setBinding(txn, "foo", id);
      txn.commit();
      txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
      store.getBinding(txn, "foo");
  }
  void run() { store.getBinding(txn, "foo"); }
    };
    @Test
    public void testGetBindingAborted() throws Exception {
  testAborted(getBinding);
    }
    @Test
    public void testGetBindingPreparedReadOnly() throws Exception {
  testPreparedReadOnly(getBinding);
    }
    @Test
    public void testGetBindingPreparedModified() throws Exception {
  testPreparedModified(getBinding);
    }
    @Test
    public void testGetBindingCommitted() throws Exception {
  testCommitted(getBinding);
    }
    @Test
    public void testGetBindingWrongTxn() throws Exception {
  testWrongTxn(getBinding);
    }
    @Test
    public void testGetBindingShuttingDownExistingTxn() throws Exception {
  testShuttingDownExistingTxn(getBinding);
    }
    @Test
    public void testGetBindingShuttingDownNewTxn() throws Exception {
  testShuttingDownNewTxn(getBinding);
    }
    @Test
    public void testGetBindingShutdown() throws Exception {
  testShutdown(getBinding);
    }

    @Test
    public void testGetBindingSuccess() throws Exception {
  store.setObject(txn, id, new byte[] { 0 });
  store.setBinding(txn, "foo", id);
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  long result = store.getBinding(txn, "foo");
  assertEquals(id, result);
  assertTrue(txn.prepare());
    }

    /* -- Test setBinding -- */

    @Test
    public void testSetBindingNullTxn() {
  try {
      store.setBinding(null, "foo", 3);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testSetBindingNullName() {
  try {
      store.setBinding(txn, null, id);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    /* -- Unusual states -- */
    private final Action setBinding = new Action() {
  void run() { store.setBinding(txn, "foo", id); }
    };
    @Test
    public void testSetBindingAborted() throws Exception {
  testAborted(setBinding);
    }
    @Test
    public void testSetBindingPreparedReadOnly() throws Exception {
  testPreparedReadOnly(setBinding);
    }
    @Test
    public void testSetBindingPreparedModified() throws Exception {
  testPreparedModified(setBinding);
    }
    @Test
    public void testSetBindingCommitted() throws Exception {
  testCommitted(setBinding);
    }
    @Test
    public void testSetBindingWrongTxn() throws Exception {
  testWrongTxn(setBinding);
    }
    @Test
    public void testSetBindingShuttingDownExistingTxn() throws Exception {
  testShuttingDownExistingTxn(setBinding);
    }
    @Test
    public void testSetBindingShuttingDownNewTxn() throws Exception {
  testShuttingDownNewTxn(setBinding);
    }
    @Test
    public void testSetBindingShutdown() throws Exception {
  testShutdown(setBinding);
    }

    @Test
    public void testSetBindingSuccess() throws Exception {
  long newId = store.createObject(txn);
  store.setObject(txn, id, new byte[] { 0 });
  store.setBinding(txn, "foo", id);
  assertFalse(txn.prepare());
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  assertEquals(id, store.getBinding(txn, "foo"));
  store.setBinding(txn, "foo", newId);
  assertEquals(newId, store.getBinding(txn, "foo"));
  txn.abort(new RuntimeException("abort"));
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  assertEquals(id, store.getBinding(txn, "foo"));
    }

    /* -- Test removeBinding -- */

    @Test
    public void testRemoveBindingNullTxn() {
  try {
      store.removeBinding(null, "foo");
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testRemoveBindingNullName() {
  try {
      store.removeBinding(txn, null);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testRemoveBindingNotFound() {
  try {
      store.removeBinding(txn, "unknown");
      fail("Expected NameNotBoundException");
  } catch (NameNotBoundException e) {
      System.err.println(e);
  }
    }

    /* -- Unusual states -- */
    private final Action removeBinding = new Action() {
  void setUp() throws Exception {
      store.setBinding(txn, "foo", id);
      store.setObject(txn, id, new byte[] { 0 });
      txn.commit();
      txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
      store.getObject(txn, id, false);
  }
  void run() { store.removeBinding(txn, "foo"); }
    };
    @Test
    public void testRemoveBindingAborted() throws Exception {
  testAborted(removeBinding);
    }
    @Test
    public void testRemoveBindingPreparedReadOnly() throws Exception {
  testPreparedReadOnly(removeBinding);
    }
    @Test
    public void testRemoveBindingPreparedModified() throws Exception {
  testPreparedModified(removeBinding);
    }
    @Test
    public void testRemoveBindingCommitted() throws Exception {
  testCommitted(removeBinding);
    }
    @Test
    public void testRemoveBindingWrongTxn() throws Exception {
  testWrongTxn(removeBinding);
    }
    @Test
    public void testRemoveBindingShuttingDownExistingTxn() throws Exception {
  testShuttingDownExistingTxn(removeBinding);
    }
    @Test
    public void testRemoveBindingShuttingDownNewTxn() throws Exception {
  testShuttingDownNewTxn(removeBinding);
    }
    @Test
    public void testRemoveBindingShutdown() throws Exception {
  testShutdown(removeBinding);
    }

    @Test
    public void testRemoveBindingSuccess() throws Exception {
  store.setObject(txn, id, new byte[] { 0 });
  store.setBinding(txn, "foo", id);
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  store.removeBinding(txn, "foo");
  assertFalse(txn.prepare());
  txn.abort(new RuntimeException("abort"));
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  assertEquals(id, store.getBinding(txn, "foo"));
  store.removeBinding(txn, "foo");
  try {
      store.getBinding(txn, "foo");
      fail("Expected NameNotBoundException");
  } catch (NameNotBoundException e) {
      System.err.println(e);
  }
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  try {
      store.getBinding(txn, "foo");
      fail("Expected NameNotBoundException");
  } catch (NameNotBoundException e) {
      System.err.println(e);
  }
    }

    /* -- Test nextBoundName -- */

    @Test
    public void testNextBoundNameNullTxn() {
  try {
      store.nextBoundName(null, "foo");
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testNextBoundNameEmpty() {
  assertEquals(null, store.nextBoundName(txn, ""));
  store.setBinding(txn, "", id);
  assertEquals("", store.nextBoundName(txn, null));
  assertEquals(null, store.nextBoundName(txn, ""));
    }

    /* -- Unusual states -- */
    private final Action nextBoundName = new Action() {
  void run() { store.nextBoundName(txn, null); }
    };
    @Test
    public void testNextBoundNameAborted() throws Exception {
  testAborted(nextBoundName);
    }
    @Test
    public void testNextBoundNamePreparedReadOnly() throws Exception {
  testPreparedReadOnly(nextBoundName);
    }
    @Test
    public void testNextBoundNamePreparedModified() throws Exception {
  testPreparedModified(nextBoundName);
    }
    @Test
    public void testNextBoundNameCommitted() throws Exception {
  testCommitted(nextBoundName);
    }
    @Test
    public void testNextBoundNameWrongTxn() throws Exception {
  testWrongTxn(nextBoundName);
    }
    @Test
    public void testNextBoundNameShuttingDownExistingTxn() throws Exception {
  testShuttingDownExistingTxn(nextBoundName);
    }
    @Test
    public void testNextBoundNameShuttingDownNewTxn() throws Exception {
  testShuttingDownNewTxn(nextBoundName);
    }
    @Test
    public void testNextBoundNameShutdown() throws Exception {
  testShutdown(nextBoundName);
    }

    @Test
    public void testNextBoundNameSuccess() throws Exception {
  for (String name = null;
       (name = store.nextBoundName(txn, name)) != null; )
  {
      store.removeBinding(txn, name);
  }
  assertNull(store.nextBoundName(txn, null));
  assertNull(store.nextBoundName(txn, "name-1"));
  assertNull(store.nextBoundName(txn, ""));
  store.setBinding(txn, "name-1", id);
  assertEquals("name-1", store.nextBoundName(txn, null));
  assertEquals(null, store.nextBoundName(txn, "name-1"));
  assertEquals(null, store.nextBoundName(txn, "name-2"));
  assertEquals(null, store.nextBoundName(txn, "name-1"));
  assertEquals("name-1", store.nextBoundName(txn, "name-0"));
  assertEquals("name-1", store.nextBoundName(txn, null));
  assertEquals("name-1", store.nextBoundName(txn, "name-0"));
  store.setBinding(txn, "name-2", id);
  txn.commit();
  txn = createTransaction();
  assertEquals("name-1", store.nextBoundName(txn, null));
  assertEquals("name-2", store.nextBoundName(txn, "name-1"));
  assertNull(store.nextBoundName(txn, "name-2"));
  assertNull(store.nextBoundName(txn, "name-3"));
  assertEquals("name-1", store.nextBoundName(txn, "name-0"));
  assertEquals("name-2", store.nextBoundName(txn, "name-1"));
  assertEquals("name-1", store.nextBoundName(txn, null));
  store.removeBinding(txn, "name-1");
  assertEquals("name-2", store.nextBoundName(txn, null));
  assertEquals("name-2", store.nextBoundName(txn, "name-1"));
  assertEquals(null, store.nextBoundName(txn, "name-2"));
  assertEquals(null, store.nextBoundName(txn, "name-3"));
  assertEquals("name-2", store.nextBoundName(txn, "name-0"));
  store.removeBinding(txn, "name-2");
  assertNull(store.nextBoundName(txn, "name-2"));
  assertNull(store.nextBoundName(txn, null));
  store.setBinding(txn, "name-1", id);
  store.setBinding(txn, "name-2", id);
  txn.commit();
  txn = createTransaction();
  assertEquals("name-1", store.nextBoundName(txn, null));
  assertEquals("name-1", store.nextBoundName(txn, null));
  store.removeBinding(txn, "name-1");
  assertEquals("name-2", store.nextBoundName(txn, null));
  store.removeBinding(txn, "name-2");
  assertNull(store.nextBoundName(txn, null));
  txn.abort(new RuntimeException("abort"));
  txn = createTransaction();
  assertEquals("name-1", store.nextBoundName(txn, null));
    }

    @Test
    public void testNextBoundNameTimeout() throws Exception {
  final long id2 = store.createObject(txn);
  for (int i = 100; i < 300; i++) {
      store.setBinding(txn, "name-" + i, id);
  }
  txn.commit();
  final Semaphore flag = new Semaphore(1);
  final Semaphore flag2 = new Semaphore(1);
  flag.acquire();
  flag2.acquire();
  class MyRunnable implements Runnable {
      Exception exception2;
      public void run() {
    DummyTransaction txn2 = null;
    try {
        txn2 = createTransaction(
      UsePrepareAndCommit.ARBITRARY, 20000);
        /* Get write lock on name-299 and notify txn */
        store.setBinding(txn2, "name-299", id2);
        flag.release();
        /* Wait for txn, then commit */
        flag2.tryAcquire(10000, TimeUnit.MILLISECONDS);
        txn2.commit();
    } catch (TransactionAbortedException e) {
        System.err.println("txn2: " + e);
        exception2 = e;
    } catch (Exception e) {
        System.err.println("txn2: " + e);
        if (txn2 != null) {
      txn2.abort(new RuntimeException("abort txn2"));
        }
    }
      }
  }
  MyRunnable runnable = new MyRunnable();
  Thread thread = new Thread(runnable, "testNextBoundNameTimeout");
  /* Start txn2 and wait for it to write lock name-299 */
  thread.start();
  flag.acquire();
  String name = "name-100";
  txn = createTransaction();
  try {
      /* Walk names, expecting to timeout on name-299 */
      while (name != null) {
    name = store.nextBoundName(txn, name);
    store.removeBinding(txn, name);
      }
      fail("Expected TransactionAbortedException");
  } catch (TransactionAbortedException e) {
      System.err.println("txn: " + e);
      txn = null;
  } catch (Exception e) {
      e.printStackTrace();
      fail("Unexpected exception: " + e);
  } finally {
      flag2.release();
      thread.join(4000);
      assertFalse("Thread should not be alive", thread.isAlive());
  }
    }

    /* -- Test abort -- */

    @Test
    public void testAbortNullTxn() throws Exception {
  store.createObject(txn);
  TransactionParticipant participant =
      txn.participants.iterator().next();
  try {
      participant.abort(null);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    /* -- Unusual states -- */
    private final Action abort = new Action() {
  TransactionParticipant participant;
  void setUp() { participant = txn.participants.iterator().next(); }
  void run() {
      participant.abort(txn);
      txn = null;
  }
    };
    @Test
    public void testAbortAborted() throws Exception {
  testAborted(abort, IllegalStateException.class);
    }
    @Test
    public void testAbortPreparedReadOnly() throws Exception {
  testPreparedReadOnly(abort);
    }
    @Test
    public void testAbortPreparedModified() throws Exception {
  abort.setUp();
  store.setObject(txn, id, new byte[] { 0 });
  txn.prepare();
  /* Aborting a prepared, modified transaction is OK. */
  abort.run();
    }
    @Test
    public void testAbortCommitted() throws Exception {
  testCommitted(abort);
    }
    @Test
    public void testAbortWrongTxn() throws Exception {
  testWrongTxn(abort);
    }
    @Test
    public void testAbortShuttingDownExistingTxn() throws Exception {
  testShuttingDownExistingTxn(abort);
    }
    @Test
    public void testAbortShuttingDownNewTxn() throws Exception {
  testShuttingDownNewTxn(abort);
    }
    @Test
    public void testAbortShutdown() throws Exception {
  testShutdown(abort);
    }

    /* -- Test prepare -- */

    @Test
    public void testPrepareNullTxn() throws Exception {
  store.createObject(txn);
  TransactionParticipant participant =
      txn.participants.iterator().next();
  try {
      participant.prepare(null);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testPrepareTimeout() throws Exception {
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.NO, 100);
  store.setObject(txn, id, new byte[] { 1 });
  Thread.sleep(200);
  try {
      txn.commit();
      fail("Expected TransactionTimeoutException");
  } catch (TransactionTimeoutException e) {
      txn = null;
      System.err.println(e);
  }
  /* Wait for transaction to end on the server. */
  Thread.sleep(1000);
    }

    /* -- Unusual states -- */
    private final Action prepare = new Action() {
  private TransactionParticipant participant;
  void setUp() { participant = txn.participants.iterator().next(); }
  void run() throws Exception {
      assertTrue(participant.prepare(txn));
      txn = null;
  }
    };
    @Test
    public void testPrepareAborted() throws Exception {
  testAborted(prepare, IllegalStateException.class);
    }
    @Test
    public void testPreparePreparedReadOnly() throws Exception {
  testPreparedReadOnly(prepare);
    }
    @Test
    public void testPreparePreparedModified() throws Exception {
  testPreparedModified(prepare);
    }
    @Test
    public void testPrepareCommitted() throws Exception {
  testCommitted(prepare);
    }
    @Test
    public void testPrepareWrongTxn() throws Exception {
  testWrongTxn(prepare);
    }
    @Test
    public void testPrepareShuttingDownExistingTxn() throws Exception {
  testShuttingDownExistingTxn(prepare);
    }
    @Test
    public void testPrepareShuttingDownNewTxn() throws Exception {
  testShuttingDownNewTxn(prepare);
    }
    @Test
    public void testPrepareShutdown() throws Exception {
  testShutdown(prepare);
    }

    /* -- Test prepareAndCommit -- */

    @Test
    public void testPrepareAndCommitNullTxn() throws Exception {
  store.createObject(txn);
  TransactionParticipant participant =
      txn.participants.iterator().next();
  try {
      participant.prepareAndCommit(null);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testPrepareAndCommitTimeout() throws Exception {
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.YES, 100);
  store.setObject(txn, id, new byte[] { 1 });
  Thread.sleep(200);
  try {
      txn.commit();
      fail("Expected TransactionTimeoutException");
  } catch (TransactionTimeoutException e) {
      txn = null;
      System.err.println(e);
  }
  /* Wait for transaction to end on the server. */
  Thread.sleep(1000);
    }

    /* -- Unusual states -- */
    private final Action prepareAndCommit = new Action() {
  private TransactionParticipant participant;
  void setUp() { participant = txn.participants.iterator().next(); }
  void run() throws Exception {
      participant.prepareAndCommit(txn);
      txn = null;
  }
    };
    @Test
    public void testPrepareAndCommitAborted() throws Exception {
  testAborted(prepareAndCommit, IllegalStateException.class);
    }
    @Test
    public void testPrepareAndCommitPrepareAndCommitReadOnly()
  throws Exception
    {
  testPreparedReadOnly(prepareAndCommit);
    }
    @Test
    public void testPrepareAndCommitPrepareAndCommitModified()
  throws Exception
    {
  testPreparedModified(prepareAndCommit);
    }
    @Test
    public void testPrepareAndCommitCommitted() throws Exception {
  testCommitted(prepareAndCommit);
    }
    @Test
    public void testPrepareAndCommitWrongTxn() throws Exception {
  testWrongTxn(prepareAndCommit);
    }
    @Test
    public void testPrepareAndCommitShuttingDownExistingTxn()
  throws Exception
    {
  testShuttingDownExistingTxn(prepareAndCommit);
    }
    @Test
    public void testPrepareAndCommitShuttingDownNewTxn() throws Exception {
  testShuttingDownNewTxn(prepareAndCommit);
    }
    @Test
    public void testPrepareAndCommitShutdown() throws Exception {
  testShutdown(prepareAndCommit);
    }

    /* -- Test commit -- */

    @Test
    public void testCommitNullTxn() throws Exception {
  store.createObject(txn);
  TransactionParticipant participant =
      txn.participants.iterator().next();
  try {
      participant.commit(null);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    /** Make sure that commits don't timeout. */
    @Test
    public void testCommitNoTimeout() throws Exception {
  txn.commit();
  txn = new DummyTransaction(UsePrepareAndCommit.NO, 1000) {
      public void join(final TransactionParticipant participant) {
    super.join(new TransactionParticipant() {
        public boolean prepare(Transaction txn) throws Exception {
      return participant.prepare(txn);
        }
        public void commit(Transaction txn) {
      try {
          Thread.sleep(2000);
          participant.commit(txn);
      } catch (Exception e) {
          e.printStackTrace();
          fail("Unexpected exception: " + e);
      }
        }
        public void prepareAndCommit(Transaction txn)
      throws Exception
        {
      participant.prepareAndCommit(txn);
        }
        public void abort(Transaction txn) {
      participant.abort(txn);
        }
                    public String getTypeName() {
                        return "DataStoreDummyParticipant";
                    }
    });
      }
  };
  initTransaction(txn);
  store.setBinding(txn, "foo", id);
  try {
      txn.commit();
  } finally {
      txn = null;
  }
    }

    /* -- Unusual states -- */
    class CommitAction extends Action {
  private TransactionParticipant participant;
  void setUp() { participant = txn.participants.iterator().next(); }
  void run() throws Exception {
      participant.commit(txn);
  }
    }
    private final CommitAction commit = new CommitAction();
    @Test
    public void testCommitAborted() throws Exception {
  testAborted(commit, IllegalStateException.class);
    }
    @Test
    public void testCommitPreparedReadOnly() throws Exception {
  testPreparedReadOnly(commit);
    }
    @Test
    public void testCommitPreparedModified() throws Exception {
  commit.setUp();
  store.setObject(txn, id, new byte[] { 0 });
  assertFalse(txn.prepare());
  /* Committing a prepared, modified transaction is OK. */
  commit.run();
    }
    @Test
    public void testCommitCommitted() throws Exception {
  testCommitted(commit);
    }
    @Test
    public void testCommitWrongTxn() throws Exception {
  testWrongTxn(commit);
    }
    @Test
    public void testCommitShuttingDownExistingTxn() throws Exception {
  commit.setUp();
  store.setObject(txn, id, new byte[] { 0 });
  assertFalse(txn.prepare());
  /* Committing a prepared, modified transaction is OK. */
  testShuttingDownExistingTxn(commit);
    }
    @Test
    public void testCommitShuttingDownNewTxn() throws Exception {
  testShuttingDownNewTxn(commit);
    }
    @Test
    public void testCommitShutdown() throws Exception {
  testShutdown(commit);
    }

    /* -- Test shutdown -- */

    @Test
    public void testShutdownAgain() throws Exception {
  txn.abort(new RuntimeException("abort"));
  txn = null;
  store.shutdown();
  ShutdownAction action = new ShutdownAction();
  try {
      assertTrue(action.waitForDone());
  } finally {
      store = null;
  }
    }

    @Test
    public void testShutdownInterrupt() throws Exception {
  ShutdownAction action = new ShutdownAction();
  action.assertBlocked();
  action.interrupt();
        assertFalse(action.waitForDone());
  store.setBinding(txn, "foo", id);
  txn.commit();
  txn = null;
  /* Complete the shutdown */
  new ShutdownAction().waitForDone();
  store = null;
    }

    @Test
    public void testConcurrentShutdownInterrupt() throws Exception {
  ShutdownAction action1 = new ShutdownAction();
  action1.assertBlocked();
  ShutdownAction action2 = new ShutdownAction();
  action2.assertBlocked();
  action1.interrupt();
        action1.assertBlocked(); // should not be interrupted
  action2.assertBlocked();
  txn.abort(new RuntimeException("abort"));
        assertTrue(action1.waitForDone());
  assertTrue(action2.waitForDone());
  txn = null;
  store = null;
    }

    @Test
    public void testConcurrentShutdownRace() throws Exception {
  ShutdownAction action1 = new ShutdownAction();
  action1.assertBlocked();
  ShutdownAction action2 = new ShutdownAction();
  action2.assertBlocked();
  txn.abort(new RuntimeException("abort"));
  assertTrue(action1.waitForDone());
  assertTrue(action2.waitForDone());
  txn = null;
  store = null;
    }

    @Test
    public void testShutdownRestart() throws Exception {
  store.setBinding(txn, "foo", id);
  byte[] bytes = { 1 };
  store.setObject(txn, id, bytes);
  txn.commit();
  store.shutdown();
  store = createDataStore();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  id = store.getBinding(txn, "foo");
  byte[] value = store.getObject(txn, id, false);
  assertTrue(Arrays.equals(bytes, value));
    }

    /* -- Test getClassId -- */

    @Test
    public void testGetClassIdNullArgs() {
  byte[] bytes = { 0, 1, 2, 3, 4 };
  try {
      store.getClassId(null, bytes);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
  try {
      store.getClassId(txn, null);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testGetClassIdNonStandardBytes() throws Exception {
  testGetClassIdBytes();
  testGetClassIdBytes((byte) 1);
  testGetClassIdBytes((byte) 2);
  testGetClassIdBytes((byte) 3, (byte) 4);
  testGetClassIdBytes((byte) 5, (byte) 6, (byte) 0xff);
  testGetClassIdBytes((byte) 0xac, (byte) 0xed, (byte) 0, (byte) 5);
  testGetClassIdBytes((byte) 0xac, (byte) 0xed, (byte) 0, (byte) 4);
    }
 
    private void testGetClassIdBytes(byte... bytes) throws Exception {
  int id = store.getClassId(txn, bytes);
  assertTrue(id > 0);
  byte[] storedBytes = store.getClassInfo(txn, id);
  assertEquals(Arrays.toString(bytes), Arrays.toString(storedBytes));
    }

    /* -- Unusual states: getClassId -- */
    private final Action getClassId = new Action() {
  void run() { store.getClassId(txn, new byte[0]); };
    };
    @Test
    public void testGetClassIdAborted() throws Exception {
  testAborted(getClassId);
    }
    @Test
    public void testGetClassIdPreparedReadOnly() throws Exception {
  testPreparedReadOnly(getClassId);
    }
    @Test
    public void testGetClassIdPreparedModified() throws Exception {
  testPreparedModified(getClassId);
    }
    @Test
    public void testGetClassIdCommitted() throws Exception {
  testCommitted(getClassId);
    }
    @Test
    public void testGetClassIdWrongTxn() throws Exception {
  testWrongTxn(getClassId);
    }
    @Test
    public void testGetClassIdShuttingDownExistingTxn() throws Exception {
  testShuttingDownExistingTxn(getClassId);
    }
    @Test
    public void testGetClassIdShuttingDownNewTxn() throws Exception {
  testShuttingDownNewTxn(getClassId);
    }
    @Test
    public void testGetClassIdShutdown() throws Exception {
  testShutdown(getClassId);
    }

    /* -- Test getClassInfo -- */

    @Test
    public void testGetClassInfoBadArgs() throws Exception {
  try {
      store.getClassInfo(null, 1);
      fail("Expected NullPointerException");
  } catch (NullPointerException e) {
      System.err.println(e);
  }
  try {
      store.getClassInfo(txn, 0);
      fail("Expected IllegalArgumentException");
  } catch (IllegalArgumentException e) {
      System.err.println(e);
  }
  try {
      store.getClassInfo(txn, -1);
      fail("Expected IllegalArgumentException");
  } catch (IllegalArgumentException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testGetClassInfoNotFound() {
  try {
      store.getClassInfo(txn, 56789);
      fail("Expected ClassInfoNotFoundException");
  } catch (ClassInfoNotFoundException e) {
      System.err.println(e);
  }
    }

    @Test
    public void testGetClassInfoAfterRestart() throws Exception {
  byte[] bytes = { 1, 2, 3, 4, 5, 6 };
  int id = store.getClassId(txn, bytes);
  txn.commit();
  store.shutdown();
  store = createDataStore();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  assertEquals(Arrays.toString(bytes),
         Arrays.toString(store.getClassInfo(txn, id)));
    }

    /* -- Unusual states: getClassInfo -- */
    private final Action getClassInfo = new Action() {
  void run() throws Exception { store.getClassInfo(txn, 1); };
    };
    @Test
    public void testGetClassInfoAborted() throws Exception {
  testAborted(getClassInfo);
    }
    @Test
    public void testGetClassInfoPreparedReadOnly() throws Exception {
  testPreparedReadOnly(getClassInfo);
    }
    @Test
    public void testGetClassInfoPreparedModified() throws Exception {
  testPreparedModified(getClassInfo);
    }
    @Test
    public void testGetClassInfoCommitted() throws Exception {
  testCommitted(getClassInfo);
    }
    @Test
    public void testGetClassInfoWrongTxn() throws Exception {
  testWrongTxn(getClassInfo);
    }
    @Test
    public void testGetClassInfoShuttingDownExistingTxn() throws Exception {
  testShuttingDownExistingTxn(getClassInfo);
    }
    @Test
    public void testGetClassInfoShuttingDownNewTxn() throws Exception {
  testShuttingDownNewTxn(getClassInfo);
    }
    @Test
    public void testGetClassInfoShutdown() throws Exception {
  testShutdown(getClassInfo);
    }

    /* -- Test nextObjectId -- */

    @Test
    public void testNextObjectIdIllegalIds() {
  long id = Long.MIN_VALUE;
  try {
      store.nextObjectId(txn, id);
      fail("Expected IllegalArgumentException");
  } catch (IllegalArgumentException e) {
      System.err.println(id);
  }
  id = -2;
  try {
      store.nextObjectId(txn, id);
      fail("Expected IllegalArgumentException");
  } catch (IllegalArgumentException e) {
      System.err.println(id);
  }
    }

    @Test
    public void testNextObjectIdBoundaryIds() {
  long first = store.nextObjectId(txn, -1);
  assertEquals(first, store.nextObjectId(txn, -1));
  assertEquals(first, store.nextObjectId(txn, 0));
  long last = -1;
  while (true) {
      long id = store.nextObjectId(txn, last);
      if (id == -1) {
    break;
      }
      last = id;
  }
  assertEquals(-1, store.nextObjectId(txn, last));
  assertEquals(-1, store.nextObjectId(txn, Long.MAX_VALUE));
    }

    @Test
    public void testNextObjectIdRemoved() throws Exception {
  long x = -1;
  while (true) {
      x = store.nextObjectId(txn, x);
      if (x == -1) {
    break;
      }
      assertFalse("Shouldn't find ID that has been created but not set",
      x == id);
  }
  store.setObject(txn, id, new byte[] { 1, 2, 3 });
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  long id2 = store.createObject(txn);
  store.setObject(txn, id2, new byte[] { 4, 5, 6, 7 });
  if (id > id2) {
      long tmp = id;
      id = id2;
      id2 = tmp;
  }
  x = id;
  while (true) {
      x = store.nextObjectId(txn, x);
      assertFalse("Didn't find id2 after id", x == -1);
      if (x == id2) {
    break;
      }
  }
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  store.removeObject(txn, id);
  x = -1;
  while (true) {
      x = store.nextObjectId(txn, x);
      if (x == -1) {
    break;
      }
      assertFalse("Shouldn't find ID removed in this txn", x == id);
  }
  x = id;
  while (true) {
      x = store.nextObjectId(txn, x);
      assertFalse("Didn't find id2 after removed id", x == -1);
      if (x == id2) {
    break;
      }
  }
  txn.commit();
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  x = -1;
  while (true) {
      x = store.nextObjectId(txn, x);
      if (x == -1) {
    break;
      }
      assertFalse("Shouldn't find ID removed in last txn", x == id);
  }
  x = id;
  while (true) {
      x = store.nextObjectId(txn, x);
      assertFalse("Didn't find id2 after removed id", x == -1);
      if (x == id2) {
    break;
      }
  }
    }

    /* -- Unusual states: nextObjectId -- */
    private final Action nextObjectId = new Action() {
  void run() throws Exception { store.nextObjectId(txn, -1); };
    };
    @Test
    public void testNextObjectIdAborted() throws Exception {
  testAborted(nextObjectId);
    }
    @Test
    public void testNextObjectIdPreparedReadOnly() throws Exception {
  testPreparedReadOnly(nextObjectId);
    }
    @Test
    public void testNextObjectIdPreparedModified() throws Exception {
  testPreparedModified(nextObjectId);
    }
    @Test
    public void testNextObjectIdCommitted() throws Exception {
  testCommitted(nextObjectId);
    }
    @Test
    public void testNextObjectIdWrongTxn() throws Exception {
  testWrongTxn(nextObjectId);
    }
    @Test
    public void testNextObjectIdShuttingDownExistingTxn() throws Exception {
  testShuttingDownExistingTxn(nextObjectId);
    }
    @Test
    public void testNextObjectIdShuttingDownNewTxn() throws Exception {
  testShuttingDownNewTxn(nextObjectId);
    }
    @Test
    public void testNextObjectIdShutdown() throws Exception {
  testShutdown(nextObjectId);
    }

    /* -- Test deadlock -- */
    @SuppressWarnings("hiding")
    @Test
    public void testDeadlock() throws Exception {
  for (int i = 0; i < 5; i++) {
      if (i > 0) {
    txn = createTransaction(
        UsePrepareAndCommit.ARBITRARY, 1000);
      }
      final long id = store.createObject(txn);
      store.setObject(txn, id, new byte[] { 0 });
      final long id2 = store.createObject(txn);
      store.setObject(txn, id2, new byte[] { 0 });
      txn.commit();
      txn = createTransaction(
    UsePrepareAndCommit.ARBITRARY, 1000);
      store.getObject(txn, id, false);
      final Semaphore flag = new Semaphore(1);
      flag.acquire();
      final int finalI = i;
      class MyRunnable implements Runnable {
    Exception exception2;
    public void run() {
        DummyTransaction txn2 = null;
        try {
      txn2 = createTransaction(
          UsePrepareAndCommit.ARBITRARY, 1000);
      store.getObject(txn2, id2, false);
      flag.release();
      store.getObject(txn2, id, true);
      System.err.println(finalI + " txn2: commit");
      txn2.commit();
        } catch (TransactionAbortedException e) {
      System.err.println(finalI + " txn2: " + e);
      exception2 = e;
        } catch (Exception e) {
      System.err.println(finalI + " txn2: " + e);
      exception2 = e;
      if (txn2 != null) {
          txn2.abort(new RuntimeException("abort"));
      }
        }
    }
      }
      MyRunnable myRunnable = new MyRunnable();
      Thread thread = new Thread(myRunnable, "testDeadlockThread");
      thread.start();
      Thread.sleep(i * 500);
      flag.acquire();
      TransactionException exception = null;
      try {
    store.getObject(txn, id2, true);
    System.err.println(i + " txn1: commit");
    txn.commit();
      } catch (TransactionAbortedException e) {
    System.err.println(i + " txn1: " + e);
    exception = e;
      }
      thread.join();
      if (myRunnable.exception2 != null &&
    !(myRunnable.exception2
      instanceof TransactionAbortedException))
      {
    throw myRunnable.exception2;
      } else if (exception == null && myRunnable.exception2 == null) {
    fail("Expected TransactionAbortedException");
      }
      txn = null;
  }
    }

    /* -- Other methods and classes -- */

    /** Creates a unique directory. */
    private String createDirectory() throws IOException {
  String name = getClass().getName();
  int dot = name.lastIndexOf('.');
  if (dot > 0) {
      name = name.substring(dot + 1);
  }
  File dir = File.createTempFile(name, "dbdir");
  if (!dir.delete()) {
      throw new RuntimeException("Problem deleting file: " + dir);
  }
  if (!dir.mkdir()) {
      throw new RuntimeException(
    "Failed to create directory: " + dir);
  }
  return dir.getPath();
    }

    /** Insures an empty version of the directory exists. */
    private static void cleanDirectory(String directory) {
  File dir = new File(directory);
  if (dir.exists()) {
      for (File f : dir.listFiles()) {
    if (!f.delete()) {
        throw new RuntimeException("Failed to delete file: " + f);
    }
      }
      if (!dir.delete()) {
    throw new RuntimeException(
        "Failed to delete directory: " + dir);
      }
  }
  if (!dir.mkdir()) {
      throw new RuntimeException(
    "Failed to create directory: " + dir);
  }
    }

    /** Creates a DataStore using the default properties. */
    protected DataStore createDataStore() throws Exception {
  return createDataStore(props);
    }

    /** Creates a DataStore using the specified properties. */
    protected DataStore createDataStore(Properties props) throws Exception {
  DataStore store = new DataStoreProfileProducer(
      new DataStoreImpl(props, systemRegistry, txnProxy),
      DummyProfileCoordinator.getCollector());
  DummyProfileCoordinator.startProfiling();
  return store;
    }

    /** Returns the default properties to use for creating data stores. */
    protected Properties getProperties() throws Exception {
  return createProperties(
      DataStoreImplClassName + ".directory", dbDirectory);
    }

    /* -- Support for testing unusual states -- */

    /**
     * An action, with an optional setup step, to be run in the context of an
     * unusual state.
     */
    abstract class Action {

  /**
   * Perform any setup required for calling the {@link #run} method.
   * This method needs to put the database in use for the current
   * transaction, but not in a way that conflicts with the operations
   * performed by {@code run}.
   *
   * @throws  Exception if the setup throws an exception
   */
  void setUp() throws Exception { };

  /**
   * Run the test action.
   *
   * @throws  Exception if the action throws an exception
   */
  abstract void run() throws Exception;
    }

    /** Tests running the action after abort. */
    void testAborted(Action action) throws Exception {
  testAborted(action, TransactionNotActiveException.class);
    }

    /**
     * Tests running the action after abort, and expecting to get the
     * specified exception type.
     */
    void testAborted(Action action, Class<? extends Exception> exceptionType)
  throws Exception
    {
  action.setUp();
  txn.abort(new RuntimeException("abort"));
  try {
      action.run();
      fail("Expected exception");
  } catch (Exception e) {
      if (exceptionType.isInstance(e)) {
    System.err.println(e);
      } else {
    fail("Expected " + exceptionType + ": " + e);
      }
  } finally {
      txn = null;
  }
    }

    /** Tests running the action after prepare returns read-only. */
    void testPreparedReadOnly(Action action) throws Exception {
  action.setUp();
  txn.prepare();
  try {
      action.run();
      fail("Expected exception");
  } catch (TransactionNotActiveException e) {
      System.err.println(e);
  } catch (IllegalStateException e) {
      System.err.println(e);
  }
    }

    /** Tests running the action after prepare returns modified. */
    void testPreparedModified(Action action) throws Exception {
  action.setUp();
  store.setObject(txn, id, new byte[] { 0 });
  txn.prepare();
  try {
      action.run();
      fail("Expected IllegalStateException");
  } catch (IllegalStateException e) {
      System.err.println(e);
  }
    }

    /** Tests running the action after commit. */
    void testCommitted(Action action) throws Exception {
  action.setUp();
  txn.commit();
  try {
      action.run();
      fail("Expected exception");
  } catch (TransactionNotActiveException e) {
      System.err.println(e);
  } catch (IllegalStateException e) {
      System.err.println(e);
  } finally {
      txn = null;
  }
    }

    /** Tests running the action in the wrong transaction. */
    void testWrongTxn(Action action) throws Exception {
  action.setUp();
  store.createObject(txn);
  DummyTransaction originalTxn = txn;
  txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
  try {
      action.run();
      fail("Expected IllegalStateException");
  } catch (IllegalStateException e) {
      System.err.println(e);
  } finally {
      originalTxn.abort(new RuntimeException("abort"));
  }
    }

    /**
     * Tests running the action in an existing transaction while shutting down.
     */
    void testShuttingDownExistingTxn(Action action) throws Exception {
  action.setUp();
  ShutdownAction shutdownAction = new ShutdownAction();
  shutdownAction.assertBlocked();
  action.run();
  if (txn != null) {
      txn.commit();
      txn = null;
  }
  assertTrue(shutdownAction.waitForDone());
  store = null;
    }

    /** Tests running the action in a new transaction while shutting down. */
    void testShuttingDownNewTxn(final Action action) throws Exception {
  action.setUp();
  DummyTransaction originalTxn = txn;
  ShutdownAction shutdownAction = new ShutdownAction();
  shutdownAction.assertBlocked();
  final AtomicReference<Throwable> exceptionHolder =
      new AtomicReference<Throwable>();
  Thread t = new Thread() {
      public void run() {
    txn = createTransaction(UsePrepareAndCommit.ARBITRARY);
    try {
        action.run();
    } catch (Throwable t) {
        exceptionHolder.set(t);
    }
    txn.abort(new RuntimeException("abort"));
    txn = null;
      }
  };
  t.start();
  t.join(1000);
  Throwable exception = exceptionHolder.get();
  assertTrue("Expected IllegalStateException: " + exception,
       exception instanceof IllegalStateException);
  originalTxn.abort(new RuntimeException("abort"));
  assertTrue(shutdownAction.waitForDone());
  store = null;
    }

    /** Tests running the action after shutdown. */
    void testShutdown(Action action) throws Exception {
  action.setUp();
  txn.abort(new RuntimeException("abort"));
  store.shutdown();
  try {
      action.run();
      fail("Expected exception");
  } catch (TransactionNotActiveException e) {
      System.err.println(e);
  } catch (IllegalStateException e) {
      System.err.println(e);
  }
  txn = null;
  store = null;
    }

    /** Use this thread to control a call to shutdown that may block. */
    protected class ShutdownAction extends Thread {
  private boolean done;
  private Throwable exception;

  /** Creates an instance of this class and starts the thread. */
  protected ShutdownAction() {
      start();
  }

  /** Performs the shutdown and collects the results. */
  public void run() {
      try {
    shutdown();
      } catch (Throwable t) {
    exception = t;
      }
      synchronized (this) {
    done = true;
    notifyAll();
      }
  }

  protected void shutdown() {
            store.shutdown();
  }

  /** Asserts that the shutdown call is blocked. */
  public synchronized void assertBlocked() throws InterruptedException {
      Thread.sleep(5);
      if (exception != null) {
    exception.printStackTrace();
    fail("Unexpected exception: " + exception);
      }
      assertFalse("Expected shutdown to be blocked", done);
  }
 
  /** Waits a while for the shutdown call to complete. */
  public synchronized boolean waitForDone() throws Exception {
      waitForDoneInternal();
      if (!done) {
    return false;
      } else if (exception == null) {
    return true;
      } else if (exception instanceof Exception) {
    throw (Exception) exception;
      } else {
    throw (Error) exception;
      }
  }

  /** Wait until done, but give up after a while. */
  private synchronized void waitForDoneInternal()
      throws InterruptedException
  {
      long wait = 2000;
      long start = System.currentTimeMillis();
      while (!done && wait > 0) {
    wait(wait);
    long now = System.currentTimeMillis();
    wait -= (now - start);
    start = now;
      }
  }
    }

    /** Assert that the two byte arrays are the same. */
    private static void assertSameBytes(byte[] x, byte[] y) {
  if (!Arrays.equals(x, y)) {
      fail("Expected " + Arrays.toString(x) + ", got " +
     Arrays.toString(y));
  }
    }

    /** Creates a transaction. */
    protected DummyTransaction createTransaction() {
  return initTransaction(new DummyTransaction());
    }

    /** Creates a transaction with explicit use of prepareAndCommit. */
    protected DummyTransaction createTransaction(
  UsePrepareAndCommit usePrepareAndCommit)
    {
  return initTransaction(new DummyTransaction(usePrepareAndCommit));
    }

    /**
     * Creates a transaction with explicit use of prepareAndCommit and a
     * non-standard timeout.
     */
    protected DummyTransaction createTransaction(
  UsePrepareAndCommit usePrepareAndCommit, long timeout)
    {
  return initTransaction(
      new DummyTransaction(usePrepareAndCommit, timeout));
    }

    /** Initializes a new transaction. */
    protected DummyTransaction initTransaction(DummyTransaction txn) {
  txnProxy.setCurrentTransaction(txn);
  accessCoordinator.notifyNewTransaction(txn, 0, 1);
  return txn;
    }
}
TOP

Related Classes of com.sun.sgs.test.impl.service.data.store.MyRunnable

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.