Skip to content

Support multipage reports when the goal is invoked directly - #244

Merged
slachiewicz merged 4 commits into
apache:masterfrom
slachiewicz:multipage-standalone-report
Oct 9, 2026
Merged

slachiewicz merged 4 commits into
apache:masterfrom
slachiewicz:multipage-standalone-report

Conversation

@slachiewicz

@slachiewicz slachiewicz commented Aug 8, 2026 •

Copy link
Copy Markdown
Member

Fixes #217.

reportToSite() passed null as the SinkFactory to generate(Sink, SinkFactory, Locale), with a TODO saying multipage reports would fail with an NPE. They do:

mvn plugin-report:3.15.1:report
...
Cannot invoke "org.apache.maven.doxia.sink.SinkFactory.createSink(java.io.File, String)"
because the return value of "PluginReport.getSinkFactory()" is null
    at PluginReport.generateMojosDocumentation (PluginReport.java:280)
    at AbstractMavenReport.reportToSite (AbstractMavenReport.java:266)

Only the site path was affected. reportToMarkup(), taken when output.format is set, already builds a sink factory, which is why use-as-direct-mojo-markup exercises the multi-page mojo happily while use-as-direct-mojo could not.

The change

Hand generate() a MultiPageSinkFactory: each createSink(File, String) builds a DocumentRenderingContext derived from the main one, wraps it in a SiteRendererSink that remembers where it belongs, and records it. After the main document is merged into the site, every collected sub-sink is merged the same way.

MultiPageSinkFactory and MultiPageSubSink are verbatim copies of the private nested classes in Maven Site Plugin's ReportDocumentRenderer, so a report behaves the same whether its goal is invoked directly or through the site, and the two copies stay diffable. apache/maven-doxia-sitetools#671 tracks moving them into doxia-site-renderer as public API and deleting both copies; the extension-less outputName StringIndexOutOfBoundsException present in both is fixed there too.

Behaviour change worth a release note: getSinkFactory() is now non-null on a direct site-mode invocation. A report that used getSinkFactory() == null to detect direct invocation now takes its multipage branch, which is what #217 asks for.

Test

The integration test for exactly this was already written and disabled with a pointer to the issue, in src/it/use-as-direct-mojo. This PR enables invoker.goals.4 = custom-reporting:multi-page again and asserts the rendered content.

The MultiPageReport fixture now renders a distinct title and body on its second page. Before, both pages were byte-identical, so an assertion on multi-second.html could not tell a sub-sink merged with the wrong context from a correct one. use-as-direct-mojo, use-as-site-report and use-as-direct-mojo-markup all assert that the second page carries the second page's content and not the first page's.

Reverting only AbstractMavenReport and rerunning use-as-direct-mojo reproduces the reported failure:

Cannot invoke "org.apache.maven.doxia.sink.SinkFactory.createSink(java.io.File, String)"
because the return value of "MultiPageReport.getSinkFactory()" is null
Passed: 1, Failed: 1

Verified: mvn verify under JDK 17 → checkstyle, RAT, 3 unit tests and 6 ITs passed; the direct-mojo run logs both pages.

--- custom-reporting:1.0-SNAPSHOT:multi-page (default-cli) @ use-as-direct-mojo ---
Rendering report to target/reports/multi-page.html
          using org.apache.maven.skins:maven-fluido-skin:jar:2.0.0-M9 site skin
Rendering report to target/reports/multi-second.html

I did not rebuild maven-plugin-report-plugin against this branch to re-verify the original report from the issue; the IT covers the same code path with the same API.

@slachiewicz
slachiewicz requested review from hboutemy and michael-o and a lite review from Copilot August 8, 2026 14:25
@slachiewicz slachiewicz added enhancement New feature or request maintenance labels Aug 8, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a NullPointerException when invoking reporting goals directly for multipage reports by providing a SinkFactory during site rendering, aligning behavior with Maven Site Plugin so multipage reports work consistently in both direct-goal and site modes.

Changes:

  • Provide a MultiPageSinkFactory to generate(...) in reportToSite() and render any collected subpage sinks after the main page is merged into the site.
  • Add internal MultiPageSubSink/MultiPageSinkFactory helpers to track subpage output locations and rendering contexts.
  • Re-enable and strengthen the existing IT (use-as-direct-mojo) to verify both multipage outputs exist and contain expected content.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/main/java/org/apache/maven/reporting/AbstractMavenReport.java Passes a sink factory for multipage sub-sinks during direct-goal site rendering and merges generated subpages into the site output.
src/it/use-as-direct-mojo/verify.groovy Re-enables assertions for multipage output and validates rendered content.
src/it/use-as-direct-mojo/invoker.properties Re-enables the multipage goal in the direct-mojo IT.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/main/java/org/apache/maven/reporting/AbstractMavenReport.java
@michael-o

Copy link
Copy Markdown
Member

Does it make sense to maven the MultiReportSink public instead of duplicating the code?

@slachiewicz

Copy link
Copy Markdown
Member Author

So what proposal you have?

@michael-o

Copy link
Copy Markdown
Member

So what proposal you have?

Two steps:

  • Merge this to solve the problem for now (with a TODO)
  • Create a ticket to make the factory public to be usable from site and direct invocation.

@slachiewicz

Copy link
Copy Markdown
Member Author

TODO added in 4489b1b, follow-up filed as apache/maven-doxia-sitetools#671 — move MultiPageSinkFactory/MultiPageSubSink into doxia-site-renderer as public API and drop both copies. That is also where the extension-less outputName StringIndexOutOfBoundsException gets fixed once instead of twice.

reportToSite() passed a null SinkFactory to generate(), so any report
that creates sub-sinks for additional pages failed with an NPE as soon as
its goal was run from the command line rather than through the site. The
maven-plugin-report-plugin report is one such report.

Provide the same MultiPageSinkFactory that Maven Site Plugin's
ReportDocumentRenderer provides, collect the sub-sinks it hands out and
merge each of them into the site next to the main document.

The integration test for this was already written and disabled with a
pointer to the issue, so it is simply enabled again.
…ugin

Generated-by: Claude Opus 5 (1M context)
Both pages were byte-identical, so the ITs could not tell a sub-sink
merged with the wrong context from a correct one.
Keeps the copies mechanically diffable against ReportDocumentRenderer
until doxia-sitetools#671 replaces both.
@slachiewicz
slachiewicz force-pushed the multipage-standalone-report branch from 5d3f6f2 to d172e7f Compare September 30, 2026 18:30

@michael-o michael-o left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I am fine with that. @hboutemy WDYT?

@slachiewicz slachiewicz added bug Something isn't working and removed enhancement New feature or request maintenance labels Oct 9, 2026
@slachiewicz slachiewicz self-assigned this Oct 9, 2026
@slachiewicz
slachiewicz merged commit bef0e6c into apache:master Oct 9, 2026
17 checks passed
@github-actions github-actions Bot added this to the 4.1.0 milestone Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NPE on reporting goal when multi-pages report

3 participants