Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

ClassCastException. Cannot cast PoJo to PoJo. Different ClassLoaders?

Alexej Geldt
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
February 25, 2014

We have a JIRA Plugin which is using external library.

<!-- Analytical Request Submission Engine -->
		<dependency>
			<groupId>com.bssnsoftware.bpm</groupId>
			<artifactId>bpm-analytical-request-engine</artifactId>
			<version>1.0.0-SNAPSHOT</version>
		</dependency>

Request Submission Engine provides a factory, which creates RequestForm PoJo. Its interface signature looks like this.

public RequestForm createRequestForm(Task parentTask) throws BPMException;

The created RequestForm PoJo is then available in our Plugin and we can work with it.

Everything fine sofar...

Then we put it into session.

request.getSession().setAttribute("requestForm", requestForm);

And get it out later on another screen.

Object requestFormObject = request.getSession().getAttribute("requestForm");
RequestForm requestForm = (RequestForm) requestFormObject;

the last line causes ClassCastException.

java.lang.ClassCastException: com.bssnsoftware.bpm.ars.model.RequestForm cannot be cast to com.bssnsoftware.bpm.ars.model.RequestForm

Findings:

1. when we dont downcast requestFrom to its Object representation. Everything works fine.

2. as soon we put it into session, we downcast it to Object. Trying to upcast it back to its real class, causes exception.

3. requestFormObject.getClass().getCanonicalName() returns com.bssnsoftware.bpm.ars.model.RequestForm

4. requestFormObject instanceof RequestForm returns true

5. requestFormObject.getClass().getClassLoader().equals(RequestForm.class.getClassLoader()) returns false

It seems, that the the JIRA Plugin and in the Request Submission Engine are using different ClassLoaders to load RequestForm class. This prevents upcasting from object to concrete class instance.

My guess is, this is related to OSGI framework. Maybe Request Submission Engine is in its own bundle with its own classloader, completely separete from the Plugin itself.

Any ideas how to fix or get around this? Can we force the Request Submission Engine use the same classloader as the plugin?

3 answers

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

0 votes
crf
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
June 7, 2014
Storing things in the session isn't really a great idea. Aside from minor reasons (Data Center safety, for example), there are some really serious problems that this causes. The biggest is that you are bleeding your plugin's classloader into publicly accessible data that can outlive your plugin's lifecycle.
Whether because it's being uninstalled or because it is being upgraded, your plugin's data needs to be able to go away. When you stick it in HTTP sessions, it can't do that until all sessions that referred to it have been destroyed or have otherwise destroyed the value. This has two nasty consequences:
  1. The sessions hold instances of classes from the old plugin, which means they hold onto the plugin's old bundle classloader. This prevents the old plugin from being garbage collected, resulting in memory leaks (both heap and PermGen).
  2. Anything that looks at the attributes (including your plugin) is going to see these old instances from a different classloader and have trouble using them normally.

It's better if you find some other way to track this. If you absolutely must put it in the session, then it may help to explicitly serialize it to a byte[] (using ObjectOutputStream around a ByteArrayOutputStream) before placing it into the session and deserializing it (using ObjectInputStream around a ByteArrayInputStream) on the way out. This assumes, of course, that RequestForm is even serializable. But then, you shouldn't be putting anything that isn't Serializable into the session either.

All that said, it isn't clear how you could run into this problem within a single lifecycle of the plugin just from one request to another. I could see this happening perhaps if you had configured Tomcat to serialize session data, but as JIRA itself also breaks the rule that you shouldn't store non-Serializable data in a session, you would have more going wrong than just your plugin.

0 votes
Alexej Geldt
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
February 25, 2014

We have found a workaround, but we are totaly unhappy with this.

we can copy the RequestForm object by value. We create a new instance manually and copy every field from the RequestForm created by the Factory. Then we can safely down- and upcast it because we have created the new instance with the same class loader.

In this particullar case - this works as a work-around, since a RequestForm doesn't have much to copy.

However, it doesn't fix the problem. We don't like the idea to find ourself in copying every object which comes from a third party library.

I'm keeping the question open. Someone who finds a better way, deserves the bounty.

0 votes
Alexej Geldt
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
February 25, 2014

One more finding:

the velocity template doesn't care about type safety. We can call methods on requestFormObject from velocity template and it works. We just cannot recast it in java.

I think this is because velocity is calling methods using reflections. Maybe we can do the same in java?

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

TAGS
AUG Leaders

Atlassian Community Events