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.transfer.artifact.ArtifactCoordinate;
55 import org.apache.maven.shared.transfer.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 System.out.flush();
408 }
409 }
410 }
411 }
412
413
414
415
416
417
418 private String toXML( String expr, Object obj )
419 {
420 XStream currentXStream = getXStream();
421
422
423 if ( obj instanceof List )
424 {
425 List<?> list = (List<?>) obj;
426 if ( list.size() > 0 )
427 {
428 Object elt = list.iterator().next();
429
430 String name = StringUtils.lowercaseFirstLetter( elt.getClass().getSimpleName() );
431 currentXStream.alias( pluralize( name ), List.class );
432 }
433 else
434 {
435
436 if ( expr.indexOf( '.' ) != -1 )
437 {
438 String name = expr.substring( expr.indexOf( '.' ) + 1, expr.indexOf( '}' ) );
439 currentXStream.alias( name, List.class );
440 }
441 }
442 }
443
444 return currentXStream.toXML( obj );
445 }
446
447
448
449
450 private XStream getXStream()
451 {
452 if ( xstream == null )
453 {
454 xstream = new XStream();
455 addAlias( xstream );
456
457
458 xstream.registerConverter( new PropertiesConverter()
459 {
460
461 public boolean canConvert( @SuppressWarnings( "rawtypes" ) Class type )
462 {
463 return Properties.class == type;
464 }
465
466
467 public void marshal( Object source, HierarchicalStreamWriter writer, MarshallingContext context )
468 {
469 Properties properties = (Properties) source;
470 Map<?, ?> map = new TreeMap<Object, Object>( properties );
471 for ( Map.Entry<?, ?> entry : map.entrySet() )
472 {
473 writer.startNode( entry.getKey().toString() );
474 writer.setValue( entry.getValue().toString() );
475 writer.endNode();
476 }
477 }
478 } );
479 }
480
481 return xstream;
482 }
483
484
485
486
487 private void addAlias( XStream xstreamObject )
488 {
489 try
490 {
491 addAlias( xstreamObject, getMavenModelJarFile(), "org.apache.maven.model" );
492 addAlias( xstreamObject, getMavenSettingsJarFile(), "org.apache.maven.settings" );
493 }
494 catch ( MojoExecutionException e )
495 {
496 if ( getLog().isDebugEnabled() )
497 {
498 getLog().debug( "MojoExecutionException: " + e.getMessage(), e );
499 }
500 }
501 catch ( ArtifactResolverException e )
502 {
503 if ( getLog().isDebugEnabled() )
504 {
505 getLog().debug( "ArtifactResolverException: " + e.getMessage(), e );
506 }
507 }
508 catch ( ProjectBuildingException e )
509 {
510 if ( getLog().isDebugEnabled() )
511 {
512 getLog().debug( "ProjectBuildingException: " + e.getMessage(), e );
513 }
514 }
515
516
517 }
518
519
520
521
522
523
524 private void addAlias( XStream xstreamObject, File jarFile, String packageFilter )
525 {
526 JarInputStream jarStream = null;
527 try
528 {
529 jarStream = new JarInputStream( new FileInputStream( jarFile ) );
530 for ( JarEntry jarEntry = jarStream.getNextJarEntry(); jarEntry != null;
531 jarEntry = jarStream.getNextJarEntry() )
532 {
533 if ( jarEntry.getName().toLowerCase( Locale.ENGLISH ).endsWith( ".class" ) )
534 {
535 String name = jarEntry.getName().substring( 0, jarEntry.getName().indexOf( "." ) );
536 name = name.replaceAll( "/", "\\." );
537
538 if ( name.contains( packageFilter ) && !name.contains( "$" ) )
539 {
540 try
541 {
542 Class<?> clazz = ClassUtils.getClass( name );
543 String alias = StringUtils.lowercaseFirstLetter( clazz.getSimpleName() );
544 xstreamObject.alias( alias, clazz );
545 if ( !clazz.equals( Model.class ) )
546 {
547 xstreamObject.omitField( clazz, "modelEncoding" );
548 }
549 }
550 catch ( ClassNotFoundException e )
551 {
552 getLog().error( e );
553 }
554 }
555 }
556
557 jarStream.closeEntry();
558 }
559
560 jarStream.close();
561 jarStream = null;
562 }
563 catch ( IOException e )
564 {
565 if ( getLog().isDebugEnabled() )
566 {
567 getLog().debug( "IOException: " + e.getMessage(), e );
568 }
569 }
570 finally
571 {
572 IOUtil.close( jarStream );
573 }
574 }
575
576
577
578
579
580
581
582 private File getMavenModelJarFile()
583 throws MojoExecutionException, ProjectBuildingException, ArtifactResolverException
584 {
585 return getArtifactFile( true );
586 }
587
588
589
590
591
592
593
594 private File getMavenSettingsJarFile()
595 throws MojoExecutionException, ProjectBuildingException, ArtifactResolverException
596 {
597 return getArtifactFile( false );
598 }
599
600
601
602
603
604
605
606
607
608
609 private File getArtifactFile( boolean isPom )
610 throws MojoExecutionException, ProjectBuildingException, ArtifactResolverException
611 {
612 List<Dependency> dependencies = getHelpPluginPom().getDependencies();
613 for ( Dependency depependency : dependencies )
614 {
615 if ( !( depependency.getGroupId().equals( "org.apache.maven" ) ) )
616 {
617 continue;
618 }
619
620 if ( isPom )
621 {
622 if ( !( depependency.getArtifactId().equals( "maven-model" ) ) )
623 {
624 continue;
625 }
626 }
627 else
628 {
629 if ( !( depependency.getArtifactId().equals( "maven-settings" ) ) )
630 {
631 continue;
632 }
633 }
634
635 ArtifactCoordinate coordinate =
636 getArtifactCoordinate( depependency.getGroupId(), depependency.getArtifactId(),
637 depependency.getVersion(), "jar" );
638 ProjectBuildingRequest pbr = new DefaultProjectBuildingRequest( session.getProjectBuildingRequest() );
639 pbr.setRemoteRepositories( remoteRepositories );
640 return artifactResolver.resolveArtifact( pbr, coordinate ).getArtifact().getFile();
641 }
642
643 throw new MojoExecutionException( "Unable to find the 'org.apache.maven:"
644 + ( isPom ? "maven-model" : "maven-settings" ) + "' artifact" );
645 }
646
647
648
649
650
651
652 private MavenProject getHelpPluginPom()
653 throws MojoExecutionException, ProjectBuildingException
654 {
655 String resource = "META-INF/maven/org.apache.maven.plugins/maven-help-plugin/pom.properties";
656
657 InputStream resourceAsStream = EvaluateMojo.class.getClassLoader().getResourceAsStream( resource );
658 if ( resourceAsStream == null )
659 {
660 throw new MojoExecutionException( "The help plugin artifact was not found." );
661 }
662 Properties properties = new Properties();
663 try
664 {
665 properties.load( resourceAsStream );
666 }
667 catch ( IOException e )
668 {
669 if ( getLog().isDebugEnabled() )
670 {
671 getLog().debug( "IOException: " + e.getMessage(), e );
672 }
673 }
674 finally
675 {
676 IOUtil.close( resourceAsStream );
677 }
678
679 String artifactString =
680 properties.getProperty( "groupId", "unknown" ) + ":"
681 + properties.getProperty( "artifactId", "unknown" ) + ":"
682 + properties.getProperty( "version", "unknown" );
683
684 return getMavenProject( artifactString );
685 }
686
687
688
689
690
691 private static String pluralize( String name )
692 {
693 if ( StringUtils.isEmpty( name ) )
694 {
695 throw new IllegalArgumentException( "name is required" );
696 }
697
698 if ( name.endsWith( "y" ) )
699 {
700 return name.substring( 0, name.length() - 1 ) + "ies";
701 }
702 else if ( name.endsWith( "s" ) )
703 {
704 return name;
705 }
706 else
707 {
708 return name + "s";
709 }
710 }
711 }