1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugin.resources.remote;
20
21 import java.io.File;
22 import java.io.FileOutputStream;
23 import java.io.FileReader;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStream;
27 import java.io.OutputStreamWriter;
28 import java.io.Reader;
29 import java.io.StringReader;
30 import java.io.Writer;
31 import java.net.MalformedURLException;
32 import java.net.URL;
33 import java.nio.charset.Charset;
34 import java.nio.file.Files;
35 import java.time.Instant;
36 import java.time.ZoneId;
37 import java.time.format.DateTimeFormatter;
38 import java.util.AbstractMap;
39 import java.util.ArrayList;
40 import java.util.Collections;
41 import java.util.Comparator;
42 import java.util.Enumeration;
43 import java.util.HashMap;
44 import java.util.LinkedHashSet;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Properties;
48 import java.util.Set;
49 import java.util.TreeMap;
50
51 import org.apache.maven.RepositoryUtils;
52 import org.apache.maven.archiver.MavenArchiver;
53 import org.apache.maven.artifact.Artifact;
54 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
55 import org.apache.maven.execution.MavenSession;
56 import org.apache.maven.model.Model;
57 import org.apache.maven.model.Organization;
58 import org.apache.maven.model.Resource;
59 import org.apache.maven.model.building.ModelBuildingRequest;
60 import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
61 import org.apache.maven.plugin.AbstractMojo;
62 import org.apache.maven.plugin.MojoExecutionException;
63 import org.apache.maven.plugin.resources.remote.io.xpp3.RemoteResourcesBundleXpp3Reader;
64 import org.apache.maven.plugin.resources.remote.io.xpp3.SupplementalDataModelXpp3Reader;
65 import org.apache.maven.plugins.annotations.Parameter;
66 import org.apache.maven.project.DefaultProjectBuildingRequest;
67 import org.apache.maven.project.MavenProject;
68 import org.apache.maven.project.ProjectBuilder;
69 import org.apache.maven.project.ProjectBuildingException;
70 import org.apache.maven.project.ProjectBuildingRequest;
71 import org.apache.maven.project.ProjectBuildingResult;
72 import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException;
73 import org.apache.maven.shared.artifact.filter.collection.ArtifactIdFilter;
74 import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts;
75 import org.apache.maven.shared.artifact.filter.collection.GroupIdFilter;
76 import org.apache.maven.shared.artifact.filter.collection.ProjectTransitivityFilter;
77 import org.apache.maven.shared.artifact.filter.collection.ScopeFilter;
78 import org.apache.maven.shared.filtering.FilteringUtils;
79 import org.apache.maven.shared.filtering.MavenFileFilter;
80 import org.apache.maven.shared.filtering.MavenFileFilterRequest;
81 import org.apache.maven.shared.filtering.MavenFilteringException;
82 import org.apache.velocity.VelocityContext;
83 import org.apache.velocity.app.Velocity;
84 import org.apache.velocity.app.VelocityEngine;
85 import org.apache.velocity.exception.MethodInvocationException;
86 import org.apache.velocity.exception.ParseErrorException;
87 import org.apache.velocity.exception.ResourceNotFoundException;
88 import org.apache.velocity.exception.VelocityException;
89 import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
90 import org.codehaus.plexus.resource.ResourceManager;
91 import org.codehaus.plexus.resource.loader.FileResourceLoader;
92 import org.codehaus.plexus.util.FileUtils;
93 import org.codehaus.plexus.util.IOUtil;
94 import org.codehaus.plexus.util.StringUtils;
95 import org.codehaus.plexus.util.io.CachingOutputStream;
96 import org.codehaus.plexus.util.xml.Xpp3Dom;
97 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
98 import org.eclipse.aether.RepositorySystem;
99 import org.eclipse.aether.artifact.ArtifactType;
100 import org.eclipse.aether.artifact.DefaultArtifact;
101 import org.eclipse.aether.resolution.ArtifactRequest;
102 import org.eclipse.aether.resolution.ArtifactResolutionException;
103 import org.eclipse.aether.resolution.ArtifactResult;
104 import org.eclipse.aether.util.artifact.JavaScopes;
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121 public abstract class AbstractProcessRemoteResourcesMojo extends AbstractMojo {
122 private static final String TEMPLATE_SUFFIX = ".vm";
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 @Parameter
146 protected List<String> filterDelimiters;
147
148
149
150
151 @Parameter(defaultValue = "true")
152 protected boolean useDefaultFilterDelimiters;
153
154
155
156
157 @Parameter(property = "encoding", defaultValue = "${project.build.sourceEncoding}")
158 protected String encoding;
159
160
161
162
163 @Parameter(property = "outputDirectory", defaultValue = "${project.build.directory}/maven-shared-archive-resources")
164 private File outputDirectory;
165
166
167
168
169 @Parameter(defaultValue = "${basedir}/src/main/appended-resources")
170 private File appendedResourcesDirectory;
171
172
173
174
175
176
177
178
179
180
181 @Parameter
182 private String[] supplementalModels;
183
184
185
186
187
188
189
190 @Parameter
191 private List<String> supplementalModelArtifacts;
192
193
194
195
196
197 @Parameter(property = "resourceBundles", required = true)
198 private List<String> resourceBundles;
199
200
201
202
203
204
205 @Parameter(property = "remoteresources.skip", defaultValue = "false")
206 private boolean skip;
207
208
209
210
211
212
213 @Parameter(defaultValue = "true", property = "attachToMain")
214 private boolean attachToMain;
215
216
217
218
219
220
221 @Parameter(defaultValue = "true", property = "attachToTest")
222 private boolean attachToTest;
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237 @Parameter
238 protected Map<String, String> properties = new HashMap<>();
239
240
241
242
243
244
245
246
247
248 @Deprecated
249 @Parameter(defaultValue = "false")
250 protected boolean includeProjectProperties = false;
251
252
253
254
255
256
257
258
259
260 @Deprecated
261 @Parameter(defaultValue = "5242880")
262 protected int velocityFilterInMemoryThreshold = 5 * 1024 * 1024;
263
264
265
266
267 @Parameter(defaultValue = "${session}", readonly = true, required = true)
268 protected MavenSession mavenSession;
269
270
271
272
273 @Parameter(defaultValue = "${project}", readonly = true, required = true)
274 protected MavenProject project;
275
276
277
278
279
280
281 @Parameter(property = "includeScope", defaultValue = "runtime")
282 protected String includeScope;
283
284
285
286
287
288
289 @Parameter(property = "excludeScope", defaultValue = "")
290 protected String excludeScope;
291
292
293
294
295
296
297
298
299
300 @Parameter
301 protected String[] resolveScopes;
302
303
304
305
306
307
308 @Parameter(property = "excludeArtifactIds", defaultValue = "")
309 protected String excludeArtifactIds;
310
311
312
313
314
315
316 @Parameter(property = "includeArtifactIds", defaultValue = "")
317 protected String includeArtifactIds;
318
319
320
321
322
323
324 @Parameter(property = "excludeGroupIds", defaultValue = "")
325 protected String excludeGroupIds;
326
327
328
329
330
331
332 @Parameter(property = "includeGroupIds", defaultValue = "")
333 protected String includeGroupIds;
334
335
336
337
338
339
340 @Parameter(property = "excludeTransitive", defaultValue = "false")
341 protected boolean excludeTransitive;
342
343
344
345
346
347
348 @Parameter(defaultValue = "${project.build.outputTimestamp}")
349 private String outputTimestamp;
350
351
352
353
354
355
356 @Parameter(defaultValue = "false")
357 private boolean useProjectFiles;
358
359
360
361
362 private Map<String, Model> supplementModels;
363
364
365
366
367
368 private final ModelInheritanceAssembler inheritanceAssembler = new ModelInheritanceAssembler();
369
370 private VelocityEngine velocity;
371
372 protected final RepositorySystem repoSystem;
373
374
375
376
377 private final MavenFileFilter fileFilter;
378
379 private final ResourceManager locator;
380
381 private final ProjectBuilder projectBuilder;
382
383 private final ArtifactHandlerManager artifactHandlerManager;
384
385 protected AbstractProcessRemoteResourcesMojo(
386 RepositorySystem repoSystem,
387 MavenFileFilter fileFilter,
388 ResourceManager locator,
389 ProjectBuilder projectBuilder,
390 ArtifactHandlerManager artifactHandlerManager) {
391 this.repoSystem = repoSystem;
392 this.fileFilter = fileFilter;
393 this.locator = locator;
394 this.projectBuilder = projectBuilder;
395 this.artifactHandlerManager = artifactHandlerManager;
396 }
397
398 @Override
399 public void execute() throws MojoExecutionException {
400 if (skip) {
401 getLog().info("Skipping remote resources execution.");
402 return;
403 }
404
405 if (encoding == null || encoding.isEmpty()) {
406 getLog().warn("File encoding has not been set, using platform encoding " + Charset.defaultCharset()
407 + ", i.e. build is platform dependent!");
408 encoding = Charset.defaultCharset().name();
409 }
410
411 if (resolveScopes == null) {
412 resolveScopes = new String[] {
413 (this.includeScope == null || this.includeScope.isEmpty()) ? JavaScopes.TEST : this.includeScope
414 };
415 }
416
417 if (supplementalModels == null) {
418 File sups = new File(appendedResourcesDirectory, "supplemental-models.xml");
419 if (sups.exists()) {
420 try {
421 supplementalModels = new String[] {sups.toURI().toURL().toString()};
422 } catch (MalformedURLException e) {
423
424 getLog().debug("URL issue with supplemental-models.xml: " + e);
425 }
426 }
427 }
428
429 configureLocator();
430
431 ClassLoader origLoader = Thread.currentThread().getContextClassLoader();
432 try {
433 validate();
434
435 List<File> resourceBundleArtifacts = downloadBundles(resourceBundles);
436 supplementModels = loadSupplements(supplementalModels);
437
438 ClassLoader classLoader = initalizeClassloader(resourceBundleArtifacts);
439
440 Thread.currentThread().setContextClassLoader(classLoader);
441
442 velocity = new VelocityEngine();
443 velocity.setProperty("resource.loaders", "classpath");
444 velocity.setProperty("resource.loader.classpath.class", ClasspathResourceLoader.class.getName());
445 velocity.init();
446
447 VelocityContext context = buildVelocityContext();
448
449 processResourceBundles(classLoader, context);
450
451 if (outputDirectory.exists()) {
452
453
454
455
456 Resource resource = new Resource();
457 resource.setDirectory(outputDirectory.getAbsolutePath());
458
459 if (attachToMain) {
460 project.getResources().add(resource);
461 }
462 if (attachToTest) {
463 project.getTestResources().add(resource);
464 }
465
466
467
468
469 try {
470 File dotFile = new File(project.getBuild().getDirectory(), ".plxarc");
471 FileUtils.mkdir(dotFile.getParentFile().getAbsolutePath());
472 FileUtils.fileWrite(dotFile.getAbsolutePath(), outputDirectory.getName());
473 } catch (IOException e) {
474 throw new MojoExecutionException("Error creating dot file for archiving instructions.", e);
475 }
476 }
477 } finally {
478 Thread.currentThread().setContextClassLoader(origLoader);
479 }
480 }
481
482 private void configureLocator() throws MojoExecutionException {
483 if (supplementalModelArtifacts != null && !supplementalModelArtifacts.isEmpty()) {
484 List<File> artifacts = downloadBundles(supplementalModelArtifacts);
485
486 for (File artifact : artifacts) {
487 if (artifact.isDirectory()) {
488 locator.addSearchPath(FileResourceLoader.ID, artifact.getAbsolutePath());
489 } else {
490 try {
491 locator.addSearchPath(
492 "jar", "jar:" + artifact.toURI().toURL().toExternalForm());
493 } catch (MalformedURLException e) {
494 throw new MojoExecutionException("Could not use jar " + artifact.getAbsolutePath(), e);
495 }
496 }
497 }
498 }
499
500 locator.addSearchPath(
501 FileResourceLoader.ID, project.getFile().getParentFile().getAbsolutePath());
502 if (appendedResourcesDirectory != null) {
503 locator.addSearchPath(FileResourceLoader.ID, appendedResourcesDirectory.getAbsolutePath());
504 }
505 locator.addSearchPath("url", "");
506 locator.setOutputDirectory(new File(project.getBuild().getDirectory()));
507 }
508
509 protected List<MavenProject> getProjects() {
510 List<MavenProject> projects = new ArrayList<>();
511
512
513 FilterArtifacts filter = new FilterArtifacts();
514
515 Set<Artifact> artifacts = new LinkedHashSet<>(getAllDependencies());
516 if (this.excludeTransitive) {
517 filter.addFilter(new ProjectTransitivityFilter(getDirectDependencies(), true));
518 }
519
520 filter.addFilter(new ScopeFilter(this.includeScope, this.excludeScope));
521 filter.addFilter(new GroupIdFilter(this.includeGroupIds, this.excludeGroupIds));
522 filter.addFilter(new ArtifactIdFilter(this.includeArtifactIds, this.excludeArtifactIds));
523
524
525 try {
526 artifacts = filter.filter(artifacts);
527 } catch (ArtifactFilterException e) {
528 throw new IllegalStateException(e.getMessage(), e);
529 }
530
531 getLog().debug("PROJECTS: " + artifacts);
532
533 for (Artifact artifact : artifacts) {
534 if (artifact.isSnapshot()) {
535 artifact.setVersion(artifact.getBaseVersion());
536 }
537
538 getLog().debug("Building project for " + artifact);
539 MavenProject p;
540 try {
541 ProjectBuildingRequest req = new DefaultProjectBuildingRequest()
542 .setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL)
543 .setProcessPlugins(false)
544 .setRepositorySession(mavenSession.getRepositorySession())
545 .setSystemProperties(mavenSession.getSystemProperties())
546 .setUserProperties(mavenSession.getUserProperties())
547 .setLocalRepository(mavenSession.getLocalRepository())
548 .setRemoteRepositories(project.getRemoteArtifactRepositories());
549 ProjectBuildingResult res = projectBuilder.build(artifact, req);
550 p = res.getProject();
551 } catch (ProjectBuildingException e) {
552 getLog().warn("Invalid project model for artifact [" + artifact.getGroupId() + ":"
553 + artifact.getArtifactId() + ":" + artifact.getVersion() + "]. "
554 + "It will be ignored by the remote resources Mojo.");
555 continue;
556 }
557
558 String supplementKey = generateSupplementMapKey(
559 p.getModel().getGroupId(), p.getModel().getArtifactId());
560
561 if (supplementModels.containsKey(supplementKey)) {
562 Model mergedModel = mergeModels(p.getModel(), supplementModels.get(supplementKey));
563 MavenProject mergedProject = new MavenProject(mergedModel);
564 projects.add(mergedProject);
565 mergedProject.setArtifact(artifact);
566 mergedProject.setVersion(artifact.getVersion());
567 getLog().debug("Adding project with groupId [" + mergedProject.getGroupId() + "] (supplemented)");
568 } else {
569 projects.add(p);
570 getLog().debug("Adding project with groupId [" + p.getGroupId() + "]");
571 }
572 }
573 projects.sort(new ProjectComparator());
574 return projects;
575 }
576
577
578
579
580 protected abstract Set<Artifact> getAllDependencies();
581
582
583
584
585 protected abstract Set<Artifact> getDirectDependencies();
586
587 protected Map<Organization, List<MavenProject>> getProjectsSortedByOrganization(List<MavenProject> projects) {
588 Map<Organization, List<MavenProject>> organizations = new TreeMap<>(new OrganizationComparator());
589 List<MavenProject> unknownOrganization = new ArrayList<>();
590
591 for (MavenProject p : projects) {
592 if (p.getOrganization() != null
593 && StringUtils.isNotEmpty(p.getOrganization().getName())) {
594 List<MavenProject> sortedProjects = organizations.get(p.getOrganization());
595 if (sortedProjects == null) {
596 sortedProjects = new ArrayList<>();
597 }
598 sortedProjects.add(p);
599
600 organizations.put(p.getOrganization(), sortedProjects);
601 } else {
602 unknownOrganization.add(p);
603 }
604 }
605 if (!unknownOrganization.isEmpty()) {
606 Organization unknownOrg = new Organization();
607 unknownOrg.setName("an unknown organization");
608 organizations.put(unknownOrg, unknownOrganization);
609 }
610
611 return organizations;
612 }
613
614 protected boolean copyResourceIfExists(
615 File outputFile, String bundleResourceName, VelocityContext context, String encoding)
616 throws IOException, MojoExecutionException {
617 for (Resource resource : project.getResources()) {
618 File resourceDirectory = new File(resource.getDirectory());
619
620 if (!resourceDirectory.exists()) {
621 continue;
622 }
623
624
625 File source = new File(resourceDirectory, bundleResourceName);
626 File templateSource = new File(resourceDirectory, bundleResourceName + TEMPLATE_SUFFIX);
627
628 if (!source.exists() && templateSource.exists()) {
629 source = templateSource;
630 }
631
632 if (source.exists() && !source.equals(outputFile)) {
633 if (source == templateSource) {
634 getLog().debug("Use project resource '" + source + "' as resource with Velocity");
635 try (CachingOutputStream os = new CachingOutputStream(outputFile);
636 Writer writer = getWriter(encoding, os);
637 Reader reader = getReader(encoding, source)) {
638 velocity.evaluate(context, writer, "", reader);
639 } catch (ParseErrorException | MethodInvocationException | ResourceNotFoundException e) {
640 throw new MojoExecutionException("Error rendering velocity resource: " + source, e);
641 }
642 } else if (resource.isFiltering()) {
643 getLog().debug("Use project resource '" + source + "' as resource with filtering");
644
645 MavenFileFilterRequest req = setupRequest(resource, source, outputFile);
646
647 try {
648 fileFilter.copyFile(req);
649 } catch (MavenFilteringException e) {
650 throw new MojoExecutionException("Error filtering resource: " + source, e);
651 }
652 } else {
653 getLog().debug("Use project resource '" + source + "' as resource");
654 FilteringUtils.copyFile(source, outputFile, null, null);
655 }
656
657
658 resource.addExclude(bundleResourceName);
659
660 return true;
661 }
662 }
663 return false;
664 }
665
666 private boolean copyProjectRootIfExists(File outputFile, String bundleResourceName) throws IOException {
667 if (!useProjectFiles) {
668 return false;
669 }
670
671 File source = new File(project.getBasedir(), bundleResourceName);
672 if (source.exists()) {
673 getLog().debug("Use project file '" + source + "' as resource");
674 FilteringUtils.copyFile(source, outputFile, null, null);
675 return true;
676 }
677
678 return false;
679 }
680
681 private Reader getReader(String readerEncoding, File file) throws IOException {
682 return Files.newBufferedReader(
683 file.toPath(), Charset.forName(readerEncoding != null ? readerEncoding : encoding));
684 }
685
686 private Writer getWriter(String writerEncoding, OutputStream outputStream) throws IOException {
687 return new OutputStreamWriter(outputStream, writerEncoding != null ? writerEncoding : encoding);
688 }
689
690 private MavenFileFilterRequest setupRequest(Resource resource, File source, File file) {
691 MavenFileFilterRequest req = new MavenFileFilterRequest();
692 req.setFrom(source);
693 req.setTo(file);
694 req.setFiltering(resource.isFiltering());
695
696 req.setMavenProject(project);
697 req.setMavenSession(mavenSession);
698 req.setInjectProjectBuildFilters(true);
699
700 req.setEncoding(encoding);
701
702 if (filterDelimiters != null && !filterDelimiters.isEmpty()) {
703 LinkedHashSet<String> delims = new LinkedHashSet<>();
704 if (useDefaultFilterDelimiters) {
705 delims.addAll(req.getDelimiters());
706 }
707
708 for (String delim : filterDelimiters) {
709 if (delim == null) {
710 delims.add("${*}");
711 } else {
712 delims.add(delim);
713 }
714 }
715
716 req.setDelimiters(delims);
717 }
718
719 return req;
720 }
721
722 protected void validate() throws MojoExecutionException {
723 int bundleCount = 1;
724
725 for (String artifactDescriptor : resourceBundles) {
726
727
728 String[] s = StringUtils.split(artifactDescriptor, ":");
729
730 if (s.length < 3 || s.length > 5) {
731 String position;
732
733 if (bundleCount == 1) {
734 position = "1st";
735 } else if (bundleCount == 2) {
736 position = "2nd";
737 } else if (bundleCount == 3) {
738 position = "3rd";
739 } else {
740 position = bundleCount + "th";
741 }
742
743 throw new MojoExecutionException("The " + position
744 + " resource bundle configured must specify a groupId, artifactId, "
745 + " version and, optionally, type and classifier for a remote resource bundle. "
746 + "Must be of the form <resourceBundle>groupId:artifactId:version</resourceBundle>, "
747 + "<resourceBundle>groupId:artifactId:version:type</resourceBundle> or "
748 + "<resourceBundle>groupId:artifactId:version:type:classifier</resourceBundle>");
749 }
750
751 bundleCount++;
752 }
753 }
754
755 private static final String KEY_PROJECTS = "projects";
756 private static final String KEY_PROJECTS_ORGS = "projectsSortedByOrganization";
757
758 protected VelocityContext buildVelocityContext() {
759
760 Map<String, Object> contextProperties = new HashMap<>(properties);
761
762 if (includeProjectProperties) {
763 final Properties projectProperties = project.getProperties();
764 for (String key : projectProperties.stringPropertyNames()) {
765 contextProperties.put(key, projectProperties.getProperty(key));
766 }
767 }
768
769
770 VelocityContext context = new VelocityContext(contextProperties) {
771 @Override
772 public Object internalGet(String key) {
773 Object result = super.internalGet(key);
774 if (result == null && key != null && key.startsWith(KEY_PROJECTS) && containsKey(key)) {
775
776 List<MavenProject> projects = getProjects();
777 put(KEY_PROJECTS, projects);
778 put(KEY_PROJECTS_ORGS, getProjectsSortedByOrganization(projects));
779 return super.internalGet(key);
780 }
781 return result;
782 }
783 };
784
785 context.put(KEY_PROJECTS, null);
786 context.put(KEY_PROJECTS_ORGS, null);
787
788
789 String inceptionYear = project.getInceptionYear();
790
791
792 String year = MavenArchiver.parseBuildOutputTimestamp(outputTimestamp)
793 .orElseGet(Instant::now)
794 .atZone(ZoneId.of("UTC+10"))
795 .format(DateTimeFormatter.ofPattern("yyyy"));
796
797 if (inceptionYear == null || inceptionYear.isEmpty()) {
798 if (getLog().isDebugEnabled()) {
799 getLog().debug("inceptionYear not specified, defaulting to " + year);
800 }
801
802 inceptionYear = year;
803 }
804 context.put("project", project);
805 context.put("presentYear", year);
806 context.put("locator", locator);
807
808 if (inceptionYear.equals(year)) {
809 context.put("projectTimespan", year);
810 } else {
811 context.put("projectTimespan", inceptionYear + "-" + year);
812 }
813 return context;
814 }
815
816 private List<File> downloadBundles(List<String> bundles) throws MojoExecutionException {
817 List<File> bundleArtifacts = new ArrayList<>();
818
819 for (String artifactDescriptor : bundles) {
820 getLog().info("Preparing remote bundle " + artifactDescriptor);
821
822 String[] s = artifactDescriptor.split(":");
823
824 File artifactFile = null;
825
826 if (mavenSession != null) {
827 List<MavenProject> list = mavenSession.getProjects();
828 for (MavenProject p : list) {
829 if (s[0].equals(p.getGroupId()) && s[1].equals(p.getArtifactId()) && s[2].equals(p.getVersion())) {
830 if (s.length >= 4 && "test-jar".equals(s[3])) {
831 artifactFile = new File(p.getBuild().getTestOutputDirectory());
832 } else {
833 artifactFile = new File(p.getBuild().getOutputDirectory());
834 }
835 }
836 }
837 }
838 if (artifactFile == null || !artifactFile.exists()) {
839 String g = s[0];
840 String a = s[1];
841 String v = s[2];
842 String type = s.length >= 4 ? s[3] : "jar";
843 ArtifactType artifactType =
844 RepositoryUtils.newArtifactType(type, artifactHandlerManager.getArtifactHandler(type));
845 String classifier = s.length == 5 ? s[4] : artifactType.getClassifier();
846
847 DefaultArtifact artifact =
848 new DefaultArtifact(g, a, classifier, artifactType.getExtension(), v, artifactType);
849
850 try {
851 ArtifactRequest request =
852 new ArtifactRequest(artifact, project.getRemoteProjectRepositories(), "remote-resources");
853 ArtifactResult result = repoSystem.resolveArtifact(mavenSession.getRepositorySession(), request);
854 artifactFile = result.getArtifact().getFile();
855 } catch (ArtifactResolutionException e) {
856 throw new MojoExecutionException("Error processing remote resources", e);
857 }
858 }
859 bundleArtifacts.add(artifactFile);
860 }
861
862 return bundleArtifacts;
863 }
864
865 private ClassLoader initalizeClassloader(List<File> artifacts) throws MojoExecutionException {
866 RemoteResourcesClassLoader cl = new RemoteResourcesClassLoader(null);
867 try {
868 for (File artifact : artifacts) {
869 cl.addURL(artifact.toURI().toURL());
870 }
871 return cl;
872 } catch (MalformedURLException e) {
873 throw new MojoExecutionException("Unable to configure resources classloader: " + e.getMessage(), e);
874 }
875 }
876
877 protected void processResourceBundles(ClassLoader classLoader, VelocityContext context)
878 throws MojoExecutionException {
879 List<Map.Entry<String, RemoteResourcesBundle>> remoteResources = new ArrayList<>();
880 int bundleCount = 0;
881 int resourceCount = 0;
882
883
884 try {
885 RemoteResourcesBundleXpp3Reader bundleReader = new RemoteResourcesBundleXpp3Reader();
886
887 for (Enumeration<URL> e = classLoader.getResources(BundleRemoteResourcesMojo.RESOURCES_MANIFEST);
888 e.hasMoreElements(); ) {
889 URL url = e.nextElement();
890 bundleCount++;
891 getLog().debug("processResourceBundle on bundle#" + bundleCount + " " + url);
892
893 RemoteResourcesBundle bundle;
894
895 try (InputStream in = url.openStream()) {
896 bundle = bundleReader.read(in);
897 }
898
899 verifyRequiredProperties(bundle, url);
900
901 int n = 0;
902 for (String bundleResource : bundle.getRemoteResources()) {
903 n++;
904 resourceCount++;
905 getLog().debug("bundle#" + bundleCount + " resource#" + n + " " + bundleResource);
906 remoteResources.add(new AbstractMap.SimpleEntry<>(bundleResource, bundle));
907 }
908 }
909 } catch (IOException ioe) {
910 throw new MojoExecutionException("Error finding remote resources manifests", ioe);
911 } catch (XmlPullParserException xppe) {
912 throw new MojoExecutionException("Error parsing remote resource bundle descriptor.", xppe);
913 }
914
915 getLog().info("Copying " + resourceCount + " resource" + ((resourceCount > 1) ? "s" : "") + " from "
916 + bundleCount + " bundle" + ((bundleCount > 1) ? "s" : "") + ".");
917
918 String velocityResource = null;
919 try {
920
921 for (Map.Entry<String, RemoteResourcesBundle> entry : remoteResources) {
922 String bundleResource = entry.getKey();
923 RemoteResourcesBundle bundle = entry.getValue();
924
925 String projectResource = bundleResource;
926
927 boolean doVelocity = false;
928 if (projectResource.endsWith(TEMPLATE_SUFFIX)) {
929 projectResource = projectResource.substring(0, projectResource.length() - 3);
930 velocityResource = bundleResource;
931 doVelocity = true;
932 }
933
934
935
936 File outputFile = new File(outputDirectory, projectResource);
937
938 FileUtils.mkdir(outputFile.getParentFile().getAbsolutePath());
939
940
941 if (copyResourceIfExists(outputFile, projectResource, context, bundle.getSourceEncoding())) {
942 continue;
943 }
944
945 if (copyProjectRootIfExists(outputFile, projectResource)) {
946 continue;
947 }
948
949 if (doVelocity) {
950 String bundleEncoding = bundle.getSourceEncoding();
951 if (bundleEncoding == null) {
952 bundleEncoding = encoding;
953 }
954
955 try (CachingOutputStream os = new CachingOutputStream(outputFile);
956 Writer writer = getWriter(bundleEncoding, os)) {
957 velocity.mergeTemplate(bundleResource, bundleEncoding, context, writer);
958 }
959 } else {
960 URL bundleResourceUrl = classLoader.getResource(bundleResource);
961 if (bundleResourceUrl != null) {
962 FileUtils.copyURLToFile(bundleResourceUrl, outputFile);
963 }
964 }
965
966 File appendedResourceFile = new File(appendedResourcesDirectory, projectResource);
967 File appendedVmResourceFile = new File(appendedResourcesDirectory, projectResource + ".vm");
968
969 if (appendedResourceFile.exists()) {
970 getLog().info("Copying appended resource: " + projectResource);
971 try (InputStream in = Files.newInputStream(appendedResourceFile.toPath());
972 OutputStream out = new FileOutputStream(outputFile, true)) {
973 IOUtil.copy(in, out);
974 }
975
976 } else if (appendedVmResourceFile.exists()) {
977 getLog().info("Filtering appended resource: " + projectResource + ".vm");
978
979 try (CachingOutputStream os = new CachingOutputStream(outputFile);
980 Reader reader = getReader(bundle.getSourceEncoding(), appendedVmResourceFile);
981 Writer writer = getWriter(bundle.getSourceEncoding(), os)) {
982 Velocity.init();
983 Velocity.evaluate(context, writer, "remote-resources", reader);
984 }
985 }
986 }
987 } catch (IOException ioe) {
988 throw new MojoExecutionException("Error reading remote resource", ioe);
989 } catch (VelocityException e) {
990 throw new MojoExecutionException("Error rendering Velocity resource '" + velocityResource + "'", e);
991 }
992 }
993
994 private void verifyRequiredProperties(RemoteResourcesBundle bundle, URL url) throws MojoExecutionException {
995 if (bundle.getRequiredProjectProperties() == null
996 || bundle.getRequiredProjectProperties().isEmpty()) {
997 return;
998 }
999
1000 for (String requiredProperty : bundle.getRequiredProjectProperties()) {
1001 if (!project.getProperties().containsKey(requiredProperty)) {
1002 throw new MojoExecutionException(
1003 "Required project property: '" + requiredProperty + "' is not present for bundle: " + url);
1004 }
1005 }
1006 }
1007
1008 protected Model getSupplement(Xpp3Dom supplementModelXml) throws MojoExecutionException {
1009 MavenXpp3Reader modelReader = new MavenXpp3Reader();
1010 Model model = null;
1011
1012 try {
1013 model = modelReader.read(new StringReader(supplementModelXml.toString()));
1014 String groupId = model.getGroupId();
1015 String artifactId = model.getArtifactId();
1016
1017 if (groupId == null || groupId.trim().isEmpty()) {
1018 throw new MojoExecutionException(
1019 "Supplemental project XML " + "requires that a <groupId> element be present.");
1020 }
1021
1022 if (artifactId == null || artifactId.trim().isEmpty()) {
1023 throw new MojoExecutionException(
1024 "Supplemental project XML " + "requires that a <artifactId> element be present.");
1025 }
1026 } catch (IOException e) {
1027 getLog().warn("Unable to read supplemental XML: " + e.getMessage(), e);
1028 } catch (XmlPullParserException e) {
1029 getLog().warn("Unable to parse supplemental XML: " + e.getMessage(), e);
1030 }
1031
1032 return model;
1033 }
1034
1035 protected Model mergeModels(Model parent, Model child) {
1036 inheritanceAssembler.assembleModelInheritance(child, parent);
1037 return child;
1038 }
1039
1040 private static String generateSupplementMapKey(String groupId, String artifactId) {
1041 return groupId.trim() + ":" + artifactId.trim();
1042 }
1043
1044 private Map<String, Model> loadSupplements(String[] models) throws MojoExecutionException {
1045 if (models == null) {
1046 getLog().debug("Supplemental data models won't be loaded. No models specified.");
1047 return Collections.emptyMap();
1048 }
1049
1050 List<Supplement> supplements = new ArrayList<>();
1051 for (String set : models) {
1052 getLog().debug("Preparing ruleset: " + set);
1053 try {
1054 File f = locator.getResourceAsFile(set, getLocationTemp(set));
1055
1056 if (null == f || !f.exists()) {
1057 throw new MojoExecutionException("Cold not resolve " + set);
1058 }
1059 if (!f.canRead()) {
1060 throw new MojoExecutionException("Supplemental data models won't be loaded. " + "File "
1061 + f.getAbsolutePath() + " cannot be read, check permissions on the file.");
1062 }
1063
1064 getLog().debug("Loading supplemental models from " + f.getAbsolutePath());
1065
1066 SupplementalDataModelXpp3Reader reader = new SupplementalDataModelXpp3Reader();
1067 SupplementalDataModel supplementalModel = reader.read(new FileReader(f));
1068 supplements.addAll(supplementalModel.getSupplement());
1069 } catch (Exception e) {
1070 String msg = "Error loading supplemental data models: " + e.getMessage();
1071 getLog().error(msg, e);
1072 throw new MojoExecutionException(msg, e);
1073 }
1074 }
1075
1076 getLog().debug("Loading supplements complete.");
1077
1078 Map<String, Model> supplementMap = new HashMap<>();
1079 for (Supplement sd : supplements) {
1080 Xpp3Dom dom = (Xpp3Dom) sd.getProject();
1081
1082 Model m = getSupplement(dom);
1083 supplementMap.put(generateSupplementMapKey(m.getGroupId(), m.getArtifactId()), m);
1084 }
1085
1086 return supplementMap;
1087 }
1088
1089
1090
1091
1092
1093
1094
1095 private String getLocationTemp(String name) {
1096 String loc = name;
1097 if (loc.indexOf('/') != -1) {
1098 loc = loc.substring(loc.lastIndexOf('/') + 1);
1099 }
1100 if (loc.indexOf('\\') != -1) {
1101 loc = loc.substring(loc.lastIndexOf('\\') + 1);
1102 }
1103 getLog().debug("Before: " + name + " After: " + loc);
1104 return loc;
1105 }
1106
1107 static class OrganizationComparator implements Comparator<Organization> {
1108 @Override
1109 public int compare(Organization org1, Organization org2) {
1110 int i = compareStrings(org1.getName(), org2.getName());
1111 if (i == 0) {
1112 i = compareStrings(org1.getUrl(), org2.getUrl());
1113 }
1114 return i;
1115 }
1116
1117 private int compareStrings(String s1, String s2) {
1118 if (s1 == null && s2 == null) {
1119 return 0;
1120 } else if (s1 == null) {
1121 return 1;
1122 } else if (s2 == null) {
1123 return -1;
1124 }
1125
1126 return s1.compareToIgnoreCase(s2);
1127 }
1128 }
1129
1130 static class ProjectComparator implements Comparator<MavenProject> {
1131 @Override
1132 public int compare(MavenProject p1, MavenProject p2) {
1133 return p1.getArtifact().compareTo(p2.getArtifact());
1134 }
1135 }
1136 }