/* * Licensed 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 com.addthis.basis.jmx; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Observable; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.AttributeNotFoundException; import javax.management.DynamicMBean; import javax.management.InvalidAttributeValueException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanConstructorInfo; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanNotificationInfo; import javax.management.MBeanOperationInfo; import javax.management.ReflectionException; /** * Implements the DynamicMBean interface by using reflection to make all * of its public instance fields available through JMX. He's capable of * registering observers that get notified when his fields are changed * through JMX. */ public class FieldBasedDynamicMBean extends Observable implements DynamicMBean { protected boolean readonly; protected Map<String, Field> fields; protected FieldBasedDynamicMBean() { this(true); } protected FieldBasedDynamicMBean(boolean readonly) { this.readonly = readonly; this.fields = findFields(); } @Override public Object getAttribute(String att) throws AttributeNotFoundException, MBeanException, ReflectionException { Field f = fields.get(att); if (f == null) { throw new AttributeNotFoundException(att); } try { return f.get(this); } catch (Exception e) { throw new ReflectionException(e, "error getting attribute " + att); } } @Override public AttributeList getAttributes(String[] atts) { AttributeList list = new AttributeList(); for (String att : atts) { try { list.add(new Attribute(att, getAttribute(att))); } catch (Exception e) { } } return list; } @Override public MBeanInfo getMBeanInfo() { List<MBeanAttributeInfo> atts = new LinkedList<>(); for (Field field : fields.values()) { MBeanAttributeInfo att = new MBeanAttributeInfo( field.getName(), field.getType().getName(), null, true, !(readonly || Modifier.isFinal(field.getModifiers())), field.getType() == Boolean.TYPE, null); atts.add(att); } return new MBeanInfo( getClass().getName(), null, atts.toArray(new MBeanAttributeInfo[atts.size()]), new MBeanConstructorInfo[0], new MBeanOperationInfo[0], new MBeanNotificationInfo[0]); } @Override public Object invoke(String actionName, Object[] params, String[] signature) throws MBeanException, ReflectionException { throw new UnsupportedOperationException(actionName); } @Override public void setAttribute(Attribute att) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { Field f = fields.get(att.getName()); if (f == null) { throw new AttributeNotFoundException(att.getName()); } if (readonly || Modifier.isFinal(f.getModifiers())) { throw new UnsupportedOperationException(att.getName() + " is read-only"); } try { f.set(this, att.getValue()); notifyObservers(f.getName()); } catch (Exception e) { throw new ReflectionException(e, "error setting attribute " + att); } } @Override public AttributeList setAttributes(AttributeList atts) { if (readonly) { return new AttributeList(); } List<String> names = new LinkedList<>(); for (Object att : atts) { try { setAttribute((Attribute) att); names.add(((Attribute) att).getName()); } catch (Exception e) { } } return getAttributes(names.toArray(new String[names.size()])); } protected Map<String, Field> findFields() { Map<String, Field> map = new HashMap<>(); for (Field f : getClass().getFields()) { int m = f.getModifiers(); if (Modifier.isPublic(m) && !Modifier.isStatic(m)) { map.put(f.getName(), f); } } return map; } }