1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.dependency.fromDependencies;
20
21 import javax.inject.Inject;
22
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.StringWriter;
26 import java.io.Writer;
27 import java.nio.charset.Charset;
28 import java.nio.charset.StandardCharsets;
29 import java.nio.file.Files;
30 import java.nio.file.Path;
31 import java.nio.file.Paths;
32 import java.util.Collections;
33 import java.util.Comparator;
34 import java.util.List;
35 import java.util.Objects;
36 import java.util.Properties;
37 import java.util.function.Function;
38 import java.util.stream.Collectors;
39
40 import org.apache.maven.artifact.Artifact;
41 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
42 import org.apache.maven.execution.MavenSession;
43 import org.apache.maven.plugin.MojoExecutionException;
44 import org.apache.maven.plugins.annotations.LifecyclePhase;
45 import org.apache.maven.plugins.annotations.Mojo;
46 import org.apache.maven.plugins.annotations.Parameter;
47 import org.apache.maven.plugins.annotations.ResolutionScope;
48 import org.apache.maven.plugins.dependency.utils.ResolverUtil;
49 import org.apache.maven.project.MavenProject;
50 import org.apache.maven.project.MavenProjectHelper;
51 import org.apache.maven.project.ProjectBuilder;
52 import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
53 import org.apache.velocity.Template;
54 import org.apache.velocity.VelocityContext;
55 import org.apache.velocity.app.VelocityEngine;
56 import org.apache.velocity.tools.generic.CollectionTool;
57 import org.sonatype.plexus.build.incremental.BuildContext;
58
59 import static java.util.Optional.ofNullable;
60
61
62
63
64
65
66 @Mojo(
67 name = "render-dependencies",
68 requiresDependencyResolution = ResolutionScope.TEST,
69 defaultPhase = LifecyclePhase.GENERATE_SOURCES,
70 threadSafe = true)
71 public class RenderDependenciesMojo extends AbstractDependencyFilterMojo {
72
73
74
75
76 @Parameter(property = "outputEncoding", defaultValue = "${project.reporting.outputEncoding}")
77 private String outputEncoding;
78
79
80
81
82
83 @Parameter(property = "mdep.outputFile")
84 private File outputFile;
85
86
87
88
89
90 @Parameter(property = "mdep.classifier", defaultValue = "template")
91 private String classifier;
92
93
94
95
96
97 @Parameter(property = "mdep.extension", defaultValue = "txt")
98 private String extension;
99
100
101
102
103
104
105 @Parameter(property = "mdep.template", required = true)
106 private String template;
107
108 private final MavenProjectHelper projectHelper;
109
110 @Inject
111 protected RenderDependenciesMojo(
112 MavenSession session,
113 BuildContext buildContext,
114 MavenProject project,
115 ResolverUtil resolverUtil,
116 ProjectBuilder projectBuilder,
117 ArtifactHandlerManager artifactHandlerManager,
118 MavenProjectHelper projectHelper) {
119 super(session, buildContext, project, resolverUtil, projectBuilder, artifactHandlerManager);
120 this.projectHelper = projectHelper;
121 }
122
123
124
125
126
127
128 @Override
129 protected void doExecute() throws MojoExecutionException {
130
131 final List<Artifact> artifacts =
132 ofNullable(getResolvedDependencies(true)).orElseGet(Collections::emptySet).stream()
133 .sorted(Comparator.comparing(Artifact::getGroupId)
134 .thenComparing(Artifact::getArtifactId)
135 .thenComparing(Artifact::getBaseVersion)
136 .thenComparing(orEmpty(Artifact::getClassifier))
137 .thenComparing(orEmpty(Artifact::getType)))
138 .collect(Collectors.toList());
139
140 if (artifacts.isEmpty()) {
141 getLog().warn("No dependencies found.");
142 }
143
144 final String rendered = render(artifacts);
145
146 if (outputFile == null) {
147 getLog().info(rendered);
148 } else {
149 store(rendered, outputFile);
150 }
151 if (classifier != null && !classifier.isEmpty()) {
152 attachFile(rendered);
153 }
154 }
155
156
157
158
159
160
161 private String render(final List<Artifact> artifacts) {
162 final Path templatePath = getTemplatePath();
163 final boolean fromFile = templatePath != null && Files.exists(templatePath);
164
165 final Properties props = new Properties();
166 props.setProperty("runtime.strict_mode.enable", "true");
167 if (fromFile) {
168 props.setProperty(
169 "resource.loader.file.path",
170 templatePath.toAbsolutePath().getParent().toString());
171 }
172
173 final VelocityEngine ve = new VelocityEngine(props);
174 ve.init();
175
176 final VelocityContext context = new VelocityContext();
177 context.put("artifacts", artifacts);
178 context.put("sorter", new CollectionTool());
179
180
181 final StringWriter writer = new StringWriter();
182 try (StringWriter ignored = writer) {
183 if (fromFile) {
184 final Template template =
185 ve.getTemplate(templatePath.getFileName().toString());
186 template.merge(context, writer);
187 } else {
188 ve.evaluate(context, writer, "tpl-" + Math.abs(hashCode()), template);
189 }
190 } catch (final IOException e) {
191
192 }
193
194 return writer.toString();
195 }
196
197 private Path getTemplatePath() {
198 try {
199 return Paths.get(template);
200 } catch (final RuntimeException re) {
201 return null;
202 }
203 }
204
205
206
207
208
209
210 private Comparator<Artifact> orEmpty(final Function<Artifact, String> getter) {
211 return Comparator.comparing(a -> ofNullable(getter.apply(a)).orElse(""));
212 }
213
214
215
216
217
218 protected void attachFile(final String content) throws MojoExecutionException {
219 final File attachedFile;
220 if (outputFile == null) {
221 attachedFile = new File(getProject().getBuild().getDirectory(), classifier);
222 store(content, attachedFile);
223 } else {
224 attachedFile = outputFile;
225 }
226 projectHelper.attachArtifact(getProject(), extension, classifier, attachedFile);
227 }
228
229
230
231
232
233
234 private void store(final String content, final File out) throws MojoExecutionException {
235
236 final Path parent = out.toPath().getParent();
237 if (parent != null) {
238 try {
239 Files.createDirectories(parent);
240 } catch (final IOException e) {
241 throw new MojoExecutionException(e);
242 }
243 }
244
245 final String encoding = Objects.toString(outputEncoding, StandardCharsets.UTF_8.name());
246 try (Writer w = Files.newBufferedWriter(out.toPath(), Charset.forName(encoding))) {
247 w.write(content);
248 getLog().info("Wrote file '" + out + "'.");
249 } catch (final IOException ex) {
250 throw new MojoExecutionException("Error while writing to file '" + out, ex);
251 }
252 }
253
254 @Override
255 protected ArtifactsFilter getMarkedArtifactFilter() {
256 return null;
257 }
258
259 public void setExtension(final String extension) {
260 this.extension = extension;
261 }
262
263 public void setOutputEncoding(final String outputEncoding) {
264 this.outputEncoding = outputEncoding;
265 }
266
267 public void setOutputFile(final File outputFile) {
268 this.outputFile = outputFile;
269 }
270
271 public void setClassifier(final String classifier) {
272 this.classifier = classifier;
273 }
274
275 public void setTemplate(final String template) {
276 this.template = template;
277 }
278 }