Versioning sets: IllegalArgumentException

We’re experiencing an issue when we try to use envers to version a persistent object that holds a set of other persistent objects, when we’re using Spring’s JTA transaction manager. We’re consistently getting a

org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is transaction was marked as rollback only and has been rolled back
at org.springframework.transaction.jta.JtaTransactionManager.doCommit(
at com.ontsys.fw.testutil.transaction.TransactionManager.commit(
at com.ontsys.db.inventory.products.dao.ProductSetDAOIntegrationTest.createProduct(
at com.ontsys.db.inventory.products.dao.ProductSetDAOIntegrationTest.testRetrieveSetWithSetOfOne(

The problem doesn’t happen when we’re using Spring’s HibernateTransactionManager.

The thing that fails succeeds at first

Something that was throwing me for a loop (so to speak) was that the place that is throwing the exception succeeds several times first.  I had a breakpoint in at the call to synchronization.beforeCompletion(), but I stepped all the way into this and back out, and it worked — the first time.

Counting the times till it fails

I had the idea to keep resuming from that breakpoint to count how many times it succeeded before the exception blew up my test.  I found that it was after it hit the breakpoint in the eighth time that the beforeCompletion event never returned.  So I set the Hit Count on that breakpoint to 8 (so it wouldn’t break there until the eighth time).

Let’s see that in super slow-motion…

Turns out that it’s the seventh time it hits org.hibernate.event.def.AbstractSaveEventListener.performSave() that the exception occurs, down in a call of  The call stack at the method.invoke() call that will result in the IllegalArgumentException looks like this:$BasicSetter.set(java.lang.Object, java.lang.Object, org.hibernate.engine.SessionFactoryImplementor) line: 66	
org.hibernate.tuple.entity.PojoEntityTuplizer(org.hibernate.tuple.entity.AbstractEntityTuplizer).setIdentifier(java.lang.Object, line: 234	
org.hibernate.persister.entity.SingleTableEntityPersister(org.hibernate.persister.entity.AbstractEntityPersister).setIdentifier(java.lang.Object,, org.hibernate.EntityMode) line: 3624	
org.hibernate.event.def.DefaultSaveEventListener(org.hibernate.event.def.AbstractSaveEventListener).performSave(java.lang.Object,, org.hibernate.persister.entity.EntityPersister, boolean, java.lang.Object, org.hibernate.event.EventSource, boolean) line: 194	
org.hibernate.event.def.DefaultSaveEventListener(org.hibernate.event.def.AbstractSaveEventListener).saveWithGeneratedId(java.lang.Object, java.lang.String, java.lang.Object, org.hibernate.event.EventSource, boolean) line: 144	
org.hibernate.event.def.DefaultSaveEventListener(org.hibernate.event.def.DefaultSaveOrUpdateEventListener).saveWithGeneratedOrRequestedId(org.hibernate.event.SaveOrUpdateEvent) line: 210	
org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(org.hibernate.event.SaveOrUpdateEvent) line: 56	
org.hibernate.event.def.DefaultSaveEventListener(org.hibernate.event.def.DefaultSaveOrUpdateEventListener).entityIsTransient(org.hibernate.event.SaveOrUpdateEvent) line: 195	
org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(org.hibernate.event.SaveOrUpdateEvent) line: 50	
org.hibernate.event.def.DefaultSaveEventListener(org.hibernate.event.def.DefaultSaveOrUpdateEventListener).onSaveOrUpdate(org.hibernate.event.SaveOrUpdateEvent) line: 93	
org.hibernate.impl.SessionImpl.fireSave(org.hibernate.event.SaveOrUpdateEvent) line: 562, java.lang.Object) line: 550, java.lang.Object) line: 67	
org.jboss.envers.synchronization.VersionsSync.executeInSession(org.hibernate.Session) line: 120	
org.jboss.envers.synchronization.VersionsSync.beforeCompletion() line: 135 line: 366 line: 142 line: 96	
org.springframework.transaction.jta.JtaTransactionManager.doCommit( line: 1028	
org.springframework.transaction.jta.JtaTransactionManager( line: 732	
org.springframework.transaction.jta.JtaTransactionManager( line: 701	
com.ontsys.fw.testutil.transaction.TransactionManager.commit() line: 129	
com.ontsys.db.inventory.products.dao.ProductSetDAOIntegrationTest.createProduct(com.ontsys.db.inventory.products.po.ProductPO, java.lang.String) line: 409	
com.ontsys.db.inventory.products.dao.ProductSetDAOIntegrationTest.testRetrieveSetWithSetOfOne() line: 123	

A Closer Look at the InvalidArgumentException

What is this IllegalArgumentException that BasicPropertyAccessor.BasicSetter.set() catches when it tries the method.invoke()?  The contained message is:

object is not an instance of declaring class

BasicPropertyAccessor.BasicSetter.set() then logs these messages:

IllegalArgumentException in class: com.ontsys.fw.datatypes.BaseTO, setter method of property: key
expected type: java.lang.String, actual value: java.lang.String

(Hmm, expected a java.lang.String but got a java.lang.String, huh?) …and then throwing an exception:

org.hibernate.PropertyAccessException: IllegalArgumentException occurred while calling setter of com.ontsys.fw.datatypes.BaseTO.key

Going back to the IllegalArgumentException’s message, though: why would our object not be an instance of the declaring class?

That, my friends, will have to wait until next time.

Remote debugging in JBoss

Have you ever wished you could debug your war project in Eclipse while it was deployed and running on JBoss? This is called remote debugging, and here’s how you set up to do it (on JBoss 4.2.2.GA running on Java 5, anyway):

  1. Make a backup copy of your current JBoss run.bat
  2. Open run.bat and find the lines that look like this:
    rem JPDA options. Uncomment and modify as appropriate to enable remote debugging.
    rem set JAVA_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y %JAVA_OPTS%
  3. Uncomment the second line, and save.
  4. Start Jboss. (It won’t do much until you connect a debugger to it.)
  5. In Eclipse, open your project, and on the toolbar click on the debugger bug arrow, then Open Debug Dialog.
  6. Choose Remote Java Application, right-click, New… and update the port to the address in the line you uncommented (possibly 8787).
  7. Click Debug
  8. Set a breakpoint somewhere in your code or a library that has source attached
  9. Exercise your project (via a web service, for instance) and when it hits one of your breakpoints, it will break in Eclipse.

Note to self: Avoid remote debugging for my own code — I should be adding log messages instead!