View Javadoc

1   /*
2    * Copyright (c) 2004 Peter Antman, Mogul  <peter.antman@mogul.com>
3    *
4    * $Id: MBeanProxy.java,v 1.1.1.1 2004/05/19 12:15:39 pra Exp $
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 2 of the License, or (at your option) any later version
10   * 
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   * 
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, write to the Free Software
18   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   */
20  package org.backsource.jmx;
21  import java.util.HashMap;
22  import java.util.Hashtable;
23  import java.rmi.RemoteException;
24  import java.lang.reflect.Method;
25  import java.lang.reflect.Proxy;
26  import java.lang.reflect.InvocationHandler;
27  import java.lang.reflect.InvocationTargetException;
28  import javax.management.*;
29  
30  import javax.naming.InitialContext;
31  import javax.naming.Context;
32  import javax.naming.NamingException;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory; 
36  
37  import org.jboss.jmx.adaptor.rmi.RMIAdaptor;
38  /***
39   * A proxy and proxy factory to access MBean as normal classes. Server may
40   * be both local or remote.
41   *
42   *
43   * @author <a href="mailto:pra@mogul.com">Peter Antman</a>
44   * @version $Revision: 1.1.1.1 $
45   */
46  
47  public class MBeanProxy implements InvocationHandler,  java.io.Serializable{
48     private static Log log = LogFactory.getLog(MBeanProxy.class);
49     //   private static final RMI_ADAPTOR_NAME = "jmx/rmi/RMIAdaptor";
50     private static final String RMI_ADAPTOR_NAME = "jmx/invoker/RMIAdaptor";
51     
52     /*** ObjectName of the MBean to invoke */
53     ObjectName name;
54     /*** JNDIUrl of a remote MBeanServer to invoke,i.e this is NOT the remtote version of the MBean server this proxy was created in, but in another JNDI context
55      */
56     String jndiURL;
57     /***
58      * The local mbean server, which will not be serialized and is therefore
59      * null when used remotely!
60      */
61     transient MBeanServer localServer;
62     /***
63      * The RMI version of the MBean server.
64      */
65     RMIAdaptor remoteServer;
66     
67     private HashMap attributeMap  = new HashMap();
68   /***
69      * Create a proxy wich will invoke the MBean by ObjectName name.
70      *
71      * <p>This proxy may be used both locally (the invokation is done directly against the MBean server) or remotely.
72      */
73     public MBeanProxy(String name) 
74        throws OperationsException, NamingException {
75        this(name,null);
76        
77     }
78  
79     /***
80      * Create a proxy wich will invoke the MBean by ObjectName name.
81      *
82      * <p>if jndiURL is non null, the MBean server will be looked up with
83      * that url as its java.naming.provider.url. This means that the proxy
84      * will actually proxy an MBean in <b>another</b> mbean server.</p>
85      */
86     public MBeanProxy(String name, String jndiURL) 
87        throws OperationsException, NamingException {
88        try {
89           this.name = new ObjectName(name);
90           this.jndiURL = jndiURL; 
91           MBeanInfo info = null;
92  
93          
94           // if JNDIUrl is set, we only have a remote server.
95           if ( jndiURL != null) {
96              remoteServer = getRemoteServer(jndiURL);
97              info = remoteServer.getMBeanInfo(this.name);
98           } // end of if ()
99           // othervise we have both. As long as the localServer is non null we will
100          // use that. But if serialized, only the remote server will be available.
101          else {
102          
103             localServer = (MBeanServer) MBeanServerFactory.findMBeanServer(null).iterator().next();
104             remoteServer = getRemoteServer(null);
105             info = localServer.getMBeanInfo(this.name);
106          } // end of else
107 
108          MBeanAttributeInfo[] attributes = info.getAttributes();
109       
110          for (int i = 0; i < attributes.length; ++i)
111             attributeMap.put(attributes[i].getName(), attributes[i]);
112       }catch (InstanceNotFoundException e)
113          {
114             throw new NamingException("Object name " + name + " not found: " + e.toString());
115          }
116       catch (IntrospectionException e)
117          {
118             throw new OperationsException(e.toString());
119          }
120       catch (ReflectionException e)
121          {
122             throw new OperationsException(e.toString());
123          }
124       catch (MalformedObjectNameException e)
125          {
126             throw new OperationsException(e.toString());
127          }
128       catch (RemoteException e)
129          {
130             throw new OperationsException(e.toString());
131          }
132    }
133 
134    private static RMIAdaptor getRemoteServer(String jndiURL) throws NamingException{
135       Context ctx = new InitialContext();
136       RMIAdaptor adaptor = null;
137       // We allways use JBoss, fill in with remote URL
138       if ( jndiURL != null) {
139          
140          Hashtable h = ctx.getEnvironment();
141          h.put("java.naming.provider.url", jndiURL);
142          ctx = new InitialContext(h);
143       } // end of if ()
144       try {
145          adaptor = (RMIAdaptor)ctx.lookup(RMI_ADAPTOR_NAME);
146       } catch (NamingException e) {
147          // This make it more comptible with MBeanProxy in jboss,
148          // i.e its possible to use locally
149          log.warn("Could not lookup RMI adapter;remove server will be null:" +e);
150       } // end of try-catch
151       return adaptor;
152    }
153    
154    public Object invoke(Object proxy, Method method, Object[] args) throws Exception
155    {
156       try {
157          String methodName = method.getName();
158 
159          if (methodName.startsWith("get") && args == null)
160             {
161                String attrName = methodName.substring(3, methodName.length());
162                
163                MBeanAttributeInfo info = (MBeanAttributeInfo)attributeMap.get(attrName);
164                if (info != null)
165                   {
166                      String retType  = method.getReturnType().getName();
167                   
168                      if (retType.equals(info.getType())) 
169                         {
170                            if ( localServer != null) {
171                               return localServer.getAttribute(name, attrName);
172                            } else if (remoteServer != null ) {
173                               return remoteServer.getAttribute(name, attrName);
174                            } else {
175                               throw new Exception("No server to invoke");
176                            } // end of else
177                      
178 
179 
180                         }
181                   }
182             }
183             
184          else if (methodName.startsWith("is") && args == null)
185             {
186                String attrName = methodName.substring(2, methodName.length());
187                
188                MBeanAttributeInfo info = (MBeanAttributeInfo)attributeMap.get(attrName);
189                if (info != null && info.isIs())
190                   {
191                      Class retType = method.getReturnType();
192                   
193                      if (retType.equals(Boolean.class) || retType.equals(Boolean.TYPE))
194                         {
195                            if ( localServer != null) {
196                               return localServer.getAttribute(name, attrName);
197                            } else if (remoteServer != null ) {
198                               return remoteServer.getAttribute(name, attrName);
199                            } else {
200                               throw new Exception("No server to invoke");
201                            } // end of else
202                         }
203                   }
204             }
205             
206          else if (methodName.startsWith("set") && args != null && args.length == 1)
207             {
208                String attrName = methodName.substring(3, methodName.length());
209                
210                MBeanAttributeInfo info = (MBeanAttributeInfo)attributeMap.get(attrName);
211                if (info != null && method.getReturnType().equals(Void.TYPE))
212                   {
213                      ClassLoader cl = Thread.currentThread().getContextClassLoader();
214                      
215                      Class signatureClass = null;
216                      String classType     = info.getType();
217                      
218                      if (isPrimitive(classType))
219                         signatureClass = getPrimitiveClass(classType);
220                      else
221                         signatureClass = cl.loadClass(info.getType());
222                      
223                      if (signatureClass.isAssignableFrom(args[0].getClass()))
224                         {
225                            if ( localServer != null) {
226                               localServer.setAttribute(name, new Attribute(attrName, args[0]));
227                            } else if (remoteServer != null ) {
228                               remoteServer.setAttribute(name, new Attribute(attrName, args[0]));
229                            } else {
230                               throw new Exception("No server to invoke");
231                            } // end of else
232                         
233                         
234                         
235                            return null;
236                         }
237                   }
238             }
239             
240          String[] signature = null;
241             
242          if (args != null)
243             {
244                signature = new String[args.length];
245                Class[] sign = method.getParameterTypes();
246                
247                for (int i = 0; i < sign.length; ++i)
248                   signature[i] = sign[i].getName();
249             }
250          if ( localServer != null) {
251             return localServer.invoke(name, methodName, args, signature);
252          } else if (remoteServer != null ) {
253             return remoteServer.invoke(name, methodName, args, signature);
254          } else {
255             throw new Exception("No server to invoke");
256          } // end of else
257       }
258          
259       // InstanceNotFound, AttributeNotFound and InvalidAttributeValue
260       // are not exceptions declared in the mgmt interface and therefore
261       // must be rethrown as runtime exceptions to avoid UndeclaredThrowable
262       // exceptions on the client
263       catch (InstanceNotFoundException e)
264          {
265             throw new RuntimeException("Instance not found: " + e.toString());
266          }
267       catch (AttributeNotFoundException e)
268          {
269             throw new RuntimeException("Attribute not found: " + e.toString());
270          }
271       catch (InvalidAttributeValueException e)
272          {
273             throw new RuntimeException("Invalid attribute value: " + e.toString());
274          }
275       catch (MBeanException e)
276          {
277             // assuming MBeanException only wraps mgmt interface "application" 
278             // exceptions therefore we can safely rethrow the target exception
279             // as its declared in the mgmt interface
280             throw e.getTargetException();
281          }
282       catch (ReflectionException e)
283          {
284             // use of reflection exception is inconsistent in the API so the 
285             // safest bet is to rethrow a runtime exception
286             
287             Exception target = e.getTargetException();
288             log.error(target);
289             if (target instanceof RuntimeException)
290                throw target;
291             else
292                throw new RuntimeException(target.toString());
293          }
294       catch (RuntimeOperationsException e)
295          {
296             // target is always a runtime exception, so its ok to throw it from here
297             throw e.getTargetException();
298          }
299       catch (RuntimeMBeanException e)
300          {
301             // target is always a runtime exception, so its ok to throw it from here
302             throw e.getTargetException();
303          }
304       catch (RuntimeErrorException e)
305          {
306             // just unwrap and throw the actual error
307             throw e.getTargetError();
308          }
309       catch (RemoteException e) {
310          if ( e.detail != null && e.detail instanceof Exception ) {
311             throw (Exception)e.detail;
312          } else {    
313             throw new RuntimeException(e.toString());
314          } // end of else
315                         
316       } // end of catch
317          
318    }
319       private boolean isPrimitive(String type)
320    {
321       if (type.equals(Integer.TYPE.getName()))   return true;
322       if (type.equals(Long.TYPE.getName()))      return true;
323       if (type.equals(Boolean.TYPE.getName()))   return true;
324       if (type.equals(Byte.TYPE.getName()))      return true;
325       if (type.equals(Character.TYPE.getName())) return true;
326       if (type.equals(Short.TYPE.getName()))     return true;
327       if (type.equals(Float.TYPE.getName()))     return true;
328       if (type.equals(Double.TYPE.getName()))    return true;
329       if (type.equals(Void.TYPE.getName()))      return true;
330       
331       return false;
332    }
333 
334    private Class getPrimitiveClass(String type)
335    {
336       if (type.equals(Integer.TYPE.getName()))  return Integer.TYPE;
337       if (type.equals(Long.TYPE.getName()))     return Long.TYPE;
338       if (type.equals(Boolean.TYPE.getName()))  return Boolean.TYPE;
339       if (type.equals(Byte.TYPE.getName()))     return Byte.TYPE;
340       if (type.equals(Character.TYPE.getName()))return Character.TYPE;
341       if (type.equals(Short.TYPE.getName()))    return Short.TYPE;
342       if (type.equals(Float.TYPE.getName()))    return Float.TYPE;
343       if (type.equals(Double.TYPE.getName()))   return Double.TYPE;
344       if (type.equals(Void.TYPE.getName()))     return Void.TYPE;
345       
346       return null;
347    }
348    
349 }// MBeanProxy