diff --git a/src/it/use-as-direct-mojo/invoker.properties b/src/it/use-as-direct-mojo/invoker.properties
index f26b482..b57113c 100644
--- a/src/it/use-as-direct-mojo/invoker.properties
+++ b/src/it/use-as-direct-mojo/invoker.properties
@@ -18,4 +18,4 @@
invoker.goals.1 = custom-reporting:custom
invoker.goals.2 = custom-reporting:custom-renderer
invoker.goals.3 = custom-reporting:external
-#invoker.goals.4 = custom-reporting:multi-page # https://github.com/apache/maven-reporting-impl/issues/217
+invoker.goals.4 = custom-reporting:multi-page
diff --git a/src/it/use-as-direct-mojo/verify.groovy b/src/it/use-as-direct-mojo/verify.groovy
index 3db9394..da36707 100644
--- a/src/it/use-as-direct-mojo/verify.groovy
+++ b/src/it/use-as-direct-mojo/verify.groovy
@@ -34,10 +34,12 @@ f = new File( outputDir, 'external/report.html' );
assert f.exists();
assert f.text.contains( '
External Report
' );
-// https://github.com/apache/maven-reporting-impl/issues/217
-/*f = new File( outputDir, 'multi-page.html' );
+f = new File( outputDir, 'multi-page.html' );
assert f.exists();
+assert f.text.contains( 'Custom Maven Report with Renderer content.' );
+
f = new File( outputDir, 'multi-second.html' );
-assert f.exists();*/
+assert f.exists();
+assert f.text.contains( 'Custom Maven Report with Renderer content.' );
return true;
diff --git a/src/main/java/org/apache/maven/reporting/AbstractMavenReport.java b/src/main/java/org/apache/maven/reporting/AbstractMavenReport.java
index c2dd8b5..86f2531 100644
--- a/src/main/java/org/apache/maven/reporting/AbstractMavenReport.java
+++ b/src/main/java/org/apache/maven/reporting/AbstractMavenReport.java
@@ -23,9 +23,11 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@@ -55,6 +57,7 @@
import org.apache.maven.shared.utils.WriterFactory;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.codehaus.plexus.util.PathTool;
import org.codehaus.plexus.util.ReaderFactory;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.RemoteRepository;
@@ -280,9 +283,10 @@ private void reportToSite() throws MojoExecutionException {
new DocumentRenderingContext(outputDirectory, getOutputPath(), reportMojoInfo);
SiteRendererSink sink = new SiteRendererSink(docRenderingContext);
+ // sink factory, for multipage reports that need sub-sinks
+ MultiPageSinkFactory multiPageSinkFactory = new MultiPageSinkFactory(this, docRenderingContext);
- // TODO Compared to Maven Site Plugin multipage reports will not work and fail with an NPE
- generate(sink, null, locale);
+ generate(sink, multiPageSinkFactory, locale);
if (!isExternalReport()) { // MSHARED-204: only render Doxia sink if not an external report
outputDirectory.mkdirs();
@@ -292,6 +296,27 @@ private void reportToSite() throws MojoExecutionException {
// render report
getSiteRenderer().mergeDocumentIntoSite(writer, sink, siteContext);
}
+
+ // render the subpages eventually created by a multipage report
+ for (MultiPageSubSink subSink : multiPageSinkFactory.sinks()) {
+ File subOutputDirectory = subSink.getOutputDirectory();
+ subOutputDirectory.mkdirs();
+
+ // the directory comes from the report, so make sure it is absolute before relativizing it
+ File subOutputFile = new File(subOutputDirectory, subSink.getOutputName());
+ getLog().info("Rendering report to "
+ + getProject()
+ .getBasedir()
+ .toPath()
+ .relativize(subOutputFile.toPath().toAbsolutePath()));
+
+ try (Writer writer =
+ new OutputStreamWriter(new FileOutputStream(subOutputFile), getOutputEncoding())) {
+ getSiteRenderer().mergeDocumentIntoSite(writer, subSink, siteContext);
+ } finally {
+ subSink.close();
+ }
+ }
}
// copy generated resources also
@@ -302,6 +327,97 @@ private void reportToSite() throws MojoExecutionException {
}
}
+ /**
+ * A sink for one subpage of a multipage report, remembering where it is meant to be written to.
+ */
+ private static class MultiPageSubSink extends SiteRendererSink {
+ private final File outputDirectory;
+
+ private final String outputName;
+
+ MultiPageSubSink(File outputDirectory, String outputName, DocumentRenderingContext docRenderingContext) {
+ super(docRenderingContext);
+ this.outputDirectory = outputDirectory;
+ this.outputName = outputName;
+ }
+
+ String getOutputName() {
+ return outputName;
+ }
+
+ File getOutputDirectory() {
+ return outputDirectory;
+ }
+ }
+
+ /**
+ * The sink factory handed to {@link #generate(Sink, SinkFactory, Locale)}, mirroring what Maven Site Plugin
+ * provides so that a multipage report behaves the same when its goal is invoked directly.
+ */
+ private static class MultiPageSinkFactory implements SinkFactory {
+ /**
+ * The report that is (maybe) generating multiple pages
+ */
+ private final MavenReport report;
+
+ /**
+ * The main DocumentRenderingContext, which is the base for the DocumentRenderingContext of subpages
+ */
+ private final DocumentRenderingContext docRenderingContext;
+
+ /**
+ * List of sinks (subpages) associated to this report
+ */
+ private final List sinks = new ArrayList<>();
+
+ MultiPageSinkFactory(MavenReport report, DocumentRenderingContext docRenderingContext) {
+ this.report = report;
+ this.docRenderingContext = docRenderingContext;
+ }
+
+ @Override
+ public Sink createSink(File outputDirectory, String outputName) {
+ // Create a new document rendering context, similar to the main one, but with a different output name
+ String document = PathTool.getRelativeFilePath(
+ report.getReportOutputDirectory().getPath(), new File(outputDirectory, outputName).getPath());
+ // Remove .html suffix since we know that we are in Site Renderer context
+ document = document.substring(0, document.lastIndexOf('.'));
+
+ DocumentRenderingContext subSinkContext = new DocumentRenderingContext(
+ docRenderingContext.getBasedir(), document, docRenderingContext.getGenerator());
+
+ // Create a sink for this subpage, based on this new document rendering context
+ MultiPageSubSink sink = new MultiPageSubSink(outputDirectory, outputName, subSinkContext);
+
+ // Add it to the list of sinks associated to this report
+ sinks.add(sink);
+
+ return sink;
+ }
+
+ @Override
+ public Sink createSink(File outputDir, String outputName, String encoding) {
+ throw new UnsupportedOperationException(
+ "Only createSink(File, String) is supported by MultiPageSinkFactory. The encoding is always determined by the site rendering context.");
+ }
+
+ @Override
+ public Sink createSink(OutputStream out) {
+ throw new UnsupportedOperationException(
+ "Only createSink(File, String) is supported by MultiPageSinkFactory. OutputStream based sinks are not supported.");
+ }
+
+ @Override
+ public Sink createSink(OutputStream out, String encoding) {
+ throw new UnsupportedOperationException(
+ "Only createSink(File, String) is supported by MultiPageSinkFactory. OutputStream based sinks are not supported.");
+ }
+
+ List sinks() {
+ return sinks;
+ }
+ }
+
private SiteRenderingContext createSiteRenderingContext(Locale locale)
throws MavenReportException, IOException, SiteToolException {
SiteModel siteModel = siteTool.getSiteModel(