/* * JBoss, Home of Professional Open Source * Copyright 2009, Red Hat Middleware LLC, and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.profileservice.management; import java.io.Serializable; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.management.Descriptor; import javax.management.DescriptorAccess; import javax.management.MBeanAttributeInfo; import javax.management.MBeanFeatureInfo; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; import javax.management.ObjectName; import org.jboss.logging.Logger; import org.jboss.managed.api.Fields; import org.jboss.managed.api.ManagedObject; import org.jboss.managed.api.ManagedOperation; import org.jboss.managed.api.ManagedParameter; import org.jboss.managed.api.ManagedProperty; import org.jboss.managed.api.ManagedOperation.Impact; import org.jboss.managed.api.annotation.ActivationPolicy; import org.jboss.managed.api.annotation.DefaultValueBuilderFactory; import org.jboss.managed.api.annotation.FieldsFactory; import org.jboss.managed.api.annotation.ManagementConstants; import org.jboss.managed.api.annotation.ManagementObjectID; import org.jboss.managed.api.annotation.ManagementObjectRef; import org.jboss.managed.api.annotation.ManagementOperation; import org.jboss.managed.api.annotation.ManagementProperty; import org.jboss.managed.api.annotation.ManagementPropertyFactory; import org.jboss.managed.api.annotation.ManagementRuntimeRef; import org.jboss.managed.api.annotation.Masked; import org.jboss.managed.api.annotation.RunStateProperty; import org.jboss.managed.api.annotation.ViewUse; import org.jboss.managed.plugins.DefaultFieldsImpl; import org.jboss.managed.plugins.ManagedObjectImpl; import org.jboss.managed.plugins.ManagedOperationImpl; import org.jboss.managed.plugins.ManagedParameterImpl; import org.jboss.managed.plugins.ManagedPropertyImpl; import org.jboss.managed.plugins.factory.AbstractManagedObjectFactory; import org.jboss.metadata.spi.MetaData; import org.jboss.metatype.api.annotations.MetaMapping; import org.jboss.metatype.api.annotations.MetaMappingFactory; import org.jboss.metatype.api.types.ArrayMetaType; import org.jboss.metatype.api.types.CollectionMetaType; import org.jboss.metatype.api.types.MetaType; import org.jboss.metatype.api.types.MetaTypeFactory; import org.jboss.metatype.api.values.MetaValue; import org.jboss.metatype.spi.values.DefaultValueBuilder; import org.jboss.metatype.spi.values.MetaMapper; import org.jboss.metatype.spi.values.MetaMapperFactory; /** * A type of ManagedObject factory that generates a ManagedObject from an MBean * MBeanInfo. * * @author Scott.Stark@jboss.org * @version $Revision:$ */ public class MBeanManagedObjectFactory { private static Logger log = Logger.getLogger(MBeanManagedObjectFactory.class); /** The meta type factory */ private MetaTypeFactory metaTypeFactory = MetaTypeFactory.getInstance(); public MetaTypeFactory getMetaTypeFactory() { return metaTypeFactory; } public void setMetaTypeFactory(MetaTypeFactory metaTypeFactory) { this.metaTypeFactory = metaTypeFactory; } /** * Builds a ManagedObject from the MBeanInfo. * TODO: none of the org.jboss.managed.api.annotation.* annotations are * taken from the MBeanInfo. The descriptor feature could be used for this. * * @param mbean * @param info * @param mbeanLoader * @param metaData * @return * @throws Exception */ public ManagedObject getManagedObject(ObjectName mbean, MBeanInfo info, ClassLoader mbeanLoader, MetaData metaData) throws Exception { return getManagedObject(mbean, info, mbeanLoader, metaData, null, null); } // FIXME - Hack until metadata mechanism is provided public ManagedObject getManagedObject(ObjectName mbean, MBeanInfo info, ClassLoader mbeanLoader, MetaData metaData, ViewUse[] defaultViewUse, Map<String, String> propertyMetaMappings) throws Exception { boolean trace = log.isTraceEnabled(); // Process the ManagementObject fields boolean isRuntime = false; String name = mbean.getCanonicalName(); String nameType = null; String attachmentName = null; Class<? extends Fields> moFieldsFactory = null; Class<? extends ManagedProperty> moPropertyFactory = null; // Build the ManagedProperties Set<ManagedProperty> properties = new HashSet<ManagedProperty>(); MBeanAttributeInfo[] attributes = info.getAttributes(); for(MBeanAttributeInfo propertyInfo : attributes) { ManagementProperty managementProperty = getAnnotation(ManagementProperty.class, propertyInfo, metaData); ManagementObjectID id = getAnnotation(ManagementObjectID.class, propertyInfo, metaData); ManagementObjectRef ref = getAnnotation(ManagementObjectRef.class, propertyInfo, metaData); ManagementRuntimeRef runtimeRef = getAnnotation(ManagementRuntimeRef.class, propertyInfo, metaData); RunStateProperty rsp = getAnnotation(RunStateProperty.class, propertyInfo, metaData); Masked masked = getAnnotation(Masked.class, propertyInfo, metaData); DefaultValueBuilderFactory defaultsFactory = getAnnotation(DefaultValueBuilderFactory.class, propertyInfo, metaData); HashMap<String, Annotation> propAnnotations = new HashMap<String, Annotation>(); if (managementProperty != null) propAnnotations.put(ManagementProperty.class.getName(), managementProperty); if (id != null) { propAnnotations.put(ManagementObjectID.class.getName(), id); // This overrides the MO nameType nameType = id.type(); } if (ref != null) propAnnotations.put(ManagementObjectRef.class.getName(), ref); if (runtimeRef != null) propAnnotations.put(ManagementRuntimeRef.class.getName(), runtimeRef); if (rsp != null) propAnnotations.put(RunStateProperty.class.getName(), rsp); if (masked != null) propAnnotations.put(Masked.class.getName(), masked); // Check whether this property should be included boolean includeProperty = propertyInfo.isReadable() | propertyInfo.isWritable(); if (includeProperty) { Fields fields = null; Class<? extends Fields> factory = moFieldsFactory; FieldsFactory ff = getAnnotation(FieldsFactory.class, propertyInfo, metaData); if(ff != null) factory = ff.value(); if (factory != null) { try { fields = factory.newInstance(); } catch (Exception e) { log.debug("Failed to created Fields", e); } } if (fields == null) fields = new DefaultFieldsImpl(); if( propertyInfo instanceof Serializable ) { Serializable pinfo = Serializable.class.cast(propertyInfo); fields.setField(Fields.PROPERTY_INFO, pinfo); } String propertyName = propertyInfo.getName(); if (managementProperty != null) propertyName = managementProperty.name(); if( propertyName.length() == 0 ) propertyName = propertyInfo.getName(); fields.setField(Fields.NAME, propertyName); // This should probably always the the propertyInfo name? String mappedName = propertyInfo.getName(); if (managementProperty != null) mappedName = managementProperty.mappedName(); if( mappedName.length() == 0 ) mappedName = propertyInfo.getName(); fields.setField(Fields.MAPPED_NAME, mappedName); String description = ManagementConstants.GENERATED; if (managementProperty != null) description = managementProperty.description(); if (description.equals(ManagementConstants.GENERATED)) description = propertyName; fields.setField(Fields.DESCRIPTION, description); if (trace) { log.trace("Building MangedProperty(name="+propertyName +",mappedName="+mappedName +") ,annotations="+propAnnotations); } boolean mandatory = false; if (managementProperty != null) mandatory = managementProperty.mandatory(); if (mandatory) fields.setField(Fields.MANDATORY, Boolean.TRUE); boolean readOnly = propertyInfo.isWritable() == false; if (readOnly == false && managementProperty != null) readOnly = managementProperty.readOnly(); if (readOnly) fields.setField(Fields.READ_ONLY, Boolean.TRUE); boolean managed = false; if (managementProperty != null) managed = managementProperty.managed(); // View Use if (managementProperty != null) { ViewUse[] use = managementProperty.use(); fields.setField(Fields.VIEW_USE, use); } else if (defaultViewUse != null) { fields.setField(Fields.VIEW_USE, defaultViewUse); } // ActivationPolicy ActivationPolicy apolicy = ActivationPolicy.IMMEDIATE; if (managementProperty != null) { apolicy = managementProperty.activationPolicy(); } fields.setField(Fields.ACTIVATION_POLICY, apolicy); // The managed property type MetaMapper[] mapperReturn = {null}; String propertyType = propertyInfo.getType(); MetaType metaType = null; Class<?> type = null; try { type = loadTypeClass(propertyType, mbeanLoader); metaType = this.getMetaType(propertyInfo, type, metaData, false, propertyMetaMappings, mapperReturn); } catch(Exception e) { log.debug("Failed to create ManagedProperty on failure to load type:"+propertyType+", for property: "+propertyInfo.getName()); continue; } // Determine meta type based on property type if(metaType == null) { if (managed) { if(type.isArray()) metaType = new ArrayMetaType(1, AbstractManagedObjectFactory.MANAGED_OBJECT_META_TYPE); else if (Collection.class.isAssignableFrom(type)) metaType = new CollectionMetaType(type.getName(), AbstractManagedObjectFactory.MANAGED_OBJECT_META_TYPE); else metaType = AbstractManagedObjectFactory.MANAGED_OBJECT_META_TYPE; } else { metaType = metaTypeFactory.resolve(type); } } fields.setField(Fields.META_TYPE, metaType); // Default value if(managementProperty != null) { String defaultValue = managementProperty.defaultValue(); if(defaultValue.length() > 0) { try { // Check for a DefaultValueBuilderFactory DefaultValueBuilder builder = null; if(defaultsFactory != null) { Class<? extends DefaultValueBuilder> factoryClass = defaultsFactory.value(); builder = factoryClass.newInstance(); } if(builder != null) { MetaValue defaultMV = builder.buildMetaValue(defaultValue); if(defaultMV != null) fields.setField(Fields.DEFAULT_VALUE, defaultMV); } else { log.warn("Failed to find DefaultValueBuilder for type: "+metaType); } } catch(Exception e) { log.warn("Failed to create default value for: "+propertyInfo, e); } } } // Property annotations if (propAnnotations.isEmpty() == false) fields.setField(Fields.ANNOTATIONS, propAnnotations); ManagedProperty property = null; Class<? extends ManagedProperty> mpClass = moPropertyFactory; ManagementPropertyFactory mpf = getAnnotation(ManagementPropertyFactory.class, propertyInfo, metaData); if (mpf != null) mpClass = mpf.value(); if (mpClass != null) property = AbstractManagedObjectFactory.createManagedProperty(mpClass, fields); if (property == null) property = new ManagedPropertyImpl(fields); // Pass the MetaMapper as an attachment if (mapperReturn[0] != null) property.setTransientAttachment(MetaMapper.class.getName(), mapperReturn[0]); properties.add(property); } else if (trace) log.trace("Ignoring property: " + propertyInfo); } /* TODO: Operations. In general the bean metadata does not contain operation information. */ Set<ManagedOperation> operations = new HashSet<ManagedOperation>(); MBeanOperationInfo[] methodInfos = info.getOperations(); if (methodInfos != null && methodInfos.length > 0) { for (MBeanOperationInfo methodInfo : methodInfos) { ManagementOperation managementOp = getAnnotation(ManagementOperation.class, methodInfo, metaData); try { ManagedOperation op = getManagedOperation(methodInfo, managementOp, mbeanLoader, metaData); operations.add(op); } catch(Exception e) { log.debug("Failed to create ManagedOperation for: "+methodInfo.getName(), e); } } } ManagedObjectImpl result = new ManagedObjectImpl(mbean.getCanonicalName(), properties); // TODO Map<String, Annotation> empty = Collections.emptyMap(); result.setAnnotations(empty); // Set the component name to name if this is a runtime MO with a name specified result.setComponentName(name); if (nameType != null) result.setNameType(nameType); if (attachmentName != null) result.setAttachmentName(attachmentName); if (operations.size() > 0 ) result.setOperations(operations); for (ManagedProperty property : properties) property.setManagedObject(result); result.setTransientAttachment(MBeanInfo.class.getName(), info); // Marker for associating the correct dispatcher result.setTransientAttachment(MBeanRuntimeComponentDispatcher.class.getName(), true); return result; } protected <X extends Annotation> X getAnnotation(Class<X> annotationType, MBeanFeatureInfo info, MetaData metaData) { X annotation = null; if(metaData != null) { annotation = metaData.getAnnotation(annotationType); if(annotation == null && info instanceof DescriptorAccess) { DescriptorAccess daccess = (DescriptorAccess) info; Descriptor descriptor = daccess.getDescriptor(); annotation = getAnnotation(annotationType, descriptor); } } return annotation; } protected <X extends Annotation> X getAnnotation(Class<X> annotationType, Descriptor descriptor) { // TODO... return null; } /** * Get the MetaType for info by looking for MetaMapping/MetaMappingFactory * annotations in addition to the info type. * * @param methodInfo * @param metaData * @return the MetaType for info's type */ protected MetaType getMetaType(MBeanFeatureInfo info, Type infoType, MetaData metaData, boolean useTypeFactory, Map<String, String> propertyMetaMappings, MetaMapper[] mapperReturn) { MetaType returnType = null; // First look for meta mappings MetaMapper<?> metaMapper = null; MetaMapping metaMapping = getAnnotation(MetaMapping.class, info, metaData); MetaMappingFactory metaMappingFactory = getAnnotation(MetaMappingFactory.class, info, metaData); if(metaMappingFactory != null) { Class<? extends MetaMapperFactory<?>> mmfClass = metaMappingFactory.value(); try { MetaMapperFactory<?> mmf = mmfClass.newInstance(); String[] args = metaMappingFactory.args(); if(args.length > 0) metaMapper = mmf.newInstance(args); else metaMapper = mmf.newInstance(); } catch(Exception e) { log.debug("Failed to create MetaMapperFactory: "+metaMappingFactory, e); } } if(metaMapping != null) { // Use the mapping for the type Class<? extends MetaMapper<?>> mapperClass = metaMapping.value(); try { metaMapper = mapperClass.newInstance(); } catch(Exception e) { log.debug("Failed to create MetaMapper: "+metaMapping, e); } } if (info instanceof MBeanAttributeInfo && propertyMetaMappings != null) { String className = propertyMetaMappings.get(info.getName()); if (className != null) { try { // Use the same loader of the profile service metaMapper = (MetaMapper<?>)Class.forName(className).newInstance(); } catch (Exception e) { log.debug("Failed to create MetaMapper: " + className + " for property: " + info.getName()); } } } if(metaMapper != null) { returnType = metaMapper.getMetaType(); // Return the MetaMapper if(mapperReturn != null && mapperReturn.length > 0) mapperReturn[0] = metaMapper; } if(returnType == null && useTypeFactory) { // Use the type factory to convert the info type returnType = metaTypeFactory.resolve(infoType); } return returnType; } protected ManagedOperation getManagedOperation(MBeanOperationInfo methodInfo, ManagementOperation opAnnotation, ClassLoader mbeanLoader, MetaData metaData) throws Exception { String name = methodInfo.getName(); String description = opAnnotation != null ? opAnnotation.description() : name; Impact impact = Impact.Unknown; switch(methodInfo.getImpact()) { case MBeanOperationInfo.ACTION: impact = Impact.WriteOnly; break; case MBeanOperationInfo.ACTION_INFO: impact = Impact.ReadWrite; break; case MBeanOperationInfo.INFO: impact = Impact.ReadOnly; break; case MBeanOperationInfo.UNKNOWN: impact = Impact.Unknown; break; } // The op return type MetaMapper[] returnTypeMapper = {null}; Class<?> returnTypeClass = loadTypeClass(methodInfo.getReturnType(), mbeanLoader); MetaType returnType = getMetaType(methodInfo, returnTypeClass, metaData, true, null, returnTypeMapper); // Process the op parameters ArrayList<ManagedParameter> mparams = new ArrayList<ManagedParameter>(); MBeanParameterInfo[] paramInfo = methodInfo.getSignature(); if( paramInfo != null ) { for(int i = 0; i < paramInfo.length; i ++) { MBeanParameterInfo pinfo = paramInfo[i]; String pname = pinfo.getName(); String pdescription = pinfo.getDescription(); // Generate a name if there is none if (pname == null) pname = "arg#" + i; Fields fields = new DefaultFieldsImpl(pname); if (pdescription != null) fields.setField(Fields.DESCRIPTION, pdescription); MetaMapper[] paramMapper = {null}; Class<?> paramType = loadTypeClass(pinfo.getType(), mbeanLoader); MetaType metaType = getMetaType(pinfo, paramType, metaData, true, null, paramMapper); fields.setField(Fields.META_TYPE, metaType); ManagedParameterImpl mp = new ManagedParameterImpl(fields); if(paramMapper[0] != null) mp.setTransientAttachment(MetaMapper.class.getName(), paramMapper[0]); mparams.add(mp); } } ManagedParameter[] parameters = new ManagedParameter[mparams.size()]; mparams.toArray(parameters); ManagedOperationImpl op = new ManagedOperationImpl(name, description, impact, parameters, returnType); if(returnTypeMapper[0] != null) op.setTransientAttachment(MetaMapper.class.getName(), returnTypeMapper[0]); return op; } protected Class<?> loadTypeClass(String propertyType, ClassLoader loader) throws ClassNotFoundException { Class<?> type = null; // Check for a primitive type if(propertyType.equals("byte")) type = byte.class; else if(propertyType.equals("char")) type = char.class; else if(propertyType.equals("short")) type = short.class; else if(propertyType.equals("int")) type = int.class; else if(propertyType.equals("long")) type = long.class; else if(propertyType.equals("float")) type = float.class; else if(propertyType.equals("double")) type = double.class; else if(propertyType.equals("void")) type = void.class; else if(propertyType.equals("boolean")) type = boolean.class; else { type = loader.loadClass(propertyType); } return type; } }