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.capability;
023:
024: import org.jboss.mx.metadata.AttributeOperationResolver;
025:
026: import javax.management.Attribute;
027: import javax.management.AttributeList;
028: import javax.management.AttributeNotFoundException;
029: import javax.management.DynamicMBean;
030: import javax.management.InvalidAttributeValueException;
031: import javax.management.MBeanAttributeInfo;
032: import javax.management.MBeanConstructorInfo;
033: import javax.management.MBeanException;
034: import javax.management.MBeanInfo;
035: import javax.management.MBeanNotificationInfo;
036: import javax.management.MBeanOperationInfo;
037: import javax.management.NotificationBroadcaster;
038: import javax.management.ReflectionException;
039: import javax.management.RuntimeErrorException;
040: import javax.management.RuntimeMBeanException;
041: import javax.management.RuntimeOperationsException;
042: import java.lang.reflect.InvocationTargetException;
043: import java.lang.reflect.Method;
044: import java.util.Iterator;
045:
046: /**
047: * Directs DynamicMBean calls to underlying resource via reflection. It's suitable
048: * for use as a StandardMBean or as the resource for a ModelMBean.
049: *
050: * @author <a href="mailto:trevor@protocool.com">Trevor Squires</a>.
051: * @author <a href="mailto:juha@jboss.org">Juha Lindfors</a>
052: */
053: public class ReflectedMBeanDispatcher implements DynamicMBean {
054: private Object resource = null;
055:
056: private AttributeOperationResolver resolver = null;
057: private MBeanConstructorInfo[] constructorInfo = null;
058: private MBeanAttributeInfo[] attributeInfo = null;
059: private MBeanOperationInfo[] operationInfo = null;
060:
061: private Method[] operations = null;
062: private MethodPair[] attributes = null;
063:
064: private boolean isBroadcaster = false;
065:
066: private String resourceClassName = null;
067: private String resourceDescription = null;
068:
069: public ReflectedMBeanDispatcher(MBeanInfo mbinfo,
070: AttributeOperationResolver resolver, Object resource) {
071: if (null == resource) {
072: throw new IllegalArgumentException(
073: "resource cannot be null");
074: }
075:
076: if (null == mbinfo) {
077: throw new IllegalArgumentException(
078: "MBeanInfo cannot be null");
079: }
080:
081: if (null == resolver) {
082: throw new IllegalArgumentException(
083: "AOresolver cannot be null");
084: }
085:
086: if (resource instanceof NotificationBroadcaster) {
087: this .isBroadcaster = true;
088: }
089:
090: this .resource = resource;
091: this .resolver = resolver;
092: this .resourceClassName = mbinfo.getClassName();
093: this .resourceDescription = mbinfo.getDescription();
094: this .constructorInfo = mbinfo.getConstructors();
095: this .attributeInfo = mbinfo.getAttributes();
096: this .operationInfo = mbinfo.getOperations();
097:
098: this .operations = new Method[operationInfo.length];
099: this .attributes = new MethodPair[attributeInfo.length];
100: }
101:
102: public void bindOperationAt(int position, Method method) {
103: try {
104: operations[position] = method;
105: } catch (ArrayIndexOutOfBoundsException e) {
106: throw new IllegalArgumentException(
107: "position out of bounds: " + position);
108: }
109: }
110:
111: public void bindAttributeAt(int position, Method getter,
112: Method setter) {
113: try {
114: attributes[position] = new MethodPair(getter, setter);
115: } catch (ArrayIndexOutOfBoundsException e) {
116: throw new IllegalArgumentException(
117: "position out of bounds: " + position);
118: }
119: }
120:
121: public String getResourceClassName() {
122: return resourceClassName;
123: }
124:
125: public Object getAttribute(String attribute)
126: throws AttributeNotFoundException, MBeanException,
127: ReflectionException {
128: if (null == attribute) {
129: throw new RuntimeOperationsException(
130: new IllegalArgumentException(
131: "attribute cannot be null"));
132: }
133:
134: Method m = null;
135: try {
136: m = attributes[resolver.lookup(attribute).intValue()].getter;
137: return m.invoke(resource, new Object[0]);
138: } catch (NullPointerException e) {
139: throw new AttributeNotFoundException("Readable attribute '"
140: + attribute + "' not found");
141: } catch (ArrayIndexOutOfBoundsException e) {
142: throw new AttributeNotFoundException("Readable attribute '"
143: + attribute + "' not found");
144: } catch (InvocationTargetException e) {
145: Throwable t = e.getTargetException();
146: if (t instanceof RuntimeException) {
147: throw new RuntimeMBeanException((RuntimeException) t,
148: "RuntimeException in MBean when getting attribute '"
149: + attribute + "'");
150: } else if (t instanceof Exception) {
151: throw new MBeanException((Exception) t,
152: "Exception in MBean when getting attribute '"
153: + attribute + "'");
154: } else // it's an error
155: {
156: throw new RuntimeErrorException((Error) t,
157: "Error in MBean when getting attribute '"
158: + attribute + "'");
159: }
160: } catch (IllegalArgumentException e) {
161: throw new AttributeNotFoundException("Readable attribute '"
162: + attribute + "' not found");
163: } catch (Exception e) // assume all other exceptions are reflection related
164: {
165: throw new ReflectionException(e,
166: "Exception in AttributeProvider for getting '"
167: + attribute + "'");
168: } catch (Error e) {
169: throw new RuntimeErrorException(e,
170: "Error in AttributeProvider for getting '"
171: + attribute + "'");
172: }
173: }
174:
175: public void setAttribute(Attribute attribute)
176: throws AttributeNotFoundException,
177: InvalidAttributeValueException, MBeanException,
178: ReflectionException {
179: if (null == attribute) {
180: throw new RuntimeOperationsException(
181: new IllegalArgumentException(
182: "attribute cannot be null"));
183: }
184:
185: Method m = null;
186: try {
187: m = attributes[resolver.lookup(attribute.getName())
188: .intValue()].setter;
189: m.invoke(resource, new Object[] { attribute.getValue() });
190: } catch (NullPointerException e) {
191: throw new AttributeNotFoundException("Writable attribute '"
192: + attribute.getName() + "' not found");
193: } catch (ArrayIndexOutOfBoundsException e) {
194: throw new AttributeNotFoundException("Writable attribute '"
195: + attribute.getName() + "' not found");
196: } catch (InvocationTargetException e) {
197: Throwable t = e.getTargetException();
198: if (t instanceof RuntimeException) {
199: throw new RuntimeMBeanException((RuntimeException) t,
200: "RuntimeException in MBean when setting attribute '"
201: + attribute.getName() + "'");
202: } else if (t instanceof Exception) {
203: throw new MBeanException((Exception) t,
204: "Exception in MBean when setting attribute '"
205: + attribute.getName() + "'");
206: } else // it's an error
207: {
208: throw new RuntimeErrorException((Error) t,
209: "Error in MBean when setting attribute '"
210: + attribute.getName() + "'");
211: }
212: } catch (IllegalArgumentException e) {
213: String valueType = (null == attribute.getValue()) ? "<null value>"
214: : attribute.getValue().getClass().getName();
215: throw new InvalidAttributeValueException(
216: "Attribute value mismatch while setting '"
217: + attribute.getName() + "': " + valueType);
218: } catch (Exception e) // assume all other exceptions are reflection related
219: {
220: throw new ReflectionException(e,
221: "Exception in AttributeProvider for setting '"
222: + attribute.getName() + "'");
223: } catch (Error e) {
224: throw new RuntimeErrorException(e,
225: "Error in AttributeProvider for setting '"
226: + attribute.getName() + "'");
227: }
228: }
229:
230: public AttributeList getAttributes(String[] attributes) {
231: if (null == attributes) {
232: throw new RuntimeOperationsException(
233: new IllegalArgumentException(
234: "attributes array cannot be null"));
235: }
236:
237: AttributeList list = new AttributeList();
238: for (int i = 0; i < attributes.length; i++) {
239: try {
240: list.add(new Attribute(attributes[i],
241: getAttribute(attributes[i])));
242: } catch (Throwable e) {
243: // QUERY - do we *really* just ignore all problems?
244: }
245: }
246:
247: return list;
248: }
249:
250: public AttributeList setAttributes(AttributeList attributes) {
251: if (null == attributes) {
252: throw new RuntimeOperationsException(
253: new IllegalArgumentException(
254: "attribute list cannot be null"));
255: }
256:
257: AttributeList list = new AttributeList();
258: for (Iterator iterator = attributes.iterator(); iterator
259: .hasNext();) {
260: Attribute toSet = (Attribute) iterator.next();
261: try {
262: setAttribute(toSet);
263: list.add(toSet);
264: } catch (Throwable e) {
265: // QUERY - do we *really* just ignore all problems?
266: }
267: }
268: return list;
269: }
270:
271: public Object invoke(String actionName, Object[] params,
272: String[] signature) throws MBeanException,
273: ReflectionException {
274: Method m = null;
275: try {
276: m = operations[resolver.lookup(actionName, signature)
277: .intValue()];
278: return m.invoke(resource, params);
279: } catch (NullPointerException e) {
280: throw new ReflectionException(new NoSuchMethodException(
281: "Unable to locate MBean operation for: "
282: + opKeyString(actionName, signature)));
283: } catch (ArrayIndexOutOfBoundsException e) {
284: throw new ReflectionException(new NoSuchMethodException(
285: "Unable to locate MBean operation for: "
286: + opKeyString(actionName, signature)));
287: } catch (InvocationTargetException e) {
288: Throwable t = e.getTargetException();
289: if (t instanceof RuntimeException) {
290: throw new RuntimeMBeanException((RuntimeException) t,
291: "RuntimeException in MBean operation '"
292: + opKeyString(actionName, signature)
293: + "'");
294: } else if (t instanceof Exception) {
295: throw new MBeanException((Exception) t,
296: "Exception in MBean operation '"
297: + opKeyString(actionName, signature)
298: + "'");
299: } else // it's an error
300: {
301: throw new RuntimeErrorException((Error) t,
302: "Error in MBean operation '"
303: + opKeyString(actionName, signature)
304: + "'");
305: }
306: } catch (Exception e) // assume all other exceptions are reflection related
307: {
308: throw new ReflectionException(e,
309: "Exception when calling method for '"
310: + opKeyString(actionName, signature) + "'");
311: } catch (Error e) {
312: throw new RuntimeErrorException(e,
313: "Error when calling method for '"
314: + opKeyString(actionName, signature) + "'");
315: }
316:
317: }
318:
319: public MBeanInfo getMBeanInfo() {
320: return new MBeanInfo(resourceClassName, resourceDescription,
321: attributeInfo, constructorInfo, operationInfo,
322: (isBroadcaster) ? this .getNotificationInfo()
323: : new MBeanNotificationInfo[0]);
324:
325: }
326:
327: // Protected -----------------------------------------------------
328: protected MBeanNotificationInfo[] getNotificationInfo() {
329: if (isBroadcaster) {
330: return ((NotificationBroadcaster) resource)
331: .getNotificationInfo();
332: } else {
333: throw new RuntimeOperationsException(
334: new UnsupportedOperationException(
335: "resource is not a NotificationBroadcaster"));
336: }
337: }
338:
339: protected Object getResourceObject() {
340: return resource;
341: }
342:
343: // ONLY used for friendly exceptions!
344: protected final String opKeyString(String name, String[] signature) {
345: StringBuffer buf = new StringBuffer(name).append('(');
346: if (null != signature) {
347: for (int i = 0; i < signature.length - 1; i++)
348: buf.append(signature[i]).append(',');
349: if (signature.length > 0)
350: buf.append(signature[signature.length - 1]);
351: }
352: return buf.append(')').toString();
353: }
354:
355: public static class MethodPair {
356: public Method getter = null;
357: public Method setter = null;
358:
359: public MethodPair(Method getter, Method setter) {
360: this.getter = getter;
361: this.setter = setter;
362: }
363: }
364: }
|