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: * A GroovyObject facade for an underlying MBean which acts like a normal
071: * groovy object but which is actually implemented via
072: * an underlying JMX MBean.
073: * Properties and normal method invocations
074: * delegate to the MBeanServer to the actual MBean.
075: *
076: * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
077: * @author Steve Button
078: * @version $Revision: 3747 $
079: */
080: public class GroovyMBean extends GroovyObjectSupport {
081:
082: private MBeanServerConnection server;
083: private ObjectName name;
084: private MBeanInfo beanInfo;
085: private Map operations = new HashMap();
086:
087: public GroovyMBean(MBeanServerConnection server, ObjectName name)
088: throws JMException, IOException {
089: this .server = server;
090: this .name = name;
091: this .beanInfo = server.getMBeanInfo(name);
092:
093: MBeanOperationInfo[] operationInfos = beanInfo.getOperations();
094: for (int i = 0; i < operationInfos.length; i++) {
095: MBeanOperationInfo info = operationInfos[i];
096: String signature[] = createSignature(info);
097:
098: // Include a simple fix here to support overloaded operations on the MBean.
099: // Construct a simple key for an operation by adding the number of parameters it uses
100: String operationKey = createOperationKey(info.getName(),
101: signature.length);
102: operations.put(operationKey, signature);
103: }
104:
105: }
106:
107: public MBeanServerConnection server() {
108: return server;
109: }
110:
111: public ObjectName name() {
112: return name;
113: }
114:
115: public MBeanInfo info() {
116: return beanInfo;
117: }
118:
119: public Object getProperty(String property) {
120: try {
121: return server.getAttribute(name, property);
122: } catch (MBeanException e) {
123: throw new GroovyRuntimeException(
124: "Could not access property: " + property
125: + ". Reason: " + e, e.getTargetException());
126: } catch (Exception e) {
127: throw new GroovyRuntimeException(
128: "Could not access property: " + property
129: + ". Reason: " + e, e);
130: }
131: }
132:
133: public void setProperty(String property, Object value) {
134: try {
135: server.setAttribute(name, new Attribute(property, value));
136: } catch (MBeanException e) {
137: throw new GroovyRuntimeException("Could not set property: "
138: + property + ". Reason: " + e, e
139: .getTargetException());
140: } catch (Exception e) {
141: throw new GroovyRuntimeException("Could not set property: "
142: + property + ". Reason: " + e, e);
143: }
144: }
145:
146: public Object invokeMethod(String method, Object arguments) {
147: // Moved this outside the try block so we can obtain the number of parameters
148: // specified in the arguments array, which is needed to find the correct method.
149: Object[] argArray = null;
150: if (arguments instanceof Object[]) {
151: argArray = (Object[]) arguments;
152: } else {
153: argArray = new Object[] { arguments };
154: }
155: // Locate the specific method based on the name and number of parameters
156: String operationKey = createOperationKey(method,
157: argArray.length);
158: String[] signature = (String[]) operations.get(operationKey);
159:
160: if (signature != null) {
161: try {
162: return server.invoke(name, method, argArray, signature);
163: } catch (MBeanException e) {
164: throw new GroovyRuntimeException(
165: "Could not invoke method: " + method
166: + ". Reason: " + e, e
167: .getTargetException());
168: } catch (Exception e) {
169: throw new GroovyRuntimeException(
170: "Could not invoke method: " + method
171: + ". Reason: " + e, e);
172: }
173: } else {
174: return super .invokeMethod(method, arguments);
175: }
176: }
177:
178: protected String[] createSignature(MBeanOperationInfo info) {
179: MBeanParameterInfo[] params = info.getSignature();
180: String[] answer = new String[params.length];
181: for (int i = 0; i < params.length; i++) {
182: answer[i] = params[i].getType();
183: }
184: return answer;
185: }
186:
187: /**
188: * Construct a simple key based on the method name and the number of parameters
189: *
190: * @param operation - the mbean operation name
191: * @param params - the number of parameters the operation supports
192: * @return simple unique identifier for a method
193: */
194: protected String createOperationKey(String operation, int params) {
195: // This could be changed to support some hash of the parameter types, etc.
196: return operation + "_" + params;
197: }
198:
199: /**
200: * List of the names of each of the attributes on the MBean
201: *
202: * @return list of attribute names
203: */
204: public Collection listAttributeNames() {
205: ArrayList list = new ArrayList();
206: try {
207: MBeanAttributeInfo[] attrs = beanInfo.getAttributes();
208: for (int i = 0; i < attrs.length; i++) {
209: MBeanAttributeInfo attr = attrs[i];
210: list.add(attr.getName());
211: }
212: } catch (Throwable t) {
213: } finally {
214: }
215: return list;
216: }
217:
218: /**
219: * The values of each of the attributes on the MBean
220: *
221: * @return list of values of each attribute
222: */
223: public List listAttributeValues() {
224: ArrayList list = new ArrayList();
225: Collection names = listAttributeNames();
226: for (Iterator iterator = names.iterator(); iterator.hasNext();) {
227: String name = (String) iterator.next();
228: try {
229: Object val = this .getProperty(name);
230: if (val != null) {
231: list.add(name + " : " + val.toString());
232: }
233: } catch (RuntimeException e) {
234: // todo: fix this behaviour properly
235: // Do nothing here, just handle the error silently.
236: //e.printStackTrace();
237: }
238: }
239: return list;
240: }
241:
242: /**
243: * List of string representations of all of the attributes on the MBean.
244: *
245: * @return list of descriptions of each attribute on the mbean
246: */
247: public Collection listAttributeDescriptions() {
248: ArrayList list = new ArrayList();
249: try {
250: MBeanAttributeInfo[] attrs = beanInfo.getAttributes();
251: for (int i = 0; i < attrs.length; i++) {
252: MBeanAttributeInfo attr = attrs[i];
253: list.add(describeAttribute(attr));
254: }
255: } catch (Throwable t) {
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(") ").append(attr.getType()).append(" ").append(
277: attr.getName());
278: return buf.toString();
279: }
280:
281: /**
282: * Description of the specified attribute name.
283: *
284: * @param attributeName - stringified name of the attribute
285: * @return the description
286: */
287: public String describeAttribute(String attributeName) {
288: String ret = "Attribute not found";
289: try {
290: MBeanAttributeInfo[] attributes = beanInfo.getAttributes();
291: for (int i = 0; i < attributes.length; i++) {
292: MBeanAttributeInfo attribute = attributes[i];
293: if (attribute.getName().equals(attributeName)) {
294: return describeAttribute(attribute);
295: }
296: }
297: } catch (Throwable t) {
298: }
299: return ret;
300: }
301:
302: /**
303: * Names of all the operations available on the MBean.
304: *
305: * @return all the operations on the MBean
306: */
307: public Collection listOperationNames() {
308: ArrayList list = new ArrayList();
309: try {
310: MBeanOperationInfo[] operations = beanInfo.getOperations();
311: for (int i = 0; i < operations.length; i++) {
312: MBeanOperationInfo operation = operations[i];
313: list.add(operation.getName());
314: }
315: } catch (Throwable t) {
316: }
317: return list;
318: }
319:
320: /**
321: * Description of all of the operations available on the MBean.
322: *
323: * @return full description of each operation on the MBean
324: */
325: public Collection listOperationDescriptions() {
326: ArrayList list = new ArrayList();
327: try {
328: MBeanOperationInfo[] operations = beanInfo.getOperations();
329: for (int i = 0; i < operations.length; i++) {
330: MBeanOperationInfo operation = operations[i];
331: list.add(describeOperation(operation));
332: }
333: } catch (Throwable t) {
334: }
335: return list;
336: }
337:
338: /**
339: * Get the dessciptions of the named operation. This returns a Collection since
340: * operations can be overloaded and one operationName can have multiple forms.
341: *
342: * @param operationName
343: * @return Collection of operation description
344: */
345: public List describeOperation(String operationName) {
346: ArrayList list = new ArrayList();
347: try {
348: MBeanOperationInfo[] operations = beanInfo.getOperations();
349: for (int i = 0; i < operations.length; i++) {
350: MBeanOperationInfo operation = operations[i];
351: if (operation.getName().equals(operationName)) {
352: list.add(describeOperation(operation));
353: }
354: }
355: } catch (Throwable t) {
356: }
357: return list;
358: }
359:
360: /**
361: * Dessciption of the named operation.
362: *
363: * @param operation
364: * @return description
365: */
366: protected String describeOperation(MBeanOperationInfo operation) {
367: StringBuffer buf = new StringBuffer();
368: buf.append(operation.getReturnType()).append(" ").append(
369: operation.getName()).append("(");
370:
371: MBeanParameterInfo[] params = operation.getSignature();
372: for (int j = 0; j < params.length; j++) {
373: MBeanParameterInfo param = params[j];
374: if (j != 0) {
375: buf.append(", ");
376: }
377: buf.append(param.getType()).append(" ").append(
378: param.getName());
379: }
380: buf.append(")");
381: return buf.toString();
382: }
383:
384: /**
385: * Return an end user readable representation of the underlying MBean
386: * @return the user readable description
387: */
388: public String toString() {
389: StringBuffer buf = new StringBuffer();
390: buf.append("MBean Name:").append("\n ").append(
391: name.getCanonicalName()).append("\n ");
392: if (!listAttributeDescriptions().isEmpty()) {
393: buf.append("\nAttributes:");
394: for (Iterator iterator = listAttributeDescriptions()
395: .iterator(); iterator.hasNext();) {
396: buf.append("\n ").append((String) iterator.next());
397: }
398: }
399: if (!listOperationDescriptions().isEmpty()) {
400: buf.append("\nOperations:");
401: for (Iterator iterator = listOperationDescriptions()
402: .iterator(); iterator.hasNext();) {
403: buf.append("\n ").append((String) iterator.next());
404: }
405: }
406: return buf.toString();
407: }
408: }
|