[exo-jcr-commits] exo-jcr SVN: r2268 - in jcr/trunk/exo.jcr.component.core/src: main/java/org/exoplatform/services/jcr/impl/storage/jdbc/statistics and 3 other directories.

do-not-reply at jboss.org do-not-reply at jboss.org
Tue Apr 13 03:03:30 EDT 2010


Author: nfilotto
Date: 2010-04-13 03:03:28 -0400 (Tue, 13 Apr 2010)
New Revision: 2268

Added:
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/statistics/
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/statistics/JCRStatisticsManager.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/statistics/Statistics.java
   jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/statistics/
   jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/statistics/TestJCRStatisticsManager.java
Modified:
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/statistics/StatisticsJDBCStorageConnection.java
Log:
EXOJCR-611: Provide a way to collect statistics around the JCR API accesses

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/statistics/StatisticsJDBCStorageConnection.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/statistics/StatisticsJDBCStorageConnection.java	2010-04-12 12:47:24 UTC (rev 2267)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/statistics/StatisticsJDBCStorageConnection.java	2010-04-13 07:03:28 UTC (rev 2268)
@@ -1,27 +1,3 @@
-package org.exoplatform.services.jcr.impl.storage.jdbc.statistics;
-
-import org.exoplatform.services.jcr.datamodel.ItemData;
-import org.exoplatform.services.jcr.datamodel.NodeData;
-import org.exoplatform.services.jcr.datamodel.PropertyData;
-import org.exoplatform.services.jcr.datamodel.QPathEntry;
-import org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer;
-import org.exoplatform.services.jcr.storage.WorkspaceStorageConnection;
-import org.exoplatform.services.log.ExoLogger;
-import org.exoplatform.services.log.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-import java.util.concurrent.atomic.AtomicLong;
-
-import javax.jcr.InvalidItemStateException;
-import javax.jcr.RepositoryException;
-
 /*
  * Copyright (C) 2003-2010 eXo Platform SAS.
  *
@@ -38,13 +14,27 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, see<http://www.gnu.org/licenses/>.
  */
+package org.exoplatform.services.jcr.impl.storage.jdbc.statistics;
 
+import org.exoplatform.services.jcr.datamodel.ItemData;
+import org.exoplatform.services.jcr.datamodel.NodeData;
+import org.exoplatform.services.jcr.datamodel.PropertyData;
+import org.exoplatform.services.jcr.datamodel.QPathEntry;
+import org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer;
+import org.exoplatform.services.jcr.statistics.JCRStatisticsManager;
+import org.exoplatform.services.jcr.statistics.Statistics;
+import org.exoplatform.services.jcr.storage.WorkspaceStorageConnection;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.RepositoryException;
+
 /**
- * This class is used to give statistics about the time spent in the database access layer. It will
- * print all the metrics value into a file in csv format. It will provide metrics of type 
- * minimum, maximum, total, times and average for each method of {@link WorkspaceStorageConnection}
- * and the global values. It will add data into the file every 5 seconds and add the last line at
- * JVM exit. To activate the statistics, set the JVM parameter called
+ * This class is used to give statistics about the time spent in the database access layer.  
+ * To activate the statistics, set the JVM parameter called
  * "JDBCWorkspaceDataContainer.statistics.enabled" to <code>true</code>.
  * 
  * Created by The eXo Platform SAS
@@ -56,11 +46,6 @@
 {
 
    /**
-    * The logger
-    */
-   private static final Log LOG = ExoLogger.getLogger("exo.jcr.component.core.StatisticsJDBCStorageConnection");
-
-   /**
     * The description of the statistics corresponding to the method 
     * <code>update(PropertyData data)</code>
     */
@@ -171,7 +156,7 @@
    /**
     * The global statistics for all the database accesses
     */
-   private final static Statistics GLOBAL_STATISTICS = new Statistics("global");
+   private final static Statistics GLOBAL_STATISTICS = new Statistics(null, "global");
 
    /**
     * The list of all the statistics, one per method
@@ -180,126 +165,47 @@
    static
    {
       // Read Methods
-      ALL_STATISTICS.put(GET_ITEM_DATA_BY_ID_DESCR, new Statistics(GET_ITEM_DATA_BY_ID_DESCR));
-      ALL_STATISTICS.put(GET_ITEM_DATA_BY_NODE_DATA_NQ_PATH_ENTRY_DESCR, new Statistics(
+      ALL_STATISTICS.put(GET_ITEM_DATA_BY_ID_DESCR, new Statistics(GLOBAL_STATISTICS, GET_ITEM_DATA_BY_ID_DESCR));
+      ALL_STATISTICS.put(GET_ITEM_DATA_BY_NODE_DATA_NQ_PATH_ENTRY_DESCR, new Statistics(GLOBAL_STATISTICS,
          GET_ITEM_DATA_BY_NODE_DATA_NQ_PATH_ENTRY_DESCR));
-      ALL_STATISTICS.put(GET_CHILD_NODES_DATA_DESCR, new Statistics(GET_CHILD_NODES_DATA_DESCR));
-      ALL_STATISTICS.put(GET_CHILD_NODES_COUNT_DESCR, new Statistics(GET_CHILD_NODES_COUNT_DESCR));
-      ALL_STATISTICS.put(GET_CHILD_PROPERTIES_DATA_DESCR, new Statistics(GET_CHILD_PROPERTIES_DATA_DESCR));
-      ALL_STATISTICS.put(LIST_CHILD_PROPERTIES_DATA_DESCR, new Statistics(LIST_CHILD_PROPERTIES_DATA_DESCR));
-      ALL_STATISTICS.put(GET_REFERENCES_DATA_DESCR, new Statistics(GET_REFERENCES_DATA_DESCR));
+      ALL_STATISTICS.put(GET_CHILD_NODES_DATA_DESCR, new Statistics(GLOBAL_STATISTICS, GET_CHILD_NODES_DATA_DESCR));
+      ALL_STATISTICS.put(GET_CHILD_NODES_COUNT_DESCR, new Statistics(GLOBAL_STATISTICS, GET_CHILD_NODES_COUNT_DESCR));
+      ALL_STATISTICS.put(GET_CHILD_PROPERTIES_DATA_DESCR, new Statistics(GLOBAL_STATISTICS,
+         GET_CHILD_PROPERTIES_DATA_DESCR));
+      ALL_STATISTICS.put(LIST_CHILD_PROPERTIES_DATA_DESCR, new Statistics(GLOBAL_STATISTICS,
+         LIST_CHILD_PROPERTIES_DATA_DESCR));
+      ALL_STATISTICS.put(GET_REFERENCES_DATA_DESCR, new Statistics(GLOBAL_STATISTICS, GET_REFERENCES_DATA_DESCR));
       // Write Methods
       // Commit
-      ALL_STATISTICS.put(COMMIT_DESCR, new Statistics(COMMIT_DESCR));
+      ALL_STATISTICS.put(COMMIT_DESCR, new Statistics(GLOBAL_STATISTICS, COMMIT_DESCR));
       // Add methods
-      ALL_STATISTICS.put(ADD_NODE_DATA_DESCR, new Statistics(ADD_NODE_DATA_DESCR));
-      ALL_STATISTICS.put(ADD_PROPERTY_DATA_DESCR, new Statistics(ADD_PROPERTY_DATA_DESCR));
+      ALL_STATISTICS.put(ADD_NODE_DATA_DESCR, new Statistics(GLOBAL_STATISTICS, ADD_NODE_DATA_DESCR));
+      ALL_STATISTICS.put(ADD_PROPERTY_DATA_DESCR, new Statistics(GLOBAL_STATISTICS, ADD_PROPERTY_DATA_DESCR));
       // Update methods
-      ALL_STATISTICS.put(UPDATE_NODE_DATA_DESCR, new Statistics(UPDATE_NODE_DATA_DESCR));
-      ALL_STATISTICS.put(UPDATE_PROPERTY_DATA_DESCR, new Statistics(UPDATE_PROPERTY_DATA_DESCR));
+      ALL_STATISTICS.put(UPDATE_NODE_DATA_DESCR, new Statistics(GLOBAL_STATISTICS, UPDATE_NODE_DATA_DESCR));
+      ALL_STATISTICS.put(UPDATE_PROPERTY_DATA_DESCR, new Statistics(GLOBAL_STATISTICS, UPDATE_PROPERTY_DATA_DESCR));
       // Delete methods
-      ALL_STATISTICS.put(DELETE_NODE_DATA_DESCR, new Statistics(DELETE_NODE_DATA_DESCR));
-      ALL_STATISTICS.put(DELETE_PROPERTY_DATA_DESCR, new Statistics(DELETE_PROPERTY_DATA_DESCR));
+      ALL_STATISTICS.put(DELETE_NODE_DATA_DESCR, new Statistics(GLOBAL_STATISTICS, DELETE_NODE_DATA_DESCR));
+      ALL_STATISTICS.put(DELETE_PROPERTY_DATA_DESCR, new Statistics(GLOBAL_STATISTICS, DELETE_PROPERTY_DATA_DESCR));
       // Rename
-      ALL_STATISTICS.put(RENAME_NODE_DATA_DESCR, new Statistics(RENAME_NODE_DATA_DESCR));
+      ALL_STATISTICS.put(RENAME_NODE_DATA_DESCR, new Statistics(GLOBAL_STATISTICS, RENAME_NODE_DATA_DESCR));
       // Rollback
-      ALL_STATISTICS.put(ROLLBACK_DESCR, new Statistics(ROLLBACK_DESCR));
+      ALL_STATISTICS.put(ROLLBACK_DESCR, new Statistics(GLOBAL_STATISTICS, ROLLBACK_DESCR));
       // Others
-      ALL_STATISTICS.put(IS_OPENED_DESCR, new Statistics(IS_OPENED_DESCR));
-      ALL_STATISTICS.put(CLOSE_DESCR, new Statistics(CLOSE_DESCR));
+      ALL_STATISTICS.put(IS_OPENED_DESCR, new Statistics(null, IS_OPENED_DESCR));
+      ALL_STATISTICS.put(CLOSE_DESCR, new Statistics(null, CLOSE_DESCR));
    }
 
-   /**
-    * The printer used to print the statistics in csv format
-    */
-   private static PrintWriter STATISTICS_WRITER;
    static
    {
       if (JDBCWorkspaceDataContainer.STATISTICS_ENABLED)
       {
-         initWriter();
-         if (STATISTICS_WRITER != null)
-         {
-            addTriggers();
-         }
+         JCRStatisticsManager.registerStatistics("JDBCStorageConnection", GLOBAL_STATISTICS, ALL_STATISTICS);
       }
    }
 
-   /**
-    * Add all the triggers that will keep the file up to date.
-    */
-   private static void addTriggers()
-   {
-      Runtime.getRuntime().addShutdownHook(new Thread("StatisticsJDBCStorageConnection-Hook")
-      {
-         public void run()
-         {
-            printData();
-         }
-      });
-      // Define the file header
-      printHeader();
-      Thread t = new Thread("StatisticsJDBCStorageConnection-Writer")
-      {
-         public void run()
-         {
-            while (true)
-            {
-               try
-               {
-                  sleep(5000);
-               }
-               catch (InterruptedException e)
-               {
-                  LOG.debug("InterruptedException", e);
-               }
-               printData();
-            }
-         }
-      };
-      t.setDaemon(true);
-      t.start();
-   }
 
    /**
-    * Initialize the {@link PrintWriter}.
-    * It will first try to create the file in the user directory, if it cannot, it will try
-    * to create it in the temporary folder.
-    */
-   private static void initWriter()
-   {
-      File file = null;
-      try
-      {
-         file =
-            new File(System.getProperty("user.dir"), "StatisticsJDBCStorageConnection-" + System.currentTimeMillis()
-               + ".csv");
-         file.createNewFile();
-         STATISTICS_WRITER = new PrintWriter(file);
-      }
-      catch (IOException e)
-      {
-         LOG
-            .error(
-               "Cannot create the file for the statistics in the user directory, we will try to create it in the temp directory",
-               e);
-         try
-         {
-            file = File.createTempFile("StatisticsJDBCStorageConnection", "-" + System.currentTimeMillis() + ".csv");
-            STATISTICS_WRITER = new PrintWriter(file);
-         }
-         catch (IOException e1)
-         {
-            LOG.error("Cannot create the file for the statistics", e1);
-         }
-      }
-      if (file != null)
-      {
-         LOG.info("The file for the statistics is " + file.getPath());
-      }
-   }
-
-   /**
     * The nested {@link WorkspaceStorageConnection}
     */
    private final WorkspaceStorageConnection wcs;
@@ -633,173 +539,4 @@
          s.end();
       }
    }
-
-   /**
-    * Print the header of the csv file
-    */
-   private static void printHeader()
-   {
-      GLOBAL_STATISTICS.printHeader(STATISTICS_WRITER);
-      for (Statistics s : ALL_STATISTICS.values())
-      {
-         STATISTICS_WRITER.print(',');
-         s.printHeader(STATISTICS_WRITER);
-      }
-      STATISTICS_WRITER.println();
-      STATISTICS_WRITER.flush();
-   }
-
-   /**
-    * Add one line of data
-    */
-   private static void printData()
-   {
-      GLOBAL_STATISTICS.printData(STATISTICS_WRITER);
-      for (Statistics s : ALL_STATISTICS.values())
-      {
-         STATISTICS_WRITER.print(',');
-         s.printData(STATISTICS_WRITER);
-      }
-      STATISTICS_WRITER.println();
-      STATISTICS_WRITER.flush();
-   }
-
-   /**
-    * The class used to manage all the metrics such as minimum, maximum, total, times and average.
-    */
-   private static class Statistics
-   {
-
-      /**
-       * The description of the statistics
-       */
-      private final String description;
-
-      /**
-       * The min value of the time spent for one call
-       */
-      private final AtomicLong min = new AtomicLong(Long.MAX_VALUE);
-
-      /**
-       * The max value of the time spent for one call
-       */
-      private final AtomicLong max = new AtomicLong(-1);
-
-      /**
-       * The total time spent for all the calls
-       */
-      private final AtomicLong total = new AtomicLong();
-
-      /**
-       * The total amount of calls
-       */
-      private final AtomicLong times = new AtomicLong();
-
-      /**
-       * The {@link ThreadLocal} used to keep the initial timestamp
-       */
-      private final ThreadLocal<Queue<Long>> currentTime = new ThreadLocal<Queue<Long>>()
-      {
-         protected Queue<Long> initialValue()
-         {
-            return new LinkedList<Long>();
-         }
-      };
-
-      /**
-       * The default constructor
-       * @param description the description of the statistics
-       */
-      public Statistics(String description)
-      {
-         this.description = description;
-      }
-
-      /**
-       * Start recording
-       */
-      public void begin()
-      {
-         GLOBAL_STATISTICS.onBegin();
-         onBegin();
-      }
-
-      /**
-       * Store the current timestamp in the {@link ThreadLocal}
-       */
-      private void onBegin()
-      {
-         Queue<Long> q = currentTime.get();
-         q.add(System.currentTimeMillis());
-      }
-
-      /**
-       * Stop recording
-       */
-      public void end()
-      {
-         onEnd();
-         GLOBAL_STATISTICS.onEnd();
-      }
-
-      /**
-       * Refresh the values of the metrics (min, max, total and times)
-       */
-      private void onEnd()
-      {
-         long result = System.currentTimeMillis() - currentTime.get().poll();
-         times.incrementAndGet();
-         if (result < min.get())
-         {
-            min.set(result);
-         }
-         if (max.get() < result)
-         {
-            max.set(result);
-         }
-         total.addAndGet(result);
-      }
-
-      /**
-       * Print the description of all the metrics into the given {@link PrintWriter}
-       */
-      public void printHeader(PrintWriter pw)
-      {
-         pw.print(description);
-         pw.print("-Min,");
-         pw.print(description);
-         pw.print("-Max,");
-         pw.print(description);
-         pw.print("-Total,");
-         pw.print(description);
-         pw.print("-Avg,");
-         pw.print(description);
-         pw.print("-Times");
-      }
-
-      /**
-       * Print the current snapshot of the metrics and evaluate the average value
-       */
-      public void printData(PrintWriter pw)
-      {
-         long lmin = min.get();
-         if (lmin == Long.MAX_VALUE)
-         {
-            lmin = -1;
-         }
-         long lmax = max.get();
-         long ltotal = total.get();
-         long ltimes = times.get();
-         float favg = ltimes == 0 ? 0f : (float)ltotal / ltimes;
-         pw.print(lmin);
-         pw.print(',');
-         pw.print(lmax);
-         pw.print(',');
-         pw.print(ltotal);
-         pw.print(',');
-         pw.print(favg);
-         pw.print(',');
-         pw.print(ltimes);
-      }
-   }
 }

Added: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/statistics/JCRStatisticsManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/statistics/JCRStatisticsManager.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/statistics/JCRStatisticsManager.java	2010-04-13 07:03:28 UTC (rev 2268)
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) 2003-2010 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see&lt;http://www.gnu.org/licenses/&gt;.
+ */
+package org.exoplatform.services.jcr.statistics;
+
+import org.exoplatform.container.ExoContainer;
+import org.exoplatform.container.ExoContainerContext;
+import org.exoplatform.management.ManagementContext;
+import org.exoplatform.management.annotations.Managed;
+import org.exoplatform.management.annotations.ManagedDescription;
+import org.exoplatform.management.jmx.annotations.NameTemplate;
+import org.exoplatform.management.jmx.annotations.Property;
+import org.exoplatform.management.rest.annotations.RESTEndpoint;
+import org.exoplatform.services.jcr.storage.WorkspaceStorageConnection;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class manages all the statistics of eXo JCR. This will print all the metrics value into a file in csv format 
+ * for all the registered statistics. It will provide metrics of type minimum, maximum, total, times and average 
+ * for each method of {@link WorkspaceStorageConnection} and the global values. It will add data into the file 
+ * every 5 seconds and add the last line at JVM exit. This class will also expose all the statistics through JMX.
+ * 
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto 
+ *          nicolas.filotto at exoplatform.com
+ * 30 mars 2010  
+ */
+ at Managed
+ at ManagedDescription("JCR statistics manager")
+ at NameTemplate({@Property(key = "view", value = "jcr"), @Property(key = "service", value = "statistic")})
+ at RESTEndpoint(path = "jcrstatistics")
+public class JCRStatisticsManager
+{
+
+   /**
+    * The logger
+    */
+   private static final Log LOG = ExoLogger.getLogger("exo.jcr.component.core.JCRStatisticsManager");
+
+   /**
+    * The name of the global statistics
+    */
+   private static final String GLOBAL_STATISTICS_NAME = "global";
+
+   /**
+    * The flag that indicates if the manager is launched or not.
+    */
+   private static volatile boolean STARTED;
+
+   /**
+    * The list of all the contexts of statistics managed
+    */
+   private static Map<String, StatisticsContext> CONTEXTS =
+      Collections.unmodifiableMap(new HashMap<String, StatisticsContext>());
+
+   /**
+    * Indicates if the persistence of the statistics has to be enabled.
+    */
+   public static final boolean PERSISTENCE_ENABLED =
+      Boolean.valueOf(System.getProperty("JCRStatisticsManager.persistence.enabled", "true"));
+
+   /**
+    * The length of time in milliseconds after which the snapshot of the statistics is persisted.
+    */
+   public static final long PERSISTENCE_TIMEOUT =
+      Long.valueOf(System.getProperty("JCRStatisticsManager.persistence.timeout", "5000"));
+
+   /**
+    * Default constructor.
+    */
+   private JCRStatisticsManager()
+   {
+   }
+
+   /**
+    * Register a new category of statistics to manage.
+    * @param category the name of the category of statistics to register.
+    * @param global the global statistics.
+    * @param allStatistics the list of all statistics corresponding to the category.
+    */
+   public static void registerStatistics(String category, Statistics global, Map<String, Statistics> allStatistics)
+   {
+      if (category == null || category.length() == 0)
+      {
+         throw new IllegalArgumentException("The category of the statistics cannot be empty");
+      }
+      if (allStatistics == null || allStatistics.isEmpty())
+      {
+         throw new IllegalArgumentException("The list of statistics " + category + " cannot be empty");
+      }
+      PrintWriter pw = null;
+      if (PERSISTENCE_ENABLED)
+      {
+         pw = initWriter(category);
+         if (pw == null)
+         {
+            LOG.warn("Cannot create the print writer for the statistics " + category);
+         }
+      }
+      startIfNeeded();
+      synchronized (JCRStatisticsManager.class)
+      {
+         Map<String, StatisticsContext> tmpContexts = new HashMap<String, StatisticsContext>(CONTEXTS);
+         StatisticsContext ctx = new StatisticsContext(pw, global, allStatistics);
+         tmpContexts.put(category, ctx);
+         if (pw != null)
+         {
+            // Define the file header
+            printHeader(ctx);
+         }
+         CONTEXTS = Collections.unmodifiableMap(tmpContexts);
+      }
+   }
+
+   /**
+    * Initialize the {@link PrintWriter}.
+    * It will first try to create the file in the user directory, if it cannot, it will try
+    * to create it in the temporary folder.
+    * @return the corresponding {@link PrintWriter}
+    */
+   private static PrintWriter initWriter(String category)
+   {
+      PrintWriter pw = null;
+      File file = null;
+      try
+      {
+         file =
+            new File(System.getProperty("user.dir"), "Statistics" + category + "-" + System.currentTimeMillis()
+               + ".csv");
+         file.createNewFile();
+         pw = new PrintWriter(file);
+      }
+      catch (IOException e)
+      {
+         LOG.error("Cannot create the file for the statistics " + category
+            + " in the user directory, we will try to create it in the temp directory", e);
+         try
+         {
+            file = File.createTempFile("Statistics" + category, "-" + System.currentTimeMillis() + ".csv");
+            pw = new PrintWriter(file);
+         }
+         catch (IOException e1)
+         {
+            LOG.error("Cannot create the file for the statistics " + category, e1);
+         }
+      }
+      if (file != null)
+      {
+         LOG.info("The file for the statistics " + category + " is " + file.getPath());
+      }
+      return pw;
+   }
+
+   /**
+    * Starts the manager if needed
+    */
+   private static void startIfNeeded()
+   {
+      if (!STARTED)
+      {
+         synchronized (JCRStatisticsManager.class)
+         {
+            if (!STARTED)
+            {
+               addTriggers();
+               ExoContainer container = ExoContainerContext.getTopContainer();
+               ManagementContext ctx = null;
+               if (container != null)
+               {
+                  ctx = container.getManagementContext();
+               }
+               if (ctx == null)
+               {
+                  LOG.warn("Cannot register the statistics");
+               }
+               else
+               {
+                  ctx.register(new JCRStatisticsManager());
+               }
+               STARTED = true;
+            }
+         }
+      }
+   }
+
+   /**
+    * Add all the triggers that will keep the file up to date only if the persistence
+    * is enabled.
+    */
+   private static void addTriggers()
+   {
+      if (!PERSISTENCE_ENABLED)
+      {
+         return;
+      }
+      Runtime.getRuntime().addShutdownHook(new Thread("JCRStatisticsManager-Hook")
+      {
+         public void run()
+         {
+            printData();
+         }
+      });
+      Thread t = new Thread("JCRStatisticsManager-Writer")
+      {
+         public void run()
+         {
+            while (true)
+            {
+               try
+               {
+                  sleep(PERSISTENCE_TIMEOUT);
+               }
+               catch (InterruptedException e)
+               {
+                  LOG.debug("InterruptedException", e);
+               }
+               printData();
+            }
+         }
+      };
+      t.setDaemon(true);
+      t.start();
+   }
+
+   /**
+    * Print the header of the csv file related to the given context.
+    */
+   private static void printHeader(StatisticsContext context)
+   {
+      if (context.writer == null)
+      {
+         return;
+      }
+      boolean first = true;
+      if (context.global != null)
+      {
+         context.global.printHeader(context.writer);
+         first = false;
+      }
+      for (Statistics s : context.allStatistics.values())
+      {
+         if (first)
+         {
+            first = false;
+         }
+         else
+         {
+            context.writer.print(',');
+         }
+         s.printHeader(context.writer);
+      }
+      context.writer.println();
+      context.writer.flush();
+   }
+
+   /**
+    * Add one line of data to all the csv files.
+    */
+   private static void printData()
+   {
+      Map<String, StatisticsContext> tmpContexts = CONTEXTS;
+      for (StatisticsContext context : tmpContexts.values())
+      {
+         printData(context);
+      }
+   }
+
+   /**
+    * Add one line of data to the csv file related to the given context.
+    */
+   private static void printData(StatisticsContext context)
+   {
+      if (context.writer == null)
+      {
+         return;
+      }
+      boolean first = true;
+      if (context.global != null)
+      {
+         context.global.printData(context.writer);
+         first = false;
+      }
+      for (Statistics s : context.allStatistics.values())
+      {
+         if (first)
+         {
+            first = false;
+         }
+         else
+         {
+            context.writer.print(',');
+         }
+         s.printData(context.writer);
+      }
+      context.writer.println();
+      context.writer.flush();
+   }
+
+   /**
+    * Retrieve statistics context of the given category.
+    * @return the related {@link StatisticsContext}, <code>null</code> otherwise.
+    */
+   private static StatisticsContext getContext(String category)
+   {
+      if (category == null)
+      {
+         return null;
+      }
+      return CONTEXTS.get(category);
+   }
+
+   /**
+    * Format the name of the statistics in the target format
+    * @param name the name of the statistics requested
+    * @return the formated statistics name
+    */
+   static String formatName(String name)
+   {
+      return name == null ? null : name.replaceAll(" ", "").replaceAll("[,;]", ", ");
+   }
+
+   /**
+    * Retrieve statistics of the given category and name.
+    * @return the related {@link Statistics}, <code>null</code> otherwise.
+    */
+   private static Statistics getStatistics(String category, String name)
+   {
+      StatisticsContext context = getContext(category);
+      if (context == null)
+      {
+         return null;
+      }
+      // Format the name
+      name = formatName(name);
+      if (name == null)
+      {
+         return null;
+      }
+      Statistics statistics;
+      if (GLOBAL_STATISTICS_NAME.equalsIgnoreCase(name))
+      {
+         statistics = context.global;
+      }
+      else
+      {
+         statistics = context.allStatistics.get(name);
+      }
+      return statistics;
+   }
+
+   /**
+    * @return the <code>min</code> value for the statistics corresponding to the given
+    * category and name.
+    */
+   @Managed
+   @ManagedDescription("The minimum value of the time spent for one call.")
+   public static long getMin(String category, String name)
+   {
+      Statistics statistics = getStatistics(category, name);
+      return statistics == null ? 0l : statistics.getMin();
+   }
+
+   /**
+    * @return the <code>max</code> value for the statistics corresponding to the given
+    * category and name.
+    */
+   @Managed
+   @ManagedDescription("The maximum value of the time spent for one call.")
+   public static long getMax(String category, String name)
+   {
+      Statistics statistics = getStatistics(category, name);
+      return statistics == null ? 0l : statistics.getMax();
+   }
+
+   /**
+    * @return the <code>total</code> value for the statistics corresponding to the given
+    * category and name.
+    */
+   @Managed
+   @ManagedDescription("The total time spent for all the calls.")
+   public static long getTotal(String category, String name)
+   {
+      Statistics statistics = getStatistics(category, name);
+      return statistics == null ? 0l : statistics.getTotal();
+   }
+
+   /**
+    * @return the <code>times</code> value for the statistics corresponding to the given
+    * category and name.
+    */
+   @Managed
+   @ManagedDescription("The total amount of calls.")
+   public static long getTimes(String category, String name)
+   {
+      Statistics statistics = getStatistics(category, name);
+      return statistics == null ? 0l : statistics.getTimes();
+   }
+
+   /**
+    * @return the <code>avg</code> value for the statistics corresponding to the given
+    * category and name.
+    */
+   @Managed
+   @ManagedDescription("The average value of the time spent for one call.")
+   public static float getAvg(String category, String name)
+   {
+      Statistics statistics = getStatistics(category, name);
+      return statistics == null ? 0l : statistics.getAvg();
+   }
+
+   /**
+    * Allows to reset the statistics corresponding to the given category and name.
+    * @param category
+    * @param name
+    */
+   @Managed
+   @ManagedDescription("Reset the statistics.")
+   public static void reset(String category, String name)
+   {
+      Statistics statistics = getStatistics(category, name);
+      if (statistics != null)
+      {
+         statistics.reset();
+      }
+   }
+
+   /**
+    * Allows to reset all the statistics corresponding to the given category.
+    * @param category
+    */
+   @Managed
+   @ManagedDescription("Reset all the statistics.")
+   public static void resetAll(String category)
+   {
+      StatisticsContext context = getContext(category);
+      if (context != null)
+      {
+         context.reset();
+      }
+   }
+
+   /**
+    * Define the context of a given category of statistics
+    */
+   private static class StatisticsContext
+   {
+
+      /**
+       * The printer used to print the statistics in csv format
+       */
+      private final PrintWriter writer;
+
+      /**
+       * The list of all the statistics
+       */
+      private final Map<String, Statistics> allStatistics;
+
+      /**
+       * The global statistics
+       */
+      private final Statistics global;
+
+      /**
+       * The default constructor.
+       */
+      public StatisticsContext(PrintWriter writer, Statistics global, Map<String, Statistics> allStatistics)
+      {
+         this.writer = writer;
+         this.global = global;
+         this.allStatistics = allStatistics;
+      }
+
+      /**
+       * Reset all the statistics related to the given context.
+       */
+      public void reset()
+      {
+         if (global != null)
+         {
+            global.reset();
+         }
+         for (Statistics statistics : allStatistics.values())
+         {
+            statistics.reset();
+         }
+      }
+   }
+}

Added: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/statistics/Statistics.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/statistics/Statistics.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/statistics/Statistics.java	2010-04-13 07:03:28 UTC (rev 2268)
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2003-2010 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see&lt;http://www.gnu.org/licenses/&gt;.
+ */
+package org.exoplatform.services.jcr.statistics;
+
+import java.io.PrintWriter;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * The class used to manage all the metrics such as minimum, maximum, total, times and average.
+ * 
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto 
+ *          nicolas.filotto at exoplatform.com
+ * 30 mars 2010  
+ */
+public class Statistics
+{
+
+   /**
+    * The global statistics on the top of the current ones
+    */
+   private final Statistics parent;
+
+   /**
+    * The description of the statistics
+    */
+   private final String description;
+
+   /**
+    * The min value of the time spent for one call
+    */
+   private final AtomicLong min = new AtomicLong(Long.MAX_VALUE);
+
+   /**
+    * The max value of the time spent for one call
+    */
+   private final AtomicLong max = new AtomicLong(-1);
+
+   /**
+    * The total time spent for all the calls
+    */
+   private final AtomicLong total = new AtomicLong();
+
+   /**
+    * The total amount of calls
+    */
+   private final AtomicLong times = new AtomicLong();
+
+   /**
+    * The {@link ThreadLocal} used to keep the initial timestamp
+    */
+   private final ThreadLocal<Queue<Long>> currentTime = new ThreadLocal<Queue<Long>>()
+   {
+      protected Queue<Long> initialValue()
+      {
+         return new LinkedList<Long>();
+      }
+   };
+
+   /**
+    * The default constructor
+    * @param parent the global statistics on the top of the current ones
+    * @param description the description of the statistics, to be compatible with the CSV format
+    * all ',' will be replaced with ';'
+    */
+   public Statistics(Statistics parent, String description)
+   {
+      this.parent = parent;
+      this.description = description.replace(',', ';');
+   }
+
+   /**
+    * Start recording
+    */
+   public void begin()
+   {
+      if (parent != null)
+      {
+         parent.onBegin();         
+      }
+      onBegin();
+   }
+
+   /**
+    * Store the current timestamp in the {@link ThreadLocal}
+    */
+   private void onBegin()
+   {
+      Queue<Long> q = currentTime.get();
+      q.add(System.currentTimeMillis());
+   }
+
+   /**
+    * Stop recording
+    */
+   public void end()
+   {
+      onEnd();
+      if (parent != null)
+      {
+         parent.onEnd();         
+      }
+   }
+
+   /**
+    * Refresh the values of the metrics (min, max, total and times)
+    */
+   private void onEnd()
+   {
+      long result = System.currentTimeMillis() - currentTime.get().poll();
+      times.incrementAndGet();
+      if (result < min.get())
+      {
+         min.set(result);
+      }
+      if (max.get() < result)
+      {
+         max.set(result);
+      }
+      total.addAndGet(result);
+   }
+
+   /**
+    * Print the description of all the metrics into the given {@link PrintWriter}
+    */
+   public void printHeader(PrintWriter pw)
+   {
+      pw.print(description);
+      pw.print("-Min,");
+      pw.print(description);
+      pw.print("-Max,");
+      pw.print(description);
+      pw.print("-Total,");
+      pw.print(description);
+      pw.print("-Avg,");
+      pw.print(description);
+      pw.print("-Times");
+   }
+
+   /**
+    * Print the current snapshot of the metrics and evaluate the average value
+    */
+   public void printData(PrintWriter pw)
+   {
+      long lmin = min.get();
+      if (lmin == Long.MAX_VALUE)
+      {
+         lmin = -1;
+      }
+      long lmax = max.get();
+      long ltotal = total.get();
+      long ltimes = times.get();
+      float favg = ltimes == 0 ? 0f : (float)ltotal / ltimes;
+      pw.print(lmin);
+      pw.print(',');
+      pw.print(lmax);
+      pw.print(',');
+      pw.print(ltotal);
+      pw.print(',');
+      pw.print(favg);
+      pw.print(',');
+      pw.print(ltimes);
+   }
+
+   /**
+    * @return The min value of the time spent for one call
+    */
+   public long getMin()
+   {
+      long lmin = min.get();
+      if (lmin == Long.MAX_VALUE)
+      {
+         lmin = -1;
+      }
+      return lmin;
+   }
+
+   /**
+    * @return The max value of the time spent for one call
+    */
+   public long getMax()
+   {
+      return max.get();
+   }
+
+   /**
+    * @return The total time spent for all the calls
+    */
+   public long getTotal()
+   {
+      return total.get();
+   }
+
+   /**
+    * @return The total amount of calls
+    */
+   public long getTimes()
+   {
+      return times.get();
+   }
+
+   /**
+    * @return The average time spent for one call
+    */
+   public float getAvg()
+   {
+      long ltotal = total.get();
+      long ltimes = times.get();
+      float favg = ltimes == 0 ? 0f : (float)ltotal / ltimes;      
+      return favg;
+   }
+   
+   /**
+    * Reset the statistics
+    */
+   public void reset()
+   {
+      min.set(Long.MAX_VALUE);
+      max.set(0);
+      total.set(0);
+      times.set(0);
+   }
+}
\ No newline at end of file

Added: jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/statistics/TestJCRStatisticsManager.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/statistics/TestJCRStatisticsManager.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/statistics/TestJCRStatisticsManager.java	2010-04-13 07:03:28 UTC (rev 2268)
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2003-2010 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see&lt;http://www.gnu.org/licenses/&gt;.
+ */
+package org.exoplatform.services.jcr.statistics;
+
+import junit.framework.TestCase;
+
+/**
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto 
+ *          nicolas.filotto at exoplatform.com
+ * 12 avr. 2010  
+ */
+public class TestJCRStatisticsManager extends TestCase
+{
+
+   public void testFormatName()
+   {
+      assertNull(JCRStatisticsManager.formatName(null));
+      assertEquals("myMethod(String, String)", JCRStatisticsManager.formatName("myMethod(String, String)"));
+      assertEquals("myMethod(String, String)", JCRStatisticsManager.formatName("  myMethod  (String ,   String)  "));
+      assertEquals("myMethod(String, String)", JCRStatisticsManager.formatName("myMethod(String;String)"));
+      assertEquals("myMethod(String, String)", JCRStatisticsManager.formatName("myMethod(String,String)"));
+   }
+}



More information about the exo-jcr-commits mailing list