(Class) cannot be cast to (interface) in a custom LiveCycle component

Problem

You are getting an error along the following lines when running a custom component for LiveCycle ES and ES2:

"[com.adobe.] cannot be cast to [com.adobe.]"

even though you know for sure that AChild is a child or implementor of AParent – for instance:

"com.adobe.idp.taskmanager.dsc.client.query.TaskRowImpl cannot be cast to com.adobe.idp.taskmanager.dsc.client.query.TaskRow"

Additionally:

-You have included the JAR files from LiveCycle’s deployment directories into your component JAR
-If you remove the Adobe JAR from your Custom Component’s JAR, you get a ClassDefNotFoundError.

Solution

-Remove the Adobe JARs from your custom component’s JAR
-Modify your component XML and add tags as appropriate to include the Adobe packages you need.

Reason

This puzzling problem is to do simply with how different class loaders interact. To abstract over any proper LiveCycle classes, I will be talking about a fictitious Pan class.

# 1) About class loaders

A Pan is not a Pan when you have two different definitions of a Pan.
When you load a class called Pan with one class loader, and you load it again with a different class loader, they are different definitions in memory of a Pan.
So when you try to assign [a Pan instance from the first class loader] to [a Pan variable defined by the second class loader], the JVM will not match them up as being the same type of Pan – and you will get the unexpected ClassCastException.

-When the JVM is started, it uses a class loader to start loading all the classes it needs, along with the web application server.
-When the app server starts loading LiveCycle, it creates several class loaders – one for each EAR, for example; and within the EARs, one for each WAR, etc.
-Many different components are loaded by many different class loaders

When you deploy your custom component, it gets loaded by its own class loader. Here’s where it gets tricky.

# 2) Delegating class loaders

If you ask LiveCycle to give you a Pan via some API call, it will give you a Pan that one of its class loaders defined. When in your code you declare a variable to hold that Pan, you’re using your class loader’s Pan definition. A definition clash ensues.

Obviously you don’t want to load your own Pan when LiveCycle already has a definition for a Pan – remove the Adobe JARs that you included with your custom component. If you run the project now, you may get a ClassDefNotFoundError. Why?

If your class loader could not find a Pan of its own, it delegates finding the Pan up to the class loader that loaded it – the parent class loader. The parent class loader will ask your class loader what packages it needs to look in – this is specified in your component XML under the tag. The parent class loader will look for all packages that it is aware of for the packages you specified in the component XML; then look in those for class definitions. If it can’t find what you are looking for, the search will be deferred to its own parent class loader, so on and so forth until the root class loader is reached.

At this point, either one of two things can happen:
-if you specified a package in component XML that contains the Pan you seek, that class definition will be passed back to your class loader.
-if you did not specify a package containing the Pan you seek, NoClassDefFoundError will be thrown.