From 96efee85d8e6c33fab06fd90b9462e0c80fc14e9 Mon Sep 17 00:00:00 2001 From: Bela Ban Date: Wed, 17 Apr 2024 15:07:00 +0200 Subject: [PATCH] - ResourceDMBean now uses MethodHandles for faster access to attrs (https://issues.redhat.com/browse/JGRP-2787) --- src/org/jgroups/jmx/ResourceDMBean.java | 54 ++++++++--- src/org/jgroups/protocols/Fragmentation.java | 3 +- src/org/jgroups/util/ExtractMetrics.java | 94 ++++++++++++++++++++ 3 files changed, 135 insertions(+), 16 deletions(-) create mode 100644 src/org/jgroups/util/ExtractMetrics.java diff --git a/src/org/jgroups/jmx/ResourceDMBean.java b/src/org/jgroups/jmx/ResourceDMBean.java index da7fbf9a6c6..977ddaa2012 100755 --- a/src/org/jgroups/jmx/ResourceDMBean.java +++ b/src/org/jgroups/jmx/ResourceDMBean.java @@ -10,6 +10,8 @@ import org.jgroups.util.Util; import javax.management.*; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -91,11 +93,18 @@ public ResourceDMBean(Object instance) { } - public MBeanInfo getMBeanInfo() { return new MBeanInfo(obj.getClass().getCanonicalName(), "DynamicMBean", attrInfo, null, opInfo, null); } + public void forAllAttributes(BiConsumer c) { + if(c == null) + return; + for(Map.Entry e: atts.entrySet()) { + c.accept(e.getKey(), e.getValue()); + } + } + public Object getAttribute(String name) { if(name == null || name.isEmpty()) throw new NullPointerException("Invalid attribute requested " + name); @@ -521,10 +530,7 @@ protected boolean setNamedAttribute(Attribute attribute) { - - - - protected static class AttributeEntry { + public static class AttributeEntry { /** The name of the field or method. Can be different from the key in atts when name in @Property or * @ManagedAttribute was used */ @@ -533,21 +539,23 @@ protected static class AttributeEntry { protected Accessor getter; protected Accessor setter; - protected AttributeEntry(String name, MBeanAttributeInfo info) { + public AttributeEntry(String name, MBeanAttributeInfo info) { this(name, info, null, null); } - protected AttributeEntry(String name, MBeanAttributeInfo info, Accessor getter, Accessor setter) { + public AttributeEntry(String name, MBeanAttributeInfo info, Accessor getter, Accessor setter) { this.name=name; this.info=info; this.getter=getter; this.setter=setter; } - protected Accessor getter() {return getter;} - protected AttributeEntry getter(Accessor new_getter) {this.getter=new_getter; return this;} - protected Accessor setter() {return setter;} - protected AttributeEntry setter(Accessor new_setter) {this.setter=new_setter; return this;} + public String name() {return name;} + public MBeanAttributeInfo info() {return info;} + public Accessor getter() {return getter;} + public AttributeEntry getter(Accessor new_getter) {this.getter=new_getter; return this;} + public Accessor setter() {return setter;} + public AttributeEntry setter(Accessor new_setter) {this.setter=new_setter; return this;} public String toString() { @@ -588,21 +596,39 @@ public Object invoke(Object new_val) throws Exception { } public static class FieldAccessor implements Accessor { - protected final Field field; - protected final Object target; + protected final Field field; + protected final Object target; + protected static final MethodHandles.Lookup LOOKUP=MethodHandles.lookup(); + protected MethodHandle mh; public FieldAccessor(Field field, Object target) { this.field=field; this.target=target; if(!field.isAccessible()) field.setAccessible(true); + try { + mh=LOOKUP.unreflectGetter(field); + } + catch(IllegalAccessException e) { + // todo: log warning? + // throw new RuntimeException(e); + } } public Field getField() {return field;} public Object invoke(Object new_val) throws Exception { - if(new_val == null) + if(new_val == null) { + if(mh != null) { + try { + return mh.invoke(target); + } + catch(Throwable e) { + ; + } + } return field.get(target); + } else { field.set(target, new_val); return null; diff --git a/src/org/jgroups/protocols/Fragmentation.java b/src/org/jgroups/protocols/Fragmentation.java index 69c50c9c37f..df43f4740a2 100644 --- a/src/org/jgroups/protocols/Fragmentation.java +++ b/src/org/jgroups/protocols/Fragmentation.java @@ -18,8 +18,7 @@ public class Fragmentation extends Protocol { @Property(description="The max number of bytes in a message. Larger messages will be fragmented", type=AttributeType.BYTES) - protected int frag_size=60000; - + protected int frag_size=60000; protected LongAdder num_frags_sent=new LongAdder(); protected LongAdder num_frags_received=new LongAdder(); diff --git a/src/org/jgroups/util/ExtractMetrics.java b/src/org/jgroups/util/ExtractMetrics.java new file mode 100644 index 00000000000..2cebf320639 --- /dev/null +++ b/src/org/jgroups/util/ExtractMetrics.java @@ -0,0 +1,94 @@ +package org.jgroups.util; + +import org.jgroups.JChannel; +import org.jgroups.jmx.ResourceDMBean; +import org.jgroups.stack.Protocol; + +import javax.management.MBeanAttributeInfo; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +/** + * Extracts all attributes and methods annotated with {@link org.jgroups.annotations.ManagedAttribute} and returns them + * as a map of names associated with [getter-method/description tuples]. E.g. for an attribute called foo, a method + * foo() or getFoo() is searched for. + * @author Bela Ban + * @since 5.4, 5.3.6 + */ +public class ExtractMetrics { + protected JChannel ch; + + public static class Entry { + protected final String description; + protected final Supplier method; + + protected Entry(String description, Supplier method) { + this.description=description; + this.method=method; + } + + public String getDescription() { + return description; + } + + public Supplier getMethod() { + return method; + } + + @Override + public String toString() { + return String.format(" %s [%s]", method.get(), description); + } + } + + public static Map> extract(JChannel ch) { + Map> map=new HashMap<>(); + for(Protocol p: ch.stack().getProtocols()) { + map.put(p.getName(), extract(p)); + } + return map; + } + + + public static Map extract(Protocol p) { + Map map=new HashMap<>(); + + ResourceDMBean dm=new ResourceDMBean(p); + dm.forAllAttributes((k,v) -> { + MBeanAttributeInfo info=v.info(); + String descr=info != null? info.getDescription() : "n/a"; + Supplier getter=() -> { + try { + return v.getter() != null? v.getter().invoke(null) : null; + } + catch(Exception e) { + System.err.printf("failed getting value for %s\n", k); + return null; + } + }; + map.put(k, new Entry(descr, getter)); + }); + + return map; + } + + protected void start() throws Exception { + ch=new JChannel().connect("bla").name("X"); + Map> map=extract(ch); + for(Map.Entry> e: map.entrySet()) { + System.out.printf("\n%s:\n---------------\n", e.getKey()); + for(Map.Entry e2: e.getValue().entrySet()) { + System.out.printf(" %s: %s\n", e2.getKey(), e2.getValue()); + } + } + Util.close(ch); + } + + public static void main(String[] args) throws Throwable { + new ExtractMetrics().start(); + + } +} + +