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

Java JMX操作可以将接口作为参数吗?,java,jmx,mbeans,Java,Jmx,Mbeans,我对采用Map作为参数的MBean有问题。如果我尝试使用代理对象通过JMX执行它,我会得到一个异常: Caused by: javax.management.ReflectionException at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:231) at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl

我对采用
Map
作为参数的
MBean
有问题。如果我尝试使用代理对象通过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)
它似乎试图使用实际的实现类而不是接口,并且不检查这是否是所需接口的子级。扩展类也会发生同样的情况(例如declare
HashMap
,传入
LinkedHashMap
)。这是否意味着不可能为此类方法使用接口?目前,我正在通过更改方法签名以接受
HashMap
来解决这个问题,但在
MBean
中无法使用接口(或扩展类)似乎有些奇怪

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

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;
    }
}
public类JmxInvocationHandler实现调用处理程序
{
...
public static T createMBean(最终类iface、SFSTestProperties属性、字符串mbean、int shHostID)
{
T newProxyInstance=(T)Proxy.newProxyInstance(iface.getClassLoader(),新类[]{iface},(InvocationHandler)新的jmxiNotationHandler(属性,mbean,shHostID));
返回newProxyInstance;
}
...
私有jmstProperties处理程序(SFSTestProperties属性、字符串mbean、int-shHostID)
{
this.mbeanName=mbean+mbean_后缀+shHostID;
msConfig=新的msConfig配置(properties.getHost(0)、properties.getMSAdminPort()、properties.getMSUser()、properties.getMSPassword());
}
...
公共对象调用(对象代理、方法、对象[]args)抛出Throwable
{
如果(管理==null)
{
management=ManagementClientStore.getInstance().getManagementClient(msConfig.getHost(),
msConfig.getAdminPort(),msConfig.getUser(),msConfig.getPassword(),false);
}
最终对象结果=management.methodCall(mbeanName,method.getName(),args==null?新对象[]{}:args);
返回结果;
}
}

明白了。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的一个棘手部分”开头的段落,因为它更详细地解释了这个问题(或者我认为您遇到的问题),但[连接]的方法是:

invoke(ObjectName name, String operationName, Object[] params, String[] signature) 
前2个和最后一个参数是导航的,因为它们精确地指定了应该调用服务器中发布的所有操作中的哪个操作。回避此问题的最佳方法是避免“猜测”签名,而只依赖ObjectName和操作名,这反过来又可以通过询问(并可能缓存)目标MBean的和来完成。MBeanOperationInfos将为您提供签名,因此您无需猜测

如果这确实是你的问题,有几种方法可以解决:

  • 如果MBean的操作名称是唯一的(即没有重载),那么您可以使用op名称来检索MBeanInfo
  • 如果MBean的操作被重载(即有多个同名但参数不同的操作)。。。但是它们都有不同的参数计数,然后您可以通过迭代MBeanOperationInfos中所有匹配的操作名并按参数计数进行匹配来轻松确定正确的签名
  • 如果#1和#2不适用。。。。这很棘手,我会重新评估MBean代码的方法签名
  • 如果#1和#2不适用且#3不符合要求,请查看调用中的此类。在最新版本中,它使用Groovy创建编译运行时接口,使用MBean的MBeanInfo使继承(和自动装箱)在方法调用中工作。同样的方法也可以在JavaScript(其优点是内置于Java6+)或其他几种JVM脚本语言中实现。或者,看看哪个试图与已知的操作签名模式匹配(实际上工作得很好,但是由于我一直在使用Groovy

  • 我希望这是有帮助的。如果这不是根本原因,那就忘了我说了什么……

    Hi Thor;你能提供创建代理对象的代码吗?我使用的是一个内部实用程序类,坦率地说,我不确定我是否能真正了解其中的情况(它做了大量与安全性相关的额外工作,这不是我见过的编写得最好的代码),但我会尽量把重要的信息提取出来,并添加到帖子中。这是一个非常有趣且深思熟虑的答案。谢谢我现在很难尝试,因为这门课在另一个项目中,因为我被赋予了一个优先处理的问题。现在,我将投票支持你的答案,因为在我看来,它很可能是正确的,一旦有机会,我会回复你我是否可以用这种方式解决它。谢谢。