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;