package org.jboss.mx.metadata;
import java.beans.IntrospectionException;
import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.management.Descriptor;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.NotCompliantMBeanException;
import javax.management.modelmbean.DescriptorSupport;
import javax.management.modelmbean.ModelMBeanAttributeInfo;
import javax.management.modelmbean.ModelMBeanConstructorInfo;
import javax.management.modelmbean.ModelMBeanInfo;
import javax.management.modelmbean.ModelMBeanInfoSupport;
import javax.management.modelmbean.ModelMBeanNotificationInfo;
import javax.management.modelmbean.ModelMBeanOperationInfo;
import org.dom4j.Attribute;
import org.dom4j.Element;
import org.jboss.mx.modelmbean.XMBeanConstants;
import org.jboss.mx.util.JBossNotCompliantMBeanException;
import org.jboss.util.propertyeditor.PropertyEditors;
import org.jboss.util.StringPropertyReplacer;
import org.jboss.util.Classes;
import org.jboss.logging.Logger;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class JBossXMBean10
extends AbstractBuilder
implements XMBeanConstants
{
private static Logger log = Logger.getLogger(JBossXMBean10.class);
private Element element;
private String mmbClassName = null;
private String resourceClassName = null;
public JBossXMBean10(String mmbClassName, String resourceClassName, Element element, Map properties)
{
super();
this.mmbClassName = mmbClassName;
this.resourceClassName = resourceClassName;
this.element = element;
setProperties(properties);
}
public MBeanInfo build() throws NotCompliantMBeanException
{
try
{
if (element == null)
{
throw new JBossNotCompliantMBeanException("No xml configuration supplied!");
}
String description = element.elementTextTrim("description");
if (resourceClassName == null)
{
resourceClassName = element.elementTextTrim("class");
}
List constructors = element.elements("constructor");
List operations = element.elements("operation");
List attributes = element.elements("attribute");
List notifications = element.elements("notification");
Descriptor descr = getDescriptor(element, mmbClassName, MBEAN_DESCRIPTOR);
ModelMBeanInfo info = buildMBeanMetaData(
description, constructors, operations,
attributes, notifications, descr
);
return (MBeanInfo) info;
}
catch (Throwable t)
{
throw new JBossNotCompliantMBeanException("Error parsing the XML file: ", t);
}
}
protected Descriptor getDescriptor(final Element parent, final String infoName, final String type)
throws NotCompliantMBeanException
{
Descriptor descr = new DescriptorSupport();
descr.setField(NAME, infoName);
descr.setField(DISPLAY_NAME, infoName);
descr.setField(DESCRIPTOR_TYPE, type);
Element descriptors = parent.element("descriptors");
if (descriptors == null)
{
return descr;
}
for (Iterator i = descriptors.elementIterator(); i.hasNext();)
{
Element descriptor = (Element)i.next();
String name = descriptor.getName();
if (name.equals("persistence"))
{
String persistPolicy = descriptor.attributeValue(PERSIST_POLICY);
String persistPeriod = descriptor.attributeValue(PERSIST_PERIOD);
String persistLocation = descriptor.attributeValue(PERSIST_LOCATION);
String persistName = descriptor.attributeValue(PERSIST_NAME);
if (persistPolicy != null)
{
validate(persistPolicy, PERSIST_POLICIES);
descr.setField(PERSIST_POLICY, persistPolicy);
}
if (persistPeriod != null)
{
descr.setField(PERSIST_PERIOD, persistPeriod);
}
if (persistLocation != null)
{
descr.setField(PERSIST_LOCATION, persistLocation);
}
if (persistName != null)
{
descr.setField(PERSIST_NAME, persistName);
}
}
else if (name.equals(CURRENCY_TIME_LIMIT))
{
descr.setField(CURRENCY_TIME_LIMIT, descriptor.attributeValue("value"));
}
else if (name.equals(DEFAULT))
{
String value = descriptor.attributeValue("value");
descr.setField(DEFAULT, value);
}
else if (name.equals("display-name")) {
String value = descriptor.attributeValue("value");
descr.setField(DISPLAY_NAME, value);
}
else if (name.equals(CACHED_VALUE))
{
String value = descriptor.attributeValue("value");
descr.setField(CACHED_VALUE, value);
}
else if (name.equals(PERSISTENCE_MANAGER))
{
descr.setField(PERSISTENCE_MANAGER, descriptor.attributeValue("value"));
}
else if (name.equals(DESCRIPTOR))
{
descr.setField(descriptor.attributeValue("name"), descriptor.attributeValue("value"));
}
else if(name.equals(INTERCEPTORS))
{
Descriptor[] interceptorDescriptors = buildInterceptors(descriptor);
descr.setField(INTERCEPTORS, interceptorDescriptors);
}
}
List injections = descriptors.elements("injection");
int count = injections != null ? injections.size() : 0;
for(int n = 0; n < count; n ++)
{
Element injection = (Element) injections.get(n);
String id = injection.attributeValue("id");
String setter = injection.attributeValue("setMethod");
descr.setField(id, setter);
}
return descr;
}
private void validate(String value, String[] valid) throws NotCompliantMBeanException
{
for (int i = 0; i< valid.length; i++)
{
if (valid[i].equalsIgnoreCase(value))
{
return;
} } throw new JBossNotCompliantMBeanException("Unknown descriptor value: " + value);
}
protected ModelMBeanInfo buildMBeanMetaData(String description,
List constructors, List operations, List attributes,
List notifications, Descriptor descr)
throws NotCompliantMBeanException
{
ModelMBeanOperationInfo[] operInfo =
buildOperationInfo(operations, attributes);
ModelMBeanAttributeInfo[] attrInfo =
buildAttributeInfo(attributes);
ModelMBeanConstructorInfo[] constrInfo =
buildConstructorInfo(constructors);
ModelMBeanNotificationInfo[] notifInfo =
buildNotificationInfo(notifications);
ModelMBeanInfo info = new ModelMBeanInfoSupport(
mmbClassName, description, attrInfo, constrInfo,
operInfo, notifInfo, descr
);
return info;
}
protected ModelMBeanConstructorInfo[] buildConstructorInfo(List constructors)
throws NotCompliantMBeanException
{
List infos = new ArrayList();
for (Iterator it = constructors.iterator(); it.hasNext();)
{
Element constr = (Element) it.next();
String name = constr.elementTextTrim("name");
String description = constr.elementTextTrim("description");
List params = constr.elements("parameter");
MBeanParameterInfo[] paramInfo =
buildParameterInfo(params);
Descriptor descr = getDescriptor(constr, name, OPERATION_DESCRIPTOR);
descr.setField(ROLE, ROLE_CONSTRUCTOR);
ModelMBeanConstructorInfo info =
new ModelMBeanConstructorInfo(name, description, paramInfo, descr);
infos.add(info);
}
return (ModelMBeanConstructorInfo[]) infos.toArray(
new ModelMBeanConstructorInfo[0]);
}
protected ModelMBeanOperationInfo[] buildOperationInfo(List operations, List attributes)
throws NotCompliantMBeanException
{
List infos = new ArrayList();
HashMap getters = new HashMap();
HashMap setters = new HashMap();
for (Iterator it = operations.iterator(); it.hasNext(); )
{
Element oper = (Element) it.next();
String name = oper.elementTextTrim("name");
String description = oper.elementTextTrim("description");
String type = oper.elementTextTrim("return-type");
String impact = oper.attributeValue("impact");
List params = oper.elements("parameter");
MBeanParameterInfo[] paramInfo =
buildParameterInfo(params);
Descriptor descr = getDescriptor(oper, name, OPERATION_DESCRIPTOR);
descr.setField(ROLE, ROLE_OPERATION);
int operImpact = MBeanOperationInfo.ACTION_INFO;
if (impact != null)
{
if (impact.equals(INFO))
operImpact = MBeanOperationInfo.INFO;
else if (impact.equals(ACTION))
operImpact = MBeanOperationInfo.ACTION;
else if (impact.equals(ACTION_INFO))
operImpact = MBeanOperationInfo.ACTION_INFO;
}
if (type == null)
type = "void";
if (paramInfo.length == 0 && type.equals("void") == false)
getters.put(name, type);
if (paramInfo.length == 1)
{
HashSet types = (HashSet) setters.get(name);
if (types == null)
{
types = new HashSet();
setters.put(name, types);
}
types.add(paramInfo[0].getType());
}
ModelMBeanOperationInfo info = new ModelMBeanOperationInfo(
name, description, paramInfo, type, operImpact, descr);
infos.add(info);
}
for (Iterator it = attributes.iterator(); it.hasNext();)
{
Element attr = (Element) it.next();
String name = attr.elementTextTrim("name");
String type = attr.elementTextTrim("type");
String getMethod = attr.attributeValue(GET_METHOD_ATTRIBUTE);
String setMethod = attr.attributeValue(SET_METHOD_ATTRIBUTE);
if (getMethod != null)
{
Object getterOpType = getters.get(getMethod);
if (getterOpType == null || getterOpType.equals(type) == false)
{
Descriptor getterDescriptor = new DescriptorSupport();
getterDescriptor.setField(NAME, getMethod);
getterDescriptor.setField(DESCRIPTOR_TYPE, OPERATION_DESCRIPTOR);
getterDescriptor.setField(ROLE, ROLE_GETTER);
ModelMBeanOperationInfo info = new ModelMBeanOperationInfo
(
getMethod,
"getMethod operation for '" + name + "' attribute.",
new MBeanParameterInfo[0],
type,
MBeanOperationInfo.INFO,
getterDescriptor
);
infos.add(info);
}
}
if (setMethod != null)
{
HashSet setterOpTypes = (HashSet) setters.get(setMethod);
if (setterOpTypes == null || setterOpTypes.contains(type) == false)
{
Descriptor setterDescriptor = new DescriptorSupport();
setterDescriptor.setField(NAME, setMethod);
setterDescriptor.setField(DESCRIPTOR_TYPE, OPERATION_DESCRIPTOR);
setterDescriptor.setField(ROLE, ROLE_SETTER);
ModelMBeanOperationInfo info = new ModelMBeanOperationInfo
(
setMethod,
"setMethod operation for '" + name + "' attribute.",
new MBeanParameterInfo[]
{
new MBeanParameterInfo("value", type, "The new value")
},
Void.TYPE.getName(),
MBeanOperationInfo.ACTION,
setterDescriptor
);
infos.add(info);
}
}
}
return (ModelMBeanOperationInfo[]) infos.toArray(
new ModelMBeanOperationInfo[0]);
}
protected ModelMBeanNotificationInfo[] buildNotificationInfo(List notifications)
throws NotCompliantMBeanException
{
List infos = new ArrayList();
for (Iterator it = notifications.iterator(); it.hasNext();)
{
Element notif = (Element) it.next();
String name = notif.elementTextTrim("name");
String description = notif.elementTextTrim("description");
List notifTypes = notif.elements("notification-type");
Descriptor descr = getDescriptor(notif, name, NOTIFICATION_DESCRIPTOR);
List types = new ArrayList();
for (Iterator iterator = notifTypes.iterator(); iterator.hasNext();)
{
Element type = (Element) iterator.next();
types.add(type.getTextTrim());
}
ModelMBeanNotificationInfo info = new ModelMBeanNotificationInfo(
(String[]) types.toArray(new String[types.size()]), name, description, descr);
infos.add(info);
}
return (ModelMBeanNotificationInfo[]) infos.toArray(
new ModelMBeanNotificationInfo[infos.size()]
);
}
protected ModelMBeanAttributeInfo[] buildAttributeInfo(List attributes)
throws NotCompliantMBeanException
{
List infos = new ArrayList();
for (Iterator it = attributes.iterator(); it.hasNext();)
{
Element attr = (Element) it.next();
String name = attr.elementTextTrim("name");
String description = attr.elementTextTrim("description");
String type = attr.elementTextTrim("type");
String access = attr.attributeValue("access");
String getMethod = attr.attributeValue("getMethod");
String setMethod = attr.attributeValue("setMethod");
Descriptor descr = getDescriptor(attr, name, ATTRIBUTE_DESCRIPTOR);
String unconvertedValue = (String)descr.getFieldValue(CACHED_VALUE);
if (unconvertedValue != null && !"java.lang.String".equals(type))
{
descr.setField(CACHED_VALUE, convertValue(unconvertedValue, type));
}
else
{
Object value = getAttributeValue(attr, type, CACHED_VALUE);
if (value != null)
descr.setField(CACHED_VALUE, value);
}
String unconvertedDefault = (String)descr.getFieldValue(DEFAULT);
if (unconvertedDefault != null && !"java.lang.String".equals(type))
{
descr.setField(DEFAULT, convertValue(unconvertedDefault, type));
}
else
{
Object value = getAttributeValue(attr, type, DEFAULT);
if (value != null)
descr.setField(DEFAULT, value);
}
if (getMethod != null)
{
descr.setField(GET_METHOD, getMethod);
}
if (setMethod != null)
{
descr.setField(SET_METHOD, setMethod);
}
boolean isReadable = true;
boolean isWritable = true;
if (access.equalsIgnoreCase("read-only"))
isWritable = false;
else if (access.equalsIgnoreCase("write-only"))
isReadable = false;
ModelMBeanAttributeInfo info = new ModelMBeanAttributeInfo(
name, type, description, isReadable, isWritable, false, descr
);
infos.add(info);
}
return (ModelMBeanAttributeInfo[]) infos.toArray(
new ModelMBeanAttributeInfo[0]
);
}
protected Object getAttributeValue(Element attribute, String typeName, String which)
throws NotCompliantMBeanException
{
Object value = null;
Element descriptors = attribute.element("descriptors");
if (descriptors != null)
{
for (Iterator i = descriptors.elementIterator(); i.hasNext();)
{
Element descriptor = (Element)i.next();
String name = descriptor.getName();
if (name.equals(which) && descriptor.hasContent())
{
try
{
org.w3c.dom.Element element = toW3CElement(descriptor);
boolean replace = true;
boolean trim = true;
String replaceAttr = element.getAttribute("replace");
if( replaceAttr.length() > 0 )
replace = Boolean.valueOf(replaceAttr).booleanValue();
String trimAttr = element.getAttribute("trim");
if( trimAttr.length() > 0 )
trim = Boolean.valueOf(trimAttr).booleanValue();
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class typeClass = Classes.getPrimitiveTypeForName(typeName);
if (typeClass == null)
{
try
{
typeClass = cl.loadClass(typeName);
}
catch (ClassNotFoundException e)
{
throw new JBossNotCompliantMBeanException
("Class not found '" + typeName + "'", e);
}
}
if (typeClass.equals(org.w3c.dom.Element.class))
{
NodeList nl = element.getChildNodes();
for (int j=0; j < nl.getLength(); j++)
{
Node n = nl.item(j);
if (n.getNodeType() == Node.ELEMENT_NODE)
{
value = n;
break;
}
}
if( replace )
{
PropertyEditor editor = PropertyEditorManager.findEditor(typeClass);
if( editor == null )
{
log.warn("Cannot perform property replace on Element");
}
else
{
editor.setValue(value);
String text = editor.getAsText();
text = StringPropertyReplacer.replaceProperties(text);
editor.setAsText(text);
value = editor.getValue();
}
}
}
if (value == null)
{
PropertyEditor editor = PropertyEditorManager.findEditor(typeClass);
if (editor == null)
{
throw new JBossNotCompliantMBeanException
("No property editor for type '" + typeName + "'");
}
String attributeText = getElementContent(element, trim, replace);
editor.setAsText(attributeText);
value = editor.getValue();
}
}
catch (org.dom4j.DocumentException e)
{
throw new JBossNotCompliantMBeanException(
"cannot convert '" + which + "' descriptor to org.w3c.dom.Element", e);
}
break;
}
}
}
return value;
}
private org.w3c.dom.Element toW3CElement(org.dom4j.Element d4element)
throws org.dom4j.DocumentException
{
org.dom4j.Document d4doc = org.dom4j.DocumentFactory.getInstance().createDocument();
org.dom4j.io.DOMWriter d4Writer = new org.dom4j.io.DOMWriter();
d4doc.setRootElement(d4element.createCopy());
org.w3c.dom.Document doc = d4Writer.write(d4doc);
return doc.getDocumentElement();
}
private String getElementContent(org.w3c.dom.Element element, boolean trim, boolean replace)
{
NodeList nl = element.getChildNodes();
String attributeText = "";
for (int i = 0; i < nl.getLength(); i++)
{
Node n = nl.item(i);
if( n instanceof org.w3c.dom.Text )
{
attributeText += ((org.w3c.dom.Text)n).getData();
}
} if( trim )
attributeText = attributeText.trim();
if (replace)
attributeText = StringPropertyReplacer.replaceProperties(attributeText);
return attributeText;
}
protected Object convertValue(String unconverted, String typeName)
throws NotCompliantMBeanException
{
Object value = null;
try
{
value = PropertyEditors.convertValue(unconverted, typeName);
}
catch (ClassNotFoundException e)
{
log.debug("Failed to load type class", e);
throw new NotCompliantMBeanException
("Class not found for type: " + typeName);
}
catch(IntrospectionException e)
{
throw new NotCompliantMBeanException
("No property editor for type=" + typeName);
}
return value;
}
protected MBeanParameterInfo[] buildParameterInfo(List parameters)
{
Iterator it = parameters.iterator();
List infos = new ArrayList();
while (it.hasNext())
{
Element param = (Element) it.next();
String name = param.elementTextTrim("name");
String type = param.elementTextTrim("type");
String descr = param.elementTextTrim("description");
MBeanParameterInfo info = new MBeanParameterInfo(name, type, descr);
infos.add(info);
}
return (MBeanParameterInfo[]) infos.toArray(new MBeanParameterInfo[0]);
}
protected Descriptor[] buildInterceptors(Element descriptor)
{
List interceptors = descriptor.elements("interceptor");
ArrayList tmp = new ArrayList();
for(int i = 0; i < interceptors.size(); i ++)
{
Element interceptor = (Element) interceptors.get(i);
String code = interceptor.attributeValue("code");
DescriptorSupport interceptorDescr = new DescriptorSupport();
interceptorDescr.setField("code", code);
List attributes = interceptor.attributes();
for(int a = 0; a < attributes.size(); a ++)
{
Attribute attr = (Attribute) attributes.get(a);
String name = attr.getName();
String value = attr.getValue();
value = StringPropertyReplacer.replaceProperties(value);
interceptorDescr.setField(name, value);
}
tmp.add(interceptorDescr);
}
Descriptor[] descriptors = new Descriptor[tmp.size()];
tmp.toArray(descriptors);
return descriptors;
}
}