View Javadoc

1   package org.apache.maven.plugin.testing;
2   
3   /*
4    * Copyright 2001-2006 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import org.apache.maven.monitor.logging.DefaultLog;
20  import org.apache.maven.plugin.Mojo;
21  import org.apache.maven.plugin.logging.Log;
22  import org.codehaus.plexus.PlexusTestCase;
23  import org.codehaus.plexus.component.configurator.ComponentConfigurator;
24  import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
25  import org.codehaus.plexus.configuration.PlexusConfiguration;
26  import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
27  import org.codehaus.plexus.util.ReflectionUtils;
28  import org.codehaus.plexus.util.xml.Xpp3Dom;
29  import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
30  
31  import java.io.File;
32  import java.io.FileReader;
33  import java.io.Reader;
34  import java.util.Map;
35  import java.util.HashMap;
36  import java.lang.reflect.Field;
37  import java.lang.reflect.AccessibleObject;
38  
39  // 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
40  // as the role hint for the mojo lookup.
41  // todo: standarize the execution of the mojo and looking at the results, but could simply have a template method
42  // for verifying the state of the mojo post execution
43  // todo: need a way to look at the state of the mojo without adding getters, this could be where we finally specify
44  // the expressions which extract values from the mojo.
45  // todo: create a standard directory structure for picking up POMs to make this even easier, we really just need a testing
46  // descriptor and make this entirely declarative!
47  public abstract class AbstractMojoTestCase
48      extends PlexusTestCase
49  {
50      private ComponentConfigurator configurator;
51  
52      /*
53       * for the harness I think we have decided against going the route of using the maven project builder.
54       * instead I think we are going to try and make an instance of the localrespository and assign that
55       * to either the project stub or into the mojo directly with injection...not sure yet though.
56       */
57      //private MavenProjectBuilder projectBuilder;
58  
59      protected void setUp()
60          throws Exception
61      {
62          super.setUp();
63  
64          configurator = (ComponentConfigurator) getContainer().lookup( ComponentConfigurator.ROLE );
65  
66          //projectBuilder = (MavenProjectBuilder) getContainer().lookup( MavenProjectBuilder.ROLE );
67      }
68  
69      /**
70       * lookup the mojo leveraging the subproject pom
71       *
72       */
73      protected Mojo lookupMojo( String goal, String pluginPom ) throws Exception
74      {
75          return lookupMojo( goal, new File( pluginPom ) );
76      }
77  
78      /**
79       * lookup an empty mojo
80       */
81      protected Mojo lookupEmptyMojo( String goal, String pluginPom ) throws Exception
82      {
83         return lookupEmptyMojo( goal, new File( pluginPom ) );
84      }
85  
86  
87      /**
88       * lookup the mojo leveraging the actual subprojects pom
89       *
90       * @param goal
91       * @param pom
92       * @return
93       * @throws Exception
94       */
95      protected Mojo lookupMojo( String goal, File pom )
96          throws Exception
97      {
98          File pluginPom = new File( getBasedir(), "pom.xml" );
99  
100         Xpp3Dom pluginPomDom = Xpp3DomBuilder.build( new FileReader( pluginPom ) );
101 
102         String artifactId = pluginPomDom.getChild( "artifactId" ).getValue();
103 
104         String groupId = resolveFromRootThenParent( pluginPomDom, "groupId" );
105 
106         String version = resolveFromRootThenParent( pluginPomDom, "version" );
107 
108         PlexusConfiguration pluginConfiguration = extractPluginConfiguration( artifactId, pom );
109 
110         return lookupMojo( groupId, artifactId, version, goal, pluginConfiguration );
111     }
112 
113     /**
114      * lookup the mojo leveraging the actual subprojects pom
115      *
116      * @param goal
117      * @param pom
118      * @return
119      * @throws Exception
120      */
121     protected Mojo lookupEmptyMojo( String goal, File pom )
122         throws Exception
123     {
124         File pluginPom = new File( getBasedir(), "pom.xml" );
125 
126         Xpp3Dom pluginPomDom = Xpp3DomBuilder.build( new FileReader( pluginPom ) );
127 
128         String artifactId = pluginPomDom.getChild( "artifactId" ).getValue();
129 
130         String groupId = resolveFromRootThenParent( pluginPomDom, "groupId" );
131 
132         String version = resolveFromRootThenParent( pluginPomDom, "version" );
133 
134         return lookupMojo( groupId, artifactId, version, goal, null );
135     }
136 
137  /*
138     protected Mojo lookupMojo( String groupId, String artifactId, String version, String goal, File pom )
139         throws Exception
140     {
141         PlexusConfiguration pluginConfiguration = extractPluginConfiguration( artifactId, pom );
142 
143         return lookupMojo( groupId, artifactId, version, goal, pluginConfiguration );
144     }
145     */
146     /**
147      * lookup the mojo while we have all of the relavent information
148      *
149      * @param groupId
150      * @param artifactId
151      * @param version
152      * @param goal
153      * @param pluginConfiguration
154      * @return
155      * @throws Exception
156      */
157     protected Mojo lookupMojo( String groupId, String artifactId, String version, String goal, PlexusConfiguration pluginConfiguration )
158         throws Exception
159     {
160         validateContainerStatus();
161 
162         // pluginkey = groupId : artifactId : version : goal
163 
164         Mojo mojo = (Mojo) lookup( Mojo.ROLE, groupId + ":" + artifactId + ":" + version + ":" + goal );
165 
166         Log mojoLogger = new DefaultLog( container.getLoggerManager().getLoggerForComponent( Mojo.ROLE ) );
167         
168         mojo.setLog( mojoLogger );
169 
170         if ( pluginConfiguration != null )
171         {
172             /* requires v10 of plexus container for lookup on expression evaluator
173             ExpressionEvaluator evaluator = (ExpressionEvaluator) getContainer().lookup( ExpressionEvaluator.ROLE, "stub-evaluator" );
174             */
175             ExpressionEvaluator evaluator = new ResolverExpressionEvaluatorStub();
176 
177             configurator.configureComponent( mojo, pluginConfiguration, evaluator, getContainer().getContainerRealm() );
178         }
179 
180         return mojo;
181     }
182 
183 
184     protected PlexusConfiguration extractPluginConfiguration( String artifactId, File pom )
185         throws Exception
186     {
187         Reader reader = new FileReader( pom );
188 
189         Xpp3Dom pomDom = Xpp3DomBuilder.build( reader );
190 
191         return extractPluginConfiguration( artifactId, pomDom );
192     }
193 
194     protected PlexusConfiguration extractPluginConfiguration( String artifactId, Xpp3Dom pomDom )
195         throws Exception
196     {
197         Xpp3Dom pluginConfigurationElement = null;
198 
199         Xpp3Dom buildElement = pomDom.getChild( "build" );
200         if ( buildElement != null )
201         {
202             Xpp3Dom pluginsRootElement = buildElement.getChild( "plugins" );
203 
204             if ( pluginsRootElement != null )
205             {
206                 Xpp3Dom[] pluginElements = pluginsRootElement.getChildren();
207 
208                 for ( int i = 0; i < pluginElements.length; i++ )
209                 {
210                     Xpp3Dom pluginElement = pluginElements[i];
211 
212                     String pluginElementArtifactId = pluginElement.getChild( "artifactId" ).getValue();
213 
214                     if ( pluginElementArtifactId.equals( artifactId ) )
215                     {
216                         pluginConfigurationElement = pluginElement.getChild( "configuration" );
217 
218                         break;
219                     }
220                 }
221             }
222         }
223 
224         if ( pluginConfigurationElement == null )
225         {
226             throw new ConfigurationException( "Cannot find a configuration element for a plugin with an artifactId of "
227                 + artifactId + "." );
228         }
229 
230         return new XmlPlexusConfiguration( pluginConfigurationElement );
231     }
232 
233     protected Mojo configureMojo( Mojo mojo, String artifactId, File pom )
234         throws Exception
235     {
236         validateContainerStatus();
237 
238         PlexusConfiguration pluginConfiguration = extractPluginConfiguration( artifactId, pom );
239 
240         ExpressionEvaluator evaluator = new ResolverExpressionEvaluatorStub();
241 
242 
243         configurator.configureComponent( mojo, pluginConfiguration, evaluator, getContainer().getContainerRealm() );
244 
245         return mojo;
246     }
247 
248     /**
249      * configure the mojo with the given plexus configuration
250      *
251      * @param mojo
252      * @param pluginConfiguration
253      * @return
254      * @throws Exception
255      */
256     protected Mojo configureMojo( Mojo mojo, PlexusConfiguration pluginConfiguration )
257         throws Exception
258     {
259         validateContainerStatus();
260 
261         ExpressionEvaluator evaluator = new ResolverExpressionEvaluatorStub();
262 
263         configurator.configureComponent( mojo, pluginConfiguration, evaluator, getContainer().getContainerRealm() );
264 
265         return mojo;
266     }
267 
268 
269     /**
270      * convience method to obtain the value of a variable on a mojo that might not have a getter.
271      *
272      * NOTE: the caller is responsible for casting to to what the desired type is.
273      *
274      * @param object
275      * @param variable
276      * @return object value of variable
277      * @throws IllegalArgumentException
278      */
279     protected Object getVariableValueFromObject( Object object, String variable )
280         throws IllegalAccessException
281     {
282         Field field = ReflectionUtils.getFieldByNameIncludingSuperclasses( variable, object.getClass() );
283 
284         field.setAccessible( true );
285 
286         return field.get( object );
287     }
288 
289 
290     /**
291      * convience method to obtain all variables and values from the mojo (including its superclasses)
292      *
293      * Note: the values in the map are of type Object so the caller is responsible for casting to desired types.
294      *
295      * @param object
296      * @return map of variable names and values
297      */
298     protected Map getVariablesAndValuesFromObject( Object object )
299         throws IllegalAccessException
300     {
301         return getVariablesAndValuesFromObject( object.getClass(), object );
302     }
303 
304     /**
305      * convience method to obtain all variables and values from the mojo (including its superclasses)
306      *
307      * Note: the values in the map are of type Object so the caller is responsible for casting to desired types.
308      *
309      * @param clazz
310      * @param object
311      * @return map of variable names and values
312      */
313     protected Map getVariablesAndValuesFromObject( Class clazz, Object object )
314         throws IllegalAccessException
315     {
316         Map map = new HashMap();
317 
318         Field[] fields = clazz.getDeclaredFields();
319 
320         AccessibleObject.setAccessible( fields, true);
321 
322         for (int i = 0; i < fields.length; ++i)
323         {
324             Field field = fields[i];
325 
326             map.put( field.getName(), field.get( object ) );
327 
328         }
329 
330         Class superclass = clazz.getSuperclass();
331 
332         if ( !Object.class.equals(  superclass ) )
333         {
334             map.putAll( getVariablesAndValuesFromObject( superclass, object ) );
335         }
336 
337         return map;
338     }
339 
340 
341     /**
342      * convience method to set values to variables in objects that don't have setters
343      * @param object
344      * @param variable
345      * @param value
346      * @throws IllegalAccessException
347      */
348     protected void setVariableValueToObject( Object object, String variable, Object value )
349         throws IllegalAccessException
350     {
351         Field field = ReflectionUtils.getFieldByNameIncludingSuperclasses( variable, object.getClass() );
352 
353         field.setAccessible( true );
354 
355         field.set(object, value );
356     }
357     /**
358      * sometimes the parent element might contain the correct value so generalize that access
359      *
360      * TODO find out where this is probably done elsewhere
361      *
362      * @param pluginPomDom
363      * @param element
364      * @return
365      * @throws Exception
366      */
367 
368     private String resolveFromRootThenParent( Xpp3Dom pluginPomDom, String element ) throws Exception
369     {
370         Xpp3Dom elementDom = pluginPomDom.getChild( element );
371 
372         // parent might have the group Id so resolve it
373         if ( elementDom == null )
374         {
375             Xpp3Dom pluginParentDom = pluginPomDom.getChild("parent");
376 
377             if (pluginParentDom != null )
378             {
379                 elementDom = pluginParentDom.getChild( element );
380 
381                 if ( elementDom == null )
382                 {
383                     throw new Exception ( "unable to determine " + element );
384                 }
385                 else
386                 {
387                     return elementDom.getValue();
388                 }
389             }
390             else
391             {
392                 throw new Exception ( "unable to determine " + element );
393             }
394         }
395         else
396         {
397             return elementDom.getValue();
398         }
399     }
400 
401 
402     /*
403      * We should make sure this is called in each method that makes use of the container,
404      * otherwise we throw ugly NPE's
405      *
406      * crops up when the subclassing code defines the setUp method but doesn't call super.setUp()
407      *
408      * @throws Exception
409      */
410     private void validateContainerStatus() throws Exception {
411         if (container != null)
412         {
413             return;
414         }
415         else
416         {
417             throw new Exception("container is null, make sure super.setUp() is called");
418         }
419     }
420 }