You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@jackrabbit.apache.org by Dominik Klaholt <do...@mail.upb.de> on 2010/08/06 02:19:27 UTC

Jackrabbit, JCA, JBoss, EJB and transactions

Hello,

i have a problem using jackrabbit with EJB transactions. I will try to 
explain what i have done and understood so far. Any corrections are 
really welcome.
I am using Jackrabbit 2.1.0 within JBoss 5.1.0 as a XA-datasource by 
help of the JCA adapter that is provided by Jackrabbit. My jcr-ds.xml 
contains the flags

<config-property name="bindSessionToTransaction" 
type="java.lang.Boolean">true</config-property>

and

<application-managed-security/>

so that the JCA-session-handles are bound to the container/bean managed 
transactions and the actual JCR sessions within the JCA connection pool are
distinguished by the parameters of the 'repository.login()' calls.

The following test-case works fine:

(1)Repository rep = (Repository) jndiContext.lookup("java:/jcr/local");
     Session s1 = rep.login(new SimpleCredentials(USER1, 
USER1.toCharArray()));
     Node nodeBys1 = s1.getRootNode().addNode("aNode");
     nodeBys1.setProperty("aProperty", USER1);
     s1.save();
     String id = nodeBys1.getIdentifier();
     s1.logout();

(2)Session s2 = rep.login(new SimpleCredentials(USER2, 
USER2.toCharArray()));
     s2.getNodeByIdentifier(id).setProperty("aProperty", USER2);
     s2.save();
     s2.logout();

(3)Session s1 = rep.login(new SimpleCredentials(USER1, 
USER1.toCharArray()));
     AssertJUnit.assertEquals(USER2, 
s1.getNodeByIdentifier(id).getProperty("aProperty").getString());

(1) User 1 logs in and creates a node with a property. (2) Then user 2 
logs in and alters the property of the
newly created node. (3) Finally user 1 logs in again and reads the 
property. He reads the value user 2 has written.

Now, i want to encapsulate each of these three steps in one method of a 
Stateless Session Bean. E.g. (1) looks like this:

     public String createNode(String username) throws Exception {
         InitialContext jndiContext = new InitialContext();
         Repository rep = (Repository) 
jndiContext.lookup("java:/jcr/local");
         Session s = rep.login(new SimpleCredentials(username, 
username.toCharArray()));
         Node node = s.getRootNode().addNode("aNode");
         node.setProperty("aProperty", username);
         s.save();
         return node.getIdentifier();
     }

Note, that this method will automatically be encapsulated by a container 
managed transaction. And due to the bindSessionToTransaction-flag in the 
jcr-ds.xml, the
JCASessionHandle used within the container managed transaction will be 
automatically closed once the transaction commits (the JCASessionHandle 
will be closed, the
associated JCR session stays alive within the JCA connection pool).
The above mentioned 3 steps of the simple test-case can now be expressed 
as a sequence of method-calls of the stateless EJB:

(1)jcrTest = (JCRTestBeanLocal) 
jndiContext.lookup("test/JCRTestBean/local");
     id = jcrTest.createNode(USER1);

(2)jcrTest.alterProperty(id, USER2, USER2);

(3)AssertJUnit.assertEquals(USER2, jcrTest.getProperty(id, USER1))

However, this test-case fails, as in step (3) user 1 does not read the 
changes committed by user 2. Interestingly the test-case passes once i 
deactivate transaction
support for step (1):

     @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
     public String createNode(String username) throws Exception {...}

Does anyone know what i could do to make the test-case pass without 
deactivating transaction support?
Any hints would be really, really appreciated.

Best regards,
Dominik


Below are the EJB-methods for the steps (2) and (3):

     public void alterProperty(String id, String newValue, String 
username) throws Exception {
         InitialContext jndiContext = new InitialContext();
         Repository rep = (Repository) 
jndiContext.lookup("java:/jcr/local");
         Session s = rep.login(new SimpleCredentials(username, 
username.toCharArray()));
         s.getNodeByIdentifier(id).setProperty("aProperty", newValue);
         s.save();
     }

     public String getProperty(String id, String username) throws 
Exception {
         InitialContext jndiContext = new InitialContext();
         Repository rep = (Repository) 
jndiContext.lookup("java:/jcr/local");
         Session s = rep.login(new SimpleCredentials(username, 
username.toCharArray()));
         return 
s.getNodeByIdentifier(id).getProperty("aProperty").getString();
     }

Re: Jackrabbit, JCA, JBoss, EJB and transactions

Posted by Dominik Klaholt <do...@mail.upb.de>.
Hi,

thanks for the quick answer. "Unfortunately" I cannot reproduce my 
problem as a test-method of XATest. So, it's either a problem with JCA 
or, which is most probable in most of the cases, it's just me doing 
something terribly wrong.

Best regards
Dominik

Am 12.08.2010 18:32, schrieb Jukka Zitting:
> Hi,
>
> On Thu, Aug 12, 2010 at 6:15 PM, Dominik Klaholt<do...@mail.upb.de>  wrote:
>    
>> please don't mind, but I have to bring this up again... I'm still looking
>> into this problem. My question now is: When two users have two sessions in
>> use concurrently, what exactly happens once one of the users makes a
>> Workspace-Write? I guess the session of the other user gets updated somehow,
>> correct?
>>      
> Yes, that's correct.
>
>    
>> Can anyone hint me to the java-class or part of Jackrabbit that is
>> responsible for synchronizing the sessions?
>>      
> The code is in jackrabbit-core, look for SharedItemStateManager [1]
> and related classes.
>
> You may also want to take a look at the XATest class [2] for test
> cases related to transactions. Can you reproduce your problem in a
> test like that?
>
> [1] http://svn.apache.org/repos/asf/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
> [2] http://svn.apache.org/repos/asf/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/XATest.java
>
> BR,
>
> Jukka Zitting
>    

Re: Jackrabbit, JCA, JBoss, EJB and transactions

Posted by Jukka Zitting <ju...@gmail.com>.
Hi,

On Thu, Aug 12, 2010 at 6:15 PM, Dominik Klaholt <do...@mail.upb.de> wrote:
> please don't mind, but I have to bring this up again... I'm still looking
> into this problem. My question now is: When two users have two sessions in
> use concurrently, what exactly happens once one of the users makes a
> Workspace-Write? I guess the session of the other user gets updated somehow,
> correct?

Yes, that's correct.

> Can anyone hint me to the java-class or part of Jackrabbit that is
> responsible for synchronizing the sessions?

The code is in jackrabbit-core, look for SharedItemStateManager [1]
and related classes.

You may also want to take a look at the XATest class [2] for test
cases related to transactions. Can you reproduce your problem in a
test like that?

[1] http://svn.apache.org/repos/asf/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
[2] http://svn.apache.org/repos/asf/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/XATest.java

BR,

Jukka Zitting

Re: Jackrabbit, JCA, JBoss, EJB and transactions

Posted by Dominik Klaholt <do...@mail.upb.de>.
Hello,

please don't mind, but I have to bring this up again... I'm still 
looking into this problem. My question now is: When two users have two 
sessions in use concurrently, what exactly happens once one of the users 
makes a Workspace-Write? I guess the session of the other user gets 
updated somehow, correct? Can anyone hint me to the java-class or part 
of Jackrabbit that is responsible for synchronizing the sessions?

Thanks
Dominik

Am 06.08.2010 16:38, schrieb Dominik Klaholt:
> I have simplified the test-case that shows the - in my opinion - 
> questionalbe behavior. Now it's without the confusing EJB stuff - just 
> Jackrabbit and transactions. Any thoughts?
>
>         /**
>          * The following test-case works, IF step 1 is done without 
> being encapsulated in a transaction (just remove the
>          * utx-lines in step 1 and add the 's1.logout()')
>          */
>         String id;
>         InitialContext jndiContext = new InitialContext();
>         rep = (Repository) jndiContext.lookup("java:/jcr/local");
>
>         // Step 1: user 1 acquires session and writes, then logs out
>         utx = (UserTransaction) jndiContext.lookup("UserTransaction");
>         utx.begin();
>         Session s1 = rep.login(new SimpleCredentials(USER1, 
> USER1.toCharArray()));
>         Node nodeBys1 = s1.getRootNode().addNode("aNode");
>         nodeBys1.setProperty("aProperty", USER1);
>         s1.save();
>         id = nodeBys1.getIdentifier();
>         // s1.logout();
>         utx.commit();
>
>         // Step 2: user 2 acquires session, alters value written by 
> user 1, then logs out
>         utx = (UserTransaction) jndiContext.lookup("UserTransaction");
>         utx.begin();
>         Session s2 = rep.login(new SimpleCredentials(USER2, 
> USER2.toCharArray()));
>         s2.getNodeByIdentifier(id).setProperty("aProperty", USER2);
>         s2.save();
>         utx.commit();
>
>         // Step 3: user 1 acquires session again, reads the property 
> altered by user 2... but does not read the correct
>         // value
>         utx = (UserTransaction) jndiContext.lookup("UserTransaction");
>         utx.begin();
>         Session s = rep.login(new SimpleCredentials(USER1, 
> USER1.toCharArray()));
>         AssertJUnit.assertEquals(USER2, 
> s.getNodeByIdentifier(id).getProperty("aProperty").getString());
>         utx.commit();
>
>
> Am 06.08.2010 02:19, schrieb Dominik Klaholt:
>> Hello,
>>
>> i have a problem using jackrabbit with EJB transactions. I will try 
>> to explain what i have done and understood so far. Any corrections 
>> are really welcome.
>> I am using Jackrabbit 2.1.0 within JBoss 5.1.0 as a XA-datasource by 
>> help of the JCA adapter that is provided by Jackrabbit. My jcr-ds.xml 
>> contains the flags
>>
>> <config-property name="bindSessionToTransaction" 
>> type="java.lang.Boolean">true</config-property>
>>
>> and
>>
>> <application-managed-security/>
>>
>> so that the JCA-session-handles are bound to the container/bean 
>> managed transactions and the actual JCR sessions within the JCA 
>> connection pool are
>> distinguished by the parameters of the 'repository.login()' calls.
>>
>> The following test-case works fine:
>>
>> (1)Repository rep = (Repository) jndiContext.lookup("java:/jcr/local");
>>     Session s1 = rep.login(new SimpleCredentials(USER1, 
>> USER1.toCharArray()));
>>     Node nodeBys1 = s1.getRootNode().addNode("aNode");
>>     nodeBys1.setProperty("aProperty", USER1);
>>     s1.save();
>>     String id = nodeBys1.getIdentifier();
>>     s1.logout();
>>
>> (2)Session s2 = rep.login(new SimpleCredentials(USER2, 
>> USER2.toCharArray()));
>>     s2.getNodeByIdentifier(id).setProperty("aProperty", USER2);
>>     s2.save();
>>     s2.logout();
>>
>> (3)Session s1 = rep.login(new SimpleCredentials(USER1, 
>> USER1.toCharArray()));
>>     AssertJUnit.assertEquals(USER2, 
>> s1.getNodeByIdentifier(id).getProperty("aProperty").getString());
>>
>> (1) User 1 logs in and creates a node with a property. (2) Then user 
>> 2 logs in and alters the property of the
>> newly created node. (3) Finally user 1 logs in again and reads the 
>> property. He reads the value user 2 has written.
>>
>> Now, i want to encapsulate each of these three steps in one method of 
>> a Stateless Session Bean. E.g. (1) looks like this:
>>
>>     public String createNode(String username) throws Exception {
>>         InitialContext jndiContext = new InitialContext();
>>         Repository rep = (Repository) 
>> jndiContext.lookup("java:/jcr/local");
>>         Session s = rep.login(new SimpleCredentials(username, 
>> username.toCharArray()));
>>         Node node = s.getRootNode().addNode("aNode");
>>         node.setProperty("aProperty", username);
>>         s.save();
>>         return node.getIdentifier();
>>     }
>>
>> Note, that this method will automatically be encapsulated by a 
>> container managed transaction. And due to the 
>> bindSessionToTransaction-flag in the jcr-ds.xml, the
>> JCASessionHandle used within the container managed transaction will 
>> be automatically closed once the transaction commits (the 
>> JCASessionHandle will be closed, the
>> associated JCR session stays alive within the JCA connection pool).
>> The above mentioned 3 steps of the simple test-case can now be 
>> expressed as a sequence of method-calls of the stateless EJB:
>>
>> (1)jcrTest = (JCRTestBeanLocal) 
>> jndiContext.lookup("test/JCRTestBean/local");
>>     id = jcrTest.createNode(USER1);
>>
>> (2)jcrTest.alterProperty(id, USER2, USER2);
>>
>> (3)AssertJUnit.assertEquals(USER2, jcrTest.getProperty(id, USER1))
>>
>> However, this test-case fails, as in step (3) user 1 does not read 
>> the changes committed by user 2. Interestingly the test-case passes 
>> once i deactivate transaction
>> support for step (1):
>>
>>     @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
>>     public String createNode(String username) throws Exception {...}
>>
>> Does anyone know what i could do to make the test-case pass without 
>> deactivating transaction support?
>> Any hints would be really, really appreciated.
>>
>> Best regards,
>> Dominik
>>
>>
>> Below are the EJB-methods for the steps (2) and (3):
>>
>>     public void alterProperty(String id, String newValue, String 
>> username) throws Exception {
>>         InitialContext jndiContext = new InitialContext();
>>         Repository rep = (Repository) 
>> jndiContext.lookup("java:/jcr/local");
>>         Session s = rep.login(new SimpleCredentials(username, 
>> username.toCharArray()));
>>         s.getNodeByIdentifier(id).setProperty("aProperty", newValue);
>>         s.save();
>>     }
>>
>>     public String getProperty(String id, String username) throws 
>> Exception {
>>         InitialContext jndiContext = new InitialContext();
>>         Repository rep = (Repository) 
>> jndiContext.lookup("java:/jcr/local");
>>         Session s = rep.login(new SimpleCredentials(username, 
>> username.toCharArray()));
>>         return 
>> s.getNodeByIdentifier(id).getProperty("aProperty").getString();
>>     }
>

Re: Jackrabbit, JCA, JBoss, EJB and transactions

Posted by Dominik Klaholt <do...@mail.upb.de>.
I have simplified the test-case that shows the - in my opinion - 
questionalbe behavior. Now it's without the confusing EJB stuff - just 
Jackrabbit and transactions. Any thoughts?

         /**
          * The following test-case works, IF step 1 is done without 
being encapsulated in a transaction (just remove the
          * utx-lines in step 1 and add the 's1.logout()')
          */
         String id;
         InitialContext jndiContext = new InitialContext();
         rep = (Repository) jndiContext.lookup("java:/jcr/local");

         // Step 1: user 1 acquires session and writes, then logs out
         utx = (UserTransaction) jndiContext.lookup("UserTransaction");
         utx.begin();
         Session s1 = rep.login(new SimpleCredentials(USER1, 
USER1.toCharArray()));
         Node nodeBys1 = s1.getRootNode().addNode("aNode");
         nodeBys1.setProperty("aProperty", USER1);
         s1.save();
         id = nodeBys1.getIdentifier();
         // s1.logout();
         utx.commit();

         // Step 2: user 2 acquires session, alters value written by 
user 1, then logs out
         utx = (UserTransaction) jndiContext.lookup("UserTransaction");
         utx.begin();
         Session s2 = rep.login(new SimpleCredentials(USER2, 
USER2.toCharArray()));
         s2.getNodeByIdentifier(id).setProperty("aProperty", USER2);
         s2.save();
         utx.commit();

         // Step 3: user 1 acquires session again, reads the property 
altered by user 2... but does not read the correct
         // value
         utx = (UserTransaction) jndiContext.lookup("UserTransaction");
         utx.begin();
         Session s = rep.login(new SimpleCredentials(USER1, 
USER1.toCharArray()));
         AssertJUnit.assertEquals(USER2, 
s.getNodeByIdentifier(id).getProperty("aProperty").getString());
         utx.commit();


Am 06.08.2010 02:19, schrieb Dominik Klaholt:
> Hello,
>
> i have a problem using jackrabbit with EJB transactions. I will try to 
> explain what i have done and understood so far. Any corrections are 
> really welcome.
> I am using Jackrabbit 2.1.0 within JBoss 5.1.0 as a XA-datasource by 
> help of the JCA adapter that is provided by Jackrabbit. My jcr-ds.xml 
> contains the flags
>
> <config-property name="bindSessionToTransaction" 
> type="java.lang.Boolean">true</config-property>
>
> and
>
> <application-managed-security/>
>
> so that the JCA-session-handles are bound to the container/bean 
> managed transactions and the actual JCR sessions within the JCA 
> connection pool are
> distinguished by the parameters of the 'repository.login()' calls.
>
> The following test-case works fine:
>
> (1)Repository rep = (Repository) jndiContext.lookup("java:/jcr/local");
>     Session s1 = rep.login(new SimpleCredentials(USER1, 
> USER1.toCharArray()));
>     Node nodeBys1 = s1.getRootNode().addNode("aNode");
>     nodeBys1.setProperty("aProperty", USER1);
>     s1.save();
>     String id = nodeBys1.getIdentifier();
>     s1.logout();
>
> (2)Session s2 = rep.login(new SimpleCredentials(USER2, 
> USER2.toCharArray()));
>     s2.getNodeByIdentifier(id).setProperty("aProperty", USER2);
>     s2.save();
>     s2.logout();
>
> (3)Session s1 = rep.login(new SimpleCredentials(USER1, 
> USER1.toCharArray()));
>     AssertJUnit.assertEquals(USER2, 
> s1.getNodeByIdentifier(id).getProperty("aProperty").getString());
>
> (1) User 1 logs in and creates a node with a property. (2) Then user 2 
> logs in and alters the property of the
> newly created node. (3) Finally user 1 logs in again and reads the 
> property. He reads the value user 2 has written.
>
> Now, i want to encapsulate each of these three steps in one method of 
> a Stateless Session Bean. E.g. (1) looks like this:
>
>     public String createNode(String username) throws Exception {
>         InitialContext jndiContext = new InitialContext();
>         Repository rep = (Repository) 
> jndiContext.lookup("java:/jcr/local");
>         Session s = rep.login(new SimpleCredentials(username, 
> username.toCharArray()));
>         Node node = s.getRootNode().addNode("aNode");
>         node.setProperty("aProperty", username);
>         s.save();
>         return node.getIdentifier();
>     }
>
> Note, that this method will automatically be encapsulated by a 
> container managed transaction. And due to the 
> bindSessionToTransaction-flag in the jcr-ds.xml, the
> JCASessionHandle used within the container managed transaction will be 
> automatically closed once the transaction commits (the 
> JCASessionHandle will be closed, the
> associated JCR session stays alive within the JCA connection pool).
> The above mentioned 3 steps of the simple test-case can now be 
> expressed as a sequence of method-calls of the stateless EJB:
>
> (1)jcrTest = (JCRTestBeanLocal) 
> jndiContext.lookup("test/JCRTestBean/local");
>     id = jcrTest.createNode(USER1);
>
> (2)jcrTest.alterProperty(id, USER2, USER2);
>
> (3)AssertJUnit.assertEquals(USER2, jcrTest.getProperty(id, USER1))
>
> However, this test-case fails, as in step (3) user 1 does not read the 
> changes committed by user 2. Interestingly the test-case passes once i 
> deactivate transaction
> support for step (1):
>
>     @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
>     public String createNode(String username) throws Exception {...}
>
> Does anyone know what i could do to make the test-case pass without 
> deactivating transaction support?
> Any hints would be really, really appreciated.
>
> Best regards,
> Dominik
>
>
> Below are the EJB-methods for the steps (2) and (3):
>
>     public void alterProperty(String id, String newValue, String 
> username) throws Exception {
>         InitialContext jndiContext = new InitialContext();
>         Repository rep = (Repository) 
> jndiContext.lookup("java:/jcr/local");
>         Session s = rep.login(new SimpleCredentials(username, 
> username.toCharArray()));
>         s.getNodeByIdentifier(id).setProperty("aProperty", newValue);
>         s.save();
>     }
>
>     public String getProperty(String id, String username) throws 
> Exception {
>         InitialContext jndiContext = new InitialContext();
>         Repository rep = (Repository) 
> jndiContext.lookup("java:/jcr/local");
>         Session s = rep.login(new SimpleCredentials(username, 
> username.toCharArray()));
>         return 
> s.getNodeByIdentifier(id).getProperty("aProperty").getString();
>     }