You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by jr...@apache.org on 2010/08/12 04:40:23 UTC

svn commit: r984633 - in /openjpa/trunk: openjpa-integration/jmx/src/test/java/org/apache/openjpa/integration/jmx/ openjpa-integration/jmx/src/test/resources/META-INF/ openjpa-kernel/src/main/java/org/apache/openjpa/datacache/ openjpa-kernel/src/main/j...

Author: jrbauer
Date: Thu Aug 12 02:40:23 2010
New Revision: 984633

URL: http://svn.apache.org/viewvc?rev=984633&view=rev
Log:
OPENJPA-1739 Added basic stats tracking to query result cache.  Updates to JMX provider.  Added tests to jmx module for query cache and prepared SQL cache instruments.

Modified:
    openjpa/trunk/openjpa-integration/jmx/src/test/java/org/apache/openjpa/integration/jmx/TestJMXPlatformMBeans.java
    openjpa/trunk/openjpa-integration/jmx/src/test/resources/META-INF/persistence.xml
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractQueryCache.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/DelegatingQueryCache.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCache.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/AbstractPreparedQueryCacheInstrument.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/AbstractQueryCacheInstrument.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/PreparedQueryCacheInstrument.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/QueryCacheInstrument.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/jmx/JMXProvider.java
    openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/instrumentation/Instrument.java

Modified: openjpa/trunk/openjpa-integration/jmx/src/test/java/org/apache/openjpa/integration/jmx/TestJMXPlatformMBeans.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-integration/jmx/src/test/java/org/apache/openjpa/integration/jmx/TestJMXPlatformMBeans.java?rev=984633&r1=984632&r2=984633&view=diff
==============================================================================
--- openjpa/trunk/openjpa-integration/jmx/src/test/java/org/apache/openjpa/integration/jmx/TestJMXPlatformMBeans.java (original)
+++ openjpa/trunk/openjpa-integration/jmx/src/test/java/org/apache/openjpa/integration/jmx/TestJMXPlatformMBeans.java Thu Aug 12 02:40:23 2010
@@ -19,14 +19,22 @@
 package org.apache.openjpa.integration.jmx;
 
 import java.lang.management.ManagementFactory;
+import java.util.List;
 import java.util.Random;
 import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 
 import javax.management.MBeanServer;
 import javax.management.ObjectName;
+import javax.persistence.Query;
 
 import org.apache.openjpa.instrumentation.DataCacheInstrument;
 import org.apache.openjpa.instrumentation.InstrumentationManager;
+import org.apache.openjpa.instrumentation.PreparedQueryCacheInstrument;
+import org.apache.openjpa.instrumentation.QueryCacheInstrument;
 import org.apache.openjpa.instrumentation.jmx.JMXProvider;
 import org.apache.openjpa.lib.instrumentation.Instrument;
 import org.apache.openjpa.lib.instrumentation.InstrumentationProvider;
@@ -35,11 +43,10 @@ import org.apache.openjpa.persistence.Op
 import org.apache.openjpa.persistence.test.AbstractPersistenceTestCase;
 
 public class TestJMXPlatformMBeans extends AbstractPersistenceTestCase {
+
     /**
-     * Verifies the data cache metrics are available through simple instrumentation.
+     * Verifies data cache metrics are available through simple instrumentation.
      */
-
-    @SuppressWarnings("deprecation")
     public void testDataCacheInstrument() {
         OpenJPAEntityManagerFactorySPI oemf = createNamedEMF("openjpa-integration-jmx");
  
@@ -78,30 +85,167 @@ public class TestJMXPlatformMBeans exten
         // Thread out to do out-of-band MBean-based validation.  This could
         // have been done on the same thread, but threading out makes for a
         // more realistic test.
-        Thread thr = new Thread(new DCMBeanThread());
-        thr.start();
+        ExecutorService executor = Executors.newFixedThreadPool(1);
+        Future<Boolean> result = executor.submit(new DCMBeanCallable());
         try {
-            thr.join(60000);  // Wait for 1 minute for the MBean thread to return.
-            if (thr.isAlive()) {
-                // MBean did not return within a minute, interrupt it.
-                thr.interrupt();
-                Thread.sleep(5000);
-                if (thr.isAlive()) {
-                    // Attempt to hard kill the thread to prevent the test from hanging
-                    thr.stop();
-                }
-                fail("DataCache MBean verification thread failed.");
-            }
+            assertTrue(result.get());
         } catch (Throwable t) {
-            fail("Caught unexpected throwable: " + t);
+            fail("DataCache verification failed: " + t);
         }
         
         closeEMF(oemf);
     }
     
-    public class DCMBeanThread implements Runnable {
+    /**
+     * Verifies query cache metrics are available through simple instrumentation.
+     */
+    public void testQueryCacheInstrument() {
+        OpenJPAEntityManagerFactorySPI oemf = createNamedEMF("openjpa-integration-jmx");
+ 
+        // Verify an EMF was created with the supplied instrumentation
+        assertNotNull(oemf);
 
-        public void run() {
+        // Verify an instrumentation manager is available
+        InstrumentationManager mgr = oemf.getConfiguration().getInstrumentationManagerInstance();
+        assertNotNull(mgr);
+        
+        // Get the in-band data cache instrument
+        Set<InstrumentationProvider> providers = mgr.getProviders();
+        assertNotNull(providers);
+        assertEquals(1, providers.size());
+        InstrumentationProvider provider = providers.iterator().next();
+        assertEquals(provider.getClass(), JMXProvider.class);
+
+        Instrument inst = provider.getInstrumentByName("QueryCache");
+        assertNotNull(inst);
+        assertTrue(inst instanceof QueryCacheInstrument);
+        QueryCacheInstrument qci = (QueryCacheInstrument)inst;
+        
+        assertEquals(0,qci.getExecutionCount());
+        assertEquals(0,qci.getTotalExecutionCount());
+        assertEquals(0,qci.getHitCount());
+        assertEquals(0,qci.getTotalHitCount());
+        
+        OpenJPAEntityManagerSPI oem = oemf.createEntityManager();
+        
+        CachedEntity ce = new CachedEntity();
+        int id = new Random().nextInt();
+        ce.setId(id);
+        CachedEntity ce2 = new CachedEntity();
+        id = new Random().nextInt();
+        ce2.setId(id);
+
+        oem.getTransaction().begin();
+        oem.persist(ce);
+        oem.persist(ce2);
+        oem.getTransaction().commit();
+       
+        Query q = oem.createQuery("SELECT ce FROM CachedEntity ce");
+        
+        List<?> result = q.getResultList();
+        assertNotNull(result);
+        assertTrue(result.size() > 1);
+        oem.clear();
+
+        result = q.getResultList();
+        
+        assertTrue(qci.getExecutionCount() > 0);
+        assertTrue(qci.getTotalExecutionCount() > 0);
+        assertTrue(qci.getHitCount() > 0);
+        assertTrue(qci.getTotalHitCount() > 0);
+
+        // Thread out to do out-of-band MBean-based validation.  This could
+        // have been done on the same thread, but threading out makes for a
+        // more realistic test.
+        ExecutorService executor = Executors.newFixedThreadPool(1);
+        Future<Boolean> execResult = executor.submit(new QueryCachesMBeanCallable(
+            QueryCachesMBeanCallable.QC_OBJNAME,
+            QueryCachesMBeanCallable.QC_QM));
+        try {
+            assertTrue(execResult.get());
+        } catch (Throwable t) {
+            fail("QueryCache verification failed: " + t);
+        }
+        closeEMF(oemf);
+    }
+
+    /**
+     * Verifies prepared query cache metrics are available through simple instrumentation.
+     */
+    public void testPreparedQueryCacheInstrument() {
+        OpenJPAEntityManagerFactorySPI oemf = createNamedEMF("openjpa-integration-jmx-qsc");
+ 
+        // Verify an EMF was created with the supplied instrumentation
+        assertNotNull(oemf);
+
+        // Verify an instrumentation manager is available
+        InstrumentationManager mgr = oemf.getConfiguration().getInstrumentationManagerInstance();
+        assertNotNull(mgr);
+        
+        // Get the in-band data cache instrument
+        Set<InstrumentationProvider> providers = mgr.getProviders();
+        assertNotNull(providers);
+        assertEquals(1, providers.size());
+        InstrumentationProvider provider = providers.iterator().next();
+        assertEquals(provider.getClass(), JMXProvider.class);
+
+        Instrument inst = provider.getInstrumentByName("QuerySQLCache");
+        assertNotNull(inst);
+        assertTrue(inst instanceof PreparedQueryCacheInstrument);
+        PreparedQueryCacheInstrument qci = (PreparedQueryCacheInstrument)inst;
+        
+        assertEquals(0,qci.getExecutionCount());
+        assertEquals(0,qci.getTotalExecutionCount());
+        assertEquals(0,qci.getHitCount());
+        assertEquals(0,qci.getTotalHitCount());
+        
+        OpenJPAEntityManagerSPI oem = oemf.createEntityManager();
+        
+        CachedEntity ce = new CachedEntity();
+        int id = new Random().nextInt();
+        ce.setId(id);
+        CachedEntity ce2 = new CachedEntity();
+        id = new Random().nextInt();
+        ce2.setId(id);
+
+        oem.getTransaction().begin();
+        oem.persist(ce);
+        oem.persist(ce2);
+        oem.getTransaction().commit();
+       
+        Query q = oem.createQuery("SELECT ce FROM CachedEntity ce");
+        
+        List<?> result = q.getResultList();
+        assertNotNull(result);
+        assertTrue(result.size() > 1);
+        oem.clear();
+
+        result = q.getResultList();
+        
+        assertTrue(qci.getExecutionCount() > 0);
+        assertTrue(qci.getTotalExecutionCount() > 0);
+        assertTrue(qci.getHitCount() > 0);
+        assertTrue(qci.getTotalHitCount() > 0);
+
+        // Thread out to do out-of-band MBean-based validation.  This could
+        // have been done on the same thread, but threading out makes for a
+        // more realistic test.
+        ExecutorService executor = Executors.newFixedThreadPool(1);
+        Future<Boolean> execResult = executor.submit(new QueryCachesMBeanCallable(
+            QueryCachesMBeanCallable.QSC_OBJNAME,
+            QueryCachesMBeanCallable.QSC_QM));
+        try {
+            assertTrue(execResult.get());
+        } catch (Throwable t) {
+            fail("QueryCache verification failed: " + t);
+        }
+
+        closeEMF(oemf);
+    }
+
+    public class DCMBeanCallable implements Callable<Boolean> {
+
+        public Boolean call() {
             MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
             assertNotNull(mbs);
             ObjectName objname = null;
@@ -144,7 +288,88 @@ public class TestJMXPlatformMBeans exten
                 assertEquals(0, clsWriteCount);
             } catch (Exception e) {
                 fail("Unexpected exception: " + e);
+                return false;
+            }
+            return true;
+        }
+    }
+
+    public class QueryCachesMBeanCallable implements Callable<Boolean> {
+
+        public static final String QC_OBJNAME = "org.apache.openjpa:type=QueryCache,cfgid=openjpa-integration-jmx,*";
+        public static final String QSC_OBJNAME = "org.apache.openjpa:type=QuerySQLCache,cfgid=openjpa-integration-jmx-qsc,*";  
+        public static final String QC_QM = "queryKeys";
+        public static final String QSC_QM = "queries";
+        
+        private String _objNameStr;
+        private String _queryMethod;
+        
+        public QueryCachesMBeanCallable(String objName, String queryMethod) {
+            setObjName(objName);
+            setQueryMethod(queryMethod);
+        }
+        
+        @SuppressWarnings("unchecked")
+        public Boolean call() {
+            MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+            assertNotNull(mbs);
+            ObjectName objname = null;
+            try {
+                // Query for the QueryCache bean
+                objname = new ObjectName(getObjName());
+                Set<ObjectName> ons = mbs.queryNames(objname, null);
+                assertEquals(1, ons.size());
+                ObjectName on = ons.iterator().next();
+                // Assert query cache attributes can be accessed and are being updated through the MBean
+                long hitCount = (Long)mbs.getAttribute(on, "HitCount");
+                long execCount = (Long)mbs.getAttribute(on, "ExecutionCount");
+                assertTrue(hitCount > 0);
+                assertTrue(execCount > 0);
+                // Assert data cache MBean methods can be invoked
+                
+                Set<String> keys = (Set<String>)mbs.invoke(on, getQueryMethod(), null, null);
+                assertNotNull(keys);
+                assertTrue(keys.size() > 0);
+                String[] sigs = new String[] { "java.lang.String" };
+                for (String key : keys) {
+                    Object[] parms = new Object[] { key };
+                    long queryHitCount = (Long)mbs.invoke(on, "getHitCount", parms, sigs);
+                    long queryReadCount = (Long)mbs.invoke(on, "getExecutionCount", parms, sigs); 
+                    assertTrue(queryHitCount > 0);
+                    assertTrue(queryReadCount > 0);
+                }
+                // Invoke the reset method and recollect stats
+                mbs.invoke(on, "reset", null, null);
+                hitCount = (Long)mbs.getAttribute(on, "HitCount");
+                execCount = (Long)mbs.getAttribute(on, "ExecutionCount");
+                assertEquals(0, hitCount);
+                assertEquals(0, execCount);
+
+                keys = (Set<String>)mbs.invoke(on, getQueryMethod(), null, null);
+                assertNotNull(keys);
+                assertEquals(0, keys.size());
+            } catch (Exception e) {
+                fail("Unexpected exception: " + e);
+                return false;
             }
+            return true;
+        }
+
+        public void setObjName(String objNameStr) {
+            this._objNameStr = objNameStr;
+        }
+
+        public String getObjName() {
+            return _objNameStr;
+        }
+
+        public void setQueryMethod(String _queryMethod) {
+            this._queryMethod = _queryMethod;
+        }
+
+        public String getQueryMethod() {
+            return _queryMethod;
         }
     }
+
 }
\ No newline at end of file

Modified: openjpa/trunk/openjpa-integration/jmx/src/test/resources/META-INF/persistence.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-integration/jmx/src/test/resources/META-INF/persistence.xml?rev=984633&r1=984632&r2=984633&view=diff
==============================================================================
--- openjpa/trunk/openjpa-integration/jmx/src/test/resources/META-INF/persistence.xml (original)
+++ openjpa/trunk/openjpa-integration/jmx/src/test/resources/META-INF/persistence.xml Thu Aug 12 02:40:23 2010
@@ -24,11 +24,23 @@
         <description>PU for JMX Platform MBean Testing</description>
         <class>org.apache.openjpa.integration.jmx.CachedEntity</class>
         <properties>
-            <property name="openjpa.Instrumentation" value="jmx(Instrument=DataCache)"/>
+            <property name="openjpa.Instrumentation" value="jmx(Instrument='DataCache,QueryCache')"/>
             <property name="openjpa.DataCache" value="true(EnableStatistics=true)"/>
+            <property name="openjpa.QueryCache" value="true(EnableStatistics=true)"/>
             <property name="openjpa.RemoteCommitProvider" value="sjvm"/>
             <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema"/>
             <property name="openjpa.DynamicEnhancementAgent" value="false"/>
         </properties>
     </persistence-unit>
+    
+    <persistence-unit name="openjpa-integration-jmx-qsc">
+        <description>PU for JMX Platform MBean Testing with PreparedQueryCache/QuerySQLCache</description>
+        <class>org.apache.openjpa.integration.jmx.CachedEntity</class>
+        <properties>
+            <property name="openjpa.Instrumentation" value="jmx(Instrument=QuerySQLCache)"/>
+            <property name="openjpa.jdbc.QuerySQLCache" value="true(EnableStatistics=true)"/>
+            <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema"/>
+            <property name="openjpa.DynamicEnhancementAgent" value="false"/>
+        </properties>
+    </persistence-unit>    
 </persistence>

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractQueryCache.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractQueryCache.java?rev=984633&r1=984632&r2=984633&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractQueryCache.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractQueryCache.java Thu Aug 12 02:40:23 2010
@@ -18,9 +18,11 @@
  */
 package org.apache.openjpa.datacache;
 
+import java.io.PrintStream;
 import java.security.AccessController;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -32,6 +34,7 @@ import java.util.concurrent.ConcurrentHa
 import org.apache.openjpa.conf.OpenJPAConfiguration;
 import org.apache.openjpa.event.RemoteCommitEvent;
 import org.apache.openjpa.event.RemoteCommitListener;
+import org.apache.openjpa.kernel.QueryStatistics;
 import org.apache.openjpa.lib.conf.Configurable;
 import org.apache.openjpa.lib.conf.Configuration;
 import org.apache.openjpa.lib.log.Log;
@@ -39,6 +42,7 @@ import org.apache.openjpa.lib.util.J2DoP
 import org.apache.openjpa.lib.util.Localizer;
 import org.apache.openjpa.lib.util.concurrent.AbstractConcurrentEventManager;
 import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashSet;
+import org.apache.openjpa.lib.util.concurrent.SizedConcurrentHashMap;
 import org.apache.openjpa.meta.ClassMetaData;
 import org.apache.openjpa.meta.MetaDataRepository;
 import org.apache.openjpa.util.Id;
@@ -77,7 +81,21 @@ public abstract class AbstractQueryCache
     
     // default evict policy
     public EvictPolicy evictPolicy = EvictPolicy.DEFAULT;
+    
+    private QueryStatistics<QueryKey> _stats;
+    private boolean _statsEnabled = false;
+
+    public void setEnableStatistics(boolean enable){
+        _statsEnabled = enable;
+    }
+    public boolean getEnableStatistics(){
+        return _statsEnabled;
+    }
 
+    public QueryStatistics<QueryKey> getStatistics() {
+        return _stats;
+    }
+    
     public void initialize(DataCacheManager manager) {
         if (evictPolicy == EvictPolicy.TIMESTAMP) {
             entityTimestampMap = new ConcurrentHashMap<String,Long>();
@@ -137,6 +155,9 @@ public abstract class AbstractQueryCache
     }
 
     public QueryResult get(QueryKey key) {
+        if (_statsEnabled) {
+            _stats.recordExecution(key);
+        }
         QueryResult o = getInternal(key);
         if (o != null && o.isTimedOut()) {
             o = null;
@@ -151,6 +172,9 @@ public abstract class AbstractQueryCache
             else
                 log.trace(s_loc.get("cache-hit", key));
         }
+        if (_statsEnabled && o != null) {
+            ((Default<QueryKey>)_stats).recordHit(key);
+        }
         return o;
     }
 
@@ -200,6 +224,9 @@ public abstract class AbstractQueryCache
         clearInternal();
         if (log.isTraceEnabled())
             log.trace(s_loc.get("cache-clear", "<query-cache>"));
+        if (_statsEnabled) {
+            _stats.clear();
+        }
     }
 
     public void close() {
@@ -339,6 +366,8 @@ public abstract class AbstractQueryCache
     }
 
     public void endConfiguration() {
+        _stats = _statsEnabled ? new Default<QueryKey>() :
+            new QueryStatistics.None<QueryKey>();
     }
 
     // ---------- AbstractEventManager implementation ----------
@@ -418,4 +447,159 @@ public abstract class AbstractQueryCache
     public String getName() {
         return _name;
     }
+    
+    /**
+     * A default implementation of query statistics for the Query result cache.
+     * 
+     * Maintains statistics for only a fixed number of queries.
+     * Statistical counts are approximate and not exact (to keep thread synchorization overhead low).
+     * 
+     */
+    public static class Default<T> implements QueryStatistics<T> {
+
+        private static final long serialVersionUID = -7889619105916307055L;
+        
+        private static final int FIXED_SIZE = 1000;
+        private static final float LOAD_FACTOR = 0.75f;
+        private static final int CONCURRENCY = 16;
+        
+        private static final int ARRAY_SIZE = 2;
+        private static final int READ  = 0;
+        private static final int HIT   = 1;
+        
+        private long[] astat = new long[ARRAY_SIZE];
+        private long[] stat  = new long[ARRAY_SIZE];
+        private Map<T, long[]> stats  = new SizedConcurrentHashMap(FIXED_SIZE, LOAD_FACTOR, CONCURRENCY);
+        private Map<T, long[]> astats = new SizedConcurrentHashMap(FIXED_SIZE, LOAD_FACTOR, CONCURRENCY);
+        private Date start = new Date();
+        private Date since = start;
+        
+        public Set<T> keys() {
+            return stats.keySet();
+        }
+
+        public long getExecutionCount() {
+            return stat[READ];
+        }
+
+        public long getTotalExecutionCount() {
+            return astat[READ];
+        }
+
+        public long getExecutionCount(T query) {
+            return getCount(stats, query, READ);
+        }
+
+        public long getTotalExecutionCount(T query) {
+            return getCount(astats, query, READ);
+        }
+
+        public long getHitCount() {
+            return stat[HIT];
+        }
+
+        public long getTotalHitCount() {
+            return astat[HIT];
+        }
+
+        public long getHitCount(T query) {
+            return getCount(stats, query, HIT);
+        }
+
+        public long getTotalHitCount(T query) {
+            return getCount(astats, query, HIT);
+        }
+
+        private long getCount(Map<T, long[]> target, T query, int i) {
+            long[] row = target.get(query);
+            return (row == null) ? 0 : row[i];
+        }
+
+        public Date since() {
+            return since;
+        }
+
+        public Date start() {
+            return start;
+        }
+
+        public synchronized void reset() {
+            stat = new long[ARRAY_SIZE];
+            stats.clear();
+            since = new Date();
+        }
+        
+        @SuppressWarnings("unchecked")
+        public synchronized void clear() {
+           astat = new long[ARRAY_SIZE];
+           stat  = new long[ARRAY_SIZE];
+           stats = new SizedConcurrentHashMap(FIXED_SIZE, LOAD_FACTOR, CONCURRENCY);
+           astats = new SizedConcurrentHashMap(FIXED_SIZE, LOAD_FACTOR, CONCURRENCY);
+           start  = new Date();
+           since  = start;
+        }
+        
+        private void addSample(T query, int index) {
+            stat[index]++;
+            astat[index]++;
+            addSample(stats, query, index);
+            addSample(astats, query, index);
+        }
+        
+        private void addSample(Map<T, long[]> target, T query, int i) {
+            long[] row = target.get(query);
+            if (row == null) {
+                row = new long[ARRAY_SIZE];
+            }
+            row[i]++;
+            target.put(query, row);
+        }
+        
+        public void recordExecution(T query) {
+            if (query == null)
+                return;
+            addSample(query, READ);
+        }
+        
+        public void recordHit(T query) {
+            addSample(query, HIT);
+        }
+        
+        public void dump(PrintStream out) {
+            String header = "Query Statistics starting from " + start;
+            out.print(header);
+            if (since == start) {
+                out.println();
+                out.println("Total Query Execution: " + toString(astat)); 
+                out.println("\tTotal \t\tQuery");
+            } else {
+                out.println(" last reset on " + since);
+                out.println("Total Query Execution since start " + 
+                        toString(astat)  + " since reset " + toString(stat));
+                out.println("\tSince Start \tSince Reset \t\tQuery");
+            }
+            int i = 0;
+            for (T key : stats.keySet()) {
+                i++;
+                long[] arow = astats.get(key);
+                if (since == start) {
+                    out.println(i + ". \t" + toString(arow) + " \t" + key);
+                } else {
+                    long[] row  = stats.get(key);
+                    out.println(i + ". \t" + toString(arow) + " \t"  + toString(row) + " \t\t" + key);
+                }
+            }
+        }
+        
+        long pct(long per, long cent) {
+            if (cent <= 0)
+                return 0;
+            return (100*per)/cent;
+        }
+        
+        String toString(long[] row) {
+            return row[READ] + ":" + row[HIT] + "(" + pct(row[HIT], row[READ]) + "%)";
+        }
+    }
+
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/DelegatingQueryCache.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/DelegatingQueryCache.java?rev=984633&r1=984632&r2=984633&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/DelegatingQueryCache.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/DelegatingQueryCache.java Thu Aug 12 02:40:23 2010
@@ -19,6 +19,7 @@
 package org.apache.openjpa.datacache;
 
 import org.apache.commons.lang.ObjectUtils;
+import org.apache.openjpa.kernel.QueryStatistics;
 import org.apache.openjpa.util.RuntimeExceptionTranslator;
 
 /**
@@ -216,4 +217,14 @@ public class DelegatingQueryCache
             throw translate(re);
 		}
 	}
+
+    public QueryStatistics<QueryKey> getStatistics() {
+        if (_cache == null)
+            return null;
+        try {
+            return _cache.getStatistics();
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCache.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCache.java?rev=984633&r1=984632&r2=984633&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCache.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCache.java Thu Aug 12 02:40:23 2010
@@ -20,6 +20,7 @@ package org.apache.openjpa.datacache;
 
 import java.util.Map;
 
+import org.apache.openjpa.kernel.QueryStatistics;
 import org.apache.openjpa.lib.util.Closeable;
 
 /**
@@ -135,4 +136,11 @@ public interface QueryCache
      * Free the resources used by this cache.
 	 */
 	public void close ();
+	
+	   /**
+     * Gets the simple statistics for query results.
+     * If the statistics gathering is disabled, an empty statistics is returned.
+     * @since 2.1.0 
+     */
+    public QueryStatistics<QueryKey> getStatistics();
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/AbstractPreparedQueryCacheInstrument.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/AbstractPreparedQueryCacheInstrument.java?rev=984633&r1=984632&r2=984633&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/AbstractPreparedQueryCacheInstrument.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/AbstractPreparedQueryCacheInstrument.java Thu Aug 12 02:40:23 2010
@@ -19,6 +19,7 @@
 package org.apache.openjpa.instrumentation;
 
 import java.util.Date;
+import java.util.Set;
 
 import org.apache.openjpa.kernel.PreparedQueryCache;
 import org.apache.openjpa.kernel.QueryStatistics;
@@ -141,6 +142,13 @@ public abstract class AbstractPreparedQu
         return null;
     }
     
+    public Set<String> queries() {
+        QueryStatistics<String> stats = getStatistics();
+        if (stats != null)
+            return stats.keys();
+        return null;
+    }
+    
     public InstrumentationLevel getLevel() {
         return InstrumentationLevel.FACTORY;
     }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/AbstractQueryCacheInstrument.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/AbstractQueryCacheInstrument.java?rev=984633&r1=984632&r2=984633&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/AbstractQueryCacheInstrument.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/AbstractQueryCacheInstrument.java Thu Aug 12 02:40:23 2010
@@ -19,9 +19,12 @@
 package org.apache.openjpa.instrumentation;
 
 import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
 
-import org.apache.openjpa.datacache.CacheStatistics;
 import org.apache.openjpa.datacache.QueryCache;
+import org.apache.openjpa.datacache.QueryKey;
+import org.apache.openjpa.kernel.QueryStatistics;
 import org.apache.openjpa.lib.instrumentation.AbstractInstrument;
 import org.apache.openjpa.lib.instrumentation.InstrumentationLevel;
 
@@ -46,94 +49,143 @@ public abstract class AbstractQueryCache
         _qc = qc;
     }
     
-    // TODO : Cache stats must be added to query cache.  They will likely be
-    // tracked by a QueryStatistics type when that takes place.
-    private CacheStatistics getStatistics() {
+    public void setConfigId(String cid) {
+        _configId = cid;
+    }
+    
+    public void setContextRef(String cref) {
+        _configRef = cref;
+    }
+    
+    public String getConfigId() {
+        return _configId;
+    }
+
+    public String getContextRef() {
+        return _configRef;
+    }
+    
+    public void setPreparedQueryCache(QueryCache qc) {
+        _qc = qc;
+    }
+    
+    private QueryStatistics<QueryKey> getStatistics() {
         if (_qc == null)
             return null;
-        return null; // _qc.getStatistics();
+        return _qc.getStatistics();
     }
 
-    public long getHitCount() {
-        CacheStatistics stats = getStatistics();
+    public long getExecutionCount() {
+        QueryStatistics<QueryKey> stats = getStatistics();
         if (stats != null)
-            return stats.getHitCount();
+            return stats.getExecutionCount();
         return NO_STATS;
     }
-            
-    public long getReadCount() {
-        CacheStatistics stats = getStatistics();
-        if (stats != null)
-            return stats.getReadCount();
+
+    public long getExecutionCount(String queryKey) {
+        QueryStatistics<QueryKey> stats = getStatistics();
+        if (stats != null) {
+            QueryKey qk = findKey(queryKey);
+            return stats.getExecutionCount(qk);
+        }
         return NO_STATS;
     }
-        
-    public long getTotalHitCount() {
-        CacheStatistics stats = getStatistics();
+
+    public long getTotalExecutionCount() {
+        QueryStatistics<QueryKey> stats = getStatistics();
         if (stats != null)
-            return stats.getTotalHitCount();
+            return stats.getTotalExecutionCount();
         return NO_STATS;
     }
-    
-    public long getTotalReadCount() {
-        CacheStatistics stats = getStatistics();
-        if (stats != null)
-            return stats.getTotalReadCount();
+
+    public long getTotalExecutionCount(String queryKey) {
+        QueryStatistics<QueryKey> stats = getStatistics();
+        if (stats != null) {
+            QueryKey qk = findKey(queryKey);
+            return stats.getTotalExecutionCount(qk);
+        }
         return NO_STATS;
     }
-    
-    public long getTotalWriteCount() {
-        CacheStatistics stats = getStatistics();
+
+    public long getHitCount() {
+        QueryStatistics<QueryKey> stats = getStatistics();
         if (stats != null)
-            return stats.getTotalWriteCount();
+            return stats.getHitCount();
         return NO_STATS;
     }
-        
-    public long getWriteCount() {
-        CacheStatistics stats = getStatistics();
+
+    public long getHitCount(String queryKey) {
+        QueryStatistics<QueryKey> stats = getStatistics();
+        if (stats != null) {
+            QueryKey qk = findKey(queryKey);
+            return stats.getHitCount(qk);
+        }
+        return NO_STATS;
+    }
+
+    public long getTotalHitCount() {
+        QueryStatistics<QueryKey> stats = getStatistics();
         if (stats != null)
-            return stats.getWriteCount();
+            return stats.getTotalHitCount();
         return NO_STATS;
     }
-    
+
+    public long getTotalHitCount(String queryKey) {
+        QueryStatistics<QueryKey> stats = getStatistics();
+        if (stats != null) {
+            QueryKey qk = findKey(queryKey);
+            return stats.getTotalHitCount(qk);
+        }
+        return NO_STATS;
+    }
+
     public void reset() {
-        CacheStatistics stats = getStatistics();
+        QueryStatistics<QueryKey> stats = getStatistics();
         if (stats != null)
             stats.reset();        
     }
     
     public Date sinceDate() {
-        CacheStatistics stats = getStatistics();
+        QueryStatistics<QueryKey> stats = getStatistics();
         if (stats != null)
             return stats.since();
         return null;
     }
     
     public Date startDate() {
-        CacheStatistics stats = getStatistics();
+        QueryStatistics<QueryKey> stats = getStatistics();
         if (stats != null)
             return stats.start();        
         return null;
     }
     
-    public String getConfigId() {
-        return _configId;
+    /**
+     * Returns all query keys currently tracked in the cache.
+     * @return
+     */
+    public Set<String> queryKeys() {
+        QueryStatistics<QueryKey> stats = getStatistics();
+        if (stats != null) {
+            Set<String> keys = new HashSet<String>();
+            for (QueryKey qk : stats.keys()) {
+                keys.add(qk.toString());
+            }
+            return keys;
+        }
+        return null;
     }
 
-    public void setConfigId(String cid) {
-        _configId = cid;
-    }
-    
-    public String getContextRef() {
-        return _configRef;
-    }
-    
-    public void setContextRef(String cref) {
-        _configRef = cref;
+    private QueryKey findKey(String key) {
+        QueryStatistics<QueryKey> stats = getStatistics();
+        for (QueryKey qk : stats.keys()) {
+            if (qk.toString().equals(key.toString())) {
+                return qk;
+            }
+        }
+        return null;
     }
-    
+
     public InstrumentationLevel getLevel() {
         return InstrumentationLevel.FACTORY;
     }
-
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/PreparedQueryCacheInstrument.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/PreparedQueryCacheInstrument.java?rev=984633&r1=984632&r2=984633&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/PreparedQueryCacheInstrument.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/PreparedQueryCacheInstrument.java Thu Aug 12 02:40:23 2010
@@ -19,6 +19,7 @@
 package org.apache.openjpa.instrumentation;
 
 import java.util.Date;
+import java.util.Set;
 
 public interface PreparedQueryCacheInstrument {
 
@@ -63,18 +64,7 @@ public interface PreparedQueryCacheInstr
      * Returns number of total read requests that has been found since start.
      */
     public long getTotalHitCount(String query);
-        
-    /**
-     * Returns the config id for the configuration attached to this cache
-     */
-    public String getConfigId();
-    
-    /**
-     * Returns the context unique id for the configuration attached to this
-     * cache
-     */
-    public String getContextRef();
-    
+
     /**
      * Resets cache statistics
      */
@@ -89,4 +79,10 @@ public interface PreparedQueryCacheInstr
      * Returns date cache statistics collection started.
      */
     public Date startDate();
+    
+    /**
+     * Returns all queries currently tracked in the cache.
+     * @return
+     */
+    public Set<String> queries();
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/QueryCacheInstrument.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/QueryCacheInstrument.java?rev=984633&r1=984632&r2=984633&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/QueryCacheInstrument.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/QueryCacheInstrument.java Thu Aug 12 02:40:23 2010
@@ -19,6 +19,7 @@
 package org.apache.openjpa.instrumentation;
 
 import java.util.Date;
+import java.util.Set;
 
 /**
  * Interface for providing instrumented data cache metrics and operations.
@@ -26,47 +27,47 @@ import java.util.Date;
 public interface QueryCacheInstrument {
 
     /**
-     * Returns number of total read requests that have been found in cache since 
-     * last reset.
+     * Returns number of total exec requests since start.
      */
-    public long getHitCount();
+    public long getTotalExecutionCount(); 
 
     /**
-     * Returns number of total read requests since last reset
+     * Returns number of total exec requests since start.
      */
-    public long getReadCount();
+    public long getTotalExecutionCount(String queryKey); 
 
     /**
-     * Returns number of total write requests since last reset.
+     * Returns number of total execution requests since last reset
      */
-    public long getWriteCount();
+    public long getExecutionCount();
 
     /**
-     * Returns number of total read requests since start.
+     * Returns number of total execution requests since last reset
      */
-    public long getTotalReadCount(); 
+    public long getExecutionCount(String queryKey);
 
     /**
-     * Returns number of total read requests that has been found since start.
+     * Returns number of total read requests that have been found in cache since 
+     * last reset.
      */
-    public long getTotalHitCount();
+    public long getHitCount();
 
     /**
-     * Returns number of total write requests for the given class since start.
+     * Returns number of total read requests that have been found in cache since 
+     * last reset.
      */
-    public long getTotalWriteCount();
-        
+    public long getHitCount(String queryKey);
+
     /**
-     * Returns the config id for the configuration attached to this cache
+     * Returns number of total read requests that has been found since start.
      */
-    public String getConfigId();
-    
+    public long getTotalHitCount();
+
     /**
-     * Returns the system unique id for the configuration attached to this
-     * cache
+     * Returns number of total read requests that has been found since start.
      */
-    public String getContextRef();
-    
+    public long getTotalHitCount(String queryKey);
+
     /**
      * Resets cache statistics
      */
@@ -81,4 +82,10 @@ public interface QueryCacheInstrument {
      * Returns date cache statistics collection started.
      */
     public Date startDate();
+    
+    /**
+     * Returns all query keys currently in the cache.
+     * @return
+     */
+    public Set<String> queryKeys();
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/jmx/JMXProvider.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/jmx/JMXProvider.java?rev=984633&r1=984632&r2=984633&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/jmx/JMXProvider.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/jmx/JMXProvider.java Thu Aug 12 02:40:23 2010
@@ -20,9 +20,11 @@ package org.apache.openjpa.instrumentati
 
 import java.lang.management.ManagementFactory;
 import java.util.Map;
+import java.util.Set;
 import java.util.Map.Entry;
 
 import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
 import javax.management.ObjectName;
 
 import org.apache.openjpa.lib.instrumentation.AbstractInstrumentationProvider;
@@ -38,12 +40,12 @@ public class JMXProvider
     // Aliases for built-in JMX Instrumentation
     public static final String[] JMX_INSTRUMENT_ALIASES = {
         "DataCache", "org.apache.openjpa.instrumentation.jmx.DataCacheJMXInstrument",
-        "QueryCache", "org.apache.openjpa.insrumentation.jmx.QueryCacheJMXInstrument",
-        "QuerySQLCache", "org.apache.openjpa.insrumentation.jmx.PreparedQueryCacheJMXInstrument"
+        "QueryCache", "org.apache.openjpa.instrumentation.jmx.QueryCacheJMXInstrument",
+        "QuerySQLCache", "org.apache.openjpa.instrumentation.jmx.PreparedQueryCacheJMXInstrument"
     };
     
     /**
-     * The standard mbean package for OpenJPA
+     * The MBean domain for OpenJPA
      */
     public static final String MBEAN_DOMAIN = "org.apache.openjpa";
     
@@ -80,14 +82,33 @@ public class JMXProvider
            if (mbs == null) {
                throw new UserException("jmx-server-failed-creation");
            }
+           setStarted(true);
        } catch (Throwable t) {
            throw new UserException("jmx-server-unavailable",t);
        }
     }
 
+    /**
+     * Stops all instruments registered with this provider and releases the 
+     * reference to the Platform MBean server instance. 
+     */
     @Override
     public void stop() {
-        // no-op with the built in MBean server
+        if (isStarted()) {
+            Set<Instrument> instruments = getInstruments();
+            if (instruments != null && instruments.size() > 0) {
+                for (Instrument inst : instruments) {
+                    stopInstrument(inst);
+                }
+            }
+            // The MBean server factory does appear to ref count properly so the 
+            // platform server cannot released from the factory once it is acquired.  
+            // Multiple attempts to capture and release the server will result in a 
+            // runtime exception.
+            // MBeanServerFactory.releaseMBeanServer(getMBeanServer());
+            // _mbs = null;
+            setStarted(false);
+        }
     }
 
     /**
@@ -125,6 +146,7 @@ public class JMXProvider
     public void startInstrument(Instrument instrument) {
         if (!instrument.isStarted()) {
             registerMBean((JMXInstrument)instrument);
+            instrument.setStarted(true);
         }
     }
 
@@ -135,6 +157,7 @@ public class JMXProvider
         if (instrument.isStarted() || force) {
             try {
                 getMBeanServer().unregisterMBean(((JMXInstrument)instrument).getObjectName());
+                instrument.setStarted(false);
             } catch (Exception e) {
                 // If force, swallow the exception since the bean may not even
                 // be registered.

Modified: openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/instrumentation/Instrument.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/instrumentation/Instrument.java?rev=984633&r1=984632&r2=984633&view=diff
==============================================================================
--- openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/instrumentation/Instrument.java (original)
+++ openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/instrumentation/Instrument.java Thu Aug 12 02:40:23 2010
@@ -87,6 +87,12 @@ public interface Instrument {
     public boolean isStarted();
 
     /**
+     * Sets whether the instrument is an available state.
+     * @param started
+     */
+    public void setStarted(boolean started);
+    
+    /**
      * Starts the instrument.  Typically this will be performed through the provider,
      * but in some cases an instrument will have its own specialized startup.
      */