[ 
https://issues.apache.org/jira/browse/JCR-1554?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12670446#action_12670446
 ] 

Martin Schreiber commented on JCR-1554:
---------------------------------------


It might be that the following explanation is not as it really is, so feel free 
to correct me:

An ItemState might have an overlayedState. What I have seen from debugging with 
lots of logging is that an itemState could have an overlayedState and this 
overlayedState could also have an overlayedState and so on. 
If there is a e.g. a property change the ItemStateManger(s) gets "informed" and 
the "stateModified" method is called. 
In transactions the the XAItemStateManager's (which is a sub class of the 
LocalItemStateManager) "stateModified" method is called.
If the state already exists the "pull()" method is called on that state 
(ItemState.pull()). The idea of the pull method is to sync up the modification 
counter with the overlayedState. And here is the issue IMHO because it is only 
done "one layer down" and not top down. 
If I saw it correctly, with the provided test class, setting a property the 
first time will end up in one ItemState, setting it the 2nd time you will have 
an itemState with an overlayedState, calling it the 3rd time, you have 
itemState->overlayedState->overlayedState. If than "pull()" is called to sync 
up the modCount, it is only done with the itemState->overlayedState and not 
with the itemState->overlayedState->overlayedState....thats IMHO the reason why 
you see it at the 3rd time and not before. 

To cut a long story short, here what i changed:

ItemState.java (line 152ff): 

original:

    synchronized void pull() {
        ItemState state = overlayedState;
        if (state != null) {
            // sync modification count
            copy(state, true);
        }
    }

my modification:

    synchronized void pull() {
        ItemState state = overlayedState;
        if (state != null) {
            // sync modification count top down
            if (state.isConnected()) {
                state.pull();
            }
            copy(state, true);
        }
    }


With that change I was able to a) build jackrabbit and b) run the provided 
TestNG successfully without the RollbackException (for both tests, setting the 
item property and deleting a node).

Jackrabbit guys, could you please take a look at my proposal.

Thanks!!




> StaleItemStateException with distributed transactions
> -----------------------------------------------------
>
>                 Key: JCR-1554
>                 URL: https://issues.apache.org/jira/browse/JCR-1554
>             Project: Jackrabbit Content Repository
>          Issue Type: Bug
>          Components: jackrabbit-core, transactions
>    Affects Versions: 1.4, core 1.4.2
>         Environment: WinXP, jdk1.5.0_14, jboss_4.2.2.GA, jackrabbit 1.4.2, 
> spring 2.5.3, spring-modules-jcr 0.8 (for integration of jackrabbit with 
> spring)
> PM of repository: OraclePersistenceManager; DB: Oracle
>            Reporter: Sven Rieckhoff
>         Attachments: jackrabbit-tx-bug.zip
>
>
> There seams to be a serious bug in jackrabbit when used in distributed 
> transactions. It does not occur with local transactions! And it seams to be 
> related to JCR-566.
> There are 2 scenarios where a StaleItemStateException occurs reproducible 
> that causes transactions to fail. All my operations (implemented in a custom 
> ServiceBean) such as setProperty() or deleteNode() run in separate 
> transactions. The transactions are configured through Spring Annotations 
> (@Transactional).
> Scenario A (setProperty):
> (1) multiple setProperty() with same property name on the same node (newly 
> created or already existent)
> => With the 3. setProperty() (and sometimes also the 5.), a 
> StaleItemStateException for the property state is raised when the transaction 
> is commited. Following setProperty invocations will not fail!
> Scenario B (deleteNode):
> (1) iterate 10 times:
> (1.1) create new node n and a subnode for n
> (1.2) delete node n
> => Deletion of node n raises a StaleItemStateException for node n in 
> iteration 1, 3 and (6 or 7), when the related transaction is commited. 
> Following deletions of node n will also fail with a predictable pattern.
> The Exception trace for scenario A (it's the same for scenario B, with one 
> difference: StaleItemStateException is raised for the node and not for the 
> property):
> org.springframework.transaction.UnexpectedRollbackException: JTA transaction 
> unexpectedly rolled back (maybe due to a timeout); nested exception is 
> javax.transaction.RollbackException: Error during one-phase commit
>       at 
> org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1031)
>       at 
> org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:709)
>       at 
> org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:678)
>       at 
> org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:321)
>       at 
> org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:116)
>       at 
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
>       at 
> org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
>       at $Proxy9.setNodeProperty(Unknown Source)
>       at 
> de.zeb.control.prototype.jrTxBug.test.TestJackrabbitTxBug.testTransactionBug001(TestJackrabbitTxBug.java:97)
>       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:585)
>       at org.testng.internal.MethodHelper.invokeMethod(MethodHelper.java:580)
>       at org.testng.internal.Invoker.invokeMethod(Invoker.java:478)
>       at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:607)
>       at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:874)
>       at 
> org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
>       at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
>       at org.testng.TestRunner.runWorkers(TestRunner.java:689)
>       at org.testng.TestRunner.privateRun(TestRunner.java:566)
>       at org.testng.TestRunner.run(TestRunner.java:466)
>       at org.testng.SuiteRunner.runTest(SuiteRunner.java:301)
>       at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:296)
>       at org.testng.SuiteRunner.privateRun(SuiteRunner.java:276)
>       at org.testng.SuiteRunner.run(SuiteRunner.java:191)
>       at org.testng.TestNG.createAndRunSuiteRunners(TestNG.java:808)
>       at org.testng.TestNG.runSuitesLocally(TestNG.java:776)
>       at org.testng.TestNG.run(TestNG.java:701)
>       at org.testng.remote.RemoteTestNG.run(RemoteTestNG.java:73)
>       at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:124)
> Caused by: javax.transaction.RollbackException: Error during one-phase commit
>       at 
> org.apache.geronimo.transaction.manager.TransactionImpl.commit(TransactionImpl.java:281)
>       at 
> org.apache.geronimo.transaction.manager.TransactionManagerImpl.commit(TransactionManagerImpl.java:143)
>       at 
> org.apache.geronimo.transaction.context.InheritableTransactionContext.complete(InheritableTransactionContext.java:196)
>       at 
> org.apache.geronimo.transaction.context.InheritableTransactionContext.commit(InheritableTransactionContext.java:146)
>       at 
> org.apache.geronimo.transaction.context.OnlineUserTransaction.commit(OnlineUserTransaction.java:80)
>       at 
> org.jencks.factory.UserTransactionFactoryBean$GeronimoUserTransaction.commit(UserTransactionFactoryBean.java:118)
>       at 
> org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1028)
>       ... 30 more
> Caused by: javax.transaction.xa.XAException
>       at 
> org.apache.jackrabbit.core.TransactionContext.prepare(TransactionContext.java:155)
>       at 
> org.apache.jackrabbit.core.XASessionImpl.commit(XASessionImpl.java:337)
>       at 
> org.apache.jackrabbit.jca.TransactionBoundXAResource.commit(TransactionBoundXAResource.java:39)
>       at 
> org.apache.geronimo.transaction.manager.WrapperNamedXAResource.commit(WrapperNamedXAResource.java:47)
>       at 
> org.apache.geronimo.transaction.manager.TransactionImpl.commit(TransactionImpl.java:272)
>       ... 36 more
> Caused by: org.apache.jackrabbit.core.TransactionException: Unable to prepare 
> transaction.
>       at 
> org.apache.jackrabbit.core.state.XAItemStateManager.prepare(XAItemStateManager.java:150)
>       at 
> org.apache.jackrabbit.core.TransactionContext.prepare(TransactionContext.java:138)
>       ... 40 more
> Caused by: org.apache.jackrabbit.core.state.StaleItemStateException: 
> bef3c056-bc91-4195-a35c-aa184182b5ad/{}TEST_PROPERTY has been modified 
> externally
>       at 
> org.apache.jackrabbit.core.state.SharedItemStateManager$Update.begin(SharedItemStateManager.java:620)
>       at 
> org.apache.jackrabbit.core.state.SharedItemStateManager.beginUpdate(SharedItemStateManager.java:843)
>       at 
> org.apache.jackrabbit.core.state.XAItemStateManager.prepare(XAItemStateManager.java:144)
>       ... 41 more
> When debugging into jackrabbit you will see, that the cause of the 
> StaleItemStateException is, that the local state und the overlayed state 
> differ in the value of the 'modCount' attribute: modCount of local state is 
> lower than modCount of overlayed state. Perhaps its a state caching problem...
>       
> I'm attaching a simple java application configured with maven and ready to 
> run standalone. The JCA container of JBoss is therefore replaced with jencks 
> in order to support distributed transactions. The configured repository uses 
> the InMemPersistenceManager. Both scenarios are implemented in a TestNG - 
> test, that catches the occuring TransactionExceptions and prints out the 
> stacktrace. Therefore you will see the exceptions, but the tests will not 
> fail.

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.

Reply via email to