View Javadoc

1   /*
2    $Id: GroovyMBean.java,v 1.6 2006/04/17 12:56:57 glaforge Exp $
3   
4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5   
6    Redistribution and use of this software and associated documentation
7    ("Software"), with or without modification, are permitted provided
8    that the following conditions are met:
9   
10   1. Redistributions of source code must retain copyright
11      statements and notices.  Redistributions must also contain a
12      copy of this document.
13  
14   2. Redistributions in binary form must reproduce the
15      above copyright notice, this list of conditions and the
16      following disclaimer in the documentation and/or other
17      materials provided with the distribution.
18  
19   3. The name "groovy" must not be used to endorse or promote
20      products derived from this Software without prior written
21      permission of The Codehaus.  For written permission,
22      please contact info@codehaus.org.
23  
24   4. Products derived from this Software may not be called "groovy"
25      nor may "groovy" appear in their names without prior written
26      permission of The Codehaus. "groovy" is a registered
27      trademark of The Codehaus.
28  
29   5. Due credit should be given to The Codehaus -
30      http://groovy.codehaus.org/
31  
32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43   OF THE POSSIBILITY OF SUCH DAMAGE.
44  
45   */
46  package groovy.util;
47  
48  import groovy.lang.GroovyObjectSupport;
49  import groovy.lang.GroovyRuntimeException;
50  
51  import java.util.HashMap;
52  import java.util.Map;
53  import java.util.Collection;
54  import java.util.ArrayList;
55  import java.util.List;
56  import java.util.Iterator;
57  import java.io.IOException;
58  
59  import javax.management.Attribute;
60  import javax.management.JMException;
61  import javax.management.MBeanException;
62  import javax.management.MBeanInfo;
63  import javax.management.MBeanOperationInfo;
64  import javax.management.MBeanParameterInfo;
65  import javax.management.ObjectName;
66  import javax.management.MBeanServerConnection;
67  import javax.management.MBeanAttributeInfo;
68  
69  
70  /***
71   * A GroovyObject facade for an underlying MBean which acts like a normal
72   * groovy object but which is actually implemented via
73   * an underlying JMX MBean.
74   * Properties and normal method invocations
75   * delegate to the MBeanServer to the actual MBean.
76   *
77   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
78   * @author Steve Button
79   * @version $Revision: 1.6 $
80   */
81  public class GroovyMBean extends GroovyObjectSupport {
82  
83      private MBeanServerConnection server;
84      private ObjectName name;
85      private MBeanInfo beanInfo;
86      private Map operations = new HashMap();
87  
88  
89      public GroovyMBean(MBeanServerConnection server, ObjectName name) throws JMException, IOException {
90          this.server = server;
91          this.name = name;
92          this.beanInfo = server.getMBeanInfo(name);
93  
94  
95          MBeanOperationInfo[] operationInfos = beanInfo.getOperations();
96          for (int i = 0; i < operationInfos.length; i++) {
97              MBeanOperationInfo info = operationInfos[i];
98              String signature[] = createSignature(info);
99  
100             // Include a simple fix here to support overloaded operations on the MBean.
101             // Construct a simple key for an operation by adding the number of parameters it uses
102             String operationKey = createOperationKey(info.getName(), signature.length);
103             operations.put(operationKey, signature);
104         }
105 
106     }
107 
108     public MBeanServerConnection server() {
109         return server;
110     }
111 
112     public ObjectName name() {
113         return name;
114     }
115 
116     public MBeanInfo info() {
117         return beanInfo;
118     }
119 
120     public Object getProperty(String property) {
121         try {
122             return server.getAttribute(name, property);
123         }
124         catch (MBeanException e) {
125             throw new GroovyRuntimeException("Could not access property: " + property + ". Reason: " + e, e.getTargetException());
126         }
127         catch (Exception e) {
128             throw new GroovyRuntimeException("Could not access property: " + property + ". Reason: " + e, e);
129         }
130     }
131 
132     public void setProperty(String property, Object value) {
133         try {
134             server.setAttribute(name, new Attribute(property, value));
135         }
136         catch (MBeanException e) {
137             throw new GroovyRuntimeException("Could not set property: " + property + ". Reason: " + e, e.getTargetException());
138         }
139         catch (Exception e) {
140             throw new GroovyRuntimeException("Could not set property: " + property + ". Reason: " + e, e);
141         }
142     }
143 
144     public Object invokeMethod(String method, Object arguments) {
145         // Moved this outside the try block so we can obtain the number of parameters
146         // specified in the arguments array, which is needed to find the correct method.
147         Object[] argArray = null;
148         if (arguments instanceof Object[]) {
149             argArray = (Object[]) arguments;
150         } else {
151             argArray = new Object[]{arguments};
152         }
153         // Locate the specific method based on the name and number of parameters
154         String operationKey = createOperationKey(method, argArray.length);
155         String[] signature = (String[]) operations.get(operationKey);
156 
157         if (signature != null) {
158             try {
159                 return server.invoke(name, method, argArray, signature);
160             }
161             catch (MBeanException e) {
162                 throw new GroovyRuntimeException("Could not invoke method: " + method + ". Reason: " + e, e.getTargetException());
163             }
164             catch (Exception e) {
165                 throw new GroovyRuntimeException("Could not invoke method: " + method + ". Reason: " + e, e);
166             }
167         } else {
168             return super.invokeMethod(method, arguments);
169         }
170     }
171 
172     protected String[] createSignature(MBeanOperationInfo info) {
173         MBeanParameterInfo[] params = info.getSignature();
174         String[] answer = new String[params.length];
175         for (int i = 0; i < params.length; i++) {
176             answer[i] = params[i].getType();
177         }
178         return answer;
179     }
180 
181     /***
182      * Construct a simple key based on the method name and the number of parameters
183      *
184      * @param operation - the mbean operation name
185      * @param params    - the number of parameters the operation supports
186      * @return simple unique identifier for a method
187      */
188     protected String createOperationKey(String operation, int params) {
189         // This could be changed to support some hash of the parameter types, etc.
190         return operation + "_" + params;
191     }
192 
193     /***
194      * List of the names of each of the attributes on the MBean
195      *
196      * @return list of attribute names
197      */
198     public Collection listAttributeNames() {
199         ArrayList list = new ArrayList();
200         try {
201             MBeanAttributeInfo[] attrs = beanInfo.getAttributes();
202             for (int i = 0; i < attrs.length; i++) {
203                 MBeanAttributeInfo attr = attrs[i];
204                 list.add(attr.getName());
205             }
206         }
207         catch (Throwable t) {
208         }
209         finally {
210         }
211         return list;
212     }
213 
214     /***
215      * The values of each of the attributes on the MBean
216      *
217      * @return list of values of each attribute
218      */
219     public List listAttributeValues() {
220         ArrayList list = new ArrayList();
221         Collection names = listAttributeNames();
222         for (Iterator iterator = names.iterator(); iterator.hasNext();) {
223             String name = (String) iterator.next();
224             try {
225                 Object val = this.getProperty(name);
226                 if (val != null) {
227                     list.add(name + " : " + val.toString());
228                 }
229             }
230             catch (RuntimeException e) {
231                 // todo: fix this behaviour properly
232                 // Do nothing here, just handle the error silently.
233                 //e.printStackTrace();
234             }
235         }
236         return list;
237     }
238 
239 
240     /***
241      * List of string representations of all of the attributes on the MBean.
242      *
243      * @return list of descriptions of each attribute on the mbean
244      */
245     public Collection listAttributeDescriptions() {
246         ArrayList list = new ArrayList();
247         try {
248             MBeanAttributeInfo[] attrs = beanInfo.getAttributes();
249             for (int i = 0; i < attrs.length; i++) {
250                 MBeanAttributeInfo attr = attrs[i];
251                 list.add(describeAttribute(attr));
252             }
253         }
254         catch (Throwable t) {
255         }
256         finally {
257         }
258         return list;
259     }
260 
261     /***
262      * Description of the specified attribute name.
263      *
264      * @param attr - the attribute
265      * @return String the description
266      */
267     protected String describeAttribute(MBeanAttributeInfo attr) {
268         StringBuffer buf = new StringBuffer();
269         buf.append("(");
270         if (attr.isReadable()) {
271             buf.append("r");
272         }
273         if (attr.isWritable()) {
274             buf.append("w");
275         }
276         buf.append(") ")
277                 .append(attr.getType())
278                 .append(" ")
279                 .append(attr.getName());
280         return buf.toString();
281     }
282 
283     /***
284      * Description of the specified attribute name.
285      *
286      * @param attributeName - stringified name of the attribute
287      * @return the description
288      */
289     public String describeAttribute(String attributeName) {
290         String ret = "Attribute not found";
291         try {
292             MBeanAttributeInfo[] attributes = beanInfo.getAttributes();
293             for (int i = 0; i < attributes.length; i++) {
294                 MBeanAttributeInfo attribute = attributes[i];
295                 if (attribute.getName().equals(attributeName)) {
296                     return describeAttribute(attribute);
297                 }
298             }
299         }
300         catch (Throwable t) {
301         }
302         return ret;
303     }
304 
305     /***
306      * Names of all the operations available on the MBean.
307      *
308      * @return all the operations on the MBean
309      */
310     public Collection listOperationNames() {
311         ArrayList list = new ArrayList();
312         try {
313             MBeanOperationInfo[] operations = beanInfo.getOperations();
314             for (int i = 0; i < operations.length; i++) {
315                 MBeanOperationInfo operation = operations[i];
316                 list.add(operation.getName());
317             }
318         }
319         catch (Throwable t) {
320         }
321         return list;
322     }
323 
324 
325     /***
326      * Description of all of the operations available on the MBean.
327      *
328      * @return full description of each operation on the MBean
329      */
330     public Collection listOperationDescriptions() {
331         ArrayList list = new ArrayList();
332         try {
333             MBeanOperationInfo[] operations = beanInfo.getOperations();
334             for (int i = 0; i < operations.length; i++) {
335                 MBeanOperationInfo operation = operations[i];
336                 list.add(describeOperation(operation));
337             }
338         }
339         catch (Throwable t) {
340         }
341         return list;
342     }
343 
344     /***
345      * Get the dessciptions of the named operation.  This returns a Collection since
346      * operations can be overloaded and one operationName can have multiple forms.
347      *
348      * @param operationName
349      * @return Collection of operation description
350      */
351     public List describeOperation(String operationName) {
352         ArrayList list = new ArrayList();
353         try {
354             MBeanOperationInfo[] operations = beanInfo.getOperations();
355             for (int i = 0; i < operations.length; i++) {
356                 MBeanOperationInfo operation = operations[i];
357                 if (operation.getName().equals(operationName)) {
358                     list.add(describeOperation(operation));
359                 }
360             }
361         }
362         catch (Throwable t) {
363         }
364         return list;
365     }
366 
367     /***
368      * Dessciption of the named operation.
369      *
370      * @param operation
371      * @return description
372      */
373     protected String describeOperation(MBeanOperationInfo operation) {
374         StringBuffer buf = new StringBuffer();
375         buf.append(operation.getReturnType())
376                 .append(" ")
377                 .append(operation.getName())
378                 .append("(");
379 
380         MBeanParameterInfo[] params = operation.getSignature();
381         for (int j = 0; j < params.length; j++) {
382             MBeanParameterInfo param = params[j];
383             if (j != 0) {
384                 buf.append(", ");
385             }
386             buf.append(param.getType())
387                     .append(" ")
388                     .append(param.getName());
389         }
390         buf.append(")");
391         return buf.toString();
392     }
393 
394 
395     /***
396      * Return an end user readable representation of the underlying MBean
397      * @return the user readable description
398      */
399     public String toString() {
400         StringBuffer buf = new StringBuffer();
401         buf.append("MBean Name:")
402                 .append("\n  ")
403                 .append(name.getCanonicalName())
404                 .append("\n  ");
405         if (!listAttributeDescriptions().isEmpty()) {
406             buf.append("\nAttributes:");
407             for (Iterator iterator = listAttributeDescriptions().iterator(); iterator.hasNext();) {
408                 buf.append("\n  ")
409                         .append((String) iterator.next());
410             }
411         }
412         if (!listOperationDescriptions().isEmpty()) {
413             buf.append("\nOperations:");
414             for (Iterator iterator = listOperationDescriptions().iterator(); iterator.hasNext();) {
415                 buf.append("\n  ")
416                         .append((String) iterator.next());
417             }
418         }
419         return buf.toString();
420   }
421 }