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