GERONIMO-6653 sigar extension
authorRomain Manni-Bucau <rmannibucau@gmail.com>
Thu, 25 Oct 2018 15:14:54 +0000 (17:14 +0200)
committerRomain Manni-Bucau <rmannibucau@gmail.com>
Thu, 25 Oct 2018 15:14:54 +0000 (17:14 +0200)
geronimo-metrics-common/pom.xml
geronimo-metrics-extensions/geronimo-metrics-sigar/pom.xml [new file with mode: 0644]
geronimo-metrics-extensions/geronimo-metrics-sigar/src/main/java/org/apache/geronimo/microprofile/metrics/extension/sigar/InitSigar.java [new file with mode: 0644]
geronimo-metrics-extensions/geronimo-metrics-sigar/src/main/java/org/apache/geronimo/microprofile/metrics/extension/sigar/MicroprofileMetricsSigarRegistrar.java [new file with mode: 0644]
geronimo-metrics-extensions/geronimo-metrics-sigar/src/main/java/org/apache/geronimo/microprofile/metrics/extension/sigar/SigarRegistrar.java [new file with mode: 0644]
geronimo-metrics-extensions/geronimo-metrics-sigar/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension [new file with mode: 0644]
geronimo-metrics-extensions/geronimo-metrics-sigar/src/test/java/org/apache/geronimo/microprofile/metrics/extension/sigar/SigarTest.java [new file with mode: 0644]
geronimo-metrics-extensions/pom.xml [new file with mode: 0644]
geronimo-metrics/pom.xml
pom.xml

index 81d5375..92e243a 100644 (file)
   <properties>
     <geronimo-metrics.Automatic-Module-Name>org.apache.geronimo.microprofile.metrics.common</geronimo-metrics.Automatic-Module-Name>
   </properties>
-
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <version>4.0.0</version>
-        <extensions>true</extensions>
-      </plugin>
-    </plugins>
-  </build>
 </project>
diff --git a/geronimo-metrics-extensions/geronimo-metrics-sigar/pom.xml b/geronimo-metrics-extensions/geronimo-metrics-sigar/pom.xml
new file mode 100644 (file)
index 0000000..4a4345a
--- /dev/null
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="
+            http://maven.apache.org/POM/4.0.0
+            http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>geronimo-metrics-extensions</artifactId>
+    <groupId>org.apache.geronimo</groupId>
+    <version>1.0.1-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>geronimo-metrics-sigar</artifactId>
+  <name>Geronimo Metrics :: Extensions :: Sigar</name>
+  <packaging>bundle</packaging>
+
+  <properties>
+    <sigar.version>1.6.4</sigar.version>
+    <geronimo-metrics.Automatic-Module-Name>org.apache.geronimo.microprofile.metrics.sigar</geronimo-metrics.Automatic-Module-Name>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-jcdi_2.0_spec</artifactId>
+      <version>1.0.1</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.fusesource</groupId>
+      <artifactId>sigar</artifactId>
+      <version>${sigar.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>log4j</groupId>
+          <artifactId>log4j</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.fusesource</groupId>
+      <artifactId>sigar</artifactId>
+      <version>${sigar.version}</version>
+      <classifier>native</classifier>
+      <exclusions>
+        <exclusion>
+          <groupId>log4j</groupId>
+          <artifactId>log4j</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-interceptor_1.2_spec</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-atinject_1.0_spec</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-annotation_1.3_spec</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.meecrowave</groupId>
+      <artifactId>meecrowave-junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.geronimo</groupId>
+      <artifactId>geronimo-metrics</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/geronimo-metrics-extensions/geronimo-metrics-sigar/src/main/java/org/apache/geronimo/microprofile/metrics/extension/sigar/InitSigar.java b/geronimo-metrics-extensions/geronimo-metrics-sigar/src/main/java/org/apache/geronimo/microprofile/metrics/extension/sigar/InitSigar.java
new file mode 100644 (file)
index 0000000..1324ceb
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geronimo.microprofile.metrics.extension.sigar;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.util.logging.Logger;
+
+import org.hyperic.jni.ArchLoaderException;
+import org.hyperic.jni.ArchNotSupportedException;
+import org.hyperic.sigar.Sigar;
+import org.hyperic.sigar.SigarException;
+import org.hyperic.sigar.SigarLoader;
+
+// note: for some integration the license is GPL3 so we can't provide it
+//       -> user will have to download sigar-dist, unpack it and point to the folder using
+//       $ -Dorg.hyperic.sigar.path=/path/to/folder/with/native/integs
+public class InitSigar {
+
+    private boolean valid;
+
+    private final File tempDir;
+
+    public InitSigar(final File tempDir) {
+        this.tempDir = tempDir;
+    }
+
+    public void ensureSigarIsSetup() {
+        valid = true;
+
+        final SigarLoader loader = new SigarLoader(Sigar.class);
+        try {
+            loadFromPath(loader);
+        } catch (final UnsatisfiedLinkError e) {
+            unavailable(e.getMessage());
+        }
+    }
+
+    private void loadFromPath(final SigarLoader loader) {
+        try {
+            final String systemProp = loader.getPackageName() + ".path";
+            final String path = System.getProperty(systemProp);
+            if (path == null) {
+                final String libraryName = loader.getLibraryName();
+                final File output = new File(tempDir, "sigar/" + libraryName);
+                if (!output.exists()) {
+                    final int dot = libraryName.lastIndexOf('.');
+                    final String resourceName = libraryName.substring(0, dot) + "-"
+                            + System.getProperty("sigar.version", "1.6.4") + libraryName.substring(dot);
+                    try (final InputStream stream = Thread.currentThread().getContextClassLoader()
+                                                          .getResourceAsStream(resourceName)) {
+                        if (stream != null) {
+                            output.getParentFile().mkdirs();
+                            Files.copy(stream, output.toPath(), StandardCopyOption.REPLACE_EXISTING);
+                        } else {
+                            unavailable("native library not found in the classloader as " + resourceName);
+                            return;
+                        }
+                        loader.load(output.getParentFile().getAbsolutePath());
+                        afterLoad(systemProp);
+                    } catch (final ArchLoaderException | IOException ex) {
+                        unavailable(ex.getMessage());
+                    }
+                }
+            } else if (!"-".equals(path)) {
+                try {
+                    loader.load(path);
+                    afterLoad(systemProp);
+                } catch (final ArchLoaderException ex) {
+                    unavailable(ex.getMessage());
+                }
+            }
+        } catch (final ArchNotSupportedException ex) {
+            unavailable(ex.getMessage());
+        }
+    }
+
+    private void unavailable(final String message) {
+        Logger.getLogger(InitSigar.class.getName()).info("Sigar is not available: " + message);
+        valid = false;
+    }
+
+    private void afterLoad(final String systemProp) {
+        // ensure it works
+        final String original = System.getProperty(systemProp);
+        System.setProperty(systemProp, "-");
+        try {
+            testItWorks();
+        } catch (final Throwable throwable) {
+            unavailable(throwable.getMessage());
+            if (original == null) {
+                System.clearProperty(systemProp);
+            } else {
+                System.setProperty(systemProp, original);
+            }
+        }
+    }
+
+    private void testItWorks() throws SigarException {
+        final Sigar sigar = new Sigar();
+        sigar.getCpu();
+        sigar.close();
+    }
+
+    public boolean isValid() {
+        return valid;
+    }
+}
diff --git a/geronimo-metrics-extensions/geronimo-metrics-sigar/src/main/java/org/apache/geronimo/microprofile/metrics/extension/sigar/MicroprofileMetricsSigarRegistrar.java b/geronimo-metrics-extensions/geronimo-metrics-sigar/src/main/java/org/apache/geronimo/microprofile/metrics/extension/sigar/MicroprofileMetricsSigarRegistrar.java
new file mode 100644 (file)
index 0000000..25b4f99
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geronimo.microprofile.metrics.extension.sigar;
+
+import static org.eclipse.microprofile.metrics.MetricType.GAUGE;
+
+import java.io.File;
+import java.lang.annotation.Annotation;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.spi.AfterDeploymentValidation;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.BeforeShutdown;
+import javax.enterprise.inject.spi.Extension;
+
+import org.eclipse.microprofile.metrics.Gauge;
+import org.eclipse.microprofile.metrics.Metadata;
+import org.eclipse.microprofile.metrics.MetricRegistry;
+import org.eclipse.microprofile.metrics.annotation.RegistryType;
+
+public class MicroprofileMetricsSigarRegistrar implements Extension {
+
+    private SigarRegistrar registrar;
+
+    void afterValidation(@Observes final AfterDeploymentValidation validation,
+                         final BeanManager beanManager) {
+        final InitSigar initSigar = new InitSigar(findTempDir());
+        initSigar.ensureSigarIsSetup();
+        if (!initSigar.isValid()) {
+            return;
+        }
+
+        final MetricRegistry.Type registryType = MetricRegistry.Type.valueOf(
+                System.getProperty("geronimo.metrics.sigar.registry.type", "BASE"));
+        final Set<Bean<?>> beans = beanManager.getBeans(MetricRegistry.class, new RegistryTypeLiteral(registryType));
+        final MetricRegistry registry = MetricRegistry.class.cast(beanManager.getReference(
+                beanManager.resolve(beans), MetricRegistry.class, beanManager.createCreationalContext(null)));
+        registrar = new SigarRegistrar(
+                def -> registry.register(
+                    new Metadata(def.getName(), def.getDisplayName(), def.getDescription(), GAUGE, def.getUnit()),
+                    (Gauge<Double>) () -> def.getEvaluator().getAsDouble()),
+                def -> registry.remove(def.getName()));
+        registrar.start();
+    }
+
+    void beforeShutdown(@Observes final BeforeShutdown beforeShutdown) {
+        if (registrar == null) {
+            return;
+        }
+        registrar.stop();
+    }
+
+    // let's try some well know temp folders and fallback on java io one
+    private File findTempDir() {
+        return new File(
+                Stream.of(
+                        "geronimo.metrics.sigar.location",
+                        "catalina.base", "catalina.base",
+                        "meecrowave.base", "tomee.base",
+                        "application.base", "application.home")
+                    .map(System::getProperty)
+                    .filter(Objects::nonNull)
+                    .map(File::new)
+                    .filter(File::exists)
+                    .flatMap(root -> Stream.of(
+                            new File(root, "work"),
+                            new File(root, "temp"),
+                            new File(root, "tmp")))
+                    .filter(File::exists)
+                    .findFirst()
+                    .orElseGet(() -> new File(System.getProperty("java.io.tmpdir", "."))),
+                System.getProperty("geronimo.metrics.sigar.folder", "sigar"));
+    }
+
+    private static class RegistryTypeLiteral implements RegistryType {
+        private final MetricRegistry.Type type;
+
+        private RegistryTypeLiteral(final MetricRegistry.Type registryType) {
+            this.type = registryType;
+        }
+
+        @Override
+        public MetricRegistry.Type type() {
+            return type;
+        }
+
+        @Override
+        public Class<? extends Annotation> annotationType() {
+            return RegistryType.class;
+        }
+    }
+}
diff --git a/geronimo-metrics-extensions/geronimo-metrics-sigar/src/main/java/org/apache/geronimo/microprofile/metrics/extension/sigar/SigarRegistrar.java b/geronimo-metrics-extensions/geronimo-metrics-sigar/src/main/java/org/apache/geronimo/microprofile/metrics/extension/sigar/SigarRegistrar.java
new file mode 100644 (file)
index 0000000..02c7f1d
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geronimo.microprofile.metrics.extension.sigar;
+
+import static java.util.stream.Collectors.toList;
+import static org.hyperic.sigar.SigarProxyCache.EXPIRE_DEFAULT;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.function.DoubleSupplier;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import org.hyperic.sigar.Cpu;
+import org.hyperic.sigar.CpuInfo;
+import org.hyperic.sigar.Sigar;
+import org.hyperic.sigar.SigarException;
+import org.hyperic.sigar.SigarProxy;
+import org.hyperic.sigar.SigarProxyCache;
+
+// important: this class is stack agnostic and must not use cdi or anything else
+public class SigarRegistrar {
+    private final Consumer<Definition> onRegister;
+    private final Consumer<Definition> onUnregister;
+    private Sigar sigarImpl;
+    private SigarProxy sigar;
+    private Thread refreshThread;
+    private volatile boolean stopped = true;
+    private long refreshInterval;
+    private final Map<String, Definition> currentDefinitions = new HashMap<>();
+
+    public SigarRegistrar(final Consumer<Definition> onRegister,
+                           final Consumer<Definition> onUnregister) {
+        this.onRegister = onRegister;
+        this.onUnregister = onUnregister;
+    }
+
+    public synchronized void start() {
+        this.sigarImpl = new Sigar();
+        this.sigar = SigarProxyCache.newInstance(sigarImpl, Integer.getInteger("geronimo.metrics.sigar.cache", EXPIRE_DEFAULT));
+
+        refreshInterval = Long.getLong("geronimo.metrics.sigar.refreshInterval", TimeUnit.MINUTES.toMillis(5));
+        if (refreshInterval > 0) {
+            refreshThread = new Thread(() -> {
+                final long iterationDuration = 250;
+                final long iterations = refreshInterval / iterationDuration;
+                while (!stopped) {
+                    for (long i = 0; i < iterations; i++) {
+                        if (stopped) {
+                            return;
+                        }
+                        try {
+                            Thread.sleep(iterationDuration);
+                        } catch (final InterruptedException e) {
+                            Thread.currentThread().interrupt();
+                        }
+                    }
+                    tick();
+                }
+            }, getClass().getName() + "-refresher-" + hashCode());
+            stopped = false;
+            refreshThread.start();
+        }
+        tick();
+    }
+
+    public synchronized void tick() {
+        final Collection<Definition> currentMetrics = collectMetrics();
+        final Collection<Definition> alreadyRegistered = currentMetrics.stream()
+                .filter(it -> currentDefinitions.containsKey(it.getName()))
+                .collect(toList());
+        final Collection<Definition> missingRegistered = new ArrayList<>(currentDefinitions.values());
+        missingRegistered.removeAll(alreadyRegistered);
+
+        // remove no more accurate metrics
+        missingRegistered.forEach(it -> {
+            currentDefinitions.remove(it.getName());
+            if (onUnregister != null) {
+                onUnregister.accept(it);
+            }
+        });
+
+        // register new metrics
+        currentMetrics.removeAll(alreadyRegistered);
+        currentMetrics.forEach(it -> onRegister.accept(new Definition(
+            it.getName(), it.getDisplayName(), it.getDescription(), it.getUnit(),
+            () -> it.getEvaluator().getAsDouble())));
+    }
+
+    public synchronized void stop() {
+        if (refreshThread != null) {
+            stopped = true;
+            try {
+                refreshThread.join(500);
+                if (refreshThread.isAlive()) {
+                    refreshThread.interrupt();
+                }
+            } catch (final InterruptedException e) {
+                Thread.currentThread().interrupt();
+            } finally {
+                refreshThread = null;
+            }
+        }
+        sigarImpl.close();
+    }
+
+    private Collection<Definition> collectMetrics() {
+        final Collection<Definition> definitions = new ArrayList<>();
+        
+        // global
+        addCpu(definitions, "sigar.cpu.", () -> sigar.getCpu());
+        addMem(definitions);
+
+        // individual CPU
+        try {
+            final CpuInfo[] cpuInfoList = sigar.getCpuInfoList();
+            IntStream.range(0, cpuInfoList.length)
+                     .forEach(idx -> addCpu(definitions, "sigar.cpu." + idx + ".", () -> sigar.getCpuList()[idx]));
+        } catch (final SigarException se) {
+            // ignore
+        }
+
+        // network
+        addNetwork(definitions);
+
+        // filesystem
+        addFileSystem(definitions);
+
+        return definitions;
+    }
+
+    private void addFileSystem(final Collection<Definition> definitions) {
+        try {
+            Stream.of(sigar.getFileSystemList())
+                  .filter(it -> !it.getDirName().startsWith("/sys") &&
+                            !it.getDirName().startsWith("/dev") &&
+                            !it.getDirName().startsWith("/proc") &&
+                            !it.getDirName().startsWith("/run") &&
+                            !it.getDirName().startsWith("/snap"))
+                  .forEach(fs -> {
+                final String devName = fs.getDevName();
+                final String baseName = "sigar.net.disk." + devName.replace('/', '_').replaceFirst("^_", "") + ".";
+                definitions.add(new Definition(
+                        baseName + "read.count", devName + " Reads",
+                        "Reads on " + devName, "count",
+                        () -> sigar.getDiskUsage(devName).getReads()));
+                definitions.add(new Definition(
+                        baseName + "write.count", devName + " Writes",
+                        "Writes on " + devName, "count",
+                        () -> sigar.getDiskUsage(devName).getWrites()));
+                definitions.add(new Definition(
+                        baseName + "read.bytes", devName + " Reads",
+                        "Reads on " + devName, "bytes",
+                        () -> sigar.getDiskUsage(devName).getReadBytes()));
+                definitions.add(new Definition(
+                        baseName + "write.bytes", devName + " Writes",
+                        "Writes on " + devName, "bytes",
+                        () -> sigar.getDiskUsage(devName).getWriteBytes()));
+            });
+        } catch (final SigarException e) {
+            // no-op
+        }
+    }
+
+    private void addNetwork(final Collection<Definition> definitions) {
+        try {
+            sigar.getTcp(); // ensure it is allowed+available
+            definitions.add(new Definition("sigar.network.tcp.active.opens", "Opening connections",
+                    "Active connections openings", "count",
+                    () -> sigar.getTcp().getActiveOpens()));
+            definitions.add(new Definition("sigar.network.tcp.passive.opens", "Passive connections",
+                    "Passive connection openings", "count",
+                    () -> sigar.getTcp().getPassiveOpens()));
+            definitions.add(new Definition("sigar.network.tcp.attempts.fails", "Failed connections",
+                    "Failed connection attempts", "count",
+                    () -> sigar.getTcp().getAttemptFails()));
+            definitions.add(new Definition("sigar.network.tcp.established.reset", "Resetted connections",
+                    "Connection resets received", "count",
+                    () -> sigar.getTcp().getEstabResets()));
+            definitions.add(new Definition("sigar.network.tcp.established.current", "Established connections",
+                    "Connections established", "count",
+                    () -> sigar.getTcp().getCurrEstab()));
+            definitions.add(new Definition("sigar.network.tcp.segments.in", "Received segments",
+                    "Received segments", "count",
+                    () -> sigar.getTcp().getInSegs()));
+            definitions.add(new Definition("sigar.network.tcp.segments.out", "Sent segments",
+                    "Send out segments", "count",
+                    () -> sigar.getTcp().getOutSegs()));
+            definitions.add(new Definition("sigar.network.tcp.segments.retrans", "Retransmitted segments",
+                    "Retransmitted segments", "count",
+                    () -> sigar.getTcp().getRetransSegs()));
+            definitions.add(new Definition("sigar.network.tcp.resets.out", "Sent resets",
+                    "Sent resets", "count",
+                    () -> sigar.getTcp().getOutRsts()));
+        } catch (final Exception | Error  notAvailable) {
+            // no-op
+        }
+        try {
+            sigar.getNetStat();
+            definitions.add(new Definition("sigar.network.tcp.output.total", "Total Outbound",
+                    "Sent bytes", "bytes",
+                    () -> sigar.getNetStat().getTcpOutboundTotal()));
+            definitions.add(new Definition("sigar.network.tcp.inbound.total", "Total Inbound",
+                    "Received bytes", "bytes",
+                    () -> sigar.getNetStat().getTcpInboundTotal()));
+            definitions.add(new Definition("sigar.network.tcp.established", "TCP established",
+                    "TCP established", "count",
+                    () -> sigar.getNetStat().getTcpEstablished()));
+            definitions.add(new Definition("sigar.network.tcp.idle", "TCP Idle",
+                    "TCP Idle", "count",
+                    () -> sigar.getNetStat().getTcpIdle()));
+            definitions.add(new Definition("sigar.network.tcp.closing", "TCP Closing",
+                    "TCP Closing", "count",
+                    () -> sigar.getNetStat().getTcpClosing()));
+            definitions.add(new Definition("sigar.network.tcp.bound", "TCP Bound",
+                    "TCP Bound", "count",
+                    () -> sigar.getNetStat().getTcpBound()));
+            definitions.add(new Definition("sigar.network.tcp.close", "TCP Close",
+                    "TCP Close", "count",
+                    () -> sigar.getNetStat().getTcpClose()));
+            definitions.add(new Definition("sigar.network.tcp.closewait", "TCP Close Wait",
+                    "TCP Close Wait", "count",
+                    () -> sigar.getNetStat().getTcpCloseWait()));
+            definitions.add(new Definition("sigar.network.tcp.listen", "TCP Listen",
+                    "TCP Listen", "count",
+                    () -> sigar.getNetStat().getTcpListen()));
+        } catch (final Exception | Error  notAvailable) {
+            // no-op
+        }
+    }
+
+    private void addMem(final Collection<Definition> definitions) {
+        definitions.add(new Definition(
+                "sigar.mem.ram", "System RAM Memory",
+                "The total amount of physical memory, in [bytes]", "bytes",
+                () -> sigar.getMem().getRam()));
+        definitions.add(new Definition(
+                "sigar.mem.total", "System Total Memory",
+                "The amount of physical memory, in [bytes]", "bytes",
+                () -> sigar.getMem().getTotal()));
+        definitions.add(new Definition(
+                "sigar.mem.used", "System Used Memory",
+                "The amount of physical memory in use, in [bytes]", "bytes",
+                () -> sigar.getMem().getUsed()));
+        definitions.add(new Definition(
+                "sigar.mem.free", "System Free Memory",
+                "The amount of free physical memory, in [bytes]", "bytes",
+                () -> sigar.getMem().getFree()));
+        definitions.add(new Definition(
+                "sigar.mem.actual.used", "System Actual Used Memory",
+                "The actual amount of physical memory in use, in [bytes]", "bytes",
+                () -> sigar.getMem().getActualUsed()));
+        definitions.add(new Definition(
+                "sigar.mem.actual.free", "System Actual Free Memory",
+                "The actual amount of free physical memory, in [bytes]", "bytes",
+                () -> sigar.getMem().getActualFree()));
+    }
+
+    private void addCpu(final Collection<Definition> definitions,
+                        final String base,
+                        final ThrowingSupplier<Cpu> provider) {
+        definitions.add(new Definition(
+                base + "idle", "CPU Idle Time",
+                "The idle time of the CPU, in [ms]", "ms",
+                () -> provider.get().getIdle()));
+        definitions.add(new Definition(
+                base + "nice", "CPU Nice Priority Time",
+                "The time of the CPU spent on nice priority, in [ms]", "ms",
+                () -> provider.get().getNice()));
+        definitions.add(new Definition(
+                base + "sys", "CPU User Time",
+                "The time of the CPU used by the system, in [ms]", "ms",
+                () -> provider.get().getSys()));
+        definitions.add(new Definition(
+                base + "total", "CPU Total Time",
+                "The total time of the CPU, in [ms]", "ms",
+                () -> provider.get().getTotal()));
+        definitions.add(new Definition(
+                base + "wait", "CPU Wait Time",
+                "The time the CPU had to wait for data to be loaded, in [ms]", "ms",
+                () -> provider.get().getWait()));
+    }
+
+    private interface ThrowingSupplier<T> {
+
+        T get() throws Throwable;
+    }
+
+    public static class Definition {
+        private final String name;
+        private final String displayName;
+        private final String description;
+        private final String unit;
+        private final DoubleSupplier evaluator;
+
+        private final int hash;
+
+        private Definition(final String name, final String displayName, final String description,
+                           final String unit, final ThrowingSupplier<Number> evaluator) {
+            this.name = name;
+            this.displayName = displayName;
+            this.description = description;
+            this.unit = unit;
+            this.evaluator = () -> {
+                try {
+                    return evaluator.get().doubleValue();
+                } catch (final Throwable throwable) {
+                    return -1;
+                }
+            };
+
+            this.hash = Objects.hash(name);
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getDisplayName() {
+            return displayName;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public String getUnit() {
+            return unit;
+        }
+
+        public DoubleSupplier getEvaluator() {
+            return evaluator;
+        }
+
+        @Override
+        public boolean equals(final Object that) {
+            if (this == that) {
+                return true;
+            }
+            if (that == null || getClass() != that.getClass()) {
+                return false;
+            }
+            return Objects.equals(name, Definition.class.cast(that).name);
+        }
+
+        @Override
+        public int hashCode() {
+            return hash;
+        }
+    }
+}
diff --git a/geronimo-metrics-extensions/geronimo-metrics-sigar/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/geronimo-metrics-extensions/geronimo-metrics-sigar/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
new file mode 100644 (file)
index 0000000..0cae7c3
--- /dev/null
@@ -0,0 +1 @@
+org.apache.geronimo.microprofile.metrics.extension.sigar.MicroprofileMetricsSigarRegistrar
diff --git a/geronimo-metrics-extensions/geronimo-metrics-sigar/src/test/java/org/apache/geronimo/microprofile/metrics/extension/sigar/SigarTest.java b/geronimo-metrics-extensions/geronimo-metrics-sigar/src/test/java/org/apache/geronimo/microprofile/metrics/extension/sigar/SigarTest.java
new file mode 100644 (file)
index 0000000..e3561ad
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geronimo.microprofile.metrics.extension.sigar;
+
+import static java.util.stream.Collectors.toList;
+import static org.eclipse.microprofile.metrics.MetricRegistry.Type.BASE;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.meecrowave.Meecrowave;
+import org.apache.meecrowave.junit.MeecrowaveRule;
+import org.eclipse.microprofile.metrics.MetricRegistry;
+import org.eclipse.microprofile.metrics.annotation.RegistryType;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+public class SigarTest {
+    @ClassRule
+    public static final MeecrowaveRule RULE = new MeecrowaveRule(new Meecrowave.Builder() {{
+        setSkipHttp(true);
+    }}, "");
+
+    @Inject
+    @RegistryType(type = BASE)
+    private MetricRegistry registry;
+
+    @Test
+    public void test() {
+        RULE.inject(this);
+        final List<String> keys = registry.getGauges()
+                                             .keySet()
+                                             .stream()
+                                             .filter(it -> it.startsWith("sigar."))
+                                             .sorted()
+                                             .collect(toList());
+        assertTrue(keys.toString(), keys.size() > 10 /*whatever, just check it is registered*/);
+        // ensure gauge is usable
+        final Object cpu = registry.getGauges().get("sigar.cpu.total").getValue();
+        assertNotNull(cpu);
+    }
+}
diff --git a/geronimo-metrics-extensions/pom.xml b/geronimo-metrics-extensions/pom.xml
new file mode 100644 (file)
index 0000000..0ed496d
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="
+            http://maven.apache.org/POM/4.0.0
+            http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>geronimo-metrics-parent</artifactId>
+    <groupId>org.apache.geronimo</groupId>
+    <version>1.0.1-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>geronimo-metrics-extensions</artifactId>
+  <packaging>pom</packaging>
+  <name>Geronimo Metrics :: Extensions</name>
+
+  <modules>
+    <module>geronimo-metrics-sigar</module>
+  </modules>
+</project>
index 9f2d066..ba2dc2b 100644 (file)
@@ -28,6 +28,7 @@
   <modelVersion>4.0.0</modelVersion>
 
   <artifactId>geronimo-metrics</artifactId>
+  <name>Geronimo Metrics :: Impl</name>
 
   <properties>
     <geronimo-metrics.Automatic-Module-Name>org.apache.geronimo.microprofile.metrics
     <dependency>
       <groupId>org.apache.geronimo.specs</groupId>
       <artifactId>geronimo-interceptor_1.2_spec</artifactId>
-      <version>1.0</version>
-      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.apache.geronimo.specs</groupId>
       <artifactId>geronimo-atinject_1.0_spec</artifactId>
-      <version>1.0</version>
-      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.apache.geronimo.specs</groupId>
       <artifactId>geronimo-jcdi_2.0_spec</artifactId>
-      <version>1.0.1</version>
-      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.apache.geronimo.specs</groupId>
       <artifactId>geronimo-annotation_1.3_spec</artifactId>
-      <version>1.0</version>
-      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.eclipse.microprofile.config</groupId>
       <artifactId>microprofile-config-api</artifactId>
-      <version>1.2</version>
-      <scope>provided</scope>
       <optional>true</optional>
     </dependency>
 
     </dependency>
 
     <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <version>4.12</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
       <groupId>org.jboss.arquillian.junit</groupId>
       <artifactId>arquillian-junit-container</artifactId>
       <version>${arquillian.version}</version>
     <dependency>
       <groupId>org.apache.meecrowave</groupId>
       <artifactId>meecrowave-arquillian</artifactId>
-      <version>1.2.3</version>
-      <scope>test</scope>
-      <exclusions>
-        <exclusion>
-          <groupId>javax.inject</groupId>
-          <artifactId>javax.inject</artifactId>
-        </exclusion>
-      </exclusions>
     </dependency>
     <dependency>
       <groupId>org.eclipse.microprofile.metrics</groupId>
diff --git a/pom.xml b/pom.xml
index c688d81..e31b473 100644 (file)
--- a/pom.xml
+++ b/pom.xml
   <properties>
     <spec.version>1.1</spec.version>
     <arquillian.version>1.1.8.Final</arquillian.version>
+    <meecrowave.version>1.2.3</meecrowave.version>
   </properties>
 
   <modules>
     <module>geronimo-metrics-common</module>
     <module>geronimo-metrics</module>
+    <module>geronimo-metrics-extensions</module>
   </modules>
 
   <dependencies>
       <version>1.0</version>
       <scope>provided</scope>
     </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.12</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.apache.geronimo.specs</groupId>
+        <artifactId>geronimo-interceptor_1.2_spec</artifactId>
+        <version>1.0</version>
+        <scope>provided</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.geronimo.specs</groupId>
+        <artifactId>geronimo-atinject_1.0_spec</artifactId>
+        <version>1.0</version>
+        <scope>provided</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.geronimo.specs</groupId>
+        <artifactId>geronimo-jcdi_2.0_spec</artifactId>
+        <version>1.0.1</version>
+        <scope>provided</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.geronimo.specs</groupId>
+        <artifactId>geronimo-annotation_1.3_spec</artifactId>
+        <version>1.0</version>
+        <scope>provided</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.microprofile.config</groupId>
+        <artifactId>microprofile-config-api</artifactId>
+        <version>1.2</version>
+        <scope>provided</scope>
+        <optional>true</optional>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.meecrowave</groupId>
+        <artifactId>meecrowave-junit</artifactId>
+        <version>${meecrowave.version}</version>
+        <scope>test</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.meecrowave</groupId>
+        <artifactId>meecrowave-arquillian</artifactId>
+        <version>${meecrowave.version}</version>
+        <scope>test</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.openwebbeans</groupId>
+        <artifactId>openwebbeans-web</artifactId>
+        <version>2.0.7</version>
+        <scope>test</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
   <build>
     <plugins>
       <plugin>
         </configuration>
       </plugin>
       <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>4.0.0</version>
+        <extensions>true</extensions>
+      </plugin>
+      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-jar-plugin</artifactId>
         <version>3.0.2</version>