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.modelmbean;
023:
024: import java.net.MalformedURLException;
025: import java.net.URL;
026: import java.util.HashMap;
027: import java.util.Iterator;
028: import java.util.List;
029:
030: import javax.management.Descriptor;
031: import javax.management.InstanceNotFoundException;
032: import javax.management.ListenerNotFoundException;
033: import javax.management.MBeanException;
034: import javax.management.MBeanInfo;
035: import javax.management.MBeanNotificationInfo;
036: import javax.management.MBeanServer;
037: import javax.management.NotCompliantMBeanException;
038: import javax.management.NotificationBroadcaster;
039: import javax.management.Notification;
040: import javax.management.NotificationFilter;
041: import javax.management.NotificationListener;
042: import javax.management.ObjectName;
043: import javax.management.RuntimeOperationsException;
044: import javax.management.StandardMBean;
045: import javax.management.modelmbean.InvalidTargetObjectTypeException;
046: import javax.management.modelmbean.ModelMBeanAttributeInfo;
047: import javax.management.modelmbean.ModelMBeanConstructorInfo;
048: import javax.management.modelmbean.ModelMBeanInfo;
049: import javax.management.modelmbean.ModelMBeanInfoSupport;
050: import javax.management.modelmbean.ModelMBeanNotificationInfo;
051: import javax.management.modelmbean.ModelMBeanOperationInfo;
052: import javax.xml.parsers.DocumentBuilder;
053: import javax.xml.parsers.DocumentBuilderFactory;
054: import javax.xml.parsers.ParserConfigurationException;
055:
056: import org.jboss.mx.interceptor.StandardMBeanInfoInterceptor;
057: import org.jboss.mx.metadata.MBeanInfoConversion;
058: import org.jboss.mx.metadata.MetaDataBuilder;
059: import org.jboss.mx.metadata.StandardMetaData;
060: import org.jboss.mx.metadata.XMLMetaData;
061:
062: /**
063: * XMBean implementation.
064: *
065: * @author <a href="mailto:juha@jboss.org">Juha Lindfors</a>.
066: * @author Matt Munz
067: * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
068: * @version $Revision: 57200 $
069: */
070: public class XMBean extends ModelMBeanInvoker implements
071: XMBeanConstants, NotificationListener {
072:
073: // Constructors --------------------------------------------------
074:
075: /**
076: * Default constructor for the XMBean Model MBean implementation. This
077: * creates an uninitialized Model MBean template.
078: */
079: public XMBean() throws MBeanException {
080: try {
081: setManagedResource(new Object(), OBJECT_REF);
082: setModelMBeanInfo(new ModelMBeanInfoSupport("XMBean",
083: "Uninitialized XMBean",
084: new ModelMBeanAttributeInfo[0],
085: new ModelMBeanConstructorInfo[0],
086: new ModelMBeanOperationInfo[0],
087: new ModelMBeanNotificationInfo[0]));
088: } catch (RuntimeException e) {
089: throw new RuntimeOperationsException(e);
090: } catch (Exception e) {
091: throw new MBeanException(e);
092: }
093: }
094:
095: /**
096: * Creates an XMBean Model MBean implementation with a predefined JMX
097: * metadata.
098: *
099: * @param info Model MBean metadata describing this MBean template
100: */
101: public XMBean(ModelMBeanInfo info) throws MBeanException {
102: super (info);
103: }
104:
105: /**
106: * Creates a XMBean instance with a given resource object and resource type. <p>
107: *
108: * This Model MBean implementation supports the following resource types: <br><pre>
109: *
110: * - {@link ModelMBeanConstants#OBJECT_REF OBJECT_REF}
111: * - {@link XMBeanConstants#STANDARD_INTERFACE STANDARD_INTERFACE}
112: * - {@link XMBeanConstants#DESCRIPTOR DESCRIPTOR}
113: * - Any valid URL string to a *.xml file.
114: *
115: * </pre>
116: *
117: * <tt><b>OBJECT_REF:</b></tt> resource object can be any Java object. The
118: * management interface must be set separately via
119: * {@link javax.management.modelmbean.ModelMBean#setModelMBeanInfo setModelMBeanInfo}
120: * method. <p>
121: *
122: * <tt><b>STANDARD_INTERFACE:</b></tt> the resource object is assumed to
123: * follow the Standard MBean naming conventions to expose its management
124: * interface, including implementing a <tt>xxxMBean</tt> interface. A
125: * corresponding Model MBean metadata is generated for the Model MBean
126: * representing this resource type. <p>
127: *
128: * <tt><b>DESCRIPTOR:</b></tt> the resource object is wrapped as a part of
129: * the {@link javax.management.Descriptor Descriptor} object passed to this
130: * Model MBean instance. The descriptor object must contain the mandatory
131: * fields {@link XMBeanConstants#RESOURCE_REFERENCE RESOURCE_REFERENCE} and
132: * {@link XMBeanConstants#RESOURCE_TYPE RESOURCE_TYPE} that identify the
133: * correct resource reference and type used for this Model MBean instance.
134: * The descriptor object may also contain additional fields, such as
135: * {@link XMBeanConstants#SAX_PARSER SAX_PARSER} and
136: * {@link XMBeanConstants#XML_VALIDATION XML_VALIDATION} that are passed as
137: * configuration properties for the metadata builder instances. Any
138: * additional descriptor fields that match the
139: * {@link XMBeanConstants#METADATA_DESCRIPTOR_PREFIX METADATA_DESCRIPTOR_PREFIX}
140: * naming pattern will be passed to the builder implementation via its
141: * {@link org.jboss.mx.metadata.MetaDataBuilder#setProperty setProperty}
142: * method. <p>
143: *
144: * <tt><b>URL String:</b></tt> if a resource type string contains an URL
145: * that ends with a *.xml file name the resource object is exposed via the
146: * XML management interface definition read from this URL. The XML parser
147: * implementation is picked based on the schema definition in the XML
148: * document.
149: *
150: * @param resource resource object or descriptor
151: * @param resourceType resource type string or URL to *.xml file
152: */
153: public XMBean(Object resource, String resourceType)
154: throws MBeanException, NotCompliantMBeanException {
155: // TODO: document STANDARD_MBEAN
156:
157: ModelMBeanInfo minfo = null;
158: try {
159: HashMap properties = new HashMap();
160:
161: if (resourceType.equals(DESCRIPTOR)) {
162: Descriptor d = (Descriptor) resource;
163:
164: // get the actual resource type from the descriptor
165: resourceType = (String) d.getFieldValue(RESOURCE_TYPE);
166:
167: // and the resource reference
168: resource = d.getFieldValue(RESOURCE_REFERENCE);
169:
170: // extract builder configuration fields
171: String[] fields = d.getFieldNames();
172:
173: for (int i = 0; i < fields.length; ++i) {
174: // extract all the fields starting with the METADATA_DESCRIPTOR_PREFIX
175: // prefix to a property map that is passed to the builder implementations
176: if (fields[i]
177: .startsWith(METADATA_DESCRIPTOR_PREFIX))
178: properties.put(fields[i], d
179: .getFieldValue(fields[i]));
180: }
181: }
182:
183: if (resourceType.equals(STANDARD_MBEAN)
184: && resource instanceof StandardMBean)
185: setManagedResource(((StandardMBean) resource)
186: .getImplementation(), resourceType);
187: else
188: setManagedResource(resource, resourceType);
189:
190: // automatically create management operations that the attributes
191: // can map to.
192: final boolean CREATE_ATTRIBUTE_OPERATION_MAPPING = true;
193:
194: // the resource extends StandardMBean
195: if (resourceType.equals(STANDARD_MBEAN)
196: && resource instanceof StandardMBean) {
197: StandardMBean standardMBean = (StandardMBean) resource;
198: minfo = MBeanInfoConversion.toModelMBeanInfo(
199: standardMBean.getMBeanInfo(),
200: CREATE_ATTRIBUTE_OPERATION_MAPPING);
201: }
202:
203: // the resource implements a Standard MBean interface
204: else if ((resourceType.equals(STANDARD_INTERFACE))
205: || (resourceType.equals(STANDARD_MBEAN))) {
206: dynamicResource = false;
207:
208: // create and configure the builder
209: MetaDataBuilder builder = new StandardMetaData(resource);
210:
211: // pass the config keys to the builder instance
212: for (Iterator it = properties.keySet().iterator(); it
213: .hasNext();) {
214: String key = (String) it.next();
215: builder.setProperty(key, properties.get(key));
216: }
217:
218: // build the metadata
219: MBeanInfo standardInfo = builder.build();
220:
221: // StandardMetaData is used by the MBean server to introspect
222: // standard MBeans. We need to now turn that Standard metadata into
223: // ModelMBean metadata (including operation mapping for attributes)
224: minfo = MBeanInfoConversion.toModelMBeanInfo(
225: standardInfo,
226: CREATE_ATTRIBUTE_OPERATION_MAPPING);
227: }
228:
229: // If the resource type string ends with an '.xml' extension attempt
230: // to create the metadata with the aggregated XML builder.
231: else if (resourceType.endsWith(".xml")) {
232: // Create and configure the builder. XMLMetaData builder is an
233: // aggregate builder that picks the correct schema specific builder
234: // based on schema declaration at the beginning of the XML file.
235:
236: MetaDataBuilder builder = new XMLMetaData(this
237: .getClass().getName(), // MMBean implementation name
238: resource.getClass().getName(), // resource class name
239: resourceType);
240:
241: // pass the config keys to the builder instance
242: for (Iterator it = properties.keySet().iterator(); it
243: .hasNext();) {
244: String key = (String) it.next();
245: builder.setProperty(key, properties.get(key));
246: }
247:
248: minfo = (ModelMBeanInfo) builder.build();
249: }
250: // Sotre the ModelMBeanInfo
251: this .setModelMBeanInfo(minfo);
252:
253: // we must try to load this MBean (as the superclass does), even if only NullPersistence
254: // is used - MMM
255: load();
256: } catch (InstanceNotFoundException e) {
257: throw new MBeanException(e);
258: } catch (InvalidTargetObjectTypeException e) {
259: if (resourceType.endsWith(".xml"))
260: throw new MBeanException(e, "Malformed URL: "
261: + resourceType);
262:
263: throw new MBeanException(e, "Unsupported resource type: "
264: + resourceType);
265: } catch (MalformedURLException e) {
266: throw new MBeanException(e, "Malformed URL: "
267: + resourceType);
268: }
269: }
270:
271: public XMBean(Object resource, URL interfaceURL)
272: throws MBeanException, NotCompliantMBeanException {
273: this (resource, interfaceURL.toString());
274: }
275:
276: public XMBean(Descriptor descriptor) throws MBeanException,
277: NotCompliantMBeanException {
278: this (descriptor, DESCRIPTOR);
279: }
280:
281: public XMBean(Object resource, org.w3c.dom.Element element,
282: String version) throws MBeanException,
283: NotCompliantMBeanException {
284: try {
285: DocumentBuilder builder = DocumentBuilderFactory
286: .newInstance().newDocumentBuilder();
287: org.w3c.dom.Document doc = builder.newDocument();
288: doc.appendChild(doc.importNode(element, true));
289:
290: org.jboss.dom4j.io.DOMReader domReader = new org.jboss.dom4j.io.DOMReader();
291: org.jboss.dom4j.Document dom4jDoc = domReader.read(doc);
292: org.jboss.dom4j.Element dom4jElem = dom4jDoc
293: .getRootElement();
294: dom4jElem.detach();
295: createXMBean(resource, dom4jElem, version);
296: } catch (ParserConfigurationException e) {
297: throw new MBeanException(e,
298: "Could not convert w3c Element to dom4j Element.");
299: }
300:
301: }
302:
303: public XMBean(Object resource, org.jboss.dom4j.Element element,
304: String version) throws MBeanException,
305: NotCompliantMBeanException {
306: // this(resource, OBJECT_REF);
307: createXMBean(resource, element, version);
308:
309: }
310:
311: private void createXMBean(Object resource,
312: org.jboss.dom4j.Element element, String version)
313: throws MBeanException, NotCompliantMBeanException {
314: try {
315: setManagedResource(resource, OBJECT_REF);
316: MetaDataBuilder builder = new XMLMetaData(this .getClass()
317: .getName(), // MMBean implementation name
318: resource.getClass().getName(), // resource class name
319: element, version);
320:
321: ModelMBeanInfo minfo = (ModelMBeanInfo) builder.build();
322: this .setModelMBeanInfo(minfo);
323: } catch (InstanceNotFoundException e) {
324: throw new MBeanException(e);
325: } catch (InvalidTargetObjectTypeException e) {
326: throw new MBeanException(e, "Unsupported resource type: "
327: + resourceType);
328: }
329:
330: }
331:
332: // Public --------------------------------------------------------
333:
334: public boolean isSupportedResourceType(Object resource,
335: String resourceType) {
336: if (resourceType == null)
337: return false;
338:
339: if (resourceType.equalsIgnoreCase(OBJECT_REF))
340: return true;
341: if (resourceType.equalsIgnoreCase(STANDARD_INTERFACE))
342: return true;
343: if (resourceType.equalsIgnoreCase(STANDARD_MBEAN))
344: return true;
345: if (resourceType.equalsIgnoreCase(DESCRIPTOR)) {
346: if (resource == null || !(resource instanceof Descriptor))
347: return false;
348:
349: Descriptor d = (Descriptor) resource;
350:
351: if (d.getFieldValue(RESOURCE_REFERENCE) == null)
352: return false;
353:
354: if (d.getFieldValue(RESOURCE_TYPE) == null)
355: return false;
356:
357: return true;
358: }
359: if (resourceType.endsWith(".xml")) {
360: try {
361: new URL(resourceType);
362: return true;
363: } catch (MalformedURLException e) {
364: return false;
365: }
366: }
367:
368: return false;
369: }
370:
371: // ModelMBeanInvoker overrides -----------------------------------
372:
373: protected void configureInterceptorStack(ModelMBeanInfo info,
374: MBeanServer server, ObjectName name) throws Exception {
375: // FIXME: do not require super calls
376:
377: super .configureInterceptorStack(info, server, name);
378:
379: if (resourceType.equals(STANDARD_MBEAN)) {
380: List interceptors = getMBeanInfoCtx.getInterceptors();
381: interceptors.add(0, new StandardMBeanInfoInterceptor());
382: getMBeanInfoCtx.setInterceptors(interceptors);
383: }
384: }
385:
386: // NotificationBroadcaster overrides -----------------------------
387:
388: // TODO: intercept these... (?) rather than do this overriding
389:
390: public void addNotificationListener(NotificationListener listener,
391: NotificationFilter filter, Object handback) {
392: // a standard mbean handles broadcasting itself (if a broadcaster)
393: if (resourceType.equals(STANDARD_MBEAN)) {
394: addNotificationListenerToResource(listener, filter,
395: handback);
396: } else {
397: // for all other types register a listener for AVCs
398: // (including XMBeans wrapping POJOs or standard mbean impls)
399: super .addNotificationListener(listener, filter, handback);
400:
401: // in addition if the resource is a broadcaster update its subscription list
402: if (getResource() instanceof NotificationBroadcaster)
403: addNotificationListenerToResource(listener, filter,
404: handback);
405: }
406: }
407:
408: public void removeNotificationListener(NotificationListener listener)
409: throws ListenerNotFoundException {
410: // a standard mbean handles broadcasting itself (if a broadcaster)
411: if (resourceType.equals(STANDARD_MBEAN)) {
412: removeNotificationListenerFromResource(listener);
413: } else {
414: // for all other types remove the listener for AVCs
415: // (including XMBeans wrapping POJOs or standard mbean impls)
416: super .removeNotificationListener(listener);
417:
418: // in addition if the resource is a broadcaster update its subscription list
419: if (getResource() instanceof NotificationBroadcaster)
420: removeNotificationListenerFromResource(listener);
421: }
422: }
423:
424: public void removeNotificationListener(
425: NotificationListener listener, NotificationFilter filter,
426: Object handback) throws ListenerNotFoundException {
427: // a standard mbean handles broadcasting itself (if a broadcaster)
428: if (resourceType.equals(STANDARD_MBEAN)) {
429: removeNotificationListenerFromResource(listener, filter,
430: handback);
431: } else {
432: // for all other types remove the listener for AVCs
433: // (including XMBeans wrapping POJOs or standard mbean impls)
434: super
435: .removeNotificationListener(listener, filter,
436: handback);
437:
438: // in addition if the resource is a broadcaster update its subscription list
439: if (getResource() instanceof NotificationBroadcaster)
440: removeNotificationListenerFromResource(listener,
441: filter, handback);
442: }
443: }
444:
445: public MBeanNotificationInfo[] getNotificationInfo() {
446: if (resourceType.equals(STANDARD_MBEAN))
447: return getNotificationInfoFromResource();
448: else
449: return super .getNotificationInfo();
450: }
451:
452: // NotificationListener overrides --------------------------------
453:
454: /**
455: * Implements NotificationListener interface by simply forwarding
456: * any received Notification to the wrapped resource, if it
457: * implements the NotificationListener interface, too.
458: *
459: * This is needed to allow the wrapped resource to register for
460: * Notifications using the XMBean ObjectName, rather than its own
461: * "this" reference - dimitris
462: */
463: public void handleNotification(Notification notification,
464: Object handback) {
465: Object resource = getResource();
466:
467: if (resource instanceof NotificationListener)
468: ((NotificationListener) resource).handleNotification(
469: notification, handback);
470: }
471:
472: }
|