With the update from v3.4.4 to v3.4.5 we encountered two ClassNotFoundException in combination with Spring Webflux and HATEOAS.

I compared what has changed between these two versions and only the following had changed:

  • Spring Boot (of course)
  • Spring Framework 6.2.5 to 6.2.6
  • Reactor BOM 2024.0.4 to 2024.0.5

After using the previous versions of Spring Framework and Reactor, the error still persists and the only change is Spring Boot. The problem also exists with the current version 3.5.0.

There are two problems, the first relates to CollectionJsonAffordanceModelFactory and the second to HtmlInputTypeFactory.

stacktace-HtmlInputTypeFactory.txt stacktace-CollectionJsonAffordanceModelFactory.txt

I have created a sample application to reproduce the error.

spring-hateoas-issue.zip

  1. mvn package
  2. java -jar .\target\cl-issue-demo-0.0.1-SNAPSHOT.jar
  3. in another console: curl -v http://localhost:8080/hello

No response is received and the stacktrace is logged.

Java version is 21.

openjdk version "21.0.4" 2024-07-16 LTS
OpenJDK Runtime Environment Temurin-21.0.4+7 (build 21.0.4+7-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.4+7 (build 21.0.4+7-LTS, mixed mode, sharing)

We were also able to create workarounds for both problems by triggering the static code on the main thread with an ApplicationListener (please see ApplicationConfiguration in the sample app).

@Bean
    public ApplicationListener<ApplicationStartedEvent> hateoasWorkaround() {
        // workaround classloader issues
        return event -> {
            // issue #1: org.springframework.hateoas.mediatype.collectionjson.CollectionJsonAffordanceModelFactory
            var link = org.springframework.hateoas.Link.of("test");
            org.springframework.hateoas.mediatype.Affordances.of(link).afford(HttpMethod.GET).build();
            // issue #2: org.springframework.hateoas.mediatype.html.HtmlInputTypeFactory
            var type = ResolvableType.forClassWithGenerics(Mono.class, ResolvableType.forClassWithGenerics(ResponseEntity.class, TestResponse.class));
            org.springframework.hateoas.mediatype.PropertyUtils.getExposedProperties(type);
        };
    }

Comment From: Somesh-coding

Hi 👋, I would like to work on this issue. Can you please assign it to me?

Comment From: snicoll

Thanks for the report and the sample. This seems to be a regression introduced by https://github.com/spring-projects/spring-boot/commit/4af0ee20d1e0c86cd2f9.

SpringFactories#getSpringFactoriesInstances is now creating the SpringResourceLoader for META-INF/spring.factories. It's cached which means that further attempt to load any factory from that file won't have the propert classloader as before.

Comment From: wilkinsona

I think there are conflicting requirements here:

  • https://github.com/spring-projects/spring-boot/pull/45014 wants to be able to use the TCCL to load the factory implementations. When the TCCL changes across factory loading calls, the different class loader should be used each time
  • This issue wants to use a fixed class loader across multiple factory loading calls

Both of these requirements are reasonable. Unfortunately, as far as I can tell, they cannot be supported at the same time while Framework's cache pollution problem remains.

Prior to 4af0ee20, the cache pollution meant that only the second requirement was met. After it, only the first requirement is met.

Given that the first requirement is new in 3.5, I'm leaning towards reverting 4af0ee20 in at least 3.3.x to restore the previous behavior in our final OSS 3.3.x release. For 3.4.x and 3.5.x we can then hopefully get a fix into Framework so that both requirements can be met.

Comment From: philwebb

+1 to reverting