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