java.lang.NoClassDefFoundError since 2016.2 update

Answered

Hello,

i'm trying to add java 8 support to following plugin https://github.com/bobi/felix-annotation-processor

but since idea 2016.2 i'm receiving java.lang.NoClassDefFoundError, on previous versions it works.

 

full stack trace:

org.jetbrains.jps.incremental.ProjectBuildException: Module 'felix-test' production: java.lang.NoClassDefFoundError: org/slf4j/spi/LoggerFactoryBinder
at org.jetbrains.jps.incremental.IncProjectBuilder.buildTargetsChunk(IncProjectBuilder.java:1012)
at org.jetbrains.jps.incremental.IncProjectBuilder.buildChunkIfAffected(IncProjectBuilder.java:870)
at org.jetbrains.jps.incremental.IncProjectBuilder.buildChunks(IncProjectBuilder.java:696)
at org.jetbrains.jps.incremental.IncProjectBuilder.runBuild(IncProjectBuilder.java:387)
at org.jetbrains.jps.incremental.IncProjectBuilder.build(IncProjectBuilder.java:194)
at org.jetbrains.jps.cmdline.BuildRunner.runBuild(BuildRunner.java:138)
at org.jetbrains.jps.cmdline.BuildSession.runBuild(BuildSession.java:294)
at org.jetbrains.jps.cmdline.BuildSession.run(BuildSession.java:125)
at org.jetbrains.jps.cmdline.BuildMain$MyMessageHandler$1.run(BuildMain.java:232)
at org.jetbrains.jps.service.impl.SharedThreadPoolImpl$1.run(SharedThreadPoolImpl.java:44)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NoClassDefFoundError: org/slf4j/spi/LoggerFactoryBinder
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:411)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.slf4j.LoggerFactory.bind(LoggerFactory.java:142)
at org.slf4j.LoggerFactory.performInitialization(LoggerFactory.java:121)
at org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:332)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:284)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:305)
at foo.bar.Test.<clinit>(Test.java:19)
at sun.misc.Unsafe.ensureClassInitialized(Native Method)
at sun.reflect.UnsafeFieldAccessorFactory.newFieldAccessor(UnsafeFieldAccessorFactory.java:43)
at sun.reflect.ReflectionFactory.newFieldAccessor(ReflectionFactory.java:142)
at java.lang.reflect.Field.acquireFieldAccessor(Field.java:1088)
at java.lang.reflect.Field.getFieldAccessor(Field.java:1069)
at java.lang.reflect.Field.get(Field.java:393)
at org.apache.felix.scrplugin.annotations.FieldAnnotation.getAnnotatedFieldValue(FieldAnnotation.java:60)
at org.apache.felix.scrplugin.processing.SCRAnnotationProcessor.createProperties(SCRAnnotationProcessor.java:407)
at org.apache.felix.scrplugin.processing.SCRAnnotationProcessor.process(SCRAnnotationProcessor.java:174)
at org.apache.felix.scrplugin.helper.AnnotationProcessorManager.process(AnnotationProcessorManager.java:94)
at org.apache.felix.scrplugin.helper.ClassScanner.processClass(ClassScanner.java:213)
at org.apache.felix.scrplugin.helper.ClassScanner.process(ClassScanner.java:161)
at org.apache.felix.scrplugin.helper.ClassScanner.scanSources(ClassScanner.java:146)
at org.apache.felix.scrplugin.SCRDescriptorGenerator.execute(SCRDescriptorGenerator.java:146)
at net.chilicat.felixscr.intellij.build.scr.AbstractScrProcessor.execute(AbstractScrProcessor.java:77)
at net.chilicat.felixscr.intellij.jps.FelixModuleLevelBuilder.build(FelixModuleLevelBuilder.java:43)
at org.jetbrains.jps.incremental.IncProjectBuilder.runModuleLevelBuilders(IncProjectBuilder.java:1237)
at org.jetbrains.jps.incremental.IncProjectBuilder.runBuildersForChunk(IncProjectBuilder.java:911)
at org.jetbrains.jps.incremental.IncProjectBuilder.buildTargetsChunk(IncProjectBuilder.java:983)
... 14 more
Caused by: java.lang.ClassNotFoundException: org.slf4j.spi.LoggerFactoryBinder
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
 

5 comments
Comment actions Permalink

Hi Andrey, 

Please note the "Caused by:...." stacktrace part.

It looks lilke net.chilicat.felixscr.intellij.jps.FelixModuleLevelBuilder creates an instance ScrProcessor and configures it with some classpath. The ScrProcessor then creates a classloader for the specified classpath, loads classes with this classLoader and processes loaded classes via Reflection API.

This particular exception is caused by the incomplete classpath that ScrProcessor prepared.

Hope this helps, 

Best regards, 

  Eugene.

 

0
Comment actions Permalink

Hi, classpath looks ok, also same code works on idea 2016.1.4 but does not work on idea 2016.2 and newer.

in general classpath and classloader looks the following

 

final Collection<String> classPath = new LinkedHashSet<String>();


classPath.add(moduleChunk.representativeTarget().getOutputDir());
classPath.add(PathUtil.getJarPathForClass(Component.class));
classPath.add(PathUtil.getJarPathForClass(BundleContext.class));

JpsJavaExtensionService service = JpsJavaExtensionService.getInstance();
JpsJavaDependenciesEnumerator enr = service.enumerateDependencies(Collections.singleton(moduleChunk.representativeTarget().getModule()));
JpsJavaDependenciesRootsEnumerator classes = enr.productionOnly().withoutSdk().recursively().classes();
for (File f : classes.getRoots()) {
// filter out non-Java classpath entries, because Felix fails processing them
if (f.getName().endsWith(".class") || f.getName().endsWith(".jar") || f.isDirectory()) {
classPath.add(f.getAbsolutePath());
}
}

final URL[] urls = new URL[classPath.size()];

for (int i = 0; i < classPath.size(); i++) {
urls[i] = new File(classPath.get(i)).toURI().toURL();
}

ClassLoader classloader = new URLClassLoader(urls, this.getClass().getClassLoader());

0
Comment actions Permalink

> looks ok, also same code works on idea 2016.1.4 but does not work on idea 2016.2 and newer.

Such behavior is quite possible. Take a look at how the classloader is created:

new URLClassLoader(urls, this.getClass().getClassLoader());

Parent classloader is not null and is the same loader that loaded the ScrProcessor. This means that created loader will first try to resolve classes against JPS classes and only then against the specified classpath urls.

Previous versions of IDEA might have had this missing class in the classpath, while the newer builds don't. There is also no guarantee that newer versions of JPS won't be using classes or libraries that would conflict with the classes present in the user's project being compiled.

Strictly speaking, using classloader in compiler for compiled project's class resolution is the easiest, but not the best approach, mainly because classes are loaded in compiler's JVM and compiler's environment influences the resolution result a lot. This case is a good example of such problems.

You can make class resolution process less dependent on builder's runtime, if you pass null to the classloader, but still there might be problems with cross-compilation:

- bytecode version of user's classes may be incompatible with builder's runtime;

- in general case there is no way to resolve JDK classes against the JDK that is specified in project configuration: with classloader-based approach system classes will always be resolved against the JDK that is used to run the JPS build system.

0
Comment actions Permalink

Thank you, make sense for me, but....

 

I need parent classloader as i need to pass log messages from SCR Generator to Idea messages window.

also i found the issue, org.slf4j.impl.StaticLoggerBinder was included into gradle plugin but slf4j was not in parent classpath.

0
Comment actions Permalink

> I need parent classloader as i need to pass log messages from SCR Generator to Idea messages window.

 

I would still re-consider using classloaders for user's code analysis.Sooner or later the problems I mentioned above will appear. The ASM bytecode analysis framework looks like a great choice for the task.

0

Please sign in to leave a comment.