diff --git a/src/org/zoodb/internal/DataDeSerializer.java b/src/org/zoodb/internal/DataDeSerializer.java index 6d3e8144..a0b74c81 100644 --- a/src/org/zoodb/internal/DataDeSerializer.java +++ b/src/org/zoodb/internal/DataDeSerializer.java @@ -296,7 +296,7 @@ public ZooPC readObject(ZooPC pc, int page, int offs) { if (clsDef.getNextVersion() != null) { throw DBLogger.newUser("Objecty has not been evolved to the latest schema version: " + - pc.jdoZooGetOid()); + Util.oidToString(oid)); } return readObjPrivate(pc, clsDef); @@ -1047,6 +1047,8 @@ private final Object hollowForOid(long oid, ZooClassDef clsDef) { } } else { //ensure latest version, otherwise there is no Class + //TODO instead we should store the schema ID, so that we automatically get the + // latest version... while (clsDef.getNextVersion() != null) { clsDef = clsDef.getNextVersion(); } diff --git a/tst/org/zoodb/test/jdo/Test_034_SchemaEvolution.java b/tst/org/zoodb/test/jdo/Test_034_SchemaEvolution.java index 89c912ba..45f99acb 100644 --- a/tst/org/zoodb/test/jdo/Test_034_SchemaEvolution.java +++ b/tst/org/zoodb/test/jdo/Test_034_SchemaEvolution.java @@ -39,7 +39,9 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; +import org.zoodb.api.impl.ZooPC; import org.zoodb.jdo.ZooJdoHelper; +import org.zoodb.jdo.ZooJdoProperties; import org.zoodb.jdo.spi.PersistenceCapableImpl; import org.zoodb.schema.ZooClass; import org.zoodb.schema.ZooField; @@ -120,6 +122,146 @@ public void testSimpleEvolution() { TestTools.closePM(); } + @Test + public void testSimpleEvolutionError_Issue71() { + PersistenceManager pm = TestTools.openPM(); + pm.currentTransaction().begin(); + ZooJdoHelper.schema(pm).addClass(TestClassTiny.class); + TestClassTiny t1 = new TestClassTiny(1, 3); + TestClassTiny t2 = new TestClassTiny(4, 5); + pm.makePersistent(t1); + pm.makePersistent(t2); + Object oid1 = pm.getObjectId(t1); + pm.currentTransaction().commit(); + + TestTools.closePM(); + pm = TestTools.openPM(); + + pm.currentTransaction().begin(); + + ZooClass s1 = ZooJdoHelper.schema(pm).getClass(TestClassTiny.class.getName()); + s1.rename(TestClassSmall.class.getName()); +// private int myInt; +// private long myLong; +// private String myString; +// private int[] myInts; +// private Object refO; +// private TestClassTiny refP; + + s1.getField("_int").remove(); + s1.addField("myInt", Integer.TYPE); + s1.getField("_long").rename("myLong"); + s1.addField("myString", String.class); + s1.addField("myInts", Integer[].class); + s1.addField("refO", Object.class); + ZooJdoHelper.schema(pm).addClass(TestClassTiny.class); + //Omit one field to trigger error + //s1.addField("refP", TestClassTiny.class); + + pm.currentTransaction().commit(); + TestTools.closePM(); + + pm = TestTools.openPM(); + pm.currentTransaction().begin(); + + try { + pm.getObjectById(oid1); + fail(); + } catch (JDOUserException e) { + assertFalse(e.getMessage(), e.getMessage().contains("class not found")); + assertTrue(e.getMessage(), e.getMessage().contains("field")); + assertTrue(e.getMessage(), e.getMessage().contains("mismatch")); + } + + pm.currentTransaction().rollback(); + TestTools.closePM(); + } + + @Test + public void testSimpleEvolutionViaReference() { + //test that evolves objects and then accesses them via hollowForOid() in the deserializer. + + ZooJdoProperties p = TestTools.getProps(); + p.setZooAutoCreateSchema(true); + PersistenceManager pm = TestTools.openPM(); + pm.currentTransaction().begin(); + ZooJdoHelper.schema(pm).addClass(TestClassTiny.class); + ZooJdoHelper.schema(pm).addClass(TestClassTinyRef.class); + TestClassTiny t1 = new TestClassTiny(1, 3); + TestClassTinyRef r = new TestClassTinyRef(t1); + pm.makePersistent(r); + Object oidR = pm.getObjectId(r); + pm.currentTransaction().commit(); + TestTools.closePM(); + + //create incompatible schema + pm = TestTools.openPM(); + pm.currentTransaction().begin(); + ZooClass s1 = ZooJdoHelper.schema(pm).getClass(TestClassTiny.class.getName()); + s1.getField("_int").remove(); + s1.addField("myInt", Integer.TYPE); + s1.addField("myString", String.class); + //evolve objects + Iterator it = s1.getHandleIterator(true); + while (it.hasNext()) { + ZooHandle h = it.next(); + h.setValue("myInt", 123); + } + pm.currentTransaction().commit(); + TestTools.closePM(); + + //change back to proper schema + pm = TestTools.openPM(); + pm.currentTransaction().begin(); + s1 = ZooJdoHelper.schema(pm).getClass(TestClassTiny.class.getName()); + s1.addField("_int", Integer.TYPE); + s1.getField("myInt").remove(); + s1.getField("myString").remove(); + pm.currentTransaction().commit(); + TestTools.closePM(); + + //Load data + pm = TestTools.openPM(); + pm.currentTransaction().begin(); + + TestClassTinyRef r2 = (TestClassTinyRef) pm.getObjectById(oidR); + assertNotNull(r2.getTiny()); + try { + assertEquals(0, r2.getTiny().getInt()); + assertEquals(3L, r2.getTiny().getLong()); + } catch (JDOUserException e) { + assertTrue(e.getMessage(), e.getMessage().contains("not been evolved")); + } + + pm.currentTransaction().rollback(); + TestTools.closePM(); + + //Okay, we need to evolve first + pm = TestTools.openPM(); + pm.currentTransaction().begin(); + s1 = ZooJdoHelper.schema(pm).getClass(TestClassTiny.class.getName()); + Iterator it2 = s1.getHandleIterator(true); + while (it2.hasNext()) { + ZooHandle h = it2.next(); + h.setValue("_int", 2); + } + pm.currentTransaction().commit(); + TestTools.closePM(); + + + //Load data again + pm = TestTools.openPM(); + pm.currentTransaction().begin(); + + TestClassTinyRef r3 = (TestClassTinyRef) pm.getObjectById(oidR); + assertNotNull(r3.getTiny()); + assertEquals(2, r3.getTiny().getInt()); + assertEquals(3L, r3.getTiny().getLong()); + + pm.currentTransaction().rollback(); + TestTools.closePM(); + } + @Test public void testDataMigration() { PersistenceManager pm = TestTools.openPM(); @@ -1176,3 +1318,22 @@ public void testLocate() { } } + +class TestClassTinyRef extends ZooPC { + private TestClassTiny tiny; + + @SuppressWarnings("unused") + private TestClassTinyRef() { + //nothing + } + + public TestClassTinyRef(TestClassTiny tiny) { + this.tiny = tiny; + } + + public TestClassTiny getTiny() { + zooActivateRead(); + return tiny; + } + +} diff --git a/tst/org/zoodb/test/jdo/Test_039_SchemaEvolBug.java b/tst/org/zoodb/test/jdo/Test_039_SchemaEvolBug.java index ce573478..35359701 100644 --- a/tst/org/zoodb/test/jdo/Test_039_SchemaEvolBug.java +++ b/tst/org/zoodb/test/jdo/Test_039_SchemaEvolBug.java @@ -22,9 +22,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import java.net.URI; import java.net.URISyntaxException; +import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.Iterator; @@ -166,6 +168,28 @@ private Object[] init(boolean createFile) { return oids; } + private Object[] initProjectBranch() { + TestTools.defineSchema(ProjectBranchPCv1.class); + + PersistenceManager pm = TestTools.openPM(); + pm.currentTransaction().begin(); + Object[] oids = new Object[10]; + + UserPC[] users = new UserPC[2]; + users[0] = new UserPC(333+0, "userPB"+0); + users[1] = new UserPC(333+1, "userPB"+1); + + for (int i = 0; i < oids.length; i++) { + ProjectBranchPCv1 a1 = new ProjectBranchPCv1(234, "PB"+i, users[i%2]); + pm.makePersistent(a1); + oids[i] = pm.getObjectId(a1); + } + + pm.currentTransaction().commit(); + TestTools.closePM(); + return oids; + } + @Test public void testIssue66NoReflection() { //populate @@ -256,17 +280,70 @@ public void testIssue66NoReflection() { public void testFull() { //populate init(true); - + PersistenceManager pm = TestTools.openPM(); applyInternal(pm); pm.currentTransaction().commit(); TestTools.closePM(); - + + pm = TestTools.openPM(); + pm.currentTransaction().begin(); + + int n = 0; + @SuppressWarnings("unchecked") + Collection c = (Collection) pm.newQuery(AssetPCv2.class).execute(); + for (AssetPCv2 a: c) { + assertNotNull(a.getFile()); + assertNotNull(a.getFile().originalFileName()); + n++; + } + assertEquals(10, n); + + pm.currentTransaction().rollback(); + TestTools.closePM(); + + } + + @Test + public void testFullWithProjectBranch() { + //populate + init(true); + initProjectBranch(); + + ZooJdoProperties p = TestTools.getProps(); + p.setZooAutoCreateSchema(true); + PersistenceManager pm = TestTools.openPM(p); + applyInternal(pm); + ZooSchema schema = ZooJdoHelper.schema(pm); + ZooClass pbClass = schema.getClass(ProjectBranchPCv1.class.getName()); + pbClass.rename(ProjectBranchPCv2.class.getName()); + //pbClass.addField("bgImage", BackgroundImagePCv2.class); + //Iterator hi = pbClass.getHandleIterator(true); + //while (hi.hasNext()) { + // //this was an attempt to enforce evolution of objects. It resulted in + // //an NPE in the Deserializer when accessing the class-map with cls=null. + // hi.next(); + //} + //schema.addClass(BackgroundImagePCv2.class); + pm.currentTransaction().commit(); + TestTools.closePM(); + pm = TestTools.openPM(); pm.currentTransaction().begin(); - + int n = 0; @SuppressWarnings("unchecked") + Collection cPB = (Collection) + pm.newQuery(ProjectBranchPCv2.class).execute(); + for (ProjectBranchPCv2 a: cPB) { + assertNotNull(a.getName()); + assertNull(a.getBackgroundImage()); + n++; + } + assertEquals(10, n); + + n = 0; + @SuppressWarnings("unchecked") Collection c = (Collection) pm.newQuery(AssetPCv2.class).execute(); for (AssetPCv2 a: c) { assertNotNull(a.getFile()); @@ -274,12 +351,12 @@ public void testFull() { n++; } assertEquals(10, n); - + pm.currentTransaction().rollback(); TestTools.closePM(); } - + private void applyInternal(PersistenceManager pm) { final String fileFieldName = "file"; @@ -373,19 +450,19 @@ private void applyInternal(PersistenceManager pm) { } -// @Test -// public void testOrig() { -// //renameOrigToTestClasses(); -// PersistenceManager pm = TestTools.openPM("IdeaGarden-Backend.db"); -// applyInternal(pm); -// pm.currentTransaction().rollback(); -// TestTools.closePM(); -// } - + // @Test + // public void testOrig() { + // //renameOrigToTestClasses(); + // PersistenceManager pm = TestTools.openPM("IdeaGarden-Backend.db"); + // applyInternal(pm); + // pm.currentTransaction().rollback(); + // TestTools.closePM(); + // } + private void renameOrigToTestClasses() { PersistenceManager pm = TestTools.openPM("IdeaGarden-Backend.db"); pm.currentTransaction().begin(); - + ZooSchema schema = ZooJdoHelper.schema(pm); schema.getClass("eu.ideagarden.backend.persistent.ThumbnailPC").rename(FilePCv1.class.getName()); schema.getClass("eu.ideagarden.backend.persistent.AssetPC").rename(AssetPCv1.class.getName()); @@ -650,3 +727,220 @@ public void setName(final String name) { this.name = name; } } + + +class BackgroundImagePCv2 extends AbstractPC { + private int i1; + private int i2; + private int coordX, coordY; + +} + +class ProjectBranchPCv1 extends AbstractIdentifiedPC { + + private String name; + private UserPC creator; + private Date creationDate; + private Date lastUpdateDate; + // private ProjectSnapshotPC initialState; + // private ProjectBranchPC sourceBranch; + // private Map snapshots; + // private ProjectSnapshotPC currentState; + // private ProjectSnapshotPC baseSnapshot; + + + protected ProjectBranchPCv1() { + // hidden default constructor for ZooDB + } + + public ProjectBranchPCv1(final long id, final String name, final UserPC creator) { + //final IdPC currentStateId) { + //, final ProjectSnapshotPC initialState, final ProjectBranchPC sourceBranch) { + super(id); + this.name = name; + this.creator = creator; + this.creationDate = Calendar.getInstance().getTime(); + this.lastUpdateDate = this.creationDate; + // this.snapshots = new HashMap(); + // this.currentState = new ProjectSnapshotPC(currentStateId.getId(), creator, "current state", null); + // currentStateId.setIdentfee(this.currentState); + // this.initialState = initialState; + // this.sourceBranch = sourceBranch; + } + + + public String getName() { + this.zooActivateRead(); + return this.name; + } + + + public UserPC getCreator() { + this.zooActivateRead(); + return this.creator; + } + + + public Date getCreationDate() { + this.zooActivateRead(); + return this.creationDate; + } + + + + public Date getLastUpdateDate() { + this.zooActivateRead(); + return this.lastUpdateDate; + } + + + /** + * Sets lastUdate date to now. + */ + public void setLastUpdateDate() { + this.zooActivateWrite(); + this.lastUpdateDate = Calendar.getInstance().getTime(); + } + + + +} + +class ProjectBranchPCv2 extends AbstractIdentifiedPC { + + private String name; + private UserPC creator; + private Date creationDate; + private Date lastUpdateDate; + // private ProjectSnapshotPC initialState; + // private ProjectBranchPC sourceBranch; + // private Map snapshots; + // private ProjectSnapshotPC currentState; + // private ProjectSnapshotPC baseSnapshot; + private BackgroundImagePCv2 bgImage; + + + protected ProjectBranchPCv2() { + // hidden default constructor for ZooDB + } + + public ProjectBranchPCv2(final long id, final String name, final UserPC creator) { + super(id); + this.name = name; + this.creator = creator; + this.creationDate = Calendar.getInstance().getTime(); + this.lastUpdateDate = this.creationDate; + } + + + public BackgroundImagePCv2 getBackgroundImage() { + zooActivateRead(); + return bgImage; + } + + public String getName() { + this.zooActivateRead(); + return this.name; + } + + + public UserPC getCreator() { + this.zooActivateRead(); + return this.creator; + } + + + public Date getCreationDate() { + this.zooActivateRead(); + return this.creationDate; + } + + + + public Date getLastUpdateDate() { + this.zooActivateRead(); + return this.lastUpdateDate; + } + + + /** + * Sets lastUdate date to now. + */ + public void setLastUpdateDate() { + this.zooActivateWrite(); + this.lastUpdateDate = Calendar.getInstance().getTime(); + } + + // public ProjectSnapshotPC getInitialState() { + // this.zooActivateRead(); + // return this.initialState; + // } + // + // + // public Map getSnapshots() { + // this.zooActivateWrite(); + // return this.snapshots; + // } + // + // + // public ProjectSnapshotPC getCurrentState() { + // this.zooActivateWrite(); + // return this.currentState; + // } + // + // + // public ProjectBranchPC getSourceBranch() { + // this.zooActivateRead(); + // return this.sourceBranch; + // } + // + // + // public ProjectSnapshotPC getBaseSnapshot() { + // this.zooActivateRead(); + // return this.baseSnapshot; + // } + // + // + // public void setBaseSnapshot(final ProjectSnapshotPC baseSnapshot) { + // this.zooActivateWrite(); + // this.baseSnapshot = baseSnapshot; + // } + // + // + // @Override + // public ProjectBranchPC copy(final IdentityHashMap copiedObjects) { + // if (copiedObjects.containsKey(this)) { + // return (ProjectBranchPC) copiedObjects.get(this); + // } + // + // final ProjectBranchPC copy = new ProjectBranchPC(); + // copiedObjects.put(this, copy); + // this.copyInternal(copy, copiedObjects); + // return copy; + // } + // + // @Override + // protected void copyInternal(final AbstractPC abstractCopy, final IdentityHashMap copiedObjects) { + // final ProjectBranchPC copy = (ProjectBranchPC) abstractCopy; + // this.zooActivateRead(); + // copy.name = this.name; + // copy.creationDate = this.creationDate; + // copy.lastUpdateDate = this.lastUpdateDate; + // copy.creator = this.creator.copy(copiedObjects); + // if (this.baseSnapshot != null) { + // copy.baseSnapshot = this.baseSnapshot.copy(copiedObjects); + // } + // copy.currentState = this.currentState.copy(copiedObjects); + // if (this.initialState != null) { + // copy.initialState = this.initialState.copy(copiedObjects); + // } + // copy.snapshots = new HashMap(); + // for (final Entry entry : this.snapshots.entrySet()) { + // copy.snapshots.put(entry.getKey(), entry.getValue().copy(copiedObjects)); + // } + // if (this.sourceBranch != null) { + // copy.sourceBranch = this.sourceBranch.copy(copiedObjects); + // } + // super.copyInternal(copy, copiedObjects); + // } +} \ No newline at end of file