From 2334e6cf82365ec08de0a1645863205ca4f942dc Mon Sep 17 00:00:00 2001 From: opencode Date: Wed, 29 Jul 2026 11:50:40 +0000 Subject: [PATCH 1/2] [MPH-191] Fix help:evaluate failing on Java 18+ due to Hashtable reflection Change PropertiesConverter.canConvert() to use isAssignableFrom() instead of exact class match, so that subclasses of Properties (e.g. SortedProperties) are handled without falling through to SerializableConverter which tries to reflectively access java.util.Hashtable.table, forbidden on Java 18+. --- src/main/java/org/apache/maven/plugins/help/EvaluateMojo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/maven/plugins/help/EvaluateMojo.java b/src/main/java/org/apache/maven/plugins/help/EvaluateMojo.java index 4121208..f850835 100644 --- a/src/main/java/org/apache/maven/plugins/help/EvaluateMojo.java +++ b/src/main/java/org/apache/maven/plugins/help/EvaluateMojo.java @@ -385,7 +385,7 @@ private XStream getXStream() { /** {@inheritDoc} */ @Override public boolean canConvert(Class type) { - return Properties.class == type; + return Properties.class.isAssignableFrom(type); } /** {@inheritDoc} */ From b6de1c710b035275d1f1dbd19efce15357e4349d Mon Sep 17 00:00:00 2001 From: opencode Date: Wed, 29 Jul 2026 11:54:58 +0000 Subject: [PATCH 2/2] Add test for Properties subclass serialization in help:evaluate --- .../maven/plugins/help/EvaluateMojoTest.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/test/java/org/apache/maven/plugins/help/EvaluateMojoTest.java b/src/test/java/org/apache/maven/plugins/help/EvaluateMojoTest.java index 5fe15de..994ff26 100644 --- a/src/test/java/org/apache/maven/plugins/help/EvaluateMojoTest.java +++ b/src/test/java/org/apache/maven/plugins/help/EvaluateMojoTest.java @@ -20,6 +20,7 @@ import java.io.ByteArrayOutputStream; import java.io.PrintStream; +import java.util.Properties; import org.apache.maven.api.di.Provides; import org.apache.maven.api.plugin.testing.InjectMojo; @@ -37,6 +38,7 @@ import static org.apache.maven.api.plugin.testing.MojoExtension.setVariableValueToObject; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -184,4 +186,45 @@ void testEvaluateQuiteModeWithOutputOnStdout(EvaluateMojo mojo) throws Exception assertEquals("org.apache.maven.its.help", stdResult); verify(log, never()).warn(anyString()); } + + /** + * Tests that a {@code Properties} subclass (like {@code SortedProperties}) is correctly serialized + * by XStream without falling through to {@code SerializableConverter} which would attempt + * reflective access to {@code java.util.Hashtable.table} (forbidden on Java 18+). + * @throws Exception in case of errors. + */ + @Test + @ResourceLock(Resources.SYSTEM_OUT) + @InjectMojo(goal = "evaluate") + @MojoParameter(name = "forceStdout", value = "true") + @MojoParameter(name = "expression", value = "project.properties") + void testEvaluateWithPropertiesSubclass(EvaluateMojo mojo) throws Exception { + Properties sortedProperties = new AbstractEffectiveMojo.SortedProperties(); + sortedProperties.setProperty("key1", "value1"); + sortedProperties.setProperty("key2", "value2"); + + when(expressionEvaluator.evaluate(anyString())).thenReturn(sortedProperties); + when(log.isInfoEnabled()).thenReturn(false); + + setVariableValueToObject(mojo, "evaluator", expressionEvaluator); + + PrintStream saveOut = System.out; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + System.setOut(new PrintStream(baos)); + + try { + mojo.execute(); + } finally { + System.setOut(saveOut); + baos.close(); + } + + String stdResult = baos.toString(); + // Verify serialization succeeded without falling through to SerializableConverter + assertTrue(stdResult.contains("key1")); + assertTrue(stdResult.contains("value1")); + assertTrue(stdResult.contains("key2")); + assertTrue(stdResult.contains("value2")); + verify(log, never()).warn(anyString()); + } }