View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.site.render;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.List;
26  import java.util.Locale;
27  import java.util.Map;
28  import java.util.TreeMap;
29  
30  import org.apache.maven.doxia.siterenderer.DocumentRenderer;
31  import org.apache.maven.doxia.siterenderer.DoxiaDocumentRenderer;
32  import org.apache.maven.doxia.siterenderer.RendererException;
33  import org.apache.maven.doxia.siterenderer.SiteRenderingContext;
34  import org.apache.maven.doxia.tools.SiteTool;
35  import org.apache.maven.execution.MavenSession;
36  import org.apache.maven.plugin.MojoExecutionException;
37  import org.apache.maven.plugin.MojoFailureException;
38  import org.apache.maven.plugins.annotations.Mojo;
39  import org.apache.maven.plugins.annotations.Parameter;
40  import org.apache.maven.plugins.annotations.ResolutionScope;
41  import org.apache.maven.project.MavenProject;
42  import org.apache.maven.reporting.MavenReport;
43  import org.apache.maven.reporting.MavenReportException;
44  import org.apache.maven.reporting.exec.MavenReportExecution;
45  import org.apache.maven.shared.utils.logging.MessageBuilder;
46  
47  import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
48  
49  /**
50   * Generates the site for a single project.
51   * <p>
52   * Note that links between module sites in a multi module build will <b>not</b> work, since local build directory
53   * structure doesn't match deployed site.
54   * </p>
55   *
56   * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
57   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
58   *
59   */
60  @Mojo(name = "site", requiresDependencyResolution = ResolutionScope.TEST, requiresReports = true, threadSafe = true)
61  public class SiteMojo extends AbstractSiteRenderingMojo {
62      /**
63       * Directory where the project sites and report distributions will be generated (as html/css/...).
64       */
65      @Parameter(property = "siteOutputDirectory", defaultValue = "${project.reporting.outputDirectory}")
66      protected File outputDirectory;
67  
68      /**
69       * Convenience parameter that allows you to disable report generation.
70       */
71      @Parameter(property = "generateReports", defaultValue = "true")
72      private boolean generateReports;
73  
74      /**
75       * Whether to validate xml input documents. If set to true, <strong>all</strong> input documents in xml format (in
76       * particular xdoc and fml) will be validated and any error will lead to a build failure.
77       *
78       * @since 2.1.1
79       */
80      @Parameter(property = "validate", defaultValue = "false")
81      private boolean validate;
82  
83      /**
84       * {@inheritDoc}
85       */
86      public void execute() throws MojoExecutionException, MojoFailureException {
87          if (skip) {
88              getLog().info("maven.site.skip = true: Skipping site generation");
89              return;
90          }
91  
92          if (getLog().isDebugEnabled()) {
93              getLog().debug("executing Site Mojo");
94          }
95  
96          checkInputEncoding();
97  
98          List<MavenReportExecution> reports;
99          if (generateReports) {
100             reports = getReports();
101         } else {
102             reports = Collections.emptyList();
103         }
104 
105         try {
106             List<Locale> localesList = getLocales();
107 
108             for (Locale locale : localesList) {
109                 getLog().info("Rendering site for "
110                         + buffer().strong(
111                                         (!locale.equals(SiteTool.DEFAULT_LOCALE)
112                                                 ? "locale '" + locale + "'"
113                                                 : "default locale"))
114                                 .toString());
115                 renderLocale(locale, reports, localesList);
116             }
117         } catch (RendererException e) {
118             if (e.getCause() instanceof MavenReportException) {
119                 // issue caused by report, not really by Doxia Site Renderer
120                 throw new MojoExecutionException(e.getMessage(), e.getCause());
121             }
122             throw new MojoExecutionException(e.getMessage(), e);
123         } catch (IOException e) {
124             throw new MojoExecutionException("Error during site generation", e);
125         }
126     }
127 
128     private void renderLocale(Locale locale, List<MavenReportExecution> reports, List<Locale> supportedLocales)
129             throws IOException, RendererException, MojoFailureException, MojoExecutionException {
130         SiteRenderingContext context = createSiteRenderingContext(locale);
131         context.addSiteLocales(supportedLocales);
132         if (!locale.equals(SiteTool.DEFAULT_LOCALE)) {
133             context.addSiteDirectory(new File(generatedSiteDirectory, locale.toString()));
134         } else {
135             context.addSiteDirectory(generatedSiteDirectory);
136         }
137 
138         context.setInputEncoding(getInputEncoding());
139         context.setOutputEncoding(getOutputEncoding());
140         context.setValidate(validate);
141         if (validate) {
142             getLog().info("Validation is switched on, xml input documents will be validated!");
143         }
144 
145         File outputDir = getOutputDirectory(locale);
146 
147         Map<String, DocumentRenderer> documents = locateDocuments(context, reports, locale);
148 
149         // copy resources
150         siteRenderer.copyResources(context, outputDir);
151 
152         // 1. render Doxia documents first
153         List<DocumentRenderer> nonDoxiaDocuments = renderDoxiaDocuments(documents, context, outputDir, false);
154 
155         // prepare external reports
156         for (MavenReportExecution mavenReportExecution : reports) {
157             MavenReport report = mavenReportExecution.getMavenReport();
158             report.setReportOutputDirectory(outputDir);
159         }
160 
161         // 2. then non-Doxia documents (e.g., reports)
162         renderNonDoxiaDocuments(nonDoxiaDocuments, context, outputDir);
163 
164         // 3. Generated docs must be (re-)done afterwards as they are often generated by reports
165         context.getSiteDirectories().clear();
166         if (!locale.equals(SiteTool.DEFAULT_LOCALE)) {
167             context.addSiteDirectory(new File(generatedSiteDirectory, locale.toString()));
168         } else {
169             context.addSiteDirectory(generatedSiteDirectory);
170         }
171 
172         Map<String, DocumentRenderer> generatedDocuments =
173                 siteRenderer.locateDocumentFiles(context, false /* not editable */);
174 
175         renderDoxiaDocuments(generatedDocuments, context, outputDir, true);
176 
177         // copy generated resources also
178         siteRenderer.copyResources(context, outputDir);
179     }
180 
181     /**
182      * Render Doxia documents from the list given, but not reports.
183      *
184      * @param documents a collection of documents containing both Doxia source files and reports
185      * @return the sublist of documents that are not Doxia source files
186      */
187     private List<DocumentRenderer> renderDoxiaDocuments(
188             Map<String, DocumentRenderer> documents, SiteRenderingContext context, File outputDir, boolean generated)
189             throws RendererException, IOException {
190         Map<String, DocumentRenderer> doxiaDocuments = new TreeMap<>();
191         List<DocumentRenderer> nonDoxiaDocuments = new ArrayList<>();
192 
193         Map<String, Integer> counts = new TreeMap<>();
194 
195         for (Map.Entry<String, DocumentRenderer> entry : documents.entrySet()) {
196             DocumentRenderer doc = entry.getValue();
197 
198             if (doc instanceof DoxiaDocumentRenderer) {
199                 doxiaDocuments.put(entry.getKey(), doc);
200 
201                 DoxiaDocumentRenderer doxia = (DoxiaDocumentRenderer) doc;
202 
203                 // count documents per parserId
204                 String parserId = doxia.getRenderingContext().getParserId();
205                 Integer count = counts.get(parserId);
206                 if (count == null) {
207                     count = 1;
208                 } else {
209                     count++;
210                 }
211                 counts.put(parserId, count);
212             } else {
213                 nonDoxiaDocuments.add(doc);
214             }
215         }
216 
217         if (doxiaDocuments.size() > 0) {
218             MessageBuilder mb = buffer();
219             mb.a("Rendering ");
220             mb.strong(doxiaDocuments.size() + (generated ? " generated" : "") + " Doxia document"
221                     + (doxiaDocuments.size() > 1 ? "s" : ""));
222             mb.a(": ");
223 
224             boolean first = true;
225             for (Map.Entry<String, Integer> entry : counts.entrySet()) {
226                 if (first) {
227                     first = false;
228                 } else {
229                     mb.a(", ");
230                 }
231                 mb.strong(entry.getValue() + " " + entry.getKey());
232             }
233 
234             getLog().info(mb.toString());
235 
236             siteRenderer.render(doxiaDocuments.values(), context, outputDir);
237         }
238 
239         return nonDoxiaDocuments;
240     }
241 
242     /**
243      * Render non-Doxia documents (e.g., reports) from the list given
244      *
245      * @param documents a collection of documents containing non-Doxia source files
246      */
247     private void renderNonDoxiaDocuments(List<DocumentRenderer> documents, SiteRenderingContext context, File outputDir)
248             throws RendererException, IOException {
249         Map<String, Integer> counts = new TreeMap<>();
250 
251         for (DocumentRenderer doc : documents) {
252             String type;
253             if (doc instanceof ReportDocumentRenderer || doc instanceof SitePluginReportDocumentRenderer) {
254                 type = "report";
255             } else {
256                 type = "other";
257             }
258 
259             Integer count = counts.get(type);
260             if (count == null) {
261                 count = 1;
262             } else {
263                 count++;
264             }
265             counts.put(type, count);
266         }
267 
268         if (documents.size() > 0) {
269             for (Map.Entry<String, Integer> entry : counts.entrySet()) {
270                 String type = entry.getKey();
271                 Integer count = entry.getValue();
272 
273                 MessageBuilder mb = buffer();
274                 mb.a("Rendering ");
275                 mb.strong(count + " " + type + " document" + (count > 1 ? "s" : ""));
276 
277                 getLog().info(mb.toString());
278             }
279 
280             siteRenderer.render(documents, context, outputDir);
281         }
282     }
283 
284     private File getOutputDirectory(Locale locale) {
285         File file;
286         if (!locale.equals(SiteTool.DEFAULT_LOCALE)) {
287             file = new File(outputDirectory, locale.toString());
288         } else {
289             file = outputDirectory;
290         }
291 
292         // Safety
293         if (!file.exists()) {
294             file.mkdirs();
295         }
296 
297         return file;
298     }
299 
300     public MavenProject getProject() {
301         return project;
302     }
303 
304     public MavenSession getSession() {
305         return mavenSession;
306     }
307 }