orika-mapper / orika

Simpler, better and faster Java bean mapping framework
http://orika-mapper.github.io/orika-docs/
Apache License 2.0
1.29k stars 269 forks source link

NullPointerException in JavassistCompilerStrategy.registerClassLoader when using ForkJoinPool #225

Open u023871 opened 7 years ago

u023871 commented 7 years ago

The issue

NullPointerException in JavassistCompilerStrategy.registerClassLoader when using ForkJoinPool

Stacktrace: ...

Caused by: *.exception.MappingException: Error getting mapper facade for locale en, srcClass class *.service.masterdata.entity.DimensionEntity and de
stClass class *.service.masterdata.model.Dimension! ; error message: java.lang.NullPointerException
        at *.persistence.mapper.AbstractBeanMapper.mapLocalized(AbstractBeanMapper.java:449)
        at *.persistence.mapper.AbstractBeanMapper$$FastClassBySpringCGLIB$$d2829c31.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:669)
        at *.service.masterdata.mapper.MasterDataBeanMapper$$EnhancerBySpringCGLIB$$d287b8ff.mapLocalized(<generated>)
        at *.service.masterdata.service.QuestionnaireService.cleanDimensions(QuestionnaireService.java:449)
        ... 35 common frames omitted
Caused by: ma.glasnost.orika.MappingException: java.lang.NullPointerException
        at ma.glasnost.orika.impl.generator.MapperGenerator.build(MapperGenerator.java:104)
        at ma.glasnost.orika.impl.DefaultMapperFactory.buildMapper(DefaultMapperFactory.java:1480)
        at ma.glasnost.orika.impl.DefaultMapperFactory.build(DefaultMapperFactory.java:1295)
        at ma.glasnost.orika.impl.DefaultMapperFactory.getMapperFacade(DefaultMapperFactory.java:883)
        at ma.glasnost.orika.impl.DefaultMapperFactory.getMapperFacade(DefaultMapperFactory.java:1609)
        at ma.glasnost.orika.impl.DefaultMapperFactory.getMapperFacade(DefaultMapperFactory.java:1598)
        at ma.glasnost.orika.impl.DefaultMapperFactory.getMapperFacade(DefaultMapperFactory.java:1621)
        at *.persistence.mapper.AbstractBeanMapper.getMapperFacade(AbstractBeanMapper.java:114)
        at *.persistence.mapper.AbstractBeanMapper.mapLocalized(AbstractBeanMapper.java:445)
        ... 40 common frames omitted
Caused by: java.lang.NullPointerException: null
        at ma.glasnost.orika.impl.generator.JavassistCompilerStrategy.registerClassLoader(JavassistCompilerStrategy.java:134)
        at ma.glasnost.orika.impl.generator.JavassistCompilerStrategy.compileClass(JavassistCompilerStrategy.java:223)
        at ma.glasnost.orika.impl.generator.SourceCodeContext.compileClass(SourceCodeContext.java:245)
        at ma.glasnost.orika.impl.generator.SourceCodeContext.getInstance(SourceCodeContext.java:262)
        at ma.glasnost.orika.impl.generator.MapperGenerator.build(MapperGenerator.java:73)
        ... 48 common frames omitted

Cause

Class: ma.glasnost.orika.impl.generator.JavassistCompilerStrategy Method compileClass: line 223 (Thread.currentThread().getContextClassLoader() -> null) Method registerClassLoader: ClassLoader cl -> null; thus ... NullPointerException in referencedLoaders.get(cl)

Why does this happen?

After some remote debugging I found out: In some cases the contextClassLoader of a thread is null. Javadoc for Thread.getContextClassLoader() says: "return: the context ClassLoader for this Thread, or null indicating the system class loader (or, failing that, the bootstrap class loader)"

In my case, where I am working multithreaded (using a CompletableFuture implementation) for generating masses of test data, I hit this case, where contextClassLoader is null sometimes:

2017-07-17 14:26:59,317 INFO  [*.persistence.mapper.AbstractBeanMapper] - Thread.currentThread(): Thread[ForkJoinPool.commonPool-worker-3,5,main]
2017-07-17 14:26:59,318 INFO  [*.persistence.mapper.AbstractBeanMapper] - Thread.currentThread() name: ForkJoinPool.commonPool-worker-3
2017-07-17 14:26:59,318 INFO  [*.persistence.mapper.AbstractBeanMapper] - Thread.currentThread() id: 82
2017-07-17 14:26:59,318 INFO  [*.persistence.mapper.AbstractBeanMapper] - Thread.currentThread() stacktrace: [Ljava.lang.StackTraceElement;@51547e0a
2017-07-17 14:26:59,318 INFO  [*.persistence.mapper.AbstractBeanMapper] - Thread.currentThread() priority: 5
2017-07-17 14:26:59,318 INFO  [*.persistence.mapper.AbstractBeanMapper] - Thread.currentThread() group: java.lang.ThreadGroup[name=main,maxpri=10]
2017-07-17 14:26:59,318 INFO  [*.persistence.mapper.AbstractBeanMapper] - Thread.currentThread() state: RUNNABLE
2017-07-17 14:26:59,318 INFO  [*.persistence.mapper.AbstractBeanMapper] - Thread.currentThread() contextClassLoader: null

My workaround

https://orika-mapper.github.io/orika-docs/troubleshooting.html I included the orika-eclipse-tools dependency in v1.5.1 and set like System.setProperty(OrikaSystemProperties.COMPILER_STRATEGY,EclipseJdtCompilerStrategy.class.getName()); i.e. what I do in my Tomcat is: JAVA_OPTS="$JAVA_OPTS -Dma.glasnost.orika.compilerStrategy=ma.glasnost.orika.impl.generator.EclipseJdtCompilerStrategy" This works fine, but I do not know about the performance difference to the Javaassist implementation...

Also tried JaninoCompilerStrategy...

BTW: The JaninoCompilerStrategy did also not work for me, it caused a ClassNotFoundException for a class which it obviously generated on itself beforehand... Well...

...

Caused by: ma.glasnost.orika.MappingException: ma.glasnost.orika.MappingException: Can not compile the generated mapper
        at ma.glasnost.orika.impl.generator.MapperGenerator.build(MapperGenerator.java:104)
        at ma.glasnost.orika.impl.DefaultMapperFactory.buildMapper(DefaultMapperFactory.java:1480)
        at ma.glasnost.orika.impl.DefaultMapperFactory.build(DefaultMapperFactory.java:1295)
        at ma.glasnost.orika.impl.DefaultMapperFactory.getMapperFacade(DefaultMapperFactory.java:883)
        at ma.glasnost.orika.impl.DefaultMapperFactory.getMapperFacade(DefaultMapperFactory.java:1609)
        at ma.glasnost.orika.impl.DefaultMapperFactory.getMapperFacade(DefaultMapperFactory.java:1598)
        at ma.glasnost.orika.impl.DefaultMapperFactory.getMapperFacade(DefaultMapperFactory.java:1621)
        at *.persistence.mapper.AbstractBeanMapper.getMapperFacade(AbstractBeanMapper.java:114)
        at *.persistence.mapper.AbstractBeanMapper.mapLocalized(AbstractBeanMapper.java:445)
        ... 57 common frames omitted
Caused by: ma.glasnost.orika.MappingException: Can not compile the generated mapper
        at ma.glasnost.orika.impl.generator.JaninoCompilerStrategy.compileClass(JaninoCompilerStrategy.java:65)
        at ma.glasnost.orika.impl.generator.SourceCodeContext.compileClass(SourceCodeContext.java:245)
        at ma.glasnost.orika.impl.generator.SourceCodeContext.getInstance(SourceCodeContext.java:262)
        at ma.glasnost.orika.impl.generator.MapperGenerator.build(MapperGenerator.java:73)
        ... 65 common frames omitted
Caused by: java.lang.ClassNotFoundException: ma.glasnost.orika.generated.Orika_FlightLegEntity_FlightLeg_Mapper30094723959871601$0
        at ma.glasnost.orika.impl.generator.ByteArrayClassLoader.findClass(ByteArrayClassLoader.java:57)
        at ma.glasnost.orika.impl.generator.JaninoCompilerStrategy.compileClass(JaninoCompilerStrategy.java:62)
        ... 68 common frames omitted

Environment

Orika v1.5.1

System: Fedora Core 20 (Linux 3.19.8-100.fc20.x86_64) Oracle JDK 1.8.0_131-b11 Apache Tomcat/8.5.14 Xmx4096m Spring Boot Starter 1.5.4

Important: I do NOT have this issue on Windows 8.1 (same test setup)!

Cheers, eztup

smvdheijden commented 5 years ago

We were experiencing the same issue using parallel streams and were able to solve it using the solution proposed here: https://stackoverflow.com/questions/49110537/parallel-stream-doesnt-set-thread-contextclassloader-after-tomcat-upgrade

elaatifi commented 5 years ago

Thank you both, also for the detailed report.

Issues (related to class loaders) with Janino are fixed not shipped yet.

Just for information javassist and eclipse will be dropped only Janino will be supported natively in the future releases for Java 11

Generated mappers still can be debugged with debug information in generated byte code.

Eclipse jdt has more important memory footprint but once mappers generated no impact on run performance

On Thu, Feb 21, 2019, 4:42 PM Sander van der Heijden < notifications@github.com wrote:

We were experiencing the same issue using parallel streams and were able to solve it using the solution proposed here:

https://stackoverflow.com/questions/49110537/parallel-stream-doesnt-set-thread-contextclassloader-after-tomcat-upgrade

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/orika-mapper/orika/issues/225#issuecomment-466047771, or mute the thread https://github.com/notifications/unsubscribe-auth/ABUmJBj0RZ-_ffhs-nE6rGHoXDYwW-9Fks5vPr5SgaJpZM4OZ90B .

smogstate commented 4 years ago

@elaatifi hi, does Eclipse jdt has bigger memory footprint as javassist?

elaatifi commented 4 years ago

Of course. This issue need to be investigated if you have some failing unit test demonstrating the issue ?

On Wed, Nov 20, 2019, 14:44 Denis Muraviev notifications@github.com wrote:

@elaatifi https://github.com/elaatifi hi, does Eclipse jdt has bigger memory footprint as javassist?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/orika-mapper/orika/issues/225?email_source=notifications&email_token=AAKSMJAWGA4XRDJON6N5BUDQUU5MPA5CNFSM4DTH3UA2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEESAOHA#issuecomment-556009244, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAKSMJCG7JLZLK3B2XJQ2Q3QUU5MPANCNFSM4DTH3UAQ .

smogstate commented 4 years ago

@elaatifi we got a different problem, memory consumption is a bit high. Default one - javassist has bigger memory consumption, but mappers generation is faster. Eclipse jdt has less memory consumption but longer start time. I came to that conclusion yesterday after running couple tests. but you say its opposite =D