Skip to content

Commit cae0fce

Browse files
authored
Add test to reproduce #285 (#289)
1 parent 1241d7d commit cae0fce

File tree

4 files changed

+92
-4
lines changed

4 files changed

+92
-4
lines changed

blackbird/src/main/java/com/fasterxml/jackson/module/blackbird/CrossLoaderAccess.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ private static boolean hasFullAccess(MethodHandles.Lookup lookup) {
106106

107107
private static Class<?> accessClassIn(MethodHandles.Lookup lookup) throws IOException, ReflectiveOperationException {
108108
Package pkg = lookup.lookupClass().getPackage();
109-
String accessClassName = pkg.getName() + "." + CLASS_NAME;
109+
final String pkgName = pkg.getName();
110+
String accessClassName = pkgName.isEmpty() ? CLASS_NAME : (pkgName + "." + CLASS_NAME);
110111
ClassLoader lookupClassLoader = lookup.lookupClass().getClassLoader();
111112
try {
112113
return Class.forName(accessClassName, true, lookupClassLoader);
@@ -115,9 +116,9 @@ private static Class<?> accessClassIn(MethodHandles.Lookup lookup) throws IOExce
115116
try {
116117
return Class.forName(accessClassName, true, lookupClassLoader);
117118
} catch (ClassNotFoundException ign) { }
118-
String fqcn = pkg.getName()
119-
.replace('.', '/')
120-
+ "/" + CLASS_NAME;
119+
String fqcn = pkgName.isEmpty() ? CLASS_NAME
120+
: (pkgName.replace('.', '/') + "/" + CLASS_NAME);
121+
121122
ByteArrayOutputStream classBytes = new ByteArrayOutputStream(HEADER.length + FOOTER.length + fqcn.length() + 16);
122123
DataOutputStream dataOut = new DataOutputStream(classBytes);
123124
for (int b : HEADER) {
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package com.fasterxml.jackson.module.blackbird.ser;
2+
3+
import java.io.File;
4+
import java.lang.reflect.Field;
5+
import java.net.URL;
6+
import java.net.URLClassLoader;
7+
import java.nio.charset.StandardCharsets;
8+
import java.nio.file.Files;
9+
import java.nio.file.Path;
10+
import java.util.Arrays;
11+
12+
import org.junit.jupiter.api.Test;
13+
14+
import com.fasterxml.jackson.databind.ObjectMapper;
15+
import com.fasterxml.jackson.module.blackbird.BlackbirdTestBase;
16+
17+
import javax.tools.*;
18+
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.junit.jupiter.api.Assertions.assertTrue;
21+
22+
public class TestNoPackageSerialization extends BlackbirdTestBase
23+
{
24+
@Test
25+
public void testSerializeDeserializeDefaultPackageClass() throws Exception {
26+
// Define the source code for a class in the default package (no package
27+
// declaration)
28+
String source = "package dynamicClassTest;" +
29+
"public class Person {" +
30+
" public String name;" +
31+
" public int age;" +
32+
" public Person() {}" +
33+
" public Person(String name, int age) {" +
34+
" this.name = name;" +
35+
" this.age = age;" +
36+
" }" +
37+
"}";
38+
39+
// Create a temporary directory for the compiled class
40+
Path tempDir = Files.createTempDirectory("dynamicClassTest");
41+
File sourceFile = new File(tempDir.toFile(), "Person.java");
42+
//System.out.println(sourceFile.getAbsolutePath());
43+
Files.write(sourceFile.toPath(), source.getBytes(StandardCharsets.UTF_8));
44+
45+
// Compile the source file using the JDK compiler
46+
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
47+
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
48+
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(sourceFile);
49+
JavaCompiler.CompilationTask task = compiler.getTask(
50+
null, fileManager, null,
51+
Arrays.asList("-d", tempDir.toString()),
52+
null, compilationUnits);
53+
assertTrue(task.call(), "Compilation failed");
54+
fileManager.close();
55+
56+
// Load the compiled class using a URLClassLoader
57+
try (URLClassLoader classLoader = new URLClassLoader(new URL[] { tempDir.toUri().toURL() })) {
58+
Class<?> personClass = classLoader.loadClass("dynamicClassTest.Person");
59+
60+
// Instantiate the Person object using reflection
61+
Object personInstance = personClass.getConstructor(String.class, int.class)
62+
.newInstance("John Doe", 42);
63+
final ObjectMapper mapper = newObjectMapper();
64+
65+
// Perform serialization and deserialization
66+
String json = mapper.writeValueAsString(personInstance);
67+
Object deserialized = mapper.readValue(json, personClass);
68+
69+
// Verify that the deserialized object has the expected field values via
70+
// reflection
71+
Field nameField = personClass.getField("name");
72+
Field ageField = personClass.getField("age");
73+
74+
assertEquals("John Doe", nameField.get(deserialized));
75+
assertEquals(42, ageField.get(deserialized));
76+
}
77+
}
78+
}

release-notes/CREDITS-2.x

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,9 @@ Jack Dunning (@JDUNNIN)
160160
* Contributed #251: Constructor is not recognized when a record uses both arrays
161161
and generic types
162162
(2.18.0)
163+
164+
Apoorva Manjunath (@apoorvam)
165+
166+
* Reported, contributed fix for #285: `JacksonBlackbirdAccess` Class access
167+
exception with Groovy Script serialization using `BlackbirdModule`
168+
(2.19.0)

release-notes/VERSION-2.x

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ Active maintainers:
2727

2828
#268: Unify testing structure/tools [JSTEP-10]
2929
(contributed by Joo-Hyuk K)
30+
#285: `JacksonBlackbirdAccess` Class access exception with Groovy Script
31+
serialization using `BlackbirdModule`
32+
(reported, fix contributed by Apoorva M)
3033

3134
2.18.3 (28-Feb-2025)
3235
2.18.2 (27-Nov-2024)

0 commit comments

Comments
 (0)