Introduce Kernel thread and statistics
authorMathieu Baudier <mbaudier@argeo.org>
Sat, 28 Feb 2015 21:18:53 +0000 (21:18 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Sat, 28 Feb 2015 21:18:53 +0000 (21:18 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@7992 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

demo/log4j.properties
org.argeo.cms/src/org/argeo/cms/internal/kernel/JackrabbitNode.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelThread.java [new file with mode: 0644]

index 122f42a67ff1047c86c7c18e6f1be41fdbc24f37..8b071c0365ad4a317c737a338abc6da750126e68 100644 (file)
@@ -1,6 +1,7 @@
 log4j.rootLogger=WARN, development
 
 log4j.logger.org.argeo=DEBUG
+#log4j.logger.argeo.stats=DEBUG
 
 ## Appenders
 log4j.appender.console=org.apache.log4j.ConsoleAppender
index 5e8669e4b4159de67d218b58501456327c6ad887..c9c100e129e74c5a8f6567ed64e6cc3886bff1d2 100644 (file)
@@ -20,6 +20,7 @@ import org.apache.jackrabbit.core.RepositoryImpl;
 import org.apache.jackrabbit.core.cache.CacheManager;
 import org.apache.jackrabbit.core.config.RepositoryConfig;
 import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
+import org.apache.jackrabbit.stats.RepositoryStatisticsImpl;
 import org.argeo.ArgeoException;
 import org.argeo.cms.CmsException;
 import org.argeo.jackrabbit.JackrabbitWrapper;
@@ -34,7 +35,6 @@ class JackrabbitNode extends JackrabbitWrapper implements KernelConstants,
                ArgeoJcrConstants {
        private static Log log = LogFactory.getLog(JackrabbitNode.class);
 
-       @SuppressWarnings("unused")
        private RepositoryContext repositoryContext;
 
        private ServiceRegistration<Repository> repositoryReg;
@@ -66,11 +66,15 @@ class JackrabbitNode extends JackrabbitWrapper implements KernelConstants,
                ((RepositoryImpl) getRepository()).shutdown();
        }
 
-       Dictionary<String, ?> getDefaults() {
-               return KernelUtils.asDictionary(getClass().getClassLoader(),
-                               "/org/argeo/cms/internal/kernel/jackrabbit-node.properties");
+       RepositoryStatisticsImpl getRepositoryStatistics() {
+               return repositoryContext.getRepositoryStatistics();
        }
 
+       // Dictionary<String, ?> getDefaults() {
+       // return KernelUtils.asDictionary(getClass().getClassLoader(),
+       // "/org/argeo/cms/internal/kernel/jackrabbit-node.properties");
+       // }
+
        private RepositoryConfig getConfiguration(JackrabbitNodeType type,
                        Hashtable<String, Object> vars) throws RepositoryException {
                ClassLoader cl = getClass().getClassLoader();
index c63184a2435d189f314ebc57319dfabb4469b11b..af4f0ff2a75cd868d7597f42f35b63d0d9a8c9ae 100644 (file)
@@ -39,10 +39,12 @@ final class Kernel implements ServiceListener {
 
        private final BundleContext bundleContext = Activator.getBundleContext();
 
-       private JackrabbitNode node;
-       private OsgiJackrabbitRepositoryFactory repositoryFactory;
-       private NodeSecurity nodeSecurity;
-       private NodeHttp nodeHttp;
+       ThreadGroup threadGroup = new ThreadGroup(Kernel.class.getSimpleName());
+       JackrabbitNode node;
+       OsgiJackrabbitRepositoryFactory repositoryFactory;
+       NodeSecurity nodeSecurity;
+       NodeHttp nodeHttp;
+       private KernelThread kernelThread;
 
        void init() {
                ClassLoader currentContextCl = Thread.currentThread()
@@ -68,6 +70,11 @@ final class Kernel implements ServiceListener {
                        ExtendedHttpService httpService = waitForHttpService();
                        nodeHttp = new NodeHttp(httpService, node, nodeSecurity);
 
+                       // Kernel thread
+                       kernelThread = new KernelThread(this);
+                       kernelThread.setContextClassLoader(Kernel.class.getClassLoader());
+                       kernelThread.start();
+
                        // Publish services to OSGi
                        nodeSecurity.publish();
                        node.publish(repositoryFactory);
@@ -94,6 +101,8 @@ final class Kernel implements ServiceListener {
        void destroy() {
                long begin = System.currentTimeMillis();
 
+               kernelThread.destroyAndJoin();
+
                if (nodeHttp != null)
                        nodeHttp.destroy();
                if (nodeSecurity != null)
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelThread.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelThread.java
new file mode 100644 (file)
index 0000000..e58cbee
--- /dev/null
@@ -0,0 +1,123 @@
+package org.argeo.cms.internal.kernel;
+
+import java.io.File;
+import java.lang.management.ManagementFactory;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.api.stats.RepositoryStatistics;
+import org.apache.jackrabbit.stats.RepositoryStatisticsImpl;
+import org.argeo.cms.CmsException;
+
+/**
+ * Background thread started by the {@link Kernel}, which gather statistics and
+ * monitor/control other processes.
+ */
+class KernelThread extends Thread {
+       @SuppressWarnings("unused")
+       private final Kernel kernel;
+       private final RepositoryStatisticsImpl repoStats;
+
+       /** The smallest period of operation, in ms */
+       private final long PERIOD = 60 * 1000l;
+       /** One ms in ns */
+       private final static long m = 1000l * 1000l;
+       private final static long M = 1024l * 1024l;
+
+       private boolean running = true;
+
+       private Log kernelStatsLog = LogFactory.getLog("argeo.stats.kernel");
+       private Log nodeStatsLog = LogFactory.getLog("argeo.stats.node");
+
+       @SuppressWarnings("unused")
+       private long cycle = 0l;
+
+       public KernelThread(Kernel kernel) {
+               super(kernel.threadGroup, kernel.getClass().getSimpleName());
+               this.kernel = kernel;
+               this.repoStats = kernel.node.getRepositoryStatistics();
+       }
+
+       private void doSmallestPeriod() {
+               if (kernelStatsLog.isDebugEnabled()) {
+                       StringBuilder line = new StringBuilder(64);
+                       line.append("§\t");
+                       long freeMem = Runtime.getRuntime().freeMemory() / M;
+                       long totalMem = Runtime.getRuntime().totalMemory() / M;
+                       long maxMem = Runtime.getRuntime().maxMemory() / M;
+                       double loadAvg = ManagementFactory.getOperatingSystemMXBean()
+                                       .getSystemLoadAverage();
+                       // in min
+                       boolean min = true;
+                       long uptime = ManagementFactory.getRuntimeMXBean().getUptime()
+                                       / (1000 * 60);
+                       if (uptime > 24 * 60) {
+                               min = false;
+                               uptime = uptime / 60;
+                       }
+                       line.append(uptime).append(min ? " min" : " h").append('\t');
+                       line.append(loadAvg).append('\t').append(maxMem).append('\t')
+                                       .append(totalMem).append('\t').append(freeMem).append('\t');
+                       kernelStatsLog.debug(line);
+               }
+
+               if (nodeStatsLog.isDebugEnabled()) {
+                       File dataDir = KernelUtils.getOsgiInstanceDir();
+                       long freeSpace = dataDir.getUsableSpace() / M;
+                       // File currentRoot = null;
+                       // for (File root : File.listRoots()) {
+                       // String rootPath = root.getAbsolutePath();
+                       // if (dataDir.getAbsolutePath().startsWith(rootPath)) {
+                       // if (currentRoot == null
+                       // || (rootPath.length() > currentRoot.getPath()
+                       // .length())) {
+                       // currentRoot = root;
+                       // }
+                       // }
+                       // }
+                       // long totalSpace = currentRoot.getTotalSpace();
+                       StringBuilder line = new StringBuilder(128);
+                       line.append("§\t").append(freeSpace)
+                                       .append(" MB left in " + dataDir);
+                       line.append('\n');
+                       for (RepositoryStatistics.Type type : RepositoryStatistics.Type
+                                       .values()) {
+                               long[] vals = repoStats.getTimeSeries(type).getValuePerMinute();
+                               long val = vals[vals.length - 1];
+                               line.append(type.name()).append('\t').append(val).append('\n');
+                       }
+                       nodeStatsLog.debug(line);
+               }
+       }
+
+       @Override
+       public void run() {
+               final long periodNs = PERIOD * m;
+               while (running) {
+                       long beginNs = System.nanoTime();
+                       doSmallestPeriod();
+
+                       long waitNs = periodNs - (System.nanoTime() - beginNs);
+                       if (waitNs < 0)
+                               continue;
+                       // wait
+                       try {
+                               sleep(waitNs / m, (int) (waitNs % m));
+                       } catch (InterruptedException e) {
+                               // silent
+                       }
+                       cycle++;
+               }
+       }
+
+       synchronized void destroyAndJoin() {
+               running = false;
+               notifyAll();
+               interrupt();
+               try {
+                       join(PERIOD * 2);
+               } catch (InterruptedException e) {
+                       throw new CmsException("Kernel thread destruction was interrupted");
+               }
+       }
+}