equalsverifier
equalsverifier copied to clipboard
Class not found error when using equalsverifier in a Java module
Describe the bug
The error occurs when a project does not declare requires java.sql
in its module-info.java
.
equalsverifier references classes from that package leading to the error.
To Reproduce Minimal sample: https://github.com/armandino/equalsverifier-java9-module-bug
Error message
Exception in thread "main" java.lang.AssertionError: EqualsVerifier found a problem in class org.example.domain.Person.
-> java/sql/Date
For more information, go to: https://www.jqno.nl/equalsverifier/errormessages
(EqualsVerifier null, JDK 19.0.1 on Linux)
at [email protected]/nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.verify(SingleTypeEqualsVerifierApi.java:316)
at user/org.example.domain.Person.main(Person.java:23)
Caused by: java.lang.NoClassDefFoundError: java/sql/Date
at [email protected]/nl.jqno.equalsverifier.internal.prefabvalues.JavaApiPrefabValues.addUncommonClasses(JavaApiPrefabValues.java:327)
at [email protected]/nl.jqno.equalsverifier.internal.prefabvalues.JavaApiPrefabValues.addJavaClasses(JavaApiPrefabValues.java:103)
at [email protected]/nl.jqno.equalsverifier.internal.prefabvalues.JavaApiPrefabValues.build(JavaApiPrefabValues.java:95)
at [email protected]/nl.jqno.equalsverifier.internal.util.Configuration.build(Configuration.java:87)
at [email protected]/nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.buildConfig(SingleTypeEqualsVerifierApi.java:389)
at [email protected]/nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.performVerification(SingleTypeEqualsVerifierApi.java:375)
at [email protected]/nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.verify(SingleTypeEqualsVerifierApi.java:312)
... 1 more
Caused by: java.lang.ClassNotFoundException: java.sql.Date
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 8 more
Expected behavior
If an application does not require java.sql
equalsverifier should still work.
Version 3.12.3 (no deps)
Additional context
Error occurs in JavaApiPrefabValues
that imports java.sql.*
classes. Loading them via Class.forName
might be a potential solution (had to do something similar myself in another project).
Thanks for letting me know, and for creating the sample project. I was able to reproduce this quite quickly.
I agree with you that things should just work when using modules. Indeed, I can load the java.sql
classes using reflection, as you suggest. The subsequent error (ClassNotFoundException sun.reflect.ReflectionFactory) is a bit harder, I'll have to research that some more.
Out of curiosity, are you running your unit tests on the module path? If so, how do you do that?
I don't use modules myself and my experience with them is limited. However I'm also working on a testing library and I'd like it to support modules for users who do use them. I discovered this issue in my project. I then tested it with equalsverifier as it also supports a lot of java types, and this has led to this bug report.
Ideally, I'd like to have a Maven test module (that is also a Java module) to verify the library works properly on the module path. However I haven't been able to get that to work in a multi-module Maven build as I'm using Automatic-Module-Name
s. I think this would require adding module-info.java
files but I could be wrong...
Yeah, I'd like to have some kind of test for this as well that I can run from Maven. Perhaps I could add a proper module-info.java
. But first let's see if I can find a solution for this bug. Please let me know if you figure something out though :wink:
@sormuras has a nice blog post on this subject.
Thanks, I think I've read that when I first looked into modules... will take another look when I pick this up.
Happy to help, if needed.
@jqno I just noticed that if you run the sample from the command line, the error does not occur. It works even without requires java.sql
:
mvn package exec:java -Dexec.mainClass=org.example.domain.Person
Regarding the second error (ClassNotFoundException sun.reflect.ReflectionFactory
), I was able to reproduce it without equalsverifier by using Objenesis directly:
Objenesis objenesis = new ObjenesisStd();
ObjectInstantiator<Person> result = objenesis.getInstantiatorOf(Person.class);
This also works using mvn exec and fails in IntelliJ. I created a separate sample to submit an Objenesis bug report, but at this point, I'm not even sure which behaviour is correct, IntelliJ or mvn exec.
I noticed that too, I think the Maven exec plugin just puts everything on the classpath by default instead of on the modulepath, and that IntelliJ is a bit smarter about this. But I'll have to investigate more :)
Did you submit something with Objenesis? If so, can you share the link? I'd like to follow that as well!
Just did. GitHub is ahead of me :)
I was going though old tickets, and tried this one again. It's still blocked on the Objenesis issue, unfortunately. I've added a label to make this clearer at a glance from the issues list.
@jqno I ended up removing it as a dependency and using Unsafe
and ReflectionFactory
directly, instead of using Objenesis. It was a fairly small change, and the benefit is that it allows checking if sun.*
classes are available at runtime and avoid using them if not. This also removes one transitive dependency for users.
Not sure if this approach will work for EV. It could be a breaking change since Objenesis provides some additional strategies for instantiating classes, however those could be handled as they arise. Might be worth investigating.
Thanks for the response. That could be a good workaround for individual cases, but it's not something I'd like to include in EqualsVerifier itself. Good to know about this option though, so I can refer others to it if they run into this.
I think I'm getting similar issue. It's not java.sql
however When I'm trying to test object that inherits from Throwable:
java.lang.AssertionError: EqualsVerifier found a problem in class church.i18n.processing.exception.ProcessingException.
-> Unable to make field private transient java.lang.Object java.lang.Throwable.backtrace accessible: module java.base does not "opens java.lang" to unnamed module @55fe41ea
I'm trying to migrate into Java17. The project it OpenSource: https://bitbucket.org/i18n_church/church.i18n.processing.exception
That's actually a different issue. OP is making their own modules, while you're extending a class form an existing, JDK-internal module.
Unfortunately, in this case you'll have to adjust your build scripts to actually open the java.lang
module. Here's a StackOverflow answer that should get you on your way: https://stackoverflow.com/questions/74006627/module-java-base-does-not-opens-java-lang-java-17-0-4-1