Confluence Servlet module class cast exception

I’m struggling… An app that has been in use for many years started taking a dump on me after an update and I can’t seem to figure out what the problem is.

Maybe some boss around here might have an idea or two I could try, hopefully?

Problem description

The app installs and generally seems to be working and running (e.g. I can see scheduled jobs from the plugin too). No errors in the logs, everything is fine, all modules enabled. You’d think we hit a jackpot with this one and I can finally retire.

However, no servlets are working. Trying to open a servlet page will result in an error like this:

024-10-18 12:11:28,190 ERROR [https-jsse-nio-8443-exec-17 url: /plugins/servlet/pua/dashboard; user: helpme] [atlassian.plugin.servlet.DefaultServletModuleManager] getInstance Unable to create new reference LazyLoadedServletReference{descriptor=com.helpme.confluence.services.pluginUsageAnalyzer:pua-dashboard-servlet (Overview Dashboard), servletContext=org.apache.catalina.core.ApplicationContextFacade@10033943}
 -- url: /plugins/servlet/pua/dashboard | userName: helpme | traceId: 434a40ba7efea427
io.atlassian.util.concurrent.LazyReference$InitializationException: java.lang.ClassCastException: class com.helpme.servlets.DashboardServlet cannot be cast to class javax.servlet.http.HttpServlet (com.helpme.servlets.DashboardServlet is in unnamed module of loader org.apache.felix.framework.BundleWiringImpl$BundleClassLoader @1a116e98; javax.servlet.http.HttpServlet is in unnamed module of loader java.net.URLClassLoader @86be70a)
        at io.atlassian.util.concurrent.LazyReference.getInterruptibly(LazyReference.java:156)
        at io.atlassian.util.concurrent.LazyReference.get(LazyReference.java:116)
        at com.atlassian.plugin.servlet.DefaultServletModuleManager.getInstance(DefaultServletModuleManager.java:431)
        at com.atlassian.plugin.servlet.DefaultServletModuleManager.getServlet(DefaultServletModuleManager.java:410)
        at com.atlassian.plugin.servlet.DefaultServletModuleManager.getServlet(DefaultServletModuleManager.java:230)
        at com.atlassian.plugin.servlet.ServletModuleContainerServlet.service(ServletModuleContainerServlet.java:40)
        at com.atlassian.confluence.servlet.ServletModuleContainerServlet.service(ServletModuleContainerServlet.java:52)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:623)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:199)

... blabla ...

Caused by: java.lang.ClassCastException: class com.helpme.servlets.DashboardServlet cannot be cast to class javax.servlet.http.HttpServlet (com.helpme.servlets.DashboardServlet is in unnamed module of loader org.apache.felix.framework.BundleWiringImpl$BundleClassLoader @1a116e98; javax.servlet.http.HttpServlet is in unnamed module of loader java.net.URLClassLoader @86be70a)
        at com.atlassian.plugin.servlet.descriptors.ServletModuleDescriptor.getModule(ServletModuleDescriptor.java:42)
        at com.atlassian.plugin.servlet.DelegatingPluginServlet.<init>(DelegatingPluginServlet.java:30)
        at com.atlassian.plugin.servlet.DefaultServletModuleManager$LazyLoadedServletReference.create(DefaultServletModuleManager.java:529)
        at com.atlassian.plugin.servlet.DefaultServletModuleManager$LazyLoadedServletReference.create(DefaultServletModuleManager.java:513)
        at io.atlassian.util.concurrent.LazyReference$Sync.run(LazyReference.java:332)
        at io.atlassian.util.concurrent.LazyReference.getInterruptibly(LazyReference.java:150)
        ... 373 more

Other notes

I’ve tried a lot… changing dependency scopes, cleaning and rebuilding, upping a few versions here and there. Bashing the wall and bandaging up. Nothing works.

Especially tried to change

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
</dependency>

from downgrading, upgrading, etc. It just doesn’t care about my pain and suffering.

More servlet info

import javax.servlet.http.HttpServlet;
..

@Named
public class DashboardServlet extends HttpServlet {
    ..
    @Override
    protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException {
        ..
    }
    ..
}

^ standard stuff

<servlet name="PUA - Dashboard Servlet" key="pua-dashboard-servlet" class="com.helpme.servlets.DashboardServlet">
        <description>Overview Dashboard</description>
        <url-pattern>/pua/dashboard</url-pattern>
</servlet>

^ also standard stuff

System info (update)

Application ServerApache: Tomcat/9.0.91
Servlet Version: 4.0
Java Version: 17.0.12
Java Vendor: Eclipse Adoptium
JVM Version: 17
JVM Vendor: Oracle Corporation
JVM Implementation Version: 17.0.12+7
Java Runtime: OpenJDK Runtime Environment
Java VM: OpenJDK 64-Bit Server VM

Assumptions

Just seeing the cause… I reckon, class loader problems, been there before. But, like, how do I fix that in here? -.-
Could try to create my own class that just extends it and see if the class loader is happy about it, but it doesn’t sound like a proper solution or even understanding of why this is happening.

Final begging moments

Any ideas? Have you ever run into class loader problems perhaps not with servlets, but something similar?
I’m not paying, but I will thank you generously for any hint or clue.

Hello Radek,

I was able to successfully use a DashboardServlet test class like yours. The dependency is correct, as I’m using exactly the same. Try changing the @Named annotation for @Component. If this does not work, please share which Confluence version you are using, and, even better, share a test project so that I can check other settings.

Regards,
Ansgar

Hi Ansgar,

Thanks for the suggestion!

The @Named annotation I suppose should be useless here (I definitely have no interest in using a servlet as a spring bean, if that’s what it’s for).

No change without it though. Also compared it to different custom plugins, ensures that they are structurally the same (as close as I could anyway). All other plugins get packaged, installed, and work fine. Aaaalll except this particular one.

It also stopped packaging on Monday after a winblows update over the weekend, now I’m getting enforcer errors due to banned dependencies, and funnily enough the dependency tree points to com.atlassian.confluence:confluence, and again only this one project having this problem, others are fine.

It must be something to do with the intellij project… Ain’t no way others are totally fine but this one is cursed in different ways.

Seems I’ll go through this project step by step and hope to find some difference somewhere. I’m convinced it’s something to do with it. Worst case scenario I will just recreate the plugin and copy paste the code. Something’s gotta give.

Thanks again though!

Regards,
Radek

Edit: My earlier self was a little unhinged and quick to jump the gun without a hint of how dependencies work. See later post what the problem was.

As for the “banned dependencies” maven enforcer errors, workaround was to add

                    <banningExcludes>
                        <exclude>commons-io:commons-io</exclude>
                        <exclude>commons-fileupload:commons-fileupload</exclude>
                    </banningExcludes>

to AMPS configuration. It otherwise wouldn’t package. However once the POM got upgraded the enforcer errors went away so I removed this.

Just copy pasted my servlet dependencies as you didn’t say anything about the Confluence versions itself. Hope that helps.

Confluence 7
===
...
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.4</version>
    <scope>provided</scope>
</dependency>
Confluence 8 & 9
===
...
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.atlassian.confluence</groupId>
            <artifactId>confluence-plugins-platform-pom</artifactId>
            <version>${confluence.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
...
<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>jakarta.servlet.jsp</groupId>
    <artifactId>jakarta.servlet.jsp-api</artifactId>
    <scope>provided</scope>
</dependency>

What was the update, exactly?

We ran into something very similar when trying to make our app compatible with Confluence 8.8:

java.lang.ClassCastException: class com.lucidchart.confluence.plugins.oauth.OAuthSetup cannot be cast to class javax.servlet.http.HttpServlet (com.lucidchart.confluence.plugins.oauth.OAuthSetup is in unnamed module of loader org.apache.felix.framework.BundleWiringImpl$BundleClassLoader @14b8160; javax.servlet.http.HttpServlet is in unnamed module of loader java.net.URLClassLoader @68be2bc2)

The fix was to add an explicit dep on javax.servlet:servlet-api in our pom.xml:

          <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.4</version>
             <scope>provided</scope>
         </dependency>

I couldn’t tell, it is mostly lying around and used sparsely (the main function of this plugin is to do background jobs and then export data to other tools for visualization). Meanwhile Confluence is upgraded, java is upgraded, etc., so it might have been either any of the update on the plugin itself or simply Confluence version at some point got incompatible. Tough to say when.

I couldn’t figure it out at first so I left it for a while. Later on, another plugin also broke, so I strapped in the chair for a bit and finally got to fix it.

With ClassLoader errors, I checked the pom dependencies to see how many instances of javax.servlet there are, and surely enough found one culprit:

$ /c/Applications/Atlassian/atlassian-plugin-sdk-8.2.7/apache-maven-3.5.4/bin/mvn dependency:tree
...
[INFO] +- org.apache.velocity:velocity-tools:jar:1.3:compile
[INFO] |  +- commons-digester:commons-digester:jar:1.8:compile
[INFO] |  +- commons-logging:commons-logging:jar:1.1:compile
[INFO] |  |  +- logkit:logkit:jar:1.0.1:compile
[INFO] |  |  \- avalon-framework:avalon-framework:jar:4.1.3:compile
[INFO] |  +- javax.servlet:servlet-api:jar:2.3:compile					// AHA, HERE YOU ARE!
[INFO] |  +- oro:oro:jar:2.0.8:compile
[INFO] |  +- sslext:sslext:jar:1.2-0:compile
[INFO] |  +- struts:struts:jar:1.2.9:compile
[INFO] |  \- velocity:velocity:jar:1.4:compile
[INFO] |     \- velocity:velocity-dep:jar:1.4:runtime

More on this bugger later.

Tied’s hint put me on the right path here – I didn’t know before you can just add <dependencyManagement> and have the plugin fetch appropriate versions on the fly (never in years had problem with dependencies so never really bothered, I only need the plugins to work for a specific version at a time since they are not commercial).
I looked into it a bit, tried to understand how it works. Create Confluence Server Ver 8 Plugin - #3 by ShellyNizri has also been a good help along the way.

Now the actual changes that I did to unbork it:

  • Add the dependencyManagement to pom
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.atlassian.confluence</groupId>
                <artifactId>confluence-plugins-platform-pom</artifactId>
                <version>${confluence.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

Good stuff.

  • Replace javax.servlet-api with jakarta:
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>jakarta.servlet.jsp</groupId>
            <artifactId>jakarta.servlet.jsp-api</artifactId>
            <scope>provided</scope>
        </dependency>
  • Check if confluence already includes velocity tools, it does! So remove this pesky compile scope with a provided one that will now use Confluence’s version:
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-tools</artifactId>
            <scope>provided</scope>
        </dependency>

I also suspect this thing was causing the CL problems, since it was including javax.servlet-api itself. I have to yet test if the Atlassian fork works fine, but that’s for tomorrow. Wouldn’t mind creating my own little util for the few methods that are used in a handful of velocity templates if it comes to it.

  • Next, I found that properties had
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>

So I just upped this to 11. (I don’t think it caused any problems though since the plugin pretty much worked fine, but since I read about it not being cool with Confluence 8 and that Java 11 is the way to go, why not.)

Wiped the target directory with clean, re-run mvn dependency:tree and checked that it does not contain any suspicious velocity or servlet dependencies, repackages, installed - and all is good and well now.

In retrospect, I really wonder for how long this might have been there, but my (limited) theory is that this worked in the past and finally gave up with Confluence 8. It just might have been a long time before somebody spotted it since we really don’t use it directly almost at all.

Thanks for the hints guys!

1 Like