通过创建MXBean接口和实现它的类,我已经在我的应用程序服务器中使用JMX公开了远程管理方法。此界面中包含用于在我的服务器上设置属性以及获取属性的当前值的操作。例如,采用以下方法:
public interface WordManagerMXBean {
public void addWord(String word);
public WordsObject getWords();
public void removeWord(String word);
}
WordsObject是一个自定义的可序列化类,用于检索有关服务器状态的数据。然后我还有一个实现上述接口的WordManager类。然后我创建一个JMX代理来管理我的资源:
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName wordManagerName = new ObjectName("com.example:type=WordManager");
mbs.registerMBean(wordManager, wordManagerName);
我创建了一个调用这些方法的客户端,这可以按预期工作。但是,我想通过添加可以发送回客户端的用户定义的异常来扩展此当前配置。所以我想将我的界面更改为:
public interface WordManagerMXBean {
public void addWord(String word) throws WordAlreadyExistsException;
public WordsObject getWords();
public void removeWord(String word);
}
我的WordAlreadyExistsException如下所示:
public class WordAlreadyExistsException extends Exception implements Serializable {
private static final long serialVersionUID = -9095552123119275304L;
public WordAlreadyExistsException() {
super();
}
}
当我在客户端调用addWord()方法时,如果单词已经存在,我想返回WordAlreadyExistsException。但是,当我这样做时,我收到如下错误:
java.rmi.UnmarshalException: Error unmarshaling return; nested exception is:
java.lang.ClassNotFoundException: com.example.WordAlreadyExistsException
WordAlreadyExistsException,WordsObject和WordManagerMXBean接口都在一个jar文件中,可供客户端和服务器使用。如果我调用getWords()方法,客户端可以轻松处理WordsObject。但是,如果抛出用户定义的异常(如上所述),则客户端会给出上面显示的错误。是否可以将JMX配置为在客户端中正确处理此异常?
经过一些搜索,我注意到有一个MBeanException类用于包装异常。我不确定这个包装是由代理自动执行的,还是我应该自己进行包装。我试过了两个,但在任何一种情况下我都会在客户端上遇到同样的错误。
我也尝试了检查和未检查的异常,再次发生同样的错误。
对此的一个解决方案是简单地将错误字符串传回一般错误中,因为所有标准java异常都有效。但我更愿意回到客户端处理的实际异常。
是否可以在JMX中处理用户定义的异常?如果是这样,任何想法如何?
编辑:包括完整堆栈跟踪
java.lang.reflect.UndeclaredThrowableException
at $Proxy4.addWord(Unknown Source)
at com.example.TestClient.addWord(TestClient.java:76)
at com.example.TestClient.execute(TestClient.java:56)
at java.lang.Thread.run(Thread.java:722
Caused by: java.rmi.UnmarshalException: Error unmarshaling return; nested exception is:
java.lang.ClassNotFoundException: com.example.WordAlreadyExistsException
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:245)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:160)
at com.sun.jmx.remote.internal.PRef.invoke(Unknown Source)
at javax.management.remote.rmi.RMIConnectionImpl_Stub.invoke(Unknown Source)
at javax.management.remote.rmi.RMIConnector$RemoteMBeanServerConnection.invoke(RMIConnector.java:1017)
at javax.management.MBeanServerInvocationHandler.invoke(MBeanServerInvocationHandler.java:305)
... 6 more
Caused by: java.lang.ClassNotFoundException: com.example.WordAlreadyExistsException
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:453)
at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:184)
at java.rmi.server.RMIClassLoader$2.loadClass(RMIClassLoader.java:637)
at java.rmi.server.RMIClassLoader.loadClass(RMIClassLoader.java:264)
at sun.rmi.server.MarshalInputStream.resolveClass(MarshalInputStream.java:216)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1593)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1514)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1750)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1964)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1888)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:243)
... 11 more
答案 0 :(得分:4)
我试图重现这个问题,但它对我来说很好;-(。请参阅下面的异常输出。我也打赌,它确实是客户端的类加载问题。你有可能吗? post(一个精简版)你的客户端jar(也可能是源代码)?
Exception in thread "main" javax.management.MBeanException: WordAlreadyExistsException
at com.sun.jmx.mbeanserver.MBeanIntrospector.unwrapInvocationTargetException(MBeanIntrospector.java:283)
at com.sun.jmx.mbeanserver.MBeanIntrospector.invokeM(MBeanIntrospector.java:210)
.....
Caused by: WordAlreadyExistsException
at WordManager.addWord(WordManager.java:7)
.....
答案 1 :(得分:1)
可能发生的事情是,您的自定义异常被序列化,然后在应用程序服务器中反序列化,但在不同的情况下,由于缺少更好的单词“classloader namespace”。
尝试将包含自定义异常类的jar(c)与AppServer正在使用的JMX jar放在一起。确保从旧位置移除旧罐子(我假设它在WEB-INF / lib中的某处。
答案 2 :(得分:1)
尽管不推荐,但绝对可以抛出自定义异常。来自JMX Best practices:
建议从MBean引发的异常 Java SE上的java。*和javax。*包中定义的标准集 平台。如果MBean抛出非标准异常,则为客户端 没有那个异常类可能会看到另一个异常 比如ClassNotFoundException。
因此,我会花一些钱,你的客户端根本没有加载WordAlreadyExistsException
类。你可以检查一下这很简单 - 在客户端中写一个方法抛出这个异常,调用它然后看看会发生什么。如果一切正常(OK =抛出自定义异常,没有ClassNotFoundException
),请确保您的客户端和服务器具有包含WordAlreadyExistsException
的相同版本的jar。即使是最轻微的不匹配(例如,在包名称中)也可能是您问题的根源。
答案 3 :(得分:0)
您的例外必须从
延伸 javax.management.JMException
或
javax.management.JMRuntimeException
以防它是RuntimeException。
答案 4 :(得分:0)
不要扩展RuntimeMBeanException,只需将RuntimeException作为RuntimeMBeanException的构造函数参数传递,并抛出该异常。确保CLASSPATH中存在jar文件。
扩展RuntimeMBeanException也可以工作,前提是jar文件存在于类路径中,但我没有测试