001: /*
002: JSPWiki - a JSP-based WikiWiki clone.
003:
004: Copyright (C) 2001-2002 Janne Jalkanen (Janne.Jalkanen@iki.fi)
005:
006: This program is free software; you can redistribute it and/or modify
007: it under the terms of the GNU Lesser General Public License as published by
008: the Free Software Foundation; either version 2.1 of the License, or
009: (at your option) any later version.
010:
011: This program is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public License
017: along with this program; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package com.ecyrd.management;
021:
022: import java.lang.reflect.InvocationTargetException;
023: import java.lang.reflect.Method;
024: import java.util.Iterator;
025:
026: import javax.management.*;
027:
028: import org.apache.commons.lang.StringUtils;
029:
030: /**
031: * A simple MBean which does not require an interface class unlike
032: * the StandardMBean class. The methods are exposed through a method
033: * call, which in turn then calls the methods using the Reflection API.
034: * <p>
035: * This class is similar to the javax.management.StandardMBean, but it does
036: * require the API interface to be declared, so it's simpler. It's not as
037: * powerful, but it does not require you to declare two classes (and keep
038: * them in sync).
039: *
040: * @author Janne Jalkanen
041: * @since 2.6
042: */
043: // FIXME: Exception handling is not probably according to spec...
044: public abstract class SimpleMBean implements DynamicMBean {
045: protected MBeanInfo m_beanInfo;
046:
047: private static Method findGetterSetter(Class clazz, String name,
048: Class parm) {
049: try {
050: Class[] params = { parm };
051: Class[] emptyparms = {};
052:
053: Method m = clazz.getDeclaredMethod(name,
054: parm != null ? params : emptyparms);
055:
056: return m;
057: } catch (Exception e) {
058: // There's nothing to do, really - we just return a null.
059: }
060:
061: return null;
062: }
063:
064: protected SimpleMBean() throws NotCompliantMBeanException {
065: //
066: // Create attributes
067: //
068: String[] attlist = getAttributeNames();
069: MBeanAttributeInfo[] attributes = null;
070:
071: if (attlist != null) {
072: attributes = new MBeanAttributeInfo[attlist.length];
073:
074: for (int i = 0; i < attlist.length; i++) {
075: String name = attlist[i];
076: name = StringUtils.capitalize(name);
077: Method getter = findGetterSetter(getClass(), "get"
078: + name, null);
079:
080: if (getter == null)
081: getter = findGetterSetter(getClass(), "is" + name,
082: null);
083:
084: Method setter = null;
085:
086: if (getter != null) {
087: setter = findGetterSetter(getClass(), "set" + name,
088: getter.getReturnType());
089: }
090:
091: //
092: // Check, if there's a description available
093: //
094: Method descriptor = findGetterSetter(getClass(), "get"
095: + name + "Description", null);
096: String description = "";
097:
098: if (descriptor != null) {
099: try {
100: description = (String) descriptor.invoke(this ,
101: (Object[]) null);
102: } catch (Exception e) {
103: description = "Exception: " + e.getMessage();
104: }
105: }
106:
107: MBeanAttributeInfo info;
108: try {
109: info = new MBeanAttributeInfo(attlist[i],
110: description, getter, setter);
111: } catch (IntrospectionException e) {
112: throw new NotCompliantMBeanException(e.getMessage());
113: }
114:
115: attributes[i] = info;
116: }
117: }
118:
119: //
120: // Create operations.
121: //
122: String[] oplist = getMethodNames();
123: MBeanOperationInfo[] operations = new MBeanOperationInfo[oplist.length];
124:
125: Method[] methods = getClass().getMethods();
126:
127: for (int i = 0; i < oplist.length; i++) {
128: Method method = null;
129:
130: for (int m = 0; m < methods.length; m++) {
131: if (methods[m].getName().equals(oplist[i])) {
132: method = methods[m];
133: }
134: }
135:
136: if (method == null) {
137: throw new NotCompliantMBeanException(
138: "Class declares method " + oplist[i]
139: + ", yet does not implement it!");
140: }
141:
142: MBeanOperationInfo info = new MBeanOperationInfo(method
143: .getName(), method);
144:
145: operations[i] = info;
146: }
147:
148: //
149: // Create the actual BeanInfo instance.
150: //
151: MBeanConstructorInfo[] constructors = null;
152: MBeanNotificationInfo[] notifications = null;
153:
154: m_beanInfo = new MBeanInfo(getClass().getName(),
155: getDescription(), attributes, constructors, operations,
156: notifications);
157: }
158:
159: /**
160: * Customization hook: Override this to get a description for your MBean. By default,
161: * this is an empty string.
162: *
163: * @return A description for the MBean.
164: */
165: protected String getDescription() {
166: return "";
167: }
168:
169: public Object getAttribute(String name)
170: throws AttributeNotFoundException, MBeanException,
171: ReflectionException {
172: Method m;
173: Object res = null;
174: try {
175: String mname = "get" + StringUtils.capitalize(name);
176: m = findGetterSetter(getClass(), mname, null);
177:
178: if (m == null)
179: throw new AttributeNotFoundException(name);
180: res = m.invoke(this , (Object[]) null);
181: } catch (SecurityException e) {
182: // TODO Auto-generated catch block
183: e.printStackTrace();
184: } catch (IllegalArgumentException e) {
185: // TODO Auto-generated catch block
186: e.printStackTrace();
187: } catch (IllegalAccessException e) {
188: // TODO Auto-generated catch block
189: e.printStackTrace();
190: } catch (InvocationTargetException e) {
191: // TODO Auto-generated catch block
192: e.printStackTrace();
193: }
194:
195: return res;
196: }
197:
198: public AttributeList getAttributes(String[] arg0) {
199: AttributeList list = new AttributeList();
200:
201: for (int i = 0; i < arg0.length; i++) {
202: try {
203: list.add(new Attribute(arg0[i], getAttribute(arg0[i])));
204: } catch (AttributeNotFoundException e) {
205: // TODO Auto-generated catch block
206: e.printStackTrace();
207: } catch (MBeanException e) {
208: // TODO Auto-generated catch block
209: e.printStackTrace();
210: } catch (ReflectionException e) {
211: // TODO Auto-generated catch block
212: e.printStackTrace();
213: }
214: }
215:
216: return list;
217: }
218:
219: public MBeanInfo getMBeanInfo() {
220: return m_beanInfo;
221: }
222:
223: public Object invoke(String arg0, Object[] arg1, String[] arg2)
224: throws MBeanException, ReflectionException {
225: Method[] methods = getClass().getMethods();
226:
227: for (int i = 0; i < methods.length; i++) {
228: if (methods[i].getName().equals(arg0)) {
229: try {
230: return methods[i].invoke(this , arg1);
231: } catch (IllegalArgumentException e) {
232: throw new ReflectionException(e, "Wrong arguments");
233: } catch (IllegalAccessException e) {
234: throw new ReflectionException(e, "No access");
235: } catch (InvocationTargetException e) {
236: throw new ReflectionException(e, "Wrong target");
237: }
238: }
239: }
240:
241: throw new ReflectionException(null, "There is no such method "
242: + arg0); // TODO: Can you put a null exception?
243: }
244:
245: public void setAttribute(Attribute attr)
246: throws AttributeNotFoundException,
247: InvalidAttributeValueException, MBeanException,
248: ReflectionException {
249: Method m;
250:
251: String mname = "set" + StringUtils.capitalize(attr.getName());
252: m = findGetterSetter(getClass(), mname, attr.getValue()
253: .getClass());
254:
255: if (m == null)
256: throw new AttributeNotFoundException(attr.getName());
257:
258: Object[] args = { attr.getValue() };
259:
260: try {
261: m.invoke(this , args);
262: } catch (IllegalArgumentException e) {
263: throw new InvalidAttributeValueException(
264: "Faulty argument: " + e.getMessage());
265: } catch (IllegalAccessException e) {
266: throw new ReflectionException(e, "Cannot access attribute "
267: + e.getMessage());
268: } catch (InvocationTargetException e) {
269: throw new ReflectionException(e, "Cannot invoke attribute "
270: + e.getMessage());
271: }
272: }
273:
274: public AttributeList setAttributes(AttributeList arg0) {
275: AttributeList result = new AttributeList();
276: for (Iterator i = arg0.iterator(); i.hasNext();) {
277: Attribute attr = (Attribute) i.next();
278:
279: //
280: // Attempt to set the attribute. If it succeeds (no exception),
281: // then we just add it to the list of successfull sets.
282: //
283: try {
284: setAttribute(attr);
285: result.add(attr);
286: } catch (AttributeNotFoundException e) {
287: // TODO Auto-generated catch block
288: e.printStackTrace();
289: } catch (InvalidAttributeValueException e) {
290: // TODO Auto-generated catch block
291: e.printStackTrace();
292: } catch (MBeanException e) {
293: // TODO Auto-generated catch block
294: e.printStackTrace();
295: } catch (ReflectionException e) {
296: // TODO Auto-generated catch block
297: e.printStackTrace();
298: }
299: }
300:
301: return result;
302: }
303:
304: /**
305: * This method must return a list of attributes which are
306: * exposed by the SimpleMBean. If there's a getXXX() method
307: * available, it'll be exposed as a getter, and if there's a
308: * setXXX() method available, it'll be exposed as a setter.
309: * For example:
310: * <pre>
311: * public void setFoo( String foo ) ...
312: * public String getFoo() ...
313: *
314: * public String[] getAttributeNames()
315: * {
316: * String[] attrs = { "foo" };
317: *
318: * return attrs;
319: * }
320: * </pre>
321: * Also, methods starting with "is" are also recognized as getters
322: * (e.g. <code>public boolean isFoo()</code>.)
323: *
324: * @return An array of attribute names that can be get and optionally set.
325: */
326: public abstract String[] getAttributeNames();
327:
328: /**
329: * This method must return a list of operations which
330: * are to be exposed by the SimpleMBean. Note that using overloaded
331: * method names is not supported - only one will be exposed as a JMX method
332: * at random.
333: *
334: * @return An array of method names that should be exposed as
335: * JMX operations.
336: */
337: public abstract String[] getMethodNames();
338: }
|