001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.mx.metadata;
023:
024: import java.beans.IntrospectionException;
025: import java.beans.PropertyEditor;
026: import java.beans.PropertyEditorManager;
027: import java.util.ArrayList;
028: import java.util.Iterator;
029: import java.util.List;
030: import java.util.Map;
031:
032: import javax.management.Descriptor;
033: import javax.management.MBeanInfo;
034: import javax.management.MBeanOperationInfo;
035: import javax.management.MBeanParameterInfo;
036: import javax.management.NotCompliantMBeanException;
037: import javax.management.modelmbean.DescriptorSupport;
038: import javax.management.modelmbean.ModelMBeanAttributeInfo;
039: import javax.management.modelmbean.ModelMBeanConstructorInfo;
040: import javax.management.modelmbean.ModelMBeanInfo;
041: import javax.management.modelmbean.ModelMBeanInfoSupport;
042: import javax.management.modelmbean.ModelMBeanNotificationInfo;
043: import javax.management.modelmbean.ModelMBeanOperationInfo;
044:
045: import org.jboss.dom4j.Attribute;
046: import org.jboss.dom4j.Element;
047: import org.jboss.logging.Logger;
048: import org.jboss.mx.modelmbean.XMBeanConstants;
049: import org.jboss.mx.util.JBossNotCompliantMBeanException;
050: import org.jboss.util.Classes;
051: import org.jboss.util.StringPropertyReplacer;
052: import org.jboss.util.propertyeditor.PropertyEditors;
053: import org.w3c.dom.Node;
054: import org.w3c.dom.NodeList;
055:
056: /**
057: * The JBoss 1.0 model mbean descriptor parser class.
058: *
059: * @author Matt Munz
060: * @author Scott.Stark@jboss.org
061: * @author Dimitris.Andreadis@jboss.org
062: * @version $Revision: 57200 $
063: */
064: public class JBossXMBean10 extends AbstractBuilder implements
065: XMBeanConstants {
066: private static Logger log = Logger.getLogger(JBossXMBean10.class);
067:
068: // Attributes ----------------------------------------------------
069:
070: private Element element;
071:
072: /**
073: * The class name of the Model MBean implementation class.
074: */
075: private String mmbClassName = null;
076:
077: /**
078: * The class name of the resource object represented by this Model MBean.
079: */
080: private String resourceClassName = null;
081:
082: // Constructors --------------------------------------------------
083:
084: public JBossXMBean10(String mmbClassName, String resourceClassName,
085: Element element, Map properties) {
086: super ();
087: this .mmbClassName = mmbClassName;
088: this .resourceClassName = resourceClassName;
089: this .element = element;
090: setProperties(properties);
091: }
092:
093: // MetaDataBuilder implementation --------------------------------
094:
095: public MBeanInfo build() throws NotCompliantMBeanException {
096: try {
097: if (element == null) {
098: throw new JBossNotCompliantMBeanException(
099: "No xml configuration supplied!");
100: }
101: String description = element.elementTextTrim("description");
102:
103: if (resourceClassName == null) {
104: resourceClassName = element.elementTextTrim("class");
105: }
106:
107: List constructors = element.elements("constructor");
108: List operations = element.elements("operation");
109: List attributes = element.elements("attribute");
110: List notifications = element.elements("notification");
111:
112: Descriptor descr = getDescriptor(element, mmbClassName,
113: MBEAN_DESCRIPTOR);
114:
115: ModelMBeanInfo info = buildMBeanMetaData(description,
116: constructors, operations, attributes,
117: notifications, descr);
118:
119: return (MBeanInfo) info;
120: } catch (Throwable t) {
121: throw new JBossNotCompliantMBeanException(
122: "Error parsing the XML file: ", t);
123: }
124: }
125:
126: // Protected -----------------------------------------------------
127:
128: protected Descriptor getDescriptor(final Element parent,
129: final String infoName, final String type)
130: throws NotCompliantMBeanException {
131: Descriptor descr = new DescriptorSupport();
132: descr.setField(NAME, infoName);
133: descr.setField(DISPLAY_NAME, infoName);
134: descr.setField(DESCRIPTOR_TYPE, type);
135:
136: Element descriptors = parent.element("descriptors");
137: if (descriptors == null) {
138: return descr;
139: }
140:
141: for (Iterator i = descriptors.elementIterator(); i.hasNext();) {
142: Element descriptor = (Element) i.next();
143: String name = descriptor.getName();
144: if (name.equals("persistence")) {
145: String persistPolicy = descriptor
146: .attributeValue(PERSIST_POLICY);
147: String persistPeriod = descriptor
148: .attributeValue(PERSIST_PERIOD);
149: String persistLocation = descriptor
150: .attributeValue(PERSIST_LOCATION);
151: String persistName = descriptor
152: .attributeValue(PERSIST_NAME);
153: if (persistPolicy != null) {
154: validate(persistPolicy, PERSIST_POLICIES);
155: descr.setField(PERSIST_POLICY, persistPolicy);
156: }
157: if (persistPeriod != null) {
158: descr.setField(PERSIST_PERIOD, persistPeriod);
159: }
160: if (persistLocation != null) {
161: descr.setField(PERSIST_LOCATION, persistLocation);
162: }
163: if (persistName != null) {
164: descr.setField(PERSIST_NAME, persistName);
165: }
166: } else if (name.equals(CURRENCY_TIME_LIMIT)) {
167: descr.setField(CURRENCY_TIME_LIMIT, descriptor
168: .attributeValue("value"));
169: } else if (name.equals(DEFAULT)) {
170: String value = descriptor.attributeValue("value");
171: descr.setField(DEFAULT, value);
172: } else if (name.equals("display-name"))//DISPLAY_NAME is displayname
173: {
174: String value = descriptor.attributeValue("value");
175: descr.setField(DISPLAY_NAME, value);
176: } else if (name.equals(CACHED_VALUE)) {
177: String value = descriptor.attributeValue("value");
178: descr.setField(CACHED_VALUE, value);
179: } else if (name.equals(PERSISTENCE_MANAGER)) {
180: descr.setField(PERSISTENCE_MANAGER, descriptor
181: .attributeValue("value"));
182: } else if (name.equals(DESCRIPTOR)) {
183: descr.setField(descriptor.attributeValue("name"),
184: descriptor.attributeValue("value"));
185: } else if (name.equals("injection")) {
186: descr.setField(descriptor.attributeValue("id"),
187: descriptor.attributeValue("setMethod"));
188: } else if (name.equals(INTERCEPTORS)) {
189: Descriptor[] interceptorDescriptors = buildInterceptors(descriptor);
190: descr.setField(INTERCEPTORS, interceptorDescriptors);
191: }
192: } // end of for ()
193:
194: return descr;
195: }
196:
197: private void validate(String value, String[] valid)
198: throws NotCompliantMBeanException {
199: for (int i = 0; i < valid.length; i++) {
200: if (valid[i].equalsIgnoreCase(value)) {
201: return;
202: } // end of if ()
203: } // end of for ()
204: throw new JBossNotCompliantMBeanException(
205: "Unknown descriptor value: " + value);
206: }
207:
208: // builder methods
209:
210: protected ModelMBeanInfo buildMBeanMetaData(String description,
211: List constructors, List operations, List attributes,
212: List notifications, Descriptor descr)
213: throws NotCompliantMBeanException {
214:
215: ModelMBeanOperationInfo[] operInfo = buildOperationInfo(operations);
216: ModelMBeanAttributeInfo[] attrInfo = buildAttributeInfo(attributes);
217: ModelMBeanConstructorInfo[] constrInfo = buildConstructorInfo(constructors);
218: ModelMBeanNotificationInfo[] notifInfo = buildNotificationInfo(notifications);
219:
220: ModelMBeanInfo info = new ModelMBeanInfoSupport(mmbClassName,
221: description, attrInfo, constrInfo, operInfo, notifInfo,
222: descr);
223:
224: return info;
225: }
226:
227: protected ModelMBeanConstructorInfo[] buildConstructorInfo(
228: List constructors) throws NotCompliantMBeanException {
229:
230: List infos = new ArrayList();
231:
232: for (Iterator it = constructors.iterator(); it.hasNext();) {
233: Element constr = (Element) it.next();
234: String name = constr.elementTextTrim("name");
235: String description = constr.elementTextTrim("description");
236: List params = constr.elements("parameter");
237:
238: MBeanParameterInfo[] paramInfo = buildParameterInfo(params);
239:
240: Descriptor descr = getDescriptor(constr, name,
241: OPERATION_DESCRIPTOR);
242: descr.setField(ROLE, ROLE_CONSTRUCTOR);
243:
244: ModelMBeanConstructorInfo info = new ModelMBeanConstructorInfo(
245: name, description, paramInfo, descr);
246:
247: infos.add(info);
248: }
249:
250: return (ModelMBeanConstructorInfo[]) infos
251: .toArray(new ModelMBeanConstructorInfo[0]);
252: }
253:
254: protected ModelMBeanOperationInfo[] buildOperationInfo(
255: List operations) throws NotCompliantMBeanException {
256: List infos = new ArrayList();
257:
258: for (Iterator it = operations.iterator(); it.hasNext();) {
259: Element oper = (Element) it.next();
260: String name = oper.elementTextTrim("name");
261: String description = oper.elementTextTrim("description");
262: String type = oper.elementTextTrim("return-type");
263: String impact = oper.attributeValue("impact");
264: List params = oper.elements("parameter");
265:
266: MBeanParameterInfo[] paramInfo = buildParameterInfo(params);
267:
268: Descriptor descr = getDescriptor(oper, name,
269: OPERATION_DESCRIPTOR);
270: descr.setField(ROLE, ROLE_OPERATION);
271:
272: // defaults to ACTION_INFO
273: int operImpact = MBeanOperationInfo.ACTION_INFO;
274:
275: if (impact != null) {
276: if (impact.equals(INFO))
277: operImpact = MBeanOperationInfo.INFO;
278: else if (impact.equals(ACTION))
279: operImpact = MBeanOperationInfo.ACTION;
280: else if (impact.equals(ACTION_INFO))
281: operImpact = MBeanOperationInfo.ACTION_INFO;
282: }
283:
284: // default return-type is void
285: if (type == null)
286: type = "void";
287:
288: ModelMBeanOperationInfo info = new ModelMBeanOperationInfo(
289: name, description, paramInfo, type, operImpact,
290: descr);
291:
292: infos.add(info);
293: }
294:
295: return (ModelMBeanOperationInfo[]) infos
296: .toArray(new ModelMBeanOperationInfo[0]);
297: }
298:
299: protected ModelMBeanNotificationInfo[] buildNotificationInfo(
300: List notifications) throws NotCompliantMBeanException {
301:
302: List infos = new ArrayList();
303:
304: for (Iterator it = notifications.iterator(); it.hasNext();) {
305: Element notif = (Element) it.next();
306: String name = notif.elementTextTrim("name");
307: String description = notif.elementTextTrim("description");
308: List notifTypes = notif.elements("notification-type");
309: Descriptor descr = getDescriptor(notif, name,
310: NOTIFICATION_DESCRIPTOR);
311:
312: List types = new ArrayList();
313:
314: for (Iterator iterator = notifTypes.iterator(); iterator
315: .hasNext();) {
316: Element type = (Element) iterator.next();
317: types.add(type.getTextTrim());
318: }
319:
320: ModelMBeanNotificationInfo info = new ModelMBeanNotificationInfo(
321: (String[]) types.toArray(new String[types.size()]),
322: name, description, descr);
323:
324: infos.add(info);
325: }
326:
327: return (ModelMBeanNotificationInfo[]) infos
328: .toArray(new ModelMBeanNotificationInfo[infos.size()]);
329: }
330:
331: protected ModelMBeanAttributeInfo[] buildAttributeInfo(
332: List attributes) throws NotCompliantMBeanException {
333:
334: List infos = new ArrayList();
335:
336: for (Iterator it = attributes.iterator(); it.hasNext();) {
337: Element attr = (Element) it.next();
338: String name = attr.elementTextTrim("name");
339: String description = attr.elementTextTrim("description");
340: String type = attr.elementTextTrim("type");
341: String access = attr.attributeValue("access");
342: String getMethod = attr.attributeValue("getMethod");
343: String setMethod = attr.attributeValue("setMethod");
344: Descriptor descr = getDescriptor(attr, name,
345: ATTRIBUTE_DESCRIPTOR);
346: //Convert types here from string to specified type
347: String unconvertedValue = (String) descr
348: .getFieldValue(CACHED_VALUE);
349: if (unconvertedValue != null
350: && !"java.lang.String".equals(type)) {
351: descr.setField(CACHED_VALUE, convertValue(
352: unconvertedValue, type));
353: } else {
354: // if <value value="xxx"/> is absent
355: // try new syntax for VALUE initialization
356: // e.g <value><nested-element/></value>
357: Object value = getAttributeValue(attr, type,
358: CACHED_VALUE);
359: if (value != null)
360: descr.setField(CACHED_VALUE, value);
361: }
362: String unconvertedDefault = (String) descr
363: .getFieldValue(DEFAULT);
364: if (unconvertedDefault != null
365: && !"java.lang.String".equals(type)) {
366: descr.setField(DEFAULT, convertValue(
367: unconvertedDefault, type));
368: } else {
369: // if <defaul value="xxx"/> is absent
370: // try new syntax for DEFAULT initialization
371: // e.g <default><nested-element/></default>
372: Object value = getAttributeValue(attr, type, DEFAULT);
373: if (value != null)
374: descr.setField(DEFAULT, value);
375: }
376: if (getMethod != null) {
377: descr.setField(GET_METHOD, getMethod);
378: } // end of if ()
379:
380: if (setMethod != null) {
381: descr.setField(SET_METHOD, setMethod);
382: } // end of if ()
383:
384: // defaults read-write
385: boolean isReadable = true;
386: boolean isWritable = true;
387:
388: if (access.equalsIgnoreCase("read-only"))
389: isWritable = false;
390:
391: else if (access.equalsIgnoreCase("write-only"))
392: isReadable = false;
393:
394: ModelMBeanAttributeInfo info = new ModelMBeanAttributeInfo(
395: name, type, description, isReadable, isWritable,
396: false, descr);
397:
398: infos.add(info);
399: }
400:
401: return (ModelMBeanAttributeInfo[]) infos
402: .toArray(new ModelMBeanAttributeInfo[0]);
403: }
404:
405: /**
406: * Get the value for the attribute descriptor "value" or "default"
407: * the same way we would do for mbean attribute overrides
408: */
409: protected Object getAttributeValue(Element attribute,
410: String typeName, String which)
411: throws NotCompliantMBeanException {
412: Object value = null;
413:
414: Element descriptors = attribute.element("descriptors");
415: if (descriptors != null) {
416: for (Iterator i = descriptors.elementIterator(); i
417: .hasNext();) {
418: // looking for 'which', i.e. "value" or "default"
419: Element descriptor = (Element) i.next();
420: String name = descriptor.getName();
421: if (name.equals(which) && descriptor.hasContent()) {
422: // at this point "value" attribute does not exist
423: // plus the descriptor has content so we know the
424: // new syntax is used.
425: //
426: // Convert to org.w3c.dom.Element so that the code
427: // from ServiceConfigurator can be applied, plus
428: // if attribute type is org.w3c.dom.Element we need
429: // to make the conversion anyway.
430:
431: // descriptor(org.jboss.dom4j.Element) -> element (org.w3c.dom.Element)
432: try {
433: org.w3c.dom.Element element = toW3CElement(descriptor);
434:
435: boolean replace = true;
436: boolean trim = true;
437:
438: String replaceAttr = element
439: .getAttribute("replace");
440: if (replaceAttr.length() > 0)
441: replace = Boolean.valueOf(replaceAttr)
442: .booleanValue();
443: String trimAttr = element.getAttribute("trim");
444: if (trimAttr.length() > 0)
445: trim = Boolean.valueOf(trimAttr)
446: .booleanValue();
447:
448: // Get the classloader for loading attribute classes.
449: ClassLoader cl = Thread.currentThread()
450: .getContextClassLoader();
451:
452: // see if it is a primitive type first
453: Class typeClass = Classes
454: .getPrimitiveTypeForName(typeName);
455: if (typeClass == null) {
456: // nope try look up
457: try {
458: typeClass = cl.loadClass(typeName);
459: } catch (ClassNotFoundException e) {
460: throw new JBossNotCompliantMBeanException(
461: "Class not found '" + typeName
462: + "'", e);
463: }
464: }
465:
466: /* Attributes of type Element are passed as is after optionally
467: performing system property replacement
468: */
469: if (typeClass.equals(org.w3c.dom.Element.class)) {
470: // Use the first child Element of this element as the value
471: NodeList nl = element.getChildNodes();
472: for (int j = 0; j < nl.getLength(); j++) {
473: Node n = nl.item(j);
474: if (n.getNodeType() == Node.ELEMENT_NODE) {
475: value = n;
476: break;
477: }
478: }
479: // Replace any ${x} references in the element text
480: if (replace) {
481: PropertyEditor editor = PropertyEditorManager
482: .findEditor(typeClass);
483: if (editor == null) {
484: log
485: .warn("Cannot perform property replace on Element");
486: } else {
487: editor.setValue(value);
488: String text = editor.getAsText();
489: text = StringPropertyReplacer
490: .replaceProperties(text);
491: editor.setAsText(text);
492: value = editor.getValue();
493: }
494: }
495: }
496:
497: if (value == null) {
498: PropertyEditor editor = PropertyEditorManager
499: .findEditor(typeClass);
500: if (editor == null) {
501: throw new JBossNotCompliantMBeanException(
502: "No property editor for type '"
503: + typeName + "'");
504: }
505: // Get the attribute value
506: String attributeText = getElementContent(
507: element, trim, replace);
508: editor.setAsText(attributeText);
509: value = editor.getValue();
510: }
511: } catch (org.jboss.dom4j.DocumentException e) {
512: throw new JBossNotCompliantMBeanException(
513: "cannot convert '"
514: + which
515: + "' descriptor to org.w3c.dom.Element",
516: e);
517: }
518:
519: // stop processing
520: break;
521: }
522: }
523: }
524: return value;
525: }
526:
527: /**
528: * Convert org.jboss.dom4j.Element->org.w3c.dom.Element
529: */
530: private org.w3c.dom.Element toW3CElement(
531: org.jboss.dom4j.Element d4element)
532: throws org.jboss.dom4j.DocumentException {
533: // prepare
534: org.jboss.dom4j.Document d4doc = org.jboss.dom4j.DocumentFactory
535: .getInstance().createDocument();
536: org.jboss.dom4j.io.DOMWriter d4Writer = new org.jboss.dom4j.io.DOMWriter();
537: // copy
538: d4doc.setRootElement(d4element.createCopy());
539: // convert
540: org.w3c.dom.Document doc = d4Writer.write(d4doc);
541: // return root Element - should I copy again?
542: return doc.getDocumentElement();
543: }
544:
545: /**
546: * Copied from ServiceConfigurator
547: */
548: private String getElementContent(org.w3c.dom.Element element,
549: boolean trim, boolean replace) {
550: NodeList nl = element.getChildNodes();
551: String attributeText = "";
552: for (int i = 0; i < nl.getLength(); i++) {
553: Node n = nl.item(i);
554: if (n instanceof org.w3c.dom.Text) {
555: attributeText += ((org.w3c.dom.Text) n).getData();
556: }
557: } // end of for ()
558: if (trim)
559: attributeText = attributeText.trim();
560: if (replace)
561: attributeText = StringPropertyReplacer
562: .replaceProperties(attributeText);
563: return attributeText;
564: }
565:
566: /**
567: * Describe <code>convertType</code> method here.
568: * Copied from ServiceConfigurator, without Element support.
569: *
570: * @param unconverted a <code>String</code> value
571: * @param typeName a <code>String</code> value
572: * @return an <code>Object</code> value
573: * @exception NotCompliantMBeanException if an error occurs
574: */
575: protected Object convertValue(String unconverted, String typeName)
576: throws NotCompliantMBeanException {
577: Object value = null;
578: try {
579: value = PropertyEditors.convertValue(unconverted, typeName);
580: } catch (ClassNotFoundException e) {
581: log.debug("Failed to load type class", e);
582: throw new NotCompliantMBeanException(
583: "Class not found for type: " + typeName);
584: } catch (IntrospectionException e) {
585: throw new NotCompliantMBeanException(
586: "No property editor for type=" + typeName);
587: }
588: return value;
589: }
590:
591: protected MBeanParameterInfo[] buildParameterInfo(List parameters) {
592: Iterator it = parameters.iterator();
593: List infos = new ArrayList();
594:
595: while (it.hasNext()) {
596: Element param = (Element) it.next();
597: String name = param.elementTextTrim("name");
598: String type = param.elementTextTrim("type");
599: String descr = param.elementTextTrim("description");
600:
601: MBeanParameterInfo info = new MBeanParameterInfo(name,
602: type, descr);
603:
604: infos.add(info);
605: }
606:
607: return (MBeanParameterInfo[]) infos
608: .toArray(new MBeanParameterInfo[0]);
609: }
610:
611: protected Descriptor[] buildInterceptors(Element descriptor) {
612: List interceptors = descriptor.elements("interceptor");
613: ArrayList tmp = new ArrayList();
614: for (int i = 0; i < interceptors.size(); i++) {
615: Element interceptor = (Element) interceptors.get(i);
616: String code = interceptor.attributeValue("code");
617: DescriptorSupport interceptorDescr = new DescriptorSupport();
618: interceptorDescr.setField("code", code);
619: List attributes = interceptor.attributes();
620: for (int a = 0; a < attributes.size(); a++) {
621: Attribute attr = (Attribute) attributes.get(a);
622: String name = attr.getName();
623: String value = attr.getValue();
624: value = StringPropertyReplacer.replaceProperties(value);
625: interceptorDescr.setField(name, value);
626: }
627: tmp.add(interceptorDescr);
628: }
629: Descriptor[] descriptors = new Descriptor[tmp.size()];
630: tmp.toArray(descriptors);
631: return descriptors;
632: }
633:
634: }
|