/* * 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; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.List; /*import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; 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; import net.sf.cglib.core.Signature; import net.sf.cglib.proxy.InterfaceMaker; 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; public MBeanProxyFactory(){ } public MBeanProxyFactory(MBeanServerConnection mbeanServer) { super(); this.mbeanServer = mbeanServer; } /** * 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); } return maker.create(); } private Type getType(String type) throws ClassNotFoundException{ return JmxAsmHelper.getAsmType(type); } /** * 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; MBeanAttributeInfo[] attributes=null; minfo = mbeanServer.getMBeanInfo(objectName); attributes=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{ methods.add(new Signature("get"+name, getType(info.getType()), new Type[0])); }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 the generated interface plus * the MBean interface. It will also be a notification broadcaster if the underlying mbean broadcasts notifications. * @param oname * @return * @throws IOException * @throws ReflectionException * @throws IntrospectionException * @throws InstanceNotFoundException * @throws * @throws MalformedObjectNameException * @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; } private class JMXInvocationHandler implements InvocationHandler, MBean{ private String objectName; private MBeanServerConnection mBeanServer; private InvocationHandler mbeanHandler; private MBeanInfo mbeanInfo; public JMXInvocationHandler(String objectName, MBeanServerConnection mbeanServer, MBeanInfo mBeanInfo, InvocationHandler mbeanHandler) { super(); this.objectName = objectName; this.mBeanServer = mbeanServer; this.mbeanHandler = mbeanHandler; this.mbeanInfo =mBeanInfo; } //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); } else return mbeanHandler.invoke(proxy, method, args); } public MBeanServerConnection getMBeanServer() { return mBeanServer; } public String getObjectName() { return objectName; } public MBeanInfo getMBeanInfo() { return mbeanInfo; } } public MBeanServerConnection getMbeanServer() { return mbeanServer; } 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(); super.setNamePrefix(namePrefix); super.setAttemptLoad(true); } } }