Hmm, nasty. I think I can see what's going on (but a code fix will be
needed).
Looking at IsisTransactionManager#executeWithinTransaction() method, I see:
public <Q> Q executeWithinTransaction(final
TransactionalClosureWithReturn<Q> closure) {
final boolean initiallyInTransaction = inTransaction();
if (!initiallyInTransaction) {
startTransaction();
}
try {
closure.preExecute();
final Q retVal = closure.execute();
closure.onSuccess();
if (!initiallyInTransaction) {
endTransaction();
}
return retVal;
} catch (final RuntimeException ex) {
closure.onFailure();
if (!initiallyInTransaction) {
abortTransaction();
}
throw ex;
}
}
public boolean inTransaction() {
return getTransaction() != null &&
!getTransaction().getState().isComplete();
}
So presumably inTransaction is returning false, that there is no
transaction in progress.
~~~
Looking at IsisTransactionManager#endTransaction(), I see (simplified):
public synchronized void endTransaction() {
...
transactionLevel--;
if (transactionLevel == 0) {
...
// Isis' transaction
getTransaction().commit();
...
// JDO transaction
transactionalResource.endTransaction();
...
}
}
Unfortunately IsisTransaction#commit() does too much work:
public synchronized final void commit() {
...
// update services
doAudit(getChangedObjectProperties());
final String currentUser =
getTransactionManager().getAuthenticationSession().getUserName();
final Timestamp endTimestamp = Clock.getTimeAsJavaSqlTimestamp();
publishActionIfRequired(currentUser, endTimestamp);
doFlush();
publishedChangedObjectsIfRequired(currentUser, endTimestamp);
doFlush();
closeServices();
doFlush();
// set as comitted
setState(State.COMMITTED);
...
}
We want the services to be updated prior to the JDO transaction being
committed, but we want the Isis Transaction's state to be left as in
progress until after the JDO transaction is committed.
~~~
Do you agree? If so, I'll raise a ticket to fix (probably split
IsisTransaction#commit into a precommit and a postcommit.
Thx
Dan
On 29 April 2014 20:14, GESCONSULTOR - Óscar Bou <[email protected]>wrote:
> Hi to all.
>
> I have an "Asset" entity with inserted() and updated() lifecycle callback
> methods.
>
> Inside that "updated()" method, I have the following code:
>
> public void updated() {
> this.insertOrUpdateRelationships();
> }
>
> public void persisted() {
> this.insertOrUpdateRelationships();
> }
>
> private void insertOrUpdateRelationships() {
> // Insert/Update Relationships.
> for (final Relationship current :
> this.relationshipsDerivedFromAnnnotations()) {
>
> this.wrap(current.getSourceAsset()).addDirectlyImpactedAsset(current.getTargetAsset(),
> current.getType());
> }
> }
>
>
> When executed inside a test, the following exception is thrown right after
> finalizing the Test method (no exceptions thrown while in the Test body):
>
>
> javax.jdo.JDOException: Unexpected error during precommit
> at
> org.datanucleus.api.jdo.NucleusJDOHelper.getJDOExceptionForNucleusException(NucleusJDOHelper.java:596)
> at
> org.datanucleus.api.jdo.JDOTransaction.commit(JDOTransaction.java:165)
> at
> org.apache.isis.objectstore.jdo.datanucleus.DataNucleusObjectStore.commitJdoTransaction(DataNucleusObjectStore.java:278)
> at
> org.apache.isis.objectstore.jdo.datanucleus.DataNucleusObjectStore.endTransaction(DataNucleusObjectStore.java:260)
> at
> org.apache.isis.core.runtime.system.transaction.IsisTransactionManager.endTransaction(IsisTransactionManager.java:406)
> at
> org.apache.isis.core.integtestsupport.IsisSystemForTest.commitTran(IsisSystemForTest.java:610)
> at
> org.apache.isis.core.integtestsupport.IntegrationTestAbstract$IsisTransactionRule$1.evaluate(IntegrationTestAbstract.java:164)
> at
> org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2$1.evaluate(JUnitRuleMockery2.java:149)
> at
> org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:168)
> at org.junit.rules.RunRules.evaluate(RunRules.java:20)
> at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
> at
> org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
> at
> org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
> at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
> at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
> at
> org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
> at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
> at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
> at
> org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
> at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
> at
> org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
> at
> org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
> at
> org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
> at
> org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
> at
> org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
> at
> org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
> NestedThrowablesStackTrace:
> java.lang.RuntimeException:
> org.apache.isis.core.metamodel.spec.DomainModelException: Callback failed.
> Calling
> UpdatedCallbackFacetViaMethod[type=UpdatedCallbackFacet,methods=[public
> void com.xms.framework.architecture.domain.model.Asset.updated()]] on
> PojoAdapter@6dc5f0ba[PR~:com.xms.framework.architecture.domain.model.business.extensions.product.Product:L_0,specification=Product,version=null,pojo-toString=
> [name=Product, [tenantId=301, [id=2ED03E03-2894-485B-8491-CE88230788B2,
> class
> name=com.xms.framework.architecture.domain.model.business.extensions.product.Product]]],pojo-hash=#64164b49]
> at
> org.apache.isis.objectstore.jdo.datanucleus.persistence.FrameworkSynchronizer.withLogging(FrameworkSynchronizer.java:278)
> at
> org.apache.isis.objectstore.jdo.datanucleus.persistence.FrameworkSynchronizer.withLogging(FrameworkSynchronizer.java:287)
> at
> org.apache.isis.objectstore.jdo.datanucleus.persistence.FrameworkSynchronizer.postStoreProcessingFor(FrameworkSynchronizer.java:140)
> at
> org.apache.isis.objectstore.jdo.datanucleus.persistence.IsisLifecycleListener$2.doRun(IsisLifecycleListener.java:95)
> at
> org.apache.isis.objectstore.jdo.datanucleus.persistence.IsisLifecycleListener$RunnableAbstract.run(IsisLifecycleListener.java:201)
> at
> org.apache.isis.objectstore.jdo.datanucleus.persistence.IsisLifecycleListener.withLogging(IsisLifecycleListener.java:180)
> at
> org.apache.isis.objectstore.jdo.datanucleus.persistence.IsisLifecycleListener.postStore(IsisLifecycleListener.java:91)
> at
> org.datanucleus.api.jdo.JDOCallbackHandler.postStore(JDOCallbackHandler.java:158)
> at
> org.datanucleus.state.JDOStateManager.flush(JDOStateManager.java:5035)
> at
> org.datanucleus.state.JDOStateManager.runReachability(JDOStateManager.java:3652)
> at
> org.datanucleus.store.fieldmanager.ReachabilityFieldManager.processPersistable(ReachabilityFieldManager.java:69)
> at
> org.datanucleus.store.fieldmanager.ReachabilityFieldManager.storeObjectField(ReachabilityFieldManager.java:121)
> at
> org.datanucleus.state.JDOStateManager.providedObjectField(JDOStateManager.java:1269)
> at
> com.xms.framework.architecture.domain.model.business.extensions.businessservice.BusinessService.jdoProvideField(BusinessService.java)
> at
> com.xms.framework.api.domain.model.isis.AbstractXMSDomainObject.jdoProvideFields(AbstractXMSDomainObject.java)
> at
> org.datanucleus.state.JDOStateManager.provideFields(JDOStateManager.java:1346)
> at
> org.datanucleus.state.JDOStateManager.runReachability(JDOStateManager.java:3678)
> at
> org.datanucleus.ExecutionContextImpl.performReachabilityAtCommit(ExecutionContextImpl.java:4437)
> at
> org.datanucleus.ExecutionContextImpl.preCommit(ExecutionContextImpl.java:4230)
> at
> org.datanucleus.ExecutionContextImpl.transactionPreCommit(ExecutionContextImpl.java:654)
> at
> org.datanucleus.TransactionImpl.internalPreCommit(TransactionImpl.java:379)
> at org.datanucleus.TransactionImpl.commit(TransactionImpl.java:268)
> at
> org.datanucleus.api.jdo.JDOTransaction.commit(JDOTransaction.java:98)
> at
> org.apache.isis.objectstore.jdo.datanucleus.DataNucleusObjectStore.commitJdoTransaction(DataNucleusObjectStore.java:278)
> at
> org.apache.isis.objectstore.jdo.datanucleus.DataNucleusObjectStore.endTransaction(DataNucleusObjectStore.java:260)
> at
> org.apache.isis.core.runtime.system.transaction.IsisTransactionManager.endTransaction(IsisTransactionManager.java:406)
> at
> org.apache.isis.core.integtestsupport.IsisSystemForTest.commitTran(IsisSystemForTest.java:610)
> at
> org.apache.isis.core.integtestsupport.IntegrationTestAbstract$IsisTransactionRule$1.evaluate(IntegrationTestAbstract.java:164)
> at
> org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2$1.evaluate(JUnitRuleMockery2.java:149)
> at
> org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:168)
> at org.junit.rules.RunRules.evaluate(RunRules.java:20)
> at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
> at
> org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
> at
> org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
> at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
> at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
> at
> org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
> at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
> at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
> at
> org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
> at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
> at
> org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
> at
> org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
> at
> org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
> at
> org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
> at
> org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
> at
> org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
> Caused by: org.apache.isis.core.metamodel.spec.DomainModelException:
> Callback failed. Calling
> UpdatedCallbackFacetViaMethod[type=UpdatedCallbackFacet,methods=[public
> void com.xms.framework.architecture.domain.model.Asset.updated()]] on
> PojoAdapter@6dc5f0ba[PR~:com.xms.framework.architecture.domain.model.business.extensions.product.Product:L_0,specification=Product,version=null,pojo-toString=
> [name=Product, [tenantId=301, [id=2ED03E03-2894-485B-8491-CE88230788B2,
> class
> name=com.xms.framework.architecture.domain.model.business.extensions.product.Product]]],pojo-hash=#64164b49]
> at
> org.apache.isis.core.metamodel.facets.object.callbacks.CallbackUtils.callCallback(CallbackUtils.java:37)
> at
> org.apache.isis.objectstore.jdo.datanucleus.persistence.FrameworkSynchronizer$2.run(FrameworkSynchronizer.java:176)
> at
> org.apache.isis.objectstore.jdo.datanucleus.persistence.FrameworkSynchronizer$7.call(FrameworkSynchronizer.java:291)
> at
> org.apache.isis.objectstore.jdo.datanucleus.persistence.FrameworkSynchronizer$7.call(FrameworkSynchronizer.java:287)
> at
> org.apache.isis.objectstore.jdo.datanucleus.persistence.FrameworkSynchronizer.withLogging(FrameworkSynchronizer.java:276)
> ... 46 more
> Caused by: java.lang.IllegalStateException: Transaction already active
> at
> org.apache.isis.objectstore.jdo.datanucleus.DataNucleusObjectStore.beginJdoTransaction(DataNucleusObjectStore.java:270)
> at
> org.apache.isis.objectstore.jdo.datanucleus.DataNucleusObjectStore.startTransaction(DataNucleusObjectStore.java:256)
> at
> org.apache.isis.core.runtime.system.transaction.IsisTransactionManager.startTransaction(IsisTransactionManager.java:290)
> at
> org.apache.isis.core.runtime.system.transaction.IsisTransactionManager.executeWithinTransaction(IsisTransactionManager.java:219)
> at
> org.apache.isis.core.runtime.transaction.facets.ActionInvocationFacetWrapTransaction.invoke(ActionInvocationFacetWrapTransaction.java:54)
> at
> org.apache.isis.core.metamodel.specloader.specimpl.ObjectActionImpl.execute(ObjectActionImpl.java:342)
> at
> org.apache.isis.core.wrapper.internal.DomainObjectInvocationHandler.handleActionMethod(DomainObjectInvocationHandler.java:509)
> at
> org.apache.isis.core.wrapper.internal.DomainObjectInvocationHandler.invoke(DomainObjectInvocationHandler.java:236)
> at
> org.apache.isis.core.wrapper.internal.InvocationHandlerMethodInterceptor.intercept(InvocationHandlerMethodInterceptor.java:37)
> at
> com.xms.framework.architecture.domain.model.business.extensions.product.Product$$EnhancerByCGLIB$$1432358f.addDirectlyImpactedAsset(<generated>)
> at
> com.xms.framework.architecture.domain.model.Asset.insertOrUpdateRelationships(Asset.java:847)
> at
> com.xms.framework.architecture.domain.model.Asset.updated(Asset.java:837)
> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> at
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
> at
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
> at java.lang.reflect.Method.invoke(Method.java:597)
> at
> org.apache.isis.core.commons.lang.MethodExtensions.invoke(MethodExtensions.java:50)
> at
> org.apache.isis.core.commons.lang.MethodExtensions.invoke(MethodExtensions.java:45)
> at
> org.apache.isis.core.commons.lang.MethodUtil.invoke(MethodUtil.java:35)
> at
> org.apache.isis.core.metamodel.adapter.util.AdapterInvokeUtils.invokeAll(AdapterInvokeUtils.java:40)
> at
> org.apache.isis.core.progmodel.facets.object.callbacks.update.UpdatedCallbackFacetViaMethod.invoke(UpdatedCallbackFacetViaMethod.java:63)
> at
> org.apache.isis.core.metamodel.facets.object.callbacks.CallbackUtils.callCallback(CallbackUtils.java:35)
> ... 50 more
>
>
>
> If I change the code to this one (removing the "wrap") it works ok...
>
> public void updated() {
> this.insertOrUpdateRelationships();
> }
>
> public void persisted() {
> this.insertOrUpdateRelationships();
> }
>
> private void insertOrUpdateRelationships() {
> // Insert/Update Relationships.
> for (final Relationship current :
> this.relationshipsDerivedFromAnnnotations()) {
>
> current.getSourceAsset().addDirectlyImpactedAsset(current.getTargetAsset(),
> current.getType());
> }
> }
>
>
> Seems that it's not properly detected that a transaction is opened.
>
> Seems there's a hard-to-debug bug deep inside?
>
>
> Thanks,
>
> Oscar
>
>