Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(java): fix find constructor error in generated serializer class caused by duplicated class classloading for Fury #1948

Merged
merged 4 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.apache.fury.resolver.ClassResolver;
import org.apache.fury.resolver.FieldResolver;
import org.apache.fury.serializer.Serializer;
import org.apache.fury.util.ClassLoaderUtils;
import org.apache.fury.util.Preconditions;

/** Codec util to create and load jit serializer class. */
Expand Down Expand Up @@ -83,9 +84,26 @@ static <T> Class<? extends Serializer<T>> loadOrGenCodecClass(
beanClassClassLoader = fury.getClass().getClassLoader();
}
ClassResolver classResolver = fury.getClassResolver();
codeGenerator = getCodeGenerator(fury, beanClassClassLoader, classResolver);
ClassLoader classLoader =
codeGenerator.compile(
Collections.singletonList(compileUnit), compileState -> compileState.lock.lock());
String className = codecBuilder.codecQualifiedClassName(beanClass);
try {
return (Class<? extends Serializer<T>>) classLoader.loadClass(className);
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Impossible because we just compiled class", e);
}
}

private static CodeGenerator getCodeGenerator(
Fury fury, ClassLoader beanClassClassLoader, ClassResolver classResolver) {
CodeGenerator codeGenerator;
try {
// generated code imported fury classes.
beanClassClassLoader.loadClass(Fury.class.getName());
if (beanClassClassLoader.loadClass(Fury.class.getName()) != Fury.class) {
throw new ClassNotFoundException();
}
codeGenerator = classResolver.getCodeGenerator(beanClassClassLoader);
if (codeGenerator == null) {
codeGenerator = CodeGenerator.getSharedCodeGenerator(beanClassClassLoader);
Expand All @@ -100,20 +118,12 @@ static <T> Class<? extends Serializer<T>> loadOrGenCodecClass(
if (codeGenerator == null) {
codeGenerator =
CodeGenerator.getSharedCodeGenerator(
beanClassClassLoader, fury.getClass().getClassLoader());
ClassLoaderUtils.FuryJarClassLoader.getInstance(), beanClassClassLoader);
// Hold strong reference of {@link CodeGenerator}, so the referent of `DelayedRef`
// won't be null.
classResolver.setCodeGenerator(loaders, codeGenerator);
}
}
ClassLoader classLoader =
codeGenerator.compile(
Collections.singletonList(compileUnit), compileState -> compileState.lock.lock());
String className = codecBuilder.codecQualifiedClassName(beanClass);
try {
return (Class<? extends Serializer<T>>) classLoader.loadClass(className);
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Impossible because we just compiled class", e);
}
return codeGenerator;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.apache.fury.Fury;
import org.apache.fury.logging.Logger;
import org.apache.fury.logging.LoggerFactory;
import org.apache.fury.util.unsafe.DefineClass;
Expand Down Expand Up @@ -168,6 +169,33 @@ public URL getResource(String name) {
}
}

/** A classloader to load Fury jar classes only. */
public static class FuryJarClassLoader extends URLClassLoader {
static {
ClassLoader.registerAsParallelCapable();
}

private static final ParentClassLoader LOADER =
new ParentClassLoader(Fury.class.getClassLoader());
private static final FuryJarClassLoader FURY_JAR_LOADER = new FuryJarClassLoader();

private FuryJarClassLoader() {
super(new URL[0]);
}

@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.startsWith(Fury.class.getPackage().getName()) && !name.contains("test")) {
return LOADER.loadClass(name, resolve);
}
return null;
}

public static FuryJarClassLoader getInstance() {
return FURY_JAR_LOADER;
}
}

/**
* A parallel loadable {@link ClassLoader} which make defineClass public for using in JDK17+.
* {@link MethodHandle} can also be used for access `defineClass`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.apache.fury.test.bean.Foo;
import org.apache.fury.util.ClassLoaderUtils;
import org.apache.fury.util.ClassLoaderUtils.ByteArrayClassLoader;
import org.apache.fury.util.ClassLoaderUtils.FuryJarClassLoader;
import org.apache.fury.util.DelayedRef;
import org.testng.Assert;
import org.testng.annotations.Test;
Expand Down Expand Up @@ -71,6 +72,13 @@ public void testGetSharedCodeGenerator() {
sharedCodeGenerator2
.get(new Object[] {getClass().getClassLoader(), Fury.class.getClassLoader()})
.get());
CodeGenerator.getSharedCodeGenerator(
FuryJarClassLoader.getInstance(), getClass().getClassLoader());
System.gc();
assertNotNull(
sharedCodeGenerator2
.get(new Object[] {FuryJarClassLoader.getInstance(), getClass().getClassLoader()})
.get());
}

@Test
Expand Down
Loading