diff options
Diffstat (limited to 'src/test')
17 files changed, 3245 insertions, 0 deletions
diff --git a/src/test/java/com/amazon/carbonado/TestStorables.java b/src/test/java/com/amazon/carbonado/TestStorables.java new file mode 100644 index 0000000..90048d7 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/TestStorables.java @@ -0,0 +1,2263 @@ +/* + * Copyright 2006 Amazon Technologies, Inc. or its affiliates. + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks + * of Amazon Technologies, Inc. or its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazon.carbonado; + +import java.util.Comparator; +import java.util.List; + +import junit.framework.TestCase; + +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; + +import com.amazon.carbonado.ConstraintException; +import com.amazon.carbonado.Cursor; +import com.amazon.carbonado.FetchException; +import com.amazon.carbonado.FetchNoneException; +import com.amazon.carbonado.OptimisticLockException; +import com.amazon.carbonado.PersistException; +import com.amazon.carbonado.PersistMultipleException; +import com.amazon.carbonado.PersistNoneException; +import com.amazon.carbonado.PrimaryKey; +import com.amazon.carbonado.Query; +import com.amazon.carbonado.Repository; +import com.amazon.carbonado.RepositoryException; +import com.amazon.carbonado.Storable; +import com.amazon.carbonado.Storage; +import com.amazon.carbonado.Trigger; +import com.amazon.carbonado.UniqueConstraintException; + +import com.amazon.carbonado.cursor.SortedCursor; + +import com.amazon.carbonado.spi.RepairExecutor; +import com.amazon.carbonado.spi.WrappedSupport; + +import com.amazon.carbonado.stored.*; + +/** + * Runs an extensive set of acceptance tests for a repository. Must be + * subclassed to specify a repository to use. + * + * @author Don Schneider + * @author Brian S O'Neill + */ +public abstract class TestStorables extends TestCase { + + public static final long sSetId = 0x1L << 0; // 0x0001 + public static final long sGetStringProp = 0x1L << 1; // 0x0002 + public static final long sSetStringProp = 0x1L << 2; // 0x0004 + public static final long sGetIntProp = 0x1L << 3; // 0x0008 + public static final long sSetIntProp = 0x1L << 4; // 0x0010 + public static final long sGetLongProp = 0x1L << 5; // 0x0020 + public static final long sSetLongProp = 0x1L << 6; // 0x0040 + public static final long sGetDoubleProp = 0x1L << 7; // 0x0080 + public static final long sSetDoubleProp = 0x1L << 8; // 0x0100 + public static final long sLoad = 0x1L << 9; // 0x0200 + public static final long sTryLoad = 0x1L << 10; // 0x0400 + public static final long sInsert = 0x1L << 11; // 0x0800 + public static final long sTryInsert = 0x1L << 31; // 0x8000 0000 + public static final long sUpdate = 0x1L << 32; // 0x0001 0000 0000 + public static final long sTryUpdate = 0x1L << 12; // 0x1000 + public static final long sDelete = 0x1L << 33; // 0x0002 0000 0000 + public static final long sTryDelete = 0x1L << 13; // 0x2000 + public static final long sStorage = 0x1L << 14; // 0x4000 + public static final long sCopy = 0x1L << 15; // 0x8000 + public static final long sToStringKeyOnly = 0x1L << 16; // 0x0001 0000 + public static final long sGetId = 0x1L << 17; // 0x0002 0000 + public static final long sCopyAllProperties = 0x1L << 18; // 0x0004 0000 + public static final long sCopyPrimaryKeyProperties = 0x1L << 19; // 0x0080 0000 + public static final long sCopyUnequalProperties = 0x1L << 20; // 0x0010 0000 + public static final long sCopyDirtyProperties = 0x1L << 21; // 0x0020 0000 + public static final long sHasDirtyProperties = 0x1L << 25; // 0x0040 0000 + public static final long sEqualKeys = 0x1L << 22; // 0x0080 0000 + public static final long sEqualProperties = 0x1L << 23; // 0x0100 0000 + public static final long sCopyVersionProperty = 0x1L << 24; // 0x0200 0000 + public static final long sMarkPropertiesClean = 0x1L << 26; // 0x0400 0000 + public static final long sMarkAllPropertiesClean = 0x1L << 27; // 0x0800 0000 + public static final long sMarkPropertiesDirty = 0x1L << 28; // 0x1000 0000 + public static final long sMarkAllPropertiesDirty = 0x1L << 29; // 0x2000 0000 + public static final long sStorableType = 0x1L << 30; // 0x4000 0000 + + public static final long ALL_SET_METHODS = // 0x00000155; + sSetId + sSetStringProp + sSetIntProp + sSetLongProp + sSetDoubleProp; + public static final long ALL_GET_METHODS = // 0x000200AA; + sGetId + sGetStringProp + sGetIntProp + sGetLongProp + sGetDoubleProp; + public static final long ALL_PRIMARY_KEYS = sSetId; // 0x00000001; + public static final long ALL_COPY_PROP_METHODS = // 0x003C0000; + sCopyAllProperties + sCopyPrimaryKeyProperties + sCopyUnequalProperties + + sCopyDirtyProperties + sCopyVersionProperty; + public static final long ALL_INTERFACE_METHODS = // 0x43C1FE00; + sLoad + sTryLoad + sInsert + sUpdate + sDelete + sStorage + sCopy + sToStringKeyOnly + + ALL_COPY_PROP_METHODS + sHasDirtyProperties + sEqualKeys + sEqualProperties + + sMarkPropertiesClean + sMarkPropertiesDirty + + sMarkAllPropertiesClean + sMarkAllPropertiesDirty + sStorableType; + + private Repository mRepository; + private static int s_Ids = 0; + + public TestStorables(String s) { + super(s); + } + + protected void setUp() throws Exception { + super.setUp(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + if (mRepository != null) { + // The test may have thrown exceptions which cause some + // repositories to kick off asynchronous repairs. They will + // immediately fail since the repository is about to be + // closed. This just eliminates uninteresting errors from being + // logged. + try { + RepairExecutor.waitForRepairsToFinish(10000); + } + catch (InterruptedException e) { + } + + mRepository.close(); + mRepository = null; + } + } + + /** + * Subclasses must implement this method to specify a repository. + */ + protected abstract Repository newRepository(boolean isMaster) + throws RepositoryException; + + /** + * provide subsequent access to the repository so the tests can do fancy things if + * interested. + * @return + */ + protected Repository getRepository() throws RepositoryException { + if (mRepository == null) { + mRepository = newRepository(true); + } + return mRepository; + } + + /** + * Create a random ID to eliminate optimistic lock conflicts due to ID collisions + * @param seed + * @return + */ + private int generateId(int seed) { + return seed*10000 + ((int)System.currentTimeMillis()) + s_Ids++; + } + + public void test_createAndRetrieve() throws Exception { + Storage<StorableTestBasic> storageSB = + getRepository().storageFor(StorableTestBasic.class); + + StorableTestBasic sb = storageSB.prepare(); + final int id = generateId(0); + sb.setId(id); + sb.setIntProp(1); + sb.setLongProp(1); + sb.setDoubleProp(1.1); + sb.setStringProp("one"); + sb.setDate(new DateTime("2005-08-26T08:09:00.000")); + sb.insert(); + + StorableTestBasic sb_load = storageSB.prepare(); + sb_load.setId(id); + sb_load.load(); + assertEquals(sb, sb_load); + + // Try re-inserting + // First, make sure the system disallows setting pk for loaded object + try { + sb.setId(id); + fail("successfully set pk on loaded object"); + } + catch (Exception e) { + } + + // Then the more common way: just create an identical new one + StorableTestBasic sbr = storageSB.prepare(); + sbr.setId(id); + sbr.setIntProp(1); + sbr.setLongProp(1); + sbr.setDoubleProp(1.1); + sbr.setStringProp("one"); + sb.setDate(new DateTime("2005-08-26T08:09:00.000")); + try { + sbr.insert(); + fail("PK constraint violation ignored"); + } + catch (UniqueConstraintException e) { + } + + + Storage<StorableTestMultiPK> storageMPK = + getRepository().storageFor(StorableTestMultiPK.class); + StorableTestMultiPK smpk = storageMPK.prepare(); + smpk.setIdPK(0); + smpk.setStringPK("zero"); + smpk.setStringData("and some data"); + smpk.insert(); + + StorableTestMultiPK smpk_load = storageMPK.prepare(); + smpk_load.setIdPK(0); + smpk_load.setStringPK("zero"); + smpk_load.load(); + assertEquals(smpk, smpk_load); + } + + public void test_storableStorableStates() throws Exception { + + Storage<StorableTestKeyValue> storageMinimal = + getRepository().storageFor(StorableTestKeyValue.class); + + // Start by just putting some targets in the repository + for (int i = 0; i < 10; i++) { + insert(storageMinimal, 100+i, 200+i); + } + + StorableTestKeyValue s = storageMinimal.prepare(); + StorableTestKeyValue s2 = storageMinimal.prepare(); + + // State: unloaded + // pk incomplete + assertInoperable(s, "new - untouched"); + assertFalse(s.hasDirtyProperties()); + + // new --set(pk)--> loadable incomplete + s.setKey1(0); + assertInoperable(s, "Loadable Incomplete"); + assertFalse(s.hasDirtyProperties()); + + s.setKey1(101); + assertInoperable(s, "loadable incomplete (2nd)"); + assertFalse(s.hasDirtyProperties()); + + // loadable incomplete --pkFilled--> loadable ready + s.setKey2(201); + assertEquals(true, s.tryDelete()); + assertFalse(s.hasDirtyProperties()); + + s.setKey1(102); + s.setKey2(202); + assertEquals(true, s.tryDelete()); + assertFalse(s.hasDirtyProperties()); + + // loadable ready --load()--> loaded + s.setKey1(103); + s.setKey2(203); + s.load(); + assertEquals(s.getValue1(), 1030); + assertEquals(s.getValue2(), 20300); + assertNoInsert(s, "written"); + assertNoSetPK(s, "written"); + assertFalse(s.hasDirtyProperties()); + + s2.setKey1(103); + s2.setKey2(203); + assertFalse(s2.hasDirtyProperties()); + s2.load(); + assertFalse(s2.hasDirtyProperties()); + + assertTrue(s.equalPrimaryKeys(s2)); + assertTrue(s.equalProperties(s2)); + assertEquals(s.storableType(), s2.storableType()); + assertTrue(s.equals(s2)); + assertEquals(s, s2); + s.setValue1(11); + s.setValue2(11111); + assertEquals(true, s.tryUpdate()); + assertEquals(11, s.getValue1()); + assertEquals(11111, s.getValue2()); + s2.load(); + assertEquals(s, s2); + + StorableTestKeyValue s3 = storageMinimal.prepare(); + s.copyPrimaryKeyProperties(s3); + s3.tryUpdate(); + assertEquals(s, s3); + + s.setValue2(222222); + assertTrue(s.hasDirtyProperties()); + s.load(); + assertFalse(s.hasDirtyProperties()); + assertEquals(s, s2); + + // Update should return true, even though it probably didn't actually + // touch the storage layer. + assertEquals(true, s.tryUpdate()); + + s.tryDelete(); + assertNoLoad(s, "deleted"); + // After delete, saved properties remain dirty. + assertTrue(s.hasDirtyProperties()); + + s.insert(); + assertFalse(s.hasDirtyProperties()); + s.load(); + assertFalse(s.hasDirtyProperties()); + assertEquals(s, s2); + } + + public void test_storableInteractions() throws Exception { + Storage<StorableTestBasic> storage = getRepository().storageFor(StorableTestBasic.class); + StorableTestBasic s = storage.prepare(); + final int id = generateId(111); + s.setId(id); + s.initBasicProperties(); + assertTrue(s.hasDirtyProperties()); + s.insert(); + assertFalse(s.hasDirtyProperties()); + + StorableTestBasic s2 = storage.prepare(); + s2.setId(id); + s2.load(); + assertTrue(s2.equalPrimaryKeys(s)); + assertTrue(s2.equalProperties(s)); + assertTrue(s2.equals(s)); + + StorableTestBasic s3 = storage.prepare(); + s3.setId(id); + s3.tryDelete(); + + // Should be able to re-insert. + s2.insert(); + s2.load(); + assertTrue(s2.equalPrimaryKeys(s)); + assertTrue(s2.equalProperties(s)); + assertTrue(s2.equals(s)); + + // Delete in preparation for next test. + s3.tryDelete(); + + s.setStringProp("updated by s"); + // The object is gone, we can't update it + assertEquals(false, s.tryUpdate()); + // ...or load it + try { + s2.load(); + fail("shouldn't be able to load a deleted object"); + } + catch (FetchException e) { + } + } + + public void test_assymetricStorable() throws Exception { + try { + Storage<StorableTestAssymetric> storage = + getRepository().storageFor(StorableTestAssymetric.class); + } + catch (Exception e) { + fail("exception creating storable with assymetric concrete getter?" + e); + } + try { + Storage<StorableTestAssymetricGet> storage = + getRepository().storageFor(StorableTestAssymetricGet.class); + fail("Created assymetric storabl`e"); + } + catch (Exception e) { + } + try { + Storage<StorableTestAssymetricSet> storage = + getRepository().storageFor(StorableTestAssymetricSet.class); + fail("Created assymetric storable"); + } + catch (Exception e) { + } + try { + Storage<StorableTestAssymetricGetSet> storage = + getRepository().storageFor(StorableTestAssymetricGetSet.class); + fail("Created assymetric storable"); + } + catch (Exception e) { + } + } + + private void assertNoSetPK(StorableTestKeyValue aStorableTestKeyValue, + String aState) + { + try { + aStorableTestKeyValue.setKey1(1111); + fail("'set pk' for '" + aState + "' succeeded"); + } + catch (Exception e) { + } + } + + private void assertInoperable(Storable aStorable, String aState) { + assertNoInsert(aStorable, aState); + assertNoUpdate(aStorable, aState); + assertNoLoad(aStorable, aState); + assertNoDelete(aStorable, aState); + } + + private void assertNoDelete(Storable aStorable, String aState) { + try { + aStorable.tryDelete(); + fail("'delete' for '" + aState + "' succeeded"); + } + catch (PersistException e) { + } + catch (IllegalStateException e) { + } + } + + private void assertNoLoad(Storable aStorable, String aState) { + try { + aStorable.load(); + fail("'load' for '" + aState + "' succeeded"); + } + catch (FetchException e) { + } + catch (IllegalStateException e) { + } + } + + private void assertNoInsert(Storable aStorable, String aState) { + try { + aStorable.insert(); + fail("'insert' for '" + aState + "' succeeded"); + } + catch (PersistException e) { + } + catch (IllegalStateException e) { + } + } + + private void assertNoUpdate(Storable aStorable, String aState) { + try { + aStorable.tryUpdate(); + fail("'update' for '" + aState + "' succeeded"); + } + catch (PersistException e) { + } + catch (IllegalStateException e) { + } + } + + private void insert(Storage<StorableTestKeyValue> aStorageMinimal, + int key1, + int key2) throws PersistException + { + StorableTestKeyValue s = aStorageMinimal.prepare(); + s.setKey1(key1); + s.setKey2(key2); + s.setValue1(key1*10); + s.setValue2(key2*100); + s.insert(); + } + + public void test_copyStorableProperties() throws Exception { + Storage<StorableTestBasic> storage = getRepository().storageFor(StorableTestBasic.class); + StorableTestBasic storable = storage.prepare(); + + InvocationTracker tracker = new InvocationTracker("tracker", false); + storable.copyAllProperties(tracker); + // unloaded, untouched; nothing happens + tracker.assertTrack(0); + + storable.setId(generateId(1)); + storable.setIntProp(1); + storable.copyAllProperties(tracker); + tracker.assertTrack(0x1 + 0x10); + tracker.clearTracks(); + + storable.initBasicProperties(); + storable.copyAllProperties(tracker); + tracker.assertTrack(ALL_SET_METHODS); + tracker.clearTracks(); + + storable = storage.prepare(); + storable.copyPrimaryKeyProperties(tracker); + tracker.assertTrack(0); + storable.initPrimaryKeyProperties(); + storable.copyPrimaryKeyProperties(tracker); + tracker.assertTrack(ALL_PRIMARY_KEYS); + tracker.clearTracks(); + + storable = storage.prepare(); + storable.copyUnequalProperties(tracker); + tracker.assertTrack(0); + storable.setIntProp(0); // this will now be dirty, and equal + storable.copyUnequalProperties(tracker); + tracker.assertTrack(0x8); + storable.setIntProp(1); // this will now be dirty and not equal + storable.copyUnequalProperties(tracker); + tracker.assertTrack(0x8 | 0x10); + + // get a fresh one + storable = storage.prepare(); + storable.setStringProp("hi"); + storable.setId(22); + storable.copyPrimaryKeyProperties(tracker); + storable.copyDirtyProperties(tracker); + tracker.assertTrack(0x05); + } + + public void test_copy() throws Exception { + Storage<StorableTestBasic> storage = getRepository().storageFor(StorableTestBasic.class); + StorableTestBasic storable = storage.prepare(); + + storable.setId(5); + storable.setStringProp("hello"); + storable.setIntProp(512); + storable.setLongProp(45354L); + storable.setDoubleProp(56734.234); + + Storable copy = storable.copy(); + + assertEquals(storable.getClass(), copy.getClass()); + assertEquals(storable, copy); + + StorableTestBasic castedCopy = (StorableTestBasic) copy; + + assertEquals(storable.getId(), castedCopy.getId()); + assertEquals(storable.getStringProp(), castedCopy.getStringProp()); + assertEquals(storable.getIntProp(), castedCopy.getIntProp()); + assertEquals(storable.getLongProp(), castedCopy.getLongProp()); + assertEquals(storable.getDoubleProp(), castedCopy.getDoubleProp()); + } + + public void test_invalidStorables() throws Exception { + try { + getRepository().storageFor(StorableTestInvalid.class); + fail("prepared invalid storable"); + } + catch (RepositoryException e) { + } + } + + public void test_invalidPatterns() throws Exception { + // Minimal -- try setting with no PK + Storage<StorableTestMinimal> storageMinimal = + getRepository().storageFor(StorableTestMinimal.class); + StorableTestMinimal s = storageMinimal.prepare(); + assertNoInsert(s, "new (minimal)"); + + s.setId(generateId(0)); + s.insert(); + + // Basic -- try combinations of PK, fields + // First, fill in all the fields but no PK + Storage<StorableTestBasic> storageBasic = + getRepository().storageFor(StorableTestBasic.class); + StorableTestBasic sb = storageBasic.prepare(); + assertNoInsert(sb, "new (basic)");; + + sb.setIntProp(0); + sb.setIntProp(1); + sb.setLongProp(1); + sb.setDoubleProp(1.1); + sb.setStringProp("one"); + sb.setDate(new DateTime("2005-08-26T08:09:00.000")); + assertNoInsert(sb, "SB: Storable incomplete (pkMissing)"); + + sb.setId(generateId(2)); + sb.insert(); + + // Now try leaving one of the fields empty. + sb = storageBasic.prepare(); + final int id = generateId(3); + sb.setId(id); + sb.setIntProp(0); + sb.setIntProp(1); + sb.setLongProp(1); + sb.setDoubleProp(1.1); + try { + sb.insert(); + fail(); + } catch (ConstraintException e) { + } + + sb = storageBasic.prepare(); + sb.setId(id); + try { + sb.load(); + fail(); + } catch (FetchNoneException e) { + } + } + + public void test_nonDestructiveUpdate() throws Exception { + Storage<StorableTestBasic> storage = getRepository().storageFor(StorableTestBasic.class); + StorableTestBasic s = storage.prepare(); + + int id = generateId(3943945); + s.setId(id); + s.setStringProp("hello"); + s.setIntProp(56); + s.setLongProp(99999999999999999L); + s.setDoubleProp(Double.NaN); + + s.insert(); + + s = storage.prepare(); + + s.setId(id); + s.setIntProp(100); + assertEquals(true, s.tryUpdate()); + + assertEquals("hello", s.getStringProp()); + assertEquals(100, s.getIntProp()); + assertEquals(99999999999999999L, s.getLongProp()); + assertEquals(Double.NaN, s.getDoubleProp()); + + s = storage.prepare(); + + s.setId(id); + s.load(); + + assertEquals("hello", s.getStringProp()); + assertEquals(100, s.getIntProp()); + assertEquals(99999999999999999L, s.getLongProp()); + assertEquals(Double.NaN, s.getDoubleProp()); + } + + public void test_updateLoadSideEffect() throws Exception { + Storage<StorableTestBasic> storage = getRepository().storageFor(StorableTestBasic.class); + + StorableTestBasic s = storage.prepare(); + final int id = generateId(500); + s.setId(id); + s.setStringProp("hello"); + s.setIntProp(10); + s.setLongProp(123456789012345L); + s.setDoubleProp(Double.POSITIVE_INFINITY); + s.insert(); + + s = storage.prepare(); + s.setId(id); + + assertEquals(id, s.getId()); + assertEquals(0, s.getIntProp()); + assertEquals(0L, s.getLongProp()); + assertEquals(0.0, s.getDoubleProp()); + + assertFalse(s.hasDirtyProperties()); + + // Even if nothing was updated, must load fresh copy. + assertTrue(s.tryUpdate()); + assertEquals(id, s.getId()); + assertEquals(10, s.getIntProp()); + assertEquals(123456789012345L, s.getLongProp()); + assertEquals(Double.POSITIVE_INFINITY, s.getDoubleProp()); + } + + public void test_versioning() throws Exception { + Storage<StorableVersioned> storage = getRepository().storageFor(StorableVersioned.class); + + StorableVersioned s = storage.prepare(); + s.setID(500); + s.setValue("hello"); + try { + // Require version property to be set. + s.tryUpdate(); + fail(); + } catch (IllegalStateException e) { + } + + s.setVersion(1); + try { + // Cannot update that which does not exist. + s.update(); + fail(); + } catch (PersistNoneException e) { + } + + s.insert(); + + s.setVersion(2); + try { + // Record version mismatch. + s.tryUpdate(); + fail(); + } catch (OptimisticLockException e) { + } + + s.setVersion(1); + s.setValue("world"); + s.tryUpdate(); + + assertEquals(2, s.getVersion()); + assertEquals("world", s.getValue()); + + // Since no properties changed, update does not increase version. + assertEquals(true, s.tryUpdate()); + assertEquals(2, s.getVersion()); + + // Simple test to ensure that version property doesn't need to be + // dirtied. + s = storage.prepare(); + s.setID(500); + s.load(); + s.setValue("hello"); + assertTrue(s.tryUpdate()); + } + + public void test_versioningWithLong() throws Exception { + Storage<StorableVersionedWithLong> storage = + getRepository().storageFor(StorableVersionedWithLong.class); + + StorableVersionedWithLong s = storage.prepare(); + s.setID(500); + s.setValue("hello"); + try { + // Require version property to be set. + s.tryUpdate(); + fail(); + } catch (IllegalStateException e) { + } + + s.setVersion(1); + try { + // Cannot update that which does not exist. + s.update(); + fail(); + } catch (PersistNoneException e) { + } + + s.insert(); + + s.setVersion(2); + try { + // Record version mismatch. + s.tryUpdate(); + fail(); + } catch (OptimisticLockException e) { + } + + s.setVersion(1); + s.setValue("world"); + s.tryUpdate(); + + assertEquals(2, s.getVersion()); + assertEquals("world", s.getValue()); + + // Since no properties changed, update does not increase version. + assertEquals(true, s.tryUpdate()); + assertEquals(2, s.getVersion()); + } + + public void test_versioningWithObj() throws Exception { + Storage<StorableVersionedWithObj> storage = + getRepository().storageFor(StorableVersionedWithObj.class); + + StorableVersionedWithObj s = storage.prepare(); + s.setID(generateId(500)); + s.setValue("hello"); + try { + // Require version property to be set. + s.tryUpdate(); + fail(); + } catch (IllegalStateException e) { + } + + s.setVersion(null); + try { + // Cannot update that which does not exist. + s.update(); + fail(); + } catch (PersistNoneException e) { + } + + s.insert(); + + assertNull(s.getVersion()); + + s.setVersion(2); + try { + // Record version mismatch. + s.tryUpdate(); + fail(); + } catch (OptimisticLockException e) { + } + + s.setVersion(null); + s.setValue("world"); + s.tryUpdate(); + + assertEquals((Integer) 1, s.getVersion()); + assertEquals("world", s.getValue()); + + s.setValue("value"); + s.tryUpdate(); + + assertEquals((Integer) 2, s.getVersion()); + assertEquals("value", s.getValue()); + + // Since no properties changed, update does not increase version. + assertEquals(true, s.tryUpdate()); + assertEquals((Integer) 2, s.getVersion()); + } + + public void test_versioningWithLongObj() throws Exception { + Storage<StorableVersionedWithLongObj> storage = + getRepository().storageFor(StorableVersionedWithLongObj.class); + + StorableVersionedWithLongObj s = storage.prepare(); + s.setID(500); + s.setValue("hello"); + try { + // Require version property to be set. + s.tryUpdate(); + fail(); + } catch (IllegalStateException e) { + } + + s.setVersion(null); + try { + // Cannot update that which does not exist. + s.update(); + fail(); + } catch (PersistNoneException e) { + } + + s.insert(); + + assertNull(s.getVersion()); + + s.setVersion(2L); + try { + // Record version mismatch. + s.tryUpdate(); + fail(); + } catch (OptimisticLockException e) { + } + + s.setVersion(null); + s.setValue("world"); + s.tryUpdate(); + + assertEquals((Long) 1L, s.getVersion()); + assertEquals("world", s.getValue()); + + s.setValue("value"); + s.tryUpdate(); + + assertEquals((Long) 2L, s.getVersion()); + assertEquals("value", s.getValue()); + + // Since no properties changed, update does not increase version. + assertEquals(true, s.tryUpdate()); + assertEquals((Long) 2L, s.getVersion()); + } + + public void test_initialVersion() throws Exception { + Storage<StorableVersioned> storage = getRepository().storageFor(StorableVersioned.class); + + StorableVersioned s = storage.prepare(); + s.setID(987); + s.setValue("hello"); + assertEquals(0, s.getVersion()); + s.insert(); + assertEquals(1, s.getVersion()); + + s = storage.prepare(); + s.setID(12345); + s.setValue("world"); + assertEquals(0, s.getVersion()); + s.setVersion(56); + assertEquals(56, s.getVersion()); + s.insert(); + assertEquals(56, s.getVersion()); + } + + public void test_initialVersionWithLong() throws Exception { + Storage<StorableVersionedWithLong> storage = + getRepository().storageFor(StorableVersionedWithLong.class); + + StorableVersionedWithLong s = storage.prepare(); + s.setID(987); + s.setValue("hello"); + assertEquals(0, s.getVersion()); + s.insert(); + assertEquals(1, s.getVersion()); + + s = storage.prepare(); + s.setID(12345); + s.setValue("world"); + assertEquals(0, s.getVersion()); + s.setVersion(56); + assertEquals(56, s.getVersion()); + s.insert(); + assertEquals(56, s.getVersion()); + } + + public void test_initialVersionWithObj() throws Exception { + Storage<StorableVersionedWithObj> storage = + getRepository().storageFor(StorableVersionedWithObj.class); + + StorableVersionedWithObj s = storage.prepare(); + s.setID(987); + s.setValue("hello"); + assertNull(s.getVersion()); + s.insert(); + assertEquals((Integer) 1, s.getVersion()); + + s = storage.prepare(); + s.setID(12345); + s.setValue("world"); + assertNull(s.getVersion()); + s.setVersion(56); + assertEquals((Integer) 56, s.getVersion()); + s.insert(); + assertEquals((Integer) 56, s.getVersion()); + } + + public void test_initialVersionWithLongObj() throws Exception { + Storage<StorableVersionedWithLongObj> storage = + getRepository().storageFor(StorableVersionedWithLongObj.class); + + StorableVersionedWithLongObj s = storage.prepare(); + s.setID(987); + s.setValue("hello"); + assertNull(s.getVersion()); + s.insert(); + assertEquals((Long) 1L, s.getVersion()); + + s = storage.prepare(); + s.setID(12345); + s.setValue("world"); + assertNull(s.getVersion()); + s.setVersion(56L); + assertEquals((Long) 56L, s.getVersion()); + s.insert(); + assertEquals((Long) 56L, s.getVersion()); + } + + public void test_versioningMissingRecord() throws Exception { + Storage<StorableVersioned> storage = getRepository().storageFor(StorableVersioned.class); + + StorableVersioned s = storage.prepare(); + s.setID(500); + s.setValue("hello"); + s.insert(); + + // Now delete it from under our feet. + StorableVersioned s2 = storage.prepare(); + s2.setID(500); + s2.delete(); + + s.setValue("world"); + s.tryUpdate(); + + s.insert(); + + // Delete it again. + s2.delete(); + + // Update without changing and properties must still reload, which should fail. + assertFalse(s.tryUpdate()); + } + + public void test_versioningDisabled() throws Exception { + // Make sure repository works properly when configured as non-master. + Repository repo = newRepository(false); + Storage<StorableVersioned> storage = repo.storageFor(StorableVersioned.class); + + StorableVersioned s = storage.prepare(); + s.setID(500); + s.setValue("hello"); + try { + // Require version property to be set. + s.tryUpdate(); + fail(); + } catch (IllegalStateException e) { + } + + s.setVersion(1); + assertEquals(false, s.tryUpdate()); + + s.insert(); + + s.setVersion(2); + assertEquals(true, s.tryUpdate()); + + s.setVersion(1); + s.setValue("world"); + s.tryUpdate(); + + assertEquals(1, s.getVersion()); + assertEquals("world", s.getValue()); + + RepairExecutor.waitForRepairsToFinish(10000); + + repo.close(); + repo = null; + } + + public void test_sequences() throws Exception { + Storage<StorableSequenced> storage = getRepository().storageFor(StorableSequenced.class); + + StorableSequenced seq = storage.prepare(); + seq.setData("hello"); + seq.insert(); + + assertEquals(1L, seq.getID()); + assertEquals(1, seq.getSomeInt()); + assertEquals(Integer.valueOf(1), seq.getSomeIntegerObj()); + assertEquals(1L, seq.getSomeLong()); + assertEquals(Long.valueOf(1L), seq.getSomeLongObj()); + assertEquals("1", seq.getSomeString()); + assertEquals("hello", seq.getData()); + + seq = storage.prepare(); + seq.setData("foo"); + seq.insert(); + + assertEquals(2L, seq.getID()); + assertEquals(2, seq.getSomeInt()); + assertEquals(Integer.valueOf(2), seq.getSomeIntegerObj()); + assertEquals(2L, seq.getSomeLong()); + assertEquals(Long.valueOf(2L), seq.getSomeLongObj()); + assertEquals("2", seq.getSomeString()); + assertEquals("foo", seq.getData()); + + seq = storage.prepare(); + seq.setSomeInt(100); + seq.setSomeLongObj(null); + seq.setData("data"); + seq.insert(); + + assertEquals(3L, seq.getID()); + assertEquals(100, seq.getSomeInt()); + assertEquals(null, seq.getSomeLongObj()); + + seq = storage.prepare(); + seq.setData("world"); + seq.insert(); + + assertEquals(4L, seq.getID()); + assertEquals(3, seq.getSomeInt()); + assertEquals(Integer.valueOf(4), seq.getSomeIntegerObj()); + assertEquals(4L, seq.getSomeLong()); + assertEquals(Long.valueOf(3L), seq.getSomeLongObj()); + assertEquals("4", seq.getSomeString()); + } + + public void test_oldIndexEntryDeletion() throws Exception { + // Very simple test that ensures that old index entries are deleted + // when the master record is updated. There is no guarantee that the + // chosen repository supports indexes, and there is no guarantee that + // it is selecting the desired index. Since the index set is simple and + // so are the queries, I think it is safe to assume that the selected + // index is what I expect it to be. Repositories that support + // custom indexing should have more rigorous tests. + + Storage<StorableTestBasicIndexed> storage = + getRepository().storageFor(StorableTestBasicIndexed.class); + + StorableTestBasicIndexed s = storage.prepare(); + final int id1 = generateId(1); + s.setId(id1); + s.setStringProp("hello"); + s.setIntProp(3); + s.setLongProp(4); + s.setDoubleProp(5); + s.insert(); + + s = storage.prepare(); + final int id6 = generateId(6); + s.setId(id6); + s.setStringProp("hello"); + s.setIntProp(8); + s.setLongProp(9); + s.setDoubleProp(10); + s.insert(); + + s = storage.prepare(); + final int id11 = generateId(11); + s.setId(id11); + s.setStringProp("world"); + s.setIntProp(3); + s.setLongProp(14); + s.setDoubleProp(15); + s.insert(); + + // First verify that queries report what we expect. Don't perform an + // orderBy on query, as that might interfere with index selection. + Query<StorableTestBasicIndexed> q = storage.query("stringProp = ?").with("hello"); + List<StorableTestBasicIndexed> list = q.fetch().toList(); + assertEquals(2, list.size()); + if (list.get(0).getId() == id1) { + assertEquals(id6, list.get(1).getId()); + } else { + assertEquals(id6, list.get(0).getId()); + } + + q = storage.query("stringProp = ?").with("world"); + list = q.fetch().toList(); + assertEquals(1, list.size()); + assertEquals(id11, list.get(0).getId()); + + q = storage.query("intProp = ?").with(3); + list = q.fetch().toList(); + assertEquals(2, list.size()); + if (list.get(0).getId() == id1) { + assertEquals(id11, list.get(1).getId()); + } else { + assertEquals(id11, list.get(0).getId()); + } + + // Now update and verify changes to query results. + s = storage.prepare(); + s.setId(id1); + s.load(); + s.setStringProp("world"); + s.tryUpdate(); + + q = storage.query("stringProp = ?").with("hello"); + list = q.fetch().toList(); + assertEquals(1, list.size()); + assertEquals(id6, list.get(0).getId()); + + q = storage.query("stringProp = ?").with("world"); + list = q.fetch().toList(); + assertEquals(2, list.size()); + if (list.get(0).getId() == id1) { + assertEquals(id11, list.get(1).getId()); + } else { + assertEquals(id11, list.get(0).getId()); + } + + q = storage.query("intProp = ?").with(3); + list = q.fetch().toList(); + assertEquals(2, list.size()); + if (list.get(0).getId() == id1) { + assertEquals(id11, list.get(1).getId()); + } else { + assertEquals(id11, list.get(0).getId()); + } + } + + public void test_falseDoubleUpdate() throws Exception { + Storage<StorableTestBasic> storage = getRepository().storageFor(StorableTestBasic.class); + + StorableTestBasic s = storage.prepare(); + s.setId(56789); + + assertFalse(s.tryUpdate()); + assertFalse(s.tryUpdate()); + } + + public void test_dateTimeIndex() throws Exception { + Storage<StorableDateIndex> storage = getRepository().storageFor(StorableDateIndex.class); + + DateTimeZone original = DateTimeZone.getDefault(); + // Set time zone different than defined in storable. + DateTimeZone.setDefault(DateTimeZone.forID("America/Los_Angeles")); + try { + DateTime now = new DateTime(); + + StorableDateIndex sdi = storage.prepare(); + sdi.setID(1); + sdi.setOrderDate(now); + sdi.insert(); + + sdi.load(); + + assertEquals(now.getMillis(), sdi.getOrderDate().getMillis()); + // Time zones will differ, since adapter is applied upon load. + assertFalse(now.equals(sdi.getOrderDate())); + + Query<StorableDateIndex> query = storage.query("orderDate=?").with(now); + // Tests that adapter is applied to index. Otherwise, consistency + // check will reject loaded storable. + StorableDateIndex sdi2 = query.tryLoadOne(); + assertNotNull(sdi2); + } finally { + DateTimeZone.setDefault(original); + } + } + + public void test_joinCache() throws Exception { + Storage<UserAddress> uaStorage = getRepository().storageFor(UserAddress.class); + Storage<UserInfo> uiStorage = getRepository().storageFor(UserInfo.class); + + UserAddress addr = uaStorage.prepare(); + addr.setAddressID(5); + addr.setLine1("123"); + addr.setCity("Seattle"); + addr.setState("WA"); + addr.setCountry("USA"); + addr.insert(); + + UserInfo user = uiStorage.prepare(); + user.setUserID(1); + user.setStateID(1); + user.setFirstName("John"); + user.setLastName("Smith"); + user.setAddress(addr); + + assertEquals("Seattle", user.getAddress().getCity()); + + user.insert(); + + addr.setCity("Bellevue"); + addr.tryUpdate(); + + // Should still refer to same address instance. + assertEquals("Bellevue", user.getAddress().getCity()); + + UserAddress addr2 = uaStorage.prepare(); + addr2.setAddressID(5); + addr2.setCity("Kirkland"); + addr2.tryUpdate(); + + // Should still refer to same address instance. + assertEquals("Bellevue", user.getAddress().getCity()); + + // Force reload of user should flush cache. + user.load(); + + assertEquals("Kirkland", user.getAddress().getCity()); + + addr2.setCity("Redmond"); + addr2.tryUpdate(); + + // Should still refer to same address instance. + assertEquals("Kirkland", user.getAddress().getCity()); + + // Update of user should flush cache (even if nothing changed) + assertEquals(true, user.tryUpdate()); + + assertEquals("Redmond", user.getAddress().getCity()); + + addr2.setCity("Renton"); + addr2.tryUpdate(); + + // Should still refer to same address instance. + assertEquals("Redmond", user.getAddress().getCity()); + + // Update of user should flush cache (when something changed) + user.setFirstName("Jim"); + assertEquals(true, user.tryUpdate()); + + assertEquals("Renton", user.getAddress().getCity()); + + addr2.setCity("Tacoma"); + addr2.tryUpdate(); + + // Should still refer to same address instance. + assertEquals("Renton", user.getAddress().getCity()); + + // Delete of user should flush cache + assertEquals(true, user.tryDelete()); + + assertEquals("Tacoma", user.getAddress().getCity()); + + addr2.setCity("Shoreline"); + addr2.tryUpdate(); + + // Should still refer to same address instance. + assertEquals("Tacoma", user.getAddress().getCity()); + + // Failed load of user should flush cache + assertEquals(false, user.tryLoad()); + + assertEquals("Shoreline", user.getAddress().getCity()); + + addr2.setCity("Vancouver"); + addr2.tryUpdate(); + + // Should still refer to same address instance. + assertEquals("Shoreline", user.getAddress().getCity()); + + // Insert of user should flush cache + user.insert(); + + assertEquals("Vancouver", user.getAddress().getCity()); + } + + public void test_updateReload() throws Exception { + Storage<UserInfo> uiStorage = getRepository().storageFor(UserInfo.class); + + UserInfo user = uiStorage.prepare(); + user.setUserID(1); + user.setStateID(1); + user.setFirstName("John"); + user.setLastName("Smith"); + user.setAddressID(0); + user.insert(); + + UserInfo user2 = uiStorage.prepare(); + user2.setUserID(1); + user2.setFirstName("Jim"); + user2.tryUpdate(); + + assertEquals("John", user.getFirstName()); + user.tryUpdate(); + assertEquals("Jim", user.getFirstName()); + + user2.setFirstName("Bob"); + user2.tryUpdate(); + + assertEquals("Jim", user.getFirstName()); + user.setLastName("Jones"); + user.tryUpdate(); + assertEquals("Bob", user.getFirstName()); + assertEquals("Jones", user.getLastName()); + } + + public void test_deleteState() throws Exception { + Storage<UserInfo> uiStorage = getRepository().storageFor(UserInfo.class); + + UserInfo user = uiStorage.prepare(); + user.setUserID(1); + user.setStateID(1); + user.setFirstName("John"); + user.setLastName("Smith"); + user.setAddressID(0); + user.insert(); + + UserInfo user2 = uiStorage.prepare(); + user2.setUserID(1); + user2.tryDelete(); + + assertFalse(user.tryLoad()); + + // Should be able to change pk now. + user.setUserID(2); + assertFalse(user.tryUpdate()); + user.setUserID(1); + user.insert(); + + user2.tryDelete(); + + assertFalse(user.tryUpdate()); + + // Should be able to change pk now. + user.setUserID(2); + assertFalse(user.tryUpdate()); + user.setUserID(1); + user.insert(); + user2.tryDelete(); + + user.setFirstName("Jim"); + assertFalse(user.tryUpdate()); + + // Should be able to change pk now. + user.setUserID(2); + assertFalse(user.tryUpdate()); + user.setUserID(1); + user.insert(); + assertEquals("Jim", user.getFirstName()); + } + + public void test_deleteUpdate() throws Exception { + Storage<UserInfo> uiStorage = getRepository().storageFor(UserInfo.class); + + UserInfo user = uiStorage.prepare(); + user.setUserID(1); + user.setStateID(1); + user.setFirstName("John"); + user.setLastName("Smith"); + user.setAddressID(0); + user.insert(); + + // Just want to change first name now + user.setFirstName("Bob"); + + // Concurrently, someone else deletes the user + UserInfo user2 = uiStorage.prepare(); + user2.setUserID(1); + assertTrue(user2.tryDelete()); + + assertFalse(user.tryUpdate()); + + // Update failed... perhaps we should try again... (wrong decision) + + // Concurrently, someone inserts a different user with re-used id. (wrong decision) + user2 = uiStorage.prepare(); + user2.setUserID(1); + user2.setStateID(1); + user2.setFirstName("Mike"); + user2.setLastName("Jones"); + user2.setAddressID(0); + user2.insert(); + + // Trying the failed update again should totally blow away the sneaked in insert. + assertTrue(user.tryUpdate()); + + assertEquals("Bob", user.getFirstName()); + assertEquals("Smith", user.getLastName()); + + // The failed update earlier dirtied all the properties, including ones + // that were not modified. As a result, the second update replaces all + // properties. This is a special edge case destructive update for which + // there is no clear solution. If just the one property was changed as + // instructed earlier, then the record would have been corrupted. This + // feels much worse. This can still happen if the record was not + // originally fully clean. + + // The cause of the error is the user creating a new record with a + // re-used id. This problem could be prevented using transactions, or + // it might be detected with optimistic locking. + } + + public void test_deleteOne() throws Exception { + Storage<UserInfo> uiStorage = getRepository().storageFor(UserInfo.class); + + UserInfo user = uiStorage.prepare(); + user.setUserID(1); + user.setStateID(1); + user.setFirstName("Bob"); + user.setLastName("Smith"); + user.setAddressID(0); + user.insert(); + + user = uiStorage.prepare(); + user.setUserID(2); + user.setStateID(1); + user.setFirstName("Bob"); + user.setLastName("Jones"); + user.setAddressID(0); + user.insert(); + + user = uiStorage.prepare(); + user.setUserID(3); + user.setStateID(1); + user.setFirstName("Indiana"); + user.setLastName("Jones"); + user.setAddressID(0); + user.insert(); + + try { + uiStorage.query("lastName = ?").with("Jones").deleteOne(); + fail(); + } catch (PersistMultipleException e) { + } + + List<UserInfo> list = uiStorage.query().fetch().toList(); + assertEquals(3, list.size()); + + uiStorage.query("lastName = ? & firstName = ?").with("Jones").with("Bob").deleteOne(); + + list = uiStorage.query().orderBy("userID").fetch().toList(); + assertEquals(2, list.size()); + + assertEquals("Bob", list.get(0).getFirstName()); + assertEquals("Smith", list.get(0).getLastName()); + assertEquals("Indiana", list.get(1).getFirstName()); + assertEquals("Jones", list.get(1).getLastName()); + } + + public void test_triggers() throws Exception { + Storage<StorableVersioned> storage = getRepository().storageFor(StorableVersioned.class); + + class InsertTrigger extends Trigger<StorableVersioned> { + Object mState; + + @Override + public Object beforeInsert(StorableVersioned s) { + assertEquals(1, s.getID()); + mState = new Object(); + return mState; + } + + @Override + public void afterInsert(StorableVersioned s, Object state) { + assertEquals(1, s.getID()); + assertEquals(mState, state); + } + }; + + InsertTrigger it = new InsertTrigger(); + + assertTrue(storage.addTrigger(it)); + + StorableVersioned s = storage.prepare(); + s.setID(1); + s.setValue("value"); + s.insert(); + + assertTrue(it.mState != null); + + assertTrue(storage.removeTrigger(it)); + + class UpdateTrigger extends Trigger<StorableVersioned> { + Object mState; + int mVersion; + + @Override + public Object beforeUpdate(StorableVersioned s) { + assertEquals(1, s.getID()); + mState = new Object(); + mVersion = s.getVersion(); + return mState; + } + + @Override + public void afterUpdate(StorableVersioned s, Object state) { + assertEquals(1, s.getID()); + assertEquals(mState, state); + assertEquals(mVersion + 1, s.getVersion()); + } + }; + + UpdateTrigger ut = new UpdateTrigger(); + + assertTrue(storage.addTrigger(ut)); + + s.setValue("value2"); + s.update(); + + assertTrue(ut.mState != null); + + assertTrue(storage.removeTrigger(ut)); + + class DeleteTrigger extends Trigger<StorableVersioned> { + Object mState; + + @Override + public Object beforeDelete(StorableVersioned s) { + assertEquals(1, s.getID()); + mState = new Object(); + return mState; + } + + @Override + public void afterDelete(StorableVersioned s, Object state) { + assertEquals(1, s.getID()); + assertEquals(mState, state); + } + }; + + DeleteTrigger dt = new DeleteTrigger(); + + assertTrue(storage.addTrigger(dt)); + + s.delete(); + + assertTrue(dt.mState != null); + + assertTrue(storage.removeTrigger(dt)); + } + + public void test_triggerFailure() throws Exception { + Storage<StorableVersioned> storage = getRepository().storageFor(StorableVersioned.class); + + class InsertTrigger extends Trigger<StorableVersioned> { + boolean failed; + + @Override + public Object beforeInsert(StorableVersioned s) { + throw new RuntimeException(); + } + + @Override + public void afterInsert(StorableVersioned s, Object state) { + fail(); + } + + @Override + public void failedInsert(StorableVersioned s, Object state) { + failed = true; + } + }; + + InsertTrigger it = new InsertTrigger(); + + assertTrue(storage.addTrigger(it)); + + StorableVersioned s = storage.prepare(); + s.setID(1); + s.setValue("value"); + try { + s.insert(); + fail(); + } catch (RuntimeException e) { + } + + assertTrue(it.failed); + + class UpdateTrigger extends Trigger<StorableVersioned> { + boolean failed; + + @Override + public Object beforeUpdate(StorableVersioned s) { + throw new RuntimeException(); + } + + @Override + public void afterUpdate(StorableVersioned s, Object state) { + fail(); + } + + @Override + public void failedUpdate(StorableVersioned s, Object state) { + failed = true; + } + }; + + UpdateTrigger ut = new UpdateTrigger(); + + assertTrue(storage.addTrigger(ut)); + + s = storage.prepare(); + s.setID(1); + s.setVersion(3); + s.setValue("value"); + try { + s.update(); + fail(); + } catch (RuntimeException e) { + } + + assertTrue(ut.failed); + + class DeleteTrigger extends Trigger<StorableVersioned> { + boolean failed; + + @Override + public Object beforeDelete(StorableVersioned s) { + throw new RuntimeException(); + } + + @Override + public void afterDelete(StorableVersioned s, Object state) { + fail(); + } + + @Override + public void failedDelete(StorableVersioned s, Object state) { + failed = true; + } + }; + + DeleteTrigger dt = new DeleteTrigger(); + + assertTrue(storage.addTrigger(dt)); + + s = storage.prepare(); + s.setID(1); + try { + s.delete(); + fail(); + } catch (RuntimeException e) { + } + + assertTrue(dt.failed); + } + + public void test_triggerChecks() throws Exception { + Storage<StorableTimestamped> storage = + getRepository().storageFor(StorableTimestamped.class); + + StorableTimestamped st = storage.prepare(); + st.setId(1); + st.setValue("value"); + + try { + st.insert(); + fail(); + } catch (ConstraintException e) { + // We forgot to set submitDate and modifyDate. + } + + // Install trigger that sets the timestamp properties. + + storage.addTrigger(new Trigger<Timestamped>() { + @Override + public Object beforeInsert(Timestamped st) { + DateTime now = new DateTime(); + st.setSubmitDateTime(now); + st.setModifyDateTime(now); + return null; + } + + @Override + public Object beforeUpdate(Timestamped st) { + DateTime now = new DateTime(); + st.setModifyDateTime(now); + return null; + } + }); + + st.insert(); + + assertNotNull(st.getSubmitDateTime()); + assertNotNull(st.getModifyDateTime()); + + DateTime dt = st.getModifyDateTime(); + + Thread.sleep(500); + + st.setValue("foo"); + st.update(); + + assertTrue(st.getModifyDateTime().getMillis() >= dt.getMillis() + 450); + } + + public void test_hashCode() throws Exception { + Storage<StorableTestBasic> storage = + getRepository().storageFor(StorableTestBasic.class); + + StorableTestBasic stb = storage.prepare(); + // Just tests to make sure generated code doesn't throw an error. + int hashCode = stb.hashCode(); + } + + public void test_stateMethods() throws Exception { + Storage<Order> storage = getRepository().storageFor(Order.class); + + Order order = storage.prepare(); + + assertUninitialized(true, order, "orderID", "orderNumber", "orderTotal", "orderComments"); + assertDirty(false, order, "orderID", "orderNumber", "orderTotal", "orderComments"); + assertClean(false, order, "orderID", "orderNumber", "orderTotal", "orderComments"); + + order.setOrderID(1); + + assertUninitialized(false, order, "orderID"); + assertUninitialized(true, order, "orderNumber", "orderTotal", "orderComments"); + assertDirty(false, order, "orderNumber", "orderTotal", "orderComments"); + assertDirty(true, order, "orderID"); + assertClean(false, order, "orderID", "orderNumber", "orderTotal", "orderComments"); + + order.setOrderNumber("123"); + order.setOrderTotal(456); + order.setAddressID(789); + + assertUninitialized(false, order, "orderID", "orderNumber", "orderTotal"); + assertUninitialized(true, order, "orderComments"); + assertDirty(true, order, "orderID", "orderNumber", "orderTotal"); + assertDirty(false, order, "orderComments"); + assertClean(false, order, "orderID", "orderNumber", "orderTotal", "orderComments"); + + // Get unknown property + try { + order.isPropertyUninitialized("foo"); + fail(); + } catch (IllegalArgumentException e) { + } + + // Get unknown property + try { + order.isPropertyDirty("foo"); + fail(); + } catch (IllegalArgumentException e) { + } + + // Get unknown property + try { + order.isPropertyClean("foo"); + fail(); + } catch (IllegalArgumentException e) { + } + + // Get join property + try { + order.isPropertyUninitialized("address"); + fail(); + } catch (IllegalArgumentException e) { + } + + // Get join property + try { + order.isPropertyDirty("address"); + fail(); + } catch (IllegalArgumentException e) { + } + + // Get join property + try { + order.isPropertyClean("address"); + fail(); + } catch (IllegalArgumentException e) { + } + + order.insert(); + + assertUninitialized(false, order, "orderID", "orderNumber", "orderTotal", "orderComments"); + assertDirty(false, order, "orderID", "orderNumber", "orderTotal", "orderComments"); + assertClean(true, order, "orderID", "orderNumber", "orderTotal", "orderComments"); + } + + public void test_count() throws Exception { + test_count(false); + } + public void test_countIndexed() throws Exception { + test_count(true); + } + + private void test_count(boolean indexed) throws Exception { + Storage<? extends StorableTestBasic> storage; + if (indexed) { + storage = getRepository().storageFor(StorableTestBasicIndexed.class); + } else { + storage = getRepository().storageFor(StorableTestBasic.class); + } + + assertEquals(0, storage.query().count()); + + StorableTestBasic sb = storage.prepare(); + sb.setId(1); + sb.setIntProp(1); + sb.setLongProp(1); + sb.setDoubleProp(1.1); + sb.setStringProp("one"); + sb.insert(); + + assertEquals(1, storage.query().count()); + assertEquals(0, storage.query().not().count()); + assertEquals(1, storage.query("stringProp = ?").with("one").count()); + assertEquals(0, storage.query("stringProp = ?").with("two").count()); + + sb = storage.prepare(); + sb.setId(2); + sb.setIntProp(2); + sb.setLongProp(2); + sb.setDoubleProp(2.1); + sb.setStringProp("two"); + sb.insert(); + + sb = storage.prepare(); + sb.setId(3); + sb.setIntProp(3); + sb.setLongProp(3); + sb.setDoubleProp(3.1); + sb.setStringProp("three"); + sb.insert(); + + assertEquals(3, storage.query().count()); + assertEquals(0, storage.query().not().count()); + assertEquals(1, storage.query("stringProp = ?").with("one").count()); + assertEquals(1, storage.query("stringProp = ?").with("two").count()); + assertEquals(1, storage.query("stringProp = ?").with("three").count()); + assertEquals(2, storage.query("stringProp = ?").not().with("one").count()); + assertEquals(2, storage.query("stringProp = ?").not().with("two").count()); + assertEquals(2, storage.query("stringProp = ?").not().with("three").count()); + assertEquals(2, storage.query("stringProp > ?").with("one").count()); + assertEquals(0, storage.query("stringProp > ?").with("two").count()); + assertEquals(1, storage.query("stringProp > ?").with("three").count()); + } + + public void test_fetchAfter() throws Exception { + Storage<ManyKeys2> storage = getRepository().storageFor(ManyKeys2.class); + + final int groupSize = 4; + final int aliasing = groupSize + 1; + + int total = 0; + for (int a=0; a<groupSize; a++) { + for (int b=0; b<groupSize; b++) { + for (int c=0; c<groupSize; c++) { + for (int d=0; d<groupSize; d++) { + for (int e=0; e<groupSize; e++) { + for (int f=0; f<groupSize; f++) { + ManyKeys2 obj = storage.prepare(); + obj.setA(a); + obj.setB(b); + obj.setC(c); + obj.setD(d); + obj.setE(e); + obj.setF(f); + obj.insert(); + total++; + } + } + } + } + } + } + + String[] orderBy = {"a", "b", "c", "d", "e", "f"}; + Query<ManyKeys2> query = storage.query().orderBy(orderBy); + Cursor<ManyKeys2> cursor = query.fetch(); + Comparator<ManyKeys2> comparator = SortedCursor.createComparator(ManyKeys2.class, orderBy); + + int actual = 0; + ManyKeys2 last = null; + while (cursor.hasNext()) { + actual++; + ManyKeys2 obj = cursor.next(); + if (last != null) { + assertTrue(comparator.compare(last, obj) < 0); + } + if (actual % aliasing == 0) { + cursor.close(); + cursor = query.fetchAfter(obj); + } + last = obj; + } + + assertEquals(total, actual); + + // Try again in reverse + + orderBy = new String[] {"-a", "-b", "-c", "-d", "-e", "-f"}; + query = storage.query().orderBy(orderBy); + cursor = query.fetch(); + + actual = 0; + last = null; + while (cursor.hasNext()) { + actual++; + ManyKeys2 obj = cursor.next(); + if (last != null) { + assertTrue(comparator.compare(last, obj) > 0); + } + if (actual % aliasing == 0) { + cursor.close(); + cursor = query.fetchAfter(obj); + } + last = obj; + } + + assertEquals(total, actual); + + // Try again with funny mix of orderings. This will likely cause sort + // operations to be performed, thus making it very slow. + + orderBy = new String[] {"-a", "b", "-c", "d", "-e", "f"}; + query = storage.query().orderBy(orderBy); + cursor = query.fetch(); + comparator = SortedCursor.createComparator(ManyKeys2.class, orderBy); + + actual = 0; + last = null; + while (cursor.hasNext()) { + actual++; + ManyKeys2 obj = cursor.next(); + if (last != null) { + assertTrue(comparator.compare(last, obj) < 0); + } + if (actual % aliasing == 0) { + cursor.close(); + cursor = query.fetchAfter(obj); + } + last = obj; + } + + assertEquals(total, actual); + } + + private void assertUninitialized(boolean expected, Storable storable, String... properties) { + for (String property : properties) { + assertEquals(expected, storable.isPropertyUninitialized(property)); + } + } + + private void assertDirty(boolean expected, Storable storable, String... properties) { + for (String property : properties) { + assertEquals(expected, storable.isPropertyDirty(property)); + } + } + + private void assertClean(boolean expected, Storable storable, String... properties) { + for (String property : properties) { + assertEquals(expected, storable.isPropertyClean(property)); + } + } + + @PrimaryKey("id") + public interface StorableTestAssymetricGet extends Storable{ + public abstract int getId(); + public abstract void setId(int id); + + public abstract int getAssymetricGET(); + } + + @PrimaryKey("id") + public interface StorableTestAssymetricSet extends Storable{ + public abstract int getId(); + public abstract void setId(int id); + + public abstract int setAssymetricSET(); + } + + @PrimaryKey("id") + public interface StorableTestAssymetricGetSet extends Storable{ + public abstract int getId(); + public abstract void setId(int id); + + public abstract int getAssymetricGET(); + + public abstract int setAssymetricSET(); + } + + public static class InvocationTracker extends StorableTestBasic implements WrappedSupport { + String mName; + long mInvocationTracks; + + boolean mTrace; + + public InvocationTracker(String name) { + this(name, false); + } + + public InvocationTracker(final String name, boolean trace) { + mName = name; + mTrace = trace; + clearTracks(); + } + + public void clearTracks() { + mInvocationTracks = 0; + } + + public long getTracks() { + return mInvocationTracks; + } + + public void assertTrack(long value) { + assertEquals(value, getTracks()); + clearTracks(); + } + + + public void setId(int id) { + if (mTrace) System.out.println("setId"); + mInvocationTracks |= sSetId; + } + + // Basic coverage of the primitives + public String getStringProp() { + if (mTrace) System.out.println("getStringProp"); + mInvocationTracks |= sGetStringProp; // 0x2 + return null; + } + + public void setStringProp(String aStringThing) { + if (mTrace) System.out.println("setStringProp"); + mInvocationTracks |= sSetStringProp; // 0x4 + } + + public int getIntProp() { + if (mTrace) System.out.println("getIntProp"); + mInvocationTracks |= sGetIntProp; // 0x8 + return 0; + } + + public void setIntProp(int anInt) { + if (mTrace) System.out.println("setIntProp"); + mInvocationTracks |= sSetIntProp; // 0x10 + } + + public long getLongProp() { + if (mTrace) System.out.println("getLongProp"); + mInvocationTracks |= sGetLongProp; // 0x20 + return 0; + } + + public void setLongProp(long aLong) { + if (mTrace) System.out.println("setLongProp"); + mInvocationTracks |= sSetLongProp; // 0x40 + } + + public double getDoubleProp() { + if (mTrace) System.out.println("getDoubleProp"); + mInvocationTracks |= sGetDoubleProp; // 0x80 + return 0; + } + + public void setDoubleProp(double aDouble) { + if (mTrace) System.out.println("setDoubleProp"); + mInvocationTracks |= sSetDoubleProp; // 0x100 + } + + public DateTime getDate() { + return null; + } + + public void setDate(DateTime date) { + } + + public void load() throws FetchException { + if (mTrace) System.out.println("load"); + mInvocationTracks |= sLoad; // 0x200 + } + + public boolean tryLoad() throws FetchException { + if (mTrace) System.out.println("tryLoad"); + mInvocationTracks |= sTryLoad; // 0x400 + return false; + } + + public void insert() throws PersistException { + if (mTrace) System.out.println("insert"); + mInvocationTracks |= sInsert; // 0x800 + } + + public boolean tryInsert() throws PersistException { + if (mTrace) System.out.println("tryInsert"); + mInvocationTracks |= sTryInsert; + return false; + } + + public void update() throws PersistException { + if (mTrace) System.out.println("update"); + mInvocationTracks |= sUpdate; + } + + public boolean tryUpdate() throws PersistException { + if (mTrace) System.out.println("tryUpdate"); + mInvocationTracks |= sTryUpdate; // 0x1000 + return false; + } + + public void delete() throws PersistException { + if (mTrace) System.out.println("delete"); + mInvocationTracks |= sDelete; + } + + public boolean tryDelete() throws PersistException { + if (mTrace) System.out.println("tryDelete"); + mInvocationTracks |= sTryDelete; // 0x2000 + return false; + } + + public WrappedSupport createSupport(Storable storable) { + return new InvocationTracker(mName, mTrace); + } + + public Storage storage() { + if (mTrace) System.out.println("storage"); + mInvocationTracks |= sStorage; // 0x4000 + return null; + } + + public Storable copy() { + if (mTrace) System.out.println("copy"); + mInvocationTracks |= sCopy; // 0x8000 + return null; + } + + public String toStringKeyOnly() { + if (mTrace) System.out.println("toStringKeyOnly"); + mInvocationTracks |= sToStringKeyOnly; // 0x10000 + return null; + } + + public int getId() { + if (mTrace) System.out.println("getId"); + mInvocationTracks |= sGetId; // 0x20000 + return 0; + } + + public void copyAllProperties(Storable storable) { + if (mTrace) System.out.println("copyAllProperties"); + mInvocationTracks |= sCopyAllProperties; // 0x40000 + } + + public void copyPrimaryKeyProperties(Storable storable) { + if (mTrace) System.out.println("copyPrimaryKeyProperties"); + mInvocationTracks |= sCopyPrimaryKeyProperties; // 0x80000 + } + + public void copyUnequalProperties(Storable storable) { + if (mTrace) System.out.println("copyUnequalProperties"); + mInvocationTracks |= sCopyUnequalProperties; // 0x10 0000 + } + + public void copyDirtyProperties(Storable storable) { + if (mTrace) System.out.println("copyDirtyProperties"); + mInvocationTracks |= sCopyDirtyProperties; // 0x20 0000 + } + + public boolean hasDirtyProperties() { + if (mTrace) System.out.println("hasDirtyProperties"); + mInvocationTracks |= sHasDirtyProperties; // 0x200 0000 + return false; + } + + public boolean equalPrimaryKeys(Object obj) { + if (mTrace) System.out.println("equalPrimaryKeys"); + mInvocationTracks |= sEqualKeys; // 0x40 0000 + return true; + } + + public boolean equalProperties(Object obj) { + if (mTrace) System.out.println("equalProperties"); + mInvocationTracks |= sEqualProperties; // 0x80 0000 + return true; + } + + public void copyVersionProperty(Storable storable) { + if (mTrace) System.out.println("copyVersionProperty"); + mInvocationTracks |= sCopyVersionProperty; // 0x100 0000 + } + + public void markPropertiesClean() { + if (mTrace) System.out.println("markPropertiesClean"); + mInvocationTracks |= sMarkPropertiesClean; // 0x400 0000 + } + + public void markAllPropertiesClean() { + if (mTrace) System.out.println("markAllPropertiesClean"); + mInvocationTracks |= sMarkAllPropertiesClean; // 0x800 0000 + } + + public void markPropertiesDirty() { + if (mTrace) System.out.println("markPropertiesDirty"); + mInvocationTracks |= sMarkPropertiesDirty; // 0x1000 0000 + } + + public void markAllPropertiesDirty() { + if (mTrace) System.out.println("markAllPropertiesDirty"); + mInvocationTracks |= sMarkAllPropertiesDirty; // 0x2000 0000 + } + + public Class storableType() { + if (mTrace) System.out.println("storableType"); + mInvocationTracks |= sStorableType; // 0x4000 0000 + return Storable.class; + } + + public boolean isPropertyUninitialized(String name) { + if (mTrace) System.out.println("isPropertyUninitialized"); + return false; + } + + public boolean isPropertyDirty(String name) { + if (mTrace) System.out.println("isPropertyDirty"); + return false; + } + + public boolean isPropertyClean(String name) { + if (mTrace) System.out.println("isPropertyClean"); + return false; + } + + public boolean isPropertySupported(String name) { + if (mTrace) System.out.println("isPropertySupported"); + return false; + } + + public Repository getRootRepository() { + return null; + } + + public Trigger getInsertTrigger() { + return null; + } + + public Trigger getUpdateTrigger() { + return null; + } + + public Trigger getDeleteTrigger() { + return null; + } + } + +} diff --git a/src/test/java/com/amazon/carbonado/spi/TestSequenceValueGenerator.java b/src/test/java/com/amazon/carbonado/spi/TestSequenceValueGenerator.java new file mode 100644 index 0000000..ae17e5d --- /dev/null +++ b/src/test/java/com/amazon/carbonado/spi/TestSequenceValueGenerator.java @@ -0,0 +1,258 @@ +/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.spi;
+
+import java.util.Random;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.PersistException;
+import com.amazon.carbonado.Repository;
+import com.amazon.carbonado.Storage;
+
+import com.amazon.carbonado.repo.toy.ToyRepository;
+
+import com.amazon.carbonado.stored.StorableTestBasic;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestSequenceValueGenerator extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestSequenceValueGenerator.class);
+ }
+
+ private Repository mRepository;
+
+ public TestSequenceValueGenerator(String name) {
+ super(name);
+ }
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ mRepository = new ToyRepository();
+ }
+
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void test_basics() throws Exception {
+ SequenceValueGenerator generator = new SequenceValueGenerator(mRepository, "foo");
+
+ for (int i=1; i<=950; i++) {
+ assertEquals(i, generator.nextLongValue());
+ }
+
+ generator.reset(1);
+
+ for (int i=1; i<=950; i++) {
+ assertEquals(i, generator.nextIntValue());
+ }
+
+ generator.reset(1);
+
+ for (int i=1; i<=950; i++) {
+ assertEquals(String.valueOf(i), generator.nextDecimalValue());
+ }
+
+ // Make sure data is persisted
+
+ generator = new SequenceValueGenerator(mRepository, "foo");
+
+ assertTrue(generator.nextLongValue() > 950);
+
+ // Make sure data is isolated
+
+ generator = new SequenceValueGenerator(mRepository, "another");
+
+ for (int i=1; i<=1050; i++) {
+ assertEquals(i, generator.nextLongValue());
+ }
+
+ // Make sure reserved values can be returned
+
+ generator.returnReservedValues();
+ generator = new SequenceValueGenerator(mRepository, "another");
+
+ assertEquals(1051, generator.nextLongValue());
+ }
+
+ public void test_highIncrement() throws Exception {
+ SequenceValueGenerator generator =
+ new SequenceValueGenerator(mRepository, "foo", 1, 125);
+
+ for (int i=0; i<950; i++) {
+ assertEquals(i * 125 + 1, generator.nextLongValue());
+ }
+ }
+
+ public void test_highInitialAndHighIncrement() throws Exception {
+ SequenceValueGenerator generator =
+ new SequenceValueGenerator(mRepository, "foo", 0x500000000L, 125);
+
+ for (int i=0; i<950; i++) {
+ assertEquals(i * 125 + 0x500000000L, generator.nextLongValue());
+ }
+
+ try {
+ // Doesn't fit in an int.
+ generator.nextIntValue();
+ fail();
+ } catch (PersistException e) {
+ }
+ }
+
+ public void test_lowReserve() throws Exception {
+ SequenceValueGenerator generator =
+ new SequenceValueGenerator(mRepository, "goo", 1, 1, 1);
+
+ for (int i=1; i<=950; i++) {
+ assertEquals(i, generator.nextLongValue());
+ }
+ }
+
+ public void test_overflow() throws Exception {
+ Storage<StoredSequence> storage = mRepository.storageFor(StoredSequence.class);
+ StoredSequence seq = storage.prepare();
+ seq.setName("overflow");
+ seq.setInitialValue(1);
+ seq.setNextValue(Long.MAX_VALUE - 50);
+ seq.insert();
+
+ SequenceValueGenerator generator = new SequenceValueGenerator(mRepository, "overflow");
+
+ for (int i=-50; i<=-1; i++) {
+ assertEquals(i, generator.nextLongValue());
+ }
+
+ // Although next value could be zero, overflow logic doesn't work this
+ // way. Its not really worth the trouble to allow zero to be returned
+ // before overflowing.
+
+ try {
+ // Overflow.
+ generator.nextLongValue();
+ fail();
+ } catch (PersistException e) {
+ }
+ }
+
+ public void test_largeNumericalValue() throws Exception {
+ // Tests string conversion to ensure large unsigned values are properly
+ // generated.
+
+ SequenceValueGenerator generator =
+ new SequenceValueGenerator(mRepository, "goo", Long.MAX_VALUE, 1);
+
+ assertEquals("9223372036854775807", generator.nextDecimalValue());
+ // Next values are too large to fit in an unsigned long
+ assertEquals("9223372036854775808", generator.nextDecimalValue());
+ assertEquals("9223372036854775809", generator.nextDecimalValue());
+ }
+
+ public void test_radix() throws Exception {
+ SequenceValueGenerator generator = new SequenceValueGenerator(mRepository, "goo");
+
+ for (int i=1; i<=1000; i++) {
+ assertEquals(Integer.toString(i, 36), generator.nextNumericalValue(36, 1));
+ }
+ }
+
+ public void test_pad() throws Exception {
+ SequenceValueGenerator generator = new SequenceValueGenerator(mRepository, "goo");
+
+ for (int i=1; i<=2000; i++) {
+ String next = generator.nextNumericalValue(10, 3);
+ assertTrue(next.length() >= 3);
+ int value = Integer.parseInt(next);
+ assertEquals(i, value);
+ }
+ }
+
+ public void test_concurrentAccess() throws Exception {
+ // Simple test ensuring that values are reserved properly even when
+ // multiple processes may be sharing the sequence.
+
+ SequenceValueGenerator g1 = new SequenceValueGenerator(mRepository, "goo", 1, 1, 100);
+ SequenceValueGenerator g2 = new SequenceValueGenerator(mRepository, "goo", 1, 1, 100);
+
+ for (int i=1; i<=100; i++) {
+ assertEquals(i, g1.nextLongValue());
+ assertEquals(i + 100, g2.nextLongValue());
+ }
+
+ for (int i=201; i<=300; i++) {
+ assertEquals(i, g2.nextLongValue());
+ assertEquals(i + 100, g1.nextLongValue());
+ }
+
+ assertTrue(g1.returnReservedValues());
+ assertFalse(g2.returnReservedValues());
+ }
+
+ // FIXME: move this test somewhere else
+ /* Takes too long
+ public void test_heavyConcurrentAccess() throws Exception {
+ // Heavy test with multiple processes sharing the sequence.
+
+ final Storage<StorableTestBasic> storage =
+ mRepository.storageFor(StorableTestBasic.class);
+ final Random rnd = new Random(376296292);
+ final int loopCount = 10000;
+
+ Thread[] threads = new Thread[10];
+ for (int i=0; i<threads.length; i++) {
+ threads[i] = new Thread() {
+ public void run() {
+ try {
+ SequenceValueGenerator generator =
+ new SequenceValueGenerator(mRepository, "seq");
+ for (int i=0; i<loopCount; i++) {
+ StorableTestBasic stb = storage.prepare();
+ stb.setId(generator.nextIntValue());
+ stb.initBasicProperties();
+ stb.insert();
+ if (rnd.nextInt(500) == 0) {
+ generator.returnReservedValues();
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace(System.out);
+ fail(e.toString());
+ }
+ }
+ };
+
+ threads[i].start();
+ }
+
+ for (Thread t : threads) {
+ t.join();
+ }
+ }
+ */
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/FileInfo.java b/src/test/java/com/amazon/carbonado/stored/FileInfo.java new file mode 100644 index 0000000..7d35faf --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/FileInfo.java @@ -0,0 +1,109 @@ +/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import java.io.*;
+
+import org.joda.time.DateTime;
+
+import com.amazon.carbonado.*;
+import com.amazon.carbonado.lob.*;
+import com.amazon.carbonado.adapter.*;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@Indexes({
+ @Index("name"),
+ @Index({"length", "lastModified"}),
+ @Index("lastModified"),
+ @Index("parentID")
+})
+@Alias("CBN_TEST_FILE_INFO")
+@AlternateKeys({
+ @Key({"parentID", "name"})
+})
+@PrimaryKey("ID")
+public abstract class FileInfo implements Storable {
+ @Sequence("com.amazon.carbonado.storables.FileInfo")
+ public abstract int getID();
+
+ public abstract void setID(int value);
+
+ @Nullable
+ public abstract Integer getParentID();
+
+ public abstract void setParentID(Integer id);
+
+ @Nullable
+ @Join(internal="parentID", external="ID")
+ public abstract FileInfo getParent() throws FetchException;
+
+ public abstract void setParent(FileInfo value);
+
+ @Join(internal="ID", external="parentID")
+ public abstract Query<FileInfo> getChildren() throws FetchException;
+
+ @Alias("FILE_NAME")
+ public abstract String getName();
+
+ public abstract void setName(String value);
+
+ @YesNoAdapter
+ public abstract boolean isDirectory();
+
+ public abstract void setDirectory(boolean value);
+
+ @Alias("FILE_LENGTH")
+ public abstract long getLength();
+
+ public abstract void setLength(long value);
+
+ @Nullable
+ public abstract DateTime getLastModified();
+
+ public abstract void setLastModified(DateTime value);
+
+ @Version
+ @Alias("RECORD_VERSION_NUMBER")
+ public abstract int getVersionNumber();
+
+ public abstract void setVersionNumber(int version);
+
+ @Nullable
+ public abstract Blob getFileData();
+
+ public abstract void setFileData(Blob data);
+
+ public String getFullPath() throws FetchException {
+ FileInfo parent;
+ try {
+ parent = getParent();
+ } catch (FetchNoneException e) {
+ parent = null;
+ }
+ if (parent == null) {
+ return getName();
+ } else {
+ return parent.getFullPath() + '/' + getName();
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/ManyKeys2.java b/src/test/java/com/amazon/carbonado/stored/ManyKeys2.java new file mode 100644 index 0000000..282ff02 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/ManyKeys2.java @@ -0,0 +1,47 @@ +/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.*;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@PrimaryKey({"a", "b", "c", "d", "e", "f"})
+public interface ManyKeys2 extends Storable {
+ int getA();
+ void setA(int value);
+
+ int getB();
+ void setB(int value);
+
+ int getC();
+ void setC(int value);
+
+ int getD();
+ void setD(int value);
+
+ int getE();
+ void setE(int value);
+
+ int getF();
+ void setF(int value);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableDateIndex.java b/src/test/java/com/amazon/carbonado/stored/StorableDateIndex.java new file mode 100644 index 0000000..5e7d063 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/StorableDateIndex.java @@ -0,0 +1,46 @@ +/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import org.joda.time.DateTime;
+
+
+import com.amazon.carbonado.Index;
+import com.amazon.carbonado.Indexes;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.adapter.DateTimeAdapter;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@Indexes(@Index("orderDate"))
+@PrimaryKey("ID")
+public interface StorableDateIndex extends Storable {
+ int getID();
+
+ void setID(int id);
+
+ @DateTimeAdapter(timeZone="America/New_York")
+ DateTime getOrderDate();
+
+ void setOrderDate(DateTime date);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableSequenced.java b/src/test/java/com/amazon/carbonado/stored/StorableSequenced.java new file mode 100644 index 0000000..770d6f0 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/StorableSequenced.java @@ -0,0 +1,67 @@ +/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.Nullable;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Sequence;
+import com.amazon.carbonado.Storable;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@PrimaryKey("ID")
+public interface StorableSequenced extends Storable<StorableSequenced> {
+ @Sequence("pk")
+ long getID();
+
+ void setID(long id);
+
+ @Sequence("some_int")
+ int getSomeInt();
+
+ void setSomeInt(int i);
+
+ @Sequence("some_IntegerObj")
+ Integer getSomeIntegerObj();
+
+ void setSomeIntegerObj(Integer i);
+
+ @Sequence("some_long")
+ long getSomeLong();
+
+ void setSomeLong(long i);
+
+ @Sequence("some_LongObj")
+ @Nullable
+ Long getSomeLongObj();
+
+ void setSomeLongObj(Long i);
+
+ @Sequence("some_String")
+ String getSomeString();
+
+ void setSomeString(String str);
+
+ String getData();
+
+ void setData(String data);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableTestAssymetric.java b/src/test/java/com/amazon/carbonado/stored/StorableTestAssymetric.java new file mode 100644 index 0000000..3808c1f --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/StorableTestAssymetric.java @@ -0,0 +1,38 @@ +/* + * Copyright 2006 Amazon Technologies, Inc. or its affiliates. + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks + * of Amazon Technologies, Inc. or its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazon.carbonado.stored; + +import com.amazon.carbonado.Storable; +import com.amazon.carbonado.PrimaryKey; + +/* + * StorableTestAssymetric + * + * @author Don Schneider + */ +@PrimaryKey("id") +public abstract class StorableTestAssymetric implements Storable { + public abstract int getId(); + public abstract void setId(int id); + + public int getAssymetricGet() + { + return getId()*3; + }; +} diff --git a/src/test/java/com/amazon/carbonado/stored/StorableTestInvalid.java b/src/test/java/com/amazon/carbonado/stored/StorableTestInvalid.java new file mode 100644 index 0000000..bd9fed2 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/StorableTestInvalid.java @@ -0,0 +1,40 @@ +/* + * Copyright 2006 Amazon Technologies, Inc. or its affiliates. + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks + * of Amazon Technologies, Inc. or its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazon.carbonado.stored; + +import com.amazon.carbonado.Storable; +import com.amazon.carbonado.PrimaryKey; + +/** + * StorableTestInvalid + * + * @author Don Schneider + */ +@PrimaryKey("pk") +public interface StorableTestInvalid extends Storable { + int getPk(); + void setPk(int id); + + Custom getCustom(); + void setCustom(Custom aCustom); + + + // Probably more than is needed + class Custom { } +} diff --git a/src/test/java/com/amazon/carbonado/stored/StorableTestKeyValue.java b/src/test/java/com/amazon/carbonado/stored/StorableTestKeyValue.java new file mode 100644 index 0000000..44ecc2f --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/StorableTestKeyValue.java @@ -0,0 +1,43 @@ +/* + * Copyright 2006 Amazon Technologies, Inc. or its affiliates. + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks + * of Amazon Technologies, Inc. or its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazon.carbonado.stored; + +import com.amazon.carbonado.Storable; +import com.amazon.carbonado.PrimaryKey; + +/** + * StorableTestKeyValue + * + * @author Don Schneider + */ +@PrimaryKey({"key1", "key2"}) +public interface StorableTestKeyValue extends Storable { + int getKey1(); + void setKey1(int id); + + int getKey2(); + void setKey2(int id); + + int getValue1(); + void setValue1(int value); + + int getValue2(); + void setValue2(int value); + +} diff --git a/src/test/java/com/amazon/carbonado/stored/StorableTestMultiPK.java b/src/test/java/com/amazon/carbonado/stored/StorableTestMultiPK.java new file mode 100644 index 0000000..ace031b --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/StorableTestMultiPK.java @@ -0,0 +1,40 @@ +/* + * Copyright 2006 Amazon Technologies, Inc. or its affiliates. + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks + * of Amazon Technologies, Inc. or its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazon.carbonado.stored; + +import com.amazon.carbonado.PrimaryKey; +import com.amazon.carbonado.Storable; + +/** + * StorableTestMultiPK + * + * @author Don Schneider + */ +@PrimaryKey({"idPK", "stringPK"}) +public interface StorableTestMultiPK extends Storable { + int getIdPK(); + void setIdPK(int id); + + // Basic coverage of the primitives + String getStringPK(); + void setStringPK(String aString); + + String getStringData(); + void setStringData(String aData); +} diff --git a/src/test/java/com/amazon/carbonado/stored/StorableTimestamped.java b/src/test/java/com/amazon/carbonado/stored/StorableTimestamped.java new file mode 100644 index 0000000..d93ffeb --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/StorableTimestamped.java @@ -0,0 +1,38 @@ +/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import org.joda.time.DateTime;
+
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.PrimaryKey;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@PrimaryKey("id")
+public interface StorableTimestamped extends Storable, Timestamped {
+ int getId();
+ void setId(int id);
+
+ String getValue();
+ void setValue(String str);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableVersioned.java b/src/test/java/com/amazon/carbonado/stored/StorableVersioned.java new file mode 100644 index 0000000..2de9640 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/StorableVersioned.java @@ -0,0 +1,50 @@ +/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.Nullable;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Version;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@PrimaryKey("ID")
+public interface StorableVersioned extends Storable {
+ int getID();
+
+ void setID(int id);
+
+ String getValue();
+
+ void setValue(String value);
+
+ @Nullable
+ String getName();
+
+ void setName(String name);
+
+ @Version
+ int getVersion();
+
+ void setVersion(int version);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableVersionedIndexed.java b/src/test/java/com/amazon/carbonado/stored/StorableVersionedIndexed.java new file mode 100644 index 0000000..906e8be --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/StorableVersionedIndexed.java @@ -0,0 +1,36 @@ +/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.Index;
+import com.amazon.carbonado.Indexes;
+import com.amazon.carbonado.PrimaryKey;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@Indexes({
+ @Index("value"),
+ @Index("name")
+})
+@PrimaryKey("ID")
+public interface StorableVersionedIndexed extends StorableVersioned {
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithLong.java b/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithLong.java new file mode 100644 index 0000000..6d55d54 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithLong.java @@ -0,0 +1,44 @@ +/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Version;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@PrimaryKey("ID")
+public interface StorableVersionedWithLong extends Storable {
+ int getID();
+
+ void setID(int id);
+
+ String getValue();
+
+ void setValue(String value);
+
+ @Version
+ long getVersion();
+
+ void setVersion(long version);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithLongObj.java b/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithLongObj.java new file mode 100644 index 0000000..2acfdf8 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithLongObj.java @@ -0,0 +1,46 @@ +/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.Nullable;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Version;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@PrimaryKey("ID")
+public interface StorableVersionedWithLongObj extends Storable {
+ int getID();
+
+ void setID(int id);
+
+ String getValue();
+
+ void setValue(String value);
+
+ @Version
+ @Nullable
+ Long getVersion();
+
+ void setVersion(Long version);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithObj.java b/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithObj.java new file mode 100644 index 0000000..29f313c --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithObj.java @@ -0,0 +1,46 @@ +/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.Nullable;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Version;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@PrimaryKey("ID")
+public interface StorableVersionedWithObj extends Storable {
+ int getID();
+
+ void setID(int id);
+
+ String getValue();
+
+ void setValue(String value);
+
+ @Version
+ @Nullable
+ Integer getVersion();
+
+ void setVersion(Integer version);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/Timestamped.java b/src/test/java/com/amazon/carbonado/stored/Timestamped.java new file mode 100644 index 0000000..b38ee28 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/Timestamped.java @@ -0,0 +1,34 @@ +/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import org.joda.time.DateTime;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public interface Timestamped {
+ DateTime getSubmitDateTime();
+ void setSubmitDateTime(DateTime dt);
+
+ DateTime getModifyDateTime();
+ void setModifyDateTime(DateTime dt);
+}
|