/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jackrabbit.core; import java.io.File; import java.io.IOException; import java.util.ArrayList; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.SimpleCredentials; import junit.framework.TestCase; import org.apache.commons.io.FileUtils; import org.apache.jackrabbit.core.config.RepositoryConfig; public class ConcurrencyTest3 extends TestCase { private static final int NUM_ITERATIONS = 1; private static final int NUM_THREADS = 5; private File repoDescriptor; private File repoHome; private RepositoryImpl repository; public void setUp() throws IOException, RepositoryException { File baseDir = new File(System.getProperty("basedir", ".")); File repoBaseDir = new File(baseDir, "target/corruption-test3"); FileUtils.deleteQuietly(repoBaseDir); repoDescriptor = new File(repoBaseDir, "repository.xml"); repoHome = new File(repoBaseDir, "repository"); repoHome.mkdirs(); File repositoryDescriptor = new File(baseDir, "src/test/repository/repository.xml"); FileUtils.copyFile(repositoryDescriptor, repoDescriptor); } public void tearDown() throws IOException, InterruptedException { FileUtils.deleteQuietly(repoHome.getParentFile()); } /** * Runs the test. */ public void testConcurrentWritingSessions() throws Exception { startRepository(); // Create test root node Session session = login(); Node testRoot = session.getRootNode().addNode("test"); Node testRoot2 = session.getRootNode().addNode("test2"); testRoot.addMixin("mix:referenceable"); session.save(); for (int i = 0; i < NUM_ITERATIONS; i++) { JcrTestThread[] threads = new JcrTestThread[NUM_THREADS]; Session[] sessions = new Session[NUM_THREADS]; for (int j = 0; j < threads.length; j++) { // create new session and a new thread Session threadSession = login(); JcrTestThread thread = new JcrTestThread(testRoot.getUUID(), threadSession); thread.setName("Iteration " + i + " - Thread " + j); thread.start(); threads[j] = thread; sessions[j] = threadSession; } for (int j = 0; j < threads.length; j++) { if (threads[j] != null) { threads[j].join(); } } for (int j = 0; j < sessions.length; j++) { if (sessions[j] != null) { sessions[j].logout(); } } } session.logout(); stopRepository(); // Restart with an empty index, scan and delete test root node deleteIndex(); startRepository(); session = login(); testRoot = session.getRootNode().getNode("test"); testRoot.addNode("new node"); session.save(); scan(testRoot); testRoot.remove(); session.save(); session.logout(); stopRepository(); } private void startRepository() throws RepositoryException { repository = RepositoryImpl.create(RepositoryConfig.create(repoDescriptor.getAbsolutePath(), repoHome .getAbsolutePath())); } private Session login() throws RepositoryException { return repository.login(new SimpleCredentials("admin", "admin".toCharArray()), null); } private void stopRepository() throws RepositoryException { repository.shutdown(); } private void deleteIndex() throws IOException { FileUtils.deleteDirectory(new File(repoHome, "workspaces/default/index")); } private void scan(Node node) throws RepositoryException { // System.err.println(node.getName() + " - " + node.getPath()); for (NodeIterator it = node.getNodes(); it.hasNext();) { Node child = it.nextNode(); scan(child); } } class JcrTestThread extends Thread { private String testRootUuid; private Session jcrSession; private ArrayList nodes = new ArrayList(); private int RUN_SIZE = 100; private int ACTION_SIZE = 1000; JcrTestThread(String uuid, Session session) throws RepositoryException { testRootUuid = uuid; jcrSession = session; } public void run() { outer: for (int i = 0; i < RUN_SIZE; i++) { for (int j = 0; j < ACTION_SIZE; j++) { int random = (int) Math.floor(2 * Math.random()); if (random == 0) { try { // Add a node called "P" Node addedNode = jcrSession.getNodeByUUID(testRootUuid).addNode("P"); addedNode.addMixin("mix:referenceable"); nodes.add(addedNode.getUUID()); } catch (RepositoryException ignore) { } } else { if (nodes.size() > 0) { int randomIndex = (int) Math.floor(nodes.size() * Math.random()); try { // Remove a random node we created within this // thread and within this session Node removeNode = jcrSession.getNodeByUUID((String) nodes.get(randomIndex)); String path = removeNode.getPath(); if (path.indexOf("test2") == -1) { jcrSession.move(removeNode.getPath(), removeNode.getParent().getPath() + "2/P"); } else { removeNode.remove(); nodes.remove(randomIndex); } } catch (RepositoryException ignore) { System.err.println(" 1 " + ignore.toString()); } } } } try { jcrSession.save(); } catch (RepositoryException e) { System.err.println(" 2 " + e.toString()); break outer; } } } } }