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