2

I'm generating the source of some java classes (using kotlin, as later this should happen in gradle). To test the generated classes, I've build a unit-test-infrastructure, which compiles the generated code and loads it into the JVM, where it is tested. This works just fine for most of the classes, but sadly not for enums.

Stacktrace:

de/yaml/snippet/test/no0/SchemasEnumBaseNameReqProp1 (wrong name: de/yaml/snippet/test/no0/SchemasEnumBasenameReqProp1)
java.lang.NoClassDefFoundError: de/yaml/snippet/test/no0/SchemasEnumBaseNameReqProp1 (wrong name: de/yaml/snippet/test/no0/SchemasEnumBasenameReqProp1)
    at java.base/java.lang.ClassLoader.defineClass1(Native Method)
    at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1017)
    at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
    at java.base/java.net.URLClassLoader.defineClass(URLClassLoader.java:550)
    at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:458)
    at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:452)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:451)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
    at java.base/java.net.FactoryURLClassLoader.loadClass(URLClassLoader.java:899)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:398)
    at JavaSourceExecutor.get(JavaSourceExecutor.kt:43)
    at SourceExecutorPackageWrapper.get(JavaSourceExecutor.kt:10)
    at BasicModelGenerationTests.testEnum(BasicModelGenerationTests.kt:365)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:686)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:205)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:201)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:137)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:71)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
    at com.sun.proxy.$Proxy2.stop(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.stop(TestWorker.java:133)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
    at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:414)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    at java.base/java.lang.Thread.run(Thread.java:834)

The class-name to load is definitive correct and if I try a wrong name, I get a different stacktrace.

Additional info (what I tried)

This is how I load the class after it was compiled: Class.forName(fullClassReference, true, classLoader)

The ClassLoader is aware of the class (The selected class in blue is the one I want):

IntelliJ debugging window, with classes the used ClassLoader knows

(listing classes known by ClassLoader using a mean trick I borrowed from here)

The non-enum-classes in the same ClassLoader can be loaded without any problem:

IntelliJ debugging window, with loaded class at same debugging line

I've also tried the other enums with no luck...

Screenshot of source directory: Screenshot of source directory (german)

Screenshot of compiled directory: Screenshot of compiled directory (german)

Decompiled enum: Decompiled enum

Question

Is there anything special about enums, that makes my logic fail? I'm happy to provide additional information. Thanks in advance for any hint you can give.

Nils-o-mat
  • 1,132
  • 17
  • 31
  • Is the `.class` file of the enum actually generated, correctly? How do you generate the classes and enums? – dan1st Mar 24 '21 at 10:37
  • @dan1st I've added a screenshot of the generated classes. The classes depend on each other, and generation would fail, if there were compiler errors in any of them. – Nils-o-mat Mar 24 '21 at 10:43
  • @dan1st I generate them using ToolProvider#getSystemJavaCompiler. I can post the code, if you think it is important, but than it would get even more messy. – Nils-o-mat Mar 24 '21 at 10:47
  • Does [`ClassLoader#loadClass(name)`](https://docs.oracle.com/javase/10/docs/api/java/lang/ClassLoader.html#loadClass(java.lang.String)) work? – dan1st Mar 24 '21 at 10:50
  • Shouldn't `de/yaml/snippet/test/no0/SchemasEnumBaseNameReqProp1` be `de.yaml.snippet.test.no0.SchemasEnumBaseNameReqProp1`? – Lino Mar 24 '21 at 10:57
  • @dan1st No, I tried that too... That fails with the same exception-message. – Nils-o-mat Mar 24 '21 at 10:57
  • @Lino This is just the exception message. Seems like the full-reference (with dots) is internally transformed to that. But, what I pass in is ```de.yaml.snippet.test.no0.SchemasEnumBasenameReqProp1``` – Nils-o-mat Mar 24 '21 at 10:59
  • The classloader you used is a `URLClassLoader`. Do you use Java 8- and the default ClassLoader or did you create the `URLClassLoader` by yourself? If so, how? Also, how did you generate the classes/enums? – dan1st Mar 24 '21 at 10:59
  • 1
    Can you decompile the generated enum? What is the package declaration? – dan1st Mar 24 '21 at 11:00
  • @dan1st Yes, I create the URLClassLoader by myself: ```URLClassLoader.newInstance(listOf(sourceDir.toURI().toURL()).toTypedArray())``` – Nils-o-mat Mar 24 '21 at 11:01
  • @dan1st The source-code generation is the main part of this program and much too long for this question. Please specify, what exactly you would like to know. – Nils-o-mat Mar 24 '21 at 11:02
  • @dan1st I added a picture of the decompiled enum. It's really just that simple :-) – Nils-o-mat Mar 24 '21 at 11:07
  • How is the `URLClassLoader` created? – dan1st Mar 24 '21 at 11:16
  • @dan1st See 4 comments above. – Nils-o-mat Mar 24 '21 at 11:16
  • Is `sourceDir` the directory where the generated `.class` files are stored? – dan1st Mar 24 '21 at 11:18
  • @dan1st No, it's the directory of the source. You can see the first package-directory ("de"), where the classes are compiled to, in the screenshot of the source directory. – Nils-o-mat Mar 24 '21 at 11:21
  • Try using the directory with the generated classes (root package) – dan1st Mar 24 '21 at 11:23
  • @dan1st I've tried to add the sub-directory "de" to the source dir. But than the class is not found at all: val cl = URLClassLoader.newInstance(listOf(File(sourceDir, "de").toURI().toURL()).toTypedArray()) cl.loadClass(aFullClassReference) – Nils-o-mat Mar 24 '21 at 11:26
  • @dan1st As the classLoader definitively finds the other classes in the same directory, it would have really surprised me, if this had worked :-( – Nils-o-mat Mar 24 '21 at 11:27
  • No, do not add the package to the subdirectory but use the directory of the compiled classes. If the compiled enum is in e.g. `target/generated-classes/de/yaml/snippet/test/no0/SchemasEnumBaseNameReqProp1`, you should select `target/generated-classes`. – dan1st Mar 24 '21 at 12:00
  • @dan1st Ok, but than we misunderstood each other. The source directory IS the directory, where I generate my classes to, so your "generated-classes"-directory is my sourceDir. (That's why the root package is there, as can be seen in the screenshot) To clarify: Source-file-path: ```/SchemasEnumBaseNameReqProp1.java``` - Class-file-path: ```/de/yaml/snippet/test/no0/SchemasEnumBaseNameReqProp1.class``` – Nils-o-mat Mar 24 '21 at 12:09
  • 4
    Did you notice the different case of the N in `SchemasEnumBasenameReqProp1` and `SchemasEnumBaseNameReqProp1 `? “Basename” vs “BaseName”. The filesystem may be case insensitive (e.g. in case of Windows) and the class file get loaded, but Java class names are case sensitive and a mismatch gets rejected. – Holger Mar 24 '21 at 12:11
  • @Holger ... That's it. I'm very sorry for wasting your time *eyesroll* – Nils-o-mat Mar 24 '21 at 12:15
  • @Holger If you like, post an answer, so I can accept it. – Nils-o-mat Mar 24 '21 at 12:15

1 Answers1

3

There’s a mismatch between the requested class name SchemasEnumBaseNameReqProp1 and the name stored in the class file SchemasEnumBasenameReqProp1.

Since this difference is only in the case of the N character, the attempt to load the class file succeeded due to the use of a case insensitive filesystem. But when trying to define an actual runtime class for the file, the JVM detects a name mismatch and throws an error.

Holger
  • 285,553
  • 42
  • 434
  • 765