From f6a07686fcac6128aebcf1168d9d085a8e40b7b8 Mon Sep 17 00:00:00 2001 From: Sylwester Lachiewicz Date: Sat, 8 Aug 2026 15:45:23 +0200 Subject: [PATCH] Keep verbatim blocks working with Doxia 1 Sink.verbatim() was only added in Doxia 2, but a report plugin never runs against its own Doxia: DefaultMavenReportExecutor imports the org.apache.maven.doxia.sink package from the Maven Site Plugin realm into the report plugin realm and excludes doxia-sink-api from the report plugin's own dependencies. So with any Maven Site Plugin older than 3.21.0, and that is still the version Maven 3.9.x binds by default, the call ends in NoSuchMethodError and the report is silently cut short. Use verbatim(SinkEventAttributes) instead, which exists in Doxia 1 and 2 alike and is null safe in both. --- .../AbstractMavenReportRenderer.java | 9 +++- .../AbstractMavenReportRendererTest.java | 42 +++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/maven/reporting/AbstractMavenReportRenderer.java b/src/main/java/org/apache/maven/reporting/AbstractMavenReportRenderer.java index 79b5e47..71a479f 100644 --- a/src/main/java/org/apache/maven/reporting/AbstractMavenReportRenderer.java +++ b/src/main/java/org/apache/maven/reporting/AbstractMavenReportRenderer.java @@ -27,6 +27,7 @@ import org.apache.maven.doxia.markup.Markup; import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.doxia.sink.SinkEventAttributes; import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet; import org.apache.maven.shared.utils.StringUtils; @@ -343,7 +344,10 @@ protected void text(String text) { * @see Sink#verbatim_() */ protected void verbatimText(String text) { - sink.verbatim(); + // Sink.verbatim() only exists since Doxia 2, while report plugins always run against the Doxia + // provided by the Maven Site Plugin in use, which may still be Doxia 1. Only the attribute + // taking overload exists in both, so stick to it (MPIR issue 103). + sink.verbatim((SinkEventAttributes) null); text(text); @@ -364,7 +368,8 @@ protected void verbatimLink(String text, String href) { if (href == null || href.isEmpty()) { verbatimText(text); } else { - sink.verbatim(); + // see verbatimText(String) for why the attribute taking overload is used here + sink.verbatim((SinkEventAttributes) null); link(href, text); diff --git a/src/test/java/org/apache/maven/reporting/AbstractMavenReportRendererTest.java b/src/test/java/org/apache/maven/reporting/AbstractMavenReportRendererTest.java index 6484bc8..36972f4 100644 --- a/src/test/java/org/apache/maven/reporting/AbstractMavenReportRendererTest.java +++ b/src/test/java/org/apache/maven/reporting/AbstractMavenReportRendererTest.java @@ -20,9 +20,12 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.util.Iterator; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import org.apache.maven.doxia.sink.Sink; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -125,4 +128,43 @@ void applyPattern() throws Throwable { "http://www.apache.org/licenses/LICENSE-1.1" }); } + + /** + * Report plugins always run against the Doxia provided by the Maven Site Plugin in use, which may still be + * Doxia 1, where {@code Sink.verbatim()} does not exist yet. A dynamic proxy is used instead of a + * {@code SinkAdapter} subclass because {@code AbstractSink.verbatim()} is final and delegates to the + * attribute taking overload, which would hide the distinction this test is about. + */ + @Test + void verbatimOnlyUsesTheOverloadCommonToDoxia1And2() { + AtomicInteger verbatimCalls = new AtomicInteger(); + Sink sink = (Sink) Proxy.newProxyInstance( + getClass().getClassLoader(), new Class[] {Sink.class}, (proxy, method, args) -> { + if ("verbatim".equals(method.getName())) { + assertEquals( + 1, + method.getParameterCount(), + "Sink.verbatim() does not exist in Doxia 1 and must not be called"); + verbatimCalls.incrementAndGet(); + } + return null; + }); + + AbstractMavenReportRenderer renderer = new AbstractMavenReportRenderer(sink) { + @Override + public String getTitle() { + return "title"; + } + + @Override + protected void renderBody() { + verbatimText("text"); + verbatimLink("text", "https://maven.apache.org/"); + } + }; + + renderer.render(); + + assertEquals(2, verbatimCalls.get()); + } }