1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.rat.mp;
20
21 import java.io.ByteArrayOutputStream;
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.OutputStreamWriter;
25 import java.io.Writer;
26 import java.nio.charset.StandardCharsets;
27 import java.nio.file.Files;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Map;
32 import java.util.ResourceBundle;
33
34 import org.apache.maven.artifact.Artifact;
35 import org.apache.maven.doxia.sink.Sink;
36 import org.apache.maven.doxia.sink.SinkFactory;
37 import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
38 import org.apache.maven.doxia.site.SiteModel;
39 import org.apache.maven.doxia.siterenderer.DocumentRenderingContext;
40 import org.apache.maven.doxia.siterenderer.Renderer;
41 import org.apache.maven.doxia.siterenderer.RendererException;
42 import org.apache.maven.doxia.siterenderer.SiteRenderingContext;
43 import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink;
44 import org.apache.maven.doxia.tools.SiteTool;
45 import org.apache.maven.doxia.tools.SiteToolException;
46 import org.apache.maven.execution.MavenSession;
47 import org.apache.maven.plugin.MojoExecutionException;
48 import org.apache.maven.plugins.annotations.Component;
49 import org.apache.maven.plugins.annotations.Mojo;
50 import org.apache.maven.plugins.annotations.Parameter;
51 import org.apache.maven.plugins.annotations.ResolutionScope;
52 import org.apache.maven.reporting.MavenMultiPageReport;
53 import org.apache.maven.reporting.MavenReportException;
54 import org.apache.rat.ReportConfiguration;
55 import org.apache.rat.Reporter;
56 import org.apache.rat.VersionInfo;
57 import org.apache.rat.api.RatException;
58 import org.apache.rat.license.LicenseSetFactory.LicenseFilter;
59 import org.apache.rat.utils.DefaultLog;
60 import org.codehaus.plexus.util.ReaderFactory;
61 import org.eclipse.aether.repository.ArtifactRepository;
62 import org.eclipse.aether.repository.RemoteRepository;
63
64 import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
65
66
67
68
69 @Mojo(name = "rat", requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true)
70 public class RatReportMojo extends AbstractRatMojo implements MavenMultiPageReport {
71
72
73
74
75
76
77
78 @Parameter(defaultValue = "${project.reporting.outputDirectory}", readonly = true, required = true)
79 protected File outputDirectory;
80
81
82
83
84 @Parameter(property = "encoding", defaultValue = "${project.build.sourceEncoding}", readonly = true)
85 private String inputEncoding;
86
87
88
89
90 @Parameter(property = "outputEncoding", defaultValue = "${project.reporting.outputEncoding}", readonly = true)
91 private String outputEncoding;
92
93
94
95
96
97 @Parameter(defaultValue = "${session}", readonly = true, required = true)
98 protected MavenSession session;
99
100
101
102
103
104
105 @Parameter(defaultValue = "${project.remoteArtifactRepositories}", readonly = true, required = true)
106 @Deprecated
107 protected List<ArtifactRepository> remoteArtifactRepositories;
108
109
110
111
112 @Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly = true, required = true)
113 protected List<RemoteRepository> remoteRepositories;
114
115
116
117
118 @Component
119 protected SiteTool siteTool;
120
121
122
123
124 @Component
125 protected Renderer siteRenderer;
126
127
128
129
130 private Sink sink;
131
132
133
134
135 private SinkFactory sinkFactory;
136
137
138
139
140 private File reportOutputDirectory;
141
142
143
144
145
146
147
148
149 @Override
150 public void execute() throws MojoExecutionException {
151 if (!canGenerateReport()) {
152 return;
153 }
154
155 File outputDirectory = new File(getOutputDirectory());
156
157 String filename = getOutputName() + ".html";
158
159 Locale locale = Locale.getDefault();
160
161 try {
162 SiteRenderingContext siteContext = createSiteRenderingContext(locale);
163
164
165 getSiteRenderer().copyResources(siteContext, outputDirectory);
166
167
168 DocumentRenderingContext docContext =
169 new DocumentRenderingContext(outputDirectory, filename, "xhtml5");
170 SiteRendererSink sink = new SiteRendererSink(docContext);
171
172 generate(sink, null, locale);
173
174
175 if (!isExternalReport()) {
176 if (!outputDirectory.exists() && !outputDirectory.mkdirs()) {
177 getLog().error("Unable to create output directory: " + outputDirectory);
178 }
179 try (Writer writer = new OutputStreamWriter(
180 Files.newOutputStream(new File(outputDirectory, filename).toPath()),
181 getOutputEncoding())) {
182
183 getSiteRenderer().mergeDocumentIntoSite(writer, sink, siteContext);
184 }
185
186 }
187
188
189 getSiteRenderer().copyResources(siteContext, outputDirectory);
190 } catch (RendererException | IOException | MavenReportException e) {
191 throw new MojoExecutionException(
192 "An error has occurred in " + getName(Locale.ENGLISH) + " report generation.", e);
193 }
194 }
195
196 private SiteRenderingContext createSiteRenderingContext(final Locale locale)
197 throws MavenReportException, IOException {
198 SiteModel siteModel = new SiteModel();
199
200 Map<String, Object> templateProperties = new HashMap<>();
201 templateProperties.put("standalone", Boolean.TRUE);
202 templateProperties.put("project", getProject());
203 templateProperties.put("inputEncoding", getInputEncoding());
204 templateProperties.put("outputEncoding", getOutputEncoding());
205 for (Map.Entry<Object, Object> entry : getProject().getProperties().entrySet()) {
206 templateProperties.put((String) entry.getKey(), entry.getValue());
207 }
208
209 org.apache.maven.doxia.site.Skin siteSkin = siteModel.getSkin();
210
211 if (siteSkin == null || siteSkin.getGroupId() == null
212 || siteSkin.getArtifactId() == null || siteSkin.getVersion() == null) {
213 getLog().debug("No skin configuration found in site.xml. Using default Maven skin configuration.");
214
215
216 siteSkin = new org.apache.maven.doxia.site.Skin();
217 siteSkin.setGroupId("org.apache.maven.skins");
218 siteSkin.setArtifactId("maven-fluido-skin");
219 }
220
221 try {
222 Artifact skinArtifact = siteTool.getSkinArtifactFromRepository(
223 session.getRepositorySession(),
224 remoteRepositories,
225 siteSkin
226 );
227
228 getLog().debug(buffer().a("Rendering content with ").strong(skinArtifact.getId() + " skin").a('.').build());
229
230 SiteRenderingContext context = siteRenderer.createContextForSkin(
231 skinArtifact,
232 templateProperties,
233 siteModel,
234 project.getName(),
235 locale
236 );
237 context.setRootDirectory(project.getBasedir());
238 return context;
239
240 } catch (SiteToolException e) {
241 throw new MavenReportException("Failed to retrieve skin artifact", e);
242 } catch (RendererException e) {
243 throw new MavenReportException("Failed to create context for skin", e);
244 }
245 }
246
247
248
249
250
251
252
253
254
255
256 @Override
257 public void generate(final Sink sink, final SinkFactory sinkFactory, final Locale locale) throws MavenReportException {
258 if (!canGenerateReport()) {
259
260 getLog().info("This report cannot be generated as part of the current build. "
261 + "The report name should be referenced in this line of output.");
262 } else {
263 this.sink = sink;
264 this.sinkFactory = sinkFactory;
265
266 if (!(sink instanceof SiteRendererSink)) {
267
268 generateReportManually(locale);
269 } else {
270 executeReport(locale);
271 }
272
273 closeReport();
274 }
275 }
276
277 private void generateReportManually(final Locale locale) throws MavenReportException {
278 try {
279 File outputDir = new File(getOutputDirectory());
280 String filename = getOutputName() + ".html";
281
282 SiteRenderingContext siteContext = createSiteRenderingContext(locale);
283
284 if (!outputDir.exists() && !outputDir.mkdirs()) {
285 getLog().error("Unable to create output directory: " + outputDir);
286 }
287
288 DocumentRenderingContext docContext =
289 new DocumentRenderingContext(outputDir, filename, "xhtml5");
290
291 SiteRendererSink sink = new SiteRendererSink(docContext);
292
293 this.sink = sink;
294 this.sinkFactory = null;
295
296 executeReport(locale);
297
298 try (Writer writer = new OutputStreamWriter(
299 Files.newOutputStream(new File(outputDir, filename).toPath()),
300 getOutputEncoding())) {
301 getSiteRenderer().mergeDocumentIntoSite(writer, sink, siteContext);
302 }
303
304 } catch (IOException | RendererException e) {
305 throw new MavenReportException("Failed to render RAT report manually", e);
306 }
307 }
308
309
310
311
312
313
314
315
316
317 @Deprecated
318 public void generate(final Sink sink, final Locale locale) throws MavenReportException {
319 generate(sink, null, locale);
320 }
321
322
323
324
325 @Override
326 public String getCategoryName() {
327 return CATEGORY_PROJECT_REPORTS;
328 }
329
330 @Override
331 public File getReportOutputDirectory() {
332 if (reportOutputDirectory == null) {
333 reportOutputDirectory = new File(getOutputDirectory());
334 }
335
336 return reportOutputDirectory;
337 }
338
339 @Override
340 public void setReportOutputDirectory(final File reportOutputDirectory) {
341 this.reportOutputDirectory = reportOutputDirectory;
342 this.outputDirectory = reportOutputDirectory;
343 }
344
345 protected String getOutputDirectory() {
346 return outputDirectory.getAbsolutePath();
347 }
348
349 protected Renderer getSiteRenderer() {
350 return siteRenderer;
351 }
352
353
354
355
356
357
358 protected String getInputEncoding() {
359 return (inputEncoding == null) ? ReaderFactory.FILE_ENCODING : inputEncoding;
360 }
361
362
363
364
365
366
367
368 protected String getOutputEncoding() {
369 return (outputEncoding == null) ? StandardCharsets.UTF_8.toString() : outputEncoding;
370 }
371
372
373
374
375 protected void closeReport() {
376 getSink().close();
377 }
378
379
380
381
382 public Sink getSink() {
383 return sink;
384 }
385
386
387
388
389 public SinkFactory getSinkFactory() {
390 return sinkFactory;
391 }
392
393
394
395
396
397 @Override
398 public boolean isExternalReport() {
399 return false;
400 }
401
402 @Override
403 public boolean canGenerateReport() {
404 return !skip;
405 }
406
407
408
409
410
411
412
413 protected void executeReport(final Locale locale) throws MavenReportException {
414 ResourceBundle bundle = getBundle(locale);
415 final String title = bundle.getString("report.rat.title");
416 sink.head();
417 sink.title();
418 sink.text(title);
419 sink.title_();
420 sink.head_();
421
422 sink.body();
423
424 sink.section1();
425 sink.sectionTitle1();
426 sink.text(title);
427 sink.sectionTitle1_();
428
429 sink.paragraph();
430 sink.text(bundle.getString("report.rat.link") + " ");
431 sink.link(bundle.getString("report.rat.url"));
432 sink.text(bundle.getString("report.rat.fullName"));
433 sink.link_();
434 sink.text(" " + new VersionInfo(RatReportMojo.class));
435 sink.text(".");
436 sink.paragraph_();
437
438 sink.paragraph();
439 sink.verbatim(new SinkEventAttributeSet());
440 try (Writer logWriter = DefaultLog.getInstance().asWriter()) {
441 try {
442 ReportConfiguration config = getConfiguration();
443 config.setFrom(getDefaultsBuilder().build());
444 logLicenses(config.getLicenses(LicenseFilter.ALL));
445 if (verbose) {
446 config.reportExclusions(logWriter);
447 }
448 ByteArrayOutputStream baos = new ByteArrayOutputStream();
449 config.setOut(() -> baos);
450 Reporter reporter = new Reporter(config);
451 reporter.output();
452 if (verbose) {
453 reporter.writeSummary(logWriter);
454 }
455 sink.text(baos.toString(StandardCharsets.UTF_8.name()));
456 } catch (IOException | MojoExecutionException | RatException e) {
457 throw new MavenReportException(e.getMessage(), e);
458 }
459 } catch (IOException e) {
460 DefaultLog.getInstance().warn("Unable to close log writer", e);
461 }
462 sink.verbatim_();
463 sink.paragraph_();
464 sink.section1_();
465 sink.body_();
466 }
467
468
469
470
471
472
473
474 private ResourceBundle getBundle(final Locale locale) {
475 return ResourceBundle.getBundle("org/apache/rat/mp/rat-report", locale, getClass().getClassLoader());
476 }
477
478
479
480
481
482
483
484
485 @Override
486 public String getDescription(final Locale locale) {
487 return getBundle(locale).getString("report.rat.description");
488 }
489
490
491
492
493
494
495
496 @Override
497 public String getName(final Locale locale) {
498 return getBundle(locale).getString("report.rat.name");
499 }
500
501
502
503
504
505
506 @Override
507 public String getOutputName() {
508 return "rat-report";
509 }
510 }