001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.mx.util;
023:
024: import java.io.Externalizable;
025: import java.io.IOException;
026: import java.io.ObjectInput;
027: import java.io.ObjectOutput;
028: import java.lang.reflect.InvocationHandler;
029: import java.lang.reflect.Method;
030: import java.lang.reflect.Proxy;
031: import java.security.AccessController;
032: import java.security.PrivilegedAction;
033: import java.util.HashMap;
034:
035: import javax.management.Attribute;
036: import javax.management.MBeanAttributeInfo;
037: import javax.management.MBeanInfo;
038: import javax.management.MBeanServer;
039: import javax.management.MBeanServerConnection;
040: import javax.management.MalformedObjectNameException;
041: import javax.management.ObjectName;
042:
043: /**
044: * A factory for producing MBean proxies.
045: *
046: * <p>Created proxies will also implement {@link org.jboss.mx.util.MBeanProxyInstance}
047: * allowing access to the proxies configuration.
048: * @author <a href="mailto:rickard.oberg@telkel.com">Rickard Oberg</a>.
049: * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>.
050: * @author <a href="mailto:adrian.brock@happeningtimes.com">Adrian Brock</a>.
051: * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>.
052: * @version <tt>$Revision: 57200 $</tt>
053: */
054: public class MBeanProxyExt implements InvocationHandler,
055: MBeanProxyInstance, Externalizable {
056: /** The serialVersionUID */
057: private static final long serialVersionUID = -2942844863242742655L;
058:
059: /**
060: * The remote MBeanServerConnection
061: */
062: public static MBeanServerConnection remote;
063:
064: /**
065: * The server to proxy invoke calls to.
066: */
067: private MBeanServerConnection server;
068:
069: /**
070: * The name of the object to invoke.
071: */
072: private ObjectName name;
073:
074: /**
075: * The MBean's attributes
076: */
077: private transient final HashMap attributeMap = new HashMap();
078: /**
079: * Have the attributes been retrieved
080: */
081: private transient boolean inited = false;
082:
083: /**
084: * For externalizable
085: */
086: public MBeanProxyExt() {
087: }
088:
089: /**
090: * Construct an MBeanProxy.
091: */
092: MBeanProxyExt(final ObjectName name, final MBeanServer server,
093: boolean lazyInit) {
094: this .name = name;
095: this .server = server;
096: if (lazyInit == false)
097: init();
098: }
099:
100: /**
101: * Used when args is null.
102: */
103: private static final Object EMPTY_ARGS[] = {};
104:
105: /**
106: * Invoke the configured MBean via the target MBeanServer and decode any
107: * resulting JMX exceptions that are thrown.
108: */
109: public Object invoke(final Object proxy, final Method method,
110: final Object[] args) throws Throwable {
111: // if the method belongs to ProxyInstance, then invoke locally
112: Class type = method.getDeclaringClass();
113: if (type == MBeanProxyInstance.class || type == Object.class) {
114: return method.invoke(this , args);
115: }
116:
117: String methodName = method.getName();
118:
119: // Get attribute
120: if (methodName.startsWith("get") && args == null) {
121: if (inited == false)
122: init();
123:
124: String attrName = methodName.substring(3);
125: MBeanAttributeInfo info = (MBeanAttributeInfo) attributeMap
126: .get(attrName);
127: if (info != null) {
128: String retType = method.getReturnType().getName();
129: if (retType.equals(info.getType())) {
130: try {
131: return server.getAttribute(name, attrName);
132: } catch (Exception e) {
133: throw JMXExceptionDecoder.decode(e);
134: }
135: }
136: }
137: }
138:
139: // Is attribute
140: else if (methodName.startsWith("is") && args == null) {
141: if (inited == false)
142: init();
143:
144: String attrName = methodName.substring(2);
145: MBeanAttributeInfo info = (MBeanAttributeInfo) attributeMap
146: .get(attrName);
147: if (info != null && info.isIs()) {
148: Class retType = method.getReturnType();
149: if (retType.equals(Boolean.class)
150: || retType.equals(Boolean.TYPE)) {
151: try {
152: return server.getAttribute(name, attrName);
153: } catch (Exception e) {
154: throw JMXExceptionDecoder.decode(e);
155: }
156: }
157: }
158: }
159:
160: // Set attribute
161: else if (methodName.startsWith("set") && args != null
162: && args.length == 1) {
163: if (inited == false)
164: init();
165:
166: String attrName = methodName.substring(3);
167: MBeanAttributeInfo info = (MBeanAttributeInfo) attributeMap
168: .get(attrName);
169: if (info != null && method.getReturnType() == Void.TYPE) {
170: try {
171: server.setAttribute(name, new Attribute(attrName,
172: args[0]));
173: return null;
174: } catch (Exception e) {
175: throw JMXExceptionDecoder.decode(e);
176: }
177: }
178: }
179:
180: // Operation
181:
182: // convert the parameter types to strings for JMX
183: Class[] types = method.getParameterTypes();
184: String[] sig = new String[types.length];
185: for (int i = 0; i < types.length; i++) {
186: sig[i] = types[i].getName();
187: }
188:
189: // invoke the server and decode JMX exceptions
190: try {
191: return server.invoke(name, methodName,
192: args == null ? EMPTY_ARGS : args, sig);
193: } catch (Exception e) {
194: throw JMXExceptionDecoder.decode(e);
195: }
196: }
197:
198: ///////////////////////////////////////////////////////////////////////////
199: // MBeanProxyInstance //
200: ///////////////////////////////////////////////////////////////////////////
201:
202: public final ObjectName getMBeanProxyObjectName() {
203: return name;
204: }
205:
206: public final MBeanServer getMBeanProxyMBeanServer() {
207: if (server instanceof MBeanServer == false)
208: throw new IllegalStateException(
209: "This operation is not available for an MBeanServerConnection");
210: return (MBeanServer) server;
211: }
212:
213: public final MBeanServerConnection getMBeanProxyMBeanServerConnection() {
214: return server;
215: }
216:
217: ///////////////////////////////////////////////////////////////////////////
218: // Object Overrides //
219: ///////////////////////////////////////////////////////////////////////////
220:
221: /**
222: * We need to override this because by default equals returns false when
223: * called on the proxy object and then relayed here.
224: */
225: public boolean equals(Object that) {
226: if (that == null)
227: return false;
228: if (that == this )
229: return true;
230:
231: // check if 'that' is an MBeanProxyExt or a Proxy instance
232: // that implements the MBeanProxyInstance interface
233: if (that instanceof MBeanProxyInstance) {
234: MBeanProxyInstance proxy = (MBeanProxyInstance) that;
235:
236: // assume equality if both the MBeanServer and ObjectName match
237: if (name.equals(proxy.getMBeanProxyObjectName())
238: && server.equals(proxy.getMBeanProxyMBeanServer())) {
239: return true;
240: }
241: }
242: return false;
243: }
244:
245: /**
246: * As with equals, use the MBeanServer + ObjectName to calculate the
247: * hashCode
248: */
249: public int hashCode() {
250: return name.hashCode() * 31 + server.hashCode();
251: }
252:
253: /**
254: * avoid the default printout, e.g. org.jboss.mx.util.MBeanProxyExt@120540c
255: */
256: public String toString() {
257: StringBuffer sbuf = new StringBuffer(128);
258:
259: sbuf.append("MBeanProxyExt[").append(name.toString()).append(
260: ']');
261:
262: return sbuf.toString();
263: }
264:
265: ///////////////////////////////////////////////////////////////////////////
266: // Factory Methods //
267: ///////////////////////////////////////////////////////////////////////////
268:
269: /**
270: * Create an MBean proxy.
271: * @param intf The interface which the proxy will implement.
272: * @param name A string used to construct the ObjectName of the MBean to
273: * proxy to.
274: * @return A MBean proxy.
275: * @throws javax.management.MalformedObjectNameException Invalid object
276: * name.
277: */
278: public static Object create(final Class intf, final String name)
279: throws MalformedObjectNameException {
280: return create(intf, new ObjectName(name));
281: }
282:
283: /**
284: * Create an MBean proxy.
285: * @param intf The interface which the proxy will implement.
286: * @param name A string used to construct the ObjectName of the MBean to
287: * proxy to.
288: * @param server The MBeanServer that contains the MBean to proxy to.
289: * @return A MBean proxy.
290: * @throws javax.management.MalformedObjectNameException Invalid object
291: * name.
292: */
293: public static Object create(final Class intf, final String name,
294: final MBeanServer server)
295: throws MalformedObjectNameException {
296: return create(intf, new ObjectName(name), server);
297: }
298:
299: /**
300: * Create an MBean proxy.
301: * @param intf The interface which the proxy will implement.
302: * @param name The name of the MBean to proxy invocations to.
303: * @return A MBean proxy.
304: */
305: public static Object create(final Class intf, final ObjectName name) {
306: return create(intf, name, MBeanServerLocator.locateJBoss());
307: }
308:
309: /**
310: * Create an MBean proxy.
311: * @param intf The interface which the proxy will implement.
312: * @param name The name of the MBean to proxy invocations to.
313: * @param server The MBeanServer that contains the MBean to proxy to.
314: * @return A MBean proxy.
315: */
316: public static Object create(final Class intf,
317: final ObjectName name, final MBeanServer server) {
318: return create(intf, name, server, false);
319: }
320:
321: /**
322: * Create an MBean proxy.
323: * @param intf The interface which the proxy will implement.
324: * @param name The name of the MBean to proxy invocations to.
325: * @param server The MBeanServer that contains the MBean to proxy to.
326: * @param lazyInit - a flag indicating if the mbean attribute info should
327: * be retrieved when the proxy is created.
328: * @return A MBean proxy.
329: */
330: public static Object create(final Class intf,
331: final ObjectName name, final MBeanServer server,
332: boolean lazyInit) {
333: // CL which delegates to MBeanProxyInstance's cl for it's class resolution
334: PrivilegedAction action = new PrivilegedAction() {
335: public Object run() {
336: ClassLoader cl = new ClassLoader(intf.getClassLoader()) {
337: public Class loadClass(final String className)
338: throws ClassNotFoundException {
339: try {
340: return super .loadClass(className);
341: } catch (ClassNotFoundException e) {
342: // only allow loading of MBeanProxyInstance from this loader
343: if (className
344: .equals(MBeanProxyInstance.class
345: .getName())) {
346: return MBeanProxyInstance.class
347: .getClassLoader().loadClass(
348: className);
349: }
350: // was some other classname, throw the CNFE
351: throw e;
352: }
353: }
354: };
355: return cl;
356: }
357: };
358: ClassLoader cl = (ClassLoader) AccessController
359: .doPrivileged(action);
360: Class[] ifaces = { MBeanProxyInstance.class, intf };
361: InvocationHandler handler = new MBeanProxyExt(name, server,
362: lazyInit);
363: return Proxy.newProxyInstance(cl, ifaces, handler);
364: }
365:
366: /**
367: * Retrieve the mbean MBeanAttributeInfo
368: */
369: private synchronized void init() {
370: // The MBean's attributes
371: inited = true;
372: try {
373: MBeanInfo info = server.getMBeanInfo(name);
374: MBeanAttributeInfo[] attributes = info.getAttributes();
375:
376: for (int i = 0; i < attributes.length; ++i)
377: attributeMap
378: .put(attributes[i].getName(), attributes[i]);
379: } catch (Exception e) {
380: throw new RuntimeException("Error creating MBeanProxy: "
381: + name, e);
382: }
383: }
384:
385: public void readExternal(ObjectInput in) throws IOException,
386: ClassNotFoundException {
387: name = (ObjectName) in.readObject();
388: server = (MBeanServerConnection) in.readObject();
389: }
390:
391: public void writeExternal(ObjectOutput out) throws IOException {
392: out.writeObject(name);
393: if (remote != null)
394: out.writeObject(remote);
395: else
396: out.writeObject(server); // This will fail for a normal MBeanServer
397: }
398: }
|