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 }