1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.doxia.tools;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.io.File;
26 import java.io.IOException;
27 import java.io.Reader;
28 import java.io.StringReader;
29 import java.io.StringWriter;
30 import java.net.MalformedURLException;
31 import java.net.URL;
32 import java.util.AbstractMap;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.List;
37 import java.util.Locale;
38 import java.util.Map;
39 import java.util.Objects;
40 import java.util.StringTokenizer;
41
42 import org.apache.commons.io.FilenameUtils;
43 import org.apache.maven.RepositoryUtils;
44 import org.apache.maven.artifact.Artifact;
45 import org.apache.maven.artifact.DefaultArtifact;
46 import org.apache.maven.artifact.handler.ArtifactHandler;
47 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
48 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
49 import org.apache.maven.artifact.versioning.VersionRange;
50 import org.apache.maven.doxia.site.Menu;
51 import org.apache.maven.doxia.site.MenuItem;
52 import org.apache.maven.doxia.site.SiteModel;
53 import org.apache.maven.doxia.site.Skin;
54 import org.apache.maven.doxia.site.inheritance.SiteModelInheritanceAssembler;
55 import org.apache.maven.doxia.site.io.xpp3.SiteXpp3Reader;
56 import org.apache.maven.doxia.site.io.xpp3.SiteXpp3Writer;
57 import org.apache.maven.model.DistributionManagement;
58 import org.apache.maven.model.Plugin;
59 import org.apache.maven.project.MavenProject;
60 import org.apache.maven.project.ProjectBuilder;
61 import org.apache.maven.reporting.MavenReport;
62 import org.codehaus.plexus.i18n.I18N;
63 import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
64 import org.codehaus.plexus.interpolation.InterpolationException;
65 import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
66 import org.codehaus.plexus.interpolation.MapBasedValueSource;
67 import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
68 import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource;
69 import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
70 import org.codehaus.plexus.util.IOUtil;
71 import org.codehaus.plexus.util.ReaderFactory;
72 import org.codehaus.plexus.util.StringUtils;
73 import org.codehaus.plexus.util.xml.Xpp3Dom;
74 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
75 import org.eclipse.aether.RepositorySystem;
76 import org.eclipse.aether.RepositorySystemSession;
77 import org.eclipse.aether.repository.RemoteRepository;
78 import org.eclipse.aether.resolution.ArtifactRequest;
79 import org.eclipse.aether.resolution.ArtifactResolutionException;
80 import org.eclipse.aether.resolution.ArtifactResult;
81 import org.eclipse.aether.transfer.ArtifactNotFoundException;
82 import org.slf4j.Logger;
83 import org.slf4j.LoggerFactory;
84
85
86
87
88
89
90 @Singleton
91 @Named
92 public class DefaultSiteTool implements SiteTool {
93 private static final Logger LOGGER = LoggerFactory.getLogger(DefaultSiteTool.class);
94
95
96
97
98
99
100
101
102 @Inject
103 protected RepositorySystem repositorySystem;
104
105
106
107
108 @Inject
109 private ArtifactHandlerManager artifactHandlerManager;
110
111
112
113
114 @Inject
115 protected I18N i18n;
116
117
118
119
120 @Inject
121 protected SiteModelInheritanceAssembler assembler;
122
123
124
125
126 @Inject
127 protected ProjectBuilder projectBuilder;
128
129
130
131
132
133
134 public Artifact getSkinArtifactFromRepository(
135 RepositorySystemSession repoSession, List<RemoteRepository> remoteProjectRepositories, Skin skin)
136 throws SiteToolException {
137 Objects.requireNonNull(repoSession, "repoSession cannot be null");
138 Objects.requireNonNull(remoteProjectRepositories, "remoteProjectRepositories cannot be null");
139 Objects.requireNonNull(skin, "skin cannot be null");
140
141 String version = skin.getVersion();
142 try {
143 if (version == null) {
144 version = Artifact.RELEASE_VERSION;
145 }
146 VersionRange versionSpec = VersionRange.createFromVersionSpec(version);
147 String type = "jar";
148 Artifact artifact = new DefaultArtifact(
149 skin.getGroupId(),
150 skin.getArtifactId(),
151 versionSpec,
152 Artifact.SCOPE_RUNTIME,
153 type,
154 null,
155 artifactHandlerManager.getArtifactHandler(type));
156 ArtifactRequest request =
157 new ArtifactRequest(RepositoryUtils.toArtifact(artifact), remoteProjectRepositories, "remote-skin");
158 ArtifactResult result = repositorySystem.resolveArtifact(repoSession, request);
159
160 return RepositoryUtils.toArtifact(result.getArtifact());
161 } catch (InvalidVersionSpecificationException e) {
162 throw new SiteToolException("The skin version '" + version + "' is not valid", e);
163 } catch (ArtifactResolutionException e) {
164 if (e.getCause() instanceof ArtifactNotFoundException) {
165 throw new SiteToolException("The skin does not exist", e.getCause());
166 }
167
168 throw new SiteToolException("Unable to find skin", e);
169 }
170 }
171
172
173
174
175
176
177 @Deprecated
178 public String getRelativePath(String to, String from) {
179 Objects.requireNonNull(to, "to cannot be null");
180 Objects.requireNonNull(from, "from cannot be null");
181
182 if (to.contains(":") && from.contains(":")) {
183 String toScheme = to.substring(0, to.lastIndexOf(':'));
184 String fromScheme = from.substring(0, from.lastIndexOf(':'));
185 if (!toScheme.equals(fromScheme)) {
186 return to;
187 }
188 }
189
190 URL toUrl = null;
191 URL fromUrl = null;
192
193 String toPath = to;
194 String fromPath = from;
195
196 try {
197 toUrl = new URL(to);
198 } catch (MalformedURLException e) {
199 try {
200 toUrl = new File(getNormalizedPath(to)).toURI().toURL();
201 } catch (MalformedURLException e1) {
202 LOGGER.warn("Unable to load a URL for '" + to + "'", e);
203 return to;
204 }
205 }
206
207 try {
208 fromUrl = new URL(from);
209 } catch (MalformedURLException e) {
210 try {
211 fromUrl = new File(getNormalizedPath(from)).toURI().toURL();
212 } catch (MalformedURLException e1) {
213 LOGGER.warn("Unable to load a URL for '" + from + "'", e);
214 return to;
215 }
216 }
217
218 if (toUrl != null && fromUrl != null) {
219
220
221 if ((toUrl.getProtocol().equalsIgnoreCase(fromUrl.getProtocol()))
222 && (toUrl.getHost().equalsIgnoreCase(fromUrl.getHost()))
223 && (toUrl.getPort() == fromUrl.getPort())) {
224
225
226 toPath = toUrl.getFile();
227 fromPath = fromUrl.getFile();
228 } else {
229
230
231 return to;
232 }
233 } else if ((toUrl != null && fromUrl == null) || (toUrl == null && fromUrl != null)) {
234
235
236 return to;
237 }
238
239
240
241
242
243 String relativePath = getRelativeFilePath(fromPath, toPath);
244
245 if (relativePath == null) {
246 relativePath = to;
247 }
248
249 if (LOGGER.isDebugEnabled() && !relativePath.toString().equals(to)) {
250 LOGGER.debug("Mapped url: " + to + " to relative path: " + relativePath);
251 }
252
253 return relativePath;
254 }
255
256 private static String getRelativeFilePath(final String oldPath, final String newPath) {
257
258
259 String fromPath = new File(oldPath).getPath();
260 String toPath = new File(newPath).getPath();
261
262
263 if (toPath.matches("^\\[a-zA-Z]:")) {
264 toPath = toPath.substring(1);
265 }
266 if (fromPath.matches("^\\[a-zA-Z]:")) {
267 fromPath = fromPath.substring(1);
268 }
269
270
271 if (fromPath.startsWith(":", 1)) {
272 fromPath = Character.toLowerCase(fromPath.charAt(0)) + fromPath.substring(1);
273 }
274 if (toPath.startsWith(":", 1)) {
275 toPath = Character.toLowerCase(toPath.charAt(0)) + toPath.substring(1);
276 }
277
278
279
280
281 if ((toPath.startsWith(":", 1) && fromPath.startsWith(":", 1))
282 && (!toPath.substring(0, 1).equals(fromPath.substring(0, 1)))) {
283
284
285
286 return null;
287 }
288
289 if ((toPath.startsWith(":", 1) && !fromPath.startsWith(":", 1))
290 || (!toPath.startsWith(":", 1) && fromPath.startsWith(":", 1))) {
291
292
293
294
295 return null;
296 }
297
298 final String relativePath = buildRelativePath(toPath, fromPath, File.separatorChar);
299
300 return relativePath.toString();
301 }
302
303
304 public File getSiteDescriptor(File siteDirectory, Locale locale) {
305 Objects.requireNonNull(siteDirectory, "siteDirectory cannot be null");
306 Objects.requireNonNull(locale, "locale cannot be null");
307
308 String variant = locale.getVariant();
309 String country = locale.getCountry();
310 String language = locale.getLanguage();
311
312 File siteDescriptor = null;
313
314 if (!variant.isEmpty()) {
315 siteDescriptor = new File(siteDirectory, "site_" + language + "_" + country + "_" + variant + ".xml");
316 }
317
318 if ((siteDescriptor == null || !siteDescriptor.isFile()) && !country.isEmpty()) {
319 siteDescriptor = new File(siteDirectory, "site_" + language + "_" + country + ".xml");
320 }
321
322 if ((siteDescriptor == null || !siteDescriptor.isFile()) && !language.isEmpty()) {
323 siteDescriptor = new File(siteDirectory, "site_" + language + ".xml");
324 }
325
326 if (siteDescriptor == null || !siteDescriptor.isFile()) {
327 siteDescriptor = new File(siteDirectory, "site.xml");
328 }
329
330 return siteDescriptor;
331 }
332
333
334
335
336
337
338
339
340
341
342
343
344
345 File getSiteDescriptorFromRepository(
346 MavenProject project,
347 RepositorySystemSession repoSession,
348 List<RemoteRepository> remoteProjectRepositories,
349 Locale locale)
350 throws SiteToolException {
351 Objects.requireNonNull(project, "project cannot be null");
352 Objects.requireNonNull(repoSession, "repoSession cannot be null");
353 Objects.requireNonNull(remoteProjectRepositories, "remoteProjectRepositories cannot be null");
354 Objects.requireNonNull(locale, "locale cannot be null");
355
356 try {
357 return resolveSiteDescriptor(project, repoSession, remoteProjectRepositories, locale);
358 } catch (ArtifactNotFoundException e) {
359 LOGGER.debug("Unable to locate site descriptor", e);
360 return null;
361 } catch (ArtifactResolutionException e) {
362 throw new SiteToolException("Unable to locate site descriptor", e);
363 } catch (IOException e) {
364 throw new SiteToolException("Unable to locate site descriptor", e);
365 }
366 }
367
368
369 public SiteModel getSiteModel(
370 File siteDirectory,
371 Locale locale,
372 MavenProject project,
373 List<MavenProject> reactorProjects,
374 RepositorySystemSession repoSession,
375 List<RemoteRepository> remoteProjectRepositories)
376 throws SiteToolException {
377 Objects.requireNonNull(locale, "locale cannot be null");
378 Objects.requireNonNull(project, "project cannot be null");
379 Objects.requireNonNull(reactorProjects, "reactorProjects cannot be null");
380 Objects.requireNonNull(repoSession, "repoSession cannot be null");
381 Objects.requireNonNull(remoteProjectRepositories, "remoteProjectRepositories cannot be null");
382
383 LOGGER.debug("Computing site model of '" + project.getId() + "' for "
384 + (locale.equals(SiteTool.DEFAULT_LOCALE) ? "default locale" : "locale '" + locale + "'"));
385
386 Map.Entry<SiteModel, MavenProject> result =
387 getSiteModel(0, siteDirectory, locale, project, repoSession, remoteProjectRepositories);
388 SiteModel siteModel = result.getKey();
389 MavenProject parentProject = result.getValue();
390
391 if (siteModel == null) {
392 LOGGER.debug("Using default site descriptor");
393 siteModel = getDefaultSiteModel();
394 }
395
396
397 String siteDescriptorContent = siteModelToString(siteModel);
398
399
400 siteDescriptorContent = getInterpolatedSiteDescriptorContent(project, siteDescriptorContent, false);
401
402 siteModel = readSiteModel(siteDescriptorContent);
403
404 if (parentProject != null) {
405 populateParentMenu(siteModel, locale, project, parentProject, true);
406 }
407
408 try {
409 populateModulesMenu(siteModel, locale, project, reactorProjects, true);
410 } catch (IOException e) {
411 throw new SiteToolException("Error while populating modules menu", e);
412 }
413
414 return siteModel;
415 }
416
417
418 public String getInterpolatedSiteDescriptorContent(
419 Map<String, String> props, MavenProject aProject, String siteDescriptorContent) throws SiteToolException {
420 Objects.requireNonNull(props, "props cannot be null");
421
422
423 return getInterpolatedSiteDescriptorContent(aProject, siteDescriptorContent, false);
424 }
425
426 private String getInterpolatedSiteDescriptorContent(
427 MavenProject aProject, String siteDescriptorContent, boolean isEarly) throws SiteToolException {
428 Objects.requireNonNull(aProject, "aProject cannot be null");
429 Objects.requireNonNull(siteDescriptorContent, "siteDescriptorContent cannot be null");
430
431 RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
432
433 if (isEarly) {
434 interpolator.addValueSource(new PrefixedObjectValueSource("this.", aProject));
435 interpolator.addValueSource(new PrefixedPropertiesValueSource("this.", aProject.getProperties()));
436 } else {
437 interpolator.addValueSource(new PrefixedObjectValueSource("project.", aProject));
438 interpolator.addValueSource(new MapBasedValueSource(aProject.getProperties()));
439
440 try {
441 interpolator.addValueSource(new EnvarBasedValueSource());
442 } catch (IOException e) {
443
444 throw new SiteToolException("Cannot interpolate environment properties", e);
445 }
446 }
447
448 interpolator.addPostProcessor(new InterpolationPostProcessor() {
449 @Override
450 public Object execute(String expression, Object value) {
451 if (value != null) {
452
453 return value.toString()
454 .replace("&", "&")
455 .replace("<", "<")
456 .replace(">", ">")
457 .replace("\"", """)
458 .replace("'", "'");
459 }
460 return null;
461 }
462 });
463
464 try {
465 return interpolator.interpolate(siteDescriptorContent);
466 } catch (InterpolationException e) {
467 throw new SiteToolException("Cannot interpolate site descriptor", e);
468 }
469 }
470
471
472
473
474
475
476
477
478
479
480
481 private void populateParentMenu(
482 SiteModel siteModel,
483 Locale locale,
484 MavenProject project,
485 MavenProject parentProject,
486 boolean keepInheritedRefs) {
487 Objects.requireNonNull(siteModel, "siteModel cannot be null");
488 Objects.requireNonNull(locale, "locale cannot be null");
489 Objects.requireNonNull(project, "project cannot be null");
490 Objects.requireNonNull(parentProject, "parentProject cannot be null");
491
492 Menu menu = siteModel.getMenuRef("parent");
493
494 if (menu == null) {
495 return;
496 }
497
498 if (keepInheritedRefs && menu.isInheritAsRef()) {
499 return;
500 }
501
502 String parentUrl = getDistMgmntSiteUrl(parentProject);
503
504 if (parentUrl != null) {
505 if (parentUrl.endsWith("/")) {
506 parentUrl += "index.html";
507 } else {
508 parentUrl += "/index.html";
509 }
510
511 parentUrl = getRelativePath(parentUrl, getDistMgmntSiteUrl(project));
512 } else {
513
514 File parentBasedir = parentProject.getBasedir();
515
516 if (parentBasedir != null) {
517
518 String parentPath = parentBasedir.getAbsolutePath();
519 String projectPath = project.getBasedir().getAbsolutePath();
520 parentUrl = getRelativePath(parentPath, projectPath) + "/index.html";
521 }
522 }
523
524
525 if (parentUrl == null) {
526 LOGGER.warn("Unable to find a URL to the parent project. The parent menu will NOT be added.");
527 } else {
528 if (menu.getName() == null) {
529 menu.setName(i18n.getString("site-tool", locale, "siteModel.menu.parentproject"));
530 }
531
532 MenuItem item = new MenuItem();
533 item.setName(parentProject.getName());
534 item.setHref(parentUrl);
535 menu.addItem(item);
536 }
537 }
538
539
540
541
542
543
544
545
546
547
548
549
550
551 private void populateModulesMenu(
552 SiteModel siteModel,
553 Locale locale,
554 MavenProject project,
555 List<MavenProject> reactorProjects,
556 boolean keepInheritedRefs)
557 throws SiteToolException, IOException {
558 Objects.requireNonNull(siteModel, "siteModel cannot be null");
559 Objects.requireNonNull(locale, "locale cannot be null");
560 Objects.requireNonNull(project, "project cannot be null");
561 Objects.requireNonNull(reactorProjects, "reactorProjects cannot be null");
562
563 Menu menu = siteModel.getMenuRef("modules");
564
565 if (menu == null) {
566 return;
567 }
568
569 if (keepInheritedRefs && menu.isInheritAsRef()) {
570 return;
571 }
572
573
574 if (!project.getModules().isEmpty()) {
575 if (menu.getName() == null) {
576 menu.setName(i18n.getString("site-tool", locale, "siteModel.menu.projectmodules"));
577 }
578
579 for (String module : project.getModules()) {
580 MavenProject moduleProject = getModuleFromReactor(project, reactorProjects, module);
581
582 if (moduleProject == null) {
583 LOGGER.debug("Module " + module + " not found in reactor");
584 continue;
585 }
586
587 final String pluginId = "org.apache.maven.plugins:maven-site-plugin";
588 String skipFlag = getPluginParameter(moduleProject, pluginId, "skip");
589 if (skipFlag == null) {
590 skipFlag = moduleProject.getProperties().getProperty("maven.site.skip");
591 }
592
593 String siteUrl = "true".equalsIgnoreCase(skipFlag) ? null : getDistMgmntSiteUrl(moduleProject);
594 String itemName =
595 (moduleProject.getName() == null) ? moduleProject.getArtifactId() : moduleProject.getName();
596 String defaultSiteUrl = "true".equalsIgnoreCase(skipFlag) ? null : moduleProject.getArtifactId();
597
598 appendMenuItem(project, menu, itemName, siteUrl, defaultSiteUrl);
599 }
600 } else if (siteModel.getMenuRef("modules").getInherit() == null) {
601
602 siteModel.removeMenuRef("modules");
603 }
604 }
605
606 private MavenProject getModuleFromReactor(MavenProject project, List<MavenProject> reactorProjects, String module)
607 throws IOException {
608 File moduleBasedir = new File(project.getBasedir(), module).getCanonicalFile();
609
610 for (MavenProject reactorProject : reactorProjects) {
611 if (moduleBasedir.equals(reactorProject.getBasedir())) {
612 return reactorProject;
613 }
614 }
615
616
617 return null;
618 }
619
620
621 public void populateReportsMenu(SiteModel siteModel, Locale locale, Map<String, List<MavenReport>> categories) {
622 Objects.requireNonNull(siteModel, "siteModel cannot be null");
623 Objects.requireNonNull(locale, "locale cannot be null");
624 Objects.requireNonNull(categories, "categories cannot be null");
625
626 Menu menu = siteModel.getMenuRef("reports");
627
628 if (menu == null) {
629 return;
630 }
631
632 if (menu.getName() == null) {
633 menu.setName(i18n.getString("site-tool", locale, "siteModel.menu.projectdocumentation"));
634 }
635
636 boolean found = false;
637 if (menu.getItems().isEmpty()) {
638 List<MavenReport> categoryReports = categories.get(MavenReport.CATEGORY_PROJECT_INFORMATION);
639 if (!isEmptyList(categoryReports)) {
640 MenuItem item = createCategoryMenu(
641 i18n.getString("site-tool", locale, "siteModel.menu.projectinformation"),
642 "/project-info.html",
643 categoryReports,
644 locale);
645 menu.getItems().add(item);
646 found = true;
647 }
648
649 categoryReports = categories.get(MavenReport.CATEGORY_PROJECT_REPORTS);
650 if (!isEmptyList(categoryReports)) {
651 MenuItem item = createCategoryMenu(
652 i18n.getString("site-tool", locale, "siteModel.menu.projectreports"),
653 "/project-reports.html",
654 categoryReports,
655 locale);
656 menu.getItems().add(item);
657 found = true;
658 }
659 }
660 if (!found) {
661 siteModel.removeMenuRef("reports");
662 }
663 }
664
665
666 public List<Locale> getSiteLocales(String locales) {
667 if (locales == null) {
668 return Collections.singletonList(DEFAULT_LOCALE);
669 }
670
671 String[] localesArray = StringUtils.split(locales, ",");
672 List<Locale> localesList = new ArrayList<Locale>(localesArray.length);
673 List<Locale> availableLocales = Arrays.asList(Locale.getAvailableLocales());
674
675 for (String localeString : localesArray) {
676 Locale locale = codeToLocale(localeString);
677
678 if (locale == null) {
679 continue;
680 }
681
682 if (!availableLocales.contains(locale)) {
683 if (LOGGER.isWarnEnabled()) {
684 LOGGER.warn("The locale defined by '" + locale
685 + "' is not available in this Java Virtual Machine ("
686 + System.getProperty("java.version")
687 + " from " + System.getProperty("java.vendor") + ") - IGNORING");
688 }
689 continue;
690 }
691
692 Locale bundleLocale = i18n.getBundle("site-tool", locale).getLocale();
693 if (!(bundleLocale.equals(locale) || bundleLocale.getLanguage().equals(locale.getLanguage()))) {
694 if (LOGGER.isWarnEnabled()) {
695 LOGGER.warn("The locale '" + locale + "' (" + locale.getDisplayName(Locale.ENGLISH)
696 + ") is not currently supported by Maven Site - IGNORING."
697 + System.lineSeparator() + "Contributions are welcome and greatly appreciated!"
698 + System.lineSeparator() + "If you want to contribute a new translation, please visit "
699 + "https://maven.apache.org/plugins/localization.html for detailed instructions.");
700 }
701
702 continue;
703 }
704
705 localesList.add(locale);
706 }
707
708 if (localesList.isEmpty()) {
709 localesList = Collections.singletonList(DEFAULT_LOCALE);
710 }
711
712 return localesList;
713 }
714
715
716
717
718
719
720
721
722
723
724
725
726
727 private Locale codeToLocale(String localeCode) {
728 if (localeCode == null) {
729 return null;
730 }
731
732 if ("system".equalsIgnoreCase(localeCode)) {
733 return Locale.getDefault();
734 }
735
736 if ("default".equalsIgnoreCase(localeCode)) {
737 return SiteTool.DEFAULT_LOCALE;
738 }
739
740 String language = "";
741 String country = "";
742 String variant = "";
743
744 StringTokenizer tokenizer = new StringTokenizer(localeCode, "_");
745 final int maxTokens = 3;
746 if (tokenizer.countTokens() > maxTokens) {
747 if (LOGGER.isWarnEnabled()) {
748 LOGGER.warn("Invalid java.util.Locale format for '" + localeCode + "' entry - IGNORING");
749 }
750 return null;
751 }
752
753 if (tokenizer.hasMoreTokens()) {
754 language = tokenizer.nextToken();
755 if (tokenizer.hasMoreTokens()) {
756 country = tokenizer.nextToken();
757 if (tokenizer.hasMoreTokens()) {
758 variant = tokenizer.nextToken();
759 }
760 }
761 }
762
763 return new Locale(language, country, variant);
764 }
765
766
767
768
769
770
771
772
773
774
775 protected static String getNormalizedPath(String path) {
776 String normalized = FilenameUtils.normalize(path);
777 if (normalized == null) {
778 normalized = path;
779 }
780 return (normalized == null) ? null : normalized.replace('\\', '/');
781 }
782
783
784
785
786
787
788
789
790
791
792
793 private ArtifactRequest createSiteDescriptorArtifactRequest(
794 MavenProject project, String localeStr, List<RemoteRepository> remoteProjectRepositories) {
795 String type = "xml";
796 ArtifactHandler artifactHandler = artifactHandlerManager.getArtifactHandler(type);
797 Artifact artifact = new DefaultArtifact(
798 project.getGroupId(),
799 project.getArtifactId(),
800 project.getVersion(),
801 Artifact.SCOPE_RUNTIME,
802 type,
803 "site" + (localeStr.isEmpty() ? "" : "_" + localeStr),
804 artifactHandler);
805 return new ArtifactRequest(
806 RepositoryUtils.toArtifact(artifact), remoteProjectRepositories, "remote-site-descriptor");
807 }
808
809
810
811
812
813
814
815
816
817
818
819 private File resolveSiteDescriptor(
820 MavenProject project,
821 RepositorySystemSession repoSession,
822 List<RemoteRepository> remoteProjectRepositories,
823 Locale locale)
824 throws IOException, ArtifactResolutionException, ArtifactNotFoundException {
825 String variant = locale.getVariant();
826 String country = locale.getCountry();
827 String language = locale.getLanguage();
828
829 String localeStr = null;
830 File siteDescriptor = null;
831 boolean found = false;
832
833 if (!variant.isEmpty()) {
834 localeStr = language + "_" + country + "_" + variant;
835 ArtifactRequest request =
836 createSiteDescriptorArtifactRequest(project, localeStr, remoteProjectRepositories);
837
838 try {
839 ArtifactResult result = repositorySystem.resolveArtifact(repoSession, request);
840
841 siteDescriptor = result.getArtifact().getFile();
842 found = true;
843 } catch (ArtifactResolutionException e) {
844 if (e.getCause() instanceof ArtifactNotFoundException) {
845 LOGGER.debug("No site descriptor found for '" + project.getId() + "' for locale '" + localeStr
846 + "', trying without variant...");
847 } else {
848 throw e;
849 }
850 }
851 }
852
853 if (!found && !country.isEmpty()) {
854 localeStr = language + "_" + country;
855 ArtifactRequest request =
856 createSiteDescriptorArtifactRequest(project, localeStr, remoteProjectRepositories);
857
858 try {
859 ArtifactResult result = repositorySystem.resolveArtifact(repoSession, request);
860
861 siteDescriptor = result.getArtifact().getFile();
862 found = true;
863 } catch (ArtifactResolutionException e) {
864 if (e.getCause() instanceof ArtifactNotFoundException) {
865 LOGGER.debug("No site descriptor found for '" + project.getId() + "' for locale '" + localeStr
866 + "', trying without country...");
867 } else {
868 throw e;
869 }
870 }
871 }
872
873 if (!found && !language.isEmpty()) {
874 localeStr = language;
875 ArtifactRequest request =
876 createSiteDescriptorArtifactRequest(project, localeStr, remoteProjectRepositories);
877
878 try {
879 ArtifactResult result = repositorySystem.resolveArtifact(repoSession, request);
880
881 siteDescriptor = result.getArtifact().getFile();
882 found = true;
883 } catch (ArtifactResolutionException e) {
884 if (e.getCause() instanceof ArtifactNotFoundException) {
885 LOGGER.debug("No site descriptor found for '" + project.getId() + "' for locale '" + localeStr
886 + "', trying without language (default locale)...");
887 } else {
888 throw e;
889 }
890 }
891 }
892
893 if (!found) {
894 localeStr = "";
895 ArtifactRequest request =
896 createSiteDescriptorArtifactRequest(project, localeStr, remoteProjectRepositories);
897 try {
898 ArtifactResult result = repositorySystem.resolveArtifact(repoSession, request);
899
900 siteDescriptor = result.getArtifact().getFile();
901 } catch (ArtifactResolutionException e) {
902 if (e.getCause() instanceof ArtifactNotFoundException) {
903 LOGGER.debug("No site descriptor found for '" + project.getId() + "' with default locale.");
904 throw (ArtifactNotFoundException) e.getCause();
905 }
906
907 throw e;
908 }
909 }
910
911 return siteDescriptor;
912 }
913
914
915
916
917
918
919
920
921
922
923
924 private Map.Entry<SiteModel, MavenProject> getSiteModel(
925 int depth,
926 File siteDirectory,
927 Locale locale,
928 MavenProject project,
929 RepositorySystemSession repoSession,
930 List<RemoteRepository> remoteProjectRepositories)
931 throws SiteToolException {
932
933 File siteDescriptor;
934 if (project.getBasedir() == null) {
935
936 try {
937 siteDescriptor =
938 getSiteDescriptorFromRepository(project, repoSession, remoteProjectRepositories, locale);
939 } catch (SiteToolException e) {
940 throw new SiteToolException("The site descriptor cannot be resolved from the repository", e);
941 }
942 } else {
943
944 siteDescriptor = getSiteDescriptor(siteDirectory, locale);
945 }
946
947
948 SiteModel siteModel = null;
949 Reader siteDescriptorReader = null;
950 try {
951 if (siteDescriptor != null && siteDescriptor.exists()) {
952 LOGGER.debug("Reading" + (depth == 0 ? "" : (" parent level " + depth)) + " site descriptor from "
953 + siteDescriptor);
954
955 siteDescriptorReader = ReaderFactory.newXmlReader(siteDescriptor);
956
957 String siteDescriptorContent = IOUtil.toString(siteDescriptorReader);
958
959
960 siteDescriptorContent = getInterpolatedSiteDescriptorContent(project, siteDescriptorContent, true);
961
962 siteModel = readSiteModel(siteDescriptorContent);
963 siteModel.setLastModified(siteDescriptor.lastModified());
964 } else {
965 LOGGER.debug("No" + (depth == 0 ? "" : (" parent level " + depth)) + " site descriptor.");
966 }
967 } catch (IOException e) {
968 throw new SiteToolException(
969 "The site descriptor for '" + project.getId() + "' cannot be read from " + siteDescriptor, e);
970 } finally {
971 IOUtil.close(siteDescriptorReader);
972 }
973
974
975 MavenProject parentProject = project.getParent();
976
977
978 if (parentProject != null && (siteModel == null || siteModel.isMergeParent())) {
979 depth++;
980 LOGGER.debug("Looking for site descriptor of level " + depth + " parent project: " + parentProject.getId());
981
982 File parentSiteDirectory = null;
983 if (parentProject.getBasedir() != null) {
984
985 String siteRelativePath = getRelativeFilePath(
986 project.getBasedir().getAbsolutePath(),
987 siteDescriptor.getParentFile().getAbsolutePath());
988
989 parentSiteDirectory = new File(parentProject.getBasedir(), siteRelativePath);
990
991
992 }
993
994 SiteModel parentSiteModel = getSiteModel(
995 depth, parentSiteDirectory, locale, parentProject, repoSession, remoteProjectRepositories)
996 .getKey();
997
998
999
1000 if (siteModel == null && parentSiteModel != null) {
1001
1002
1003 siteModel = new SiteModel();
1004 }
1005
1006 String name = project.getName();
1007 if (siteModel != null && StringUtils.isNotEmpty(siteModel.getName())) {
1008 name = siteModel.getName();
1009 }
1010
1011
1012 String projectDistMgmnt = getDistMgmntSiteUrl(project);
1013 String parentDistMgmnt = getDistMgmntSiteUrl(parentProject);
1014 if (LOGGER.isDebugEnabled()) {
1015 LOGGER.debug("Site model inheritance: assembling child with level " + depth
1016 + " parent: distributionManagement.site.url child = " + projectDistMgmnt + " and parent = "
1017 + parentDistMgmnt);
1018 }
1019 assembler.assembleModelInheritance(
1020 name,
1021 siteModel,
1022 parentSiteModel,
1023 projectDistMgmnt,
1024 parentDistMgmnt == null ? projectDistMgmnt : parentDistMgmnt);
1025 }
1026
1027 return new AbstractMap.SimpleEntry<SiteModel, MavenProject>(siteModel, parentProject);
1028 }
1029
1030
1031
1032
1033
1034
1035 private SiteModel readSiteModel(String siteDescriptorContent) throws SiteToolException {
1036 try {
1037 return new SiteXpp3Reader().read(new StringReader(siteDescriptorContent));
1038 } catch (XmlPullParserException e) {
1039 throw new SiteToolException("Error parsing site descriptor", e);
1040 } catch (IOException e) {
1041 throw new SiteToolException("Error reading site descriptor", e);
1042 }
1043 }
1044
1045 private SiteModel getDefaultSiteModel() throws SiteToolException {
1046 String siteDescriptorContent;
1047
1048 Reader reader = null;
1049 try {
1050 reader = ReaderFactory.newXmlReader(getClass().getResourceAsStream("/default-site.xml"));
1051 siteDescriptorContent = IOUtil.toString(reader);
1052 } catch (IOException e) {
1053 throw new SiteToolException("Error reading default site descriptor", e);
1054 } finally {
1055 IOUtil.close(reader);
1056 }
1057
1058 return readSiteModel(siteDescriptorContent);
1059 }
1060
1061 private String siteModelToString(SiteModel siteModel) throws SiteToolException {
1062 StringWriter writer = new StringWriter();
1063
1064 try {
1065 new SiteXpp3Writer().write(writer, siteModel);
1066 return writer.toString();
1067 } catch (IOException e) {
1068 throw new SiteToolException("Error reading site descriptor", e);
1069 } finally {
1070 IOUtil.close(writer);
1071 }
1072 }
1073
1074 private static String buildRelativePath(final String toPath, final String fromPath, final char separatorChar) {
1075
1076 StringTokenizer toTokeniser = new StringTokenizer(toPath, String.valueOf(separatorChar));
1077 StringTokenizer fromTokeniser = new StringTokenizer(fromPath, String.valueOf(separatorChar));
1078
1079 int count = 0;
1080
1081
1082 while (toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens()) {
1083 if (separatorChar == '\\') {
1084 if (!fromTokeniser.nextToken().equalsIgnoreCase(toTokeniser.nextToken())) {
1085 break;
1086 }
1087 } else {
1088 if (!fromTokeniser.nextToken().equals(toTokeniser.nextToken())) {
1089 break;
1090 }
1091 }
1092
1093 count++;
1094 }
1095
1096
1097
1098
1099 toTokeniser = new StringTokenizer(toPath, String.valueOf(separatorChar));
1100 fromTokeniser = new StringTokenizer(fromPath, String.valueOf(separatorChar));
1101
1102 while (count-- > 0) {
1103 fromTokeniser.nextToken();
1104 toTokeniser.nextToken();
1105 }
1106
1107 StringBuilder relativePath = new StringBuilder();
1108
1109
1110 while (fromTokeniser.hasMoreTokens()) {
1111 fromTokeniser.nextToken();
1112
1113 relativePath.append("..");
1114
1115 if (fromTokeniser.hasMoreTokens()) {
1116 relativePath.append(separatorChar);
1117 }
1118 }
1119
1120 if (relativePath.length() != 0 && toTokeniser.hasMoreTokens()) {
1121 relativePath.append(separatorChar);
1122 }
1123
1124
1125 while (toTokeniser.hasMoreTokens()) {
1126 relativePath.append(toTokeniser.nextToken());
1127
1128 if (toTokeniser.hasMoreTokens()) {
1129 relativePath.append(separatorChar);
1130 }
1131 }
1132 return relativePath.toString();
1133 }
1134
1135
1136
1137
1138
1139
1140
1141
1142 private void appendMenuItem(MavenProject project, Menu menu, String name, String href, String defaultHref) {
1143 String selectedHref = href;
1144
1145 if (selectedHref == null) {
1146 selectedHref = defaultHref;
1147 }
1148
1149 MenuItem item = new MenuItem();
1150 item.setName(name);
1151
1152 if (selectedHref != null) {
1153 String baseUrl = getDistMgmntSiteUrl(project);
1154 if (baseUrl != null) {
1155 selectedHref = getRelativePath(selectedHref, baseUrl);
1156 }
1157
1158 if (selectedHref.endsWith("/")) {
1159 item.setHref(selectedHref + "index.html");
1160 } else {
1161 item.setHref(selectedHref + "/index.html");
1162 }
1163 }
1164 menu.addItem(item);
1165 }
1166
1167
1168
1169
1170
1171
1172
1173
1174 private MenuItem createCategoryMenu(String name, String href, List<MavenReport> categoryReports, Locale locale) {
1175 MenuItem item = new MenuItem();
1176 item.setName(name);
1177 item.setCollapse(true);
1178 item.setHref(href);
1179
1180
1181
1182
1183 for (MavenReport report : categoryReports) {
1184 MenuItem subitem = new MenuItem();
1185 subitem.setName(report.getName(locale));
1186 subitem.setHref(report.getOutputName() + ".html");
1187 item.getItems().add(subitem);
1188 }
1189
1190 return item;
1191 }
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203 private static boolean isEmptyList(List<?> list) {
1204 return list == null || list.isEmpty();
1205 }
1206
1207
1208
1209
1210
1211
1212
1213 private static String getDistMgmntSiteUrl(MavenProject project) {
1214 return getDistMgmntSiteUrl(project.getDistributionManagement());
1215 }
1216
1217 private static String getDistMgmntSiteUrl(DistributionManagement distMgmnt) {
1218 if (distMgmnt != null
1219 && distMgmnt.getSite() != null
1220 && distMgmnt.getSite().getUrl() != null) {
1221
1222 return urlEncode(distMgmnt.getSite().getUrl());
1223 }
1224
1225 return null;
1226 }
1227
1228
1229
1230
1231
1232
1233 private static Plugin getPlugin(MavenProject project, String pluginId) {
1234 if ((project.getBuild() == null) || (project.getBuild().getPluginsAsMap() == null)) {
1235 return null;
1236 }
1237
1238 Plugin plugin = project.getBuild().getPluginsAsMap().get(pluginId);
1239
1240 if ((plugin == null)
1241 && (project.getBuild().getPluginManagement() != null)
1242 && (project.getBuild().getPluginManagement().getPluginsAsMap() != null)) {
1243 plugin = project.getBuild().getPluginManagement().getPluginsAsMap().get(pluginId);
1244 }
1245
1246 return plugin;
1247 }
1248
1249
1250
1251
1252
1253
1254
1255 private static String getPluginParameter(MavenProject project, String pluginId, String param) {
1256 Plugin plugin = getPlugin(project, pluginId);
1257 if (plugin != null) {
1258 Xpp3Dom xpp3Dom = (Xpp3Dom) plugin.getConfiguration();
1259 if (xpp3Dom != null
1260 && xpp3Dom.getChild(param) != null
1261 && StringUtils.isNotEmpty(xpp3Dom.getChild(param).getValue())) {
1262 return xpp3Dom.getChild(param).getValue();
1263 }
1264 }
1265
1266 return null;
1267 }
1268
1269 private static String urlEncode(final String url) {
1270 if (url == null) {
1271 return null;
1272 }
1273
1274 try {
1275 return new File(url).toURI().toURL().toExternalForm();
1276 } catch (MalformedURLException ex) {
1277 return url;
1278 }
1279 }
1280 }