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