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();
}