Confluence DC throws ClassCastException when getting object from Bandana after app upgrade

My app is keeping some objects in the Bandana table. (I know it is not best practice but wasn’t my choice). When I install a new package of my app I start getting ClassCastException while getting my objects via BandanaManager.getValue().

To be clear, BandanaManager.getValue() successfully returns an object but it can’t be cast to the desired type. While debugging I can clearly see that the class name (and even the hashcode) of the object I am getting from BandanaManager.getValue() and the class name that I am trying to cast to have the exact same Fully Qualified Name, yet I get an exception.

The package I am uploading does not have to be a new version of the app. Even uploading the same jar twice causes this problem. It happens only on DataCenter, even if it works as a single node, Confluence Server does not have this. I keep getting the exception until Confluence instance is restarted.

The issue reported below seems to be related to this. I guess it has something to do with how Bandana Caches work and which classloaders are used. Unlike the issue reported below, I am not creating and/or operating those caches, Confluence is.

I don’t want to use reflection as a workaround for performance considerations and I also don’t want to de-serialize/re-serialize the object, also based on performance considerations.

Any possible workarounds are welcome.

1 Like

We prepared the following sample Confuence App project that reproduces the problem.

  • Compile and install the app on a Confluence DC instance.
  • Call the following endpoint which will save something to the Bandana table.
    ** <confluenceUrl>/rest/bandanaBugTest/latest/bandana/save/{SPACE_KEY}?id={ID}&name={NAME}
    ** ID is an integer and NAME is a string.
  • Call the following endpoint to confirm that you can read the object successfully.
    ** <confluenceUrl>/rest/bandanaBugTest/latest/bandana/get/{SPACE_KEY}/{ID}
  • Upload the same plugin jar file without changing (or even compiling) anything.
  • Call the second endpoint (get endpoint) again to get the exception.
1 Like

Workaround: Serialize/deserialize on your own, and only store a String in Bandana.

HI!

I have the same problem with Confluence 7.13.0 DC.

Did You find a solution/workaround ?

regards,
Hans

Hi,

the cause of this issue is different versions of the same object. When you install your app the first time, the jvm class loader will create an instance of the class. Class loaders can only access classes created by themself. Then an object of this class is created, persisted and added to a cache. Now you update the installed app, and install it again. The (new) class loader will create another version of the class. When accessing the object in the cache (createed by the old class loader) the ClassCastException is thrown, because the object was created by a different class loader. To avoid this, you can now clear the cache. Trying to access this object, which does not exist in the cache now, will create a new object (from the new class loader) and load the serialized object from the bandana, which will now be deserialized by the new class loader.

So the solution we frequently use is to add a event listener, that listens for the PluginEnabledEvent and clears our caches.