1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jboss.mx.capability;
23
24 import org.jboss.mx.metadata.AttributeOperationResolver;
25
26 import javax.management.Attribute;
27 import javax.management.AttributeList;
28 import javax.management.AttributeNotFoundException;
29 import javax.management.DynamicMBean;
30 import javax.management.InvalidAttributeValueException;
31 import javax.management.MBeanAttributeInfo;
32 import javax.management.MBeanConstructorInfo;
33 import javax.management.MBeanException;
34 import javax.management.MBeanInfo;
35 import javax.management.MBeanNotificationInfo;
36 import javax.management.MBeanOperationInfo;
37 import javax.management.NotificationBroadcaster;
38 import javax.management.ReflectionException;
39 import javax.management.RuntimeErrorException;
40 import javax.management.RuntimeMBeanException;
41 import javax.management.RuntimeOperationsException;
42 import java.lang.reflect.InvocationTargetException;
43 import java.lang.reflect.Method;
44 import java.util.Iterator;
45
46 /**
47 * Directs DynamicMBean calls to underlying resource via reflection. It's suitable
48 * for use as a StandardMBean or as the resource for a ModelMBean.
49 *
50 * @author <a href="mailto:trevor@protocool.com">Trevor Squires</a>.
51 * @author <a href="mailto:juha@jboss.org">Juha Lindfors</a>
52 */
53 public class ReflectedMBeanDispatcher implements DynamicMBean
54 {
55 private Object resource = null;
56
57 private AttributeOperationResolver resolver = null;
58 private MBeanConstructorInfo[] constructorInfo = null;
59 private MBeanAttributeInfo[] attributeInfo = null;
60 private MBeanOperationInfo[] operationInfo = null;
61
62 private Method[] operations = null;
63 private MethodPair[] attributes = null;
64
65 private boolean isBroadcaster = false;
66
67 private String resourceClassName = null;
68 private String resourceDescription = null;
69
70 public ReflectedMBeanDispatcher(MBeanInfo mbinfo, AttributeOperationResolver resolver, Object resource)
71 {
72 if (null == resource)
73 {
74 throw new IllegalArgumentException("resource cannot be null");
75 }
76
77 if (null == mbinfo)
78 {
79 throw new IllegalArgumentException("MBeanInfo cannot be null");
80 }
81
82 if (null == resolver)
83 {
84 throw new IllegalArgumentException("AOresolver cannot be null");
85 }
86
87
88 if (resource instanceof NotificationBroadcaster)
89 {
90 this.isBroadcaster = true;
91 }
92
93 this.resource = resource;
94 this.resolver = resolver;
95 this.resourceClassName = mbinfo.getClassName();
96 this.resourceDescription = mbinfo.getDescription();
97 this.constructorInfo = mbinfo.getConstructors();
98 this.attributeInfo = mbinfo.getAttributes();
99 this.operationInfo = mbinfo.getOperations();
100
101 this.operations = new Method[operationInfo.length];
102 this.attributes = new MethodPair[attributeInfo.length];
103 }
104
105 public void bindOperationAt(int position, Method method)
106 {
107 try
108 {
109 operations[position] = method;
110 }
111 catch (ArrayIndexOutOfBoundsException e)
112 {
113 throw new IllegalArgumentException("position out of bounds: " + position);
114 }
115 }
116
117 public void bindAttributeAt(int position, Method getter, Method setter)
118 {
119 try
120 {
121 attributes[position] = new MethodPair(getter, setter);
122 }
123 catch (ArrayIndexOutOfBoundsException e)
124 {
125 throw new IllegalArgumentException("position out of bounds: " + position);
126 }
127 }
128
129 public String getResourceClassName()
130 {
131 return resourceClassName;
132 }
133
134 public Object getAttribute(String attribute)
135 throws AttributeNotFoundException, MBeanException, ReflectionException
136 {
137 if (null == attribute)
138 {
139 throw new RuntimeOperationsException(new IllegalArgumentException("attribute cannot be null"));
140 }
141
142 Method m = null;
143 try
144 {
145 m = attributes[resolver.lookup(attribute).intValue()].getter;
146 return m.invoke(resource, new Object[0]);
147 }
148 catch (NullPointerException e)
149 {
150 throw new AttributeNotFoundException("Readable attribute '" + attribute + "' not found");
151 }
152 catch (ArrayIndexOutOfBoundsException e)
153 {
154 throw new AttributeNotFoundException("Readable attribute '" + attribute + "' not found");
155 }
156 catch (InvocationTargetException e)
157 {
158 Throwable t = e.getTargetException();
159 if (t instanceof RuntimeException)
160 {
161 throw new RuntimeMBeanException((RuntimeException) t, "RuntimeException in MBean when getting attribute '" + attribute + "'");
162 }
163 else if (t instanceof Exception)
164 {
165 throw new MBeanException((Exception) t, "Exception in MBean when getting attribute '" + attribute + "'");
166 }
167 else // it's an error
168 {
169 throw new RuntimeErrorException((Error) t, "Error in MBean when getting attribute '" + attribute + "'");
170 }
171 }
172 catch (IllegalArgumentException e)
173 {
174 throw new AttributeNotFoundException("Readable attribute '" + attribute + "' not found");
175 }
176 catch (Exception e) // assume all other exceptions are reflection related
177 {
178 throw new ReflectionException(e, "Exception in AttributeProvider for getting '" + attribute + "'");
179 }
180 catch (Error e)
181 {
182 throw new RuntimeErrorException(e, "Error in AttributeProvider for getting '" + attribute + "'");
183 }
184 }
185
186
187 public void setAttribute(Attribute attribute)
188 throws AttributeNotFoundException, InvalidAttributeValueException,
189 MBeanException, ReflectionException
190 {
191 if (null == attribute)
192 {
193 throw new RuntimeOperationsException(new IllegalArgumentException("attribute cannot be null"));
194 }
195
196 Method m = null;
197 try
198 {
199 m = attributes[resolver.lookup(attribute.getName()).intValue()].setter;
200 m.invoke(resource, new Object[]{attribute.getValue()});
201 }
202 catch (NullPointerException e)
203 {
204 throw new AttributeNotFoundException("Writable attribute '" + attribute.getName() + "' not found");
205 }
206 catch (ArrayIndexOutOfBoundsException e)
207 {
208 throw new AttributeNotFoundException("Writable attribute '" + attribute.getName() + "' not found");
209 }
210 catch (InvocationTargetException e)
211 {
212 Throwable t = e.getTargetException();
213 if (t instanceof RuntimeException)
214 {
215 throw new RuntimeMBeanException((RuntimeException) t, "RuntimeException in MBean when setting attribute '" + attribute.getName() + "'");
216 }
217 else if (t instanceof Exception)
218 {
219 throw new MBeanException((Exception) t, "Exception in MBean when setting attribute '" + attribute.getName() + "'");
220 }
221 else // it's an error
222 {
223 throw new RuntimeErrorException((Error) t, "Error in MBean when setting attribute '" + attribute.getName() + "'");
224 }
225 }
226 catch (IllegalArgumentException e)
227 {
228 String valueType = (null == attribute.getValue()) ? "<null value>" : attribute.getValue().getClass().getName();
229 throw new InvalidAttributeValueException("Attribute value mismatch while setting '" + attribute.getName() + "': " + valueType);
230 }
231 catch (Exception e) // assume all other exceptions are reflection related
232 {
233 throw new ReflectionException(e, "Exception in AttributeProvider for setting '" + attribute.getName() + "'");
234 }
235 catch (Error e)
236 {
237 throw new RuntimeErrorException(e, "Error in AttributeProvider for setting '" + attribute.getName() + "'");
238 }
239 }
240
241
242 public AttributeList getAttributes(String[] attributes)
243 {
244 if (null == attributes)
245 {
246 throw new RuntimeOperationsException(new IllegalArgumentException("attributes array cannot be null"));
247 }
248
249 AttributeList list = new AttributeList();
250 for (int i = 0; i < attributes.length; i++)
251 {
252 try
253 {
254 list.add(new Attribute(attributes[i], getAttribute(attributes[i])));
255 }
256 catch (Throwable e)
257 {
258 // QUERY - do we *really* just ignore all problems?
259 }
260 }
261
262 return list;
263 }
264
265 public AttributeList setAttributes(AttributeList attributes)
266 {
267 if (null == attributes)
268 {
269 throw new RuntimeOperationsException(new IllegalArgumentException("attribute list cannot be null"));
270 }
271
272 AttributeList list = new AttributeList();
273 for (Iterator iterator = attributes.iterator(); iterator.hasNext();)
274 {
275 Attribute toSet = (Attribute) iterator.next();
276 try
277 {
278 setAttribute(toSet);
279 list.add(toSet);
280 }
281 catch (Throwable e)
282 {
283 // QUERY - do we *really* just ignore all problems?
284 }
285 }
286 return list;
287 }
288
289 public Object invoke(String actionName,
290 Object[] params,
291 String[] signature)
292 throws MBeanException, ReflectionException
293 {
294 Method m = null;
295 try
296 {
297 m = operations[resolver.lookup(actionName, signature).intValue()];
298 return m.invoke(resource, params);
299 }
300 catch (NullPointerException e)
301 {
302 throw new ReflectionException(new NoSuchMethodException("Unable to locate MBean operation for: " + opKeyString(actionName, signature)));
303 }
304 catch (ArrayIndexOutOfBoundsException e)
305 {
306 throw new ReflectionException(new NoSuchMethodException("Unable to locate MBean operation for: " + opKeyString(actionName, signature)));
307 }
308 catch (InvocationTargetException e)
309 {
310 Throwable t = e.getTargetException();
311 if (t instanceof RuntimeException)
312 {
313 throw new RuntimeMBeanException((RuntimeException) t, "RuntimeException in MBean operation '" + opKeyString(actionName, signature) + "'");
314 }
315 else if (t instanceof Exception)
316 {
317 throw new MBeanException((Exception) t, "Exception in MBean operation '" + opKeyString(actionName, signature) + "'");
318 }
319 else // it's an error
320 {
321 throw new RuntimeErrorException((Error) t, "Error in MBean operation '" + opKeyString(actionName, signature) + "'");
322 }
323 }
324 catch (Exception e) // assume all other exceptions are reflection related
325 {
326 throw new ReflectionException(e, "Exception when calling method for '" + opKeyString(actionName, signature) + "'");
327 }
328 catch (Error e)
329 {
330 throw new RuntimeErrorException(e, "Error when calling method for '" + opKeyString(actionName, signature) + "'");
331 }
332
333 }
334
335 public MBeanInfo getMBeanInfo()
336 {
337 return new MBeanInfo(resourceClassName, resourceDescription,
338 attributeInfo, constructorInfo,
339 operationInfo, (isBroadcaster) ? this.getNotificationInfo() : new MBeanNotificationInfo[0]);
340
341 }
342
343 // Protected -----------------------------------------------------
344 protected MBeanNotificationInfo[] getNotificationInfo()
345 {
346 if (isBroadcaster)
347 {
348 return ((NotificationBroadcaster) resource).getNotificationInfo();
349 }
350 else
351 {
352 throw new RuntimeOperationsException(new UnsupportedOperationException("resource is not a NotificationBroadcaster"));
353 }
354 }
355
356 protected Object getResourceObject()
357 {
358 return resource;
359 }
360
361 // ONLY used for friendly exceptions!
362 protected final String opKeyString(String name, String[] signature)
363 {
364 StringBuffer buf = new StringBuffer(name).append('(');
365 if (null != signature)
366 {
367 for (int i = 0; i < signature.length-1; i++)
368 buf.append(signature[i]).append(',');
369 if (signature.length > 0)
370 buf.append(signature[signature.length-1]);
371 }
372 return buf.append(')').toString();
373 }
374
375 public static class MethodPair
376 {
377 public Method getter = null;
378 public Method setter = null;
379
380 public MethodPair(Method getter, Method setter)
381 {
382 this.getter = getter;
383 this.setter = setter;
384 }
385 }
386 }