On Thu, Oct 20, 2005 at 03:54:31PM +0200, Marcel Reutegger wrote:
> If I'm not totally mistaken your test creates multiple repository 
> instances with the same configuration thus running on the same location. 
> this is not allowed and should actually result in an error when 
> initializing a repository that works on data that is already access by 
> another instance.
> please make sure there is only one repository instance running.

okay, I got you. Right now there is some other errors. I modified
createSession to operate only on single repository (private static Repository
repo). When several threads are working within single session, everything
seems to be ok, but with several sessions there are a lot of exceptions about 

 [java] ERROR 20/03/05 05:03:04 [30] (JCRTest$SingleSessionAccessThread:370) - 
javax.jcr.In
validItemStateException: db5e6d2d-6a07-49ac-a2b0-9cfe40d1e9a9/{}test: the item 
cannot be saved 
because it has been modified externally.
     [java] javax.jcr.InvalidItemStateException: 
db5e6d2d-6a07-49ac-a2b0-9cfe40d1e9a9/{}test: t
he item cannot be saved because it has been modified externally.
     [java]     at 
org.apache.jackrabbit.core.ItemImpl.getTransientStates(ItemImpl.java:391)
     [java]     at org.apache.jackrabbit.core.ItemImpl.save(ItemImpl.java:1215)
     [java]     at 
org.apache.jackrabbit.core.SessionImpl.save(SessionImpl.java:765)
     [java]     at JCRTest.releaseSession(JCRTest.java:226)
     [java]     at JCRTest$SingleSessionAccessThread.run(JCRTest.java:368)

the same exception is thrown with LocalFS when session.save() method in 
releaseSession() is called.

-- 
Eugene N Dzhurinsky
import java.util.Date;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.jcr.ItemExistsException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.query.Query;
import javax.jcr.query.QueryResult;
import javax.jcr.version.VersionException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.apache.jackrabbit.core.jndi.RegistryHelper;
import org.apache.jackrabbit.core.value.BLOBFileValue;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

public class JCRTest {

    /**
     * Repository to operate on
     */
    private static Repository repo = null;

    /**
     * Node path
     */
    private static final String STATIC_NODE_PATH = "/node1_3/node2_3";

    /**
     * Number of nodes in memory, after which we need to flush repository
     */
    private static final int SAVE_INTERVAL = 200;

    /**
     * Number of childs per node
     */
    private static final int CHILD_NODE_COUNT = 3;

    /**
     * Root node name
     */
    private static final String RANDOM_NODE_NAME = "random";

    /**
     * Root node type
     */
    private static final String TEST_NODE_TYPE = "nt:unstructured";

    /**
     * THREADS NUMBER
     */
    private static final int THREAD_NUMBER = 50;

    /**
     * Timeout to wait until some thread finishes
     */
    private static final int TIMEOUT = 50 * 1000;

    private static final Logger log = Logger.getLogger(JCRTest.class);

    private static final String configFile = "repotest/repository.xml";

    private static final String repHomeDir = "repotest";

    private Session session;

    /**
     * Date object to use for time consumption calculation
     */
    Date d = null;

    long totalNodesCreated = 0;

    long buildTime = 0;

    long traverseTime = 0;

    long searchTime = 0;

    long accessTime = 0;

    /**
     * number of references for Session objects
     */
    int refcount;

    public JCRTest() throws NamingException, RepositoryException {
        session = createSession("");
        refcount = 0;
    }

    /**
     * Creates repository and performs some tests for time consumption on
     * searching operations
     */
    public void runTest() {
        try {
            Node rn = session.getRootNode();
            log.debug("Creating random node");
            Date start = new Date();
            if (!rn.hasNode(RANDOM_NODE_NAME)) {
                Node root = rn.addNode(RANDOM_NODE_NAME, TEST_NODE_TYPE);
                buildNode(root, 1);
                session.save();
            }
            Date stop = new Date();
            buildTime = (stop.getTime() - start.getTime());
            start = new Date();
            /*
             * if (rn.hasNode(RANDOM_NODE_NAME)) { log.debug("Traversing back");
             * Node root = rn.getNode(RANDOM_NODE_NAME);
             * log.debug(root.getPath()); traverseNode(root); }
             */
            stop = new Date();
            traverseTime = stop.getTime() - start.getTime();
            start = new Date();
            Node n = rn.getNode(RANDOM_NODE_NAME + STATIC_NODE_PATH);
            stop = new Date();
            accessTime = stop.getTime() - start.getTime();
            dumpProperties(n);
            String query = "//*[jcr:contains(@prop, '3_3_1')]";
            log.debug(query);
            Query q = session.getWorkspace().getQueryManager().createQuery(
                    query, javax.jcr.query.Query.XPATH);
            start = new Date();
            QueryResult queryResult = q.execute();
            stop = new Date();
            NodeIterator nodeIterator = queryResult.getNodes();
            searchTime = stop.getTime() - start.getTime();
            log.debug("Dump " + nodeIterator.getSize() + " results ");
            while (nodeIterator.hasNext()) {
                dumpProperties(nodeIterator.nextNode());
            }
            log
                    .debug("Build " + totalNodesCreated + " in " + buildTime
                            + " ms");
            log.debug("Traverse " + totalNodesCreated + " in " + traverseTime
                    + " ms");
            log.debug("Random access " + searchTime + " ms");
            log.debug("Search " + searchTime + " ms");
        } catch (Exception e) {
            log.error(e, e);
        }
    }

    /**
     * Shows node's childs and properties
     */
    private void traverseNode(Node root) throws RepositoryException {
        if (root.hasNodes()) {
            NodeIterator it = root.getNodes();
            while (it.hasNext()) {
                Node n = it.nextNode();
                log.debug(n.getPath());
                traverseNode(n);
            }
        }
    }

    /**
     * Recursively duilds children for node
     */
    private void buildNode(Node root, int level) throws ItemExistsException,
            PathNotFoundException, NoSuchNodeTypeException, LockException,
            VersionException, ConstraintViolationException,
            RepositoryException, ValueFormatException,
            UnsupportedRepositoryOperationException {
        for (int i = 1; i <= CHILD_NODE_COUNT; i++) {
            Node n = root.addNode("node" + level + "_" + i, TEST_NODE_TYPE);
            if (++totalNodesCreated % SAVE_INTERVAL == 0) {
                long time = -1;
                if (d != null) {
                    Date current = new Date();
                    time = current.getTime() - d.getTime();
                    d = current;
                } else
                    d = new Date();
                log.debug("Saving session with " + totalNodesCreated
                        + (time > 0 ? " in " + time + " ms" : ""));
                session.save();
                log.debug("Session saved");
            }
            n.setProperty("prop_blob", new BLOBFileValue(
                    (level + "_" + i + "_1").getBytes()));
            n.setProperty("prop", session.getValueFactory().createValue(
                    level + "_" + i + "_1"));
            if (level <= CHILD_NODE_COUNT)
                buildNode(n, level + 1);
        }
    }

    /**
     * returns the session and increases internal counter for number of
     * references for this object
     */
    public Session getSession() {
        ++refcount;
        return session;
    }

    /**
     * decrements the reference counter and if it is equals to 0 - closes
     * session
     */
    public void releaseSession() throws Exception {
        if (--refcount == 0) {
            log.debug("Flushing session");
            session.save();
            session.logout();
        }
    }

    /**
     * dumps properties of the node
     */
    public void dumpProperties(Node n) throws RepositoryException {
        log.info(n.getPath());
        PropertyIterator pit = n.getProperties();
        while (pit.hasNext()) {
            Property p = pit.nextProperty();
            StringBuffer b = new StringBuffer(p.getPath() + "=");
            if (p.getDefinition().isMultiple()) {
                Value[] values = p.getValues();
                for (int i = 0; i < values.length; i++) {
                    b.append(values[i].getString());
                    if (i < values.length - 1)
                        b.append(',');
                }
            } else {
                b.append(p.getString());
            }
            log.info(b);
        }
    }

    /**
     * Creates session object for given username
     */
    private Session createSession(String username) throws NamingException,
            RepositoryException {
        if (repo == null) {
            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", configFile,
                    repHomeDir, true);
            repo = (Repository) ctx.lookup("repo");
        }
        return repo.login(new SimpleCredentials(username, "".toCharArray()));
    }

    /**
     * Main method
     */
    public static void main(String[] args) throws Exception {
        PropertyConfigurator.configure("log4j.properties");
        JCRTest test = new JCRTest();
        test.runTest();
        ThreadContainer cont = new ThreadContainer();
        testSessionsForUser(test, cont);
        cont.start();
        cont.waitForRelease();
        log.debug("Exitting with thread " + cont.getLastThread());
        dumpResults(test, cont);
        for (int i = 0; i < THREAD_NUMBER; i++) {
            test = new JCRTest();
            testSessionsForUser(test, cont);
        }
        cont.start();
        log.debug("Waiting for threads");
        cont.waitForRelease();
        log.debug("Exitting with thread " + cont.getLastThread());
        test = new JCRTest();
        dumpResults(test, cont);
    }

    /**
     * Dumps the results after multi-threaded testing
     */
    private static void dumpResults(JCRTest test, ThreadContainer cont) {
        try {
            Session session = test.createSession("");
            Node root = session.getRootNode();
            Node r = root.getNode(RANDOM_NODE_NAME + STATIC_NODE_PATH);
            test.dumpProperties(r);
            log.debug("Property is "
                    + r.getProperty("test").getValue().getString() + " == "
                    + cont.getLastThread());
            session.logout();
        } catch (Exception e) {
            log.error(e, e);
        }
    }

    /**
     * Creates set of threads to be executed for single "user"
     */
    private static void testSessionsForUser(JCRTest test, ThreadContainer cont) {
        for (int i = 0; i < THREAD_NUMBER; i++)
            new SingleSessionAccessThread(cont, test, STATIC_NODE_PATH, String
                    .valueOf(i));
    }

    /**
     * Emulates single access tot the same property within same session
     */
    static class SingleSessionAccessThread extends Thread {

        private JCRTest test;

        private Session session;

        private String nodePath;

        private String propertyValue;

        private ThreadContainer container;

        public SingleSessionAccessThread(ThreadContainer container,
                JCRTest test, String nodePath, String propertyValue) {
            super(propertyValue);
            this.container = container;
            this.test = test;
            this.nodePath = nodePath;
            this.propertyValue = propertyValue;
            this.session = test.getSession();
            container.add(this);
        }

        public void run() {
            try {
                Node root = session.getRootNode();
                sleep(Math.round(Math.random() * 1000));
                log
                        .debug("Setting property for " + RANDOM_NODE_NAME
                                + nodePath);
                Node current = root.getNode(RANDOM_NODE_NAME + nodePath);
                current.setProperty("test", propertyValue);
            } catch (RepositoryException e) {
                log.error(e, e);
            } catch (InterruptedException e) {
                log.error(e, e);
            } finally {
                container.remove(this);
                try {
                    test.releaseSession();
                } catch (Exception e) {
                    log.error(e, e);
                }
            }
        }
    }

    /**
     * Collects threads and waits for all threads to be finished
     */
    static class ThreadContainer {
        private List threads;

        private String lastThread;

        public ThreadContainer() {
            threads = new LinkedList();
        }

        public synchronized void add(SingleSessionAccessThread obj) {
            threads.add(obj);
        }

        public synchronized void remove(SingleSessionAccessThread obj) {
            threads.remove(obj);
            lastThread = obj.getName();
            notifyAll();
        }

        public synchronized void waitForRelease() {
            try {
                while (!threads.isEmpty())
                    wait(TIMEOUT);
            } catch (InterruptedException e) {
                log.error(e);
            }
        }

        /**
         * @return Returns the lastThread.
         */
        public String getLastThread() {
            return lastThread;
        }

        public synchronized void start() {
            for (Iterator it = threads.iterator(); it.hasNext();)
                ((Thread) it.next()).start();
        }

    }

}

Reply via email to