package org.commons.jconfig.internal; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.AttributeNotFoundException; import javax.management.DynamicMBean; import javax.management.IntrospectionException; import javax.management.InvalidAttributeValueException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.ReflectionException; import javax.management.RuntimeErrorException; import org.commons.jconfig.annotations.ConfigLoaderAdapter; import org.commons.jconfig.annotations.ConfigResource; import org.commons.jconfig.annotations.ConfigResourceId; import org.commons.jconfig.config.ConfigManager; /** * For reference use * http://blogs.oracle.com/jmxetc/entry/dynamicmbeans,_modelmbeans,_and_pojos... * * @author lafa * */ public class ConfigMBean implements DynamicMBean { // Utilitary tuple private class Tuple { public Tuple(final Object object, final Method m) { instance = object; method = m; } Object instance; Method method; }; private final Map<String, Tuple> getters; private final Map<String, Tuple> setters; private final Set<Tuple> operations; private final Class<?> configClazz; private final MBeanInfo info; private final ConfigManager manager; /** Encapsulation to hold {@link ConfigLoaderAdapter} value and insert it to MBean attribute */ private final LoaderAdapter adapter; /** Encapsulation to holder {@link ConfigResource} value and insert it to MBean attribute */ private final ConfigResourceAttr configResource; /** * Creates a new instance of ConfigObjectMBean * * @param obj */ public ConfigMBean(@Nonnull final ConfigManager configManager, @Nonnull final Class<?> configClazz) { manager = configManager; getters = new LinkedHashMap<String,Tuple>(); setters = new LinkedHashMap<String,Tuple>(); operations = new LinkedHashSet<Tuple>(); this.configClazz = configClazz; adapter = new LoaderAdapter(configClazz); configResource = new ConfigResourceAttr(configClazz); try { info = initialize(); } catch (IntrospectionException ex) { throw new IllegalArgumentException(configClazz.getName(),ex); } } private MBeanInfo initialize() throws IntrospectionException { final List<MBeanAttributeInfo> attributesInfo = new ArrayList<MBeanAttributeInfo>(); final List<MBeanOperationInfo> operationsInfo = new ArrayList<MBeanOperationInfo>(); final Set<String> attributesName = new HashSet<String>(); final ArrayList<Tuple> ops = new ArrayList<Tuple>(); for (Method m:configClazz.getMethods()) { if (m.getDeclaringClass().equals(Object.class)) continue; if (m.getName().startsWith("get") && !m.getName().equals("get") && !m.getName().equals("getClass") && m.getParameterTypes().length == 0 && m.getReturnType() != void.class) { if (m.isAnnotationPresent(ConfigResourceId.class)) { getters.put(m.getAnnotation(ConfigResourceId.class).value(), new Tuple(null,m)); } else { getters.put(m.getName().substring(3), new Tuple(null,m)); } } else if (m.getName().startsWith("is") && !m.getName().equals("is") && m.getParameterTypes().length == 0 && m.getReturnType() == boolean.class) { getters.put(m.getName().substring(2),new Tuple(null,m)); } else if (m.getName().startsWith("set") && !m.getName().equals("set") && m.getParameterTypes().length == 1 && m.getReturnType().equals(void.class)) { if (m.isAnnotationPresent(ConfigResourceId.class)) { setters.put(m.getAnnotation(ConfigResourceId.class).value(), new Tuple(null,m)); } else { setters.put(m.getName().substring(3), new Tuple(null,m)); } } else { ops.add(new Tuple(null,m)); } } attributesName.addAll(getters.keySet()); attributesName.addAll(setters.keySet()); // Register for ConfigLoaderAdapter getters.put("ConfigLoaderAdapter", new Tuple(adapter, adapter.getGetMethod())); getters.put("ConfigResource", new Tuple(configResource, configResource.getGetMethod())); MBeanAttributeInfo adapterAttr = new MBeanAttributeInfo("ConfigLoaderAdapter", "ConfigLoaderAdapter", adapter.getGetMethod(), adapter.getSetMethod()); MBeanAttributeInfo configResourceAttr = new MBeanAttributeInfo("ConfigResource", "ConfigResource", configResource.getGetMethod(), configResource.getSetMethod()); attributesInfo.add(adapterAttr); attributesInfo.add(configResourceAttr); for (String attrName : attributesName) { final Tuple get = getters.get(attrName); Tuple set = setters.get(attrName); if (get != null && set != null && get.method.getReturnType() != set.method.getParameterTypes()[0]) { set = null; ops.add(setters.remove(attrName)); } final MBeanAttributeInfo mbi = new MBeanAttributeInfo(attrName,attrName, get == null ? null:get.method, set == null ? null:set.method); if (mbi != null) attributesInfo.add(mbi); } for (Tuple t:ops) { if(t.method.getDeclaringClass()!= Object.class) { operations.add(t); operationsInfo.add(new MBeanOperationInfo(t.method.getName(),t.method)); } } final MBeanAttributeInfo[] attrsOI = attributesInfo.toArray(new MBeanAttributeInfo[attributesInfo.size()]); final MBeanOperationInfo[] opsOI = operationsInfo.toArray(new MBeanOperationInfo[operationsInfo.size()]); return new MBeanInfo(configClazz.getName(), configClazz.getName(),attrsOI,null,opsOI,null); } @Override public Object getAttribute(final String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException { final Tuple get = getters.get(attribute); if (get == null) throw new AttributeNotFoundException("Fail to find method: " + configClazz.getName() + ".get" + attribute); try { return get.method.invoke(get.instance); } catch (IllegalArgumentException ex) { throw new ReflectionException(ex); } catch (InvocationTargetException ex) { final Throwable cause = ex.getCause(); if (cause instanceof Exception) throw new MBeanException((Exception) cause); throw new RuntimeErrorException((Error) cause); } catch (IllegalAccessException ex) { throw new ReflectionException(ex); } } @Override public AttributeList getAttributes(final String[] attributes) { if (attributes == null) return new AttributeList(); final List<Attribute> result = new ArrayList<Attribute>(attributes.length); for (String attr : attributes) { try { result.add(new Attribute(attr, getAttribute(attr))); } catch (Exception x) { continue; } } return new AttributeList(result); } @Override public void setAttribute(final Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { final Tuple set = setters.get(attribute.getName()); if (set == null) throw new AttributeNotFoundException(attribute.getName()); ConfigManagerCache adapter = manager.getCache(); if (attribute.getValue() instanceof String) { adapter.insertValue(configClazz.getName(), (String) attribute.getValue()); } else { throw new IllegalArgumentException("Attribute " + configClazz.getName() + ".set" + attribute.getName() + " has value in different datatype " + attribute.getValue()); } } @Override public AttributeList setAttributes(final AttributeList attributes) { if (attributes == null) return new AttributeList(); final List<Attribute> result = new ArrayList<Attribute>(attributes.size()); for (Object item : attributes) { final Attribute attribute = (Attribute)item; final String name = attribute.getName(); try { setAttribute(attribute); result.add(new Attribute(name, getAttribute(name))); } catch (Exception x) { continue; } } return new AttributeList(result); } @Override public Object invoke(final String actionName, Object[] params, String[] signature) throws MBeanException, ReflectionException { Tuple toInvoke = null; if (params == null) params = new Object[0]; if (signature == null) signature = new String[0]; for (Tuple t : operations) { if (!t.method.getName().equals(actionName)) continue; final Class<?>[] sig = t.method.getParameterTypes(); if (sig.length == params.length) { if (sig.length == 0) toInvoke=t; else if (signature.length == sig.length) { toInvoke = t; for (int i=0;i<sig.length;i++) { if (!sig[i].getName().equals(signature[i])) { toInvoke = null; break; } } } } if (toInvoke != null) break; } if (toInvoke == null) throw new ReflectionException(new NoSuchMethodException(actionName)); try { return toInvoke.method.invoke(toInvoke.instance,params); } catch (IllegalArgumentException ex) { throw new ReflectionException(ex); } catch (InvocationTargetException ex) { final Throwable cause = ex.getCause(); if (cause instanceof Exception) throw new MBeanException((Exception)cause); throw new RuntimeErrorException((Error)cause); } catch (IllegalAccessException ex) { throw new ReflectionException(ex); } } @Override public MBeanInfo getMBeanInfo() { return info; } }