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