hi again,
i wrote a test which (i hope) explains the problem (though i now get a different error message :) ):

i'm creating two threads each having a session and they read and write randomly into the repositiory:

package at.systemone.wiki.threadthreat;

import junit.framework.TestCase;
import org.apache.jackrabbit.core.jndi.RegistryHelper;
import org.apache.log4j.Logger;

import javax.jcr.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.io.File;
import java.util.Hashtable;
import java.util.Random;

/**
*
*/
public class JCRThreadTest extends TestCase {

   protected Logger log;
   Repository r;

   public void setUp() throws Exception {
       super.setUp();
       r = createRepository();
   }

public void testTwoThreads() throws NamingException, RepositoryException, InterruptedException {
       // create two distinct sessions
       Session s1 = createSession();
       Session s2 = createSession();

       // create two threads
       MyThread t1;
       MyThread t2;
       t1 = new MyThread(s1);
       t2 = new MyThread(s2);
       Thread th1 = new Thread(t1);
       Thread th2 = new Thread(t2);

       // let them roll
       th1.start();
       th2.start();

       // end
       th1.join();
       th2.join();
   }

private Repository createRepository() throws NamingException, RepositoryException {
       deleteFolderStructure("repo");
       Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.jackrabbit.core.jndi.provider.DummyInitialContextFactory");
       env.put(Context.PROVIDER_URL, "localhost");
       InitialContext ctx = new InitialContext(env);
RegistryHelper.registerRepository(ctx, "repo", new File("repo/repository.xml").toString(), "repo", true);
       return (Repository) ctx.lookup("repo");
   }

private Session createSession() throws RepositoryException, LoginException { SimpleCredentials cred = new SimpleCredentials("userid" + Math.random(), "".toCharArray());
       return r.login(cred, null);
   }

   private void deleteFolderStructure(String repHomeDir) {
// called to errase the foldes which have been created during the test
       // setup
       deleteDirectory(new File(repHomeDir + "/meta"));
       deleteDirectory(new File(repHomeDir + "/versions"));
       deleteDirectory(new File(repHomeDir + "/nodetypes"));
       deleteDirectory(new File(repHomeDir + "/namespaces"));
       deleteDirectory(new File(repHomeDir + "/workspaces"));
   }

   private static boolean deleteDirectory(File path) {
       if (path.exists()) {
           File[] files = path.listFiles();
           for (File file : files) {
               if (file.isDirectory()) {
                   deleteDirectory(file);
               } else {
                   file.delete();
               }
           }
       }
       return (path.delete());
   }

// simple thread containing just the session and randomly reading/writing nodes
   class MyThread implements Runnable {
       private Session s;
       private Random r;

       public MyThread(Session s) {
           this.s = s;
           r = new Random();
       }

       public void run() {
           try {
               int i = r.nextInt();
               int counter = 0;
               String nodeName = "node" + i;
               while (counter++ < 100) {
                   try {
                       s.getRootNode().getNode(nodeName);
                   } catch (PathNotFoundException e) {
                       Node rootNode = s.getRootNode();
                       rootNode.addNode(nodeName);
                       s.save();
                   }
                   Thread.sleep((int) (Math.random() * 1000));
               }
           } catch (Exception e) {
               e.printStackTrace();
           }
       }
   }
}


this is what happens:

javax.jcr.InvalidItemStateException: cafebabe-cafe-babe-cafe-babecafebabe has been modified externally
   at org.apache.jackrabbit.core.ItemImpl.save(ItemImpl.java:1371)
   at org.apache.jackrabbit.core.SessionImpl.save(SessionImpl.java:765)
at at.systemone.wiki.threadthreat.JCRThreadTest$MyThread.run(JCRThreadTest.java:110)
   at java.lang.Thread.run(Thread.java:595)

do you knwo what i do wrong?

cheers
stephan




Stefan Guggisberg schrieb:

i quickly checked the implementation and i am pretty sure that
you are sharing a session among multiple threads. the thread
that does the import is most probably using the same session
as the other thread that performs the write operations.

cheers
stefan

On 12/15/05, Stefan Guggisberg <[EMAIL PROTECTED]> wrote:
hi stephan

On 12/15/05, stephan lohwasser <[EMAIL PROTECTED]> wrote:
hi,
i have a problem concerning threads and synchronisation and i have not
the faintest idea how to solve it:

we're writing a wiki-weblog application based on jackrabbit. now when i
run a thread importing data from wikipedia with one session while a
normal user is logged into the system via another session, a
'javax.jcr.RepositoryException: Unable to start edit operation: Already
in edit mode: Already in edit mode' is thrown when the user tries to
write or read data out of the repository.
i guess it is a problem of multiple threads working on the same data,
but i'm not sure.

is there any synchronisation of sessions working on the repository, or
do i have to take care of it by myself?
do you have any hints for me?
jackrabbit should be thread-safe although i can't guarantee that
there aren't still some hidden issues. as long as you don't share
Session objects among multiple threads there shouldn't be any
unexpected issues. if n threads modify the *same* item concurrently
only one thread will succeed and the others will get InvalidItemStateExceptions
informing them that their modifications have become stale.
this is by design. to prevent this you have to lock the node you're
working on.

your problem though seems to be caused by another issue.
please provide a full thread dump and ideally some simple
code to reproduce the problem.

cheers
stefan

thanks a lot.
stephan.





Reply via email to