Index: src/test/java/org/apache/jackrabbit/core/jmx/core/CoreStatSessionTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/core/jmx/core/CoreStatSessionTest.java	(revision 0)
+++ src/test/java/org/apache/jackrabbit/core/jmx/core/CoreStatSessionTest.java	(revision 0)
@@ -0,0 +1,81 @@
+/*
+ * 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.jmx.core;
+
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.jackrabbit.core.jmx.AbstractJmxTest;
+
+/**
+ * JMX test cases for Core Stats.
+ */
+public class CoreStatSessionTest extends AbstractJmxTest {
+
+    private CoreStat cs;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        cs = jmxRegistry.getCoreStat();
+        cs.resetNumberOfSessions();
+    }
+
+    public void testNumberOfSessions1() throws Exception {
+
+        cs.setEnabled(true);
+        assertEquals(0, cs.getNumberOfSessions());
+
+        Session s = superuser.impersonate(new SimpleCredentials("anonymous", ""
+                .toCharArray()));
+        assertNotNull(s);
+        assertEquals(1, cs.getNumberOfSessions());
+
+        s.logout();
+        assertEquals(0, cs.getNumberOfSessions());
+    }
+
+    public void testNumberOfSessions2() throws Exception {
+
+        cs.setEnabled(false);
+        assertEquals(0, cs.getNumberOfSessions());
+
+        Session s = superuser.impersonate(new SimpleCredentials("anonymous", ""
+                .toCharArray()));
+        assertNotNull(s);
+        assertEquals(0, cs.getNumberOfSessions());
+
+        s.logout();
+        assertEquals(0, cs.getNumberOfSessions());
+    }
+
+    public void testNumberOfSessions3() throws Exception {
+
+        cs.setEnabled(true);
+        assertEquals(0, cs.getNumberOfSessions());
+
+        Session s = superuser.impersonate(new SimpleCredentials("anonymous", ""
+                .toCharArray()));
+        assertNotNull(s);
+        assertEquals(1, cs.getNumberOfSessions());
+
+        cs.resetNumberOfSessions();
+        assertEquals(0, cs.getNumberOfSessions());
+
+        s.logout();
+        assertEquals(0, cs.getNumberOfSessions());
+    }
+}
Index: src/test/java/org/apache/jackrabbit/core/jmx/util/CachingOpsPerSecondDtoTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/core/jmx/util/CachingOpsPerSecondDtoTest.java	(revision 0)
+++ src/test/java/org/apache/jackrabbit/core/jmx/util/CachingOpsPerSecondDtoTest.java	(revision 0)
@@ -0,0 +1,135 @@
+/*
+ * 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.jmx.util;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.jackrabbit.test.JUnitTest;
+
+public class CachingOpsPerSecondDtoTest extends JUnitTest {
+
+    private final CachingOpsPerSecondDto test = new CachingOpsPerSecondDto(
+            1000 * 10000);
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        test.reset();
+    }
+
+    public void testSimple() throws Exception {
+
+        long t = System.currentTimeMillis();
+        test.reset();
+        test.onOp(10);
+        test.onOp(1);
+        TimeUnit.MILLISECONDS.sleep(1300);
+        test.onOp(2);
+        long d_min = System.currentTimeMillis() - t;
+        test.refresh();
+        long d_max = System.currentTimeMillis() - t;
+
+        // 3 ops in ~ 1.3 seconds = 2.3 ops / sec
+        double expected_min = BigDecimal.valueOf(3 * 1000)
+                .divide(BigDecimal.valueOf(d_min), new MathContext(3))
+                .round(new MathContext(2, RoundingMode.DOWN)).doubleValue();
+        double expected_max = BigDecimal.valueOf(3 * 1000)
+                .divide(BigDecimal.valueOf(d_max), new MathContext(3))
+                .add(new BigDecimal(0.001))
+                .round(new MathContext(2, RoundingMode.UP)).doubleValue();
+        double opsPerSecond = test.getOpsPerSecond();
+
+        assertTrue(opsPerSecond + "?" + expected_min,
+                opsPerSecond >= expected_min);
+        assertTrue(opsPerSecond + "?" + expected_max,
+                opsPerSecond <= expected_max);
+        assertEquals(4.33, test.getOpAvgTime());
+
+    }
+
+    public void testMT() throws Exception {
+        int threads = 20;
+        int ops = 60 * threads;
+
+        final Random r = new Random();
+        test.reset();
+        long t = System.currentTimeMillis();
+        int aggDuration = 0;
+        ExecutorService executor = Executors.newFixedThreadPool(threads);
+        List<Future<Void>> futures = new ArrayList<Future<Void>>();
+        for (int i = 0; i < ops; i++) {
+            int duration = 35 + r.nextInt(10);
+            boolean shouldRefresh = i % 10 == 0;
+            futures.add(executor.submit(newCallable(test, duration, 75,
+                    shouldRefresh)));
+            aggDuration += duration;
+        }
+        executor.shutdown();
+        for (Future<Void> f : futures) {
+            f.get();
+        }
+
+        long d_min = System.currentTimeMillis() - t;
+        test.refresh();
+        long d_max = System.currentTimeMillis() - t;
+
+        double expected_min = BigDecimal.valueOf(ops * 1000)
+                .divide(BigDecimal.valueOf(d_min), new MathContext(3))
+                .round(new MathContext(2, RoundingMode.DOWN)).doubleValue();
+        double expected_max = BigDecimal.valueOf(ops * 1000)
+                .divide(BigDecimal.valueOf(d_max), new MathContext(3))
+                .add(new BigDecimal(0.001))
+                .round(new MathContext(2, RoundingMode.UP)).doubleValue();
+        double opsPerSecond = test.getOpsPerSecond();
+
+        assertTrue(opsPerSecond + "?" + expected_min,
+                opsPerSecond >= expected_min);
+        assertTrue(opsPerSecond + "?" + expected_max,
+                opsPerSecond <= expected_max);
+
+        double expectedAvg = BigDecimal
+                .valueOf(aggDuration)
+                .divide(BigDecimal.valueOf(ops),
+                        new MathContext(2, RoundingMode.DOWN)).doubleValue();
+        assertEquals(expectedAvg, BigDecimal.valueOf(test.getOpAvgTime())
+                .round(new MathContext(2, RoundingMode.DOWN)).doubleValue());
+    }
+
+    private Callable<Void> newCallable(final CachingOpsPerSecondDto test,
+            final int duration, final long sleep, final boolean shouldRefresh) {
+        return new Callable<Void>() {
+
+            public Void call() throws Exception {
+                test.onOp(duration);
+                TimeUnit.MILLISECONDS.sleep(sleep);
+                if (shouldRefresh) {
+                    test.refresh();
+                }
+                return null;
+            }
+        };
+    }
+}
Index: src/test/java/org/apache/jackrabbit/core/jmx/AbstractJmxTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/core/jmx/AbstractJmxTest.java	(revision 0)
+++ src/test/java/org/apache/jackrabbit/core/jmx/AbstractJmxTest.java	(revision 0)
@@ -0,0 +1,35 @@
+/*
+ * 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.jmx;
+
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+
+/**
+ * The base for various JMX test cases.
+ */
+public abstract class AbstractJmxTest extends AbstractJCRTest {
+
+    protected JmxRegistry jmxRegistry;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        RepositoryImpl r = (RepositoryImpl) superuser.getRepository();
+        jmxRegistry = r.getJmxRegistry();
+    }
+}
\ No newline at end of file
Index: src/main/java/org/apache/jackrabbit/core/RepositoryContext.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/RepositoryContext.java	(revision 1154877)
+++ src/main/java/org/apache/jackrabbit/core/RepositoryContext.java	(working copy)
@@ -21,6 +21,7 @@
 import org.apache.jackrabbit.core.fs.FileSystem;
 import org.apache.jackrabbit.core.id.NodeId;
 import org.apache.jackrabbit.core.id.NodeIdFactory;
+import org.apache.jackrabbit.core.jmx.JmxRegistry;
 import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
 import org.apache.jackrabbit.core.security.JackrabbitSecurityManager;
 import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry;
@@ -103,6 +104,8 @@
      * Repository-wide timer instance.
      */
     private final Timer timer = new Timer(false);
+    
+    private JmxRegistry jmxRegistry;
 
     /**
      * Creates a component context for the given repository.
@@ -359,4 +362,13 @@
         return nodeIdFactory;
     }
 
+    public JmxRegistry getJmxRegistry() {
+        assert jmxRegistry != null;
+        return jmxRegistry;
+    }
+
+    public void setJmxRegistry(JmxRegistry jmxRegistry) {
+        assert jmxRegistry != null;
+        this.jmxRegistry = jmxRegistry;
+    }
 }
Index: src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java	(revision 1154877)
+++ src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java	(working copy)
@@ -82,6 +82,8 @@
 import org.apache.jackrabbit.core.fs.FileSystemResource;
 import org.apache.jackrabbit.core.id.NodeId;
 import org.apache.jackrabbit.core.id.NodeIdFactory;
+import org.apache.jackrabbit.core.jmx.JmxRegistry;
+import org.apache.jackrabbit.core.jmx.JmxRegistryImpl;
 import org.apache.jackrabbit.core.lock.LockManager;
 import org.apache.jackrabbit.core.lock.LockManagerImpl;
 import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
@@ -231,6 +233,8 @@
      */
     private final CacheManager cacheMgr = new CacheManager();
 
+    private final JmxRegistry jmxRegistry = new JmxRegistryImpl();
+
     /**
      * Chanel for posting create workspace messages.
      */
@@ -355,6 +359,9 @@
             // initialize system search manager
             getSystemSearchManager(repConfig.getDefaultWorkspaceName());
 
+            //this has to be live before initSecurityManager(), to be able to track all the queries
+            initJmxRegistry();
+
             // Initialise the security manager;
             initSecurityManager();
 
@@ -461,6 +468,10 @@
         return cacheMgr;
     }
 
+    public JmxRegistry getJmxRegistry() {
+        return jmxRegistry;
+    }
+
     /**
      * Creates the {@link org.apache.jackrabbit.core.security.JackrabbitSecurityManager SecurityManager}
      * of this <code>Repository</code> and adds it to the repository context.
@@ -1002,6 +1013,7 @@
         synchronized (activeSessions) {
             session.addListener(this);
             activeSessions.put(session, session);
+            jmxRegistry.getCoreStat().sessionCreated();
         }
     }
 
@@ -1192,6 +1204,8 @@
 
         context.getTimer().cancel();
 
+        jmxRegistry.stop();
+
         log.info("Repository has been shutdown");
     }
 
@@ -1452,6 +1466,11 @@
         return new GarbageCollector(context.getDataStore(), ipmList, sessions);
     }
 
+    protected void initJmxRegistry(){
+        this.jmxRegistry.start();
+        this.context.setJmxRegistry(jmxRegistry);
+    }
+
     //-----------------------------------------------------------< Repository >
     /**
      * {@inheritDoc}
@@ -1586,6 +1605,7 @@
         synchronized (activeSessions) {
             // remove session from active sessions
             activeSessions.remove(session);
+            jmxRegistry.getCoreStat().sessionLoggedOut();
         }
     }
 
Index: src/main/java/org/apache/jackrabbit/core/jmx/JmxRegistry.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/jmx/JmxRegistry.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/jmx/JmxRegistry.java	(revision 0)
@@ -0,0 +1,59 @@
+/*
+ * 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.jmx;
+
+import javax.management.ObjectName;
+
+import org.apache.jackrabbit.core.jmx.core.CoreStat;
+
+/**
+ * JMX Mbean registration service
+ * 
+ */
+public interface JmxRegistry {
+
+    /**
+     * starts the service
+     */
+    void start();
+
+    /**
+     * stops the service
+     */
+    void stop();
+
+    /**
+     * Registers a new MBEan under the given name
+     * 
+     * @param bean
+     *            to be registered
+     * @param name
+     * @throws Exception
+     */
+    void register(JackrabbitBaseMBean bean, ObjectName name) throws Exception;
+
+    /**
+     * Unregisters a bean
+     * 
+     * @param name
+     * @throws Exception
+     */
+    void unregister(ObjectName name) throws Exception;
+
+    CoreStat getCoreStat();
+
+}
Index: src/main/java/org/apache/jackrabbit/core/jmx/util/CachingOpsPerSecondDto.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/jmx/util/CachingOpsPerSecondDto.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/jmx/util/CachingOpsPerSecondDto.java	(revision 0)
@@ -0,0 +1,148 @@
+/*
+ * 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.jmx.util;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+public class CachingOpsPerSecondDto {
+
+    // @ 1 min
+    public static long DEFAULT_UPDATE_FREQ_MS = 1000 * 60 * 1;
+
+    private final long updateFreqMs;
+
+    private final ReadWriteLock rwl = new ReentrantReadWriteLock();
+    private final Lock r = rwl.readLock();
+    private final Lock w = rwl.writeLock();
+
+    // intermediary values
+
+    private long lastUpdate = System.currentTimeMillis();
+
+    private long startMs = System.currentTimeMillis();
+
+    private long operations = 0;
+
+    private long totalTimeNs = 0;
+
+    // cached stats
+
+    private double opsPerSecond = 0;
+
+    private double opAvgTime = 0;
+
+    public CachingOpsPerSecondDto(long updateFreqMs) {
+        this.updateFreqMs = updateFreqMs;
+    }
+
+    public CachingOpsPerSecondDto() {
+        this(DEFAULT_UPDATE_FREQ_MS);
+    }
+
+    public void onOp(long timeNs) {
+        w.lock();
+        try {
+            final long localStart = System.currentTimeMillis() - timeNs / 1000;
+            if (localStart < startMs) {
+                startMs = localStart;
+            }
+            operations++;
+            totalTimeNs += timeNs;
+        } finally {
+            w.unlock();
+        }
+    }
+
+    public double getOpsPerSecond() {
+        checkUpdate(false);
+        return opsPerSecond;
+    }
+
+    public double getOpAvgTime() {
+        checkUpdate(false);
+        return opAvgTime;
+    }
+
+    private void checkUpdate(boolean forceUpdate) {
+        r.lock();
+        final long now = System.currentTimeMillis();
+        try {
+            if (!forceUpdate && now - lastUpdate < updateFreqMs) {
+                return;
+            }
+        } finally {
+            r.unlock();
+        }
+        w.lock();
+        try {
+            if (!forceUpdate && now - lastUpdate < updateFreqMs) {
+                return;
+            }
+            update(now);
+        } finally {
+            w.unlock();
+        }
+    }
+
+    private final static BigDecimal thousand = BigDecimal.valueOf(1000);
+
+    private final static MathContext DEFAULT_CONTEXT = new MathContext(3);
+
+    private void update(long now) {
+        if (operations == 0) {
+            opsPerSecond = 0;
+            opAvgTime = 0;
+            return;
+        }
+        long durationMs = now - startMs;
+        if (durationMs == 0) {
+            durationMs = 1000;
+        }
+        opsPerSecond = BigDecimal.valueOf(operations).multiply(thousand)
+                .divide(BigDecimal.valueOf(durationMs), DEFAULT_CONTEXT)
+                .doubleValue();
+        opAvgTime = BigDecimal.valueOf(totalTimeNs)
+                .divide(BigDecimal.valueOf(operations), DEFAULT_CONTEXT)
+                .doubleValue();
+        // reset if needed
+        if (operations > Long.MAX_VALUE - 5000) {
+            reset();
+        }
+    }
+
+    public void reset() {
+        w.lock();
+        try {
+            opsPerSecond = 0;
+            opAvgTime = 0;
+            lastUpdate = System.currentTimeMillis();
+            operations = 0;
+            startMs = lastUpdate;
+            totalTimeNs = 0;
+        } finally {
+            w.unlock();
+        }
+    }
+
+    protected void refresh() {
+        checkUpdate(true);
+    }
+}
Index: src/main/java/org/apache/jackrabbit/core/jmx/JmxRegistryImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/jmx/JmxRegistryImpl.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/jmx/JmxRegistryImpl.java	(revision 0)
@@ -0,0 +1,130 @@
+/*
+ * 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.jmx;
+
+import java.lang.management.ManagementFactory;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.jackrabbit.core.jmx.core.CoreStat;
+import org.apache.jackrabbit.core.jmx.core.CoreStatImpl;
+import org.apache.jackrabbit.core.jmx.core.CoreStatManager;
+import org.apache.jackrabbit.core.jmx.core.CoreStatManagerMBean;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * JmxRegistry default implementation
+ * 
+ */
+public class JmxRegistryImpl implements JmxRegistry {
+
+    private static Logger log = LoggerFactory.getLogger(JmxRegistryImpl.class);
+
+    private MBeanServer server;
+
+    private List<ObjectName> registry = new ArrayList<ObjectName>();
+
+    private CoreStat coreStat;
+
+    public JmxRegistryImpl() {
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.jackrabbit.core.jmx.JmxRegistry#start()
+     */
+    public void start() {
+        server = ManagementFactory.getPlatformMBeanServer();
+        log.debug("Started JMX Registry.");
+        try {
+            coreStat = new CoreStatImpl();
+            register(new CoreStatManager(coreStat), new ObjectName(
+                    CoreStatManagerMBean.NAME));
+            log.debug("JMX Registry - registered CoreStats.");
+        } catch (Exception e) {
+            log.error("JMX Registry - Unable to register CoreStats.", e);
+            coreStat.setEnabled(false);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.jackrabbit.core.jmx.JmxRegistry#stop()
+     */
+    public void stop() {
+        if (server == null) {
+            return;
+        }
+
+        List<ObjectName> registryCopy = new ArrayList<ObjectName>(registry);
+        for (ObjectName o : registryCopy) {
+            try {
+                unregister(o);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        registry.clear();
+        server = null;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.jackrabbit.core.jmx.JmxRegistry#register(org.apache.jackrabbit
+     * .core.jmx.JackrabbitMBean, javax.management.ObjectName)
+     */
+    public void register(JackrabbitBaseMBean bean, ObjectName name)
+            throws Exception {
+        if (server == null || server.isRegistered(name)) {
+            return;
+        }
+        this.server.registerMBean(bean, name);
+        this.registry.add(name);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.jackrabbit.core.jmx.JmxRegistry#unregister(javax.management
+     * .ObjectName)
+     */
+    public void unregister(ObjectName name) throws Exception {
+        if (server == null || !server.isRegistered(name)) {
+            return;
+        }
+        registry.remove(name);
+        server.unregisterMBean(name);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.jackrabbit.core.jmx.JmxRegistry#getCoreStat()
+     */
+    public CoreStat getCoreStat() {
+        return coreStat;
+    }
+}
Index: src/main/java/org/apache/jackrabbit/core/jmx/JackrabbitBaseMBean.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/jmx/JackrabbitBaseMBean.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/jmx/JackrabbitBaseMBean.java	(revision 0)
@@ -0,0 +1,49 @@
+/*
+ * 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.jmx;
+
+/**
+ * http://www.oracle.com/technetwork/java/javase/tech/best-practices-jsp-136021.
+ * html
+ * 
+ * This should be the base for any MBean Services built for Jackrabbit
+ * 
+ */
+public interface JackrabbitBaseMBean {
+
+    String BASE_NAME = "org.apache.jackrabbit";
+
+    /**
+     * If this service is currently registering stats
+     * 
+     * @return <code>true</code> if the service is enabled
+     */
+    boolean isEnabled();
+
+    /**
+     * Enables/Disables the service
+     * 
+     * @param enabled
+     */
+    void setEnabled(boolean enabled);
+
+    /**
+     * clears all data
+     */
+    void reset();
+
+}
Index: src/main/java/org/apache/jackrabbit/core/jmx/core/CoreStat.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/jmx/core/CoreStat.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/jmx/core/CoreStat.java	(revision 0)
@@ -0,0 +1,62 @@
+/*
+ * 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.jmx.core;
+
+public interface CoreStat {
+
+    /** -- SESSION INFO -- **/
+
+    void sessionCreated();
+
+    void sessionLoggedOut();
+
+    long getNumberOfSessions();
+
+    void resetNumberOfSessions();
+
+    /**
+     * @param timeNs
+     *            as given by timeNs = System.nanoTime() - timeNs;
+     */
+    void onSessionOperation(boolean isWrite, long timeNs);
+
+    double getReadOpsPerSecond();
+
+    double getWriteOpsPerSecond();
+
+    void resetNumberOfOperations();
+
+    /**
+     * If this service is currently registering stats
+     * 
+     * @return <code>true</code> if the service is enabled
+     */
+    boolean isEnabled();
+
+    /**
+     * Enables/Disables the service
+     * 
+     * @param enabled
+     */
+    void setEnabled(boolean enabled);
+
+    /**
+     * clears all data
+     */
+    void reset();
+
+}
Index: src/main/java/org/apache/jackrabbit/core/jmx/core/CoreStatImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/jmx/core/CoreStatImpl.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/jmx/core/CoreStatImpl.java	(revision 0)
@@ -0,0 +1,98 @@
+/*
+ * 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.jmx.core;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.jackrabbit.core.jmx.util.CachingOpsPerSecondDto;
+
+public class CoreStatImpl implements CoreStat {
+
+    /** -- SESSION INFO -- **/
+
+    private final AtomicLong sessions = new AtomicLong(0);
+
+    private boolean enabled = false;
+
+    private final CachingOpsPerSecondDto reads = new CachingOpsPerSecondDto();
+
+    private final CachingOpsPerSecondDto writes = new CachingOpsPerSecondDto();
+
+    public void sessionCreated() {
+        if (!enabled) {
+            return;
+        }
+        sessions.incrementAndGet();
+    }
+
+    public void sessionLoggedOut() {
+        if (!enabled || sessions.get() == 0) {
+            return;
+        }
+        sessions.decrementAndGet();
+    }
+
+    public long getNumberOfSessions() {
+        return sessions.get();
+    }
+
+    public void resetNumberOfSessions() {
+        sessions.set(0);
+    }
+
+    public void onSessionOperation(boolean isWrite, long timeNs) {
+        if (!enabled) {
+            return;
+        }
+        if (isWrite) {
+            writes.onOp(timeNs);
+        } else {
+            reads.onOp(timeNs);
+        }
+    }
+
+    public double getReadOpsPerSecond() {
+        return reads.getOpsPerSecond();
+    }
+
+    public double getWriteOpsPerSecond() {
+        return writes.getOpsPerSecond();
+    }
+
+    public void resetNumberOfOperations() {
+        reads.reset();
+        writes.reset();
+    }
+
+    /** -- GENERAL INFO -- **/
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+        if (!this.enabled) {
+            reset();
+        }
+    }
+
+    public void reset() {
+        resetNumberOfSessions();
+        resetNumberOfOperations();
+    }
+}
Index: src/main/java/org/apache/jackrabbit/core/jmx/core/CoreStatManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/jmx/core/CoreStatManager.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/jmx/core/CoreStatManager.java	(revision 0)
@@ -0,0 +1,59 @@
+/*
+ * 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.jmx.core;
+
+public class CoreStatManager implements CoreStatManagerMBean {
+
+    private final CoreStat coreStat;
+
+    public CoreStatManager(final CoreStat coreStat) {
+        this.coreStat = coreStat;
+    }
+
+    public long getNumberOfSessions() {
+        return coreStat.getNumberOfSessions();
+    }
+
+    public void resetNumberOfSessions() {
+        this.coreStat.resetNumberOfSessions();
+    }
+
+    public boolean isEnabled() {
+        return this.coreStat.isEnabled();
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.coreStat.setEnabled(enabled);
+    }
+
+    public void reset() {
+        this.coreStat.reset();
+    }
+
+    public double getReadOpsPerSecond() {
+        return this.coreStat.getReadOpsPerSecond();
+    }
+
+    public double getWriteOpsPerSecond() {
+        return this.coreStat.getWriteOpsPerSecond();
+    }
+
+    public void resetNumberOfOperations() {
+        this.coreStat.resetNumberOfOperations();
+
+    }
+}
Index: src/main/java/org/apache/jackrabbit/core/jmx/core/CoreStatManagerMBean.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/jmx/core/CoreStatManagerMBean.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/jmx/core/CoreStatManagerMBean.java	(revision 0)
@@ -0,0 +1,35 @@
+/*
+ * 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.jmx.core;
+
+import org.apache.jackrabbit.core.jmx.JackrabbitBaseMBean;
+
+public interface CoreStatManagerMBean extends JackrabbitBaseMBean {
+
+    String NAME = BASE_NAME + ":type=CoreStats";
+
+    long getNumberOfSessions();
+
+    void resetNumberOfSessions();
+
+    double getReadOpsPerSecond();
+
+    double getWriteOpsPerSecond();
+
+    void resetNumberOfOperations();
+
+}
Index: src/main/java/org/apache/jackrabbit/core/session/SessionState.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/session/SessionState.java	(revision 1154877)
+++ src/main/java/org/apache/jackrabbit/core/session/SessionState.java	(working copy)
@@ -23,6 +23,7 @@
 import javax.jcr.Session;
 
 import org.apache.jackrabbit.core.WorkspaceManager;
+import org.apache.jackrabbit.core.jmx.core.CoreStat;
 import org.apache.jackrabbit.core.observation.ObservationDispatcher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -180,20 +181,22 @@
             }
 
             try {
+                final CoreStat coreStat = context.getRepositoryContext().getJmxRegistry().getCoreStat();
                 // Perform the actual operation, optionally with debug logs
-                if (log.isDebugEnabled()) {
-                    log.debug("Performing {}", operation);
+                if (log.isDebugEnabled() || coreStat.isEnabled()) {
                     long start = System.nanoTime();
+                    log.debug("Performing {}", operation);
                     try {
                         return operation.perform(context);
                     } finally {
                         long time = System.nanoTime() - start;
+                        coreStat.onSessionOperation(isWriteOperation, time);
                         if (time > NS_PER_MS) {
-                            log.debug("Performed {} in {}ms",
-                                    operation, time / NS_PER_MS);
+                            log.debug("Performed {} in {}ms", operation, time
+                                    / NS_PER_MS);
                         } else {
-                            log.debug("Performed {} in {}us",
-                                    operation, time / NS_PER_US);
+                            log.debug("Performed {} in {}us", operation, time
+                                    / NS_PER_US);
                         }
                     }
                 } else {
