/* * JBoss, Home of Professional Open Source * Copyright 2006, 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.system.deployers.managed; import java.beans.PropertyEditor; import java.io.Serializable; import java.lang.reflect.Type; import java.util.HashMap; import java.util.List; import javax.management.AttributeNotFoundException; import javax.management.InstanceNotFoundException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanServer; import javax.management.ObjectName; import org.jboss.beans.info.spi.BeanInfo; import org.jboss.beans.info.spi.PropertyInfo; import org.jboss.logging.Logger; import org.jboss.managed.api.ManagedProperty; import org.jboss.managed.spi.factory.InstanceClassFactory; import org.jboss.metadata.spi.MetaData; import org.jboss.metatype.api.types.MetaType; import org.jboss.metatype.api.values.MetaValue; import org.jboss.metatype.api.values.MetaValueFactory; import org.jboss.metatype.spi.values.MetaMapper; import org.jboss.system.ServiceConfigurator; import org.jboss.system.ServiceController; import org.jboss.system.metadata.ServiceAnnotationMetaData; import org.jboss.system.metadata.ServiceAttributeMetaData; import org.jboss.system.metadata.ServiceDependencyValueMetaData; import org.jboss.system.metadata.ServiceElementValueMetaData; import org.jboss.system.metadata.ServiceMetaData; import org.jboss.system.metadata.ServiceTextValueMetaData; import org.jboss.system.metadata.ServiceValueContext; import org.jboss.system.metadata.ServiceValueMetaData; import org.jboss.util.propertyeditor.PropertyEditors; import org.w3c.dom.Element; /** * The InstanceClassFactory implementation for ServiceMetaData. * * @author Scott.Stark@jboss.org * @author Dimitris.Andreadis@jboss.org * @author <a href="mailto:emuckenh@redhat.com">Emanuel Muckenhuber</a> * @version $Revision: 88716 $ */ public class ServiceMetaDataICF implements InstanceClassFactory<ServiceMetaData> { private static final Logger log = Logger.getLogger(ServiceMetaDataICF.class); private static final String MOCLASS_ANNOTATION = '@' + ManagementObjectClass.class.getName(); //private static final ThreadLocal<> attributeMap; private MBeanServer mbeanServer; private ServiceController controller; /** The meta value factory */ private MetaValueFactory metaValueFactory = MetaValueFactory.getInstance(); public MBeanServer getMbeanServer() { return mbeanServer; } public void setMbeanServer(MBeanServer mbeanServer) { this.mbeanServer = mbeanServer; } public ServiceController getController() { return controller; } public void setController(ServiceController controller) { this.controller = controller; } public Class<ServiceMetaData> getType() { return ServiceMetaData.class; } public Class<? extends Serializable> getManagedObjectClass(ServiceMetaData md) throws ClassNotFoundException { ClassLoader prevLoader = SecurityActions.getContextClassLoader(); try { ClassLoader loader = getServiceMetaDataCL(md); Class moClass = loader.loadClass(md.getCode()); // Set the mbean class loader as the TCL SecurityActions.setContextClassLoader(loader); // Looks for a ManagementObjectClass annotation that defines // an alternate class to scan for management annotations List<ServiceAnnotationMetaData> samlist = md.getAnnotations(); for (ServiceAnnotationMetaData sam : samlist) { // Annotations are not yet introduced to the actual mbean // so just look for the annotation string String anString = sam.getAnnotation(); if (anString.startsWith(MOCLASS_ANNOTATION)) { Class<?> originalClass = moClass; ManagementObjectClass moc = (ManagementObjectClass)sam.getAnnotationInstance(loader); moClass = moc.code(); log.debug("Using alternate class '" + moClass + "' for class " + originalClass); break; } } return moClass; } catch(InstanceNotFoundException e) { throw new ClassNotFoundException("Failed to obtain mbean class loader", e); } finally { SecurityActions.setContextClassLoader(prevLoader); } } public MetaValue getValue(BeanInfo beanInfo, ManagedProperty property, MetaData metaData, ServiceMetaData md) { // First look to the mapped name String name = property.getMappedName(); if (name == null) name = property.getName(); ClassLoader prevLoader = SecurityActions.getContextClassLoader(); Object value = null; MetaType metaType = property.getMetaType(); MetaValue mvalue = null; ObjectName mbean = md.getObjectName(); String attrName = null; try { ClassLoader loader = getServiceMetaDataCL(md); // Set the mbean class loader as the TCL SecurityActions.setContextClassLoader(loader); // Get the attribute value from the metadata for (ServiceAttributeMetaData amd : md.getAttributes()) { // The compare is case-insensitve due to the attribute/javabean case mismatch if (amd.getName().equalsIgnoreCase(name)) { value = amd.getValue(); attrName = amd.getName(); break; } } // If the value is null, look to mbean for the value if (value == null && getMbeanServer() != null) { try { value = getMbeanServer().getAttribute(mbean, name); } catch (AttributeNotFoundException e) { // Try the alternate name String attribute = name; if(Character.isUpperCase(name.charAt(0))) attribute = Character.toLowerCase(name.charAt(0)) + name.substring(1); else attribute = Character.toUpperCase(name.charAt(0)) + name.substring(1); try { value = getMbeanServer().getAttribute(mbean, attribute); } catch(Exception e2) { log.debug("Failed to get value from mbean for: "+attribute, e2); } } catch(Exception e) { log.debug("Failed to get value from mbean for: "+name, e); } } // Unwrap the ServiceValueMetaData types try { if (value instanceof ServiceTextValueMetaData) { ServiceTextValueMetaData text = (ServiceTextValueMetaData) value; try { // TODO: cache this somehow HashMap<String, MBeanAttributeInfo> attrs = ServiceConfigurator.getAttributeMap(mbeanServer, mbean); MBeanAttributeInfo mbi = attrs.get(attrName); ServiceValueContext svc = new ServiceValueContext(mbeanServer, controller, mbi, loader); value = text.getValue(svc); } catch(Exception e) { // TODO: better way to determine if the bean was installed, as this does not make much sense PropertyEditor editor = PropertyEditors.getEditor(metaType.getTypeName()); editor.setAsText(text.getText()); value = editor.getValue(); } } else if (value instanceof ServiceDependencyValueMetaData) { ServiceDependencyValueMetaData depends = (ServiceDependencyValueMetaData) value; value = depends.getObjectName(); } else if (value instanceof ServiceElementValueMetaData) { value = ((ServiceElementValueMetaData)value).getElement(); } // TODO: unwrap other ServiceValueMetaData types } catch(Exception e) { log.debug("Failed to get value from mbean for: "+name, e); } PropertyInfo propertyInfo = beanInfo.getProperty(name); MetaMapper metaMapper = property.getTransientAttachment(MetaMapper.class); try { if(metaMapper != null) { mvalue = metaMapper.createMetaValue(property.getMetaType(), value); } else { mvalue = metaValueFactory.create(value, propertyInfo.getType()); } } catch(Exception e) { log.debug("Failed to get property value for bean: "+beanInfo.getName() +", property: "+propertyInfo.getName(), e); mvalue = metaValueFactory.create(null, propertyInfo.getType()); return mvalue; } } catch(InstanceNotFoundException e) { throw new IllegalStateException("Failed to obtain mbean class loader", e); } finally { SecurityActions.setContextClassLoader(prevLoader); } return mvalue; } public void setValue(BeanInfo beanInfo, ManagedProperty property, ServiceMetaData md, MetaValue value) { ClassLoader prevLoader = SecurityActions.getContextClassLoader(); try { ClassLoader loader = getServiceMetaDataCL(md); // Set the mbean class loader as the TCL SecurityActions.setContextClassLoader(loader); // First look to the mapped name String name = property.getMappedName(); if (name == null) name = property.getName(); // Get the attribute value ServiceValueMetaData attributeValue = null; for (ServiceAttributeMetaData amd : md.getAttributes()) { // The compare is case-insensitive due to the attribute/javabean case mismatch if (amd.getName().equalsIgnoreCase(name)) { attributeValue = amd.getValue(); break; } } // There may not be an attribute value, see if there is a matching property // Unwrap the value before, so that we can recreate empty values Object plainValue = null; // Look for a MetaMapper MetaType propertyType = property.getMetaType(); MetaMapper metaMapper = property.getTransientAttachment(MetaMapper.class); Type mappedType = null; if(metaMapper != null) { mappedType = metaMapper.mapToType(); plainValue = metaMapper.unwrapMetaValue(value); } else { PropertyInfo propertyInfo = beanInfo.getProperty(name); plainValue = metaValueFactory.unwrap(value, propertyInfo.getType()); } if (attributeValue == null) { String aname = mapAttributeName(md, name); if(aname != null) { ServiceAttributeMetaData attr = new ServiceAttributeMetaData(); attr.setName(aname); // Check if this is mapped to a Element if(mappedType != null && mappedType.equals(Element.class)) { attributeValue = new ServiceElementValueMetaData(); } else if(plainValue != null) { // Create a text value String textValue = String.valueOf(plainValue); // Don't create a empty value if(textValue.trim().length() > 0 ) attributeValue = new ServiceTextValueMetaData(textValue); } // Don't create a null serviceAttribute if(attributeValue == null) return; // Add attr.setValue(attributeValue); md.addAttribute(attr); } } if (attributeValue != null) { // Unwrap the ServiceValueMetaData types if (attributeValue instanceof ServiceTextValueMetaData) { String textValue = plainValue != null ? String.valueOf(plainValue) : null; ServiceTextValueMetaData text = (ServiceTextValueMetaData) attributeValue; text.setText(textValue); } else if (attributeValue instanceof ServiceElementValueMetaData) { if(plainValue != null) ((ServiceElementValueMetaData) attributeValue).setElement((Element) plainValue); } else if (attributeValue instanceof ServiceDependencyValueMetaData) { ServiceDependencyValueMetaData depends = (ServiceDependencyValueMetaData) attributeValue; if (plainValue instanceof ObjectName) depends.setObjectName((ObjectName) plainValue); else depends.setDependency(String.valueOf(plainValue)); } // TODO: unwrap other ServiceValueMetaData types else { throw new IllegalArgumentException("Unhandled attribute value type: " + name + "/" + md+", class="+attributeValue.getClass()); } } else throw new IllegalArgumentException("No matching attribute found: " + name + "/" + md); } catch(InstanceNotFoundException e) { throw new IllegalStateException("Failed to obtain mbean class loader", e); } finally { SecurityActions.setContextClassLoader(prevLoader); } } /** * The service context uses the canonical object name string * @return the service metadata canonical object name string */ public Object getComponentName(BeanInfo beanInfo, ManagedProperty property, ServiceMetaData md, MetaValue value) { ObjectName objectName = md.getObjectName(); String canonicalName = objectName.getCanonicalName(); return canonicalName; } /** * Obtains the ServiceMetaData class loader from the * getClassLoaderName value if there is an mbeanServer. * * @param md - the mbean metadata * @return the ServiceMetaData.ClassLoaderName class loader if * the mbeanServer has been set, the current TCL otherwise. * @throws InstanceNotFoundException if no mbean class loader can be * found by the ServiceMetaData.ClassLoaderName */ private ClassLoader getServiceMetaDataCL(ServiceMetaData md) throws InstanceNotFoundException { ClassLoader loader = null; if(mbeanServer != null) loader = mbeanServer.getClassLoader(md.getClassLoaderName()); // Fallback to TCL if there is no mbeanServer if(loader == null) loader = Thread.currentThread().getContextClassLoader(); return loader; } /** * Try to find a matching mbean attribute * @param name * @return */ private String mapAttributeName(ServiceMetaData md, String name) { ObjectName mbean = md.getObjectName(); String attrName = null; try { mbeanServer.getAttribute(mbean, name); attrName = name; } catch(Exception e) { char c = name.charAt(0); if(Character.isLowerCase(c)) name = Character.toUpperCase(c) + name.substring(1); else name = Character.toLowerCase(c) + name.substring(1); try { mbeanServer.getAttribute(mbean, name); attrName = name; } catch(Exception e2) { } } // FIXME if(attrName == null) { char c = name.charAt(0); name = Character.toUpperCase(c) + name.substring(1); return name; } return attrName; } }