Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 1 | /*
|
| 2 | * Copyright 2005 The Apache Software Foundation
|
| 3 | *
|
| 4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 | * you may not use this file except in compliance with the License.
|
| 6 | * You may obtain a copy of the License at
|
| 7 | *
|
| 8 | * http://www.apache.org/licenses/LICENSE-2.0
|
| 9 | *
|
| 10 | * Unless required by applicable law or agreed to in writing, software
|
| 11 | * distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 | * See the License for the specific language governing permissions and
|
| 14 | * limitations under the License.
|
| 15 | *
|
| 16 | */
|
| 17 |
|
| 18 | package org.apache.felix.jmxintrospector;
|
| 19 |
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 20 | import java.io.IOException;
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 21 | import java.lang.reflect.InvocationHandler;
|
| 22 | import java.lang.reflect.Method;
|
| 23 | import java.lang.reflect.Proxy;
|
| 24 | import java.util.ArrayList;
|
| 25 | import java.util.List;
|
| 26 |
|
Manuel L. Santillan | c51e43c | 2006-09-12 16:01:05 +0000 | [diff] [blame] | 27 | /*import javassist.CannotCompileException;
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 28 | import javassist.ClassPool;
|
| 29 | import javassist.CtClass;
|
| 30 | import javassist.CtMethod;
|
| 31 | import javassist.NotFoundException;
|
Manuel L. Santillan | c51e43c | 2006-09-12 16:01:05 +0000 | [diff] [blame] | 32 | */
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 33 | import javax.management.InstanceNotFoundException;
|
| 34 | import javax.management.IntrospectionException;
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 35 | import javax.management.MBeanAttributeInfo;
|
| 36 | import javax.management.MBeanInfo;
|
| 37 | import javax.management.MBeanNotificationInfo;
|
| 38 | import javax.management.MBeanOperationInfo;
|
| 39 | import javax.management.MBeanServerConnection;
|
| 40 | import javax.management.MBeanServerInvocationHandler;
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 41 | import javax.management.MalformedObjectNameException;
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 42 | import javax.management.NotificationEmitter;
|
| 43 | import javax.management.ObjectName;
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 44 | import javax.management.ReflectionException;
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 45 |
|
Manuel L. Santillan | c51e43c | 2006-09-12 16:01:05 +0000 | [diff] [blame] | 46 | import net.sf.cglib.core.NamingPolicy;
|
| 47 | import net.sf.cglib.core.Predicate;
|
| 48 | import net.sf.cglib.core.Signature;
|
| 49 | import net.sf.cglib.proxy.InterfaceMaker;
|
| 50 |
|
| 51 | import org.objectweb.asm.Type;
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 52 |
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 53 | /**
|
| 54 | *
|
| 55 | * The MBeanProxyFactory is the central element of the jmxintrospector library. The main method is the {@link MBeanProxyFactory#newProxyInstance(String)}
|
| 56 | * method, that accepts an objectname in String form and returns a proxy for that mbean.
|
| 57 | * Internally, it uses the cglib library, although it be made to work with other bytecode libraries. More specifically,prior versions used the Javassist library
|
| 58 | */
|
| 59 |
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 60 | public class MBeanProxyFactory {
|
| 61 | private MBeanServerConnection mbeanServer;
|
| 62 |
|
| 63 | public MBeanProxyFactory(){
|
| 64 | }
|
| 65 |
|
| 66 | public MBeanProxyFactory(MBeanServerConnection mbeanServer) {
|
| 67 | super();
|
| 68 | this.mbeanServer = mbeanServer;
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 69 | }
|
| 70 |
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 71 | /**
|
| 72 | * Internal method for generating the Class object for the dynamically-generated interface for the mbean.
|
| 73 | * The name given to the class is based on the actual classname of the mbean, although some extensions are added
|
| 74 | * to it by the cglib library to avoid namespace clashes.
|
| 75 | * @param oname
|
| 76 | * @return
|
| 77 | * @throws IOException
|
| 78 | * @throws InstanceNotFoundException
|
| 79 | * @throws ClassNotFoundException
|
| 80 | * @throws ReflectionException
|
| 81 | * @throws IntrospectionException
|
| 82 | * @throws
|
| 83 | * @throws MalformedObjectNameException
|
| 84 | * @throws Exception
|
| 85 | */
|
| 86 | private Class getInterface(String oname) throws MalformedObjectNameException, InstanceNotFoundException, IOException, IntrospectionException, ReflectionException {
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 87 | ObjectName objectName=ObjectName.getInstance(oname);
|
Manuel L. Santillan | c51e43c | 2006-09-12 16:01:05 +0000 | [diff] [blame] | 88 | String ifaceName=mbeanServer.getObjectInstance(objectName).getClassName();
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 89 | //uses the ifaceName as the prefix for the class name
|
Manuel L. Santillan | c51e43c | 2006-09-12 16:01:05 +0000 | [diff] [blame] | 90 | InterfaceMaker maker=new MBeanInterfaceMaker(ifaceName);
|
| 91 | for (Signature s : getSignatures(objectName)) {
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 92 | //add each method
|
Manuel L. Santillan | c51e43c | 2006-09-12 16:01:05 +0000 | [diff] [blame] | 93 | maker.add(s, null);
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 94 | }
|
Manuel L. Santillan | c51e43c | 2006-09-12 16:01:05 +0000 | [diff] [blame] | 95 | return maker.create();
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 96 | }
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 97 | private Type getType(String type) throws ClassNotFoundException{
|
Manuel L. Santillan | c51e43c | 2006-09-12 16:01:05 +0000 | [diff] [blame] | 98 | return JmxAsmHelper.getAsmType(type);
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 99 | }
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 100 | /**
|
| 101 | * Internal method for generating the signatures of the mbeans.
|
| 102 | * @param objectName
|
| 103 | * @return
|
| 104 | * @throws IOException
|
| 105 | * @throws ReflectionException
|
| 106 | * @throws IntrospectionException
|
| 107 | * @throws InstanceNotFoundException
|
| 108 | */
|
| 109 | private List<Signature> getSignatures(ObjectName objectName)throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException{
|
Manuel L. Santillan | c51e43c | 2006-09-12 16:01:05 +0000 | [diff] [blame] | 110 | List<Signature> methods=new ArrayList<Signature>();
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 111 | MBeanInfo minfo;
|
| 112 | MBeanAttributeInfo[] attributes=null;
|
| 113 | minfo = mbeanServer.getMBeanInfo(objectName);
|
| 114 | attributes=minfo.getAttributes();
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 115 |
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 116 | for (MBeanAttributeInfo info : attributes) {
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 117 | String name=info.getName().substring(0, 1).toUpperCase()+info.getName().substring(1);
|
| 118 | if(info.isReadable()){
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 119 | //For each readable attribute, we generate a getter method (following the isXX for booleans
|
| 120 | //when it is being used on the remote side)
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 121 | if(info.isIs()){
|
Manuel L. Santillan | c51e43c | 2006-09-12 16:01:05 +0000 | [diff] [blame] | 122 | methods.add(new Signature("is"+name, Type.BOOLEAN_TYPE, new Type[0]));
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 123 | }
|
| 124 | else{
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 125 | try{
|
Manuel L. Santillan | c51e43c | 2006-09-12 16:01:05 +0000 | [diff] [blame] | 126 | methods.add(new Signature("get"+name, getType(info.getType()), new Type[0]));
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 127 | }catch(ClassNotFoundException cnfe){
|
| 128 | System.out.println("JMXINTROSPECTOR WARNING: "+info.getType()+" could not be found. Attribute will not be added to proxy.");
|
| 129 | continue;
|
| 130 | }
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 131 | }
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 132 | }
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 133 | //Same with each writable att, but with setters.
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 134 | if(info.isWritable()){
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 135 | try{
|
Manuel L. Santillan | c51e43c | 2006-09-12 16:01:05 +0000 | [diff] [blame] | 136 | Type [] params=new Type[]{getType(info.getType())};
|
| 137 | Signature s=new Signature("set"+name, Type.VOID_TYPE, params);
|
| 138 | methods.add(s);
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 139 | }catch(ClassNotFoundException cnfe){
|
| 140 | System.out.println("JMXINTROSPECTOR WARNING: "+info.getType()+" could not be found. Attribute will not be added to proxy.");
|
| 141 | continue;
|
| 142 | }
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 143 | }
|
| 144 | }
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 145 | //same for each operation
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 146 | for (MBeanOperationInfo info : minfo.getOperations()) {
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 147 | try{
|
Manuel L. Santillan | c51e43c | 2006-09-12 16:01:05 +0000 | [diff] [blame] | 148 | Type[] params=new Type[info.getSignature().length];
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 149 | for (int i = 0; i < params.length; i++) {
|
Manuel L. Santillan | c51e43c | 2006-09-12 16:01:05 +0000 | [diff] [blame] | 150 | params[i]=getType(info.getSignature()[i].getType());
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 151 | }
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 152 |
|
Manuel L. Santillan | c51e43c | 2006-09-12 16:01:05 +0000 | [diff] [blame] | 153 | Signature s=new Signature(info.getName(), getType(info.getReturnType()), params);
|
| 154 | methods.add(s);
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 155 | }catch(ClassNotFoundException cnfe){
|
| 156 | System.out.println("JMXINTROSPECTOR WARNING: "+info.toString()+" could not be created. Operation will not be added to proxy.");
|
| 157 | continue;
|
| 158 |
|
| 159 | }
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 160 |
|
| 161 | }
|
| 162 | return methods;
|
| 163 | }
|
| 164 | /**
|
| 165 | * Returns a proxy object for the MBean specified by the object name oname in the
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 166 | * mbean server associated with this factory. This proxy object implements the generated interface plus
|
| 167 | * the MBean interface. It will also be a notification broadcaster if the underlying mbean broadcasts notifications.
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 168 | * @param oname
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 169 | * @return
|
| 170 | * @throws IOException
|
| 171 | * @throws ReflectionException
|
| 172 | * @throws IntrospectionException
|
| 173 | * @throws InstanceNotFoundException
|
| 174 | * @throws
|
| 175 | * @throws MalformedObjectNameException
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 176 | * @throws Exception
|
| 177 | */
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 178 | public Object newProxyInstance(String oname) throws MalformedObjectNameException, InstanceNotFoundException, IntrospectionException, ReflectionException, IOException{
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 179 | ObjectName objectName=ObjectName.getInstance(oname);
|
| 180 | Class iface=getInterface(oname);
|
| 181 | MBeanInfo info=mbeanServer.getMBeanInfo(objectName);
|
| 182 | boolean isBroadcaster=false;
|
| 183 | MBeanNotificationInfo[] notifs=info.getNotifications();
|
| 184 | if (notifs!=null && notifs.length!=0) isBroadcaster=true;
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 185 | //We first create the proxy for the remote mbean. If broadcasting supported, then it adds the broadcasting interface
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 186 | Object proxy=MBeanServerInvocationHandler.newProxyInstance(mbeanServer, objectName, iface, isBroadcaster);
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 187 | //We get the underlying invocation handler, needed for the wrapper handler. The wrapper adds the mbean interface functionality
|
| 188 | //and integrates JMX invocation handler.
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 189 | InvocationHandler h=Proxy.getInvocationHandler(proxy);
|
| 190 | InvocationHandler wrapper=new JMXInvocationHandler(oname,mbeanServer, mbeanServer.getMBeanInfo(objectName), h);
|
| 191 | Class[] ifaces;
|
| 192 | if (isBroadcaster) {
|
| 193 | ifaces=new Class[]{iface, NotificationEmitter.class, MBean.class};
|
| 194 | }else ifaces=new Class[]{iface, MBean.class};
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 195 | //finally, we create the proxy with the appropriate classloader, the interfaces and the invocation handler
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 196 | Object mbeanProxy=Proxy.newProxyInstance(proxy.getClass().getClassLoader(), ifaces, wrapper);
|
| 197 | return mbeanProxy;
|
| 198 | }
|
| 199 | private class JMXInvocationHandler implements InvocationHandler, MBean{
|
| 200 | private String objectName;
|
| 201 | private MBeanServerConnection mBeanServer;
|
| 202 | private InvocationHandler mbeanHandler;
|
| 203 | private MBeanInfo mbeanInfo;
|
| 204 |
|
| 205 | public JMXInvocationHandler(String objectName, MBeanServerConnection mbeanServer, MBeanInfo mBeanInfo, InvocationHandler mbeanHandler) {
|
| 206 | super();
|
| 207 | this.objectName = objectName;
|
| 208 | this.mBeanServer = mbeanServer;
|
| 209 | this.mbeanHandler = mbeanHandler;
|
| 210 | this.mbeanInfo =mBeanInfo;
|
| 211 |
|
| 212 | }
|
| 213 |
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 214 | //FIXME: hashCode() and equals do not work if not exposed in management interface. I have not thought of a workaround for that yet.
|
| 215 | //note that this is needed for hashmaps and comparisons.
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 216 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 217 | //decide wether to invoke the JMXInvocationHandler we have implemented
|
| 218 | //or use the one that was created by the MBeanServerInvocationHandler
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 219 | if(method.getDeclaringClass().equals(MBean.class)){
|
| 220 | return this.getClass().getMethod(method.getName(), null).invoke(this, args);
|
| 221 | }
|
| 222 | else return mbeanHandler.invoke(proxy, method, args);
|
| 223 | }
|
| 224 |
|
| 225 | public MBeanServerConnection getMBeanServer() {
|
| 226 | return mBeanServer;
|
| 227 | }
|
| 228 |
|
| 229 | public String getObjectName() {
|
| 230 | return objectName;
|
| 231 | }
|
| 232 |
|
| 233 | public MBeanInfo getMBeanInfo() {
|
| 234 | return mbeanInfo;
|
| 235 | }
|
| 236 |
|
| 237 |
|
| 238 |
|
| 239 | }
|
| 240 |
|
| 241 | public MBeanServerConnection getMbeanServer() {
|
| 242 | return mbeanServer;
|
| 243 | }
|
| 244 |
|
| 245 | public void setMbeanServer(MBeanServerConnection mbs) {
|
| 246 | this.mbeanServer = mbs;
|
| 247 | }
|
Manuel L. Santillan | 7f25bab | 2006-09-27 23:03:57 +0000 | [diff] [blame] | 248 | /**
|
| 249 | * This class is used to be able to modify the super class,
|
| 250 | * because setNamePrefix and setAttemptLoad are protected methods
|
| 251 | *
|
| 252 | */
|
Manuel L. Santillan | c51e43c | 2006-09-12 16:01:05 +0000 | [diff] [blame] | 253 | private class MBeanInterfaceMaker extends InterfaceMaker{
|
| 254 | public MBeanInterfaceMaker(String namePrefix) {
|
| 255 | super();
|
| 256 | super.setNamePrefix(namePrefix);
|
| 257 | super.setAttemptLoad(true);
|
| 258 | }
|
| 259 | }
|
Manuel L. Santillan | 0b66800 | 2006-09-09 16:06:26 +0000 | [diff] [blame] | 260 |
|
| 261 | }
|