kostaskougios / cloning

deep clone java objects
Other
589 stars 111 forks source link

ClassNotFoundException when cloning JavaFX's SimpleListProperty #45

Open sidola opened 8 years ago

sidola commented 8 years ago

I'm trying to clone a class I have that uses the SimpleListProperty class from JavaFX 8. But for some reason it's not playing along. I tried finding the cause my self but I kind of got lost in all the cloning logic.

Can you think of any reason why this would fail?

public class CloneTest {

    public static void main(String[] args) {
        Cloner cloner = new Cloner();

        Data myData = new Data();
        myData.slp.add("Hello");

        SimpleListProperty<Object> simpleListProp = new SimpleListProperty<>();
        simpleListProp.set(myData.slp);

        // Exception here
        Data clonedData = cloner.deepClone(myData);

        myData.slp.set(0, "World");

        System.out.println(myData.slp.get(0));
        System.out.println(clonedData.slp.get(0));
    }

    public static class Data {
        SimpleListProperty<Object> slp = new SimpleListProperty<>(FXCollections.observableArrayList());
    }

}
kostaskougios commented 8 years ago

Hi, do you get an exception or the cloned object is not correct?

On 09/11/15 20:14, Sid wrote:

I'm trying to clone a class I have that uses the |SimpleListProperty| https://docs.oracle.com/javase/8/javafx/api/javafx/beans/property/SimpleListProperty.html class from JavaFX 8. But for some reason it's not playing along. I tried finding the cause my self but I kind of got lost in all the cloning logic.

Can you think of any reason why this would fail?

|public class CloneTest { public static void main(String[] args) { Cloner cloner = new Cloner(); Data myData = new Data(); myData.slp.add("Hello"); SimpleListProperty simpleListProp = new SimpleListProperty<>(); simpleListProp.set(myData.slp); // Exception here Data clonedData = cloner.deepClone(myData); myData.slp.set(0, "World"); System.out.println(myData.slp.get(0)); System.out.println(clonedData.slp.get(0)); } public static class Data { SimpleListProperty slp = new SimpleListProperty<>(FXCollections.observableArrayList()); } } |

— Reply to this email directly or view it on GitHub https://github.com/kostaskougios/cloning/issues/45.

sidola commented 8 years ago

I get an exception, the clone is never returned. Here's the exact exception for the code above, forgot to include it:

Exception in thread "main" java.lang.NoClassDefFoundError: javafx/beans/property/ListPropertyBase$$Lambda$1/109961541
    at sun.reflect.GeneratedSerializationConstructorAccessor3.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:45)
    at org.objenesis.ObjenesisBase.newInstance(ObjenesisBase.java:73)
    at com.rits.cloning.ObjenesisInstantiationStrategy.newInstance(ObjenesisInstantiationStrategy.java:18)
    at com.rits.cloning.Cloner.newInstance(Cloner.java:271)
    at com.rits.cloning.Cloner.cloneObject(Cloner.java:436)
    at com.rits.cloning.Cloner.cloneInternal(Cloner.java:431)
    at com.rits.cloning.Cloner.cloneObject(Cloner.java:453)
    at com.rits.cloning.Cloner.cloneInternal(Cloner.java:431)
    at com.rits.cloning.Cloner.cloneObject(Cloner.java:453)
    at com.rits.cloning.Cloner.cloneInternal(Cloner.java:431)
    at com.rits.cloning.Cloner.deepClone(Cloner.java:301)
    at poc.CloneTest.main(CloneTest.java:20)
Caused by: java.lang.ClassNotFoundException: javafx.beans.property.ListPropertyBase$$Lambda$1.109961541
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 14 more
paraplexed commented 8 years ago

I was having this same issue, and found it was happening with any lambdas I had in my code. Converting the lambdas to regular java fixed the issue for me temporarily (though this won't help if you don't have access to the source code using the lambdas). I was able to reliably reproduce it with the following code:

package asdf;

import com.rits.cloning.Cloner;
import org.junit.Test;

public class dev {
    @Test
    public void run() {
        dev2 test = new dev2();
        Cloner cloner = new Cloner();

        cloner.deepClone(test);
    }
}

class dev2 {
    dev3 test;

    dev2() {
        test = new dev3();
    }

}
class dev3 {
    Runnable r1;

    dev3() {
        r1 = () -> System.out.println("Hello world!");
    }
}

Here's the stacktrace/error I got back:

java.lang.NoClassDefFoundError: asdf/dev3$$Lambda$1/1509514333

    at sun.reflect.GeneratedSerializationConstructorAccessor4.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:45)
    at org.objenesis.ObjenesisBase.newInstance(ObjenesisBase.java:73)
    at com.rits.cloning.ObjenesisInstantiationStrategy.newInstance(ObjenesisInstantiationStrategy.java:18)
    at com.rits.cloning.Cloner.newInstance(Cloner.java:271)
    at com.rits.cloning.Cloner.cloneObject(Cloner.java:436)
    at com.rits.cloning.Cloner.cloneInternal(Cloner.java:431)
    at com.rits.cloning.Cloner.cloneObject(Cloner.java:453)
    at com.rits.cloning.Cloner.cloneInternal(Cloner.java:431)
    at com.rits.cloning.Cloner.cloneObject(Cloner.java:453)
    at com.rits.cloning.Cloner.cloneInternal(Cloner.java:431)
    at com.rits.cloning.Cloner.deepClone(Cloner.java:301)
    at asdf.dev.run(dev.java:12)
    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 org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:119)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
    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 com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.ClassNotFoundException: asdf.dev3$$Lambda$1.1509514333
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 41 more

I think it has something to do with assigning the results of the lambdas to r1 and the object creation to test. If you move the scope of those variables to locally in those blocks, it will pass, ie:

package asdf;

import com.rits.cloning.Cloner;
import org.junit.Test;

public class dev {
    @Test
    public void run() {
        dev2 test = new dev2();
        Cloner cloner = new Cloner();

        cloner.deepClone(test);
    }
}

class dev2 {
    //dev3 test;

    dev2() {
        dev3 test = new dev3();
    }

}
class dev3 {
    //Runnable r1;

    dev3() {
        Runnable r1 = () -> System.out.println("Hello world!");
    }
}

Do that in either dev2 or dev3 in this example individually fixes the problem, it only happens when both are assigning variables in that scope.

Unfortunately this is about as far as I got with trying to find the exact problem. Let me know if there's anything more I can do to help debug this further. For the time being I'm going to refactor our lambdas to get a fix in.

exal99 commented 7 years ago

This problem seems to still be an because when I try to clone my own object I get NoClassDefFoundError and the cause is the same: "java.lang.NoClassDefFoundError: javax/swing/plaf/metal/MetalLookAndFeel$$Lambda$9/363771819" all tough I cannot change swings code. Is there no solution to this? Dose this mean that I cant use Cloning for my project?

kostaskougios commented 7 years ago

It seems this is due to the lambda class names contain a /. i.e. com.rits.tests.cloning.Jdk8Class2$$Lambda$1/836514715 Then the classloader replaces / with a dot and tries to load com.rits.tests.cloning.Jdk8Class2$$Lambda$1.836514715 which is not valid. So far I didn't find a work around as the problem is not part of my code.

Petikoch commented 7 years ago

Also suffering because of this. Just tried to use objenesis 2.5 instead of 2.1, same problem...

PS: The current cloning release 1.9.3 uses objenesis 2.1 from 2013-10-10 (http://objenesis.org/notes.html).

Petikoch commented 7 years ago

It's probably in general not a good idea to deep-clone a SimpleListProperty instance, since it can have listeners. Deep-cloning will also clone the listeners, which is probably unexpected,

mikefeldmangh commented 6 years ago

I think we are also having this same problem. We are migrating our application to Java 8 and WildFly 10. I'm surprised nobody has commented on this since January. Is there a solution I missed? Is the actual problem in Objenesis? I'm using the latest Cloner which uses Objenesis 2.5.1. Could it be fixed in 2.6? I'm going to see if I can try that but if anyone has already, you could save me the time. I'm not really sure what else we can do. We had other strategies in our code but I think they were too slow.

mikefeldmangh commented 6 years ago

I downloaded the cloning library, changed the objenesis library version to 2.6. I thought it seemed to fix the problem at first but I was wrong.

kostaskougios commented 6 years ago

I just published 1.9.6 with objenesis 2.6. But Mike above says it wouldn't fix the problem.

deedeky commented 6 years ago

yes, problem is still there with 1.9.6

mikefeldmangh commented 6 years ago

Has anyone tried creating an issue against the objenesis library? https://github.com/easymock/objenesis/issues There are no open issues at all. I haven't found a good solution around this yet. Some of the lambdas causing me problems are within hibernate so I can't remove those.

mikefeldmangh commented 6 years ago

@kostaskougios can you open a ticket against Objenesis if that is where the problem is? I could do it but you could probably explain the problem in a ticket a lot better.

kostaskougios commented 6 years ago

I don't think it is an objenesis problem, rather a jdk issue regarding lambdas:

It seems this is due to the lambda class names contain a /. i.e. com.rits.tests.cloning.Jdk8Class2$$Lambda$1/836514715 Then the classloader replaces / with a dot and tries to load com.rits.tests.cloning.Jdk8Class2$$Lambda$1.836514715 which is not valid. So far I didn't find a work around as the problem is not part of my code.

mikefeldmangh commented 6 years ago

@kostaskougios , sorry I misunderstood. I thought you meant the objenesis project code could handle that in a better way. Thanks.

lvyahui8 commented 5 years ago

Triggered NoClassDefFoundError/ClassNotFoundException

Test Code
package org.lyh.testcode4java.lambda;

import java.time.temporal.Temporal;
import java.util.function.ToLongFunction;

/**
 * @author  lvyahui8@gmail.com
 * @date 2018/12/26
 */
public class ParentClass<T extends Temporal> {
    protected ToLongFunction<T> getEpochMillis;

    public ParentClass(ToLongFunction<T> getEpochMillis) {
        this.getEpochMillis = getEpochMillis;
    }
}
package org.lyh.testcode4java.lambda;

import com.google.common.base.Function;

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

/**
 * @author  lvyahui8@gmail.com
 * @date 2018/12/26
 */
public class ChildClass extends ParentClass<ZonedDateTime> {
    public ChildClass(DateTimeFormatter dateTimeFormatter) {
        super(dt -> dt.toInstant().toEpochMilli());
        Function<ZonedDateTime, Integer> getNano = ZonedDateTime::getNano;
        Function<ZonedDateTime, Long> toEpochSecond = ZonedDateTime::toEpochSecond;
    }
}
package org.lyh.testcode4java.lambda;

import com.rits.cloning.Cloner;

import java.time.format.DateTimeFormatter;

/**
 * @author lvyahui8@gmail.com
 * @date 2018/12/26
 */
public class TestMain {
    public static void main(String[] args) {
        Cloner cloner = new Cloner();
        ChildClass childClass = new ChildClass(DateTimeFormatter.ISO_DATE_TIME);
        ChildClass childClass1 = cloner.deepClone(childClass);
    }
}
<dependency>
    <groupId>uk.com.robust-it</groupId>
    <artifactId>cloning</artifactId>
    <version>1.9.6</version>
</dependency>
Output :
[Loaded org.lyh.testcode4java.lambda.ParentClass from file:/Users/figo/work/test-code4java/target/classes/]
[Loaded org.lyh.testcode4java.lambda.ChildClass from file:/Users/figo/work/test-code4java/target/classes/]
[Loaded org.lyh.testcode4java.lambda.ChildClass$$Lambda$7/1789447862 from org.lyh.testcode4java.lambda.ChildClass]
[Loaded org.lyh.testcode4java.lambda.ChildClass$$Lambda$8/1289696681 from org.lyh.testcode4java.lambda.ChildClass]
[Loaded org.lyh.testcode4java.lambda.ChildClass$$Lambda$9/1607460018 from org.lyh.testcode4java.lambda.ChildClass]
Exception in thread "main" java.lang.NoClassDefFoundError: org/lyh/testcode4java/lambda/ChildClass$$Lambda$7/1789447862
    at org.lyh.testcode4java.lambda.TestMain.main(TestMain.java:15)
Caused by: java.lang.ClassNotFoundException: org.lyh.testcode4java.lambda.ChildClass$$Lambda$7.1789447862

ChildClass$$Lambda$7/1789447862 --> ChildClass$$Lambda$7.1789447862

I saw the '/' in the Class name was replaced with '.', but I didn't find the replacement action in the code for Cloning. Is it a JDK bug?

ZhenXiongQian commented 3 years ago

I think this issue is fixed by Objenesis (the version I use is 3.0.1). this is my test code:

public class LambdaTest {
        public ArrayList<Integer> list;

        public LambdaTest() {
            list = new ArrayList<>();
            list.add(1);
            list.add(2);
            list.add(3);
            list.add(4);
        }

        public void show() {
            list.stream()
                    .filter(i -> i % 2 == 0)
                    .forEach(System.out::println);
        }
    }

    public void testLambda() {
        LambdaTest l = new LambdaTest();
        LambdaTest f = cloner.deepClone(l);
        f.show();
    }

and output:

clone>class com.rits.tests.cloning.TestJia$LambdaTest
clone>class com.rits.tests.cloning.TestJia$LambdaTest
cloned field>public java.util.ArrayList com.rits.tests.cloning.TestJia$LambdaTest.list  -- of class class com.rits.tests.cloning.TestJia$LambdaTest
clone>class com.rits.tests.cloning.TestJia
cloned field>private final com.rits.cloning.Cloner com.rits.tests.cloning.TestJia.cloner  -- of class class com.rits.tests.cloning.TestJia
cloned field>final com.rits.tests.cloning.TestJia com.rits.tests.cloning.TestJia$LambdaTest.this$0  -- of class class com.rits.tests.cloning.TestJia$LambdaTest
2
4
kostaskougios commented 3 years ago

@ZhenXiongQian your code doesn't clone any lambda.

I tried v3.2 of objenesis with this test:

    public void testLambda() {
        Function<ZonedDateTime, Integer> f = ZonedDateTime::getNano;
        cloner.deepClone(f);
    }

But still fails with

Caused by: java.lang.ClassNotFoundException: com.rits.tests.cloning.TestCloner$$Lambda$54.0x0000000800c24210
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:636)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:182)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:519)
ZhenXiongQian commented 3 years ago

@kostaskougios ,sorry my code is a wrong example. And I try tu run your lest,. There is a question, why I got a java.lang.NoClassDefFoundError with the same code that you got java.lang.ClassNotFoundException.

java.lang.NoClassDefFoundError: com/rits/tests/cloning/TestJia$$Lambda$42/0x0000000800bc97f0

    at jdk.internal.reflect.GeneratedSerializationConstructorAccessor2.newInstance(Unknown Source)
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
    at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:48)
    at com.rits.cloning.Cloner$CloneObjectCloner.deepClone(Cloner.java:627)
    at com.rits.cloning.Cloner.cloneInternal(Cloner.java:454)
    at com.rits.cloning.Cloner.deepClone(Cloner.java:345)
    at com.rits.tests.cloning.TestJia.testLambda2(TestJia.java:104)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at junit.framework.TestCase.runTest(TestCase.java:177)
    at junit.framework.TestCase.runBare(TestCase.java:142)
    at junit.framework.TestResult$1.protect(TestResult.java:122)
    at junit.framework.TestResult.runProtected(TestResult.java:142)
    at junit.framework.TestResult.run(TestResult.java:125)
    at junit.framework.TestCase.run(TestCase.java:130)
    at junit.framework.TestSuite.runTest(TestSuite.java:241)
    at junit.framework.TestSuite.run(TestSuite.java:236)
    at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:90)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
ZhenXiongQian commented 3 years ago

@kostaskougios I am confused now. I use your test code in an new empty project and get the clone library by maven. quicker_2021-07-24_15-01-52 quicker_2021-07-24_15-02-25 quicker_2021-07-24_15-03-00

kostaskougios commented 3 years ago

Just to clarify @ZhenXiongQian , the full exception stacktrace:

java.lang.NoClassDefFoundError: com/rits/tests/cloning/TestCloner$$Lambda$46/0x0000000800c11230

    at jdk.internal.reflect.GeneratedSerializationConstructorAccessor2.newInstance(Unknown Source)
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
    at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:48)
    at com.rits.cloning.Cloner$CloneObjectCloner.deepClone(Cloner.java:622)
    at com.rits.cloning.Cloner.cloneInternal(Cloner.java:449)
    at com.rits.cloning.Cloner.deepClone(Cloner.java:340)
    at com.rits.tests.cloning.TestCloner.testLambda(TestCloner.java:924)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at junit.framework.TestCase.runTest(TestCase.java:177)
    at junit.framework.TestCase.runBare(TestCase.java:142)
    at junit.framework.TestResult$1.protect(TestResult.java:122)
    at junit.framework.TestResult.runProtected(TestResult.java:142)
    at junit.framework.TestResult.run(TestResult.java:125)
    at junit.framework.TestCase.run(TestCase.java:130)
    at junit.framework.TestSuite.runTest(TestSuite.java:241)
    at junit.framework.TestSuite.run(TestSuite.java:236)
    at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:90)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.lang.ClassNotFoundException: com.rits.tests.cloning.TestCloner$$Lambda$46.0x0000000800c11230
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:636)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:182)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:519)
    ... 26 more

Process finished with exit code 255

So NoClassDefFoundError which was caused by ClassNotFoundException

ZhenXiongQian commented 3 years ago

why it is not report error in the case of this time

kostaskougios commented 3 years ago

@ZhenXiongQian hmm not sure. Which java & cloner version are you using?

ZhenXiongQian commented 3 years ago

@kostaskougios version: image image test code: image result: image

kostaskougios commented 3 years ago

@ZhenXiongQian indeed for adopt-open-jdk9 it works correctly. It seems this jdk instantiates functions in a different way than the others