Issue Details (XML | Word | Printable)

Key: JMS_SPEC-24
Type: Improvement Improvement
Status: Open Open
Priority: Major Major
Assignee: Unassigned
Reporter: F.Degenaar
Votes: 0
Watchers: 1
Operations

If you were logged in you would be able to see more operations.
jms-spec

Clarify classloader used in ObjectMessage.getObject() and/or provide new method getObject(ClassLoader classLoader)

Created: 04/Jul/11 05:21 PM   Updated: 28/Mar/12 10:35 AM
Component/s: None
Affects Version/s: 2.0
Fix Version/s: None

Time Tracking:
Not Specified

Tags: pd20-forreview-minor
Participants: F.Degenaar and Nigel Deakin


 Description  « Hide

It is specified neither in the spec nor in the API JavaDoc which ClassLoader is to be used, when the object gets deserialized in ObjectMessage.getObject().
This omission lead to different implementations (and bugs) among JMS providers, as can be seen here:
http://www-01.ibm.com/support/docview.wss?uid=swg1IC72855
http://www.coderanch.com/t/313402/EJB-JEE/java/Exception-receiving-ObjectMessage-JMS
http://forums.oracle.com/forums/thread.jspa?threadID=738545
http://community.jboss.org/message/437144#437144

Alternatively, if it isn't possible to specify the class loader, add a method "Serializable getObject(ClassLoader classLoader) to ObjectMessage.



Nigel Deakin added a comment - 08/Mar/12 12:25 PM

Would the person who raised this issue like to propose what kind of clarification they would want to see?

Standard Java deserialization uses the java.io.ObjectInputStream.readObject() method.

The default behaviour of ObjectInputStream.readObject() is described in the javadoc for ObjectInputStream.resolveClass() here. This states that the class loader loader is determined as follows:

if there is a method on the current thread's stack whose declaring class was defined by a user-defined class loader (and was not a generated [class] to implement reflective invocations), then loader is [the] class loader corresponding to the closest such method to the currently executing frame; otherwise, loader is null

However some JMS providers appear to override this behaviour, perhaps to use the context class loader if using the class loader provided by default cannot load the class.

Trying the context class loader is more flexible, though it still relies on the context class loader being configured appropriately, which is not under direct application control in a MDB or message listener where the thread is created by the JMS provider or application server. In addition, the first of the four examples given above suggest that even when messages are consumed synchronously using a application thread, the deserialization may still be performed in a separate JMS provider thread. So requiring the deserialization to try the context class loader may not always solve the problem.

The submitted proposes a new method getObject(ClassLoader classLoader) on java.jms.ObjectMessage. This would be simple to implement and not impact on existing JMS providers or applications, so seems a good candidate for consideration.

I'll raise this topic with the expert group. Tagging appropriately.


Nigel Deakin added a comment - 12/Mar/12 04:21 PM

Description changed from:
Clarify, which ClassLoader to use when invoking ObjectMessage.getObject()
to:
Clarify classloader used in ObjectMessage.getObject() or provide new method getObject(ClassLoader classLoader)


F.Degenaar added a comment - 13/Mar/12 05:41 PM

My take on the first example was a different one: the problem was, that the object was deserialized internally before getObject() was called. So, using the context class loader should have helped avoiding the issue.
Still, neither spec nor javadoc mention the default behaviour from ObjectInputStream.resolveClass(). Defining it as default will not resolve all problems, but most of them. I still see this as an improvement, especially when an alternative classloader can be chosen with the overloaded version.


Nigel Deakin added a comment - 13/Mar/12 05:55 PM

@F.Degenaar: In the first example, the object was deserialized in an internal JMS thread. How would it help to mandate that it should use the context class loader, since the application has no way to configure the context class loader of an internal JMS thread?


F.Degenaar added a comment - 13/Mar/12 11:44 PM

@Nigel Deakin: A new method getObject(ClassLoader classLoader) alone cannot solve the problem described in the first example. The ClassNotFoundException will be thrown either which way, because the the deserialization happens independently in an internal thread.
At present it is impossible to predict, if ObjectMessage can be used to transport non-JDK classes even with a certified JMS provider.
If the context class loader (or the one specified as an parameter) is mandated as the only one to use, the control will be in the hands of the application or the application server.
Anything else would than be a vendor extension, which can be used at will.


Nigel Deakin added a comment - 14/Mar/12 10:11 AM - edited

F.Degenaar writes:

A new method getObject(ClassLoader classLoader) alone cannot solve the problem described in the first example. The ClassNotFoundException will be thrown either which way, because the the deserialization happens independently in an internal thread.

We would define getObject(ClassLoader classLoader) as meaning that the object must be deserialised using the specified class loader. So if a suitable class loader is specified it will not fail.

If the provider also deserialises the object eagerly in a separate thread created by the JMS provider (to improve the performance of a subsequent call to the existing getObject()) then how are you suggesting we specify which class loader it uses? Specifying that it uses the context class loader isn't a help, since the application doesn't have any way to configure the context class loader if a provider-created thread.

I'm not sure exactly what you are asking for here. Please do suggest some text!


F.Degenaar added a comment - 14/Mar/12 06:40 PM

Please keep in mind, that English is not my native language. That said:
As an addition to the Returns:-JavaDoc of ObjectMessage.getObject():
"Any deserialization necessary must only happen using the context class loader of the thread, in which this method was invoked."
For the proposed method getObject(ClassLoader classLoader) the JavaDoc for the parameter classLoader would read:
"The class loader to be exclusively used for any deserialization necessary."

What I am trying to achieve here?

  • If no deserialization is necessary, the class loader obviously doesn't matter (think in-memory provider).
  • If a deserialization happens, the developer or an application server have control over which class loader is used by setting the context class loader beforehand.
  • Every spec-compliant optimization is transparent to the application.
  • Any optimization violating the spec will be explicit by a cast to a vendor-specific class or interface, in the configuration of the provider or the initialization of factory objects etc.

Nigel Deakin added a comment - 15/Mar/12 11:04 AM

Thanks for clarifying what you are asking for. My goal at this stage is simply to ensure that this issue is clearly described. Now it is, we can now leave it for later review by the expert group.