001: /*
002: * Copyright (c) xsocket.org, 2006 - 2008. All rights reserved.
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2.1 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: * Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
019: * The latest copy of this software may be found on http://www.xsocket.org/
020: */
021: package org.xsocket;
022:
023: import java.lang.reflect.Method;
024: import java.util.HashMap;
025: import java.util.List;
026: import java.util.Map;
027: import java.util.logging.Level;
028: import java.util.logging.Logger;
029:
030: import javax.management.Attribute;
031: import javax.management.AttributeList;
032: import javax.management.AttributeNotFoundException;
033: import javax.management.DynamicMBean;
034: import javax.management.InvalidAttributeValueException;
035: import javax.management.MBeanAttributeInfo;
036: import javax.management.MBeanException;
037: import javax.management.MBeanInfo;
038: import javax.management.ReflectionException;
039:
040: /**
041: * introspection based dynamic mbean, which exposes the getter and setter methods
042: * (all visibilities) of the underlying object by using introspection <br>
043: *
044: * <br/><br/><b>This is a xSocket internal class and subject to change</b>
045: *
046: * @author grro@xsocket.org
047: */
048: public final class IntrospectionBasedDynamicMBean implements
049: DynamicMBean {
050:
051: private static final Logger LOG = Logger
052: .getLogger(IntrospectionBasedDynamicMBean.class.getName());
053:
054: private Object obj = null;
055:
056: private final Map<String, Info> properties = new HashMap<String, Info>();
057:
058: /**
059: * constructor
060: *
061: * @param obj the object to create a mbean for
062: */
063: public IntrospectionBasedDynamicMBean(Object obj) {
064: this .obj = obj;
065: }
066:
067: /**
068: * @see javax.management.DynamicMBean#getAttribute(java.lang.String)
069: */
070: public Object getAttribute(String attribute)
071: throws AttributeNotFoundException, MBeanException,
072: ReflectionException {
073: String methodName = "get" + attribute;
074: try {
075: Method method = getMethod(obj.getClass(), methodName,
076: new Class[0]);
077:
078: if (method == null) {
079: methodName = "is" + attribute;
080: method = getMethod(obj.getClass(), methodName,
081: new Class[0]);
082: }
083:
084: method.setAccessible(true);
085: return method.invoke(obj, new Object[0]);
086:
087: } catch (Exception e) {
088: if (LOG.isLoggable(Level.FINE)) {
089: LOG.fine("error occured by accessing attribute "
090: + attribute + ": " + e.toString());
091: }
092: throw new ReflectionException(e);
093: }
094: }
095:
096: @SuppressWarnings("unchecked")
097: private Method getMethod(Class clazz, String methodname,
098: Class[] params) {
099: do {
100: try {
101: Method method = clazz.getDeclaredMethod(methodname,
102: params);
103: method.setAccessible(true);
104: return method;
105: } catch (Exception ignore) {
106: }
107:
108: for (Class interf : clazz.getInterfaces()) {
109: getMethod(interf, methodname, params);
110: }
111:
112: clazz = clazz.getSuperclass();
113:
114: } while (clazz != null);
115:
116: return null;
117: }
118:
119: /**
120: * {@inheritDoc}
121: */
122: public AttributeList getAttributes(String[] attributes) {
123: AttributeList list = new AttributeList();
124: for (String attribute : attributes) {
125: try {
126: list.add(new Attribute(attribute,
127: getAttribute(attribute)));
128: } catch (Exception ignore) {
129: }
130: }
131: return list;
132: }
133:
134: /**
135: * {@inheritDoc}
136: */
137: public void setAttribute(Attribute attribute)
138: throws AttributeNotFoundException,
139: InvalidAttributeValueException, MBeanException,
140: ReflectionException {
141: String methodName = "set" + attribute.getName();
142:
143: Info info = getInfo(attribute.getName());
144:
145: try {
146: Method method = getMethod(obj.getClass(), methodName,
147: new Class[] { info.propertyType });
148: method.setAccessible(true);
149: method.invoke(obj, new Object[] { attribute.getValue() });
150: } catch (Exception e) {
151: throw new ReflectionException(e);
152: }
153: }
154:
155: /**
156: * {@inheritDoc}
157: */
158: @SuppressWarnings("unchecked")
159: public AttributeList setAttributes(AttributeList attributes) {
160: AttributeList result = new AttributeList();
161:
162: Attribute[] attrs = (Attribute[]) attributes
163: .toArray(new Attribute[attributes.size()]);
164: for (Attribute attr : attrs) {
165: try {
166: setAttribute(attr);
167: result.add(new Attribute(attr.getName(), attr
168: .getValue()));
169: } catch (Exception ignore) {
170: }
171: }
172: return result;
173: }
174:
175: /**
176: * {@inheritDoc}
177: */
178: public Object invoke(String actionName, Object[] params,
179: String[] signature) throws MBeanException,
180: ReflectionException {
181: // TODO Auto-generated method stub
182: return null;
183: }
184:
185: /**
186: * {@inheritDoc}
187: */
188: public synchronized MBeanInfo getMBeanInfo() {
189:
190: analyze(obj);
191:
192: String[] attributes = properties.keySet().toArray(
193: new String[properties.size()]);
194: MBeanAttributeInfo[] attrs = new MBeanAttributeInfo[attributes.length];
195: for (int i = 0; i < attrs.length; i++) {
196: attrs[i] = properties.get(attributes[i])
197: .asbMBeanAttributeInfo();
198: }
199:
200: return new MBeanInfo(obj.getClass().getName(), "", attrs, null, // constructors
201: null, null); // notifications
202: }
203:
204: @SuppressWarnings("unchecked")
205: private void analyze(Object obj) {
206: Class clazz = obj.getClass();
207: do {
208: analyzeType(clazz);
209:
210: for (Class interf : clazz.getInterfaces()) {
211: analyzeType(interf);
212: }
213:
214: clazz = clazz.getSuperclass();
215:
216: } while (clazz != null);
217: }
218:
219: @SuppressWarnings("unchecked")
220: private void analyzeType(Class clazz) {
221: for (Method method : clazz.getDeclaredMethods()) {
222: String name = method.getName();
223:
224: if ((name.length() > 3) && name.startsWith("get")) {
225: if (method.getParameterTypes().length == 0) {
226: Class propertyType = method.getReturnType();
227:
228: if (isAcceptedPropertyType(propertyType)) {
229: Info info = getInfo(name.substring(3, name
230: .length()));
231: info.isReadable = true;
232: info.propertyType = propertyType;
233: }
234: }
235: }
236:
237: if ((name.length() > 2) && name.startsWith("is")) {
238: if (method.getParameterTypes().length == 0) {
239: Class propertyType = method.getReturnType();
240:
241: if (isAcceptedPropertyType(propertyType)) {
242: Info info = getInfo(name.substring(2, name
243: .length()));
244: info.isReadable = true;
245: info.propertyType = propertyType;
246: }
247: }
248: }
249:
250: if ((name.length() > 3) && name.startsWith("set")) {
251: if (method.getParameterTypes().length == 1) {
252: Class propertyType = method.getParameterTypes()[0];
253:
254: if (isAcceptedPropertyType(propertyType)) {
255: Info info = getInfo(name.substring(3, name
256: .length()));
257: info.isWriteable = true;
258: info.propertyType = propertyType;
259: }
260: }
261: }
262: }
263: }
264:
265: private Info getInfo(String name) {
266: Info info = properties.get(name);
267: if (info == null) {
268: info = new Info();
269: info.propertyName = name;
270: info.propertyDescription = "Property " + info.propertyName;
271: properties.put(name, info);
272: }
273: return info;
274: }
275:
276: @SuppressWarnings("unchecked")
277: private boolean isAcceptedPropertyType(Class clazz) {
278: if (clazz.isAssignableFrom(List.class)) {
279: return true;
280: }
281:
282: String name = clazz.getName();
283: return name.equals("int") || name.equals("java.lang.Integer")
284: || name.equals("long") || name.equals("java.lang.Long")
285: || name.equals("double")
286: || name.equals("java.lang.Double")
287: || name.equals("boolean")
288: || name.equals("java.lang.Boolean")
289: || name.equals("float")
290: || name.equals("java.lang.String")
291: || name.equals("java.lang.Float");
292: }
293:
294: private static class Info {
295: String propertyName = null;
296: @SuppressWarnings("unchecked")
297: Class propertyType = null;
298: String propertyDescription = null;
299: boolean isReadable = false;
300: boolean isWriteable = false;
301: boolean isIs = false;
302:
303: MBeanAttributeInfo asbMBeanAttributeInfo() {
304: return new MBeanAttributeInfo(propertyName, propertyType
305: .getName(), propertyDescription, isReadable,
306: isWriteable, isIs);
307: }
308: }
309: }
|