Versioning sets: IllegalArgumentException
Posted by danielmeyer on January 20, 2009
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 bitronix.tm.internal.BitronixRollbackException: transaction was marked as rollback only and has been rolled back at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1031) at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:732) at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:701) at com.ontsys.fw.testutil.transaction.TransactionManager.commit(TransactionManager.java:129) at com.ontsys.db.inventory.products.dao.ProductSetDAOIntegrationTest.createProduct(ProductSetDAOIntegrationTest.java:409) at com.ontsys.db.inventory.products.dao.ProductSetDAOIntegrationTest.testRetrieveSetWithSetOfOne(ProductSetDAOIntegrationTest.java:123)
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 bitronix.tm.BitronixTransaction.fireBeforeCompletionEvent() 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 bitronix.tm.BitronixTransaction.fireBeforeCompletionEvent() 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 org.hibernate.property.BasicPropertyAccessor.BasicSetter.set(). The call stack at the method.invoke() call that will result in the IllegalArgumentException looks like this:
org.hibernate.property.BasicPropertyAccessor$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, java.io.Serializable) line: 234 org.hibernate.persister.entity.SingleTableEntityPersister(org.hibernate.persister.entity.AbstractEntityPersister).setIdentifier(java.lang.Object, java.io.Serializable, org.hibernate.EntityMode) line: 3624 org.hibernate.event.def.DefaultSaveEventListener(org.hibernate.event.def.AbstractSaveEventListener).performSave(java.lang.Object, java.io.Serializable, 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 org.hibernate.impl.SessionImpl.save(java.lang.String, java.lang.Object) line: 550 org.jboss.envers.synchronization.work.PersistentCollectionChangeWorkUnit.perform(org.hibernate.Session, java.lang.Object) line: 67 org.jboss.envers.synchronization.VersionsSync.executeInSession(org.hibernate.Session) line: 120 org.jboss.envers.synchronization.VersionsSync.beforeCompletion() line: 135 bitronix.tm.BitronixTransaction.fireBeforeCompletionEvent() line: 366 bitronix.tm.BitronixTransaction.commit() line: 142 bitronix.tm.BitronixTransactionManager.commit() line: 96 org.springframework.transaction.jta.JtaTransactionManager.doCommit(org.springframework.transaction.support.DefaultTransactionStatus) line: 1028 org.springframework.transaction.jta.JtaTransactionManager(org.springframework.transaction.support.AbstractPlatformTransactionManager).processCommit(org.springframework.transaction.support.DefaultTransactionStatus) line: 732 org.springframework.transaction.jta.JtaTransactionManager(org.springframework.transaction.support.AbstractPlatformTransactionManager).commit(org.springframework.transaction.TransactionStatus) 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.
Zoran said
Greatings,
You need more rest i think
Thanks
Zoran