Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 11 upgrade from 8 parallel streams throws ClassNotFoundException

I upgraded my spring boot application from Java 8 and tomcat 8 to java 11 and tomcat 9. Everything seems to work fine except the part that I use parallel streams on lists.

list.addAll(items
                .parallelStream()
                .filter(item -> !SomeFilter.isOk(item.getId()))
                .map(logic::getSubItem)
                .collect(Collectors.toList()));

The previous part of code used to work fine with Java 8 and Tomcat 8 but after Java 9 changes on how the classes are loaded with Fork/Join common pool threads return the system class loader as their thread context class loader.

I know that under the hood parallel streams use ForkJoinPool and I created a custom bean class but still, it is not used by the app. Most probably due to the fact that maybe they are created before this bean.

@Bean
public ForkJoinPool myForkJoinPool() {
    return new ForkJoinPool(threadPoolSize, makeFactory("APP"), null, false);
}

private ForkJoinPool.ForkJoinWorkerThreadFactory makeFactory(String prefix) {
    return pool -> {
        final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
        worker.setName(prefix + worker.getPoolIndex());
        worker.setContextClassLoader(Application.class.getClassLoader());
        return worker;
    };
}

Lastly, I also tried to wrap it around an instance of my ForkJoinPool but it is done asynchronously and I do not want to. I also do not want to use the submit and get because that means that I have to wrap up all the parallel stream that I have on the application with try/catch and the code will be nasty to read.

forkJoinPool.execute(() -> 
  list.addAll(items
                .parallelStream()
                .filter(item -> !SomeFilter.isOk(item.getId()))
                .map(logic::getSubItem)
                .collect(Collectors.toList())));

Ideally, I would like all my parallel streams that are used in the application to use the class loader from the application and not the system one.

Any idea?

like image 492
PavlMits Avatar asked Feb 17 '21 10:02

PavlMits


1 Answers

If using a third-party is an option, you could use the Parallel Collectors library:

list.addAll(items
            .stream()
            .filter(item -> !SomeFilter.isOk(item.getId()))
            .collect(parallel(logic::getSubItem, Collectors.toList(), forkJoinPool)
            .join());
like image 61
M A Avatar answered Sep 19 '22 14:09

M A