All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgroups.util.Metrics Maven / Gradle / Ivy

There is a newer version: 5.4.1.Final
Show newest version
package org.jgroups.util;

import org.jgroups.JChannel;
import org.jgroups.annotations.ManagedAttribute;
import org.jgroups.annotations.Property;
import org.jgroups.jmx.ResourceDMBean;
import org.jgroups.stack.Protocol;

import javax.management.MBeanAttributeInfo;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Predicate;
import java.util.function.Supplier;

import static org.jgroups.conf.AttributeType.BYTES;
import static org.jgroups.conf.AttributeType.SCALAR;

/**
 * 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 Metrics {
    protected JChannel ch;
    public static final Predicate IS_NUMBER=obj -> {
        if(obj instanceof Field)
            return isNumberAndScalar(obj, ((Field)obj).getType());
        if(obj instanceof Method) {
            Method m=(Method)obj;
            return isNumberAndScalar(obj, m.getReturnType());
        }
        return false;
    };


    public static class Entry {
        protected final AccessibleObject type; // Field or Method
        protected final String           description;
        protected final Supplier      supplier;

        protected Entry(AccessibleObject type, String description, Supplier method) {
            this.type=type;
            this.description=description;
            this.supplier=method;
        }

        public AccessibleObject type()        {return type;}
        public String           description() {return description;}
        public Supplier      supplier()    {return supplier;}

        @Override
        public String toString() {
            return String.format("  %s [%s]", supplier.get(), description);
        }
    }

    public static Map>> extract(JChannel ch) {
        return extract(ch, null);
    }

    public static Map>> extract(JChannel ch, Predicate filter) {
        Map>> map=new LinkedHashMap<>();
        for(Protocol p: ch.stack().getProtocols())
            map.put(p.getName(), extract(p, filter));
        return map;
    }

    public static Map> extract(Protocol p) {
        return extract(p, null);
    }

    public static Map> extract(Protocol p, Predicate filter) {
        Map> map=new TreeMap<>();
        ResourceDMBean dm=new ResourceDMBean(p, filter);
        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<>(v.type(), descr, getter));
        });

        return map;
    }

    protected void start(boolean numeric) throws Exception {
        ch=new JChannel().connect("bla").name("X");
        Map>> m=extract(ch, numeric? IS_NUMBER : null);
        if(numeric) {
            Map>> map=convert(m);
            print(map);
        }
        else
            print(m);
        Util.close(ch);
    }

    protected static  void print(Map>> map) {
        for(Map.Entry>> e: map.entrySet()) {
            System.out.printf("\n%s:\n---------------\n", e.getKey());
            for(Map.Entry> e2: e.getValue().entrySet()) {
                Entry entry=e2.getValue();
                Supplier s=entry.supplier();
                if(s != null)
                    System.out.printf("  %s: %s\n", e2.getKey(), s.get());
            }
        }
    }

    public static Map>> convert(Map>> m) {
        Map>> retval=new LinkedHashMap<>();
        for(Map.Entry>> entry: m.entrySet()) {
            Map> m1=entry.getValue();
            Map> m2=convertProtocol(m1);
            retval.put(entry.getKey(), m2);
        }
        return retval;
    }

    public static Map> convertProtocol(Map> m) {
        Map> retval=new TreeMap<>();
        for(Map.Entry> e: m.entrySet()) {
            Entry en=e.getValue();
            AccessibleObject type=en.type();
            Class cl=(type instanceof Field)? ((Field)type).getType() : ((Method)type).getReturnType();
            if(Number.class.isAssignableFrom(cl)) {
                Entry tmp=new Entry<>(en.type(), en.description(), () -> (Number)en.supplier().get());
                retval.put(e.getKey(), tmp);
                continue;
            }
            if(int.class.isAssignableFrom(cl)) {
                Entry tmp=new Entry<>(en.type(), en.description(), () -> (Integer)en.supplier().get());
                retval.put(e.getKey(), tmp);
                continue;
            }
            if(long.class.isAssignableFrom(cl)) {
                Entry tmp=new Entry<>(en.type(), en.description(), () -> (Long)en.supplier().get());
                retval.put(e.getKey(), tmp);
                continue;
            }
            if(double.class.isAssignableFrom(cl)) {
                Entry tmp=new Entry<>(en.type(), en.description(), () -> (Double)en.supplier().get());
                retval.put(e.getKey(), tmp);
                continue;
            }
            if(float.class.isAssignableFrom(cl)) {
                Entry tmp=new Entry<>(en.type(), en.description(), () -> (Float)en.supplier().get());
                retval.put(e.getKey(), tmp);
                continue;
            }
            if(AverageMinMax.class.isAssignableFrom(cl)) {
                Entry tmp=new Entry<>(en.type(), en.description(), () -> ((AverageMinMax)en.supplier().get()).min());
                retval.put(e.getKey() + ".min", tmp);
                tmp=new Entry<>(en.type(), en.description(), () -> ((AverageMinMax)en.supplier().get()).average());
                retval.put(e.getKey(), tmp);
                tmp=new Entry<>(en.type(), en.description(), () -> ((AverageMinMax)en.supplier().get()).max());
                retval.put(e.getKey() + ".max", tmp);
                continue;
            }
            if(Average.class.isAssignableFrom(cl)) {
                Entry tmp=new Entry<>(en.type(), en.description(), () -> ((Average)en.supplier()).average());
                retval.put(e.getKey(), tmp);
            }
        }
        return retval;
    }

    protected static boolean isNumberAndScalar(AccessibleObject obj, Class cl) {
        if(cl.equals(float.class) || cl.equals(Float.class) || cl.equals(double.class) || cl.equals(Double.class)
          || Average.class.isAssignableFrom(cl))
            return true;
        boolean is_number=cl.equals(int.class) || cl.equals(Integer.class) || cl.equals(long.class) || cl.equals(Long.class)
          || Number.class.isAssignableFrom(cl);
        return is_number && isScalar(obj);
    }

    protected static boolean isScalar(AccessibleObject obj) {
        ManagedAttribute annotation=obj.getAnnotation(ManagedAttribute.class);
        if(annotation != null && (annotation.type() == SCALAR || annotation.type() == BYTES))
            return true;
        Property prop=obj.getAnnotation(Property.class);
        return prop != null && (prop.type() == SCALAR || prop.type() == BYTES);
    }

    public static void main(String[] args) throws Throwable {
        boolean numeric=args.length > 0 && Boolean.parseBoolean(args[0]);
        new Metrics().start(numeric);

    }
}