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.