All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.mx.mxbean.MXBeanInvocationHandler Maven / Gradle / Ivy

There is a newer version: 6.0.0.M1
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2008, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.mx.mxbean;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import javax.management.Attribute;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.MBeanServerConnection;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.openmbean.OpenMBeanAttributeInfo;
import javax.management.openmbean.OpenMBeanInfo;
import javax.management.openmbean.OpenMBeanOperationInfo;
import javax.management.openmbean.OpenMBeanParameterInfo;
import javax.management.openmbean.OpenType;

import org.jboss.mx.util.JMXExceptionDecoder;

/**
 * MXBeanInvocationHandler.
 * 
 * @author Adrian Brock
 * @version $Revision: 1.1 $
 */
public class MXBeanInvocationHandler implements InvocationHandler, Serializable
{
   /** The serialVersionUID */
   private static final long serialVersionUID = -2872014223541692039L;
   
   private static final Class[] LISTENER = new Class[] { NotificationListener.class };
   private static final Class[] TRIPLET = new Class[] { NotificationListener.class, NotificationFilter.class, Object.class };

   private static final Method EQUALS;
   private static final Method HASH_CODE;
   private static final Method TO_STRING;

   private static final Method ADD_NOTIFICATION_LISTENER;
   private static final Method GET_NOTIFICATION_INFO;
   private static final Method REMOVE_NOTIFICATION_LISTENER;
   private static final Method REMOVE_NOTIFICATION_LISTENER_TRIPLET;

   /** The connection */
   private MBeanServerConnection mbeanServerConnection;
   
   /** The interface */
   private Class mxbeanInterface;
   
   /** The object name */
   private ObjectName objectName;

   /** The method mappings */
   private transient Map mappings;
   
   /** The MBean Info */
   private transient OpenMBeanInfo mbeanInfo;

   static
   {
      try
      {
         ADD_NOTIFICATION_LISTENER = NotificationBroadcaster.class.getDeclaredMethod("addNotificationListener",  TRIPLET);
         GET_NOTIFICATION_INFO = NotificationBroadcaster.class.getDeclaredMethod("getNotificationInfo",  new Class[0]);
         REMOVE_NOTIFICATION_LISTENER = NotificationBroadcaster.class.getDeclaredMethod("removeNotificationListener",  LISTENER);
         REMOVE_NOTIFICATION_LISTENER_TRIPLET = NotificationEmitter.class.getDeclaredMethod("removeNotificationListener",  TRIPLET);
         EQUALS = Object.class.getDeclaredMethod("equals",  new Class[] { Object.class });
         HASH_CODE = Object.class.getDeclaredMethod("hashCode",  new Class[0]);
         TO_STRING = Object.class.getDeclaredMethod("toString",  new Class[0]);
      }
      catch (Exception e)
      {
         throw new RuntimeException(e);
      }
   }
   
   /**
    * Create a new MXBeanInvocationHandler.
    * 
    * @param mbeanServerConnection the connection
    * @param mxbeanInterface the interface
    * @param objectName the object name
    */
   public MXBeanInvocationHandler(MBeanServerConnection mbeanServerConnection, Class mxbeanInterface, ObjectName objectName)
   {
      if (mbeanServerConnection == null)
         throw new IllegalArgumentException("Null mbeanServerConnection");
      if (mxbeanInterface == null)
         throw new IllegalArgumentException("Null mxmbeanInterface");
      if (objectName == null)
         throw new IllegalArgumentException("Null objectName");

      this.mbeanServerConnection = mbeanServerConnection;
      this.mxbeanInterface = mxbeanInterface;
      this.objectName = objectName;
   }

   /**
    * Get the mbeanServerConnection.
    * 
    * @return the mbeanServerConnection.
    */
   public MBeanServerConnection getMBeanServerConnection()
   {
      return mbeanServerConnection;
   }

   /**
    * Get the mxbeanInterface.
    * 
    * @return the mxbeanInterface.
    */
   public Class getMXBeanInterface()
   {
      return mxbeanInterface;
   }

   /**
    * Get the objectName.
    * 
    * @return the objectName.
    */
   public ObjectName getObjectName()
   {
      return objectName;
   }

   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
   {
      try
      {
         return getAction(proxy, method).perform(args);
      }
      catch (Throwable t)
      {
         throw JMXExceptionDecoder.decode(t);
      }
   }

   /**
    * Get the actions for this method
    * @param proxy the proxy
    * @param method the method
    * @return the action
    * @throws Throwable for any error
    */
   private Action getAction(Object proxy, Method method) throws Throwable
   {
      // Doesn't really matter if the mappings are
      // setup twice by two different threads, they are the same.
      if (mappings == null)
         mappings = getMappings(proxy);

      // Check the action
      Action result = mappings.get(method);
      if (result == null)
         throw new UnsupportedOperationException("Unknown method: " + method);

      // Return the result
      return result;
   }

   /**
    * Set up the mappings
    * 
    * @param proxy the proxy 
    * @return the mapping
    * @throws Throwable for any error
    */
   private Map getMappings(Object proxy) throws Throwable
   {
      mbeanInfo = (OpenMBeanInfo) mbeanServerConnection.getMBeanInfo(objectName);
      Map attributesMap = new HashMap();
      MBeanAttributeInfo[] attributes = mbeanInfo.getAttributes();
      for (int i = 0; i < attributes.length; ++i)
      {
         OpenMBeanAttributeInfo openAttribute = (OpenMBeanAttributeInfo) attributes[i];
         attributesMap.put(openAttribute.getName(), openAttribute);
      }
      MBeanOperationInfo[] operations = mbeanInfo.getOperations();
      
      Map result = new HashMap();

      Class[] interfaces = proxy.getClass().getInterfaces();
      for (int i = 0; i < interfaces.length; ++i)
      {
         if (NotificationBroadcaster.class.isAssignableFrom(interfaces[i]))
         {
            result.put(ADD_NOTIFICATION_LISTENER, new AddNotificationListenerAction());
            result.put(GET_NOTIFICATION_INFO, new GetNotificationInfoAction());
            result.put(REMOVE_NOTIFICATION_LISTENER, new RemoveNotificationListenerAction());
            result.put(REMOVE_NOTIFICATION_LISTENER_TRIPLET, new RemoveNotificationListenerTripletAction());
         }
         else
         {
            Method[] methods = interfaces[i].getMethods();
            for (Method method : methods)
            {
               String methodName = method.getName();
               Class returnType = method.getReturnType();
               Class[] parameterTypes = method.getParameterTypes();

               // Getter
               if (methodName.startsWith("get")  && 
                   methodName.length() > 3 &&
                   Void.TYPE.equals(returnType) == false &&
                   parameterTypes.length == 0)
               {
                  String name = methodName.substring(3);
                  OpenMBeanAttributeInfo attribute = attributesMap.get(name);
                  if (attribute != null)
                  {
                     Type type = method.getGenericReturnType();
                     result.put(method, new GetAction(attribute, type));
                     continue;
                  }
               }
               // Getter (is)
               else if(methodName.startsWith("is")  && 
                     methodName.length() > 2 &&
                     Boolean.TYPE.equals(returnType) &&
                     parameterTypes.length == 0)
               {
                  String name = methodName.substring(2);
                  OpenMBeanAttributeInfo attribute = attributesMap.get(name);
                  if (attribute != null)
                  {
                     Type type = method.getGenericReturnType();
                     result.put(method, new GetAction(attribute, type));
                     continue;
                  }
               }
               // Setter
               else if(methodName.startsWith("set")  && 
                     methodName.length() > 3 &&
                     Void.TYPE.equals(returnType) &&
                     parameterTypes.length == 1)
               {
                  String name = methodName.substring(3);
                  OpenMBeanAttributeInfo attribute = attributesMap.get(name);
                  if (attribute != null)
                  {
                     result.put(method, new SetAction(attribute));
                     continue;
                  }
               }
               // Invoker
               OpenMBeanOperationInfo operation = findOperation(methodName, method.getGenericParameterTypes(), operations);
               if (operation != null)
               {
                  String[] signature = getSignature(method);
                  Type type = method.getGenericReturnType();
                  result.put(method, new InvokeAction(operation, signature, type));
               }
               else
               {
                  result.put(method, new InvalidAction(method));
               }
            }
         }
      }

      // Add the Object mappings
      result.put(EQUALS, new EqualsAction());
      result.put(HASH_CODE, new HashCodeAction());
      result.put(TO_STRING, new ToStringAction());
      
      return result;
   }

   private static OpenMBeanOperationInfo findOperation(String name, Type[] parameterTypes, MBeanOperationInfo[] operations)
   {
      OpenType[] signature = getSignature(parameterTypes);
      for (int i = 0; i < operations.length; ++i)
      {
         if (operations[i].getName().equals(name) == false)
            continue;
         MBeanParameterInfo[] parameters = operations[i].getSignature();
         boolean match = true;
         for (int p = 0; p < parameters.length && match; ++p)
         {
            OpenMBeanParameterInfo openMBeanParameterInfo = (OpenMBeanParameterInfo) parameters[p];
            if (signature[p].equals(openMBeanParameterInfo.getOpenType()) == false)
               match = false;
         }
         if (match)
            return (OpenMBeanOperationInfo) operations[i];
      }
      return null;
   }
   
   private static String[] getSignature(final Method method)
   {
      Class[] parameterTypes = method.getParameterTypes();
      String[] signature = new String[parameterTypes.length];
      for (int p = 0; p < parameterTypes.length; ++p)
          signature[p] = parameterTypes[p].getName();
      return signature;
   }
   
   private static OpenType[] getSignature(final Type[] parameterTypes)
   {
      OpenType[] signature = new OpenType[parameterTypes.length];
      for (int p = 0; p < parameterTypes.length; ++p)
          signature[p] = MXBeanUtils.getOpenType(parameterTypes[p]);
      return signature;
   }

   private interface Action
   {
      public Object perform(Object[] args) throws Throwable;
   }

   private class GetAction implements Action
   {
      private OpenMBeanAttributeInfo attribute;
      private Type type;

      public GetAction(OpenMBeanAttributeInfo attribute, Type type)
      {
         this.attribute = attribute;
         this.type = type;
      }

      public Object perform(Object[] args) throws Throwable
      {
         Object result = mbeanServerConnection.getAttribute(objectName, attribute.getName());
         
         return MXBeanUtils.reconstruct(attribute.getOpenType(), type, result, "Get attribute: " + attribute.getName());
      }
   }

   private class SetAction implements Action
   {
      private OpenMBeanAttributeInfo attribute;

      public SetAction(OpenMBeanAttributeInfo attribute)
      {
         this.attribute = attribute;
      }

      public Object perform(Object[] args) throws Throwable
      {
         Object value = MXBeanUtils.construct(attribute.getOpenType(), args[0], "Set attribute: " + attribute.getName());
         
         Attribute attr = new Attribute(attribute.getName(), value);
         mbeanServerConnection.setAttribute(objectName, attr);
         return null;
      }
   }

   private class InvokeAction implements Action
   {
      private OpenMBeanOperationInfo operation;
      private String[] signature;
      private Type type;

      public InvokeAction(OpenMBeanOperationInfo operation, String[] signature, Type type)
      {
         this.operation = operation;
         this.signature = signature;
         this.type = type;
      }

      public Object perform(Object[] args) throws Throwable
      {
         MBeanParameterInfo[] parameters = operation.getSignature();
         Object[] arguments = new Object[args.length];
         for (int i = 0; i < parameters.length; ++i)
         {
            OpenMBeanParameterInfo parameter = (OpenMBeanParameterInfo) parameters[i];
            arguments[i] = MXBeanUtils.construct(parameter.getOpenType(), args[i], operation.getName());
         }
         
         Object result = mbeanServerConnection.invoke(objectName, operation.getName(), arguments, signature);
         
         return MXBeanUtils.reconstruct(operation.getReturnOpenType(), type, result, operation.getName());
      }
   }

   private class InvalidAction implements Action
   {
      private Method method;
      
      public InvalidAction(Method method)
      {
         this.method = method;
      }
      
      public Object perform(Object[] args) throws Throwable
      {
         throw new UnsupportedOperationException(method + " is not mapped to the MBeanInfo operations for " + objectName);
      }
   }
   
   private class EqualsAction implements Action
   {
      public Object perform(Object[] args) throws Throwable
      {
         Object object = args[0];
         if (object == null || object instanceof Proxy == false)
            return false;
         InvocationHandler handler = Proxy.getInvocationHandler(object);
         if (handler instanceof MXBeanInvocationHandler == false)
            return false;
         MXBeanInvocationHandler other = (MXBeanInvocationHandler) handler;
         return mbeanServerConnection.equals(other.mbeanServerConnection) && objectName.equals(other.objectName);
      }
   }
   
   private class HashCodeAction implements Action
   {
      public Object perform(Object[] args) throws Throwable
      {
         return objectName.hashCode();
      }
   }
   
   private class ToStringAction implements Action
   {
      public Object perform(Object[] args) throws Throwable
      {
         return "MXBeanInvocationHandler(" + objectName + ")";
      }
   }
   
   private class AddNotificationListenerAction implements Action
   {
      public Object perform(Object[] args) throws Throwable
      {
         NotificationListener listener = (NotificationListener) args[0];
         NotificationFilter filter = (NotificationFilter) args[1];
         Object handback = args[2];
         mbeanServerConnection.addNotificationListener(objectName, listener, filter, handback);
         return null;
      }
   }

   private class GetNotificationInfoAction implements Action
   {
      public Object perform(Object[] args) throws Throwable
      {
         return mbeanServerConnection.getMBeanInfo(objectName).getNotifications();
      }
   }

   private class RemoveNotificationListenerAction implements Action
   {
      public Object perform(Object[] args) throws Throwable
      {
         NotificationListener listener = (NotificationListener) args[0];
         mbeanServerConnection.removeNotificationListener(objectName, listener);
         return null;
      }
   }

   private class RemoveNotificationListenerTripletAction implements Action
   {
      public Object perform(Object[] args) throws Throwable
      {
         NotificationListener listener = (NotificationListener) args[0];
         NotificationFilter filter = (NotificationFilter) args[1];
         Object handback = args[2];
         mbeanServerConnection.removeNotificationListener(objectName, listener, filter, handback);
         return null;
      }
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy