001: /* JFox, the OpenSource J2EE Application Server
002: *
003: * Copyright (C) 2002 huihoo.com
004: * Distributable under GNU LGPL license
005: * See the GNU Lesser General Public License for more details.
006: */
007:
008: package javax.management;
009:
010: import java.lang.reflect.InvocationHandler;
011: import java.lang.reflect.Method;
012: import java.lang.reflect.Proxy;
013:
014: import org.huihoo.jfox.jmx.MethodHelper;
015:
016: /**
017: * <p>{@link java.lang.reflect.InvocationHandler} that forwards methods in an MBean's
018: * management interface through the MBean server to the MBean.</p>
019: *
020: * <p>Given an {@link MBeanServerConnection}, the {@link ObjectName}
021: * of an MBean within that MBean server, and a Java interface
022: * <code>Intf</code> that describes the management interface of the
023: * MBean using the patterns for a Standard MBean, this class can be
024: * used to construct a proxy for the MBean. The proxy implements
025: * the interface <code>Intf</code> such that all of its methods are
026: * forwarded through the MBean server to the MBean.</p>
027: *
028: * <p>If you have an MBean server <code>mbs</code> containing an MBean
029: * with {@link ObjectName} <code>name</code>, and if the MBean's
030: * management interface is described by the Java interface
031: * <code>Intf</code>, you can construct a proxy for the MBean like
032: * this:</p>
033: *
034: * <pre>
035: * Intf proxy = (Intf)
036: * MBeanServerInvocationHandler.{@link #newProxyInstance newProxyInstance}(mbs,
037: * name,
038: * Intf.class,
039: * false);
040: * </pre>
041: *
042: * <p>Suppose, for example, <code>Intf</code> looks like this:</p>
043: *
044: * <pre>
045: * public interface Intf {
046: * public String getSomeAttribute();
047: * public void setSomeAttribute(String value);
048: * public void someOperation(String param1, int param2);
049: * }
050: * </pre>
051: *
052: * <p>Then you can execute:</p>
053: *
054: * <ul>
055: *
056: * <li><code>proxy.getSomeAttribute()</code> which will result in a
057: * call to <code>mbs.</code>{@link MBeanServerConnection#getAttribute
058: * getAttribute}<code>(name, "SomeAttribute")</code>.
059: *
060: * <li><code>proxy.setSomeAttribute("whatever")</code> which will result
061: * in a call to <code>mbs.</code>{@link MBeanServerConnection#setAttribute
062: * setAttribute}<code>(name, new Attribute("SomeAttribute", "whatever"))</code>.
063: *
064: * <li><code>proxy.someOperation("param1", 2)</code> which will be
065: * translated into a call to <code>mbs.</code>{@link
066: * MBeanServerConnection#invoke invoke}<code>(name, "someOperation", <etc>)</code>.
067: *
068: * </ul>
069: *
070: * <p>If the last parameter to {@link #newProxyInstance
071: * newProxyInstance} is <code>true</code>, then the MBean is assumed
072: * to be a {@link NotificationBroadcaster} or {@link
073: * NotificationEmitter} and the returned proxy will implement {@link
074: * NotificationEmitter}. A call to {@link
075: * NotificationBroadcaster#addNotificationListener} on the proxy will
076: * result in a call to {@link
077: * MBeanServerConnection#addNotificationListener(ObjectName,
078: * NotificationListener, NotificationFilter, Object)}, and likewise
079: * for the other methods of {@link NotificationBroadcaster} and {@link
080: * NotificationEmitter}.</p>
081: *
082: * <p>The methods {@link Object#toString()}, {@link Object#hashCode()},
083: * and {@link Object#equals(Object)}, when invoked on a proxy using
084: * this invocation handler, are forwarded to the MBean server as
085: * methods on the proxied MBean. This will only work if the MBean
086: * declares those methods in its management interface.</p>
087: *
088: * @since JMX 1.2
089: *
090: * @author <a href="mailto:young_yy@hotmail.com">Young Yang</a>
091: */
092:
093: public class MBeanServerInvocationHandler implements InvocationHandler {
094: private final MBeanServerConnection connection;
095: private final ObjectName objectName;
096: private final boolean isNotificationBroadcaster;
097:
098: /**
099: * <p>Invocation handler that forwards methods through an MBean
100: * server. This constructor may be called instead of relying on
101: * {@link #newProxyInstance}, for instance if you need to supply a
102: * different {@link ClassLoader} to {@link
103: * Proxy#newProxyInstance Proxy.newProxyInstance}.</p>
104: *
105: * @param connection the MBean server connection through which all
106: * methods of a proxy using this handler will be forwarded.
107: *
108: * @param objectName the name of the MBean within the MBean server
109: * to which methods will be forwarded.
110: */
111: public MBeanServerInvocationHandler(
112: MBeanServerConnection connection, ObjectName objectName,
113: boolean isNotificationBroadcaster) {
114: this .connection = connection;
115: this .objectName = objectName;
116: this .isNotificationBroadcaster = isNotificationBroadcaster;
117:
118: /* Could check here whether the MBean exists. */
119: }
120:
121: /**
122: * <p>Return a proxy that implements the given interface by
123: * forwarding its methods through the given MBean server to the
124: * named MBean.</p>
125: *
126: * <p>This method is equivalent to {@link Proxy#newProxyInstance
127: * Proxy.newProxyInstance}<code>(interfaceClass.getClassLoader(),
128: * interfaces, handler)</code>. Here <code>handler</code> is the
129: * result of {@link #MBeanServerInvocationHandler new
130: * MBeanServerInvocationHandler(connection, objectName)}, and
131: * <code>interfaces</code> is an array that has one element if
132: * <code>isNotificationBroadcaster</code> is false and two if it is
133: * true. The first element of <code>interfaces</code> is
134: * <code>interfaceClass</code> and the second, if present, is
135: * <code>NotificationEmitter.class</code>.
136: *
137: * @param connection the MBean server to forward to.
138: * @param objectName the name of the MBean within
139: * <code>connection</code> to forward to.
140: * @param interfaceClass the management interface that the MBean
141: * exports, which will also be implemented by the returned proxy.
142: * @param isNotificationBroadcaster make the returned proxy
143: * implement {@link NotificationEmitter} by forwarding its methods
144: * via <code>connection</code>.
145: *
146: * @return the new proxy instance.
147: */
148: public static Object newProxyInstance(
149: MBeanServerConnection connection, ObjectName objectName,
150: Class interfaceClass, boolean isNotificationBroadcaster) {
151: final InvocationHandler handler = new MBeanServerInvocationHandler(
152: connection, objectName, isNotificationBroadcaster);
153: final Class[] interfaces;
154: if (isNotificationBroadcaster) {
155: interfaces = new Class[] { interfaceClass,
156: NotificationEmitter.class };
157: } else {
158: interfaces = new Class[] { interfaceClass };
159: }
160: return Proxy.newProxyInstance(interfaceClass.getClassLoader(),
161: interfaces, handler);
162: }
163:
164: // Note: Getters and setters cannot be invoked through the invoke method (page 29)
165: public Object invoke(Object proxy, Method method, Object[] args)
166: throws Throwable {
167: final String methodName = method.getName();
168: final Class[] paramTypes = method.getParameterTypes();
169: final int nargs = (args == null) ? 0 : args.length;
170:
171: // the args length not eq the method params length
172: if (nargs != paramTypes.length) {
173: StringBuffer argString = new StringBuffer();
174: for (int i = 0; i < args.length; i++) {
175: argString.append(args[i].getClass().getName());
176: if (i + 1 < args.length) {
177: argString.append(", ");
178: }
179: }
180: throw new NoSuchMethodException(method.getReturnType()
181: + methodName + "(" + argString + ")");
182: }
183:
184: if (isNotificationBroadcaster) { // is a notification
185: if (methodName.equals("addNotificationListener")) {
186: if (nargs != 3) {
187: throw new IllegalArgumentException(
188: "Bad arg count to addNotificationListener: "
189: + nargs);
190: }
191: /* Other inconsistencies will produce ClassCastException
192: below. */
193: NotificationListener listener = (NotificationListener) args[0];
194: NotificationFilter filter = (NotificationFilter) args[1];
195: Object handback = args[2];
196: connection.addNotificationListener(objectName,
197: listener, filter, handback);
198: return null;
199:
200: } else if (methodName.equals("removeNotificationListener")) {
201:
202: /* NullPointerException if method with no args, but that
203: shouldn't happen because removeNL does have args. */
204: NotificationListener listener = (NotificationListener) args[0];
205:
206: switch (nargs) {
207: case 1:
208: connection.removeNotificationListener(objectName,
209: listener);
210: return null;
211:
212: case 3:
213: NotificationFilter filter = (NotificationFilter) args[1];
214: Object handback = args[2];
215: connection.removeNotificationListener(objectName,
216: listener, filter, handback);
217: return null;
218:
219: default:
220: final String msg = "Bad arg count to removeNotificationListener: "
221: + nargs;
222: throw new IllegalArgumentException(msg);
223: }
224:
225: } else if (methodName.equals("getNotificationInfo")) {
226: if (args != null) {
227: throw new IllegalArgumentException(
228: "getNotificationInfo has args");
229: }
230:
231: MBeanInfo info = connection.getMBeanInfo(objectName);
232: return info.getNotifications();
233:
234: }
235: }
236: // common method
237: if (MethodHelper.isGetterMethod(method)) { // getter
238: String attribute = null;
239: if (methodName.startsWith("get")) {
240: attribute = methodName.substring(3);
241: } else {
242: attribute = methodName.substring(2);
243: }
244: return connection.getAttribute(objectName, attribute);
245: } else if (MethodHelper.isSetterMethod(method)) { // setter
246: Attribute attr = new Attribute(methodName.substring(3),
247: args[0]);
248: connection.setAttribute(objectName, attr);
249: return null;
250: } else { // operation
251: final String[] signature = new String[paramTypes.length];
252: for (int i = 0; i < paramTypes.length; i++) {
253: signature[i] = paramTypes[i].getName();
254: }
255: try {
256: return connection.invoke(objectName, methodName, args,
257: signature);
258: } catch (MBeanException e) {
259: throw e.getTargetException();
260: }
261: }
262: }
263:
264: public static void main(String[] args) {
265:
266: }
267: }
|