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.