This is an automated email from the ASF dual-hosted git repository.

joerghoh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git


The following commit(s) were added to refs/heads/master by this push:
     new 49e17d5e improved JMXExporter
     new f9aee5ce Merge branch 'master' of github.com:apache/sling-whiteboard
49e17d5e is described below

commit 49e17d5e58ac5d37250ee18c3e48e712077d29e5
Author: Joerg Hoh <[email protected]>
AuthorDate: Mon Apr 4 17:46:55 2022 +0200

    improved JMXExporter
---
 jmx-exporter/README.md                             |  4 ++
 jmx-exporter/pom.xml                               |  6 ++
 .../jmxexporter/impl/JmxExporterImplFactory.java   | 77 ++++++++++++++--------
 .../jmxexporter/impl/JmxExporterFactoryTest.java   | 65 +++++++++++++++---
 jmx-exporter/src/test/resources/logback-test.xml   | 30 +++++++++
 5 files changed, 145 insertions(+), 37 deletions(-)

diff --git a/jmx-exporter/README.md b/jmx-exporter/README.md
new file mode 100644
index 00000000..d0274663
--- /dev/null
+++ b/jmx-exporter/README.md
@@ -0,0 +1,4 @@
+
+# JMX Exporter
+
+Make values of mbeans available via Sling Metrics
diff --git a/jmx-exporter/pom.xml b/jmx-exporter/pom.xml
index 06639a83..4a5d905b 100644
--- a/jmx-exporter/pom.xml
+++ b/jmx-exporter/pom.xml
@@ -154,6 +154,12 @@
           <version>3.2.2</version>
           <scope>provided</scope>
         </dependency>
+        <dependency>
+        <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.2.6</version>
+            <scope>test</scope>
+        </dependency>
         
         <dependency>
             <groupId>junit</groupId>
diff --git 
a/jmx-exporter/src/main/java/org/apache/sling/whiteboard/jmxexporter/impl/JmxExporterImplFactory.java
 
b/jmx-exporter/src/main/java/org/apache/sling/whiteboard/jmxexporter/impl/JmxExporterImplFactory.java
index 1da4f58a..acdd5797 100644
--- 
a/jmx-exporter/src/main/java/org/apache/sling/whiteboard/jmxexporter/impl/JmxExporterImplFactory.java
+++ 
b/jmx-exporter/src/main/java/org/apache/sling/whiteboard/jmxexporter/impl/JmxExporterImplFactory.java
@@ -49,19 +49,19 @@ import 
org.osgi.service.metatype.annotations.ObjectClassDefinition;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-@Component(configurationPolicy = ConfigurationPolicy.REQUIRE, immediate=true)
+@Component()
 @Designate(ocd=JmxExporterImplFactory.Config.class, factory=true)
 public class JmxExporterImplFactory {
     
     @ObjectClassDefinition(name="JMX to Metrics Exporter")
     public @interface Config {
         
-        @AttributeDefinition(name="objectname", description="A objectname 
pattern "
+        @AttributeDefinition(name="objectnames", description="export all 
attribute of the MBeans matching these objectnames as Sling Metrics"
                 + "(see 
https://docs.oracle.com/en/java/javase/11/docs/api/java.management/javax/management/ObjectName.html";)
-        String objectname();
+        String[] objectnames();
         
         @AttributeDefinition
-        String webconsole_configurationFactory_nameHint() default "JMX to 
Metrics Exporter for pattern {objectname}";
+        String webconsole_configurationFactory_nameHint() default "Pattern: 
{objectnames}";
     }
     
     
@@ -75,7 +75,7 @@ public class JmxExporterImplFactory {
     @Activate
     public void activate(Config config) {
         server = ManagementFactory.getPlatformMBeanServer();
-        registerMetrics(config.objectname());
+        registerMetrics(config.objectnames());
     }
     
 
@@ -83,24 +83,29 @@ public class JmxExporterImplFactory {
      * Register all applicable metrics for an objectname pattern
      * @param pattern describes a objectname pattern
      */
-    private void registerMetrics(String patternString) {
+    private void registerMetrics(String[] patterns) {
         
-        try {
-            ObjectName pattern = new ObjectName(patternString);
-            Set<ObjectName> allMBeans = server.queryNames(pattern, null);
-            allMBeans.forEach(objectname -> {
-                LOG.debug("registering properties for {}", 
objectname.toString());
-                try {
-                    registerMBeanProperties(objectname);
-                } catch (IntrospectionException | InstanceNotFoundException | 
ReflectionException e) {
-                    LOG.error("Cannot register metrics for objectname = {}", 
objectname.toString());
+        for (String patternString : patterns) {
+            try {
+                ObjectName pattern = new ObjectName(patternString);
+                Set<ObjectName> allMBeans = server.queryNames(pattern, null);
+                if (allMBeans.isEmpty()) {
+                    LOG.info("pattern {} does not match any MBean", 
patternString);
+                } else {
+                    allMBeans.forEach(objectname -> {
+                        LOG.debug("registering properties for {}", 
objectname.toString());
+                        try {
+                            registerMBeanProperties(objectname);
+                        } catch (IntrospectionException | 
InstanceNotFoundException | ReflectionException e) {
+                            LOG.error("Cannot register metrics for objectname 
= {}", objectname.toString());
+                        }
+                    });
                 }
-            });
-        } catch (MalformedObjectNameException e) {
-            LOG.error("cannot create an objectname from pattern 
{}",patternString,e);
+            } catch (MalformedObjectNameException e) {
+                LOG.error("cannot create an objectname from pattern 
{}",patternString,e);
+            }
         }
         
-        
     }
     
     
@@ -110,24 +115,38 @@ public class JmxExporterImplFactory {
         for (MBeanAttributeInfo attr : attributes) {
             LOG.debug("Checking mbean = {}, name = {}, type={}",objectname, 
attr.getName(), attr.getType());
             
+            Supplier<?> supplier = null;
             if ("int".equals(attr.getType())) {
+                supplier = getSupplier(objectname, attr.getName(),-1);
+            } else if ("long".equals(attr.getType())) {
+                supplier = getSupplier(objectname, attr.getName(),-1L);
+            }
+            
+            if (supplier != null) {
                 String metricName = toMetricName(objectname, attr.getName());
-                LOG.info("Registering as metric: {}", metricName);
-                Supplier<Integer> supplier = () -> {
-                    try {
-                        return (Integer) server.getAttribute(objectname, 
attr.getName());
-                    } catch (InstanceNotFoundException | 
AttributeNotFoundException | ReflectionException
-                            | MBeanException e) {
-                        LOG.warn("error when retrieving value for mbean 
{}/attribute {}",objectname, attr.getName(),e);
-                        return -1;
-                    }
-                };
+                LOG.info("Registering metric {} from MBean (objectname={}, 
name={}, type={})", 
+                        metricName,objectname.toString(), attr.getName(), 
attr.getType());
                 metrics.gauge(metricName, supplier);
             }
         }
     }
     
     
+    private <T> Supplier<T> getSupplier ( ObjectName name, String 
attributeName, T defaultValue ) {
+        
+        Supplier<T> supplier = () -> {
+            try {
+                return (T) server.getAttribute(name, attributeName);
+            } catch (InstanceNotFoundException | AttributeNotFoundException | 
ReflectionException
+                    | MBeanException e) {
+                LOG.warn("error when retrieving value for mbean {}/attribute 
{}",name, attributeName,e);
+                return defaultValue;
+            }
+            
+        };
+        return supplier;
+    }
+    
     
     protected String toMetricName(ObjectName objectName, String attributeName) 
{
         String name = "sling"; // default domain
diff --git 
a/jmx-exporter/src/test/java/org/apache/sling/whiteboard/jmxexporter/impl/JmxExporterFactoryTest.java
 
b/jmx-exporter/src/test/java/org/apache/sling/whiteboard/jmxexporter/impl/JmxExporterFactoryTest.java
index 841db99b..6d3ab80e 100644
--- 
a/jmx-exporter/src/test/java/org/apache/sling/whiteboard/jmxexporter/impl/JmxExporterFactoryTest.java
+++ 
b/jmx-exporter/src/test/java/org/apache/sling/whiteboard/jmxexporter/impl/JmxExporterFactoryTest.java
@@ -22,6 +22,8 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
 import java.lang.management.ManagementFactory;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.function.Supplier;
 
 import javax.management.InstanceAlreadyExistsException;
@@ -53,21 +55,36 @@ public class JmxExporterFactoryTest {
     @Captor
     ArgumentCaptor<Supplier<Integer>> intSupplierCaptor;
     
+    @Captor
+    ArgumentCaptor<Supplier<Long>> longSupplierCaptor;
+    
     JmxExporterImplFactory exporter;
     
-    private static final String OBJECT_NAME = 
"org.apache.sling.whiteboard.jmxexporter.impl:type=sample";
+    private static final String OBJECT_NAME_0 = 
"org.apache.sling.whiteboard.jmxexporter.impl0:type=sample1";
+    private static final String OBJECT_NAME_1 = 
"org.apache.sling.whiteboard.jmxexporter.impl0.impl2:type=sample2";
+    private static final String OBJECT_NAME_2 = 
"org.apache.sling.whiteboard.jmxexporter.impl1:type=sample3";
+    
+    // Query which will only match OBJECT_NAME_0 and OBJECT_NAME_1
+    private static final String OBJECT_NAME_QUERY = 
"org.apache.sling.whiteboard.jmxexporter.impl0*:type=*";
     
-    private static final String EXPECTED_INT_NAME = 
"org.apache.sling.whiteboard.jmxexporter.impl.sample.Int";
+    private static final String EXPECTED_0_INT_NAME  = 
"org.apache.sling.whiteboard.jmxexporter.impl0.sample1.Int";
+    private static final String EXPECTED_0_LONG_NAME = 
"org.apache.sling.whiteboard.jmxexporter.impl0.sample1.Long";
+    
+    private static final String EXPECTED_1_INT_NAME  = 
"org.apache.sling.whiteboard.jmxexporter.impl0.impl2.sample2.Int";
+    private static final String EXPECTED_1_LONG_NAME = 
"org.apache.sling.whiteboard.jmxexporter.impl0.impl2.sample2.Long";
     
     MetricsService metrics;
     
+    SimpleBean mbeans[] = { new SimpleBean(0,0L), new SimpleBean(1,1L), new 
SimpleBean(2,2L)};
+    
     
     @Before
     public void setup() throws MalformedObjectNameException, 
InstanceAlreadyExistsException, MBeanRegistrationException, 
NotCompliantMBeanException {
         MBeanServer server = ManagementFactory.getPlatformMBeanServer();
-        ObjectName mbeanName = new ObjectName(OBJECT_NAME);
-        SimpleBean mbean = new SimpleBean();
-        server.registerMBean(mbean, mbeanName);
+
+        server.registerMBean(mbeans[0],new ObjectName(OBJECT_NAME_0));        
+        server.registerMBean(mbeans[1],new ObjectName(OBJECT_NAME_1));
+        server.registerMBean(mbeans[2],new ObjectName(OBJECT_NAME_2));
         
         exporter = new JmxExporterImplFactory(); 
         metrics = Mockito.mock(MetricsService.class);
@@ -76,22 +93,53 @@ public class JmxExporterFactoryTest {
     
     @Test
     public void test() {
-        context.registerInjectActivateService(exporter, 
"objectname",OBJECT_NAME);
-        Mockito.verify(metrics).gauge(Mockito.eq(EXPECTED_INT_NAME), 
intSupplierCaptor.capture());
+        Map<String,Object> props = new HashMap<>();
+        props.put("objectnames", new String[]{OBJECT_NAME_QUERY});
+        context.registerInjectActivateService(exporter, props);
+        
+        
+        // Integer
+        Mockito.verify(metrics).gauge(Mockito.eq(EXPECTED_0_INT_NAME), 
intSupplierCaptor.capture());
+        assertEquals(new Integer(0),intSupplierCaptor.getValue().get());
+        mbeans[0].setInt(10);
+        Mockito.verify(metrics).gauge(Mockito.eq(EXPECTED_0_INT_NAME), 
intSupplierCaptor.capture());
+        assertEquals(new Integer(10),intSupplierCaptor.getValue().get());
+        
+        // Long
+        Mockito.verify(metrics).gauge(Mockito.eq(EXPECTED_0_LONG_NAME), 
longSupplierCaptor.capture());
+        assertEquals(new Long(0L),longSupplierCaptor.getValue().get());
+        
+        // MBean 1
+        Mockito.verify(metrics).gauge(Mockito.eq(EXPECTED_1_INT_NAME), 
intSupplierCaptor.capture());
         assertEquals(new Integer(1),intSupplierCaptor.getValue().get());
         
+        Mockito.verify(metrics).gauge(Mockito.eq(EXPECTED_1_LONG_NAME), 
longSupplierCaptor.capture());
+        assertEquals(new Long(1L),longSupplierCaptor.getValue().get());
+        
+        
     }
     
     static class SimpleBean implements SimpleBeanMBean {
 
         
-        int internalInt = 1;
+        int internalInt = 0;
+        long internalLong = 0L;
+        
+        public SimpleBean(int i, long l) {
+            internalInt = i;
+            internalLong = l;
+        }
         
         @Override
         public int getInt() {
             return internalInt;
         }
         
+        @Override
+        public long getLong() {
+            return internalLong;
+        }
+        
         public void setInt(int value) {
             internalInt = value;
         }
@@ -103,6 +151,7 @@ public class JmxExporterFactoryTest {
     static public interface SimpleBeanMBean {
         
         public int getInt();
+        public long getLong();
         
     }
     
diff --git a/jmx-exporter/src/test/resources/logback-test.xml 
b/jmx-exporter/src/test/resources/logback-test.xml
new file mode 100644
index 00000000..c0f6ce11
--- /dev/null
+++ b/jmx-exporter/src/test/resources/logback-test.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  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.
+-->
+<configuration>
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - 
%msg%n</pattern>
+    </encoder>
+  </appender>
+
+  <root level="info">
+    <appender-ref ref="STDOUT" />
+  </root>
+</configuration>
\ No newline at end of file

Reply via email to