package org.jolokia.jmx; /* * Copyright 2009-2013 Roland Huss * * 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. */ import java.lang.management.ManagementFactory; import java.lang.reflect.Field; import java.util.HashSet; import java.util.Set; import javax.management.*; import javax.management.modelmbean.ModelMBean; import javax.management.openmbean.OpenType; import org.jolokia.converter.Converters; import org.jolokia.converter.json.JsonConvertOptions; import org.jolokia.converter.json.ValueFaultHandler; /** * Dedicate MBeanServer for registering Jolokia-only MBeans * * @author roland * @since 11.01.13 */ class JolokiaMBeanServer extends MBeanServerProxy { // MBeanServer to delegate to for JsonMBeans private MBeanServer delegateServer; private Set<ObjectName> delegatedMBeans; private Converters converters; /** * Create a private MBean server */ public JolokiaMBeanServer() { MBeanServer mBeanServer = MBeanServerFactory.newMBeanServer(); delegatedMBeans = new HashSet<ObjectName>(); delegateServer = ManagementFactory.getPlatformMBeanServer(); converters = new Converters(); init(mBeanServer); } @Override public ObjectInstance registerMBean(Object object, ObjectName name) throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException { // Register MBean first on this MBean Server ObjectInstance ret = super.registerMBean(object, name); // Check, whether it is annotated with @JsonMBean. Only the outermost class of an inheritance is // considered. JsonMBean anno = extractJsonMBeanAnnotation(object); if (anno != null) { // The real name can be different than the given one in case the default // domain was omitted and/or the MBean implements MBeanRegistration ObjectName realName = ret.getObjectName(); try { // Fetch real MBeanInfo and create a dynamic MBean with modified signature MBeanInfo info = super.getMBeanInfo(realName); JsonDynamicMBeanImpl mbean = new JsonDynamicMBeanImpl(this,realName,info,getJsonConverterOptions(anno)); // Register MBean on delegate MBeanServer delegatedMBeans.add(realName); delegateServer.registerMBean(mbean,realName); } catch (InstanceNotFoundException e) { throw new MBeanRegistrationException(e,"Cannot obtain MBeanInfo from Jolokia-Server for " + realName); } catch (IntrospectionException e) { throw new MBeanRegistrationException(e,"Cannot obtain MBeanInfo from Jolokia-Server for " + realName); } catch (ReflectionException e) { throw new MBeanRegistrationException(e,"Cannot obtain MBeanInfo from Jolokia-Server for " + realName); } } return ret; } // Lookup a JsonMBean annotation private JsonMBean extractJsonMBeanAnnotation(Object object) { // Try directly Class<?> clazz = object.getClass(); JsonMBean anno = clazz.getAnnotation(JsonMBean.class); if (anno == null && ModelMBean.class.isAssignableFrom(object.getClass())) { // For ModelMBean we try some heuristic to get to the managed resource // This works for all subclasses of RequiredModelMBean as provided by the JDK // but maybe for other ModelMBean classes as well Boolean isAccessible = null; Field field = null; try { field = findField(clazz, "managedResource"); if (field != null) { isAccessible = field.isAccessible(); field.setAccessible(true); Object managedResource = field.get(object); anno = managedResource.getClass().getAnnotation(JsonMBean.class); } } catch (IllegalAccessException e) { // Ignored silently, but we tried it at least } finally { if (isAccessible != null) { field.setAccessible(isAccessible); } } } return anno; } // Find a field in an inheritance hierarchy private Field findField(Class<?> pClazz, String pField) { Class c = pClazz; do { try { return c.getDeclaredField(pField); } catch (NoSuchFieldException e) { c = pClazz.getSuperclass(); } } while (c != null); return null; } @Override public void unregisterMBean(ObjectName name) throws InstanceNotFoundException, MBeanRegistrationException { super.unregisterMBean(name); if (delegatedMBeans.contains(name)) { delegatedMBeans.remove(name); delegateServer.unregisterMBean(name); } } /** * Converter used by JsonMBean for converting from Object to JSON representation * * @param object object to serialize * @param pConvertOptions options used for conversion * @return serialized object */ String toJson(Object object, JsonConvertOptions pConvertOptions) { try { Object ret = converters.getToJsonConverter().convertToJson(object,null,pConvertOptions); return ret.toString(); } catch (AttributeNotFoundException exp) { // Cannot happen, since we dont use a path return ""; } } /** * Convert from a JSON or other string representation to real object. Used when preparing operation * argument. If the JSON structure cannot be converted, an {@link IllegalArgumentException} is thrown. * * @param type type to convert to * @param json string to deserialize * @return the deserialized object */ Object fromJson(String type, String json) { return converters.getToObjectConverter().convertFromString(type,json); } /** * Convert from JSON for OpenType objects. Throws an {@link IllegalArgumentException} if * * @param type open type * @param json JSON representation to convert from * @return the converted object */ Object fromJson(OpenType type, String json) { return converters.getToOpenTypeConverter().convertToObject(type,json); } // Extract convert options from annotation private JsonConvertOptions getJsonConverterOptions(JsonMBean pAnno) { // Extract conversion options from the annotation if (pAnno == null) { return JsonConvertOptions.DEFAULT; } else { ValueFaultHandler faultHandler = pAnno.faultHandling() == JsonMBean.FaultHandler.IGNORE_ERRORS ? ValueFaultHandler.IGNORING_VALUE_FAULT_HANDLER : ValueFaultHandler.THROWING_VALUE_FAULT_HANDLER; return new JsonConvertOptions.Builder() .maxCollectionSize(pAnno.maxCollectionSize()) .maxDepth(pAnno.maxDepth()) .maxObjects(pAnno.maxObjects()) .faultHandler(faultHandler) .build(); } } }