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