Now using the more license-friendly cglib instead of javassist
git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@450624 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/JmxAsmHelper.java b/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/JmxAsmHelper.java
index 951d58d..6d7f45b 100644
--- a/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/JmxAsmHelper.java
+++ b/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/JmxAsmHelper.java
@@ -1,9 +1,29 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
package org.apache.felix.jmxintrospector;
import java.util.Hashtable;
import org.objectweb.asm.Type;
+/**
+ * This class just provides a mapping between JMX types and ASM ones
+ *
+ */
public class JmxAsmHelper {
private static Hashtable<String, Class> primitives=new Hashtable<String, Class>();
diff --git a/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/MBean.java b/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/MBean.java
index f983408..711fe5f 100644
--- a/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/MBean.java
+++ b/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/MBean.java
@@ -21,6 +21,7 @@
import javax.management.MBeanServerConnection;
/**
+ *
* Proxies returned implement this interface, to ease handling of mbeans (filtering and so on)
*
*/
diff --git a/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/MBeanProxyFactory.java b/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/MBeanProxyFactory.java
index 04fae91..472a58c 100644
--- a/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/MBeanProxyFactory.java
+++ b/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/MBeanProxyFactory.java
@@ -17,6 +17,7 @@
package org.apache.felix.jmxintrospector;
+import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@@ -29,14 +30,18 @@
import javassist.CtMethod;
import javassist.NotFoundException;
*/
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
+import javax.management.MalformedObjectNameException;
import javax.management.NotificationEmitter;
import javax.management.ObjectName;
+import javax.management.ReflectionException;
import net.sf.cglib.core.NamingPolicy;
import net.sf.cglib.core.Predicate;
@@ -45,6 +50,13 @@
import org.objectweb.asm.Type;
+/**
+ *
+ * The MBeanProxyFactory is the central element of the jmxintrospector library. The main method is the {@link MBeanProxyFactory#newProxyInstance(String)}
+ * method, that accepts an objectname in String form and returns a proxy for that mbean.
+ * Internally, it uses the cglib library, although it be made to work with other bytecode libraries. More specifically,prior versions used the Javassist library
+ */
+
public class MBeanProxyFactory {
private MBeanServerConnection mbeanServer;
@@ -56,85 +68,131 @@
this.mbeanServer = mbeanServer;
}
- private Class getInterface(String oname)throws Exception{
+ /**
+ * Internal method for generating the Class object for the dynamically-generated interface for the mbean.
+ * The name given to the class is based on the actual classname of the mbean, although some extensions are added
+ * to it by the cglib library to avoid namespace clashes.
+ * @param oname
+ * @return
+ * @throws IOException
+ * @throws InstanceNotFoundException
+ * @throws ClassNotFoundException
+ * @throws ReflectionException
+ * @throws IntrospectionException
+ * @throws
+ * @throws MalformedObjectNameException
+ * @throws Exception
+ */
+ private Class getInterface(String oname) throws MalformedObjectNameException, InstanceNotFoundException, IOException, IntrospectionException, ReflectionException {
ObjectName objectName=ObjectName.getInstance(oname);
String ifaceName=mbeanServer.getObjectInstance(objectName).getClassName();
+ //uses the ifaceName as the prefix for the class name
InterfaceMaker maker=new MBeanInterfaceMaker(ifaceName);
for (Signature s : getSignatures(objectName)) {
+ //add each method
maker.add(s, null);
}
- try {
return maker.create();
- } catch (Exception e) {
- System.out.println(e.getCause());
- throw e;
- }
}
- private Type getType(String type) throws Exception{
- System.out.println("getting Type for: "+type);
+ private Type getType(String type) throws ClassNotFoundException{
return JmxAsmHelper.getAsmType(type);
}
- private List<Signature> getSignatures(ObjectName objectName)throws Exception{
+ /**
+ * Internal method for generating the signatures of the mbeans.
+ * @param objectName
+ * @return
+ * @throws IOException
+ * @throws ReflectionException
+ * @throws IntrospectionException
+ * @throws InstanceNotFoundException
+ */
+ private List<Signature> getSignatures(ObjectName objectName)throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException{
List<Signature> methods=new ArrayList<Signature>();
- MBeanInfo minfo =mbeanServer.getMBeanInfo(objectName);
+ MBeanInfo minfo;
+ MBeanAttributeInfo[] attributes=null;
+ minfo = mbeanServer.getMBeanInfo(objectName);
+ attributes=minfo.getAttributes();
- for (MBeanAttributeInfo info : minfo.getAttributes()) {
+ for (MBeanAttributeInfo info : attributes) {
String name=info.getName().substring(0, 1).toUpperCase()+info.getName().substring(1);
if(info.isReadable()){
+ //For each readable attribute, we generate a getter method (following the isXX for booleans
+ //when it is being used on the remote side)
if(info.isIs()){
methods.add(new Signature("is"+name, Type.BOOLEAN_TYPE, new Type[0]));
}
else{
-// try {
-
+ try{
methods.add(new Signature("get"+name, getType(info.getType()), new Type[0]));
-// }catch (NotFoundException nfe) {
-// String n=nfe.getMessage();
-// String notFoundName=n.startsWith("[")?n.substring(1, n.length()):n;
-// Class notFound=(Class)mbeanServer.invoke(ObjectName.getInstance(TypeGetterMBean.MBEAN_NAME), "getType", new Object[] {notFoundName}, new String[] {String.class.getName()});
-//
-// m=new CtMethod(pool.get(info.getType()), "get"+ name,null, ctIface);
-// }
+ }catch(ClassNotFoundException cnfe){
+ System.out.println("JMXINTROSPECTOR WARNING: "+info.getType()+" could not be found. Attribute will not be added to proxy.");
+ continue;
+ }
}
}
+ //Same with each writable att, but with setters.
if(info.isWritable()){
+ try{
Type [] params=new Type[]{getType(info.getType())};
Signature s=new Signature("set"+name, Type.VOID_TYPE, params);
methods.add(s);
+ }catch(ClassNotFoundException cnfe){
+ System.out.println("JMXINTROSPECTOR WARNING: "+info.getType()+" could not be found. Attribute will not be added to proxy.");
+ continue;
+ }
}
}
+ //same for each operation
for (MBeanOperationInfo info : minfo.getOperations()) {
+ try{
Type[] params=new Type[info.getSignature().length];
for (int i = 0; i < params.length; i++) {
params[i]=getType(info.getSignature()[i].getType());
}
+
Signature s=new Signature(info.getName(), getType(info.getReturnType()), params);
methods.add(s);
+ }catch(ClassNotFoundException cnfe){
+ System.out.println("JMXINTROSPECTOR WARNING: "+info.toString()+" could not be created. Operation will not be added to proxy.");
+ continue;
+
+ }
}
return methods;
}
/**
* Returns a proxy object for the MBean specified by the object name oname in the
- * mbean server associated with this factory. This proxy object implements
+ * mbean server associated with this factory. This proxy object implements the generated interface plus
+ * the MBean interface. It will also be a notification broadcaster if the underlying mbean broadcasts notifications.
* @param oname
- * @return
+ * @return
+ * @throws IOException
+ * @throws ReflectionException
+ * @throws IntrospectionException
+ * @throws InstanceNotFoundException
+ * @throws
+ * @throws MalformedObjectNameException
* @throws Exception
*/
- public Object newProxyInstance(String oname)throws Exception{
+ public Object newProxyInstance(String oname) throws MalformedObjectNameException, InstanceNotFoundException, IntrospectionException, ReflectionException, IOException{
ObjectName objectName=ObjectName.getInstance(oname);
Class iface=getInterface(oname);
MBeanInfo info=mbeanServer.getMBeanInfo(objectName);
boolean isBroadcaster=false;
MBeanNotificationInfo[] notifs=info.getNotifications();
if (notifs!=null && notifs.length!=0) isBroadcaster=true;
+ //We first create the proxy for the remote mbean. If broadcasting supported, then it adds the broadcasting interface
Object proxy=MBeanServerInvocationHandler.newProxyInstance(mbeanServer, objectName, iface, isBroadcaster);
+ //We get the underlying invocation handler, needed for the wrapper handler. The wrapper adds the mbean interface functionality
+ //and integrates JMX invocation handler.
InvocationHandler h=Proxy.getInvocationHandler(proxy);
InvocationHandler wrapper=new JMXInvocationHandler(oname,mbeanServer, mbeanServer.getMBeanInfo(objectName), h);
Class[] ifaces;
if (isBroadcaster) {
ifaces=new Class[]{iface, NotificationEmitter.class, MBean.class};
}else ifaces=new Class[]{iface, MBean.class};
+ //finally, we create the proxy with the appropriate classloader, the interfaces and the invocation handler
Object mbeanProxy=Proxy.newProxyInstance(proxy.getClass().getClassLoader(), ifaces, wrapper);
return mbeanProxy;
}
@@ -153,8 +211,11 @@
}
- //FIXME: hashCode() and equals do not work if not exposed in management interface
+ //FIXME: hashCode() and equals do not work if not exposed in management interface. I have not thought of a workaround for that yet.
+ //note that this is needed for hashmaps and comparisons.
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ //decide wether to invoke the JMXInvocationHandler we have implemented
+ //or use the one that was created by the MBeanServerInvocationHandler
if(method.getDeclaringClass().equals(MBean.class)){
return this.getClass().getMethod(method.getName(), null).invoke(this, args);
}
@@ -184,6 +245,11 @@
public void setMbeanServer(MBeanServerConnection mbs) {
this.mbeanServer = mbs;
}
+ /**
+ * This class is used to be able to modify the super class,
+ * because setNamePrefix and setAttemptLoad are protected methods
+ *
+ */
private class MBeanInterfaceMaker extends InterfaceMaker{
public MBeanInterfaceMaker(String namePrefix) {
super();
diff --git a/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/MBeanProxyManager.java b/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/MBeanProxyManager.java
index b554342..271f6ee 100644
--- a/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/MBeanProxyManager.java
+++ b/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/MBeanProxyManager.java
@@ -24,25 +24,54 @@
import java.util.logging.Logger;
import javax.management.MBeanServerConnection;
+import javax.management.NotificationBroadcaster;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
+/**
+ * This class is the main entry point to the jmxintrospector library.
+ * It uses the {@link MBeanProxyFactory#newProxyInstance(String)} to create
+ * proxies for the mbeans found in the mbean servers.
+ * MBean servers can be added through the addXXServer methods. Some helper methods
+ * for browsing through the MBeans are also provided.
+ *
+ */
public class MBeanProxyManager {
//FIXME: mixing up Server and MBeanServerConnections is dirty and error-prone
+ /*
+ *Currently, we wrap the MBeanProxy Server with a Server in order to add identification
+ *this is quite dirty.
+ */
private List<Server> servers = new ArrayList<Server>();
private List<Object> objects = new ArrayList<Object>();
private Logger logger = Logger.getLogger(this.getClass().getName());
+
+ /**
+ * Adds a remote MBean server that has a RMI connector at host:1099/path
+ * Same as {@link MBeanProxyManager}{@link #addRMIServer(host, path, 1099)}
+ * @param host
+ * @param path
+ * @throws Exception
+ */
public void addRMIServer(String host, String path) throws Exception {
addRMIServer(host, path, 1099);
}
-
+ /**
+ * Adds a remote MBean server that has a RMI connector running at
+ * {@literal service:jmx:rmi:///jndi/rmi://host:port/path}
+ * Same as {@link MBeanProxyManager#addRemoteServer(service:jmx:rmi:///jndi/rmi://host:port/path)}
+ * @param host
+ * @param path
+ * @param port
+ * @throws Exception
+ */
public void addRMIServer(String host, String path, int port)
throws Exception {
- addRemoteServer("service:jmx:rmi:///jndi/rmi://" + host + ":" + port + path);
+ addRemoteServer("service:jmx:rmi:///jndi/rmi://" + host + ":" + port +"/" +path);
}
public void addRemoteServer(String url) throws Exception {
@@ -51,7 +80,10 @@
.getMBeanServerConnection();
add(url, s);
}
-
+ /**
+ * The same for the local server. Note that this will make an MBeanServer if it was not already available.
+ * @throws Exception
+ */
public void addLocalServer() throws Exception {
MBeanServerConnection s=ManagementFactory.getPlatformMBeanServer();
add(s.getDefaultDomain(), s);
@@ -60,10 +92,11 @@
private void add(String id, MBeanServerConnection s) throws Exception {
Server server=new Server(s,id);
servers.add(server);
- Set<ObjectName> onames = s.queryNames(ObjectName.WILDCARD, null);
+ Set onames = s.queryNames(ObjectName.getInstance("*:*"), null);
MBeanProxyFactory introspector = new MBeanProxyFactory(server);
- for (ObjectName name : onames) {
+ for (Object o : onames) {
try {
+ ObjectName name=(ObjectName) o;
objects.add(introspector.newProxyInstance(name
.toString()));
} catch (Exception e) {
@@ -74,6 +107,10 @@
}
}
+ /**
+ * it removes the specified server
+ * @param server
+ */
public void removeServer(Server server) {
servers.remove(server);
for (Object o : objects) {
@@ -83,6 +120,18 @@
///////////
//Finders//
///////////
+
+ /**
+ * Returns all the created proxies.
+ * Remember that each proxy object can implement up to 3 interfaces:
+ * <ol>
+ * <li>{@link MBean}. Always implemented</li>
+ * <li>The implicit interface (dynamically generated) of the remote MBean. This can only be called through reflection
+ * or using a dynamic language on top of Java</li>
+ * <li>{@link NotificationBroadcaster} if the underlying mbean broadcasts notifications.
+ * </li>
+ * </ol>
+ */
public List<Object> getObjects() {
return objects;
}
diff --git a/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/Server.java b/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/Server.java
index 1d6beb1..7105c66 100644
--- a/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/Server.java
+++ b/jmxintrospector/src/main/java/org/apache/felix/jmxintrospector/Server.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
package org.apache.felix.jmxintrospector;
import java.io.IOException;