Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 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
@@ -1,5 +1,5 @@
{
"https://github.com/apache/beam/pull/32648": "testing addition of Flink 1.19 support",
"https://github.com/apache/beam/pull/34830": "testing",
"trigger-2026-04-04": "portable_runner expand_sdf opt-in"
"trigger-2026-05-18": "Fix deserializeNode NPE"
}
Original file line number Diff line number Diff line change
Expand Up @@ -1746,7 +1746,7 @@ private static JsonDeserializer<Object> computeDeserializerForMethod(Method meth

TypeDeserializer typeDeserializer =
context.getFactory().findTypeDeserializer(context.getConfig(), prop.getType());
if (typeDeserializer != null) {
if (typeDeserializer != null && jsonDeserializer != null) {
jsonDeserializer = new TypeWrappedDeserializer(typeDeserializer, jsonDeserializer);
}

Expand Down Expand Up @@ -1801,11 +1801,22 @@ static Object deserializeNode(JsonNode node, Method method) throws IOException {
return null;
}

JsonParser parser = new TreeTraversingParser(node, MAPPER);
parser.nextToken();

JsonDeserializer<Object> jsonDeserializer = getDeserializerForMethod(method);
return jsonDeserializer.deserialize(parser, DESERIALIZATION_CONTEXT.copy());
if (jsonDeserializer == null) {
JavaType javaType = MAPPER.constructType(method.getGenericReturnType());
return MAPPER.treeToValue(node, javaType);
}

try {
JsonParser parser = new TreeTraversingParser(node, MAPPER);
parser.nextToken();
return jsonDeserializer.deserialize(parser, DESERIALIZATION_CONTEXT.copy());
} catch (NullPointerException e) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

catch an NPE is almost always an anti-pattern. Let me check what happens caused NPE here, and stack trace?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahh right it is not a long term fix, i refactored it and her'es the stack trace:

java.lang.IllegalArgumentException: Error while populating display data for component
'org.apache.beam.sdk.options.ProxyInvocationHandler$PipelineOptionsDisplayData': null
at PipelineOptionsTranslation.toProto(...)
at FlinkJobInvoker.createJobInvocation(...)
...
Caused by: java.lang.NullPointerException
at PipelineOptionsFactory.deserializeNode(PipelineOptionsFactory.java:1813)
at ProxyInvocationHandler.getValueFromJson(...)
at ProxyInvocationHandler$PipelineOptionsDisplayData.populateDisplayData(...:446)
at DisplayData.from(...)
at ProxyInvocationHandler$Serializer.serialize(...)

Caused by: java.lang.NullPointerException
at org.apache.beam.sdk.options.PipelineOptionsFactory.deserializeNode(...)
at org.apache.beam.sdk.options.ProxyInvocationHandler.getValueFromJson(...)
at org.apache.beam.sdk.options.ProxyInvocationHandler$PipelineOptionsDisplayData.populateDisplayData(...:446)
at org.apache.beam.sdk.transforms.display.DisplayData.from(...)
at org.apache.beam.sdk.options.ProxyInvocationHandler$Serializer.serialize(...)
at org.apache.beam.sdk.util.construction.PipelineOptionsTranslation.toProto(...)
at org.apache.beam.runners.flink.FlinkJobInvoker.createJobInvocation(...)

// Jackson 2.18+ can yield a non-null contextual JsonDeserializer that still NPEs for some
// pipeline option shapes during display-data serialization (Flink ValidatesRunner Kafka).
JavaType javaType = MAPPER.constructType(method.getGenericReturnType());
return MAPPER.treeToValue(node, javaType);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
import org.apache.beam.sdk.testing.ExpectedLogs;
import org.apache.beam.sdk.testing.InterceptingUrlClassLoader;
import org.apache.beam.sdk.testing.RestoreSystemProperties;
import org.apache.beam.sdk.transforms.display.DisplayData;
import org.apache.beam.sdk.util.common.ReflectHelpers;
import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.ArrayListMultimap;
import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.Collections2;
Expand Down Expand Up @@ -1130,6 +1131,24 @@ public void testPolymorphicType() {
assertEquals(PolymorphicTypeTwo.class, options.getObjectValue().get().getClass());
}

/**
* Regression test for display-data population after JSON round-trip. The contextual
* JsonDeserializer path must not NPE without recovery; {@link DisplayData#from} exercises {@link
* PipelineOptionsFactory#deserializeNode} for jsonOptions.
*/
@Test
public void testDisplayDataFromDeserializedPolymorphicOption() throws Exception {
PolymorphicTypes options =
PipelineOptionsFactory.fromArgs("--object={\"key\":\"value\",\"@type\":\"one\"}")
.as(PolymorphicTypes.class);
ObjectMapper mapper =
new ObjectMapper()
.registerModules(ObjectMapper.findModules(ReflectHelpers.findClassLoader()));
PipelineOptions deserialized =
mapper.readValue(mapper.writeValueAsString(options), PipelineOptions.class);
DisplayData.from(deserialized);
}

@Test
public void testMissingArgument() {
String[] args = new String[] {};
Expand Down
Loading