Hello everyone!

We've run into a data migration-test problem, and I'm wondering if
anyone would have any advice on how to proceed...

We're building a system that needs to allow hot-swapping of
application builds while customers are actively using our webapp, and
that means we need to support data object "schema" migration in-
place.  We do this by implementing LoadCallback for version migration,
and @Deprecated-ing no-longer-used fields.  So far, so good.

Thing is, we'd like to build up a test suite that allows developers to
write unit tests for data migration against a local environment. That
means no uploading to GAE, but using
com.google.appengine.tools.development.testing.LocalServiceTestHelper
instead.

We have two "classes" directories: Both containing DataNucleus-JDO-
enhanced .class files, one of the "old" schema, and one of the
current.

We have a MigrationTestHelper that creates a GroovyClassLoader with
the old schema set as its classpath.  The targetted class under test
is found, instantiated, and passed to a test-supplied Closure that
initializes the object with the old data to be migrated.  The Helper
calls PersistenceManager.makePersistent and returns
pm.getObjectId(newlyPersisted).getKey().

The second half of the test involves creating a new GroovyClassLoader
and setting the context class loader:
Thread.currentThread().setContextClassLoader(loader).  Then we use a
different PersistenceManager (from a different
PersistenceManagerFactory) to load the object by its Key.

What happens is we get a ClassCastException:
java.lang.ClassCastException: java.lang.Long cannot be cast to
java.util.List
        at com.x.model.User.jdoReplaceField(User.groovy)
        at com.x.model.User.jdoReplaceFields(User.groovy)
        at
org.datanucleus.state.JDOStateManagerImpl.replaceFields(JDOStateManagerImpl.java:
2772)
        at
org.datanucleus.state.JDOStateManagerImpl.replaceFields(JDOStateManagerImpl.java:
2791)
        at
org.datanucleus.store.appengine.DatastorePersistenceHandler.fetchObject(DatastorePersistenceHandler.java:
480)

It *appears* as though the JDOStateManagerImpl uses an array of fields
to get/set values.  You can crack open the enhanced .class files and
see there's a method called __jdoFieldNamesInit() that sets up a
static list of Fields.

The old User contains the field: "balance", which is a Long.
The new User contains the fields: "balance", which is @Deprecated and
is Long, and "_balances", which is a List.

Looking in the .class files, it looks like each field is ordered
alphabetically, so in old User, "balance" is index 0, where in new
User, "balance" is index 1 and "_balances" is index 0.  Hence the
ClassCastException.

I'm stuck on how to proceed.  I'd have thought that fields would be
replaced based on field name, not on an index...  Especially an index
that we don't appear to be able to control.  But if it's the case that
fields are placed by index on load, how does data migration work on
AppEngine itself?  Our migration code works fine there...

Is this a DataNucleus issue?  An issue with LocalServiceTestHelper?
An issue of developer sanity? ;)

Any insight would be greatly appriciated!

Thanks!
Jason

-- 
You received this message because you are subscribed to the Google Groups 
"Google App Engine for Java" group.
To post to this group, send email to google-appengine-java@googlegroups.com.
To unsubscribe from this group, send email to 
google-appengine-java+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/google-appengine-java?hl=en.

Reply via email to