JMX操作可以将接口作为参数吗?

时间:2012-06-26 09:55:34

标签: java jmx mbeans

我遇到以MBean作为参数的Map<String, Object>时出现问题。如果我尝试使用代理对象通过JMX执行它,我会得到一个异常:

Caused by: javax.management.ReflectionException
    at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:231)
    at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:668)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
Caused by: java.lang.IllegalArgumentException: Unable to find operation updateProperties(java.util.HashMap)

它似乎尝试使用实际的实现类而不是接口,并且不检查它是否是所需接口的子级。扩展类也会发生同样的事情(例如声明HashMap,传入LinkedHashMap)。这是否意味着不可能为这种方法使用接口?目前我通过更改方法签名来接受HashMap来解决这个问题,但似乎很奇怪我无法在MBeans中使用接口(或扩展类)。

编辑:代理对象由名为JmxInvocationHandler的内部实用程序类创建。 (希望)相关部分如下:

public class JmxInvocationHandler implements InvocationHandler
{
    ...
    public static <T> T createMBean(final Class<T> iface, SFSTestProperties properties, String mbean, int shHostID)
    {
        T newProxyInstance = (T) Proxy.newProxyInstance(iface.getClassLoader(), new Class[] { iface }, (InvocationHandler) new JmxInvocationHandler(properties, mbean, shHostID));
        return newProxyInstance;
    }
    ...
    private JmxInvocationHandler(SFSTestProperties properties, String mbean, int shHostID)
    {
        this.mbeanName = mbean + MBEAN_SUFFIX + shHostID;
        msConfig = new MsConfiguration(properties.getHost(0), properties.getMSAdminPort(), properties.getMSUser(), properties.getMSPassword());
    }
    ...
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        if (management == null)
        {
            management = ManagementClientStore.getInstance().getManagementClient(msConfig.getHost(),
                    msConfig.getAdminPort(), msConfig.getUser(), msConfig.getPassword(), false);
        }

        final Object result =  management.methodCall(mbeanName, method.getName(), args ==  null?  new Object[] {} : args);
        return result;
    }
}

1 个答案:

答案 0 :(得分:2)

知道了。 JMX调用有时会使最佳预期实用程序类的炮灰......:)

我怀疑这个人是个问题:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        if (management == null)
        {
            management = ManagementClientStore.getInstance().getManagementClient(msConfig.getHost(),
                    msConfig.getAdminPort(), msConfig.getUser(), msConfig.getPassword(), false);
        }

        final Object result =  management.methodCall(mbeanName, method.getName(), args ==  null?  new Object[] {} : args);
        return result;
    }

因为MBean的操作签名(不关心继承)不是从传递的参数的类中确定的。由于您无法传递getClass()将返回java.util.Map的实际具体对象,因此您永远不会使用参数本身的直接类型进行匹配。 (出于同样的原因,基元也会出现类似的问题)。

从“制作MetaMBean的一个棘手部分”开头的段落开头看这个blog post,因为它解释了这个问题(或者我认为你遇到的问题)更详细一点,但invoke [连接]的MBeanServer方法是:

invoke(ObjectName name, String operationName, Object[] params, String[] signature) 

前两个和最后一个参数是导航的,它们确切地指定应该调用在服务器中发布的所有操作中的哪个操作。避免此问题的最佳方法是避免必须“猜测”签名并仅依赖于ObjectName和操作名称,而后者又可以通过询问(并可能缓存)MBeanInfo和{{ 3}}目标MBean。 MBeanOperationInfos将为您提供签名,因此您无需猜测。

如果确实是您的问题,可以通过以下几种方式解决问题:

  1. 如果MBean的操作名称是唯一的(即没有重载),那么您只需使用操作名称来检索MBeanInfo。
  2. 如果MBean的操作过载(即有多个操作具有相同的名称但参数不同)......但它们都有不同的参数计数,那么您可以通过迭代所有匹配的操作名称来轻松确定正确的签名MBeanOperationInfos和param count匹配。
  3. 如果#1和#2不适用....那么它很棘手,我会重新评估你的MBean代码的方法签名。
  4. 如果#1和#2不适用且#3不符合要求,请在名为MBeanOperationInfoGmx中查看此课程。在最新版本中,它使用Groovy使用MBean的MBeanInfo创建编译的运行时接口,以使继承(和自动装箱)在方法调用中起作用。可以在JavaScript(具有内置于Java 6+中的优点)或其他几种JVM脚本语言中实现相同的方法。或者,查看尝试与已知操作签名进行模式匹配的MetaMBean(实际上工作得很好,但是因为我正在使用Groovy ......)
  5. 我希望这会有所帮助。如果事实证明这不是根本原因,那就忘记我说了什么......