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