001 /*
002 $Id: GroovyMBean.java 3747 2006-04-17 12:56:57Z glaforge $
003
004 Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005
006 Redistribution and use of this software and associated documentation
007 ("Software"), with or without modification, are permitted provided
008 that the following conditions are met:
009
010 1. Redistributions of source code must retain copyright
011 statements and notices. Redistributions must also contain a
012 copy of this document.
013
014 2. Redistributions in binary form must reproduce the
015 above copyright notice, this list of conditions and the
016 following disclaimer in the documentation and/or other
017 materials provided with the distribution.
018
019 3. The name "groovy" must not be used to endorse or promote
020 products derived from this Software without prior written
021 permission of The Codehaus. For written permission,
022 please contact info@codehaus.org.
023
024 4. Products derived from this Software may not be called "groovy"
025 nor may "groovy" appear in their names without prior written
026 permission of The Codehaus. "groovy" is a registered
027 trademark of The Codehaus.
028
029 5. Due credit should be given to The Codehaus -
030 http://groovy.codehaus.org/
031
032 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
036 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043 OF THE POSSIBILITY OF SUCH DAMAGE.
044
045 */
046 package groovy.util;
047
048 import groovy.lang.GroovyObjectSupport;
049 import groovy.lang.GroovyRuntimeException;
050
051 import java.util.HashMap;
052 import java.util.Map;
053 import java.util.Collection;
054 import java.util.ArrayList;
055 import java.util.List;
056 import java.util.Iterator;
057 import java.io.IOException;
058
059 import javax.management.Attribute;
060 import javax.management.JMException;
061 import javax.management.MBeanException;
062 import javax.management.MBeanInfo;
063 import javax.management.MBeanOperationInfo;
064 import javax.management.MBeanParameterInfo;
065 import javax.management.ObjectName;
066 import javax.management.MBeanServerConnection;
067 import javax.management.MBeanAttributeInfo;
068
069
070 /**
071 * A GroovyObject facade for an underlying MBean which acts like a normal
072 * groovy object but which is actually implemented via
073 * an underlying JMX MBean.
074 * Properties and normal method invocations
075 * delegate to the MBeanServer to the actual MBean.
076 *
077 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
078 * @author Steve Button
079 * @version $Revision: 3747 $
080 */
081 public class GroovyMBean extends GroovyObjectSupport {
082
083 private MBeanServerConnection server;
084 private ObjectName name;
085 private MBeanInfo beanInfo;
086 private Map operations = new HashMap();
087
088
089 public GroovyMBean(MBeanServerConnection server, ObjectName name) throws JMException, IOException {
090 this.server = server;
091 this.name = name;
092 this.beanInfo = server.getMBeanInfo(name);
093
094
095 MBeanOperationInfo[] operationInfos = beanInfo.getOperations();
096 for (int i = 0; i < operationInfos.length; i++) {
097 MBeanOperationInfo info = operationInfos[i];
098 String signature[] = createSignature(info);
099
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 }