001: /*
002: * Copyright (C) The MX4J Contributors.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the MX4J License version 1.0.
006: * See the terms of the MX4J License in the documentation provided with this software.
007: */
008:
009: package javax.management;
010:
011: import java.lang.reflect.InvocationHandler;
012: import java.lang.reflect.Method;
013: import java.lang.reflect.Proxy;
014:
015: import mx4j.util.Utils;
016:
017: /**
018: */
019: public class MBeanServerInvocationHandler implements InvocationHandler {
020: private final MBeanServerConnection connection;
021: private final ObjectName objectName;
022:
023: public MBeanServerInvocationHandler(
024: MBeanServerConnection connection, ObjectName objectName) {
025: this .connection = connection;
026: this .objectName = objectName;
027: }
028:
029: public static Object newProxyInstance(
030: MBeanServerConnection connection, ObjectName name,
031: Class mbeanInterface, boolean notificationBroadcaster) {
032: if (mbeanInterface == null)
033: throw new IllegalArgumentException(
034: "MBean interface cannot be null");
035: if (!mbeanInterface.isInterface())
036: throw new IllegalArgumentException(
037: "Class parameter must be an interface");
038: if (name == null)
039: throw new IllegalArgumentException(
040: "MBean ObjectName cannot be null");
041: if (connection == null)
042: throw new IllegalArgumentException(
043: "MBeanServerConnection cannot be null");
044:
045: Class[] interfaces = null;
046: if (notificationBroadcaster
047: && !(mbeanInterface.equals(NotificationEmitter.class))) {
048: if ((mbeanInterface.equals(NotificationBroadcaster.class))) {
049: interfaces = new Class[] { NotificationEmitter.class };
050: } else {
051: interfaces = new Class[] { mbeanInterface,
052: NotificationEmitter.class };
053: }
054: } else {
055: interfaces = new Class[] { mbeanInterface };
056: }
057:
058: // The client must be able to cast the returned object to the mbeanInterface it passes,
059: // so the classloader must be the same
060: ClassLoader loader = mbeanInterface.getClassLoader();
061: return Proxy.newProxyInstance(loader, interfaces,
062: new MBeanServerInvocationHandler(connection, name));
063: }
064:
065: public Object invoke(Object proxy, Method method, Object[] args)
066: throws Throwable {
067: Class[] declared = method.getExceptionTypes();
068:
069: Class declaringClass = method.getDeclaringClass();
070: if (declaringClass.equals(NotificationBroadcaster.class)
071: || declaringClass.equals(NotificationEmitter.class)) {
072: return invokeNotificationMethod(proxy, method, args,
073: declared);
074: }
075:
076: // No need to check for consistency between the signature and the args parameter,
077: // since the invocation it is not done by reflection, but statically
078:
079: if (Utils.isAttributeSetter(method)) {
080: String name = method.getName().substring(3);
081: Attribute attribute = new Attribute(name, args[0]);
082: try {
083: connection.setAttribute(objectName, attribute);
084: return null;
085: } catch (Throwable x) {
086: unwrapThrowable(x, declared);
087: }
088: } else if (Utils.isAttributeGetter(method)) {
089: String n = method.getName();
090: String name = null;
091: if (n.startsWith("is"))
092: name = n.substring(2);
093: else
094: name = n.substring(3);
095:
096: try {
097: return connection.getAttribute(objectName, name);
098: } catch (Throwable x) {
099: unwrapThrowable(x, declared);
100: }
101: } else {
102: Class[] parameters = method.getParameterTypes();
103: String[] params = new String[parameters.length];
104: for (int i = 0; i < parameters.length; ++i) {
105: params[i] = parameters[i].getName();
106: }
107:
108: try {
109: return connection.invoke(objectName, method.getName(),
110: args, params);
111: } catch (Throwable x) {
112: unwrapThrowable(x, declared);
113: }
114: }
115:
116: return null;
117: }
118:
119: /**
120: * Convenience method that invokes Notification-related methods.
121: *
122: * @param proxy Proxy object created by the newProxyInstance method
123: * @param method The <code>java.lang.Method</code> to be invoked
124: * @param args The method's arguments
125: * @param declared The method's declared exceptions
126: */
127: private Object invokeNotificationMethod(Object proxy,
128: Method method, Object[] args, Class[] declared)
129: throws Throwable {
130: String methodName = method.getName();
131: int numArgs = (args == null) ? 0 : args.length;
132:
133: if (methodName.equals("addNotificationListener")) {
134: try {
135: connection.addNotificationListener(objectName,
136: (NotificationListener) args[0],
137: (NotificationFilter) args[1], args[2]);
138: } catch (Throwable t) {
139: unwrapThrowable(t, declared);
140: }
141: return null;
142: } else if (methodName.equals("removeNotificationListener")) {
143: switch (numArgs) {
144: case 1:
145: try {
146: connection.removeNotificationListener(objectName,
147: (NotificationListener) args[0]);
148: } catch (Throwable t) {
149: unwrapThrowable(t, declared);
150: }
151: return null;
152:
153: case 3:
154: try {
155: connection.removeNotificationListener(objectName,
156: (NotificationListener) args[0],
157: (NotificationFilter) args[1], args[2]);
158: } catch (Throwable t) {
159: unwrapThrowable(t, declared);
160: }
161: return null;
162:
163: default:
164: throw new IllegalArgumentException(
165: "Method removeNotificationListener must have 1 or 3 arguments");
166: }
167:
168: } else if (methodName.equals("getNotificationInfo")) {
169: try {
170: MBeanInfo info = connection.getMBeanInfo(objectName);
171: return info.getNotifications();
172: } catch (Throwable t) {
173: unwrapThrowable(t, declared);
174: }
175: return null;
176: } else {
177: throw new IllegalArgumentException("Method " + methodName
178: + " not known to MBean: " + objectName);
179: }
180: }
181:
182: /**
183: * Rethrows 'as is' the given throwable. If it is an instance of one of the given <code>declared</code> classes,
184: * this method tries to (recursively) unwrap it and rethrow it.
185: *
186: * @param x The <code>java.lang.Throwable</code> to unwrap
187: * @param declared An array of <code>java.lang.Class</code> objects representing the declared exceptions
188: * of the invoked method.
189: * @throws java.lang.Throwable
190: */
191: private void unwrapThrowable(Throwable x, Class[] declared)
192: throws Throwable {
193: if (declared != null) {
194: // See if the exception is declared by the method
195: // If so, just rethrow it.
196: for (int i = 0; i < declared.length; ++i) {
197: Class exception = declared[i];
198: if (exception.isInstance(x))
199: throw x;
200: }
201: }
202:
203: // The exception is not declared, try to unwrap it
204: if (x instanceof MBeanException) {
205: unwrapThrowable(((MBeanException) x).getTargetException(),
206: declared);
207: } else if (x instanceof ReflectionException) {
208: unwrapThrowable(((ReflectionException) x)
209: .getTargetException(), declared);
210: } else if (x instanceof RuntimeOperationsException) {
211: unwrapThrowable(((RuntimeOperationsException) x)
212: .getTargetException(), declared);
213: } else if (x instanceof RuntimeMBeanException) {
214: unwrapThrowable(((RuntimeMBeanException) x)
215: .getTargetException(), declared);
216: } else if (x instanceof RuntimeErrorException) {
217: unwrapThrowable(((RuntimeErrorException) x)
218: .getTargetError(), declared);
219: } else {
220: // Rethrow as is. Since this exception is not declared by the methods of the interface,
221: // if it is checked it will be thrown as an UndeclaredThrowableException, if it is unchecked
222: // it will be rethrown as is.
223: throw x;
224: }
225: }
226: }
|