From e61ad5033ab2b151b83af7cc849a2b140a172430 Mon Sep 17 00:00:00 2001 From: Shunping Huang Date: Mon, 18 May 2026 16:16:55 -0400 Subject: [PATCH 1/3] Fix Pythn Flink PVR --- .../sdk/options/PipelineOptionsFactory.java | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/options/PipelineOptionsFactory.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/options/PipelineOptionsFactory.java index ac76a57b6b07..2338285e12a0 100644 --- a/sdks/java/core/src/main/java/org/apache/beam/sdk/options/PipelineOptionsFactory.java +++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/options/PipelineOptionsFactory.java @@ -502,13 +502,6 @@ Class getProxyClass() { new ObjectMapper() .registerModules(ObjectMapper.findModules(ReflectHelpers.findClassLoader())); - private static final DefaultDeserializationContext DESERIALIZATION_CONTEXT = - new DefaultDeserializationContext.Impl(MAPPER.getDeserializationContext().getFactory()) - .createInstance( - MAPPER.getDeserializationConfig(), - new TokenBuffer(MAPPER, false).asParser(), - new InjectableValues.Std()); - static final DefaultSerializerProvider SERIALIZER_PROVIDER = new DefaultSerializerProvider.Impl() .createInstance(MAPPER.getSerializationConfig(), MAPPER.getSerializerFactory()); @@ -1733,7 +1726,15 @@ private static JsonDeserializer computeDeserializerForMethod(Method meth BeanProperty prop = createBeanProperty(method); AnnotatedMember annotatedMethod = prop.getMember(); - DefaultDeserializationContext context = DESERIALIZATION_CONTEXT.copy(); + // Initialize a new context that is properly associated with a dummy parser. + // Using copy() here would leave the context's transient parser field as null, + // causing NullPointerExceptions in Jackson 2.14+ when deserializers try to + // query the parser for format constraints or coercion validations. + JsonParser dummyParser = new TokenBuffer(MAPPER, false).asParser(); + DefaultDeserializationContext context = + ((DefaultDeserializationContext) MAPPER.getDeserializationContext()) + .createInstance( + MAPPER.getDeserializationConfig(), dummyParser, new InjectableValues.Std()); Object maybeDeserializerClass = context.getAnnotationIntrospector().findDeserializer(annotatedMethod); @@ -1805,7 +1806,14 @@ static Object deserializeNode(JsonNode node, Method method) throws IOException { parser.nextToken(); JsonDeserializer jsonDeserializer = getDeserializerForMethod(method); - return jsonDeserializer.deserialize(parser, DESERIALIZATION_CONTEXT.copy()); + // Create a fresh context that is correctly associated with the active parser. + // Using copy() here would leave the context's transient parser field as null, + // causing NullPointerExceptions in Jackson 2.14+ deserializers during coercion + // validations and length checks. + DefaultDeserializationContext context = + ((DefaultDeserializationContext) MAPPER.getDeserializationContext()) + .createInstance(MAPPER.getDeserializationConfig(), parser, new InjectableValues.Std()); + return jsonDeserializer.deserialize(parser, context); } /** From be71cf65a388e162571103041840c3da3c8b7f15 Mon Sep 17 00:00:00 2001 From: Shunping Huang Date: Mon, 18 May 2026 16:23:35 -0400 Subject: [PATCH 2/3] Trigger test. --- .github/trigger_files/beam_PreCommit_Python_PVR_Flink.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/trigger_files/beam_PreCommit_Python_PVR_Flink.json diff --git a/.github/trigger_files/beam_PreCommit_Python_PVR_Flink.json b/.github/trigger_files/beam_PreCommit_Python_PVR_Flink.json new file mode 100644 index 000000000000..616d37428c01 --- /dev/null +++ b/.github/trigger_files/beam_PreCommit_Python_PVR_Flink.json @@ -0,0 +1,4 @@ +{ + "comment": "Modify this file in a trivial way to cause this test suite to run", + "revision": 1 +} From e7ab07d28c161214265bbd0dfee7f8bc8abf8691 Mon Sep 17 00:00:00 2001 From: Shunping Huang Date: Mon, 18 May 2026 20:34:51 -0400 Subject: [PATCH 3/3] Refactor --- .../beam/sdk/options/PipelineOptionsFactory.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/options/PipelineOptionsFactory.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/options/PipelineOptionsFactory.java index 2338285e12a0..b9061e1734cf 100644 --- a/sdks/java/core/src/main/java/org/apache/beam/sdk/options/PipelineOptionsFactory.java +++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/options/PipelineOptionsFactory.java @@ -1731,10 +1731,7 @@ private static JsonDeserializer computeDeserializerForMethod(Method meth // causing NullPointerExceptions in Jackson 2.14+ when deserializers try to // query the parser for format constraints or coercion validations. JsonParser dummyParser = new TokenBuffer(MAPPER, false).asParser(); - DefaultDeserializationContext context = - ((DefaultDeserializationContext) MAPPER.getDeserializationContext()) - .createInstance( - MAPPER.getDeserializationConfig(), dummyParser, new InjectableValues.Std()); + DefaultDeserializationContext context = createDeserializationContext(dummyParser); Object maybeDeserializerClass = context.getAnnotationIntrospector().findDeserializer(annotatedMethod); @@ -1757,6 +1754,11 @@ private static JsonDeserializer computeDeserializerForMethod(Method meth } } + private static DefaultDeserializationContext createDeserializationContext(JsonParser parser) { + return ((DefaultDeserializationContext) MAPPER.getDeserializationContext()) + .createInstance(MAPPER.getDeserializationConfig(), parser, new InjectableValues.Std()); + } + private static Optional> computeCustomSerializerForMethod(Method method) { try { BeanProperty prop = createBeanProperty(method); @@ -1810,9 +1812,7 @@ static Object deserializeNode(JsonNode node, Method method) throws IOException { // Using copy() here would leave the context's transient parser field as null, // causing NullPointerExceptions in Jackson 2.14+ deserializers during coercion // validations and length checks. - DefaultDeserializationContext context = - ((DefaultDeserializationContext) MAPPER.getDeserializationContext()) - .createInstance(MAPPER.getDeserializationConfig(), parser, new InjectableValues.Std()); + DefaultDeserializationContext context = createDeserializationContext(parser); return jsonDeserializer.deserialize(parser, context); }