import javax.jcr.Node;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;

import org.apache.jackrabbit.core.TestRepository;

public class ConcurrentTest {

    private final Repository repository;

    private final Thread[] threads;

    private int count = 0;

    private long sum = 0;

    private long min = Long.MAX_VALUE;

    private long max = Long.MIN_VALUE;

    private volatile boolean running;

    private volatile boolean counting;

    public ConcurrentTest(Repository repository, int threads) {
        this.repository = repository;
        this.threads = new Thread[threads];
    }

    private synchronized void add(long n) {
        if (counting) {
            if (n < min) {
                min = n;
            }
            if (n > max) {
                max = n;
            }
            sum += n;
            count++;
        }
    }

    private void execute() throws InterruptedException {
        // Prepare
        counting = false;
        running = true;

        // Start threads
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(new Test());
            threads[i].start();
        }

        // Sleep for 30 seconds to get all threads up and running
        Thread.sleep(30 * 1000);

        // Count results for the next 60 seconds
        counting = true;
        Thread.sleep(60 * 1000);
        counting = false;

        // Close down all threads
        running = false;
        for (int i = 0; i < threads.length; i++) {
            threads[i].join();
        }

        System.out.println(
                threads.length + "," + count + "," + min + "," + sum + "," + max);
    }

    private class Test implements Runnable {

        public void run() {
            try {
                Session session = repository.login();
                try {
                    Node root = session.getRootNode();
                    while (running) {
                        long start = System.currentTimeMillis();
                        for (int n = 0; n < 1000; n++) {
                            int i = (int) (Math.random() * 100);
                            int j = (int) (Math.random() * 100);
                            Node node = root.getNode("test/test" + i + "/test" + j);
                            node.getProperty("test").getString();
                        }
                        add(System.currentTimeMillis() - start);
                    }
                } finally {
                    session.logout();
                }
            } catch (RepositoryException e) {
            }
        }

    }

    public static void main(String[] args) throws Exception {
        Repository repository = TestRepository.getInstance();

        // Keep this session alive to avoid repository shutdown 
        Session session = repository.login();
        try {
            // Clean up any remains of previous tests
            while (session.itemExists("/test")) {
                session.getItem("/test").remove();
                session.save();
            }

            // Set up the content tree
            Node test0 = session.getRootNode().addNode("test");
            for (int i = 0; i < 100; i++) {
                Node test1 = test0.addNode("test" + i);
                for (int j = 0; j < 100; j++) {
                    Node test2 = test1.addNode("test" + j);
                    test2.setProperty("test", "test");
                }
            }
            session.save();

            System.out.println("Threads,Count,Min,Sum,Max");
            for (int i = 1; i <= 10; i++) {
                ConcurrentTest test = new ConcurrentTest(repository, i * 10);
                test.execute();
            }

            // Tear down the content tree
            test0.remove();
            session.save();
        } finally {
            session.logout();
        }
    }

}

