org.ndnx.ndn.io.content.NDNNetworkObjectTestRepo.java Source code

Java tutorial

Introduction

Here is the source code for org.ndnx.ndn.io.content.NDNNetworkObjectTestRepo.java

Source

/*
 * A NDNx library test.
 *
 * Portions Copyright (C) 2013 Regents of the University of California.
 * 
 * Based on the CCNx C Library by PARC.
 * Copyright (C) 2008-2013 Palo Alto Research Center, Inc.
 *
 * This work 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.
 * This work 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, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

package org.ndnx.ndn.io.content;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InvalidObjectException;

import org.bouncycastle.util.Arrays;
import org.ndnx.ndn.NDNHandle;
import org.ndnx.ndn.KeyManager;
import org.ndnx.ndn.config.SystemConfiguration;
import org.ndnx.ndn.impl.NDNFlowControl.SaveType;
import org.ndnx.ndn.impl.security.crypto.util.DigestHelper;
import org.ndnx.ndn.impl.support.Log;
import org.ndnx.ndn.impl.support.ConcurrencyUtils.Waiter;
import org.ndnx.ndn.io.NDNVersionedInputStream;
import org.ndnx.ndn.io.content.NDNNetworkObject;
import org.ndnx.ndn.io.content.NDNStringObject;
import org.ndnx.ndn.io.content.Collection;
import org.ndnx.ndn.io.content.Link;
import org.ndnx.ndn.io.content.LinkAuthenticator;
import org.ndnx.ndn.io.content.LocalCopyListener;
import org.ndnx.ndn.io.content.LocalCopyWrapper;
import org.ndnx.ndn.io.content.UpdateListener;
import org.ndnx.ndn.io.content.Collection.CollectionObject;
import org.ndnx.ndn.profiles.VersioningProfile;
import org.ndnx.ndn.profiles.repo.RepositoryControl;
import org.ndnx.ndn.protocol.NDNTime;
import org.ndnx.ndn.protocol.ContentName;
import org.ndnx.ndn.protocol.PublisherID;
import org.ndnx.ndn.protocol.SignedInfo;
import org.ndnx.ndn.protocol.PublisherID.PublisherType;
import org.ndnx.ndn.NDNTestHelper;
import org.ndnx.ndn.TestUtils;
import org.ndnx.ndn.io.content.NDNNetworkObjectTest.CounterListener;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 * Test basic network object functionality, writing objects to a repository.
 **/
public class NDNNetworkObjectTestRepo extends NDNNetworkObjectTestBase {

    /**
     * Handle naming for the test
     */
    static NDNTestHelper testHelper = new NDNTestHelper(NDNNetworkObjectTestRepo.class);

    static void setupNamespace(ContentName name) throws IOException {
    }

    static void removeNamepspace(ContentName name) throws IOException {
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
        Log.info(Log.FAC_TEST, "Tearing down NDNNetworkObjectTestRepo, prefix {0}", testHelper.getClassNamespace());
    }

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        Log.info(Log.FAC_TEST, "Setting up NDNNetworkObjectTestRepo, prefix {0}", testHelper.getClassNamespace());

        NDNHandle myhandle = NDNHandle.open();
        try {
            ns = new ContentName[NUM_LINKS];
            for (int i = 0; i < NUM_LINKS; ++i) {
                ns[i] = new ContentName(testHelper.getClassNamespace(), "Links", prefix + Integer.toString(i));
            }
            Arrays.fill(publisherid1, (byte) 6);
            Arrays.fill(publisherid2, (byte) 3);

            pubID1 = new PublisherID(publisherid1, PublisherType.KEY);
            pubID2 = new PublisherID(publisherid2, PublisherType.ISSUER_KEY);

            las[0] = new LinkAuthenticator(pubID1);
            las[1] = null;
            las[2] = new LinkAuthenticator(pubID2, null, null, SignedInfo.ContentType.DATA, contenthash1);
            las[3] = new LinkAuthenticator(pubID1, null, NDNTime.now(), null, contenthash1);

            for (int j = 4; j < NUM_LINKS; ++j) {
                las[j] = new LinkAuthenticator(pubID2, null, NDNTime.now(), null, null);
            }

            lrs = new Link[NUM_LINKS];
            for (int i = 0; i < lrs.length; ++i) {
                lrs[i] = new Link(ns[i], las[i]);
            }

            empty = new Collection();
            small1 = new Collection();
            small2 = new Collection();
            for (int i = 0; i < 5; ++i) {
                small1.add(lrs[i]);
                small2.add(lrs[i + 5]);
            }
            big = new Collection();
            for (int i = 0; i < NUM_LINKS; ++i) {
                big.add(lrs[i]);
            }
            Log.info(Log.FAC_TEST, "Finihed setting up NDNNetworkObjectTestRepo, prefix {0}",
                    testHelper.getClassNamespace());
        } finally {
            myhandle.close();
            KeyManager.closeDefaultKeyManager();
        }
    }

    @Test
    public void testVersioning() throws Exception {
        Log.info(Log.FAC_TEST, "Starting testVersioning");

        // Testing problem of disappearing versions, inability to get latest. Use simpler
        // object than a collection.
        NDNHandle lput = NDNHandle.open();
        NDNHandle lget = NDNHandle.open();

        try {
            ContentName testName = new ContentName(testHelper.getTestNamespace("testVersioning"), stringObjName);

            NDNStringObject so = new NDNStringObject(testName, "First value", SaveType.REPOSITORY, lput);
            setupNamespace(testName);
            NDNStringObject ro = null;
            NDNStringObject ro2 = null;
            NDNStringObject ro3, ro4; // make each time, to get a new handle.
            NDNTime soTime, srTime, sr2Time, sr3Time, sr4Time, so2Time;
            for (int i = 0; i < numbers.length; ++i) {
                soTime = saveAndLog(numbers[i], so, null, numbers[i]);
                TestUtils.checkObject(lput, so);
                ro = new NDNStringObject(testName, lget);
                srTime = waitForDataAndLog(numbers[i], ro);
                ro2 = new NDNStringObject(testName, null);
                sr2Time = waitForDataAndLog(numbers[i], ro2);
                ro3 = new NDNStringObject(ro.getVersionedName(), null); // read specific version
                sr3Time = waitForDataAndLog("UpdateToROVersion", ro3);
                // Save a new version and pull old
                so2Time = saveAndLog(numbers[i] + "-Update", so, null, numbers[i] + "-Update");
                TestUtils.checkObject(lput, so);
                ro4 = new NDNStringObject(ro.getVersionedName(), null); // read specific version
                sr4Time = waitForDataAndLog("UpdateAnotherToROVersion", ro4);
                Log.info(Log.FAC_TEST, "Update " + i + ": Times: " + soTime + " " + srTime + " " + sr2Time + " "
                        + sr3Time + " different: " + so2Time);
                Assert.assertEquals("SaveTime doesn't match first read", soTime, srTime);
                Assert.assertEquals("SaveTime doesn't match second read", soTime, sr2Time);
                Assert.assertEquals("SaveTime doesn't match specific version read", soTime, sr3Time);
                Assert.assertFalse("UpdateTime isn't newer than read time", soTime.equals(so2Time));
                Assert.assertEquals("SaveTime doesn't match specific version read", soTime, sr4Time);
            }
        } finally {
            lput.close();
            lget.close();
            KeyManager.closeDefaultKeyManager();
        }

        Log.info(Log.FAC_TEST, "Completed testVersioning");
    }

    @Test
    public void testSaveToVersion() throws Exception {
        Log.info(Log.FAC_TEST, "Starting testSaveToVersion");

        // Testing problem of disappearing versions, inability to get latest. Use simpler
        // object than a collection.
        NDNHandle lput = NDNHandle.open();
        NDNHandle lget = NDNHandle.open();
        try {
            ContentName testName = new ContentName(testHelper.getTestNamespace("testSaveToVersion"), stringObjName);

            NDNTime desiredVersion = NDNTime.now();

            NDNStringObject so = new NDNStringObject(testName, "First value", SaveType.REPOSITORY, lput);
            setupNamespace(testName);
            saveAndLog("SpecifiedVersion", so, desiredVersion, "Time: " + desiredVersion);
            TestUtils.checkObject(lput, so);
            Assert.assertEquals("Didn't write correct version", desiredVersion, so.getVersion());

            NDNStringObject ro = new NDNStringObject(testName, lget);
            ro.waitForData();
            Assert.assertEquals("Didn't read correct version", desiredVersion, ro.getVersion());
            ContentName versionName = ro.getVersionedName();

            saveAndLog("UpdatedVersion", so, null, "ReplacementData");
            TestUtils.checkObject(lput, so);
            updateAndLog("UpdatedData", ro, null);
            Assert.assertTrue(
                    "New version " + so.getVersion() + " should be later than old version " + desiredVersion,
                    (desiredVersion.before(so.getVersion())));
            Assert.assertEquals("Didn't read correct version", so.getVersion(), ro.getVersion());

            NDNStringObject ro2 = new NDNStringObject(versionName, null);
            ro2.waitForData();
            Assert.assertEquals("Didn't read correct version", desiredVersion, ro2.getVersion());
        } finally {
            lput.close();
            lget.close();
            KeyManager.closeDefaultKeyManager();
        }

        Log.info(Log.FAC_TEST, "Completed testSaveToVersion");
    }

    @Test
    public void testEmptySave() throws Exception {
        Log.info(Log.FAC_TEST, "Starting testEmptySave");

        boolean caught = false;
        NDNHandle handle = NDNHandle.open();
        try {
            ContentName testName = new ContentName(testHelper.getTestNamespace("testEmptySave"), collectionObjName);
            CollectionObject emptycoll = new CollectionObject(testName, (Collection) null, SaveType.REPOSITORY,
                    handle);
            setupNamespace(testName);
            try {
                emptycoll.setData(small1); // set temporarily to non-null
                saveAndLog("Empty", emptycoll, null, null);
                TestUtils.checkObject(handle, emptycoll);
            } catch (InvalidObjectException iox) {
                // this is what we expect to happen
                caught = true;
            }
            Assert.assertTrue("Failed to produce expected exception.", caught);
        } finally {
            handle.close();
            KeyManager.closeDefaultKeyManager();
        }

        Log.info(Log.FAC_TEST, "Completed testEmptySave");
    }

    @Test
    public void testStreamUpdate() throws Exception {
        Log.info(Log.FAC_TEST, "Starting testStreamUpdate");

        NDNHandle tHandle = NDNHandle.open();
        try {
            ContentName testName = new ContentName(testHelper.getTestNamespace("testStreamUpdate"),
                    collectionObjName);
            CollectionObject testCollectionObject = new CollectionObject(testName, small1, SaveType.REPOSITORY,
                    tHandle);
            setupNamespace(testName);

            saveAndLog("testStreamUpdate", testCollectionObject, null, small1);
            TestUtils.checkObject(tHandle, testCollectionObject);
            Log.info(Log.FAC_TEST, "testCollectionObject name: " + testCollectionObject.getVersionedName());

            NDNVersionedInputStream vis = new NDNVersionedInputStream(testCollectionObject.getVersionedName());
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buf = new byte[128];
            // Will incur a timeout
            while (!vis.eof()) {
                int read = vis.read(buf);
                if (read > 0)
                    baos.write(buf, 0, read);
            }
            Log.info(Log.FAC_TEST, "Read " + baos.toByteArray().length + " bytes, digest: "
                    + DigestHelper.printBytes(DigestHelper.digest(baos.toByteArray()), 16));

            Collection decodedData = new Collection();
            decodedData.decode(baos.toByteArray());
            Log.info(Log.FAC_TEST, "Decoded collection data: " + decodedData);
            Assert.assertEquals("Decoding via stream fails to give expected result!", decodedData, small1);

            NDNVersionedInputStream vis2 = new NDNVersionedInputStream(testCollectionObject.getVersionedName());
            ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
            // Will incur a timeout
            while (!vis2.eof()) {
                int val = vis2.read();
                if (val < 0)
                    break;
                baos2.write((byte) val);
            }
            Log.info(Log.FAC_TEST, "Read " + baos2.toByteArray().length + " bytes, digest: "
                    + DigestHelper.printBytes(DigestHelper.digest(baos2.toByteArray()), 16));
            Assert.assertArrayEquals("Reading same object twice gets different results!", baos.toByteArray(),
                    baos2.toByteArray());

            Collection decodedData2 = new Collection();
            decodedData2.decode(baos2.toByteArray());
            Assert.assertEquals("Decoding via stream byte read fails to give expected result!", decodedData2,
                    small1);

            NDNVersionedInputStream vis3 = new NDNVersionedInputStream(testCollectionObject.getVersionedName());
            Collection decodedData3 = new Collection();
            decodedData3.decode(vis3);
            Assert.assertEquals("Decoding via stream full read fails to give expected result!", decodedData3,
                    small1);
        } finally {
            tHandle.close();
            KeyManager.closeDefaultKeyManager();
        }

        Log.info(Log.FAC_TEST, "Completed testStreamUpdate");
    }

    @Test
    public void testVersionOrdering() throws Exception {
        Log.info(Log.FAC_TEST, "Starting testVersionOrdering");

        NDNHandle tHandle = NDNHandle.open();
        NDNHandle rHandle = NDNHandle.open();
        try {
            ContentName testName = new ContentName(testHelper.getTestNamespace("testVersionOrdering"),
                    collectionObjName, "name1");
            ContentName testName2 = new ContentName(testHelper.getTestNamespace("testVersionOrdering"),
                    collectionObjName, "name2");

            CollectionObject c0 = new CollectionObject(testName, empty, SaveType.REPOSITORY, rHandle);
            setupNamespace(testName);
            NDNTime t0 = saveAndLog("Empty", c0, null, empty);
            TestUtils.checkObject(rHandle, c0);

            CollectionObject c1 = new CollectionObject(testName2, small1, SaveType.REPOSITORY, tHandle);
            CollectionObject c2 = new CollectionObject(testName2, small1, SaveType.REPOSITORY, null);
            setupNamespace(testName2);
            NDNTime t1 = saveAndLog("Small", c1, null, small1);
            TestUtils.checkObject(tHandle, c1);
            Assert.assertTrue("First version should come before second", t0.before(t1));

            NDNTime t2 = saveAndLog("Small2ndWrite", c2, null, small1);
            TestUtils.checkObject(c2.getHandle(), c2);
            Assert.assertTrue("Third version should come after second", t1.before(t2));
            Assert.assertTrue(c2.contentEquals(c1));
            Assert.assertFalse(c2.equals(c1));
            Assert.assertTrue(VersioningProfile.isLaterVersionOf(c2.getVersionedName(), c1.getVersionedName()));
        } finally {
            tHandle.close();
            rHandle.close();
            KeyManager.closeDefaultKeyManager();
        }

        Log.info(Log.FAC_TEST, "Completed testVersionOrdering");
    }

    @Test
    public void testUpdateInBackground() throws Exception {
        Log.info(Log.FAC_TEST, "Starting testUpdateInBackground");

        NDNHandle thandle = NDNHandle.open();
        NDNHandle thandle2 = NDNHandle.open();
        NDNHandle thandle3 = NDNHandle.open();

        try {
            ContentName testName = new ContentName(testHelper.getTestNamespace("testUpdateInBackground"),
                    stringObjName, "name1");
            // Make writer first, so it picks up readers first interests even before it writes.
            NDNStringObject c2 = new NDNStringObject(testName, (String) null, SaveType.REPOSITORY, thandle);

            NDNStringObject c0 = new NDNStringObject(testName, (String) null, SaveType.REPOSITORY, thandle2);
            c0.updateInBackground();

            NDNStringObject c1 = new NDNStringObject(testName, (String) null, SaveType.REPOSITORY, thandle3);
            c1.updateInBackground(true);

            Assert.assertFalse(c0.available());
            Assert.assertFalse(c0.isSaved());
            Assert.assertFalse(c1.available());
            Assert.assertFalse(c1.isSaved());

            NDNTime t1 = saveAndLog("First string", c2, null, "Here is the first string.");
            TestUtils.checkObject(thandle, c2);
            Log.info(Log.FAC_TEST, "Saved c2: " + c2.getVersionedName() + " c0 available? " + c0.available()
                    + " c1 available? " + c1.available());
            c0.waitForData();
            Assert.assertEquals("c0 update", c0.getVersion(), c2.getVersion());
            c1.waitForData();
            Assert.assertEquals("c1 update", c1.getVersion(), c2.getVersion());

            NDNTime t2 = saveAndLog("Second string", c2, null, "Here is the second string.");
            TestUtils.checkObject(thandle, c2);
            doWait(c1, t2);
            Assert.assertEquals("c1 update 2", c1.getVersion(), c2.getVersion());
            Assert.assertEquals("c0 unchanged", c0.getVersion(), t1);

            // Sleep for a while and see how fast the interests come. Should be only 2 interests.
            Log.info(Log.FAC_TEST, "Sleeping, count background interests.");
            long time = System.currentTimeMillis();
            Thread.sleep(2000);
            long elapsed = System.currentTimeMillis() - time;
            long count = (elapsed / 4000) + 1;
            Log.info(Log.FAC_TEST,
                    "Slept " + elapsed / 1000.0 + " seconds, should have been " + count + " interests.");

            NDNTime t3 = saveAndLog("Third string", c2, null, "Here is the third string.");
            TestUtils.checkObject(thandle, c2);
            doWait(c1, t3);
            Assert.assertEquals("c1 update 3", c1.getVersion(), c2.getVersion());
            Assert.assertEquals("c0 unchanged", c0.getVersion(), t1);

            c1.cancelInterest();
        } finally {
            thandle.close();
            thandle2.close();
            thandle3.close();
            KeyManager.closeDefaultKeyManager();
        }

        Log.info(Log.FAC_TEST, "Completed testUpdateInBackground");
    }

    @Test
    public void testUpdateOtherName() throws Exception {
        Log.info(Log.FAC_TEST, "Starting testUpdateOtherName");

        NDNHandle thandle = NDNHandle.open();
        NDNHandle rhandle = NDNHandle.open();
        try {
            ContentName testName = new ContentName(testHelper.getTestNamespace("testUpdateOtherName"),
                    collectionObjName, "name1");
            ContentName testName2 = new ContentName(testHelper.getTestNamespace("testUpdateOtherName"),
                    collectionObjName, "name2");

            CollectionObject c0 = new CollectionObject(testName, empty, SaveType.REPOSITORY, rhandle);
            setupNamespace(testName);
            NDNTime t0 = saveAndLog("Empty", c0, null, empty);
            TestUtils.checkObject(rhandle, c0);

            CollectionObject c1 = new CollectionObject(testName2, small1, SaveType.REPOSITORY, thandle);
            // Cheat a little, make this one before the setupNamespace...
            CollectionObject c2 = new CollectionObject(testName2, small1, SaveType.REPOSITORY, null);
            setupNamespace(testName2);
            NDNTime t1 = saveAndLog("Small", c1, null, small1);
            TestUtils.checkObject(thandle, c1);
            Assert.assertTrue("First version should come before second", t0.before(t1));

            NDNTime t2 = saveAndLog("Small2ndWrite", c2, null, small1);
            TestUtils.checkObject(c2.getHandle(), c2);
            Assert.assertTrue("Third version should come after second", t1.before(t2));
            Assert.assertTrue(c2.contentEquals(c1));
            Assert.assertFalse(c2.equals(c1));

            NDNTime t3 = updateAndLog(c0.getVersionedName().toString(), c0, testName2);
            Assert.assertTrue(VersioningProfile.isVersionOf(c0.getVersionedName(), testName2));
            Assert.assertEquals(t3, t2);
            Assert.assertTrue(c0.contentEquals(c2));

            t3 = updateAndLog(c0.getVersionedName().toString(), c0, c1.getVersionedName());
            Assert.assertTrue(VersioningProfile.isVersionOf(c0.getVersionedName(), testName2));
            Assert.assertEquals(t3, t1);
            Assert.assertTrue(c0.contentEquals(c1));
        } finally {
            thandle.close();
            rhandle.close();
            KeyManager.closeDefaultKeyManager();
        }

        Log.info(Log.FAC_TEST, "Completed testUpdateOtherName");
    }

    @Test
    public void testSaveAsGone() throws Exception {
        Log.info(Log.FAC_TEST, "Starting testSaveAsGone");

        NDNHandle thandle = NDNHandle.open();
        NDNHandle rhandle = NDNHandle.open();
        NDNHandle shandle = NDNHandle.open();

        try {
            ContentName testName = new ContentName(testHelper.getTestNamespace("testSaveAsGone"),
                    collectionObjName);
            CollectionObject c0 = new CollectionObject(testName, empty, SaveType.REPOSITORY, thandle);
            setupNamespace(testName);
            NDNTime t0 = saveAsGoneAndLog("Gone", c0);
            Assert.assertTrue("Should be gone", c0.isGone());
            ContentName goneVersionName = c0.getVersionedName();

            NDNTime t1 = saveAndLog("NotGone", c0, null, small1);
            TestUtils.checkObject(thandle, c0);
            Assert.assertFalse("Should not be gone", c0.isGone());
            Assert.assertTrue(t1.after(t0));

            CollectionObject c1 = new CollectionObject(testName, rhandle);
            NDNTime t2 = waitForDataAndLog(testName.toString(), c1);
            Assert.assertFalse("Read back should not be gone", c1.isGone());
            Assert.assertEquals(t2, t1);

            NDNTime t3 = updateAndLog(goneVersionName.toString(), c1, goneVersionName);
            Assert.assertTrue(VersioningProfile.isVersionOf(c1.getVersionedName(), testName));
            Assert.assertEquals(t3, t0);
            Assert.assertTrue("Read back should be gone.", c1.isGone());

            t0 = saveAsGoneAndLog("GoneAgain", c0);
            Assert.assertTrue("Should be gone", c0.isGone());

            CollectionObject c2 = new CollectionObject(testName, shandle);
            NDNTime t4 = waitForDataAndLog(testName.toString(), c2);
            Assert.assertTrue(
                    "Read back of " + c0.getVersionedName() + " should be gone, got " + c2.getVersionedName(),
                    c2.isGone());
            Assert.assertEquals(t4, t0);
        } finally {
            thandle.close();
            rhandle.close();
            shandle.close();
            KeyManager.closeDefaultKeyManager();
        }

        Log.info(Log.FAC_TEST, "Completed testSaveAsGone");
    }

    @Test
    public void testUpdateDoesNotExist() throws Exception {
        Log.info(Log.FAC_TEST, "Starting testUpdateDoesNotExist");

        NDNHandle thandle = NDNHandle.open();
        NDNHandle rhandle = NDNHandle.open();
        try {
            ContentName testName = new ContentName(testHelper.getTestNamespace("testUpdateDoesNotExist"),
                    collectionObjName);
            NDNStringObject so = new NDNStringObject(testName, rhandle);

            // so should catch exception thrown by underlying stream when it times out.
            Assert.assertFalse(so.available());

            // ask for it in background
            so.updateInBackground();

            NDNStringObject sowrite = new NDNStringObject(testName, "Now we write something.", SaveType.REPOSITORY,
                    thandle);
            setupNamespace(testName);
            saveAndLog("Delayed write", sowrite, null, "Now we write something.");
            TestUtils.checkObject(thandle, sowrite);
            so.waitForData();
            Assert.assertTrue(so.available());
            Assert.assertEquals(so.string(), sowrite.string());
            Assert.assertEquals(so.getVersionedName(), sowrite.getVersionedName());
        } finally {
            thandle.close();
            rhandle.close();
            KeyManager.closeDefaultKeyManager();
        }

        Log.info(Log.FAC_TEST, "Completed testUpdateDoesNotExist");
    }

    @Test
    public void testUpdateListener() throws Exception {
        Log.info(Log.FAC_TEST, "Starting testUpdateListener");

        SaveType saveType = SaveType.REPOSITORY;
        NDNHandle writeHandle = NDNHandle.open();
        NDNHandle readHandle = NDNHandle.open();

        try {
            ContentName testName = new ContentName(testHelper.getTestNamespace("testUpdateListener"),
                    stringObjName);

            NDNStringObject writeObject = new NDNStringObject(testName, "Something to listen to.", saveType,
                    writeHandle);
            writeObject.save();

            CounterListener ourListener = new CounterListener();
            NDNStringObject readObject = new NDNStringObject(testName, null, null, readHandle);
            readObject.addListener(ourListener);
            boolean result = readObject.update();

            Assert.assertTrue(result);
            Assert.assertTrue(ourListener.getCounter() == 1);

            readObject.updateInBackground();

            writeObject.save("New stuff! New stuff!");
            synchronized (readObject) {
                while (ourListener.getCounter() == 1)
                    readObject.wait();
            }
            // For some reason, we're getting two updates on our updateInBackground...
            Assert.assertTrue(ourListener.getCounter() > 1);
        } finally {
            writeHandle.close();
            readHandle.close();
            KeyManager.closeDefaultKeyManager();
        }

        Log.info(Log.FAC_TEST, "Completed testUpdateListener");
    }

    @Test
    public void testLocalCopyWrapper() throws Exception {
        Log.info(Log.FAC_TEST, "Starting testLocalCopyWrapper");

        NDNHandle thandle = NDNHandle.open();
        NDNHandle rhandle = NDNHandle.open();

        try {
            ContentName testName = new ContentName(testHelper.getTestNamespace("testLocalCopyWrapper"),
                    collectionObjName);
            NDNStringObject so = new NDNStringObject(testName, rhandle);
            LocalCopyWrapper wo = new LocalCopyWrapper(so);
            Assert.assertFalse(wo.available());
            class Record {
                boolean callback = false;
            }
            Record record = new Record();

            class Listener implements UpdateListener {
                Record _rec;

                public Listener(Record r) {
                    _rec = r;
                }

                public void newVersionAvailable(NDNNetworkObject<?> newVersion, boolean wasSave) {
                    synchronized (_rec) {
                        _rec.callback = true;
                        _rec.notifyAll();
                    }
                }
            }
            ;

            // ask for it in background
            wo.updateInBackground(false, new Listener(record));

            NDNStringObject sowrite = new NDNStringObject(testName, "Now we write", SaveType.RAW, thandle);
            setupNamespace(testName);
            saveAndLog("Delayed write", sowrite, null, "Now we write");
            TestUtils.checkObject(thandle, sowrite);
            wo.waitForData();
            Assert.assertTrue(wo.available());
            Assert.assertEquals(((NDNStringObject) wo.object()).string(), sowrite.string());
            Assert.assertEquals(wo.getVersionedName(), sowrite.getVersionedName());

            new Waiter(UPDATE_TIMEOUT) {
                @Override
                protected boolean check(Object o, Object check) throws Exception {
                    return ((Record) o).callback;
                }
            }.wait(record, record);
            Assert.assertEquals(true, record.callback);

            if (!RepositoryControl.localRepoSync(rhandle, so)) {
                Thread.sleep(SystemConfiguration.MEDIUM_TIMEOUT);
                // Should be in the repo by now
                Assert.assertTrue(RepositoryControl.localRepoSync(rhandle, so));
            }
        } finally {
            thandle.close();
            rhandle.close();
            KeyManager.closeDefaultKeyManager();
        }

        Log.info(Log.FAC_TEST, "Completed testLocalCopyWrapper");
    }

    @Test
    public void testLocalCopyListener() throws Exception {
        Log.info(Log.FAC_TEST, "Starting testLocalCopyListener");

        NDNHandle rhandle = NDNHandle.open();
        try {
            // The local copy wrapper API isn't applicable in very many situations. Try this instead.
            ContentName testName = new ContentName(testHelper.getTestNamespace("testLocalCopyListener"),
                    collectionObjName);

            LocalCopyListener copyListener = new LocalCopyListener();

            // Would like to test functionality that arranges for existing version to get written
            // to the repo, but can't do that without a flosser or using a flow server.
            NDNStringObject so = new NDNStringObject(testName, rhandle);
            so.addListener(copyListener);

            Assert.assertFalse(so.available());

            class Record {
                boolean callback = false;
            }
            Record record = new Record();

            class Listener implements UpdateListener {
                Record _rec;

                public Listener(Record r) {
                    _rec = r;
                }

                public void newVersionAvailable(NDNNetworkObject<?> newVersion, boolean wasSave) {
                    synchronized (_rec) {
                        _rec.callback = true;
                        _rec.notifyAll();
                    }
                }
            }
            ;

            // ask for it in background
            so.updateInBackground(false, new Listener(record));

            NDNHandle thandle = NDNHandle.open();
            try {
                NDNStringObject sowrite = new NDNStringObject(testName, "Now we write", SaveType.RAW, thandle);
                setupNamespace(testName);
                saveAndLog("Delayed write", sowrite, null, "Now we write");
                TestUtils.checkObject(thandle, sowrite);
                so.waitForData();
                Assert.assertTrue(so.available());
                Assert.assertEquals(so.string(), sowrite.string());
                Assert.assertEquals(so.getVersionedName(), sowrite.getVersionedName());

                new Waiter(UPDATE_TIMEOUT) {
                    @Override
                    protected boolean check(Object o, Object check) throws Exception {
                        return ((Record) o).callback;
                    }
                }.wait(record, record);
                Assert.assertEquals(true, record.callback);
                if (!RepositoryControl.localRepoSync(rhandle, so)) {
                    Thread.sleep(SystemConfiguration.MEDIUM_TIMEOUT);
                    // Should be in the repo by now
                    Assert.assertTrue(RepositoryControl.localRepoSync(rhandle, so));
                }
            } finally {
                thandle.close();
            }
        } finally {
            rhandle.close();
            KeyManager.closeDefaultKeyManager();
        }

        Log.info(Log.FAC_TEST, "Completed testLocalCopyListener");

    }
}