[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<http://www.gnu.org/licenses/>.
+ */
+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<http://www.gnu.org/licenses/>.
+ */
+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<http://www.gnu.org/licenses/>.
+ */
+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