1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.help;
20
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.StringWriter;
26 import java.util.List;
27 import java.util.Locale;
28 import java.util.Map;
29 import java.util.Properties;
30 import java.util.TreeMap;
31 import java.util.jar.JarEntry;
32 import java.util.jar.JarInputStream;
33
34 import com.thoughtworks.xstream.XStream;
35 import com.thoughtworks.xstream.converters.MarshallingContext;
36 import com.thoughtworks.xstream.converters.collections.PropertiesConverter;
37 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
38 import org.apache.maven.lifecycle.internal.MojoDescriptorCreator;
39 import org.apache.maven.model.Dependency;
40 import org.apache.maven.model.Model;
41 import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
42 import org.apache.maven.plugin.MojoExecution;
43 import org.apache.maven.plugin.MojoExecutionException;
44 import org.apache.maven.plugin.MojoFailureException;
45 import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
46 import org.apache.maven.plugin.descriptor.MojoDescriptor;
47 import org.apache.maven.plugins.annotations.Component;
48 import org.apache.maven.plugins.annotations.Mojo;
49 import org.apache.maven.plugins.annotations.Parameter;
50 import org.apache.maven.project.MavenProject;
51 import org.apache.maven.settings.Settings;
52 import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer;
53 import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
54 import org.codehaus.plexus.components.interactivity.InputHandler;
55 import org.codehaus.plexus.util.StringUtils;
56 import org.eclipse.aether.RepositoryException;
57 import org.eclipse.aether.artifact.Artifact;
58 import org.eclipse.aether.artifact.DefaultArtifact;
59
60
61
62
63
64
65
66 @Mojo(name = "evaluate", requiresProject = false)
67 public class EvaluateMojo extends AbstractHelpMojo {
68
69
70
71
72
73
74
75 @Component
76 private InputHandler inputHandler;
77
78
79
80
81 @Component
82 private MojoDescriptorCreator mojoDescriptorCreator;
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97 @Parameter(property = "output")
98 private File output;
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115 @Parameter(property = "forceStdout", defaultValue = "false")
116 private boolean forceStdout;
117
118
119
120
121
122
123 @Parameter(property = "artifact")
124 private String artifact;
125
126
127
128
129 @Parameter(property = "expression")
130 private String expression;
131
132
133
134
135 @Parameter(defaultValue = "${settings}", readonly = true, required = true)
136 private Settings settings;
137
138
139
140
141
142
143 private PluginParameterExpressionEvaluator evaluator;
144
145
146 private XStream xstream;
147
148
149
150
151
152
153 public void execute() throws MojoExecutionException, MojoFailureException {
154 if (expression == null && !settings.isInteractiveMode()) {
155
156 getLog().error("Maven is configured to NOT interact with the user for input. "
157 + "This Mojo requires that 'interactiveMode' in your settings file is flag to 'true'.");
158 return;
159 }
160
161 validateParameters();
162
163 if (artifact != null && !artifact.isEmpty()) {
164 project = getMavenProject(artifact);
165 }
166
167 if (expression == null) {
168 if (output != null) {
169 getLog().warn("When prompting for input, the result will be written to the console, "
170 + "ignoring 'output'.");
171 }
172 while (true) {
173 getLog().info("Enter the Maven expression i.e. ${project.groupId} or 0 to exit?:");
174
175 try {
176 String userExpression = inputHandler.readLine();
177 if (userExpression == null
178 || userExpression.toLowerCase(Locale.ENGLISH).equals("0")) {
179 break;
180 }
181
182 handleResponse(userExpression, null);
183 } catch (IOException e) {
184 throw new MojoExecutionException("Unable to read from standard input.", e);
185 }
186 }
187 } else {
188 handleResponse("${" + expression + "}", output);
189 }
190 }
191
192
193
194
195
196
197
198
199 private void validateParameters() {
200 if (artifact == null) {
201
202 getLog().info("No artifact parameter specified, using '" + project.getId() + "' as project.");
203 }
204 }
205
206
207
208
209
210 private PluginParameterExpressionEvaluator getEvaluator() throws MojoFailureException {
211 if (evaluator == null) {
212 MojoDescriptor mojoDescriptor;
213 try {
214 mojoDescriptor = mojoDescriptorCreator.getMojoDescriptor("help:evaluate", session, project);
215 } catch (Exception e) {
216 throw new MojoFailureException("Failure while evaluating.", e);
217 }
218 MojoExecution mojoExecution = new MojoExecution(mojoDescriptor);
219
220 MavenProject currentProject = session.getCurrentProject();
221
222
223 synchronized (session) {
224 session.setCurrentProject(project);
225 evaluator = new PluginParameterExpressionEvaluator(session, mojoExecution);
226 session.setCurrentProject(currentProject);
227 }
228 }
229
230 return evaluator;
231 }
232
233
234
235
236
237
238
239 private void handleResponse(String expr, File output) throws MojoExecutionException, MojoFailureException {
240 StringBuilder response = new StringBuilder();
241
242 Object obj;
243 try {
244 obj = getEvaluator().evaluate(expr);
245 } catch (ExpressionEvaluationException e) {
246 throw new MojoExecutionException("Error when evaluating the Maven expression", e);
247 }
248
249 if (obj != null && expr.equals(obj.toString())) {
250 getLog().warn("The Maven expression was invalid. Please use a valid expression.");
251 return;
252 }
253
254
255 if (obj == null) {
256 response.append("null object or invalid expression");
257 }
258
259 else if (obj instanceof String) {
260 response.append(obj.toString());
261 } else if (obj instanceof Boolean) {
262 response.append(obj.toString());
263 } else if (obj instanceof Byte) {
264 response.append(obj.toString());
265 } else if (obj instanceof Character) {
266 response.append(obj.toString());
267 } else if (obj instanceof Double) {
268 response.append(obj.toString());
269 } else if (obj instanceof Float) {
270 response.append(obj.toString());
271 } else if (obj instanceof Integer) {
272 response.append(obj.toString());
273 } else if (obj instanceof Long) {
274 response.append(obj.toString());
275 } else if (obj instanceof Short) {
276 response.append(obj.toString());
277 }
278
279 else if (obj instanceof File) {
280 File f = (File) obj;
281 response.append(f.getAbsolutePath());
282 }
283
284 else if (obj instanceof MavenProject) {
285 MavenProject projectAsked = (MavenProject) obj;
286 StringWriter sWriter = new StringWriter();
287 MavenXpp3Writer pomWriter = new MavenXpp3Writer();
288 try {
289 pomWriter.write(sWriter, projectAsked.getModel());
290 } catch (IOException e) {
291 throw new MojoExecutionException("Error when writing pom", e);
292 }
293
294 response.append(sWriter.toString());
295 }
296
297 else if (obj instanceof Settings) {
298 Settings settingsAsked = (Settings) obj;
299 StringWriter sWriter = new StringWriter();
300 SettingsXpp3Writer settingsWriter = new SettingsXpp3Writer();
301 try {
302 settingsWriter.write(sWriter, settingsAsked);
303 } catch (IOException e) {
304 throw new MojoExecutionException("Error when writing settings", e);
305 }
306
307 response.append(sWriter.toString());
308 } else {
309
310 response.append(toXML(expr, obj));
311 }
312
313 if (output != null) {
314 try {
315 writeFile(output, response);
316 } catch (IOException e) {
317 throw new MojoExecutionException("Cannot write evaluation of expression to output: " + output, e);
318 }
319 getLog().info("Result of evaluation written to: " + output);
320 } else {
321 if (getLog().isInfoEnabled()) {
322 getLog().info(LS + response.toString());
323 } else {
324 if (forceStdout) {
325 System.out.print(response.toString());
326 System.out.flush();
327 }
328 }
329 }
330 }
331
332
333
334
335
336
337 private String toXML(String expr, Object obj) {
338 XStream currentXStream = getXStream();
339
340
341 if (obj instanceof List) {
342 List<?> list = (List<?>) obj;
343 if (!list.isEmpty()) {
344 Object elt = list.iterator().next();
345
346 String name = StringUtils.lowercaseFirstLetter(elt.getClass().getSimpleName());
347 currentXStream.alias(pluralize(name), List.class);
348 } else {
349
350 if (expr.indexOf('.') != -1) {
351 String name = expr.substring(expr.indexOf('.') + 1, expr.indexOf('}'));
352 currentXStream.alias(name, List.class);
353 }
354 }
355 }
356
357 return currentXStream.toXML(obj);
358 }
359
360
361
362
363 private XStream getXStream() {
364 if (xstream == null) {
365 xstream = new XStream();
366 addAlias(xstream);
367
368
369 xstream.registerConverter(new PropertiesConverter() {
370
371 @Override
372 public boolean canConvert(Class type) {
373 return Properties.class == type;
374 }
375
376
377 @Override
378 public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
379 Properties properties = (Properties) source;
380 Map<?, ?> map = new TreeMap<>(properties);
381 for (Map.Entry<?, ?> entry : map.entrySet()) {
382 writer.startNode(entry.getKey().toString());
383 writer.setValue(entry.getValue().toString());
384 writer.endNode();
385 }
386 }
387 });
388 }
389
390 return xstream;
391 }
392
393
394
395
396 private void addAlias(XStream xstreamObject) {
397 try {
398 addAlias(xstreamObject, getArtifactFile("maven-model"), "org.apache.maven.model");
399 addAlias(xstreamObject, getArtifactFile("maven-settings"), "org.apache.maven.settings");
400 } catch (MojoExecutionException | RepositoryException e) {
401 if (getLog().isDebugEnabled()) {
402 getLog().debug(e.getMessage(), e);
403 }
404 }
405
406
407 }
408
409
410
411
412
413
414 private void addAlias(XStream xstreamObject, File jarFile, String packageFilter) {
415 try (FileInputStream fis = new FileInputStream(jarFile);
416 JarInputStream jarStream = new JarInputStream(fis)) {
417 for (JarEntry jarEntry = jarStream.getNextJarEntry();
418 jarEntry != null;
419 jarEntry = jarStream.getNextJarEntry()) {
420 if (jarEntry.getName().toLowerCase(Locale.ENGLISH).endsWith(".class")) {
421 String name =
422 jarEntry.getName().substring(0, jarEntry.getName().indexOf("."));
423 name = name.replace("/", "\\.");
424
425 if (name.contains(packageFilter) && !name.contains("$")) {
426 try {
427 Class<?> clazz = Class.forName(name);
428 String alias = StringUtils.lowercaseFirstLetter(clazz.getSimpleName());
429 xstreamObject.alias(alias, clazz);
430 if (!clazz.equals(Model.class)) {
431 xstreamObject.omitField(clazz, "modelEncoding");
432 }
433 } catch (ClassNotFoundException e) {
434 getLog().error(e);
435 }
436 }
437 }
438
439 jarStream.closeEntry();
440 }
441 } catch (IOException e) {
442 if (getLog().isDebugEnabled()) {
443 getLog().debug("IOException: " + e.getMessage(), e);
444 }
445 }
446 }
447
448
449
450
451
452 private File getArtifactFile(String artifactId) throws MojoExecutionException, RepositoryException {
453 List<Dependency> dependencies = getHelpPluginPom().getDependencies();
454 for (Dependency dependency : dependencies) {
455 if ("org.apache.maven".equals(dependency.getGroupId())) {
456 if (artifactId.equals(dependency.getArtifactId())) {
457 Artifact mavenArtifact = new DefaultArtifact(
458 dependency.getGroupId(), dependency.getArtifactId(), "jar", dependency.getVersion());
459
460 return resolveArtifact(mavenArtifact).getArtifact().getFile();
461 }
462 }
463 }
464
465 throw new MojoExecutionException("Unable to find the 'org.apache.maven:" + artifactId + "' artifact");
466 }
467
468
469
470
471
472 private MavenProject getHelpPluginPom() throws MojoExecutionException {
473 String resource = "META-INF/maven/org.apache.maven.plugins/maven-help-plugin/pom.properties";
474
475 InputStream resourceAsStream = EvaluateMojo.class.getClassLoader().getResourceAsStream(resource);
476 if (resourceAsStream == null) {
477 throw new MojoExecutionException("The help plugin artifact was not found.");
478 }
479 Properties properties = new Properties();
480 try (InputStream is = resourceAsStream) {
481 properties.load(is);
482 } catch (IOException e) {
483 if (getLog().isDebugEnabled()) {
484 getLog().debug("IOException: " + e.getMessage(), e);
485 }
486 }
487
488 String artifactString = properties.getProperty("groupId", "unknown") + ":"
489 + properties.getProperty("artifactId", "unknown") + ":"
490 + properties.getProperty("version", "unknown");
491
492 return getMavenProject(artifactString);
493 }
494
495
496
497
498
499 private static String pluralize(String name) {
500 if (name == null || name.isEmpty()) {
501 throw new IllegalArgumentException("name is required");
502 }
503
504 if (name.endsWith("y")) {
505 return name.substring(0, name.length() - 1) + "ies";
506 } else if (name.endsWith("s")) {
507 return name;
508 } else {
509 return name + "s";
510 }
511 }
512 }