We've encountered a connectivity problem after migrating from Spring Boot 3.1.5/3.1.6 to 3.2.0. The error log points to a missing class:

java.lang.ClassNotFoundException: com.mongodb.event.ServerClosedEvent

This is affecting our connection to MongoDB "AWS DocumentDB." Any guidance or resolution on this matter would be greatly appreciated.

Opera Snapshot_2023-11-30_121747_us-east-1 console aws amazon com Opera Snapshot_2023-11-30_121933_us-east-1 console aws amazon com

Comment From: mhalbritter

Hello!

I can't see any code from Boot in the stacktrace, besides the JAR loader. I'm not sure if this has something to do with the Uber JAR rewrite. Can you please try with the classic loader implementation and report back if that fixes the issue?

See here for details: https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.2-Release-Notes#nested-jar-support

Comment From: manjian14

The connectivity issue with MongoDB "AWS DocumentDB" appears resolved after implementing the classic loader in Spring Boot 3.2.0:

bootJar {
    archiveFileName = "${rootProject.name}.${archiveExtension.get()}"
    loaderImplementation = LoaderImplementation.CLASSIC
}

Any additional insights or recommendations moving forward would be appreciated.

Comment From: mhalbritter

cc @philwebb

Comment From: wilkinsona

@manjian14 The stack trace would suggest that the problem occurs when there's a change in the topology of the cluster but I haven't been able to reproduce it. Can you please provide a minimal sample and the steps to follow to cause the failure?

Comment From: manjian14

I've configured my application to connect to an AWS DocDB cluster using the standard driver configuration. Below is a snippet from my MongoDbConfiguration class:

@Slf4j
@Configuration
@EnableMongoRepositories(value = "com.????")
@ConditionalOnProperty(prefix = "mongo.connection", value = "string")
public class MongoDbConfiguration extends AbstractMongoClientConfiguration {

    @Value("${mongo.connection.string}")
    private String connectionString;

    @Override
    @Primary
    @Bean
    public @NonNull MongoClient mongoClient() {
        return MongoClients.create(connectionString);
    }

    @Override
    protected @NonNull String getDatabaseName() {
        return "???";
    }
}

In this setup, I've embedded the username and password within the cluster URL. The cluster has two replica sets, one for reading and one for writing. Additionally, the database is not accessible from outside; access is restricted either by whitelisted IP addresses or by project roles within the ECS cluster.

The AWS DocDB version is 5.0.0, the latest version as per AWS recommendations. The project is using Gradle version 8.5 and Java version 17. The base image is amazoncorretto:17-al2023-headful, and during the image build process, I've imported the SSL certificate using the keytool tool.

If you have any specific questions or if there's anything else you'd like to know, feel free to ask!

Comment From: philwebb

@manjian14 Is there any chance you could switch back to the DEFAULT loader implementation and try 3.2.1-SNAPSHOT? We've fixed a few bugs with the nested loader. I'm doubtful that we've caught this one, but I'd like to check since it seems hard for us to replicate the problem locally.

Comment From: manjian14

I tried using 3.2.1-SNAPSHOT, but ran into a problem—' springboot binaries are incompatible with your platform'

Comment From: wilkinsona

What platform are you using? It's rather unfortunate that it apparently won't allow you to test snapshot artifacts.

Comment From: msievers

Seeing the same error with our own MongoDB cluster(s) deployed to our own Kubernetes starting with Spring Boot 3.2.0.

java.lang.NoClassDefFoundError: com/mongodb/event/ServerClosedEvent

I'll give it a try with 3.2.1-SNAPSHOT.

Comment From: msievers

Same thing with 3.2.1-SNAPSHOT

java.lang.NoClassDefFoundError: com/mongodb/event/ServerClosedEvent
    at com.mongodb.internal.connection.DefaultServer.close(DefaultServer.java:172)
    at com.mongodb.internal.connection.AbstractMultiServerCluster.removeExtraHosts(AbstractMultiServerCluster.java:436)
    at com.mongodb.internal.connection.AbstractMultiServerCluster.ensureServers(AbstractMultiServerCluster.java:407)
    at com.mongodb.internal.connection.AbstractMultiServerCluster.handleReplicaSetMemberChanged(AbstractMultiServerCluster.java:248)
    at com.mongodb.internal.connection.AbstractMultiServerCluster.lambda$onChange$3(AbstractMultiServerCluster.java:197)
    at com.mongodb.internal.Locks.lambda$withInterruptibleLock$1(Locks.java:53)
    at com.mongodb.internal.Locks.checkedWithInterruptibleLock(Locks.java:66)
    at com.mongodb.internal.Locks.withInterruptibleLock(Locks.java:59)
    at com.mongodb.internal.Locks.withInterruptibleLock(Locks.java:52)
    at com.mongodb.internal.connection.BaseCluster.withLock(BaseCluster.java:226)
    at com.mongodb.internal.connection.AbstractMultiServerCluster.withLock(AbstractMultiServerCluster.java:54)
    at com.mongodb.internal.connection.AbstractMultiServerCluster.onChange(AbstractMultiServerCluster.java:165)
    at com.mongodb.internal.connection.DefaultSdamServerDescriptionManager.updateDescription(DefaultSdamServerDescriptionManager.java:113)
    at com.mongodb.internal.connection.DefaultSdamServerDescriptionManager.lambda$update$0(DefaultSdamServerDescriptionManager.java:75)
    at com.mongodb.internal.Locks.lambda$withInterruptibleLock$1(Locks.java:53)
    at com.mongodb.internal.Locks.checkedWithInterruptibleLock(Locks.java:66)
    at com.mongodb.internal.Locks.withInterruptibleLock(Locks.java:59)
    at com.mongodb.internal.Locks.withInterruptibleLock(Locks.java:52)
    at com.mongodb.internal.connection.BaseCluster.withLock(BaseCluster.java:226)
    at com.mongodb.internal.connection.AbstractMultiServerCluster.withLock(AbstractMultiServerCluster.java:54)
    at com.mongodb.internal.connection.DefaultSdamServerDescriptionManager.update(DefaultSdamServerDescriptionManager.java:60)
    at com.mongodb.internal.connection.DefaultServerMonitor$ServerMonitorRunnable.run(DefaultServerMonitor.java:169)
    at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.ClassNotFoundException: com.mongodb.event.ServerClosedEvent
    at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:593)
    at org.springframework.boot.loader.net.protocol.jar.JarUrlClassLoader.loadClass(JarUrlClassLoader.java:104)
    at org.springframework.boot.loader.launch.LaunchedClassLoader.loadClass(LaunchedClassLoader.java:91)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
    ... 23 more 

Comment From: msievers

The errors are gone when using <loaderImplementation>CLASSIC</loaderImplementation> with Spring Boot 3.2.0

Comment From: philwebb

@msievers Are you able to provide a sample application that we can debug?

Comment From: msievers

@philwebb I'll give it a try and ping you once I got it. As I cannot take one of our company apps where I see this happen, I hope it can be reproduced with some simple playground app.

Comment From: ANigam123

Hi @msievers I have been facing a similar problem, tried CLASSIC loader implementation but it's still failing for my maven project. It worked fine with one of my Gradle projects. Can you please share the plugin config ? Thanks. I tried with :

        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
                <execution>
                    <configuration>
                        <loaderImplementation>CLASSIC</loaderImplementation>
                    </configuration>
                </execution>
            </executions>
        </plugin>

Comment From: msievers

@philwebb Here is a minimal application with which I was able to see the error locally. It seems to be triggered when starting the app with a MongoDB replication set, see

https://github.com/msievers/spring-boot-issue-38611

Comment From: msievers

@ANigam123 I'm afraid my configuration looks exactly like yours, besides that I don't have separate executions but only the top-level configuration like this (but that shouldn't make any difference).

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <loaderImplementation>CLASSIC</loaderImplementation>
    </configuration>
</plugin>

Comment From: wilkinsona

Thanks very much, @msievers. I've now been able to reproduce the ClassNotFoundException.

Comment From: ANigam123

@msievers actually it did solve the problem for me. I moved the configuration to top-level and it started working. Not sure what's the difference. Thanks for the help!

Comment From: wilkinsona

As an alternative to switching back to the classic loader implementation, another workaround is to load the class up front:

@SpringBootApplication
public class Application {

    public static void main(String[] args) throws Exception {
        Class.forName("com.mongodb.event.ServerClosedEvent");
        SpringApplication.run(Application.class, args);
    }

}

Comment From: wilkinsona

This is another variant of https://github.com/spring-projects/spring-boot/issues/38154, but this time allowing the interruption to continue by rethrowing the ClosedByInterruptException is insufficient. We really need to perform the read in an uninterruptible manner. Unfortunately, the JDK's support for that is an implementation detail of sun.nio.ch.FileChannelImpl so we'll need to implement our own.

Comment From: wilkinsona

Possible fix: https://github.com/wilkinsona/spring-boot/tree/gh-38611.

Comment From: philwebb

We've just pushed a fix for this. It would be much appreciated if someone could try the latest 3.2.1 SNAPSHOT with the default loader to see if we've fixed the problem.

Comment From: msievers

@philwebb I'll give it a try and report back.

Comment From: msievers

@philwebb It's still failing with 3.2.1-SNAPSHOT. Was the last CI build completed successfully? It's orange, maybe the changes were not published/pushed, see

https://ci.spring.io/teams/spring-boot/pipelines/spring-boot-3.2.x/jobs/build/builds/802

Comment From: philwebb

Gahh, those failures are a pain. It wasn't published but I also didn't get a notification. I've kicked it off again and I'll comment back here when it's done. Sorry about that.

Comment From: philwebb

Sorry @msievers, we're having some trouble with our CI right now and we haven't managed to publish jars yet.

Comment From: wilkinsona

@msievers Our CI's been fixed and a new snapshot 3.2.1-SNAPSHOT build has been published. Could you please give it a try if you have the time?

Comment From: msievers

@philwebb @wilkinsona I can confirm that for my two use cases, one being our internal application and the other the application I created on GitHub to reproduce the error, the issue seems to have disappeared with the use of the current Spring Boot 3.2.1-SNAPSHOT version.

Comment From: wilkinsona

Thank you, @msievers. That's some greatly appreciated good news.