Hello

We have a problem with using unlocking and the move operation in the same JTA 
transaction and we are not sure if this is a bug in Jackrabbit or a problem on 
our side.


Without transactions, when you create a node, lock it and then move it, the 
(now nonexistent)

source node will no longer be locked, but the target node will be. If the 
target node should

not be locked, we can either unlock the source node before move, or unlock the 
target node

after move. However, this stops working as soon as we start using transactions.



In our example code (see below), the following problems occur:



a)      If we call unlock on the source node before the move, committing the 
transaction fails and complains that the target (!) node is not locked



b)      If we call unlock on the target node after the move instead (see 
commented out line), committing the transaction fails and complains that the 
target node is not locked



c)       If we don't call unlock at all, the assertion that tests the lock 
after the move fails because the target node is locked



As far as I have investigated, this is the problem:

Because we are in a transaction, the real changes of the move are not done 
immediately but upon commit (as it should be). This also includes updating the 
node path in the global LockManager. The unlocking however doesn't happen 
during the actual commit, but during the prepare, so it will always happen 
before the move. Because the move didn't happen yet, the global LockManager 
doesn't have an entry for the new path of the node yet, so if 
org.apache.jackrabbit.core.lock.LockManagerImpl  tries to do the unlock in the 
method internalUnlock(NodeImpl node), it will throw "LockException: Node not 
locked", because it can't find the correct path for the lock.



I'm not sure if we're doing something wrong, or if this isn't supposed to work 
at all (which would be a serious problem for us), or if there's a bug in 
Jackrabbit. It would be great if someone could clarify if this is a problem on 
our side, so we could fix/workaround it or if it is a bug, which we should 
report.



----------------------------------------------

Test code:

----------------------------------------------



package com.infinica.tests.jackrabbit;



import java.io.File;

import java.io.IOException;



import javax.jcr.LoginException;

import javax.jcr.Node;

import javax.jcr.Repository;

import javax.jcr.RepositoryException;

import javax.jcr.Session;

import javax.jcr.SimpleCredentials;

import javax.jcr.lock.LockException;

import javax.jcr.lock.LockManager;

import javax.transaction.HeuristicMixedException;

import javax.transaction.HeuristicRollbackException;

import javax.transaction.NotSupportedException;

import javax.transaction.RollbackException;

import javax.transaction.SystemException;

import javax.transaction.xa.XAException;

import javax.transaction.xa.XAResource;



import org.apache.geronimo.transaction.manager.GeronimoTransactionManager;

import org.apache.jackrabbit.core.TransientRepository;

import org.junit.After;

import org.junit.Assert;

import org.junit.Before;

import org.junit.Test;



public class JackrabbitTransactionTest

{

                public static final String NODE_CONTENT = "content";

                public static final String PROPERTY_DATA = "data";

                private File tempDir;



                @Before

                public void before() throws IOException

                {

                               tempDir = File.createTempFile("jackrabbit_test", 
"");

                               tempDir.delete();

                }



                @After

                public void after()

                {

                               if(tempDir != null)

                               {

                                               delete(tempDir);

                               }

                }



                private static void delete(File file)

                {

                               File[] children = file.listFiles();



                               if(children != null)

                               {

                                               for(File child: children)

                                               {

                                                               delete(child);

                                               }

                               }



                               if(!file.delete())

                               {

                                               System.err.println("Could not 
delete: " + file);

                               }

                }



                @Test

                public void testMoveAndLock() throws LoginException, 
RepositoryException,

XAException, NotSupportedException, SystemException, IllegalStateException, 
RollbackException,

SecurityException, HeuristicMixedException, HeuristicRollbackException

                {

                               GeronimoTransactionManager tm = new 
GeronimoTransactionManager();



                               tm.begin();



                               Repository repository = new 
TransientRepository(tempDir);

                               String token;

                               Session session = repository.login(new 
SimpleCredentials("admin",

"admin".toCharArray()));

                               tm.getTransaction().enlistResource((XAResource) 
session);

                               LockManager lockMan = 
session.getWorkspace().getLockManager();



                               try

                               {

                                               String user = 
session.getUserID();

                                               String name = 
repository.getDescriptor(Repository.REP_NAME_DESC);

                                               System.out.println("Logged in as 
" + user +

" to a " + name + " repository.");



                                               Node root = 
session.getRootNode();



                                               Node folderSrc = 
createNode(root, "src");

                                               Node fileSrc = 
createFile(folderSrc, "test.txt",

"Test");

                                               System.out.println("Created 
source file: "

+ fileSrc.getPath());

                                               session.save();



                                               token = 
lockMan.lock("/src/test.txt", false,

false, Long.MAX_VALUE, "admin").getLockToken();

                               }

                               finally

                               {

                                               removeLockTokens(lockMan);

                                               tm.commit();

                                               session.logout();

                               }



                               tm.begin();

                               session = repository.login(new 
SimpleCredentials("admin", "admin".toCharArray()));

                               tm.getTransaction().enlistResource((XAResource) 
session);

                               lockMan = 
session.getWorkspace().getLockManager();



                               try

                               {

                                               Node root = 
session.getRootNode();



                                               lockMan.addLockToken(token);

                                               lockMan.unlock("/src/test.txt");



                                               Node folderDest = 
createNode(root, "dest");

                                               session.move("/src/test.txt", 
"/dest/test.txt");

                                               session.save();

//                                           lockMan.unlock("/dest/test.txt");



                                               
Assert.assertTrue(root.hasNode("src"));

                                               
Assert.assertFalse(root.hasNode("src/test.txt"));



                                               folderDest = 
root.getNode(folderDest.getName());

                                               Node fileDest = 
folderDest.getNode("test.txt");

                                               Assert.assertEquals("Test", 
getFileStringProperty(fileDest,

PROPERTY_DATA));

                                               
Assert.assertFalse(lockMan.isLocked(fileDest.getPath()));

                               }

                               finally

                               {

                                               removeLockTokens(lockMan);

                                               tm.commit();

                                               session.logout();

                               }

                }



                /**

                * Necessary because of JCR-3438.

                */

                private static void removeLockTokens(LockManager man) throws 
LockException,

RepositoryException

                {

                               if(man == null)

                               {

                                               return;

                               }



                               for(String token: man.getLockTokens())

                               {

                                               man.removeLockToken(token);

                               }

                }



                private String getFileStringProperty(Node file, String 
property) throws RepositoryException

                {

                               Node contentNode = file.getNode(NODE_CONTENT);

                               return 
contentNode.getProperty(property).getString();

                }



                private Node createFile(Node parent, String path, String 
content) throws RepositoryException

                {

                               Node node = parent.addNode(path);

                               node.addMixin("mix:lockable");

                               Node contentNode = node.addNode(NODE_CONTENT);

                               contentNode.setProperty(PROPERTY_DATA, content);

                               return node;

                }



                private static Node createNode(Node parent, String path) throws 
RepositoryException

                {

                               return parent.addNode(path);

                }

}



----------------------------------------------

Maven POM:

----------------------------------------------



<project

                xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd<http://maven.apache.org/POM/4.0.0%20http:/maven.apache.org/maven-v4_0_0.xsd>"

                xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";>

                <modelVersion>4.0.0</modelVersion>

                <groupId>com.infinica</groupId>

                <artifactId>infinica-jackrabbit-tests</artifactId>

                <name>INFINICA - Jackrabbit test cases</name>

                <version>1.0.0-SNAPSHOT</version>



                <dependencies>

                               <dependency>

                                               <groupId>javax.jcr</groupId>

                                               <artifactId>jcr</artifactId>

                                               <version>2.0</version>

                                               <scope>test</scope>

                               </dependency>



                               <dependency>

                                               
<groupId>org.apache.jackrabbit</groupId>

                                               
<artifactId>jackrabbit-core</artifactId>

                                               <version>2.10.0</version>

                                               <scope>test</scope>

                               </dependency>



                               <dependency>

                                               <groupId>org.slf4j</groupId>

                                               
<artifactId>slf4j-log4j12</artifactId>

                                               <version>1.7.2</version>

                                               <scope>test</scope>

                               </dependency>



                               <dependency>

                                               <groupId>junit</groupId>

                                               <artifactId>junit</artifactId>

                                               <version>4.11</version>

                                               <scope>test</scope>

                               </dependency>



                               <dependency>

                                               
<groupId>org.apache.geronimo.components</groupId>

                                               
<artifactId>geronimo-transaction</artifactId>

                                               <version>3.1.1</version>

                                               <scope>test</scope>

                               </dependency>

                </dependencies>

</project>



----------------------------------------------



Thank you,

Thomas





--
BSc
Thomas Thonhofer
Entwickler



[cid:[email protected]]


a: Infinica GmbH, Leonard-Bernstein-Straße 10, 1220 Wien, Österreich
e:  [email protected]<mailto:[email protected]> w: 
www.infinica.com<http://www.infinica.com/>
t: +43 1 263 12 96


Please consider the environment before printing this email



Reply via email to