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