blob: 472a58c5a1b72c1a7f65df4d1beb8069f091f544 [file] [log] [blame]
Manuel L. Santillan0b668002006-09-09 16:06:26 +00001/*
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
18package org.apache.felix.jmxintrospector;
19
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +000020import java.io.IOException;
Manuel L. Santillan0b668002006-09-09 16:06:26 +000021import java.lang.reflect.InvocationHandler;
22import java.lang.reflect.Method;
23import java.lang.reflect.Proxy;
24import java.util.ArrayList;
25import java.util.List;
26
Manuel L. Santillanc51e43c2006-09-12 16:01:05 +000027/*import javassist.CannotCompileException;
Manuel L. Santillan0b668002006-09-09 16:06:26 +000028import javassist.ClassPool;
29import javassist.CtClass;
30import javassist.CtMethod;
31import javassist.NotFoundException;
Manuel L. Santillanc51e43c2006-09-12 16:01:05 +000032*/
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +000033import javax.management.InstanceNotFoundException;
34import javax.management.IntrospectionException;
Manuel L. Santillan0b668002006-09-09 16:06:26 +000035import javax.management.MBeanAttributeInfo;
36import javax.management.MBeanInfo;
37import javax.management.MBeanNotificationInfo;
38import javax.management.MBeanOperationInfo;
39import javax.management.MBeanServerConnection;
40import javax.management.MBeanServerInvocationHandler;
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +000041import javax.management.MalformedObjectNameException;
Manuel L. Santillan0b668002006-09-09 16:06:26 +000042import javax.management.NotificationEmitter;
43import javax.management.ObjectName;
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +000044import javax.management.ReflectionException;
Manuel L. Santillan0b668002006-09-09 16:06:26 +000045
Manuel L. Santillanc51e43c2006-09-12 16:01:05 +000046import net.sf.cglib.core.NamingPolicy;
47import net.sf.cglib.core.Predicate;
48import net.sf.cglib.core.Signature;
49import net.sf.cglib.proxy.InterfaceMaker;
50
51import org.objectweb.asm.Type;
Manuel L. Santillan0b668002006-09-09 16:06:26 +000052
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +000053/**
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. Santillan0b668002006-09-09 16:06:26 +000060public 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. Santillan0b668002006-09-09 16:06:26 +000069 }
70
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +000071 /**
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. Santillan0b668002006-09-09 16:06:26 +000087 ObjectName objectName=ObjectName.getInstance(oname);
Manuel L. Santillanc51e43c2006-09-12 16:01:05 +000088 String ifaceName=mbeanServer.getObjectInstance(objectName).getClassName();
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +000089 //uses the ifaceName as the prefix for the class name
Manuel L. Santillanc51e43c2006-09-12 16:01:05 +000090 InterfaceMaker maker=new MBeanInterfaceMaker(ifaceName);
91 for (Signature s : getSignatures(objectName)) {
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +000092 //add each method
Manuel L. Santillanc51e43c2006-09-12 16:01:05 +000093 maker.add(s, null);
Manuel L. Santillan0b668002006-09-09 16:06:26 +000094 }
Manuel L. Santillanc51e43c2006-09-12 16:01:05 +000095 return maker.create();
Manuel L. Santillan0b668002006-09-09 16:06:26 +000096 }
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +000097 private Type getType(String type) throws ClassNotFoundException{
Manuel L. Santillanc51e43c2006-09-12 16:01:05 +000098 return JmxAsmHelper.getAsmType(type);
Manuel L. Santillan0b668002006-09-09 16:06:26 +000099 }
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000100 /**
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. Santillanc51e43c2006-09-12 16:01:05 +0000110 List<Signature> methods=new ArrayList<Signature>();
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000111 MBeanInfo minfo;
112 MBeanAttributeInfo[] attributes=null;
113 minfo = mbeanServer.getMBeanInfo(objectName);
114 attributes=minfo.getAttributes();
Manuel L. Santillan0b668002006-09-09 16:06:26 +0000115
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000116 for (MBeanAttributeInfo info : attributes) {
Manuel L. Santillan0b668002006-09-09 16:06:26 +0000117 String name=info.getName().substring(0, 1).toUpperCase()+info.getName().substring(1);
118 if(info.isReadable()){
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000119 //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. Santillan0b668002006-09-09 16:06:26 +0000121 if(info.isIs()){
Manuel L. Santillanc51e43c2006-09-12 16:01:05 +0000122 methods.add(new Signature("is"+name, Type.BOOLEAN_TYPE, new Type[0]));
Manuel L. Santillan0b668002006-09-09 16:06:26 +0000123 }
124 else{
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000125 try{
Manuel L. Santillanc51e43c2006-09-12 16:01:05 +0000126 methods.add(new Signature("get"+name, getType(info.getType()), new Type[0]));
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000127 }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. Santillan0b668002006-09-09 16:06:26 +0000131 }
Manuel L. Santillan0b668002006-09-09 16:06:26 +0000132 }
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000133 //Same with each writable att, but with setters.
Manuel L. Santillan0b668002006-09-09 16:06:26 +0000134 if(info.isWritable()){
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000135 try{
Manuel L. Santillanc51e43c2006-09-12 16:01:05 +0000136 Type [] params=new Type[]{getType(info.getType())};
137 Signature s=new Signature("set"+name, Type.VOID_TYPE, params);
138 methods.add(s);
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000139 }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. Santillan0b668002006-09-09 16:06:26 +0000143 }
144 }
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000145 //same for each operation
Manuel L. Santillan0b668002006-09-09 16:06:26 +0000146 for (MBeanOperationInfo info : minfo.getOperations()) {
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000147 try{
Manuel L. Santillanc51e43c2006-09-12 16:01:05 +0000148 Type[] params=new Type[info.getSignature().length];
Manuel L. Santillan0b668002006-09-09 16:06:26 +0000149 for (int i = 0; i < params.length; i++) {
Manuel L. Santillanc51e43c2006-09-12 16:01:05 +0000150 params[i]=getType(info.getSignature()[i].getType());
Manuel L. Santillan0b668002006-09-09 16:06:26 +0000151 }
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000152
Manuel L. Santillanc51e43c2006-09-12 16:01:05 +0000153 Signature s=new Signature(info.getName(), getType(info.getReturnType()), params);
154 methods.add(s);
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000155 }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. Santillan0b668002006-09-09 16:06:26 +0000160
161 }
162 return methods;
163 }
164 /**
165 * Returns a proxy object for the MBean specified by the object name oname in the
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000166 * 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. Santillan0b668002006-09-09 16:06:26 +0000168 * @param oname
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000169 * @return
170 * @throws IOException
171 * @throws ReflectionException
172 * @throws IntrospectionException
173 * @throws InstanceNotFoundException
174 * @throws
175 * @throws MalformedObjectNameException
Manuel L. Santillan0b668002006-09-09 16:06:26 +0000176 * @throws Exception
177 */
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000178 public Object newProxyInstance(String oname) throws MalformedObjectNameException, InstanceNotFoundException, IntrospectionException, ReflectionException, IOException{
Manuel L. Santillan0b668002006-09-09 16:06:26 +0000179 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. Santillan7f25bab2006-09-27 23:03:57 +0000185 //We first create the proxy for the remote mbean. If broadcasting supported, then it adds the broadcasting interface
Manuel L. Santillan0b668002006-09-09 16:06:26 +0000186 Object proxy=MBeanServerInvocationHandler.newProxyInstance(mbeanServer, objectName, iface, isBroadcaster);
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000187 //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. Santillan0b668002006-09-09 16:06:26 +0000189 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. Santillan7f25bab2006-09-27 23:03:57 +0000195 //finally, we create the proxy with the appropriate classloader, the interfaces and the invocation handler
Manuel L. Santillan0b668002006-09-09 16:06:26 +0000196 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. Santillan7f25bab2006-09-27 23:03:57 +0000214 //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. Santillan0b668002006-09-09 16:06:26 +0000216 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Manuel L. Santillan7f25bab2006-09-27 23:03:57 +0000217 //decide wether to invoke the JMXInvocationHandler we have implemented
218 //or use the one that was created by the MBeanServerInvocationHandler
Manuel L. Santillan0b668002006-09-09 16:06:26 +0000219 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. Santillan7f25bab2006-09-27 23:03:57 +0000248 /**
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. Santillanc51e43c2006-09-12 16:01:05 +0000253 private class MBeanInterfaceMaker extends InterfaceMaker{
254 public MBeanInterfaceMaker(String namePrefix) {
255 super();
256 super.setNamePrefix(namePrefix);
257 super.setAttemptLoad(true);
258 }
259 }
Manuel L. Santillan0b668002006-09-09 16:06:26 +0000260
261}