Skip to content

Commit 4c96440

Browse files
authored
Fix #334: make BlackbirdModule java.io.Serializable (#335)
1 parent bd94855 commit 4c96440

7 files changed

Lines changed: 161 additions & 19 deletions

File tree

android-record/src/main/java/tools/jackson/module/androidrecord/AndroidRecordModule.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@
3535
*
3636
* @author Eran Leshem
3737
**/
38-
public class AndroidRecordModule extends SimpleModule
38+
public class AndroidRecordModule
39+
extends SimpleModule
40+
implements java.io.Serializable
3941
{
40-
private static final long serialVersionUID = 1L;
42+
private static final long serialVersionUID = 3L;
4143

4244
static final class AndroidRecordNaming
4345
extends DefaultAccessorNamingStrategy
@@ -72,7 +74,7 @@ public String findNameForRegularGetter(AnnotatedMethod am, String name)
7274

7375
static class AndroidRecordClassIntrospector extends BasicClassIntrospector
7476
{
75-
private static final long serialVersionUID = 1L;
77+
private static final long serialVersionUID = 3L;
7678

7779
public AndroidRecordClassIntrospector() {
7880
super();
@@ -106,7 +108,7 @@ public BasicClassIntrospector forOperation(MapperConfig<?> config) {
106108

107109
static class AndroidRecordAnnotationIntrospector extends AnnotationIntrospector
108110
{
109-
private static final long serialVersionUID = 1L;
111+
private static final long serialVersionUID = 3L;
110112

111113
@Override
112114
public Version version() {

blackbird/src/main/java/tools/jackson/module/blackbird/BlackbirdModule.java

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,77 @@
1414
import tools.jackson.module.blackbird.ser.BBSerializerModifier;
1515

1616
public class BlackbirdModule extends JacksonModule
17+
implements java.io.Serializable // @since 3.1
1718
{
18-
private Function<Class<?>, Lookup> _lookups;
19+
private static final long serialVersionUID = 3L;
20+
21+
// 13-Feb-2026, tatu: [blackbird#334] This is a mess due to need for
22+
// backwards-compatibility... but has to be
23+
24+
private final Supplier<MethodHandles.Lookup> _lookupSupplier;
25+
26+
private final Function<Class<?>, MethodHandles.Lookup> _lookupFunction;
1927

2028
public BlackbirdModule() {
21-
this(MethodHandles::lookup);
29+
_lookupSupplier = null;
30+
_lookupFunction = null;
31+
}
32+
33+
/**
34+
* @since 3.1 Use default (no-args) constructor and override
35+
* {@link #findLookupSupplier()}) instead.
36+
*/
37+
@Deprecated // @since 3.1
38+
public BlackbirdModule(Supplier<MethodHandles.Lookup> lookupS) {
39+
_lookupSupplier = lookupS;
40+
_lookupFunction = null;
2241
}
2342

24-
public BlackbirdModule(Function<Class<?>, MethodHandles.Lookup> lookups) {
25-
_lookups = lookups;
43+
/**
44+
* @since 3.1 Use default (no-args) constructor and override
45+
* {@link #findLookup}) instead.
46+
*/
47+
@Deprecated // @since 3.1
48+
public BlackbirdModule(Function<Class<?>, MethodHandles.Lookup> lookupF) {
49+
_lookupSupplier = null;
50+
_lookupFunction = lookupF;
2651
}
2752

28-
public BlackbirdModule(Supplier<MethodHandles.Lookup> lookup) {
29-
this(c -> {
53+
/**
54+
* Overridable method module uses to access {@code MethodHandles.Lookup} supplier;
55+
* needed to keep module itself {@link java.io.Serializable}.
56+
*
57+
* @since 3.1
58+
*/
59+
protected Function<Class<?>, Lookup> findLookup() {
60+
if (_lookupFunction != null) {
61+
return _lookupFunction;
62+
}
63+
return _wrapWithJdkClassCheck(findLookupSupplier());
64+
}
65+
66+
/**
67+
* Overridable method module uses to access {@code MethodHandles.Lookup} supplier
68+
* used to create actual lookup {@code Function}
69+
*
70+
* @since 3.1
71+
*/
72+
protected Supplier<MethodHandles.Lookup> findLookupSupplier() {
73+
return (_lookupSupplier != null)
74+
? _lookupSupplier
75+
: MethodHandles::lookup;
76+
77+
}
78+
79+
protected Function<Class<?>, Lookup> _wrapWithJdkClassCheck(Supplier<MethodHandles.Lookup> lookup)
80+
{
81+
return c -> {
3082
final String className = c.getName();
3183
return (className.startsWith("java.")
3284
// 23-Apr-2021, tatu: [modules-base#131] "sun.misc" problematic too
3385
|| className.startsWith("sun.misc."))
3486
? null : lookup.get();
35-
});
87+
};
3688
}
3789

3890
@Override
@@ -43,9 +95,10 @@ public void setupModule(SetupContext context)
4395
{
4496
return;
4597
}
98+
Function<Class<?>, Lookup> lookup = findLookup();
4699
CrossLoaderAccess openSesame = new CrossLoaderAccess();
47-
context.addDeserializerModifier(new BBDeserializerModifier(_lookups, openSesame));
48-
context.addSerializerModifier(new BBSerializerModifier(_lookups, openSesame));
100+
context.addDeserializerModifier(new BBDeserializerModifier(lookup, openSesame));
101+
context.addSerializerModifier(new BBSerializerModifier(lookup, openSesame));
49102
}
50103

51104
@Override
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package tools.jackson.module.blackbird;
2+
3+
import java.io.*;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
import tools.jackson.databind.ObjectMapper;
8+
9+
import static org.junit.jupiter.api.Assertions.*;
10+
11+
public class BlackbirdModuleJDKSerializabilityTest extends BlackbirdTestBase
12+
{
13+
static class Point {
14+
public int x, y;
15+
}
16+
17+
// First: verify that newly constructed module (registered to Mapper)
18+
// can be JDK serialized, deserialized
19+
@Test
20+
public void testMapperWithoutUse() throws Exception
21+
{
22+
ObjectMapper mapper = newBlackbirdMapper();
23+
byte[] ser = jdkSerialize(mapper);
24+
ObjectMapper m2 = jdkDeserialize(ser);
25+
assertNotNull(m2);
26+
27+
// and that it can be used successfully
28+
_serDeserPointWith(m2);
29+
}
30+
31+
// 24-May-2020, tatu: Fine with 3.0
32+
@Test
33+
public void testMapperAfterUse() throws Exception
34+
{
35+
ObjectMapper mapper = newBlackbirdMapper();
36+
37+
// force use of mapper first
38+
_serDeserPointWith(mapper);
39+
40+
// then freeze/thaw
41+
byte[] ser = jdkSerialize(mapper);
42+
ObjectMapper m3 = jdkDeserialize(ser);
43+
assertNotNull(m3);
44+
45+
_serDeserPointWith(m3);
46+
}
47+
48+
/*
49+
/**********************************************************
50+
/* Helper methods
51+
/**********************************************************
52+
*/
53+
54+
private void _serDeserPointWith(ObjectMapper mapper) throws Exception
55+
{
56+
final Point input = new Point();
57+
byte[] rawPoint = mapper.writeValueAsBytes(input);
58+
Point result = mapper.readValue(rawPoint, Point.class);
59+
assertNotNull(result);
60+
assertEquals(input.x, result.x);
61+
assertEquals(input.y, result.y);
62+
}
63+
64+
protected byte[] jdkSerialize(Object o) throws IOException
65+
{
66+
ByteArrayOutputStream bytes = new ByteArrayOutputStream(1000);
67+
ObjectOutputStream obOut = new ObjectOutputStream(bytes);
68+
obOut.writeObject(o);
69+
obOut.close();
70+
return bytes.toByteArray();
71+
}
72+
73+
@SuppressWarnings("unchecked")
74+
protected <T> T jdkDeserialize(byte[] raw) throws IOException
75+
{
76+
ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(raw));
77+
try {
78+
return (T) objIn.readObject();
79+
} catch (ClassNotFoundException e) {
80+
fail("Missing class: "+e.getMessage());
81+
return null;
82+
} finally {
83+
objIn.close();
84+
}
85+
}
86+
}

blackbird/src/test/java/tools/jackson/module/blackbird/BlackbirdTestBase.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package tools.jackson.module.blackbird;
22

3-
import java.lang.invoke.MethodHandles;
43
import java.nio.charset.StandardCharsets;
54
import java.util.Arrays;
65

@@ -217,7 +216,7 @@ protected static JsonMapper.Builder blackbirdMapperBuilder() {
217216
protected static JsonMapper.Builder mapperBuilder() {
218217
return JsonMapper.builder()
219218
.polymorphicTypeValidator(new NoCheckSubTypeValidator())
220-
.addModule(new BlackbirdModule(MethodHandles::lookup));
219+
.addModule(new BlackbirdModule());
221220
}
222221

223222
protected static JsonMapper newVanillaJSONMapper() {

blackbird/src/test/java/tools/jackson/module/blackbird/TestAccessFallback.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package tools.jackson.module.blackbird;
22

3-
import java.lang.reflect.Proxy;
4-
53
import tools.jackson.databind.ObjectMapper;
64

75
import org.junit.jupiter.api.Test;

blackbird/src/test/java/tools/jackson/module/blackbird/TestVersions.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package tools.jackson.module.blackbird;
22

33
import java.io.*;
4-
import java.lang.invoke.MethodHandles;
54

65
import org.junit.jupiter.api.Test;
76

@@ -18,7 +17,7 @@ public class TestVersions extends BlackbirdTestBase
1817
@Test
1918
public void testMapperVersions() throws IOException
2019
{
21-
BlackbirdModule module = new BlackbirdModule(c -> MethodHandles.lookup());
20+
BlackbirdModule module = new BlackbirdModule();
2221
assertVersion(module);
2322
}
2423

release-notes/VERSION

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ Active maintainers:
2222
=== Releases ===
2323
------------------------------------------------------------------------
2424

25+
3.1.0 (not yet released)
26+
27+
#334: (blackbird) `BlackbirdModule` does not implement `java.io.Serializable`
28+
(reported by @waldemarmeier)
29+
2530
3.1.0-rc1 (27-Jan-2026)
2631

2732
#229: Add `jackson-module-spi-subtypes` module

0 commit comments

Comments
 (0)