ClassNotFoundException when run from sbt

477 views
Skip to first unread message

Steven Soloff

unread,
Aug 5, 2015, 3:19:59 AM8/5/15
to equalsverifier
Hi Jan,

I recently started using EqualsVerifier in a Scala project and ran into a problem that took me about 4 hours to work around.

When applying EqualsVerifier to a class that has a function field, I received an exception chain with the root cause:

    java.lang.ClassNotFoundException: nl.jqno.equalsverifier.internal.cglib.proxy.Factory

when running my tests from sbt.  Below is an MCVE that reproduces the exception:

// build.sbt
    name := "equalsverifier-test"
    version
:= "1.0"
    scalaVersion
:= "2.11.7"

    libraryDependencies
+= "nl.jqno.equalsverifier" % "equalsverifier" % "1.7.3"

// src/main/scala/Foo.scala
    import nl.jqno.equalsverifier._

   
final class Foo(val value: Double, val f: () => Double) {
     
def apply(): Double = value * f()

     
override def equals(other: Any): Boolean = other match {
       
case that: Foo => value.compareTo(that.value) == 0 && f == that.f
       
case _ => false
     
}

     
override def hashCode(): Int = {
       
var hashCode = 17
        hashCode
= 31 * hashCode + value.hashCode
        hashCode
= 31 * hashCode + f.hashCode
        hashCode
     
}
   
}

   
object Foo {
     
def main(args: Array[String]): Unit = {
       
EqualsVerifier.forClass(classOf[Foo])
         
.suppress(Warning.NULL_FIELDS)
         
.verify()
        println
("OK")
     
}
   
}

Running "sbt run" for the above project produces an exception with the following stack trace:

    java.lang.AssertionError: nl.jqno.equalsverifier.internal.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
    For more information, go to: http://www.jqno.nl/equalsverifier/errormessages
        at nl.jqno.equalsverifier.EqualsVerifier.handleError(EqualsVerifier.java:406)
        at nl.jqno.equalsverifier.EqualsVerifier.verify(EqualsVerifier.java:392)
        at Foo$.main(Foo.scala:23)
        at Foo.main(Foo.scala)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
    Caused by: nl.jqno.equalsverifier.internal.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
        at nl.jqno.equalsverifier.internal.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:237)
        at nl.jqno.equalsverifier.internal.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
        at nl.jqno.equalsverifier.internal.cglib.proxy.Enhancer.createClass(Enhancer.java:317)
        at nl.jqno.equalsverifier.util.Instantiator.createDynamicSubclass(Instantiator.java:103)
        at nl.jqno.equalsverifier.util.Instantiator.of(Instantiator.java:49)
        at nl.jqno.equalsverifier.util.ClassAccessor.<init>(ClassAccessor.java:63)
        at nl.jqno.equalsverifier.util.ClassAccessor.of(ClassAccessor.java:55)
        at nl.jqno.equalsverifier.util.PrefabValues.createAndPutInstances(PrefabValues.java:242)
        at nl.jqno.equalsverifier.util.PrefabValues.putFor(PrefabValues.java:197)
        at nl.jqno.equalsverifier.util.PrefabValues.putFor(PrefabValues.java:173)
        at nl.jqno.equalsverifier.util.FieldAccessor.createPrefabValues(FieldAccessor.java:194)
        at nl.jqno.equalsverifier.util.FieldAccessor.access$300(FieldAccessor.java:28)
        at nl.jqno.equalsverifier.util.FieldAccessor$FieldChanger.modify(FieldAccessor.java:276)
        at nl.jqno.equalsverifier.util.FieldAccessor.modify(FieldAccessor.java:163)
        at nl.jqno.equalsverifier.util.FieldAccessor.changeField(FieldAccessor.java:153)
        at nl.jqno.equalsverifier.util.ObjectAccessor.scramble(ObjectAccessor.java:153)
        at nl.jqno.equalsverifier.util.ClassAccessor.getRedAccessor(ClassAccessor.java:262)
        at nl.jqno.equalsverifier.util.ClassAccessor.getRedObject(ClassAccessor.java:252)
        at nl.jqno.equalsverifier.AbstractDelegationChecker.check(AbstractDelegationChecker.java:49)
        at nl.jqno.equalsverifier.EqualsVerifier.verifyWithoutExamples(EqualsVerifier.java:428)
        at nl.jqno.equalsverifier.EqualsVerifier.performVerification(EqualsVerifier.java:416)
        at nl.jqno.equalsverifier.EqualsVerifier.verify(EqualsVerifier.java:386)
        at Foo$.main(Foo.scala:23)
        at Foo.main(Foo.scala)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
    Caused by: java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at nl.jqno.equalsverifier.internal.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:384)
        at nl.jqno.equalsverifier.internal.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:219)
        at nl.jqno.equalsverifier.internal.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
        at nl.jqno.equalsverifier.internal.cglib.proxy.Enhancer.createClass(Enhancer.java:317)
        at nl.jqno.equalsverifier.util.Instantiator.createDynamicSubclass(Instantiator.java:103)
        at nl.jqno.equalsverifier.util.Instantiator.of(Instantiator.java:49)
        at nl.jqno.equalsverifier.util.ClassAccessor.<init>(ClassAccessor.java:63)
        at nl.jqno.equalsverifier.util.ClassAccessor.of(ClassAccessor.java:55)
        at nl.jqno.equalsverifier.util.PrefabValues.createAndPutInstances(PrefabValues.java:242)
        at nl.jqno.equalsverifier.util.PrefabValues.putFor(PrefabValues.java:197)
        at nl.jqno.equalsverifier.util.PrefabValues.putFor(PrefabValues.java:173)
        at nl.jqno.equalsverifier.util.FieldAccessor.createPrefabValues(FieldAccessor.java:194)
        at nl.jqno.equalsverifier.util.FieldAccessor.access$300(FieldAccessor.java:28)
        at nl.jqno.equalsverifier.util.FieldAccessor$FieldChanger.modify(FieldAccessor.java:276)
        at nl.jqno.equalsverifier.util.FieldAccessor.modify(FieldAccessor.java:163)
        at nl.jqno.equalsverifier.util.FieldAccessor.changeField(FieldAccessor.java:153)
        at nl.jqno.equalsverifier.util.ObjectAccessor.scramble(ObjectAccessor.java:153)
        at nl.jqno.equalsverifier.util.ClassAccessor.getRedAccessor(ClassAccessor.java:262)
        at nl.jqno.equalsverifier.util.ClassAccessor.getRedObject(ClassAccessor.java:252)
        at nl.jqno.equalsverifier.AbstractDelegationChecker.check(AbstractDelegationChecker.java:49)
        at nl.jqno.equalsverifier.EqualsVerifier.verifyWithoutExamples(EqualsVerifier.java:428)
        at nl.jqno.equalsverifier.EqualsVerifier.performVerification(EqualsVerifier.java:416)
        at nl.jqno.equalsverifier.EqualsVerifier.verify(EqualsVerifier.java:386)
        at Foo$.main(Foo.scala:23)
        at Foo.main(Foo.scala)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
    Caused by: java.lang.NoClassDefFoundError: nl/jqno/equalsverifier/internal/cglib/proxy/Factory
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at nl.jqno.equalsverifier.internal.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:384)
        at nl.jqno.equalsverifier.internal.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:219)
        at nl.jqno.equalsverifier.internal.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
        at nl.jqno.equalsverifier.internal.cglib.proxy.Enhancer.createClass(Enhancer.java:317)
        at nl.jqno.equalsverifier.util.Instantiator.createDynamicSubclass(Instantiator.java:103)
        at nl.jqno.equalsverifier.util.Instantiator.of(Instantiator.java:49)
        at nl.jqno.equalsverifier.util.ClassAccessor.<init>(ClassAccessor.java:63)
        at nl.jqno.equalsverifier.util.ClassAccessor.of(ClassAccessor.java:55)
        at nl.jqno.equalsverifier.util.PrefabValues.createAndPutInstances(PrefabValues.java:242)
        at nl.jqno.equalsverifier.util.PrefabValues.putFor(PrefabValues.java:197)
        at nl.jqno.equalsverifier.util.PrefabValues.putFor(PrefabValues.java:173)
        at nl.jqno.equalsverifier.util.FieldAccessor.createPrefabValues(FieldAccessor.java:194)
        at nl.jqno.equalsverifier.util.FieldAccessor.access$300(FieldAccessor.java:28)
        at nl.jqno.equalsverifier.util.FieldAccessor$FieldChanger.modify(FieldAccessor.java:276)
        at nl.jqno.equalsverifier.util.FieldAccessor.modify(FieldAccessor.java:163)
        at nl.jqno.equalsverifier.util.FieldAccessor.changeField(FieldAccessor.java:153)
        at nl.jqno.equalsverifier.util.ObjectAccessor.scramble(ObjectAccessor.java:153)
        at nl.jqno.equalsverifier.util.ClassAccessor.getRedAccessor(ClassAccessor.java:262)
        at nl.jqno.equalsverifier.util.ClassAccessor.getRedObject(ClassAccessor.java:252)
        at nl.jqno.equalsverifier.AbstractDelegationChecker.check(AbstractDelegationChecker.java:49)
        at nl.jqno.equalsverifier.EqualsVerifier.verifyWithoutExamples(EqualsVerifier.java:428)
        at nl.jqno.equalsverifier.EqualsVerifier.performVerification(EqualsVerifier.java:416)
        at nl.jqno.equalsverifier.EqualsVerifier.verify(EqualsVerifier.java:386)
        at Foo$.main(Foo.scala:23)
        at Foo.main(Foo.scala)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
    Caused by: java.lang.ClassNotFoundException: nl.jqno.equalsverifier.internal.cglib.proxy.Factory
        at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at nl.jqno.equalsverifier.internal.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:384)
        at nl.jqno.equalsverifier.internal.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:219)
        at nl.jqno.equalsverifier.internal.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
        at nl.jqno.equalsverifier.internal.cglib.proxy.Enhancer.createClass(Enhancer.java:317)
        at nl.jqno.equalsverifier.util.Instantiator.createDynamicSubclass(Instantiator.java:103)
        at nl.jqno.equalsverifier.util.Instantiator.of(Instantiator.java:49)
        at nl.jqno.equalsverifier.util.ClassAccessor.<init>(ClassAccessor.java:63)
        at nl.jqno.equalsverifier.util.ClassAccessor.of(ClassAccessor.java:55)
        at nl.jqno.equalsverifier.util.PrefabValues.createAndPutInstances(PrefabValues.java:242)
        at nl.jqno.equalsverifier.util.PrefabValues.putFor(PrefabValues.java:197)
        at nl.jqno.equalsverifier.util.PrefabValues.putFor(PrefabValues.java:173)
        at nl.jqno.equalsverifier.util.FieldAccessor.createPrefabValues(FieldAccessor.java:194)
        at nl.jqno.equalsverifier.util.FieldAccessor.access$300(FieldAccessor.java:28)
        at nl.jqno.equalsverifier.util.FieldAccessor$FieldChanger.modify(FieldAccessor.java:276)
        at nl.jqno.equalsverifier.util.FieldAccessor.modify(FieldAccessor.java:163)
        at nl.jqno.equalsverifier.util.FieldAccessor.changeField(FieldAccessor.java:153)
        at nl.jqno.equalsverifier.util.ObjectAccessor.scramble(ObjectAccessor.java:153)
        at nl.jqno.equalsverifier.util.ClassAccessor.getRedAccessor(ClassAccessor.java:262)
        at nl.jqno.equalsverifier.util.ClassAccessor.getRedObject(ClassAccessor.java:252)
        at nl.jqno.equalsverifier.AbstractDelegationChecker.check(AbstractDelegationChecker.java:49)
        at nl.jqno.equalsverifier.EqualsVerifier.verifyWithoutExamples(EqualsVerifier.java:428)
        at nl.jqno.equalsverifier.EqualsVerifier.performVerification(EqualsVerifier.java:416)
        at nl.jqno.equalsverifier.EqualsVerifier.verify(EqualsVerifier.java:386)
        at Foo$.main(Foo.scala:23)
        at Foo.main(Foo.scala)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)


If I remove field f from class Foo, the code runs without issue.

Thankfully, I came across a commit message in the Netflix atlas project [1] that identified a similar problem.  The workaround identified in that commit is to ensure the code using EqualsVerifier is run in a separate JVM.  Sure enough, adding the following line to build.sbt allows the code to run as expected:

    fork in run := true

I don't know if this is an issue with EqualsVerifier, cglib, or sbt (I didn't try to debug it further to identify the root cause), but I wanted to bring it to your attention, just in case.  Also, hopefully the presence of this thread will make it easier for the next person who may run into the same problem

Thanks,
Steve

[1] https://github.com/Netflix/atlas/commit/0183c43ed232adcba08157ed104511019ad960b8

Jan Ouwens

unread,
Aug 5, 2015, 3:26:28 AM8/5/15
to equalsverifier
Hi Steven,

Wow, thanks for the incredibly detailed post!
I don't have time to look into this right now, but I will probably do so during the weekend. I'll let you know what I find out; if it's something I can fix in EqualsVerifier, I will certainly do so.


Regards,
Jan


--
You received this message because you are subscribed to the Google Groups "equalsverifier" group.
To unsubscribe from this group and stop receiving emails from it, send an email to equalsverifie...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Michel Jung

unread,
Aug 6, 2015, 1:09:52 AM8/6/15
to equalsverifier
Same exception when using JavaFX properties:

import javafx.beans.property.StringProperty;

public class TestObject {

  private StringProperty name;
}

Jan Ouwens

unread,
Aug 8, 2015, 8:25:18 AM8/8/15
to equalsverifier
Hi Steven, Michel,

I've reproduced this issue. It's a strange one! I can't figure out the root cause of this, but I did find a few ways to avoid the problem.

* Somehow, it only seems to occur when run from SBT. Running the same test from IntelliJ does seem to work. Also, I've created a Maven pom to replace the SBT script, and then the error also doesn't occur. But you obviously shouldn't do that on a real project, just to make EqualsVerifier work.

* The problem occurs when EqualsVerifier creates an instance of an Instantiator (an internal EqualsVerifier class) for the Function0 (an interface) or the StringProperty (an abstract class). Currently, EqualsVerifier creates such an instance, even if it doesn't need it. I've created a GitHub issue[1] to refactor it so that the object is only instantiated when actually used. That way, you can add a prefab value for the offending type (i.e., the Function0 or the StringProperty), and the problem won't occur. I'll do that for the next release of EqualsVerifier, which I should be able to release soon.

* Building on that, I can add StringProperty and the other JavaFX properties to EqualsVerifier's internal list of prefab values. That way, you won't have to explicitly add those anymore. I've made an issue for that as well[2], which will also be in the next release. Unfortunately, I can't do it for Function0, because that's not a type that exists in the Java APIs.

* I've been experimenting with replacing CGLib with ByteBuddy, which seems like a good thing to do because CGLib hasn't been updated since before Java 8 came out. It turns out that that also solves the issue; no more Function0 prefab values necessary. Given that that has a much higher impact, it won't be in the next release, though. I've started working on a version 2.0 of EqualsVerifier, and that's where this will happen. Obviously I can't give a good time frame for that, though: it will be released when it's released :).

I will let you know when the next release is out.


I hope this helps.

Jan


Steven Soloff

unread,
Aug 8, 2015, 10:40:22 PM8/8/15
to equalsverifier
Hi Jan,

Thanks for spending quality time looking into this.

Your second finding makes sense.  My first attempt to work around the exception was to provide prefab values for Function0.  Now I know why it didn't work. :-)

Looking forward to the improvements, and thanks for this great tool!

Regards,
Steve

Steven Soloff

unread,
Aug 18, 2015, 1:18:01 PM8/18/15
to equalsverifier
I just upgraded to 1.7.4 and confirmed that by adding prefab values for Function0 and removing fork in run := true, the above MCVE runs without error.  Thanks, Jan!



On Saturday, August 8, 2015 at 8:25:18 AM UTC-4, Jan Ouwens wrote:

Jan Ouwens

unread,
Aug 19, 2015, 3:28:04 AM8/19/15
to equalsverifier
Hi Steven,

Thanks for trying it out and letting me know! Glad to hear it works well :).


Jan

Reply all
Reply to author
Forward
0 new messages