View Javadoc

1   package org.apache.maven.plugin.testing;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.BufferedReader;
23  import java.io.File;
24  import java.io.FileInputStream;
25  import java.io.InputStream;
26  import java.io.Reader;
27  import java.lang.reflect.AccessibleObject;
28  import java.lang.reflect.Field;
29  import java.util.HashMap;
30  import java.util.Map;
31  
32  import org.apache.maven.monitor.logging.DefaultLog;
33  import org.apache.maven.plugin.Mojo;
34  import org.apache.maven.plugin.descriptor.PluginDescriptor;
35  import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
36  import org.apache.maven.plugin.logging.Log;
37  import org.codehaus.plexus.ContainerConfiguration;
38  import org.codehaus.plexus.DefaultContainerConfiguration;
39  import org.codehaus.plexus.DefaultPlexusContainer;
40  import org.codehaus.plexus.PlexusContainer;
41  import org.codehaus.plexus.PlexusContainerException;
42  import org.codehaus.plexus.PlexusTestCase;
43  import org.codehaus.plexus.classworlds.ClassWorld;
44  import org.codehaus.plexus.component.configurator.ComponentConfigurator;
45  import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
46  import org.codehaus.plexus.component.repository.ComponentDescriptor;
47  import org.codehaus.plexus.configuration.PlexusConfiguration;
48  import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
49  import org.codehaus.plexus.logging.LoggerManager;
50  import org.codehaus.plexus.util.InterpolationFilterReader;
51  import org.codehaus.plexus.util.ReaderFactory;
52  import org.codehaus.plexus.util.ReflectionUtils;
53  import org.codehaus.plexus.util.xml.XmlStreamReader;
54  import org.codehaus.plexus.util.xml.Xpp3Dom;
55  import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
56  
57  /**
58   * TODO: add a way to use the plugin POM for the lookup so that the user doesn't have to provide the a:g:v:goal
59   * as the role hint for the mojo lookup.
60   * TODO: standardize the execution of the mojo and looking at the results, but could simply have a template method
61   * for verifying the state of the mojo post execution
62   * TODO: need a way to look at the state of the mojo without adding getters, this could be where we finally specify
63   * the expressions which extract values from the mojo.
64   * TODO: create a standard directory structure for picking up POMs to make this even easier, we really just need a testing
65   * descriptor and make this entirely declarative!
66   *
67   * @author jesse
68   * @version $Id: AbstractMojoTestCase.java 805786 2009-08-19 13:04:24Z bentmann $
69   */
70  public abstract class AbstractMojoTestCase
71      extends PlexusTestCase
72  {
73      private ComponentConfigurator configurator;
74  
75      private PlexusContainer container;
76      
77      /*
78       * for the harness I think we have decided against going the route of using the maven project builder.
79       * instead I think we are going to try and make an instance of the localrespository and assign that
80       * to either the project stub or into the mojo directly with injection...not sure yet though.
81       */
82      //private MavenProjectBuilder projectBuilder;
83  
84      protected void setUp()
85          throws Exception
86      {
87          configurator = getContainer().lookup( ComponentConfigurator.class, "basic" );
88  
89          InputStream is = getClass().getResourceAsStream( "/" + getPluginDescriptorLocation() );
90  
91          XmlStreamReader reader = ReaderFactory.newXmlReader( is );
92  
93          InterpolationFilterReader interpolationFilterReader =
94              new InterpolationFilterReader( new BufferedReader( reader ), container.getContext().getContextData() );
95  
96          PluginDescriptor pluginDescriptor = new PluginDescriptorBuilder().build( interpolationFilterReader );
97  
98          for ( ComponentDescriptor<?> desc : pluginDescriptor.getComponents() )
99          {
100             getContainer().addComponentDescriptor( desc );
101         }
102     }
103 
104     protected InputStream getPublicDescriptorStream()
105         throws Exception
106     {
107         return new FileInputStream( new File( getPluginDescriptorPath() ) );
108     }
109 
110     protected String getPluginDescriptorPath()
111     {
112         return getBasedir() + "/target/classes/META-INF/maven/plugin.xml";
113     }
114 
115     protected String getPluginDescriptorLocation()
116     {
117         return "META-INF/maven/plugin.xml";
118     }
119 
120     protected void setupContainer()
121     {
122         ClassWorld classWorld = new ClassWorld( "plexus.core", Thread.currentThread().getContextClassLoader() );
123 
124         ContainerConfiguration cc =
125             new DefaultContainerConfiguration().setClassWorld( classWorld ).setName( "embedder" );
126         try
127         {
128             container = new DefaultPlexusContainer( cc );
129         }
130         catch ( PlexusContainerException e )
131         {
132             e.printStackTrace();
133             fail( "Failed to create plexus container." );
134         }   
135     }
136     
137     protected PlexusContainer getContainer()
138     {
139         if ( container == null )
140         {
141             setupContainer();
142         }
143 
144         return container;
145     }    
146     
147     /**
148      * Lookup the mojo leveraging the subproject pom
149      *
150      * @param goal
151      * @param pluginPom
152      * @return a Mojo instance
153      * @throws Exception
154      */
155     protected Mojo lookupMojo( String goal, String pluginPom )
156         throws Exception
157     {
158         return lookupMojo( goal, new File( pluginPom ) );
159     }
160 
161     /**
162      * Lookup an empty mojo
163      *
164      * @param goal
165      * @param pluginPom
166      * @return a Mojo instance
167      * @throws Exception
168      */
169     protected Mojo lookupEmptyMojo( String goal, String pluginPom )
170         throws Exception
171     {
172         return lookupEmptyMojo( goal, new File( pluginPom ) );
173     }
174 
175     /**
176      * Lookup the mojo leveraging the actual subprojects pom
177      *
178      * @param goal
179      * @param pom
180      * @return a Mojo instance
181      * @throws Exception
182      */
183     protected Mojo lookupMojo( String goal, File pom )
184         throws Exception
185     {
186         File pluginPom = new File( getBasedir(), "pom.xml" );
187 
188         Xpp3Dom pluginPomDom = Xpp3DomBuilder.build( ReaderFactory.newXmlReader( pluginPom ) );
189 
190         String artifactId = pluginPomDom.getChild( "artifactId" ).getValue();
191 
192         String groupId = resolveFromRootThenParent( pluginPomDom, "groupId" );
193 
194         String version = resolveFromRootThenParent( pluginPomDom, "version" );
195 
196         PlexusConfiguration pluginConfiguration = extractPluginConfiguration( artifactId, pom );
197 
198         return lookupMojo( groupId, artifactId, version, goal, pluginConfiguration );
199     }
200 
201     /**
202      * Lookup the mojo leveraging the actual subprojects pom
203      *
204      * @param goal
205      * @param pom
206      * @return a Mojo instance
207      * @throws Exception
208      */
209     protected Mojo lookupEmptyMojo( String goal, File pom )
210         throws Exception
211     {
212         File pluginPom = new File( getBasedir(), "pom.xml" );
213 
214         Xpp3Dom pluginPomDom = Xpp3DomBuilder.build( ReaderFactory.newXmlReader( pluginPom ) );
215 
216         String artifactId = pluginPomDom.getChild( "artifactId" ).getValue();
217 
218         String groupId = resolveFromRootThenParent( pluginPomDom, "groupId" );
219 
220         String version = resolveFromRootThenParent( pluginPomDom, "version" );
221 
222         return lookupMojo( groupId, artifactId, version, goal, null );
223     }
224 
225     /*
226      protected Mojo lookupMojo( String groupId, String artifactId, String version, String goal, File pom )
227      throws Exception
228      {
229      PlexusConfiguration pluginConfiguration = extractPluginConfiguration( artifactId, pom );
230 
231      return lookupMojo( groupId, artifactId, version, goal, pluginConfiguration );
232      }
233      */
234     /**
235      * lookup the mojo while we have all of the relavent information
236      *
237      * @param groupId
238      * @param artifactId
239      * @param version
240      * @param goal
241      * @param pluginConfiguration
242      * @return a Mojo instance
243      * @throws Exception
244      */
245     protected Mojo lookupMojo( String groupId, String artifactId, String version, String goal,
246                                PlexusConfiguration pluginConfiguration )
247         throws Exception
248     {
249         validateContainerStatus();
250 
251         // pluginkey = groupId : artifactId : version : goal
252 
253         Mojo mojo = (Mojo) lookup( Mojo.ROLE, groupId + ":" + artifactId + ":" + version + ":" + goal );
254 
255         LoggerManager loggerManager = (LoggerManager) getContainer().lookup( LoggerManager.class );
256         
257         Log mojoLogger = new DefaultLog( loggerManager.getLoggerForComponent( Mojo.ROLE ) );
258 
259         mojo.setLog( mojoLogger );
260 
261         if ( pluginConfiguration != null )
262         {
263             /* requires v10 of plexus container for lookup on expression evaluator
264              ExpressionEvaluator evaluator = (ExpressionEvaluator) getContainer().lookup( ExpressionEvaluator.ROLE,
265                                                                                          "stub-evaluator" );
266              */
267             ExpressionEvaluator evaluator = new ResolverExpressionEvaluatorStub();
268 
269             configurator.configureComponent( mojo, pluginConfiguration, evaluator, getContainer().getContainerRealm() );
270         }
271 
272         return mojo;
273     }
274 
275     /**
276      * @param artifactId
277      * @param pom
278      * @return the plexus configuration
279      * @throws Exception
280      */
281     protected PlexusConfiguration extractPluginConfiguration( String artifactId, File pom )
282         throws Exception
283     {
284         Reader reader = ReaderFactory.newXmlReader( pom );
285 
286         Xpp3Dom pomDom = Xpp3DomBuilder.build( reader );
287 
288         return extractPluginConfiguration( artifactId, pomDom );
289     }
290 
291     /**
292      * @param artifactId
293      * @param pomDom
294      * @return the plexus configuration
295      * @throws Exception
296      */
297     protected PlexusConfiguration extractPluginConfiguration( String artifactId, Xpp3Dom pomDom )
298         throws Exception
299     {
300         Xpp3Dom pluginConfigurationElement = null;
301 
302         Xpp3Dom buildElement = pomDom.getChild( "build" );
303         if ( buildElement != null )
304         {
305             Xpp3Dom pluginsRootElement = buildElement.getChild( "plugins" );
306 
307             if ( pluginsRootElement != null )
308             {
309                 Xpp3Dom[] pluginElements = pluginsRootElement.getChildren();
310 
311                 for ( int i = 0; i < pluginElements.length; i++ )
312                 {
313                     Xpp3Dom pluginElement = pluginElements[i];
314 
315                     String pluginElementArtifactId = pluginElement.getChild( "artifactId" ).getValue();
316 
317                     if ( pluginElementArtifactId.equals( artifactId ) )
318                     {
319                         pluginConfigurationElement = pluginElement.getChild( "configuration" );
320 
321                         break;
322                     }
323                 }
324 
325                 if ( pluginConfigurationElement == null )
326                 {
327                     throw new ConfigurationException( "Cannot find a configuration element for a plugin with an "
328                         + "artifactId of " + artifactId + "." );
329                 }
330             }
331         }
332 
333         if ( pluginConfigurationElement == null )
334         {
335             throw new ConfigurationException( "Cannot find a configuration element for a plugin with an artifactId of "
336                 + artifactId + "." );
337         }
338 
339         return new XmlPlexusConfiguration( pluginConfigurationElement );
340     }
341 
342     /**
343      * Configure the mojo
344      *
345      * @param mojo
346      * @param artifactId
347      * @param pom
348      * @return a Mojo instance
349      * @throws Exception
350      */
351     protected Mojo configureMojo( Mojo mojo, String artifactId, File pom )
352         throws Exception
353     {
354         validateContainerStatus();
355 
356         PlexusConfiguration pluginConfiguration = extractPluginConfiguration( artifactId, pom );
357 
358         ExpressionEvaluator evaluator = new ResolverExpressionEvaluatorStub();
359 
360         configurator.configureComponent( mojo, pluginConfiguration, evaluator, getContainer().getContainerRealm() );
361 
362         return mojo;
363     }
364 
365     /**
366      * Configure the mojo with the given plexus configuration
367      *
368      * @param mojo
369      * @param pluginConfiguration
370      * @return a Mojo instance
371      * @throws Exception
372      */
373     protected Mojo configureMojo( Mojo mojo, PlexusConfiguration pluginConfiguration )
374         throws Exception
375     {
376         validateContainerStatus();
377 
378         ExpressionEvaluator evaluator = new ResolverExpressionEvaluatorStub();
379 
380         configurator.configureComponent( mojo, pluginConfiguration, evaluator, getContainer().getContainerRealm() );
381 
382         return mojo;
383     }
384 
385     /**
386      * Convenience method to obtain the value of a variable on a mojo that might not have a getter.
387      *
388      * NOTE: the caller is responsible for casting to to what the desired type is.
389      *
390      * @param object
391      * @param variable
392      * @return object value of variable
393      * @throws IllegalArgumentException
394      */
395     protected Object getVariableValueFromObject( Object object, String variable )
396         throws IllegalAccessException
397     {
398         Field field = ReflectionUtils.getFieldByNameIncludingSuperclasses( variable, object.getClass() );
399 
400         field.setAccessible( true );
401 
402         return field.get( object );
403     }
404 
405     /**
406      * Convenience method to obtain all variables and values from the mojo (including its superclasses)
407      *
408      * Note: the values in the map are of type Object so the caller is responsible for casting to desired types.
409      *
410      * @param object
411      * @return map of variable names and values
412      */
413     protected Map getVariablesAndValuesFromObject( Object object )
414         throws IllegalAccessException
415     {
416         return getVariablesAndValuesFromObject( object.getClass(), object );
417     }
418 
419     /**
420      * Convenience method to obtain all variables and values from the mojo (including its superclasses)
421      *
422      * Note: the values in the map are of type Object so the caller is responsible for casting to desired types.
423      *
424      * @param clazz
425      * @param object
426      * @return map of variable names and values
427      */
428     protected Map getVariablesAndValuesFromObject( Class clazz, Object object )
429         throws IllegalAccessException
430     {
431         Map map = new HashMap();
432 
433         Field[] fields = clazz.getDeclaredFields();
434 
435         AccessibleObject.setAccessible( fields, true );
436 
437         for ( int i = 0; i < fields.length; ++i )
438         {
439             Field field = fields[i];
440 
441             map.put( field.getName(), field.get( object ) );
442 
443         }
444 
445         Class superclass = clazz.getSuperclass();
446 
447         if ( !Object.class.equals( superclass ) )
448         {
449             map.putAll( getVariablesAndValuesFromObject( superclass, object ) );
450         }
451 
452         return map;
453     }
454 
455     /**
456      * Convenience method to set values to variables in objects that don't have setters
457      *
458      * @param object
459      * @param variable
460      * @param value
461      * @throws IllegalAccessException
462      */
463     protected void setVariableValueToObject( Object object, String variable, Object value )
464         throws IllegalAccessException
465     {
466         Field field = ReflectionUtils.getFieldByNameIncludingSuperclasses( variable, object.getClass() );
467 
468         field.setAccessible( true );
469 
470         field.set( object, value );
471     }
472 
473     /**
474      * sometimes the parent element might contain the correct value so generalize that access
475      *
476      * TODO find out where this is probably done elsewhere
477      *
478      * @param pluginPomDom
479      * @param element
480      * @return
481      * @throws Exception
482      */
483     private String resolveFromRootThenParent( Xpp3Dom pluginPomDom, String element )
484         throws Exception
485     {
486         Xpp3Dom elementDom = pluginPomDom.getChild( element );
487 
488         // parent might have the group Id so resolve it
489         if ( elementDom == null )
490         {
491             Xpp3Dom pluginParentDom = pluginPomDom.getChild( "parent" );
492 
493             if ( pluginParentDom != null )
494             {
495                 elementDom = pluginParentDom.getChild( element );
496 
497                 if ( elementDom == null )
498                 {
499                     throw new Exception( "unable to determine " + element );
500                 }
501 
502                 return elementDom.getValue();
503             }
504 
505             throw new Exception( "unable to determine " + element );
506         }
507 
508         return elementDom.getValue();
509     }
510 
511     /**
512      * We should make sure this is called in each method that makes use of the container,
513      * otherwise we throw ugly NPE's
514      *
515      * crops up when the subclassing code defines the setUp method but doesn't call super.setUp()
516      *
517      * @throws Exception
518      */
519     private void validateContainerStatus()
520         throws Exception
521     {
522         if ( getContainer() != null )
523         {
524             return;
525         }
526 
527         throw new Exception( "container is null, make sure super.setUp() is called" );
528     }
529 }