8

I have exposed methods for remote management in my application server using JMX by creating an MXBean interface, and a class to implement it. Included in this interface are operations for setting attributes on my server, and for getting the current value of attributes. For example, take the following methods:

public interface WordManagerMXBean {
     public void addWord(String word);
     public WordsObject getWords();
     public void removeWord(String word);
}

The WordsObject is a custom, serializable class used to retrieve data about the state of the server. Then I also have a WordManager class that implements the above interface. I then create a JMX agent to manage my resource:

MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName wordManagerName = new ObjectName("com.example:type=WordManager");
mbs.registerMBean(wordManager, wordManagerName);

I have created a client that invokes these methods, and this works as expected. However, I would like to extend this current configuration by adding user defined exceptions that can be sent back to my client. So I would like to change my interface to something like this:

public interface WordManagerMXBean {
     public void addWord(String word) throws WordAlreadyExistsException;
     public WordsObject getWords();
     public void removeWord(String word);
}

My WordAlreadyExistsException looks like this:

public class WordAlreadyExistsException extends Exception implements Serializable {

private static final long serialVersionUID = -9095552123119275304L;

public WordAlreadyExistsException() {
    super();
    }
}

When I call the addWord() method in my client, I would like to get back a WordAlreadyExistsException if the word already exists. However, when I do this, I get an error like this:

java.rmi.UnmarshalException: Error unmarshaling return; nested exception is: 
java.lang.ClassNotFoundException: com.example.WordAlreadyExistsException

The WordAlreadyExistsException, the WordsObject and the WordManagerMXBean interface are all in a single jar file that is available to both the client and the server. If I call the getWords() method, the client has no difficulty handling the WordsObject. However, if a user defined exception, like the one above, is thrown, then the client gives the error shown above. Is it possible to configure JMX to handle this exception correctly in the client?

Following some searching, I noticed that there is an MBeanException class that is used to wrap exceptions. I'm not sure if this wrapping is performed by the agent automatically, or if I'm supposed to do the wrapping myself. I tried both, but in either case I get the same error on the client.

I have also tried this with both checked and unchecked exceptions, again the same error occurs.

One solution to this is to simply pass back the error string inside a generic error, as all of the standard java exceptions work. But I'd prefer to get back the actual exception for processing by the client.

Is it possible to handle user defined exceptions in JMX? If so, any ideas how?

EDIT: Including full stack trace

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
4

5 回答 5

4

我试图重现这个问题,但是它对我来说很好;-(。请参阅下面的异常输出。我也敢打赌,这确实是客户端的类加载问题。你可以发布(a精简版)您的客户端 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)
.....
于 2012-12-04T22:45:44.633 回答
1

尽管不推荐,但绝对可以抛出自定义异常。来自JMX 最佳实践

建议从 Java SE 平台的 java.* 和 javax.* 包中定义的标准集中提取 MBean 引发的异常。如果 MBean 引发非标准异常,则没有该异常类的客户端可能会看到另一个异常,例如 ClassNotFoundException。

因此,我会投入一些钱,让您的客户根本没有加载WordAlreadyExistsException课程。您可以检查这相当简单 -在客户端中编写一个引发此异常的方法,调用它并查看会发生什么。如果一切顺利(OK = 引发自定义异常,否ClassNotFoundException),请确保您的客户端和服务器具有相同版本的 jar,其中包含WordAlreadyExistsException. 即使是最轻微的不匹配(例如,在包名称中)也可能是问题的根源。

于 2012-12-04T22:23:47.220 回答
1

可能发生的情况是您的自定义异常被序列化,然后在应用程序服务器中反序列化,但由于缺少更好的词“类加载器命名空间”而不同。

尝试将包含自定义异常类的 jar(c) 与 AppServer 正在使用的 JMX jar 放在一起。确保从旧位置移除旧 jar(我假设它位于 WEB-INF/lib.

于 2012-12-04T21:33:54.330 回答
0

而不是扩展 RuntimeMBeanException ,只需将 RuntimeException 作为 RuntimeMBeanException 的构造函数参数传递并抛出该异常。确保 jar 文件存在于 CLASSPATH 中。

如果 jar 文件存在于类路径中,扩展 RuntimeMBeanException 也可以工作,但我没有测试

于 2012-12-04T13:41:42.807 回答
0

您的例外必须从

    javax.management.JMException

或者

    javax.management.JMRuntimeException

如果它是一个 RuntimeException。

于 2012-12-04T07:24:21.550 回答