Closed
Description
I discovered this in a long yak-shaving session migrating an app to Boot 2.4.1 and Hazelcast 4.0.3.
This fails when you run it from the jar:
@SpringBootApplication
public class IssueApplication {
public static void main(String[] args) {
SpringApplication.run(IssueApplication.class, args);
}
@Bean
CommandLineRunner runner(HazelcastInstance hazelcast) {
IMap<String, Foo> sessions = hazelcast.getMap("foos");
return args -> {
sessions.set("foo", new Foo("foo"));
System.err.println(sessions.get("foo"));
sessions.getAsync("foo").whenComplete((u, t) -> System.err.println(u)).toCompletableFuture().get();
};
}
}
class Foo implements Serializable {
private String value;
public Foo() {
}
public Foo(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public String toString() {
return "Foo [value=" + this.value + "]";
}
}
It's fine when you run in an IDE, or with java -classpath ...
. The problem is that Hazelcast uses the Thread.crrentThread().getContextClassLoader()
by default, and in the async background thread this is the JarLauncher
not the AppClassLoader
.
I worked around it with this
@Bean
Config hazelcastConfig() {
Config config = new Config();
config.setClassLoader(SpringApplication.class.getClassLoader());
...
return config;
}
but the issue is more generic really - probably any library that uses JDK fork-join utilities will end up with the same class loader.