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.report.projectinfo;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.net.MalformedURLException;
24  import java.util.List;
25  import java.util.Locale;
26  
27  import org.apache.maven.artifact.repository.ArtifactRepository;
28  import org.apache.maven.doxia.sink.Sink;
29  import org.apache.maven.doxia.tools.SiteTool;
30  import org.apache.maven.model.DistributionManagement;
31  import org.apache.maven.model.Site;
32  import org.apache.maven.plugin.logging.Log;
33  import org.apache.maven.plugins.annotations.Mojo;
34  import org.apache.maven.project.DefaultProjectBuildingRequest;
35  import org.apache.maven.project.MavenProject;
36  import org.apache.maven.project.ProjectBuilder;
37  import org.apache.maven.project.ProjectBuildingException;
38  import org.apache.maven.project.ProjectBuildingRequest;
39  import org.apache.maven.reporting.MavenReportException;
40  import org.codehaus.plexus.i18n.I18N;
41  
42  /**
43   * Generates the Project Modules report.
44   *
45   * @author ltheussl
46   * @since 2.2
47   */
48  @Mojo(name = "modules")
49  public class ModulesReport extends AbstractProjectInfoReport {
50      // ----------------------------------------------------------------------
51      // Public methods
52      // ----------------------------------------------------------------------
53  
54      @Override
55      public boolean canGenerateReport() throws MavenReportException {
56          boolean result = super.canGenerateReport();
57          if (result && skipEmptyReport) {
58              result = !isEmpty(getProject().getModel().getModules());
59          }
60  
61          return result;
62      }
63  
64      @Override
65      public void executeReport(Locale locale) {
66          new ModulesRenderer(
67                          getSink(),
68                          getProject(),
69                          getReactorProjects(),
70                          projectBuilder,
71                          getSession().getProjectBuildingRequest(),
72                          localRepository,
73                          getI18N(locale),
74                          locale,
75                          getLog(),
76                          siteTool)
77                  .render();
78      }
79  
80      /** {@inheritDoc} */
81      public String getOutputName() {
82          return "modules";
83      }
84  
85      @Override
86      protected String getI18Nsection() {
87          return "modules";
88      }
89  
90      // ----------------------------------------------------------------------
91      // Private
92      // ----------------------------------------------------------------------
93  
94      /**
95       * Internal renderer class
96       */
97      static class ModulesRenderer extends AbstractProjectInfoRenderer {
98  
99          protected final Log log;
100 
101         protected MavenProject project;
102 
103         protected List<MavenProject> reactorProjects;
104 
105         protected ProjectBuilder projectBuilder;
106 
107         protected ProjectBuildingRequest buildingRequest;
108 
109         protected ArtifactRepository localRepository;
110 
111         protected SiteTool siteTool;
112 
113         ModulesRenderer(
114                 Sink sink,
115                 MavenProject project,
116                 List<MavenProject> reactorProjects,
117                 ProjectBuilder projectBuilder,
118                 ProjectBuildingRequest buildingRequest,
119                 ArtifactRepository localRepository,
120                 I18N i18n,
121                 Locale locale,
122                 Log log,
123                 SiteTool siteTool) {
124             super(sink, i18n, locale);
125 
126             this.project = project;
127             this.reactorProjects = reactorProjects;
128             this.projectBuilder = projectBuilder;
129             this.buildingRequest = buildingRequest;
130             this.localRepository = localRepository;
131             this.siteTool = siteTool;
132             this.log = log;
133         }
134 
135         @Override
136         protected String getI18Nsection() {
137             return "modules";
138         }
139 
140         @Override
141         protected void renderBody() {
142             List<String> modules = project.getModel().getModules();
143 
144             if (modules == null || modules.isEmpty()) {
145                 startSection(getTitle());
146 
147                 paragraph(getI18nString("nolist"));
148 
149                 endSection();
150 
151                 return;
152             }
153 
154             startSection(getTitle());
155 
156             paragraph(getI18nString("intro"));
157 
158             startTable();
159 
160             String name = getI18nString("header.name");
161             String description = getI18nString("header.description");
162             tableHeader(new String[] {name, description});
163 
164             final String baseUrl = getDistMgmntSiteUrl(project);
165 
166             ProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest(this.buildingRequest);
167             buildingRequest.setLocalRepository(localRepository);
168             buildingRequest.setProcessPlugins(false);
169 
170             for (String module : modules) {
171                 MavenProject moduleProject = getModuleFromReactor(project, reactorProjects, module);
172 
173                 if (moduleProject == null) {
174                     log.warn("Module " + module + " not found in reactor: loading locally");
175 
176                     File f = new File(project.getBasedir(), module + "/pom.xml");
177                     if (f.exists()) {
178                         try {
179                             moduleProject =
180                                     projectBuilder.build(f, buildingRequest).getProject();
181                         } catch (ProjectBuildingException e) {
182                             throw new IllegalStateException("Unable to read local module POM", e);
183                         }
184                     } else {
185                         moduleProject = new MavenProject();
186                         moduleProject.setName(module);
187                         moduleProject.setDistributionManagement(new DistributionManagement());
188                         moduleProject.getDistributionManagement().setSite(new Site());
189                         moduleProject.getDistributionManagement().getSite().setUrl(module);
190                     }
191                 }
192                 final String moduleName =
193                         (moduleProject.getName() == null) ? moduleProject.getArtifactId() : moduleProject.getName();
194                 final String moduleHref =
195                         getRelativeLink(baseUrl, getDistMgmntSiteUrl(moduleProject), moduleProject.getArtifactId());
196 
197                 tableRow(new String[] {linkedName(moduleName, moduleHref), moduleProject.getDescription()});
198             }
199 
200             endTable();
201 
202             endSection();
203         }
204 
205         private MavenProject getModuleFromReactor(
206                 MavenProject project, List<MavenProject> reactorProjects, String module) {
207             // Mainly case of unit test
208             if (reactorProjects == null) {
209                 return null;
210             }
211             try {
212                 File moduleBasedir = new File(project.getBasedir(), module).getCanonicalFile();
213 
214                 for (MavenProject reactorProject : reactorProjects) {
215                     if (moduleBasedir.equals(reactorProject.getBasedir())) {
216                         return reactorProject;
217                     }
218                 }
219             } catch (IOException e) {
220                 log.error("Error while populating modules menu: " + e.getMessage(), e);
221             }
222             // module not found in reactor
223             return null;
224         }
225 
226         /**
227          * Return distributionManagement.site.url if defined, null otherwise.
228          *
229          * @param project not null
230          * @return could be null
231          */
232         private static String getDistMgmntSiteUrl(MavenProject project) {
233             return getDistMgmntSiteUrl(project.getDistributionManagement());
234         }
235 
236         private static String getDistMgmntSiteUrl(DistributionManagement distMgmnt) {
237             if (distMgmnt != null
238                     && distMgmnt.getSite() != null
239                     && distMgmnt.getSite().getUrl() != null) {
240                 return urlEncode(distMgmnt.getSite().getUrl());
241             }
242 
243             return null;
244         }
245 
246         private static String urlEncode(final String url) {
247             if (url == null) {
248                 return null;
249             }
250 
251             try {
252                 return new File(url).toURI().toURL().toExternalForm();
253             } catch (MalformedURLException ex) {
254                 return url; // this will then throw somewhere else
255             }
256         }
257 
258         // adapted from DefaultSiteTool#appendMenuItem
259         private String getRelativeLink(String baseUrl, String href, String defaultHref) {
260             String selectedHref = href;
261 
262             if (selectedHref == null) {
263                 selectedHref = defaultHref;
264             }
265 
266             if (baseUrl != null) {
267                 selectedHref = siteTool.getRelativePath(selectedHref, baseUrl);
268             }
269 
270             if (selectedHref.endsWith("/")) {
271                 selectedHref = selectedHref.concat("index.html");
272             } else {
273                 selectedHref = selectedHref.concat("/index.html");
274             }
275 
276             return selectedHref;
277         }
278 
279         private String linkedName(String name, String link) {
280             return "{" + name + ", ./" + link + "}";
281         }
282     }
283 }